mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
* Rename asp > server * Rename pool > round * Consolidate naming for pubkey/prvkey vars and types * Fix * Fix * Fix wasm * Rename congestionTree > vtxoTree * Fix wasm * Rename payment > request * Rename congestionTree > vtxoTree after syncing with master * Fix Send API in SDK * Fix wasm * Fix wasm * Fixes * Fixes after review * Fix * Fix naming * Fix * Fix e2e tests
548 lines
12 KiB
Go
548 lines
12 KiB
Go
package domain_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/ark-network/ark/common/tree"
|
|
"github.com/ark-network/ark/server/internal/core/domain"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
dustAmount = uint64(450)
|
|
requests = []domain.TxRequest{
|
|
{
|
|
Id: "0",
|
|
Inputs: []domain.Vtxo{
|
|
{
|
|
VtxoKey: domain.VtxoKey{
|
|
Txid: txid,
|
|
VOut: 0,
|
|
},
|
|
PubKey: pubkey,
|
|
Amount: 2000,
|
|
},
|
|
},
|
|
Receivers: []domain.Receiver{
|
|
{
|
|
PubKey: pubkey,
|
|
Amount: 700,
|
|
},
|
|
{
|
|
PubKey: pubkey,
|
|
Amount: 700,
|
|
},
|
|
{
|
|
PubKey: pubkey,
|
|
Amount: 600,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Id: "1",
|
|
Inputs: []domain.Vtxo{
|
|
{
|
|
VtxoKey: domain.VtxoKey{
|
|
Txid: txid,
|
|
VOut: 0,
|
|
},
|
|
PubKey: pubkey,
|
|
Amount: 1000,
|
|
},
|
|
{
|
|
VtxoKey: domain.VtxoKey{
|
|
Txid: txid,
|
|
VOut: 0,
|
|
},
|
|
PubKey: pubkey,
|
|
Amount: 1000,
|
|
},
|
|
},
|
|
Receivers: []domain.Receiver{{
|
|
PubKey: pubkey,
|
|
Amount: 2000,
|
|
}},
|
|
},
|
|
}
|
|
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
|
|
emptyTx = "0200000000000000000000"
|
|
txid = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
vtxoTree = tree.VtxoTree{
|
|
{
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
{
|
|
Txid: txid,
|
|
Tx: emptyPtx,
|
|
ParentTxid: txid,
|
|
},
|
|
},
|
|
}
|
|
connectors = []string{emptyPtx, emptyPtx, emptyPtx}
|
|
forfeitTxs = []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx}
|
|
roundTx = emptyTx
|
|
)
|
|
|
|
func TestRound(t *testing.T) {
|
|
testStartRegistration(t)
|
|
|
|
testRegisterTxRequests(t)
|
|
|
|
testStartFinalization(t)
|
|
|
|
testEndFinalization(t)
|
|
|
|
testFail(t)
|
|
}
|
|
|
|
func testStartRegistration(t *testing.T) {
|
|
t.Run("start_registration", func(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
round := domain.NewRound(dustAmount)
|
|
require.NotNil(t, round)
|
|
require.NotEmpty(t, round.Id)
|
|
require.Empty(t, round.Events())
|
|
require.False(t, round.IsStarted())
|
|
require.False(t, round.IsEnded())
|
|
require.False(t, round.IsFailed())
|
|
|
|
events, err := round.StartRegistration()
|
|
require.NoError(t, err)
|
|
require.Len(t, events, 1)
|
|
require.True(t, round.IsStarted())
|
|
require.False(t, round.IsEnded())
|
|
require.False(t, round.IsFailed())
|
|
|
|
event, ok := events[0].(domain.RoundStarted)
|
|
require.True(t, ok)
|
|
require.Equal(t, round.Id, event.Id)
|
|
require.Equal(t, round.StartingTimestamp, event.Timestamp)
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
fixtures := []struct {
|
|
round *domain.Round
|
|
expectedErr string
|
|
}{
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.UndefinedStage,
|
|
Failed: true,
|
|
},
|
|
},
|
|
expectedErr: "not in a valid stage to start tx requests registration",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
},
|
|
},
|
|
expectedErr: "not in a valid stage to start tx requests registration",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
},
|
|
},
|
|
expectedErr: "not in a valid stage to start tx requests registration",
|
|
},
|
|
}
|
|
|
|
for _, f := range fixtures {
|
|
events, err := f.round.StartRegistration()
|
|
require.EqualError(t, err, f.expectedErr)
|
|
require.Empty(t, events)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func testRegisterTxRequests(t *testing.T) {
|
|
t.Run("register_tx_requests", func(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
round := domain.NewRound(dustAmount)
|
|
events, err := round.StartRegistration()
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.RegisterTxRequests(requests)
|
|
require.NoError(t, err)
|
|
require.Len(t, events, 1)
|
|
require.Condition(t, func() bool {
|
|
for _, request := range requests {
|
|
_, ok := round.TxRequests[request.Id]
|
|
if !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
|
|
event, ok := events[0].(domain.TxRequestsRegistered)
|
|
require.True(t, ok)
|
|
require.Equal(t, round.Id, event.Id)
|
|
require.Equal(t, requests, event.TxRequests)
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
fixtures := []struct {
|
|
round *domain.Round
|
|
requests []domain.TxRequest
|
|
expectedErr string
|
|
}{
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{},
|
|
},
|
|
requests: requests,
|
|
expectedErr: "not in a valid stage to register tx requests",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
Failed: true,
|
|
},
|
|
},
|
|
requests: requests,
|
|
expectedErr: "not in a valid stage to register tx requests",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
},
|
|
},
|
|
requests: requests,
|
|
expectedErr: "not in a valid stage to register tx requests",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "id",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
},
|
|
},
|
|
requests: nil,
|
|
expectedErr: "missing tx requests to register",
|
|
},
|
|
}
|
|
|
|
for _, f := range fixtures {
|
|
events, err := f.round.RegisterTxRequests(f.requests)
|
|
require.EqualError(t, err, f.expectedErr)
|
|
require.Empty(t, events)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func testStartFinalization(t *testing.T) {
|
|
t.Run("start_finalization", func(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
round := domain.NewRound(dustAmount)
|
|
events, err := round.StartRegistration()
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.RegisterTxRequests(requests)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.StartFinalization("", connectors, vtxoTree, roundTx)
|
|
require.NoError(t, err)
|
|
require.Len(t, events, 1)
|
|
require.True(t, round.IsStarted())
|
|
require.False(t, round.IsEnded())
|
|
require.False(t, round.IsFailed())
|
|
|
|
event, ok := events[0].(domain.RoundFinalizationStarted)
|
|
require.True(t, ok)
|
|
require.Equal(t, round.Id, event.Id)
|
|
require.Exactly(t, connectors, event.Connectors)
|
|
require.Exactly(t, vtxoTree, event.VtxoTree)
|
|
require.Exactly(t, roundTx, event.RoundTx)
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
requestsById := map[string]domain.TxRequest{}
|
|
for _, p := range requests {
|
|
requestsById[p.Id] = p
|
|
}
|
|
fixtures := []struct {
|
|
round *domain.Round
|
|
connectors []string
|
|
tree tree.VtxoTree
|
|
roundTx string
|
|
expectedErr string
|
|
}{
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
},
|
|
TxRequests: requestsById,
|
|
},
|
|
connectors: connectors,
|
|
tree: vtxoTree,
|
|
roundTx: "",
|
|
expectedErr: "missing unsigned round tx",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
},
|
|
TxRequests: nil,
|
|
},
|
|
connectors: connectors,
|
|
tree: vtxoTree,
|
|
roundTx: roundTx,
|
|
expectedErr: "no tx requests registered",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.UndefinedStage,
|
|
},
|
|
TxRequests: requestsById,
|
|
},
|
|
connectors: connectors,
|
|
tree: vtxoTree,
|
|
roundTx: roundTx,
|
|
expectedErr: "not in a valid stage to start finalization",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
Failed: true,
|
|
},
|
|
TxRequests: requestsById,
|
|
},
|
|
connectors: connectors,
|
|
tree: vtxoTree,
|
|
roundTx: roundTx,
|
|
expectedErr: "not in a valid stage to start finalization",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
},
|
|
TxRequests: requestsById,
|
|
},
|
|
connectors: connectors,
|
|
tree: vtxoTree,
|
|
roundTx: roundTx,
|
|
expectedErr: "not in a valid stage to start finalization",
|
|
},
|
|
}
|
|
|
|
for _, f := range fixtures {
|
|
// TODO fix this
|
|
events, err := f.round.StartFinalization("", f.connectors, f.tree, f.roundTx)
|
|
require.EqualError(t, err, f.expectedErr)
|
|
require.Empty(t, events)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func testEndFinalization(t *testing.T) {
|
|
t.Run("end_registration", func(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
round := domain.NewRound(dustAmount)
|
|
events, err := round.StartRegistration()
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.RegisterTxRequests(requests)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.StartFinalization("", connectors, vtxoTree, roundTx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.EndFinalization(forfeitTxs, txid)
|
|
require.NoError(t, err)
|
|
require.Len(t, events, 1)
|
|
require.False(t, round.IsStarted())
|
|
require.True(t, round.IsEnded())
|
|
require.False(t, round.IsFailed())
|
|
|
|
event, ok := events[0].(domain.RoundFinalized)
|
|
require.True(t, ok)
|
|
require.Equal(t, round.Id, event.Id)
|
|
require.Exactly(t, txid, event.Txid)
|
|
require.Exactly(t, forfeitTxs, event.ForfeitTxs)
|
|
require.Exactly(t, round.EndingTimestamp, event.Timestamp)
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
requestsById := map[string]domain.TxRequest{}
|
|
for _, p := range requests {
|
|
requestsById[p.Id] = p
|
|
}
|
|
fixtures := []struct {
|
|
round *domain.Round
|
|
forfeitTxs []string
|
|
txid string
|
|
expectedErr string
|
|
}{
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
},
|
|
TxRequests: requestsById,
|
|
},
|
|
forfeitTxs: nil,
|
|
txid: txid,
|
|
expectedErr: "missing list of signed forfeit txs",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
},
|
|
},
|
|
forfeitTxs: forfeitTxs,
|
|
txid: "",
|
|
expectedErr: "missing round txid",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
},
|
|
forfeitTxs: forfeitTxs,
|
|
txid: txid,
|
|
expectedErr: "not in a valid stage to end finalization",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.RegistrationStage,
|
|
},
|
|
},
|
|
forfeitTxs: forfeitTxs,
|
|
txid: txid,
|
|
expectedErr: "not in a valid stage to end finalization",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
Failed: true,
|
|
},
|
|
},
|
|
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
|
txid: txid,
|
|
expectedErr: "not in a valid stage to end finalization",
|
|
},
|
|
{
|
|
round: &domain.Round{
|
|
Id: "0",
|
|
Stage: domain.Stage{
|
|
Code: domain.FinalizationStage,
|
|
Ended: true,
|
|
},
|
|
},
|
|
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
|
txid: txid,
|
|
expectedErr: "round already finalized",
|
|
},
|
|
}
|
|
|
|
for _, f := range fixtures {
|
|
events, err := f.round.EndFinalization(f.forfeitTxs, f.txid)
|
|
require.EqualError(t, err, f.expectedErr)
|
|
require.Empty(t, events)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func testFail(t *testing.T) {
|
|
t.Run("fail", func(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
round := domain.NewRound(dustAmount)
|
|
events, err := round.StartRegistration()
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
events, err = round.RegisterTxRequests(requests)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, events)
|
|
|
|
reason := fmt.Errorf("some valid reason")
|
|
events = round.Fail(reason)
|
|
require.Len(t, events, 1)
|
|
require.False(t, round.IsStarted())
|
|
require.False(t, round.IsEnded())
|
|
require.True(t, round.IsFailed())
|
|
|
|
event, ok := events[0].(domain.RoundFailed)
|
|
require.True(t, ok)
|
|
require.Exactly(t, round.Id, event.Id)
|
|
require.Exactly(t, round.EndingTimestamp, event.Timestamp)
|
|
require.EqualError(t, reason, event.Err)
|
|
|
|
events = round.Fail(reason)
|
|
require.Empty(t, events)
|
|
})
|
|
})
|
|
}
|