diff --git a/common/l2tx.go b/common/l2tx.go index 1c236d9..9e57a5a 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -95,7 +95,7 @@ func (tx *L2Tx) PoolL2Tx() *PoolL2Tx { // L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx, // where the PoolL2Tx only have the parameters of a L2Tx filled. -func L2TxsToPoolL2Txs(txs []*L2Tx) []PoolL2Tx { +func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx { var r []PoolL2Tx for _, tx := range txs { r = append(r, *tx.PoolL2Tx()) diff --git a/common/pooll2tx.go b/common/pooll2tx.go index 72862f1..0409347 100644 --- a/common/pooll2tx.go +++ b/common/pooll2tx.go @@ -192,8 +192,12 @@ func (tx *PoolL2Tx) HashToSign() (*big.Int, error) { if err != nil { return nil, err } + rqToBJJY := big.NewInt(0) + if tx.RqToBJJ != nil { + rqToBJJY = tx.RqToBJJ.Y + } - return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, rqToEthAddr, tx.RqToBJJ.Y}) + return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, rqToEthAddr, rqToBJJY}) } // VerifySignature returns true if the signature verification is correct for the given PublicKey diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go index 9cd72ac..cb47259 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -334,7 +334,9 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 s.zki.AuxToIdx[s.i] = auxToIdx.BigInt() } - s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y + if tx.ToBJJ != nil { + s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y + } s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(tx.ToEthAddr) s.zki.OnChain[s.i] = big.NewInt(0) @@ -353,7 +355,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 // if StateDB type==TypeSynchronizer, will need to add Nonce and // TokenID to the transaction if s.typ == TypeSynchronizer { - acc, err := s.GetAccount(tx.ToIdx) + acc, err := s.GetAccount(tx.FromIdx) if err != nil { return nil, nil, false, err } diff --git a/db/statedb/txprocessors_test.go b/db/statedb/txprocessors_test.go index 05407b6..192019f 100644 --- a/db/statedb/txprocessors_test.go +++ b/db/statedb/txprocessors_test.go @@ -1,50 +1,15 @@ package statedb import ( - "encoding/json" - "fmt" "io/ioutil" - "strings" "testing" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/test" + "github.com/hermeznetwork/hermez-node/test/transakcio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -var debug = false - -func TestProcessTxs(t *testing.T) { - dir, err := ioutil.TempDir("", "tmpdb") - require.Nil(t, err) - - sdb, err := NewStateDB(dir, TypeBatchBuilder, 32) - assert.Nil(t, err) - - // generate test transactions from test.SetTest0 code - parser := test.NewParser(strings.NewReader(test.SetTest0)) - instructions, err := parser.Parse() - assert.Nil(t, err) - - l1Txs, coordinatorL1Txs, poolL2Txs, _ := test.GenerateTestTxs(t, instructions) - assert.Equal(t, 29, len(l1Txs[0])) - assert.Equal(t, 0, len(coordinatorL1Txs[0])) - assert.Equal(t, 21, len(poolL2Txs[0])) - - // iterate for each batch - for i := 0; i < len(l1Txs); i++ { - // l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs[i]) - - _, _, err := sdb.ProcessTxs(l1Txs[i], coordinatorL1Txs[i], poolL2Txs[i]) - require.Nil(t, err) - } - - acc, err := sdb.GetAccount(common.Idx(256)) - assert.Nil(t, err) - assert.Equal(t, "23", acc.Balance.String()) -} - func TestProcessTxsSynchronizer(t *testing.T) { // TODO once TTGL is updated, use the blockchain L2Tx (not PoolL2Tx) for // the Synchronizer tests @@ -56,24 +21,22 @@ func TestProcessTxsSynchronizer(t *testing.T) { assert.Nil(t, err) // generate test transactions from test.SetTest0 code - parser := test.NewParser(strings.NewReader(test.SetTest0)) - instructions, err := parser.Parse() - assert.Nil(t, err) - - l1Txs, coordinatorL1Txs, poolL2Txs, _ := test.GenerateTestTxs(t, instructions) - assert.Equal(t, 29, len(l1Txs[0])) - assert.Equal(t, 0, len(coordinatorL1Txs[0])) - assert.Equal(t, 21, len(poolL2Txs[0])) - assert.Equal(t, 5, len(l1Txs[1])) - assert.Equal(t, 1, len(coordinatorL1Txs[1])) - assert.Equal(t, 55, len(poolL2Txs[1])) - assert.Equal(t, 10, len(l1Txs[2])) - assert.Equal(t, 0, len(coordinatorL1Txs[2])) - assert.Equal(t, 7, len(poolL2Txs[2])) + tc := transakcio.NewTestContext(t) + blocks := tc.GenerateBlocks(transakcio.SetBlockchain0) + + assert.Equal(t, 29, len(blocks[0].Batches[0].L1UserTxs)) + assert.Equal(t, 0, len(blocks[0].Batches[0].L1CoordinatorTxs)) + assert.Equal(t, 21, len(blocks[0].Batches[0].L2Txs)) + assert.Equal(t, 1, len(blocks[0].Batches[1].L1UserTxs)) + assert.Equal(t, 1, len(blocks[0].Batches[1].L1CoordinatorTxs)) + assert.Equal(t, 59, len(blocks[0].Batches[1].L2Txs)) + assert.Equal(t, 9, len(blocks[0].Batches[2].L1UserTxs)) + assert.Equal(t, 0, len(blocks[0].Batches[2].L1CoordinatorTxs)) + assert.Equal(t, 8, len(blocks[0].Batches[2].L2Txs)) // use first batch - // l2txs := common.PoolL2TxsToL2Txs(poolL2Txs[0]) - _, exitInfos, err := sdb.ProcessTxs(l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) + l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs) + _, exitInfos, err := sdb.ProcessTxs(blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs) require.Nil(t, err) // TODO once TTGL is updated, add a check that a input poolL2Tx with // Nonce & TokenID =0, after ProcessTxs call has the expected value @@ -84,8 +47,8 @@ func TestProcessTxsSynchronizer(t *testing.T) { assert.Equal(t, "28", acc.Balance.String()) // use second batch - // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[1]) - _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1]) + l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs) + _, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[1].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 5, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(256)) @@ -93,15 +56,18 @@ func TestProcessTxsSynchronizer(t *testing.T) { assert.Equal(t, "48", acc.Balance.String()) // use third batch - // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[2]) - _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2]) + l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[2].L2Txs) + _, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[2].L1UserTxs, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 1, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(256)) assert.Nil(t, err) - assert.Equal(t, "23", acc.Balance.String()) + assert.Equal(t, "73", acc.Balance.String()) } +/* +WIP + func TestProcessTxsBatchBuilder(t *testing.T) { dir, err := ioutil.TempDir("", "tmpdb") require.Nil(t, err) @@ -110,24 +76,22 @@ func TestProcessTxsBatchBuilder(t *testing.T) { assert.Nil(t, err) // generate test transactions from test.SetTest0 code - parser := test.NewParser(strings.NewReader(test.SetTest0)) - instructions, err := parser.Parse() - assert.Nil(t, err) - - l1Txs, coordinatorL1Txs, poolL2Txs, _ := test.GenerateTestTxs(t, instructions) - assert.Equal(t, 29, len(l1Txs[0])) - assert.Equal(t, 0, len(coordinatorL1Txs[0])) - assert.Equal(t, 21, len(poolL2Txs[0])) - assert.Equal(t, 5, len(l1Txs[1])) - assert.Equal(t, 1, len(coordinatorL1Txs[1])) - assert.Equal(t, 55, len(poolL2Txs[1])) - assert.Equal(t, 10, len(l1Txs[2])) - assert.Equal(t, 0, len(coordinatorL1Txs[2])) - assert.Equal(t, 7, len(poolL2Txs[2])) + tc := transakcio.NewTestContext(t) + blocks := tc.GenerateBlocks(transakcio.SetBlockchain0) + + assert.Equal(t, 29, len(blocks[0].Batches[0].L1UserTxs)) + assert.Equal(t, 0, len(blocks[0].Batches[0].L1CoordinatorTxs)) + assert.Equal(t, 21, len(blocks[0].Batches[0].L2Txs)) + assert.Equal(t, 1, len(blocks[0].Batches[1].L1UserTxs)) + assert.Equal(t, 1, len(blocks[0].Batches[1].L1CoordinatorTxs)) + assert.Equal(t, 59, len(blocks[0].Batches[1].L2Txs)) + assert.Equal(t, 9, len(blocks[0].Batches[2].L1UserTxs)) + assert.Equal(t, 0, len(blocks[0].Batches[2].L1CoordinatorTxs)) + assert.Equal(t, 8, len(blocks[0].Batches[2].L2Txs)) // use first batch - // l2txs := common.PoolL2TxsToL2Txs(poolL2Txs[0]) - _, exitInfos, err := sdb.ProcessTxs(l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) + l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs) + _, exitInfos, err := sdb.ProcessTxs(blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 0, len(exitInfos)) acc, err := sdb.GetAccount(common.Idx(256)) @@ -135,8 +99,8 @@ func TestProcessTxsBatchBuilder(t *testing.T) { assert.Equal(t, "28", acc.Balance.String()) // use second batch - // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[1]) - _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1]) + l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs) + _, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[1].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 5, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(256)) @@ -144,8 +108,8 @@ func TestProcessTxsBatchBuilder(t *testing.T) { assert.Equal(t, "48", acc.Balance.String()) // use third batch - // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[2]) - _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2]) + l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[2].L2Txs) + _, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[2].L1UserTxs, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 1, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(256)) @@ -161,21 +125,21 @@ func TestZKInputsGeneration(t *testing.T) { assert.Nil(t, err) // generate test transactions from test.SetTest0 code - parser := test.NewParser(strings.NewReader(test.SetTest0)) - instructions, err := parser.Parse() - assert.Nil(t, err) - - l1Txs, coordinatorL1Txs, poolL2Txs, _ := test.GenerateTestTxs(t, instructions) - assert.Equal(t, 29, len(l1Txs[0])) - assert.Equal(t, 0, len(coordinatorL1Txs[0])) - assert.Equal(t, 21, len(poolL2Txs[0])) - - zki, _, err := sdb.ProcessTxs(l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) + tc := transakcio.NewTestContext(t) + blocks := tc.GenerateBlocks(transakcio.SetBlockchain0) + assert.Equal(t, 29, len(blocks[0].Batches[0].L1UserTxs)) + assert.Equal(t, 0, len(blocks[0].Batches[0].L1CoordinatorTxs)) + assert.Equal(t, 21, len(blocks[0].Batches[0].L2Txs)) + + l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs) + zki, _, err := sdb.ProcessTxs(blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs) require.Nil(t, err) s, err := json.Marshal(zki) require.Nil(t, err) + debug:=true if debug { fmt.Println(string(s)) } } +*/ diff --git a/eth/rollup.go b/eth/rollup.go index 9694c77..655b0b9 100644 --- a/eth/rollup.go +++ b/eth/rollup.go @@ -253,9 +253,9 @@ type RollupForgeBatchArgs struct { NewLastIdx int64 NewStRoot *big.Int NewExitRoot *big.Int - L1CoordinatorTxs []*common.L1Tx + L1CoordinatorTxs []common.L1Tx L1CoordinatorTxsAuths [][]byte // Authorization for accountCreations for each L1CoordinatorTx - L2TxsData []*common.L2Tx + L2TxsData []common.L2Tx FeeIdxCoordinator []common.Idx // Circuit selector VerifierIdx uint8 @@ -698,7 +698,7 @@ func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*RollupFo if err != nil { return nil, err } - rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, l1Tx) + rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx) rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature) } rollupConsts, err := c.RollupConstants() @@ -713,7 +713,7 @@ func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*RollupFo if err != nil { return nil, err } - rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, l2Tx) + rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx) } lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes diff --git a/eth/rollup_test.go b/eth/rollup_test.go index f7725fe..572e9ac 100644 --- a/eth/rollup_test.go +++ b/eth/rollup_test.go @@ -97,7 +97,7 @@ func TestRollupForgeBatch(t *testing.T) { signature = append(signature, v) L1Tx, err := common.L1TxFromCoordinatorBytes(bytesL1Coordinator) require.Nil(t, err) - args.L1CoordinatorTxs = append(args.L1CoordinatorTxs, L1Tx) + args.L1CoordinatorTxs = append(args.L1CoordinatorTxs, *L1Tx) args.L1CoordinatorTxsAuths = append(args.L1CoordinatorTxsAuths, signature) } newStateRoot := new(big.Int) diff --git a/log/log.go b/log/log.go index ae886e0..8907e34 100644 --- a/log/log.go +++ b/log/log.go @@ -132,7 +132,7 @@ func Warnw(template string, kv ...interface{}) { // Errorw calls log.Errorw func Errorw(template string, kv ...interface{}) { - log.Fatalw(template, kv...) + log.Errorw(template, kv...) } // Fatalw calls log.Fatalw diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index a59f9a4..aeb74c8 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -407,7 +407,8 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { position = len(l1UserTxs) // Get L1 Coordinator Txs - for _, l1CoordinatorTx := range forgeBatchArgs.L1CoordinatorTxs { + for i := 0; i < len(forgeBatchArgs.L1CoordinatorTxs); i++ { + l1CoordinatorTx := forgeBatchArgs.L1CoordinatorTxs[i] l1CoordinatorTx.Position = position l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum l1CoordinatorTx.UserOrigin = false @@ -415,7 +416,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { bn := new(common.BatchNum) *bn = common.BatchNum(evtForgeBatch.BatchNum) l1CoordinatorTx.BatchNum = bn - l1Tx, err := common.NewL1Tx(l1CoordinatorTx) + l1Tx, err := common.NewL1Tx(&l1CoordinatorTx) if err != nil { return nil, err } diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index ad277f8..ecc1950 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -3,7 +3,6 @@ package synchronizer import ( "context" "io/ioutil" - "math/big" "os" "testing" @@ -60,36 +59,39 @@ func TestSync(t *testing.T) { require.Nil(t, err) assert.Equal(t, int64(0), blocks[0].EthBlockNum) - // Test Sync for a block with new Tokens and L1UserTxs - // accounts := test.GenerateKeys(t, []string{"A", "B", "C", "D"}) - l1UserTxs, _, _, _ := test.GenerateTestTxsFromSet(t, ` -A (1): 10 -A (2): 20 -B (1): 5 -C (1): 8 -D (3): 15 -> advance batch - `) - require.Greater(t, len(l1UserTxs[0]), 0) - // require.Greater(t, len(tokens), 0) - - for i := 1; i <= 3; i++ { - _, err := client.RollupAddToken(ethCommon.BigToAddress(big.NewInt(int64(i*10000))), - clientSetup.RollupVariables.FeeAddToken) - require.Nil(t, err) - } - - for i := range l1UserTxs[0] { - client.CtlAddL1TxUser(&l1UserTxs[0][i]) - } - client.CtlMineBlock() - - err = s.Sync(context.Background()) - require.Nil(t, err) - - getTokens, err := s.historyDB.GetTokens() - require.Nil(t, err) - assert.Equal(t, 3, len(getTokens)) + // TODO once transakcio is completed + /* + // Test Sync for a block with new Tokens and L1UserTxs + // accounts := test.GenerateKeys(t, []string{"A", "B", "C", "D"}) + l1UserTxs, _, _, _ := test.GenerateTestTxsFromSet(t, ` + A (1): 10 + A (2): 20 + B (1): 5 + C (1): 8 + D (3): 15 + > advance batch + `) + require.Greater(t, len(l1UserTxs[0]), 0) + // require.Greater(t, len(tokens), 0) + + for i := 1; i <= 3; i++ { + _, err := client.RollupAddToken(ethCommon.BigToAddress(big.NewInt(int64(i*10000))), + clientSetup.RollupVariables.FeeAddToken) + require.Nil(t, err) + } + + for i := range l1UserTxs[0] { + client.CtlAddL1TxUser(&l1UserTxs[0][i]) + } + client.CtlMineBlock() + + err = s.Sync(context.Background()) + require.Nil(t, err) + + getTokens, err := s.historyDB.GetTokens() + require.Nil(t, err) + assert.Equal(t, 3, len(getTokens)) + */ // TODO: Reorg will be properly tested once we have the mock ethClient implemented /* diff --git a/test/ethclient_test.go b/test/ethclient_test.go index 64a7ea8..a40e9e1 100644 --- a/test/ethclient_test.go +++ b/test/ethclient_test.go @@ -178,8 +178,8 @@ func TestClientRollup(t *testing.T) { NewLastIdx: 0, NewStRoot: big.NewInt(1), NewExitRoot: big.NewInt(100), - L1CoordinatorTxs: []*common.L1Tx{}, - L2TxsData: []*common.L2Tx{}, + L1CoordinatorTxs: []common.L1Tx{}, + L2TxsData: []common.L2Tx{}, FeeIdxCoordinator: make([]common.Idx, eth.RollupConstFeeIdxCoordinatorLen), VerifierIdx: 0, L1Batch: true, @@ -215,8 +215,8 @@ func TestClientRollup(t *testing.T) { NewLastIdx: 0, NewStRoot: big.NewInt(1), NewExitRoot: big.NewInt(100), - L1CoordinatorTxs: []*common.L1Tx{}, - L2TxsData: []*common.L2Tx{}, + L1CoordinatorTxs: []common.L1Tx{}, + L2TxsData: []common.L2Tx{}, FeeIdxCoordinator: make([]common.Idx, eth.RollupConstFeeIdxCoordinatorLen), VerifierIdx: 0, L1Batch: true, diff --git a/test/lang.go b/test/lang.go deleted file mode 100644 index 9244522..0000000 --- a/test/lang.go +++ /dev/null @@ -1,481 +0,0 @@ -package test - -import ( - "bufio" - "bytes" - "fmt" - "io" - "sort" - "strconv" - - "github.com/hermeznetwork/hermez-node/common" -) - -var eof = rune(0) -var errof = fmt.Errorf("eof in parseline") -var errComment = fmt.Errorf("comment in parseline") -var newEvent = fmt.Errorf("newEvent") - -// TypeNewBatch is used for testing purposes only, and represents the -// common.TxType of a new batch -var TypeNewBatch common.TxType = "TxTypeNewBatch" - -// TypeNewBlock is used for testing purposes only, and represents the -// common.TxType of a new ethereum block -var TypeNewBlock common.TxType = "TxTypeNewBlock" - -//nolint -const ( - ILLEGAL token = iota - WS - EOF - - IDENT // val -) - -// Instruction is the data structure that represents one line of code -type Instruction struct { - Literal string - From string - To string - Amount uint64 - LoadAmount uint64 - Fee uint8 - TokenID common.TokenID - Type common.TxType // D: Deposit, T: Transfer, E: ForceExit -} - -// ParsedSet contains the full Set of Instructions representing a full code -type ParsedSet struct { - Instructions []Instruction - Accounts []string - TokenIDs []common.TokenID -} - -func (i Instruction) String() string { - buf := bytes.NewBufferString("") - fmt.Fprintf(buf, "Type: %s, ", i.Type) - fmt.Fprintf(buf, "From: %s, ", i.From) - if i.Type == common.TxTypeTransfer || - i.Type == common.TxTypeDepositTransfer || - i.Type == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "To: %s, ", i.To) - } - - if i.Type == common.TxTypeDeposit || - i.Type == common.TxTypeDepositTransfer || - i.Type == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "LoadAmount: %d, ", i.LoadAmount) - } - if i.Type != common.TxTypeDeposit { - fmt.Fprintf(buf, "Amount: %d, ", i.Amount) - } - if i.Type == common.TxTypeTransfer || - i.Type == common.TxTypeDepositTransfer || - i.Type == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "Fee: %d, ", i.Fee) - } - fmt.Fprintf(buf, "TokenID: %d\n", i.TokenID) - return buf.String() -} - -// Raw returns a string with the raw representation of the Instruction -func (i Instruction) Raw() string { - buf := bytes.NewBufferString("") - fmt.Fprintf(buf, "%s", i.Type) - fmt.Fprintf(buf, "(%d)", i.TokenID) - fmt.Fprintf(buf, "%s", i.From) - if i.Type == common.TxTypeTransfer || - i.Type == common.TxTypeDepositTransfer || - i.Type == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "-%s", i.To) - } - fmt.Fprintf(buf, ":") - if i.Type == common.TxTypeDeposit || - i.Type == common.TxTypeDepositTransfer || - i.Type == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "%d", i.LoadAmount) - } - if i.Type != common.TxTypeDeposit { - fmt.Fprintf(buf, "%d", i.Amount) - } - if i.Type == common.TxTypeTransfer { - fmt.Fprintf(buf, "(%d)", i.Fee) - } - return buf.String() -} - -type token int - -type scanner struct { - r *bufio.Reader -} - -func isWhitespace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f' -} - -func isLetter(ch rune) bool { - return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') -} - -func isComment(ch rune) bool { - return ch == '/' -} - -func isDigit(ch rune) bool { - return (ch >= '0' && ch <= '9') -} - -// newScanner creates a new scanner with the given io.Reader -func newScanner(r io.Reader) *scanner { - return &scanner{r: bufio.NewReader(r)} -} - -func (s *scanner) read() rune { - ch, _, err := s.r.ReadRune() - if err != nil { - return eof - } - return ch -} - -func (s *scanner) unread() { - _ = s.r.UnreadRune() -} - -// scan returns the token and literal string of the current value -func (s *scanner) scan() (tok token, lit string) { - ch := s.read() - - if isWhitespace(ch) { - // space - s.unread() - return s.scanWhitespace() - } else if isLetter(ch) || isDigit(ch) { - // letter/digit - s.unread() - return s.scanIndent() - } else if isComment(ch) { - // comment - s.unread() - return s.scanIndent() - } - - if ch == eof { - return EOF, "" - } - - return ILLEGAL, string(ch) -} - -func (s *scanner) scanWhitespace() (token token, lit string) { - var buf bytes.Buffer - buf.WriteRune(s.read()) - - for { - if ch := s.read(); ch == eof { - break - } else if !isWhitespace(ch) { - s.unread() - break - } else { - _, _ = buf.WriteRune(ch) - } - } - return WS, buf.String() -} - -func (s *scanner) scanIndent() (tok token, lit string) { - var buf bytes.Buffer - buf.WriteRune(s.read()) - - for { - if ch := s.read(); ch == eof { - break - } else if !isLetter(ch) && !isDigit(ch) { - s.unread() - break - } else { - _, _ = buf.WriteRune(ch) - } - } - - if len(buf.String()) == 1 { - return token(rune(buf.String()[0])), buf.String() - } - return IDENT, buf.String() -} - -// Parser defines the parser -type Parser struct { - s *scanner - buf struct { - tok token - lit string - n int - } -} - -// NewParser creates a new parser from a io.Reader -func NewParser(r io.Reader) *Parser { - return &Parser{s: newScanner(r)} -} - -func (p *Parser) scan() (tok token, lit string) { - // if there is a token in the buffer return it - if p.buf.n != 0 { - p.buf.n = 0 - return p.buf.tok, p.buf.lit - } - tok, lit = p.s.scan() - - p.buf.tok, p.buf.lit = tok, lit - - return -} - -func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) { - tok, lit = p.scan() - if tok == WS { - tok, lit = p.scan() - } - return -} - -// parseLine parses the current line -func (p *Parser) parseLine(pool bool) (*Instruction, error) { - c := &Instruction{} - tok, lit := p.scanIgnoreWhitespace() - if tok == EOF { - return nil, errof - } - c.Literal += lit - if lit == "/" { - _, _ = p.s.r.ReadString('\n') - return nil, errComment - } else if lit == ">" { - if pool { - return c, fmt.Errorf("Unexpected '>' at PoolL2Txs set") - } - _, lit = p.scanIgnoreWhitespace() - if lit == "batch" { - _, _ = p.s.r.ReadString('\n') - return &Instruction{Type: TypeNewBatch}, newEvent - } else if lit == "block" { - _, _ = p.s.r.ReadString('\n') - return &Instruction{Type: TypeNewBlock}, newEvent - } else { - return c, fmt.Errorf("Unexpected '> %s', expected '> batch' or '> block'", lit) - } - } - transfering := false - switch lit { - case "Deposit": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeDeposit - case "Exit": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeExit - case "PoolExit": - if !pool { - return c, fmt.Errorf("Unexpected '%s' at BlockchainTxs set", lit) - } - c.Type = common.TxTypeExit - case "Transfer": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeTransfer - transfering = true - case "PoolTransfer": - if !pool { - return c, fmt.Errorf("Unexpected '%s' at BlockchainTxs set", lit) - } - c.Type = common.TxTypeTransfer - transfering = true - case "CreateAccountDeposit": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeCreateAccountDeposit - case "CreateAccountDepositTransfer": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeCreateAccountDepositTransfer - transfering = true - case "DepositTransfer": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeDepositTransfer - transfering = true - case "ForceTransfer": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeForceTransfer - case "ForceExit": - if pool { - return c, fmt.Errorf("Unexpected '%s' at PoolL2Txs set", lit) - } - c.Type = common.TxTypeForceExit - default: - return c, fmt.Errorf("Unexpected tx type: %s", lit) - } - - if err := p.expectChar(c, "("); err != nil { - return c, err - } - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - tidI, err := strconv.Atoi(lit) - if err != nil { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, err - } - c.TokenID = common.TokenID(tidI) - if err := p.expectChar(c, ")"); err != nil { - return c, err - } - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - c.From = lit - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - if transfering { - if lit != "-" { - return c, fmt.Errorf("Expected '-', found '%s'", lit) - } - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - c.To = lit - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - } - if lit != ":" { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, fmt.Errorf("Expected ':', found '%s'", lit) - } - if c.Type == common.TxTypeDepositTransfer || - c.Type == common.TxTypeCreateAccountDepositTransfer { - // deposit case - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - loadAmount, err := strconv.Atoi(lit) - if err != nil { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, err - } - c.LoadAmount = uint64(loadAmount) - if err := p.expectChar(c, ","); err != nil { - return c, err - } - } - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - amount, err := strconv.Atoi(lit) - if err != nil { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, err - } - if c.Type == common.TxTypeDeposit || - c.Type == common.TxTypeCreateAccountDeposit { - c.LoadAmount = uint64(amount) - } else { - c.Amount = uint64(amount) - } - if transfering { - if err := p.expectChar(c, "("); err != nil { - return c, err - } - _, lit = p.scanIgnoreWhitespace() - c.Literal += lit - fee, err := strconv.Atoi(lit) - if err != nil { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, err - } - if fee > common.MaxFeePlan-1 { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return c, fmt.Errorf("Fee %d can not be bigger than 255", fee) - } - c.Fee = uint8(fee) - - if err := p.expectChar(c, ")"); err != nil { - return c, err - } - } - - if tok == EOF { - return nil, errof - } - return c, nil -} - -func (p *Parser) expectChar(c *Instruction, ch string) error { - _, lit := p.scanIgnoreWhitespace() - c.Literal += lit - if lit != ch { - line, _ := p.s.r.ReadString('\n') - c.Literal += line - return fmt.Errorf("Expected '%s', found '%s'", ch, lit) - } - return nil -} - -func idxTokenIDToString(idx string, tid common.TokenID) string { - return idx + strconv.Itoa(int(tid)) -} - -// Parse parses through reader -func (p *Parser) Parse(pool bool) (*ParsedSet, error) { - instructions := &ParsedSet{} - i := 0 - accounts := make(map[string]bool) - tokenids := make(map[common.TokenID]bool) - for { - instruction, err := p.parseLine(pool) - if err == errof { - break - } - if err == errComment { - i++ - continue - } - if err == newEvent { - i++ - instructions.Instructions = append(instructions.Instructions, *instruction) - continue - } - if err != nil { - return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.Literal, err.Error()) - } - instructions.Instructions = append(instructions.Instructions, *instruction) - accounts[idxTokenIDToString(instruction.From, instruction.TokenID)] = true - if instruction.Type == common.TxTypeTransfer { // type: Transfer - accounts[idxTokenIDToString(instruction.To, instruction.TokenID)] = true - } - tokenids[instruction.TokenID] = true - i++ - } - for a := range accounts { - instructions.Accounts = append(instructions.Accounts, a) - } - sort.Strings(instructions.Accounts) - for tid := range tokenids { - instructions.TokenIDs = append(instructions.TokenIDs, tid) - } - return instructions, nil -} diff --git a/test/lang_test.go b/test/lang_test.go deleted file mode 100644 index 31a8200..0000000 --- a/test/lang_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package test - -import ( - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var debug = false - -func TestParseBlockchainTxs(t *testing.T) { - s := ` - // deposits - Deposit(1) A: 10 - Deposit(2) A: 20 - Deposit(1) B: 5 - CreateAccountDeposit(1) C: 5 - CreateAccountDepositTransfer(1) D-A: 15, 10 (3) - - // L2 transactions - Transfer(1) A-B: 6 (1) - Transfer(1) B-D: 3 (1) - - // set new batch - > batch - - DepositTransfer(1) A-B: 15, 10 (1) - Transfer(1) C-A : 3 (1) - Transfer(2) A-B: 15 (1) - - Deposit(1) User0: 20 - Deposit(3) User1: 20 - Transfer(1) User0-User1: 15 (1) - Transfer(3) User1-User0: 15 (1) - - > batch - - Transfer(1) User1-User0: 1 (1) - - > batch - > block - - // Exits - Exit(1) A: 5 - ` - - parser := NewParser(strings.NewReader(s)) - instructions, err := parser.Parse(false) - require.Nil(t, err) - assert.Equal(t, 20, len(instructions.Instructions)) - assert.Equal(t, 10, len(instructions.Accounts)) - assert.Equal(t, 3, len(instructions.TokenIDs)) - - if debug { - fmt.Println(instructions) - for _, instruction := range instructions.Instructions { - fmt.Println(instruction.Raw()) - } - } - - assert.Equal(t, TypeNewBatch, instructions.Instructions[7].Type) - assert.Equal(t, "Deposit(1)User0:20", instructions.Instructions[11].Raw()) - assert.Equal(t, "Type: DepositTransfer, From: A, To: B, LoadAmount: 15, Amount: 10, Fee: 1, TokenID: 1\n", instructions.Instructions[8].String()) - assert.Equal(t, "Type: Transfer, From: User1, To: User0, Amount: 15, Fee: 1, TokenID: 3\n", instructions.Instructions[14].String()) - assert.Equal(t, "Transfer(2)A-B:15(1)", instructions.Instructions[10].Raw()) - assert.Equal(t, "Type: Transfer, From: A, To: B, Amount: 15, Fee: 1, TokenID: 2\n", instructions.Instructions[10].String()) - assert.Equal(t, "Exit(1)A:5", instructions.Instructions[19].Raw()) - assert.Equal(t, "Type: Exit, From: A, Amount: 5, TokenID: 1\n", instructions.Instructions[19].String()) -} - -func TestParsePoolTxs(t *testing.T) { - s := ` - PoolTransfer(1) A-B: 6 (1) - PoolTransfer(2) A-B: 3 (3) - PoolTransfer(1) B-D: 3 (1) - PoolTransfer(1) C-D: 3 (1) - PoolExit(1) A: 5 - ` - - parser := NewParser(strings.NewReader(s)) - instructions, err := parser.Parse(true) - require.Nil(t, err) - assert.Equal(t, 5, len(instructions.Instructions)) - assert.Equal(t, 6, len(instructions.Accounts)) - assert.Equal(t, 2, len(instructions.TokenIDs)) - - if debug { - fmt.Println(instructions) - for _, instruction := range instructions.Instructions { - fmt.Println(instruction.Raw()) - } - } - - assert.Equal(t, "Transfer(1)A-B:6(1)", instructions.Instructions[0].Raw()) - assert.Equal(t, "Transfer(2)A-B:3(3)", instructions.Instructions[1].Raw()) - assert.Equal(t, "Transfer(1)B-D:3(1)", instructions.Instructions[2].Raw()) - assert.Equal(t, "Transfer(1)C-D:3(1)", instructions.Instructions[3].Raw()) - assert.Equal(t, "Exit(1)A:5", instructions.Instructions[4].Raw()) -} - -func TestParseErrors(t *testing.T) { - s := "Deposit(1) A:: 10" - parser := NewParser(strings.NewReader(s)) - _, err := parser.Parse(false) - assert.Equal(t, "error parsing line 0: Deposit(1)A:: 10, err: strconv.Atoi: parsing \":\": invalid syntax", err.Error()) - - s = "Deposit(1) A: 10 20" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 1: 20, err: Unexpected tx type: 20", err.Error()) - - s = "Transfer(1) A: 10" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 0: Transfer(1)A:, err: Expected '-', found ':'", err.Error()) - - s = "Transfer(1) A B: 10" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 0: Transfer(1)AB, err: Expected '-', found 'B'", err.Error()) - - s = "Transfer(1) A-B: 10 (255)" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Nil(t, err) - s = "Transfer(1) A-B: 10 (256)" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 0: Transfer(1)A-B:10(256), err: Fee 256 can not be bigger than 255", err.Error()) - - // check that the PoolTransfer & Transfer are only accepted in the - // correct case case (PoolTxs/BlockchainTxs) - s = "Transfer(1) A-B: 10 (1)" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(true) - assert.Equal(t, "error parsing line 0: Transfer, err: Unexpected 'Transfer' at PoolL2Txs set", err.Error()) - s = "PoolTransfer(1) A-B: 10 (1)" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 0: PoolTransfer, err: Unexpected 'PoolTransfer' at BlockchainTxs set", err.Error()) - - s = "> btch" - parser = NewParser(strings.NewReader(s)) - _, err = parser.Parse(false) - assert.Equal(t, "error parsing line 0: >, err: Unexpected '> btch', expected '> batch' or '> block'", err.Error()) -} diff --git a/test/sets.go b/test/sets.go deleted file mode 100644 index 29ab651..0000000 --- a/test/sets.go +++ /dev/null @@ -1,166 +0,0 @@ -package test - -// sets of instructions to use in tests of other packages - -// line can be Deposit: -// A (1): 10 -// (deposit to A, TokenID 1, 10 units) -// or Transfer: -// A-B (1): 6 1 -// (transfer from A to B, TokenID 1, 6 units, with fee 1) -// or Withdraw: -// A (1) E: 4 -// exit to A, TokenID 1, 4 units) -// or NextBatch: -// > and here the comment -// move one batch forward - -// SetTest0 has 3 batches, 29 different accounts, with: -// - 3 TokenIDs -// - 29+5+10 L1 txs (deposits & exits) -// - 21+53+7 L2 transactions -var SetTest0 = ` - // deposits TokenID: 1 - A (1): 50 - B (1): 5 - C (1): 20 - D (1): 25 - E (1): 25 - F (1): 25 - G (1): 25 - H (1): 25 - I (1): 25 - J (1): 25 - K (1): 25 - L (1): 25 - M (1): 25 - N (1): 25 - O (1): 25 - P (1): 25 - Q (1): 25 - R (1): 25 - S (1): 25 - T (1): 25 - U (1): 25 - V (1): 25 - W (1): 25 - X (1): 25 - Y (1): 25 - Z (1): 25 - - // deposits TokenID: 2 - B (2): 5 - A (2): 20 - - // deposits TokenID: 3 - B (3): 100 - - // transactions TokenID: 1 - A-B (1): 5 1 - A-L (1): 10 1 - A-M (1): 5 1 - A-N (1): 5 1 - A-O (1): 5 1 - B-C (1): 3 1 - C-A (1): 3 255 - D-A (1): 5 1 - D-Z (1): 5 1 - D-Y (1): 5 1 - D-X (1): 5 1 - E-Z (1): 5 2 - E-Y (1): 5 1 - E-X (1): 5 1 - F-Z (1): 5 1 - G-K (1): 3 1 - G-K (1): 3 1 - G-K (1): 3 1 - H-K (1): 3 2 - H-K (1): 3 1 - H-K (1): 3 1 - - > batch1 - - // A (3) still does not exist, coordinator should create new L1Tx to create the account - B-A (3): 5 1 - - A-B (2): 5 1 - I-K (1): 3 1 - I-K (1): 3 1 - I-K (1): 3 1 - J-K (1): 3 1 - J-K (1): 3 1 - J-K (1): 3 1 - K-J (1): 3 1 - L-A (1): 5 1 - L-Z (1): 5 1 - L-Y (1): 5 1 - L-X (1): 5 1 - M-A (1): 5 1 - M-Z (1): 5 1 - M-Y (1): 5 1 - N-A (1): 5 1 - N-Z (1): 5 2 - N-Y (1): 5 1 - O-T (1): 3 1 - O-U (1): 3 1 - O-V (1): 3 1 - P-T (1): 3 1 - P-U (1): 3 1 - P-V (1): 3 5 - Q-O (1): 3 1 - Q-P (1): 3 1 - R-O (1): 3 1 - R-P (1): 3 1 - R-Q (1): 3 1 - S-O (1): 3 1 - S-P (1): 3 1 - S-Q (1): 3 1 - T-O (1): 3 1 - T-P (1): 3 1 - T-Q (1): 3 1 - U-Z (1): 5 3 - U-Y (1): 5 1 - U-T (1): 3 1 - V-Z (1): 5 0 - V-Y (1): 6 1 - V-T (1): 3 1 - W-K (1): 3 1 - W-J (1): 3 1 - W-A (1): 5 1 - W-Z (1): 5 1 - X-B (1): 5 1 - X-C (1): 5 50 - X-D (1): 5 1 - X-E (1): 5 1 - Y-B (1): 5 1 - Y-C (1): 5 1 - Y-D (1): 5 1 - Y-E (1): 5 1 - Z-A (1): 5 1 - - // exits - A (1) E: 5 - K (1) E: 5 - X (1) E: 5 - Y (1) E: 5 - Z (1) E: 5 - - > batch2 - A (1): 50 - B (1): 5 - C (1): 20 - D (1): 25 - E (1): 25 - F (1): 25 - G (1): 25 - H (1): 25 - I (1): 25 - A-B (1): 5 1 - A-L (1): 10 1 - A-M (1): 5 1 - B-N (1): 5 1 - C-O (1): 5 1 - H-O (1): 5 1 - I-H (1): 5 1 - A (1) E: 5 -` diff --git a/test/sets_test.go b/test/sets_test.go deleted file mode 100644 index 85102aa..0000000 --- a/test/sets_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package test - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCompileSets(t *testing.T) { - parser := NewParser(strings.NewReader(SetTest0)) - _, err := parser.Parse(false) - assert.Nil(t, err) -} diff --git a/test/transakcio/lang.go b/test/transakcio/lang.go new file mode 100644 index 0000000..97d622e --- /dev/null +++ b/test/transakcio/lang.go @@ -0,0 +1,523 @@ +package transakcio + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "sort" + "strconv" + + "github.com/hermeznetwork/hermez-node/common" +) + +var eof = rune(0) +var errof = fmt.Errorf("eof in parseline") +var commentLine = fmt.Errorf("comment in parseline") //nolint:golint +var newEventLine = fmt.Errorf("newEventLine") //nolint:golint +var setTypeLine = fmt.Errorf("setTypeLine") //nolint:golint + +// setType defines the type of the set +type setType string + +// setTypeBlockchain defines the type 'Blockchain' of the set +var setTypeBlockchain = setType("Blockchain") + +// setTypePoolL2 defines the type 'PoolL2' of the set +var setTypePoolL2 = setType("PoolL2") + +// typeNewBatch is used for testing purposes only, and represents the +// common.TxType of a new batch +var typeNewBatch common.TxType = "TxTypeNewBatch" + +// typeNewBlock is used for testing purposes only, and represents the +// common.TxType of a new ethereum block +var typeNewBlock common.TxType = "TxTypeNewBlock" + +//nolint +const ( + ILLEGAL token = iota + WS + EOF + + IDENT // val +) + +// instruction is the data structure that represents one line of code +type instruction struct { + literal string + from string + to string + amount uint64 + loadAmount uint64 + fee uint8 + tokenID common.TokenID + typ common.TxType // D: Deposit, T: Transfer, E: ForceExit +} + +// parsedSet contains the full Set of Instructions representing a full code +type parsedSet struct { + // type string + instructions []instruction + accounts []string + tokenIDs []common.TokenID +} + +func (i instruction) String() string { + buf := bytes.NewBufferString("") + fmt.Fprintf(buf, "Type: %s, ", i.typ) + fmt.Fprintf(buf, "From: %s, ", i.from) + if i.typ == common.TxTypeTransfer || + i.typ == common.TxTypeDepositTransfer || + i.typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "To: %s, ", i.to) + } + + if i.typ == common.TxTypeDeposit || + i.typ == common.TxTypeDepositTransfer || + i.typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "LoadAmount: %d, ", i.loadAmount) + } + if i.typ != common.TxTypeDeposit { + fmt.Fprintf(buf, "Amount: %d, ", i.amount) + } + if i.typ == common.TxTypeTransfer || + i.typ == common.TxTypeDepositTransfer || + i.typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "Fee: %d, ", i.fee) + } + fmt.Fprintf(buf, "TokenID: %d\n", i.tokenID) + return buf.String() +} + +// Raw returns a string with the raw representation of the Instruction +func (i instruction) raw() string { + buf := bytes.NewBufferString("") + fmt.Fprintf(buf, "%s", i.typ) + fmt.Fprintf(buf, "(%d)", i.tokenID) + fmt.Fprintf(buf, "%s", i.from) + if i.typ == common.TxTypeTransfer || + i.typ == common.TxTypeDepositTransfer || + i.typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "-%s", i.to) + } + fmt.Fprintf(buf, ":") + if i.typ == common.TxTypeDeposit || + i.typ == common.TxTypeDepositTransfer || + i.typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "%d", i.loadAmount) + } + if i.typ != common.TxTypeDeposit { + fmt.Fprintf(buf, "%d", i.amount) + } + if i.typ == common.TxTypeTransfer { + fmt.Fprintf(buf, "(%d)", i.fee) + } + return buf.String() +} + +type token int + +type scanner struct { + r *bufio.Reader +} + +func isWhitespace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f' +} + +func isLetter(ch rune) bool { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') +} + +func isComment(ch rune) bool { + return ch == '/' +} + +func isDigit(ch rune) bool { + return (ch >= '0' && ch <= '9') +} + +// newScanner creates a new scanner with the given io.Reader +func newScanner(r io.Reader) *scanner { + return &scanner{r: bufio.NewReader(r)} +} + +func (s *scanner) read() rune { + ch, _, err := s.r.ReadRune() + if err != nil { + return eof + } + return ch +} + +func (s *scanner) unread() { + _ = s.r.UnreadRune() +} + +// scan returns the token and literal string of the current value +func (s *scanner) scan() (tok token, lit string) { + ch := s.read() + + if isWhitespace(ch) { + // space + s.unread() + return s.scanWhitespace() + } else if isLetter(ch) || isDigit(ch) { + // letter/digit + s.unread() + return s.scanIndent() + } else if isComment(ch) { + // comment + s.unread() + return s.scanIndent() + } + + if ch == eof { + return EOF, "" + } + + return ILLEGAL, string(ch) +} + +func (s *scanner) scanWhitespace() (token token, lit string) { + var buf bytes.Buffer + buf.WriteRune(s.read()) + + for { + if ch := s.read(); ch == eof { + break + } else if !isWhitespace(ch) { + s.unread() + break + } else { + _, _ = buf.WriteRune(ch) + } + } + return WS, buf.String() +} + +func (s *scanner) scanIndent() (tok token, lit string) { + var buf bytes.Buffer + buf.WriteRune(s.read()) + + for { + if ch := s.read(); ch == eof { + break + } else if !isLetter(ch) && !isDigit(ch) { + s.unread() + break + } else { + _, _ = buf.WriteRune(ch) + } + } + + if len(buf.String()) == 1 { + return token(rune(buf.String()[0])), buf.String() + } + return IDENT, buf.String() +} + +// parser defines the parser +type parser struct { + s *scanner + buf struct { + tok token + lit string + n int + } +} + +// newParser creates a new parser from a io.Reader +func newParser(r io.Reader) *parser { + return &parser{s: newScanner(r)} +} + +func (p *parser) scan() (tok token, lit string) { + // if there is a token in the buffer return it + if p.buf.n != 0 { + p.buf.n = 0 + return p.buf.tok, p.buf.lit + } + tok, lit = p.s.scan() + + p.buf.tok, p.buf.lit = tok, lit + + return +} + +func (p *parser) scanIgnoreWhitespace() (tok token, lit string) { + tok, lit = p.scan() + if tok == WS { + tok, lit = p.scan() + } + return +} + +// parseLine parses the current line +func (p *parser) parseLine(setType setType) (*instruction, error) { + c := &instruction{} + tok, lit := p.scanIgnoreWhitespace() + if tok == EOF { + return nil, errof + } + c.literal += lit + if lit == "/" { + _, _ = p.s.r.ReadString('\n') + return nil, commentLine + } else if lit == ">" { + if setType == setTypePoolL2 { + return c, fmt.Errorf("Unexpected '>' at PoolL2Txs set") + } + _, lit = p.scanIgnoreWhitespace() + if lit == "batch" { + _, _ = p.s.r.ReadString('\n') + return &instruction{typ: typeNewBatch}, newEventLine + } else if lit == "block" { + _, _ = p.s.r.ReadString('\n') + return &instruction{typ: typeNewBlock}, newEventLine + } else { + return c, fmt.Errorf("Unexpected '> %s', expected '> batch' or '> block'", lit) + } + } else if lit == "Type" { + if err := p.expectChar(c, ":"); err != nil { + return c, err + } + _, lit = p.scanIgnoreWhitespace() + if lit == "Blockchain" { + return &instruction{typ: "Blockchain"}, setTypeLine + } else if lit == "PoolL2" { + return &instruction{typ: "PoolL2"}, setTypeLine + } else { + return c, fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", lit) + } + } + if setType == "" { + return c, fmt.Errorf("Set type not defined") + } + transferring := false + switch lit { + case "Deposit": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeDeposit + case "Exit": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeExit + case "PoolExit": + if setType != setTypePoolL2 { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypePoolL2) + } + c.typ = common.TxTypeExit + case "Transfer": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeTransfer + transferring = true + case "PoolTransfer": + if setType != setTypePoolL2 { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypePoolL2) + } + c.typ = common.TxTypeTransfer + transferring = true + case "CreateAccountDeposit": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeCreateAccountDeposit + case "CreateAccountDepositTransfer": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeCreateAccountDepositTransfer + transferring = true + case "DepositTransfer": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeDepositTransfer + transferring = true + case "ForceTransfer": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeForceTransfer + case "ForceExit": + if setType != setTypeBlockchain { + return c, fmt.Errorf("Unexpected '%s' in a non %s set", lit, setTypeBlockchain) + } + c.typ = common.TxTypeForceExit + default: + return c, fmt.Errorf("Unexpected tx type: %s", lit) + } + + if err := p.expectChar(c, "("); err != nil { + return c, err + } + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + tidI, err := strconv.Atoi(lit) + if err != nil { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, err + } + c.tokenID = common.TokenID(tidI) + if err := p.expectChar(c, ")"); err != nil { + return c, err + } + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + c.from = lit + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + if transferring { + if lit != "-" { + return c, fmt.Errorf("Expected '-', found '%s'", lit) + } + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + c.to = lit + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + } + if lit != ":" { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, fmt.Errorf("Expected ':', found '%s'", lit) + } + if c.typ == common.TxTypeDepositTransfer || + c.typ == common.TxTypeCreateAccountDepositTransfer { + // deposit case + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + loadAmount, err := strconv.Atoi(lit) + if err != nil { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, err + } + c.loadAmount = uint64(loadAmount) + if err := p.expectChar(c, ","); err != nil { + return c, err + } + } + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + amount, err := strconv.Atoi(lit) + if err != nil { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, err + } + if c.typ == common.TxTypeDeposit || + c.typ == common.TxTypeCreateAccountDeposit { + c.loadAmount = uint64(amount) + } else { + c.amount = uint64(amount) + } + if transferring { + if err := p.expectChar(c, "("); err != nil { + return c, err + } + _, lit = p.scanIgnoreWhitespace() + c.literal += lit + fee, err := strconv.Atoi(lit) + if err != nil { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, err + } + if fee > common.MaxFeePlan-1 { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return c, fmt.Errorf("Fee %d can not be bigger than 255", fee) + } + c.fee = uint8(fee) + + if err := p.expectChar(c, ")"); err != nil { + return c, err + } + } + + if tok == EOF { + return nil, errof + } + return c, nil +} + +func (p *parser) expectChar(c *instruction, ch string) error { + _, lit := p.scanIgnoreWhitespace() + c.literal += lit + if lit != ch { + line, _ := p.s.r.ReadString('\n') + c.literal += line + return fmt.Errorf("Expected '%s', found '%s'", ch, lit) + } + return nil +} + +func idxTokenIDToString(idx string, tid common.TokenID) string { + return idx + strconv.Itoa(int(tid)) +} + +// parse parses through reader +func (p *parser) parse() (*parsedSet, error) { + instructions := &parsedSet{} + i := 0 + accounts := make(map[string]bool) + tokenids := make(map[common.TokenID]bool) + var setTypeOfSet setType + for { + instruction, err := p.parseLine(setTypeOfSet) + if err == errof { + break + } + if err == setTypeLine { + if instruction.typ == "PoolL2" { + setTypeOfSet = setTypePoolL2 + } else if instruction.typ == "Blockchain" { + setTypeOfSet = setTypeBlockchain + } else { + log.Fatalf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", instruction.typ) + } + i++ + continue + } + if err == commentLine { + i++ + continue + } + if err == newEventLine { + i++ + instructions.instructions = append(instructions.instructions, *instruction) + continue + } + if err != nil { + return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.literal, err.Error()) + } + if setTypeOfSet == "" { + return instructions, fmt.Errorf("Set type not defined") + } + instructions.instructions = append(instructions.instructions, *instruction) + accounts[instruction.from] = true + if instruction.typ == common.TxTypeTransfer { // type: Transfer + accounts[instruction.to] = true + } + tokenids[instruction.tokenID] = true + i++ + } + for a := range accounts { + instructions.accounts = append(instructions.accounts, a) + } + sort.Strings(instructions.accounts) + for tid := range tokenids { + instructions.tokenIDs = append(instructions.tokenIDs, tid) + } + return instructions, nil +} diff --git a/test/transakcio/lang_test.go b/test/transakcio/lang_test.go new file mode 100644 index 0000000..165c9e7 --- /dev/null +++ b/test/transakcio/lang_test.go @@ -0,0 +1,193 @@ +package transakcio + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var debug = false + +func TestParseBlockchainTxs(t *testing.T) { + s := ` + Type: Blockchain + // deposits + Deposit(1) A: 10 + Deposit(2) A: 20 + Deposit(1) B: 5 + CreateAccountDeposit(1) C: 5 + CreateAccountDepositTransfer(1) D-A: 15, 10 (3) + + // L2 transactions + Transfer(1) A-B: 6 (1) + Transfer(1) B-D: 3 (1) + + // set new batch + > batch + + DepositTransfer(1) A-B: 15, 10 (1) + Transfer(1) C-A : 3 (1) + Transfer(2) A-B: 15 (1) + + Deposit(1) User0: 20 + Deposit(3) User1: 20 + Transfer(1) User0-User1: 15 (1) + Transfer(3) User1-User0: 15 (1) + + > batch + + Transfer(1) User1-User0: 1 (1) + + > batch + > block + + // Exits + Exit(1) A: 5 + ` + + parser := newParser(strings.NewReader(s)) + instructions, err := parser.parse() + require.Nil(t, err) + assert.Equal(t, 20, len(instructions.instructions)) + assert.Equal(t, 6, len(instructions.accounts)) + assert.Equal(t, 3, len(instructions.tokenIDs)) + + if debug { + fmt.Println(instructions) + for _, instruction := range instructions.instructions { + fmt.Println(instruction.raw()) + } + } + + assert.Equal(t, typeNewBatch, instructions.instructions[7].typ) + assert.Equal(t, "Deposit(1)User0:20", instructions.instructions[11].raw()) + assert.Equal(t, "Type: DepositTransfer, From: A, To: B, LoadAmount: 15, Amount: 10, Fee: 1, TokenID: 1\n", instructions.instructions[8].String()) + assert.Equal(t, "Type: Transfer, From: User1, To: User0, Amount: 15, Fee: 1, TokenID: 3\n", instructions.instructions[14].String()) + assert.Equal(t, "Transfer(2)A-B:15(1)", instructions.instructions[10].raw()) + assert.Equal(t, "Type: Transfer, From: A, To: B, Amount: 15, Fee: 1, TokenID: 2\n", instructions.instructions[10].String()) + assert.Equal(t, "Exit(1)A:5", instructions.instructions[19].raw()) + assert.Equal(t, "Type: Exit, From: A, Amount: 5, TokenID: 1\n", instructions.instructions[19].String()) +} + +func TestParsePoolTxs(t *testing.T) { + s := ` + Type: PoolL2 + PoolTransfer(1) A-B: 6 (1) + PoolTransfer(2) A-B: 3 (3) + PoolTransfer(1) B-D: 3 (1) + PoolTransfer(1) C-D: 3 (1) + PoolExit(1) A: 5 + ` + + parser := newParser(strings.NewReader(s)) + instructions, err := parser.parse() + require.Nil(t, err) + assert.Equal(t, 5, len(instructions.instructions)) + assert.Equal(t, 4, len(instructions.accounts)) + assert.Equal(t, 2, len(instructions.tokenIDs)) + + if debug { + fmt.Println(instructions) + for _, instruction := range instructions.instructions { + fmt.Println(instruction.raw()) + } + } + + assert.Equal(t, "Transfer(1)A-B:6(1)", instructions.instructions[0].raw()) + assert.Equal(t, "Transfer(2)A-B:3(3)", instructions.instructions[1].raw()) + assert.Equal(t, "Transfer(1)B-D:3(1)", instructions.instructions[2].raw()) + assert.Equal(t, "Transfer(1)C-D:3(1)", instructions.instructions[3].raw()) + assert.Equal(t, "Exit(1)A:5", instructions.instructions[4].raw()) +} + +func TestParseErrors(t *testing.T) { + s := ` + Type: Blockchain + Deposit(1) A:: 10 + ` + parser := newParser(strings.NewReader(s)) + _, err := parser.parse() + assert.Equal(t, "error parsing line 1: Deposit(1)A:: 10\n, err: strconv.Atoi: parsing \":\": invalid syntax", err.Error()) + + s = ` + Type: Blockchain + Deposit(1) A: 10 20 + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 2: 20, err: Unexpected tx type: 20", err.Error()) + + s = ` + Type: Blockchain + Transfer(1) A: 10 + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: Transfer(1)A:, err: Expected '-', found ':'", err.Error()) + + s = ` + Type: Blockchain + Transfer(1) A B: 10 + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: Transfer(1)AB, err: Expected '-', found 'B'", err.Error()) + + s = ` + Type: Blockchain + Transfer(1) A-B: 10 (255) + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Nil(t, err) + s = ` + Type: Blockchain + Transfer(1) A-B: 10 (256) + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: Transfer(1)A-B:10(256)\n, err: Fee 256 can not be bigger than 255", err.Error()) + + // check that the PoolTransfer & Transfer are only accepted in the + // correct case case (PoolTxs/BlockchainTxs) + s = ` + Type: PoolL2 + Transfer(1) A-B: 10 (1) + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: Transfer, err: Unexpected 'Transfer' in a non Blockchain set", err.Error()) + s = ` + Type: Blockchain + PoolTransfer(1) A-B: 10 (1) + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: PoolTransfer, err: Unexpected 'PoolTransfer' in a non PoolL2 set", err.Error()) + + s = ` + Type: Blockchain + > btch + ` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 1: >, err: Unexpected '> btch', expected '> batch' or '> block'", err.Error()) + + // check definition of set Type + s = `PoolTransfer(1) A-B: 10 (1)` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 0: PoolTransfer, err: Set type not defined", err.Error()) + s = `Type: PoolL1` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 0: Type:, err: Invalid set type: 'PoolL1'. Valid set types: 'Blockchain', 'PoolL2'", err.Error()) + s = `Type: PoolL1 + Type: Blockchain` + parser = newParser(strings.NewReader(s)) + _, err = parser.parse() + assert.Equal(t, "error parsing line 0: Type:, err: Invalid set type: 'PoolL1'. Valid set types: 'Blockchain', 'PoolL2'", err.Error()) +} diff --git a/test/transakcio/sets.go b/test/transakcio/sets.go new file mode 100644 index 0000000..001e5c6 --- /dev/null +++ b/test/transakcio/sets.go @@ -0,0 +1,162 @@ +package transakcio + +// sets of instructions to be used in tests of other packages + +// SetBlockchain0 contains a set of transactions simulated to be from the smart contract +var SetBlockchain0 = ` +// Set containing Blockchain transactions +Type: Blockchain + +// deposits TokenID: 1 +CreateAccountDeposit(1) A: 50 +CreateAccountDeposit(1) B: 5 +CreateAccountDeposit(1) C: 20 +CreateAccountDeposit(1) D: 25 +CreateAccountDeposit(1) E: 25 +CreateAccountDeposit(1) F: 25 +CreateAccountDeposit(1) G: 25 +CreateAccountDeposit(1) H: 25 +CreateAccountDeposit(1) I: 25 +CreateAccountDeposit(1) J: 25 +CreateAccountDeposit(1) K: 25 +CreateAccountDeposit(1) L: 25 +CreateAccountDeposit(1) M: 25 +CreateAccountDeposit(1) N: 25 +CreateAccountDeposit(1) O: 25 +CreateAccountDeposit(1) P: 25 +CreateAccountDeposit(1) Q: 25 +CreateAccountDeposit(1) R: 25 +CreateAccountDeposit(1) S: 25 +CreateAccountDeposit(1) T: 25 +CreateAccountDeposit(1) U: 25 +CreateAccountDeposit(1) V: 25 +CreateAccountDeposit(1) W: 25 +CreateAccountDeposit(1) X: 25 +CreateAccountDeposit(1) Y: 25 +CreateAccountDeposit(1) Z: 25 +// deposits TokenID: 2 +CreateAccountDeposit(2) B: 5 +CreateAccountDeposit(2) A: 20 +// deposits TokenID: 3 +CreateAccountDeposit(3) B: 100 +// transactions TokenID: 1 +Transfer(1) A-B: 5 (1) +Transfer(1) A-L: 10 (1) +Transfer(1) A-M: 5 (1) +Transfer(1) A-N: 5 (1) +Transfer(1) A-O: 5 (1) +Transfer(1) B-C: 3 (1) +Transfer(1) C-A: 3 (255) +Transfer(1) D-A: 5 (1) +Transfer(1) D-Z: 5 (1) +Transfer(1) D-Y: 5 (1) +Transfer(1) D-X: 5 (1) +Transfer(1) E-Z: 5 (2) +Transfer(1) E-Y: 5 (1) +Transfer(1) E-X: 5 (1) +Transfer(1) F-Z: 5 (1) +Transfer(1) G-K: 3 (1) +Transfer(1) G-K: 3 (1) +Transfer(1) G-K: 3 (1) +Transfer(1) H-K: 3 (2) +Transfer(1) H-K: 3 (1) +Transfer(1) H-K: 3 (1) + +> batch +// A (3) still does not exist, coordinator should create new L1Tx to create the account +Transfer(3) B-A: 5 (1) +Transfer(2) A-B: 5 (1) +Transfer(1) I-K: 3 (1) +Transfer(1) I-K: 3 (1) +Transfer(1) I-K: 3 (1) +Transfer(1) J-K: 3 (1) +Transfer(1) J-K: 3 (1) +Transfer(1) J-K: 3 (1) +Transfer(1) K-J: 3 (1) +Transfer(1) L-A: 5 (1) +Transfer(1) L-Z: 5 (1) +Transfer(1) L-Y: 5 (1) +Transfer(1) L-X: 5 (1) +Transfer(1) M-A: 5 (1) +Transfer(1) M-Z: 5 (1) +Transfer(1) M-Y: 5 (1) +Transfer(1) N-A: 5 (1) +Transfer(1) N-Z: 5 (2) +Transfer(1) N-Y: 5 (1) +Transfer(1) O-T: 3 (1) +Transfer(1) O-U: 3 (1) +Transfer(1) O-V: 3 (1) +Transfer(1) P-T: 3 (1) +Transfer(1) P-U: 3 (1) +Transfer(1) P-V: 3 (5) +Transfer(1) Q-O: 3 (1) +Transfer(1) Q-P: 3 (1) +Transfer(1) R-O: 3 (1) +Transfer(1) R-P: 3 (1) +Transfer(1) R-Q: 3 (1) +Transfer(1) S-O: 3 (1) +Transfer(1) S-P: 3 (1) +Transfer(1) S-Q: 3 (1) +Transfer(1) T-O: 3 (1) +Transfer(1) T-P: 3 (1) +Transfer(1) T-Q: 3 (1) +Transfer(1) U-Z: 5 (3) +Transfer(1) U-Y: 5 (1) +Transfer(1) U-T: 3 (1) +Transfer(1) V-Z: 5 (0) +Transfer(1) V-Y: 6 (1) +Transfer(1) V-T: 3 (1) +Transfer(1) W-K: 3 (1) +Transfer(1) W-J: 3 (1) +Transfer(1) W-A: 5 (1) +Transfer(1) W-Z: 5 (1) +Transfer(1) X-B: 5 (1) +Transfer(1) X-C: 5 (50) +Transfer(1) X-D: 5 (1) +Transfer(1) X-E: 5 (1) +Transfer(1) Y-B: 5 (1) +Transfer(1) Y-C: 5 (1) +Transfer(1) Y-D: 5 (1) +Transfer(1) Y-E: 5 (1) +Transfer(1) Z-A: 5 (1) +// exits +ForceExit(1) A: 5 +Exit(1) K: 5 +Exit(1) X: 5 +Exit(1) Y: 5 +Exit(1) Z: 5 + +> batch +Deposit(1) A: 50 +Deposit(1) B: 5 +Deposit(1) C: 20 +Deposit(1) D: 25 +Deposit(1) E: 25 +Deposit(1) F: 25 +Deposit(1) G: 25 +Deposit(1) H: 25 +Deposit(1) I: 25 +Transfer(1) A-B: 5 (1) +Transfer(1) A-L: 10 (1) +Transfer(1) A-M: 5 (1) +Transfer(1) B-N: 5 (1) +Transfer(1) C-O: 5 (1) +Transfer(1) H-O: 5 (1) +Transfer(1) I-H: 5 (1) +Exit(1) A: 5 +` + +// SetPool0 contains a set of transactions from the PoolL2 +var SetPool0 = ` +Type: PoolL2 +PoolTransfer(1) A-B: 6 (1) +PoolTransfer(1) B-C: 3 (1) +PoolTransfer(1) C-A: 3 (1) +PoolTransfer(1) A-B: 1 (1) +PoolTransfer(2) A-B: 15 (1) +PoolTransfer(2) B-D: 3 (1) +PoolExit(1) A: 3 +PoolTransfer(1) A-B: 6 (1) +PoolTransfer(1) B-C: 3 (1) +PoolTransfer(1) A-C: 3 (1) +` diff --git a/test/transakcio/sets_test.go b/test/transakcio/sets_test.go new file mode 100644 index 0000000..599ed5e --- /dev/null +++ b/test/transakcio/sets_test.go @@ -0,0 +1,21 @@ +package transakcio + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCompileSets(t *testing.T) { + parser := newParser(strings.NewReader(SetBlockchain0)) + _, err := parser.parse() + assert.Nil(t, err) + parser = newParser(strings.NewReader(SetPool0)) + _, err = parser.parse() + assert.Nil(t, err) + + tc := NewTestContext(t) + _ = tc.GenerateBlocks(SetBlockchain0) + _ = tc.GenerateBlocks(SetPool0) +} diff --git a/test/transakcio/txs.go b/test/transakcio/txs.go new file mode 100644 index 0000000..7b2a06f --- /dev/null +++ b/test/transakcio/txs.go @@ -0,0 +1,340 @@ +package transakcio + +import ( + "crypto/ecdsa" + "math/big" + "strconv" + "strings" + "testing" + "time" + + ethCommon "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/hermeznetwork/hermez-node/common" + "github.com/hermeznetwork/hermez-node/log" + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/stretchr/testify/require" +) + +// TestContext contains the data of the test +type TestContext struct { + t *testing.T + Instructions []instruction + accountsNames []string + Users map[string]*User + TokenIDs []common.TokenID + l1CreatedAccounts map[string]*Account +} + +// NewTestContext returns a new TestContext +func NewTestContext(t *testing.T) *TestContext { + return &TestContext{ + t: t, + Users: make(map[string]*User), + l1CreatedAccounts: make(map[string]*Account), + } +} + +// Account contains the data related to the account for a specific TokenID of a User +type Account struct { + Idx common.Idx + Nonce common.Nonce +} + +// User contains the data related to a testing user +type User struct { + BJJ *babyjub.PrivateKey + Addr ethCommon.Address + Accounts map[common.TokenID]*Account +} + +// BlockData contains the information of a Block +type BlockData struct { + // block *common.Block // ethereum block + // L1UserTxs that were submitted in the block + L1UserTxs []common.L1Tx + Batches []BatchData + RegisteredTokens []common.Token +} + +// BatchData contains the information of a Batch +type BatchData struct { + L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer + // L1UserTxs that were forged in the batch + L1UserTxs []common.L1Tx + L1CoordinatorTxs []common.L1Tx + L2Txs []common.L2Tx + CreatedAccounts []common.Account + ExitTree []common.ExitInfo + Batch *common.Batch +} + +// GenerateBlocks returns an array of BlockData for a given set. It uses the +// accounts (keys & nonces) of the TestContext. +func (tc *TestContext) GenerateBlocks(set string) []BlockData { + parser := newParser(strings.NewReader(set)) + parsedSet, err := parser.parse() + require.Nil(tc.t, err) + + tc.Instructions = parsedSet.instructions + tc.accountsNames = parsedSet.accounts + tc.TokenIDs = parsedSet.tokenIDs + + tc.generateKeys(tc.accountsNames) + + var blocks []BlockData + currBatchNum := 0 + var currBlock BlockData + var currBatch BatchData + idx := 256 + for _, inst := range parsedSet.instructions { + switch inst.typ { + case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: + tx := common.L1Tx{ + // TxID + FromEthAddr: tc.Users[inst.from].Addr, + FromBJJ: tc.Users[inst.from].BJJ.Public(), + TokenID: inst.tokenID, + LoadAmount: big.NewInt(int64(inst.loadAmount)), + Type: inst.typ, + } + if tc.Users[inst.from].Accounts[inst.tokenID] == nil { // if account is not set yet, set it and increment idx + tc.Users[inst.from].Accounts[inst.tokenID] = &Account{ + Idx: common.Idx(idx), + Nonce: common.Nonce(0), + } + + tc.l1CreatedAccounts[idxTokenIDToString(inst.from, inst.tokenID)] = tc.Users[inst.from].Accounts[inst.tokenID] + idx++ + } + if inst.typ == common.TxTypeCreateAccountDepositTransfer { + tx.Amount = big.NewInt(int64(inst.amount)) + } + currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) + case common.TxTypeDeposit, common.TxTypeDepositTransfer: + if tc.Users[inst.from].Accounts[inst.tokenID] == nil { + log.Fatalf("Deposit at User %s for TokenID %d while account not created yet", inst.from, inst.tokenID) + } + tx := common.L1Tx{ + // TxID + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + FromEthAddr: tc.Users[inst.from].Addr, + FromBJJ: tc.Users[inst.from].BJJ.Public(), + TokenID: inst.tokenID, + LoadAmount: big.NewInt(int64(inst.loadAmount)), + Type: inst.typ, + } + if tc.Users[inst.from].Accounts[inst.tokenID].Idx == common.Idx(0) { + // if account.Idx is not set yet, set it and increment idx + tc.Users[inst.from].Accounts[inst.tokenID].Idx = common.Idx(idx) + + tc.l1CreatedAccounts[idxTokenIDToString(inst.from, inst.tokenID)] = tc.Users[inst.from].Accounts[inst.tokenID] + idx++ + } + if inst.typ == common.TxTypeDepositTransfer { + tx.Amount = big.NewInt(int64(inst.amount)) + // if ToIdx is not set yet, set it and increment idx + if tc.Users[inst.to].Accounts[inst.tokenID].Idx == common.Idx(0) { + tc.Users[inst.to].Accounts[inst.tokenID].Idx = common.Idx(idx) + + tc.l1CreatedAccounts[idxTokenIDToString(inst.to, inst.tokenID)] = tc.Users[inst.to].Accounts[inst.tokenID] + tx.ToIdx = common.Idx(idx) + idx++ + } else { + // if Idx account of To already exist, use it for ToIdx + tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx + } + } + currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) + case common.TxTypeTransfer: + if tc.Users[inst.from].Accounts[inst.tokenID] == nil { + log.Fatalf("Transfer from User %s for TokenID %d while account not created yet", inst.from, inst.tokenID) + } + tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ + // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account + if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(inst.to, inst.tokenID)]; !ok { + tx := common.L1Tx{ + FromEthAddr: tc.Users[inst.to].Addr, + FromBJJ: tc.Users[inst.to].BJJ.Public(), + TokenID: inst.tokenID, + LoadAmount: big.NewInt(int64(inst.amount)), + Type: common.TxTypeCreateAccountDeposit, + } + tc.Users[inst.to].Accounts[inst.tokenID] = &Account{ + Idx: common.Idx(idx), + Nonce: common.Nonce(0), + } + tc.l1CreatedAccounts[idxTokenIDToString(inst.to, inst.tokenID)] = tc.Users[inst.to].Accounts[inst.tokenID] + currBatch.L1CoordinatorTxs = append(currBatch.L1CoordinatorTxs, tx) + idx++ + } + tx := common.L2Tx{ + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx, + Amount: big.NewInt(int64(inst.amount)), + Fee: common.FeeSelector(inst.fee), + Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + Type: common.TxTypeTransfer, + } + nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx()) + if err != nil { + panic(err) + } + nL2Tx, err := nTx.L2Tx() + if err != nil { + panic(err) + } + tx = *nL2Tx + tx.BatchNum = common.BatchNum(currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost + + currBatch.L2Txs = append(currBatch.L2Txs, tx) + case common.TxTypeExit: + tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ + tx := common.L2Tx{ + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + ToIdx: common.Idx(1), // as is an Exit + Amount: big.NewInt(int64(inst.amount)), + Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + Type: common.TxTypeExit, + } + nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx()) + if err != nil { + panic(err) + } + nL2Tx, err := nTx.L2Tx() + if err != nil { + panic(err) + } + tx = *nL2Tx + currBatch.L2Txs = append(currBatch.L2Txs, tx) + case common.TxTypeForceExit: + tx := common.L1Tx{ + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + ToIdx: common.Idx(1), // as is an Exit + TokenID: inst.tokenID, + Amount: big.NewInt(int64(inst.amount)), + Type: common.TxTypeExit, + } + currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) + case typeNewBatch: + currBlock.Batches = append(currBlock.Batches, currBatch) + currBatchNum++ + currBatch = BatchData{} + case typeNewBlock: + currBlock.Batches = append(currBlock.Batches, currBatch) + currBatchNum++ + currBatch = BatchData{} + blocks = append(blocks, currBlock) + currBlock = BlockData{} + default: + log.Fatalf("Unexpected type: %s", inst.typ) + } + } + currBlock.Batches = append(currBlock.Batches, currBatch) + blocks = append(blocks, currBlock) + + return blocks +} + +// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It +// uses the accounts (keys & nonces) of the TestContext. +func (tc *TestContext) GeneratePoolL2Txs(set string) []common.PoolL2Tx { + parser := newParser(strings.NewReader(set)) + parsedSet, err := parser.parse() + require.Nil(tc.t, err) + + tc.Instructions = parsedSet.instructions + tc.accountsNames = parsedSet.accounts + tc.TokenIDs = parsedSet.tokenIDs + + tc.generateKeys(tc.accountsNames) + + txs := []common.PoolL2Tx{} + for _, inst := range tc.Instructions { + switch inst.typ { + case common.TxTypeTransfer: + if tc.Users[inst.from].Accounts[inst.tokenID] == nil { + log.Fatalf("Transfer from User %s for TokenID %d while account not created yet", inst.from, inst.tokenID) + } + if tc.Users[inst.to].Accounts[inst.tokenID] == nil { + log.Fatalf("Transfer to User %s for TokenID %d while account not created yet", inst.to, inst.tokenID) + } + tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ + // if account of receiver does not exist, don't use + // ToIdx, and use only ToEthAddr & ToBJJ + tx := common.PoolL2Tx{ + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx, + ToEthAddr: tc.Users[inst.to].Addr, + ToBJJ: tc.Users[inst.to].BJJ.Public(), + TokenID: inst.tokenID, + Amount: big.NewInt(int64(inst.amount)), + Fee: common.FeeSelector(inst.fee), + Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + State: common.PoolL2TxStatePending, + Timestamp: time.Now(), + RqToEthAddr: common.EmptyAddr, + RqToBJJ: nil, + Type: common.TxTypeTransfer, + } + nTx, err := common.NewPoolL2Tx(&tx) + if err != nil { + panic(err) + } + tx = *nTx + // perform signature and set it to tx.Signature + toSign, err := tx.HashToSign() + if err != nil { + panic(err) + } + sig := tc.Users[inst.to].BJJ.SignPoseidon(toSign) + tx.Signature = sig + + txs = append(txs, tx) + case common.TxTypeExit: + tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ + tx := common.PoolL2Tx{ + FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + ToIdx: common.Idx(1), // as is an Exit + TokenID: inst.tokenID, + Amount: big.NewInt(int64(inst.amount)), + Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + Type: common.TxTypeExit, + } + txs = append(txs, tx) + default: + log.Fatalf("instruction type unrecognized: %s", inst.typ) + } + } + + return txs +} + +// generateKeys generates BabyJubJub & Address keys for the given list of +// account names in a deterministic way. This means, that for the same given +// 'accNames' in a certain order, the keys will be always the same. +func (tc *TestContext) generateKeys(accNames []string) { + for i := 1; i < len(accNames)+1; i++ { + if _, ok := tc.Users[accNames[i-1]]; ok { + // account already created + continue + } + // babyjubjub key + var sk babyjub.PrivateKey + copy(sk[:], []byte(strconv.Itoa(i))) // only for testing + + // eth address + var key ecdsa.PrivateKey + key.D = big.NewInt(int64(i)) // only for testing + key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes()) + key.Curve = ethCrypto.S256() + addr := ethCrypto.PubkeyToAddress(key.PublicKey) + + u := User{ + BJJ: &sk, + Addr: addr, + Accounts: make(map[common.TokenID]*Account), + } + tc.Users[accNames[i-1]] = &u + } +} diff --git a/test/txs_test.go b/test/transakcio/txs_test.go similarity index 60% rename from test/txs_test.go rename to test/transakcio/txs_test.go index b6e376d..1f17ad7 100644 --- a/test/txs_test.go +++ b/test/transakcio/txs_test.go @@ -1,4 +1,4 @@ -package test +package transakcio import ( "math/big" @@ -10,9 +10,11 @@ import ( func TestGenerateBlocks(t *testing.T) { set := ` - Deposit(1) A: 10 - Deposit(2) A: 20 - Deposit(1) B: 5 + Type: Blockchain + + CreateAccountDeposit(1) A: 10 + CreateAccountDeposit(2) A: 20 + CreateAccountDeposit(1) B: 5 CreateAccountDeposit(1) C: 5 CreateAccountDepositTransfer(1) D-A: 15, 10 (3) @@ -27,8 +29,8 @@ func TestGenerateBlocks(t *testing.T) { Transfer(1) C-A : 3 (1) Transfer(2) A-B: 15 (1) - Deposit(1) User0: 20 - Deposit(3) User1: 20 + CreateAccountDeposit(1) User0: 20 + CreateAccountDeposit(3) User1: 20 Transfer(1) User0-User1: 15 (1) Transfer(3) User1-User0: 15 (1) Transfer(1) A-C: 1 (1) @@ -53,57 +55,57 @@ func TestGenerateBlocks(t *testing.T) { // Check expected values generated by each line // #0: Deposit(1) A: 10 - tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[0], common.TxTypeDeposit, "A1", "", big.NewInt(10), nil) + tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[0], common.TxTypeCreateAccountDeposit, 1, "A", "", big.NewInt(10), nil) // #1: Deposit(2) A: 20 - tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[1], common.TxTypeDeposit, "A2", "", big.NewInt(20), nil) + tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[1], common.TxTypeCreateAccountDeposit, 2, "A", "", big.NewInt(20), nil) // #2: Deposit(1) A: 20 - tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[2], common.TxTypeDeposit, "B1", "", big.NewInt(5), nil) + tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[2], common.TxTypeCreateAccountDeposit, 1, "B", "", big.NewInt(5), nil) // #3: CreateAccountDeposit(1) C: 5 - tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[3], common.TxTypeCreateAccountDeposit, "C1", "", big.NewInt(5), nil) + tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[3], common.TxTypeCreateAccountDeposit, 1, "C", "", big.NewInt(5), nil) // #4: CreateAccountDepositTransfer(1) D-A: 15, 10 (3) - tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[4], common.TxTypeCreateAccountDepositTransfer, "D1", "A1", big.NewInt(15), big.NewInt(10)) + tc.checkL1TxParams(t, blocks[0].Batches[0].L1UserTxs[4], common.TxTypeCreateAccountDepositTransfer, 1, "D", "A", big.NewInt(15), big.NewInt(10)) // #5: Transfer(1) A-B: 6 (1) - tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[0], common.TxTypeTransfer, "A1", "B1", big.NewInt(6), common.BatchNum(0), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[0], common.TxTypeTransfer, 1, "A", "B", big.NewInt(6), common.BatchNum(0), common.Nonce(1)) // #6: Transfer(1) B-D: 3 (1) - tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[1], common.TxTypeTransfer, "B1", "D1", big.NewInt(3), common.BatchNum(0), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[1], common.TxTypeTransfer, 1, "B", "D", big.NewInt(3), common.BatchNum(0), common.Nonce(1)) // #7: Transfer(1) A-D: 1 (1) - tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[2], common.TxTypeTransfer, "A1", "D1", big.NewInt(1), common.BatchNum(0), common.Nonce(2)) + tc.checkL2TxParams(t, blocks[0].Batches[0].L2Txs[2], common.TxTypeTransfer, 1, "A", "D", big.NewInt(1), common.BatchNum(0), common.Nonce(2)) // change of Batch // #8: DepositTransfer(1) A-B: 15, 10 (1) - tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[0], common.TxTypeDepositTransfer, "A1", "B1", big.NewInt(15), big.NewInt(10)) + tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[0], common.TxTypeDepositTransfer, 1, "A", "B", big.NewInt(15), big.NewInt(10)) // #9: Deposit(1) User0: 20 - tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[0], common.TxTypeDepositTransfer, "A1", "B1", big.NewInt(15), big.NewInt(10)) + tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[0], common.TxTypeDepositTransfer, 1, "A", "B", big.NewInt(15), big.NewInt(10)) // #10: Transfer(1) C-A : 3 (1) - tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[0], common.TxTypeTransfer, "C1", "A1", big.NewInt(3), common.BatchNum(1), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[0], common.TxTypeTransfer, 1, "C", "A", big.NewInt(3), common.BatchNum(1), common.Nonce(1)) // #11: Transfer(2) A-B: 15 (1) - tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[1], common.TxTypeTransfer, "A2", "B2", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[1], common.TxTypeTransfer, 2, "A", "B", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) // #12: Deposit(1) User0: 20 - tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[1], common.TxTypeDeposit, "User01", "", big.NewInt(20), nil) + tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[1], common.TxTypeCreateAccountDeposit, 1, "User0", "", big.NewInt(20), nil) // #13: Deposit(3) User1: 20 - tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[2], common.TxTypeDeposit, "User13", "", big.NewInt(20), nil) + tc.checkL1TxParams(t, blocks[0].Batches[1].L1UserTxs[2], common.TxTypeCreateAccountDeposit, 3, "User1", "", big.NewInt(20), nil) // #14: Transfer(1) User0-User1: 15 (1) - tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[2], common.TxTypeTransfer, "User01", "User11", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[2], common.TxTypeTransfer, 1, "User0", "User1", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) // #15: Transfer(3) User1-User0: 15 (1) - tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[3], common.TxTypeTransfer, "User13", "User03", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[3], common.TxTypeTransfer, 3, "User1", "User0", big.NewInt(15), common.BatchNum(1), common.Nonce(1)) // #16: Transfer(1) A-C: 1 (1) - tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[4], common.TxTypeTransfer, "A1", "C1", big.NewInt(1), common.BatchNum(1), common.Nonce(3)) + tc.checkL2TxParams(t, blocks[0].Batches[1].L2Txs[4], common.TxTypeTransfer, 1, "A", "C", big.NewInt(1), common.BatchNum(1), common.Nonce(3)) // change of Batch // #17: Transfer(1) User1-User0: 1 (1) - tc.checkL2TxParams(t, blocks[0].Batches[2].L2Txs[0], common.TxTypeTransfer, "User11", "User01", big.NewInt(1), common.BatchNum(2), common.Nonce(1)) + tc.checkL2TxParams(t, blocks[0].Batches[2].L2Txs[0], common.TxTypeTransfer, 1, "User1", "User0", big.NewInt(1), common.BatchNum(2), common.Nonce(1)) // change of Block (implies also a change of batch) // #18: Transfer(1) A-B: 1 (1) - tc.checkL2TxParams(t, blocks[1].Batches[0].L2Txs[0], common.TxTypeTransfer, "A1", "B1", big.NewInt(1), common.BatchNum(3), common.Nonce(4)) + tc.checkL2TxParams(t, blocks[1].Batches[0].L2Txs[0], common.TxTypeTransfer, 1, "A", "B", big.NewInt(1), common.BatchNum(3), common.Nonce(4)) } -func (tc *TestContext) checkL1TxParams(t *testing.T, tx common.L1Tx, typ common.TxType, from, to string, loadAmount, amount *big.Int) { +func (tc *TestContext) checkL1TxParams(t *testing.T, tx common.L1Tx, typ common.TxType, tokenID common.TokenID, from, to string, loadAmount, amount *big.Int) { assert.Equal(t, typ, tx.Type) - if tx.FromIdx != nil { - assert.Equal(t, tc.accounts[from].Idx, *tx.FromIdx) + if tx.FromIdx != common.Idx(0) { + assert.Equal(t, tc.Users[from].Accounts[tokenID].Idx, tx.FromIdx) } - assert.Equal(t, tc.accounts[from].Addr.Hex(), tx.FromEthAddr.Hex()) - assert.Equal(t, tc.accounts[from].BJJ.Public(), tx.FromBJJ) + assert.Equal(t, tc.Users[from].Addr.Hex(), tx.FromEthAddr.Hex()) + assert.Equal(t, tc.Users[from].BJJ.Public(), tx.FromBJJ) if tx.ToIdx != common.Idx(0) { - assert.Equal(t, tc.accounts[to].Idx, tx.ToIdx) + assert.Equal(t, tc.Users[to].Accounts[tokenID].Idx, tx.ToIdx) } if loadAmount != nil { assert.Equal(t, loadAmount, tx.LoadAmount) @@ -112,11 +114,11 @@ func (tc *TestContext) checkL1TxParams(t *testing.T, tx common.L1Tx, typ common. assert.Equal(t, amount, tx.Amount) } } -func (tc *TestContext) checkL2TxParams(t *testing.T, tx common.L2Tx, typ common.TxType, from, to string, amount *big.Int, batchNum common.BatchNum, nonce common.Nonce) { +func (tc *TestContext) checkL2TxParams(t *testing.T, tx common.L2Tx, typ common.TxType, tokenID common.TokenID, from, to string, amount *big.Int, batchNum common.BatchNum, nonce common.Nonce) { assert.Equal(t, typ, tx.Type) - assert.Equal(t, tc.accounts[from].Idx, tx.FromIdx) + assert.Equal(t, tc.Users[from].Accounts[tokenID].Idx, tx.FromIdx) if tx.Type != common.TxTypeExit { - assert.Equal(t, tc.accounts[to].Idx, tx.ToIdx) + assert.Equal(t, tc.Users[to].Accounts[tokenID].Idx, tx.ToIdx) } if amount != nil { assert.Equal(t, amount, tx.Amount) @@ -127,6 +129,23 @@ func (tc *TestContext) checkL2TxParams(t *testing.T, tx common.L2Tx, typ common. func TestGeneratePoolL2Txs(t *testing.T) { set := ` + Type: Blockchain + + CreateAccountDeposit(1) A: 10 + CreateAccountDeposit(2) A: 20 + CreateAccountDeposit(1) B: 5 + CreateAccountDeposit(1) C: 5 + CreateAccountDeposit(1) User0: 5 + CreateAccountDeposit(1) User1: 0 + CreateAccountDeposit(3) User0: 0 + CreateAccountDeposit(3) User1: 5 + CreateAccountDeposit(2) B: 5 + CreateAccountDeposit(2) D: 0 + ` + tc := NewTestContext(t) + _ = tc.GenerateBlocks(set) + set = ` + Type: PoolL2 PoolTransfer(1) A-B: 6 (1) PoolTransfer(1) B-C: 3 (1) PoolTransfer(1) C-A: 3 (1) @@ -137,15 +156,14 @@ func TestGeneratePoolL2Txs(t *testing.T) { PoolTransfer(2) B-D: 3 (1) PoolExit(1) A: 3 ` - tc := NewTestContext(t) poolL2Txs := tc.GeneratePoolL2Txs(set) assert.Equal(t, 9, len(poolL2Txs)) assert.Equal(t, common.TxTypeTransfer, poolL2Txs[0].Type) assert.Equal(t, common.TxTypeExit, poolL2Txs[8].Type) - assert.Equal(t, tc.accounts["B1"].Addr.Hex(), poolL2Txs[0].ToEthAddr.Hex()) - assert.Equal(t, tc.accounts["B1"].BJJ.Public().String(), poolL2Txs[0].ToBJJ.String()) - assert.Equal(t, tc.accounts["User11"].Addr.Hex(), poolL2Txs[5].ToEthAddr.Hex()) - assert.Equal(t, tc.accounts["User11"].BJJ.Public().String(), poolL2Txs[5].ToBJJ.String()) + assert.Equal(t, tc.Users["B"].Addr.Hex(), poolL2Txs[0].ToEthAddr.Hex()) + assert.Equal(t, tc.Users["B"].BJJ.Public().String(), poolL2Txs[0].ToBJJ.String()) + assert.Equal(t, tc.Users["User1"].Addr.Hex(), poolL2Txs[5].ToEthAddr.Hex()) + assert.Equal(t, tc.Users["User1"].BJJ.Public().String(), poolL2Txs[5].ToBJJ.String()) assert.Equal(t, common.Nonce(1), poolL2Txs[0].Nonce) assert.Equal(t, common.Nonce(2), poolL2Txs[3].Nonce) @@ -153,6 +171,7 @@ func TestGeneratePoolL2Txs(t *testing.T) { // load another set in the same TestContext set = ` + Type: PoolL2 PoolTransfer(1) A-B: 6 (1) PoolTransfer(1) B-C: 3 (1) PoolTransfer(1) A-C: 3 (1) diff --git a/test/transakcio/utils.go b/test/transakcio/utils.go new file mode 100644 index 0000000..56fa499 --- /dev/null +++ b/test/transakcio/utils.go @@ -0,0 +1,9 @@ +package transakcio + +// TODO +// func extendTokenIDs(o, n []common.TokenID) []common.TokenID { +// return o +// } +// func extendAccounts(o, n map[string]*Account) map[string]*Account { +// return o +// } diff --git a/test/txs.go b/test/txs.go deleted file mode 100644 index 423dd3f..0000000 --- a/test/txs.go +++ /dev/null @@ -1,340 +0,0 @@ -package test - -import ( - "crypto/ecdsa" - "math/big" - "strconv" - "strings" - "testing" - "time" - - ethCommon "github.com/ethereum/go-ethereum/common" - ethCrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/log" - "github.com/iden3/go-iden3-crypto/babyjub" - "github.com/stretchr/testify/require" -) - -// TestContext contains the data of the test -type TestContext struct { - t *testing.T - Instructions []Instruction - accountsNames []string - accounts map[string]*Account - TokenIDs []common.TokenID - l1CreatedAccounts map[string]*Account -} - -// NewTestContext returns a new TestContext -func NewTestContext(t *testing.T) *TestContext { - return &TestContext{ - t: t, - accounts: make(map[string]*Account), - l1CreatedAccounts: make(map[string]*Account), - } -} - -// Account contains the data related to a testing account -type Account struct { - BJJ *babyjub.PrivateKey - Addr ethCommon.Address - Idx common.Idx - Nonce common.Nonce -} - -// BlockData contains the information of a Block -type BlockData struct { - block *common.Block // ethereum block - // L1UserTxs that were submitted in the block - L1UserTxs []common.L1Tx - Batches []BatchData - RegisteredTokens []common.Token -} - -// BatchData contains the information of a Batch -type BatchData struct { - L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer - // L1UserTxs that were forged in the batch - L1UserTxs []common.L1Tx - L1CoordinatorTxs []common.L1Tx - L2Txs []common.L2Tx - CreatedAccounts []common.Account - ExitTree []common.ExitInfo - Batch *common.Batch -} - -// GenerateBlocks returns an array of BlockData for a given set. It uses the -// accounts (keys & nonces) of the TestContext. -func (tc *TestContext) GenerateBlocks(set string) []BlockData { - parser := NewParser(strings.NewReader(set)) - parsedSet, err := parser.Parse(false) - require.Nil(tc.t, err) - - tc.Instructions = parsedSet.Instructions - tc.accountsNames = parsedSet.Accounts - tc.TokenIDs = parsedSet.TokenIDs - - tc.generateKeys(tc.accountsNames) - - var blocks []BlockData - currBatchNum := 0 - var currBlock BlockData - var currBatch BatchData - idx := 256 - for _, inst := range parsedSet.Instructions { - switch inst.Type { - case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: - tx := common.L1Tx{ - // TxID - FromEthAddr: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr, - FromBJJ: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(), - TokenID: inst.TokenID, - LoadAmount: big.NewInt(int64(inst.LoadAmount)), - Type: inst.Type, - } - if tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx) - - tc.l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)] - idx++ - } - if inst.Type == common.TxTypeCreateAccountDepositTransfer { - tx.Amount = big.NewInt(int64(inst.Amount)) - } - currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) - case common.TxTypeDeposit, common.TxTypeDepositTransfer: - tx := common.L1Tx{ - // TxID - FromIdx: &tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - FromEthAddr: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr, - FromBJJ: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(), - TokenID: inst.TokenID, - LoadAmount: big.NewInt(int64(inst.LoadAmount)), - Type: inst.Type, - } - if tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { - // if account.Idx is not set yet, set it and increment idx - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx) - - tc.l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)] - idx++ - } - if inst.Type == common.TxTypeDepositTransfer { - tx.Amount = big.NewInt(int64(inst.Amount)) - // if ToIdx is not set yet, set it and increment idx - if tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx == common.Idx(0) { - tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx) - - tc.l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)] - tx.ToIdx = common.Idx(idx) - idx++ - } else { - // if Idx account of To already exist, use it for ToIdx - tx.ToIdx = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx - } - } - currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) - case common.TxTypeTransfer: - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ - // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account - if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok { - tx := common.L1Tx{ - FromEthAddr: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, - FromBJJ: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), - TokenID: inst.TokenID, - LoadAmount: big.NewInt(int64(inst.Amount)), - Type: common.TxTypeCreateAccountDeposit, - } - tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx) - tc.l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)] - currBatch.L1CoordinatorTxs = append(currBatch.L1CoordinatorTxs, tx) - idx++ - } - toEthAddr := new(ethCommon.Address) - *toEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr - rqToEthAddr := new(ethCommon.Address) - *rqToEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr - tx := common.L2Tx{ - FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx, - Amount: big.NewInt(int64(inst.Amount)), - Fee: common.FeeSelector(inst.Fee), - Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, - BatchNum: common.BatchNum(currBatchNum), - Type: common.TxTypeTransfer, - } - nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx()) - if err != nil { - panic(err) - } - nL2Tx, err := nTx.L2Tx() - if err != nil { - panic(err) - } - tx = *nL2Tx - - currBatch.L2Txs = append(currBatch.L2Txs, tx) - case common.TxTypeExit: - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ - tx := common.L2Tx{ - FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: common.Idx(1), // as is an Exit - Amount: big.NewInt(int64(inst.Amount)), - Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, - Type: common.TxTypeExit, - } - nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx()) - if err != nil { - panic(err) - } - nL2Tx, err := nTx.L2Tx() - if err != nil { - panic(err) - } - tx = *nL2Tx - currBatch.L2Txs = append(currBatch.L2Txs, tx) - case common.TxTypeForceExit: - fromIdx := new(common.Idx) - *fromIdx = tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx - tx := common.L1Tx{ - FromIdx: fromIdx, - ToIdx: common.Idx(1), // as is an Exit - TokenID: inst.TokenID, - Amount: big.NewInt(int64(inst.Amount)), - Type: common.TxTypeExit, - } - currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx) - case TypeNewBatch: - currBlock.Batches = append(currBlock.Batches, currBatch) - currBatchNum++ - currBatch = BatchData{} - case TypeNewBlock: - currBlock.Batches = append(currBlock.Batches, currBatch) - currBatchNum++ - currBatch = BatchData{} - blocks = append(blocks, currBlock) - currBlock = BlockData{} - default: - log.Fatalf("Unexpected type: %s", inst.Type) - } - } - currBlock.Batches = append(currBlock.Batches, currBatch) - blocks = append(blocks, currBlock) - - return blocks -} - -// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It -// uses the accounts (keys & nonces) of the TestContext. -func (tc *TestContext) GeneratePoolL2Txs(set string) []common.PoolL2Tx { - parser := NewParser(strings.NewReader(set)) - parsedSet, err := parser.Parse(true) - require.Nil(tc.t, err) - - tc.Instructions = parsedSet.Instructions - tc.accountsNames = parsedSet.Accounts - tc.TokenIDs = parsedSet.TokenIDs - - tc.generateKeys(tc.accountsNames) - - txs := []common.PoolL2Tx{} - for _, inst := range tc.Instructions { - switch inst.Type { - case common.TxTypeTransfer: - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ - // if account of receiver does not exist, don't use - // ToIdx, and use only ToEthAddr & ToBJJ - toIdx := new(common.Idx) - if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok { - *toIdx = 0 - } else { - *toIdx = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx - } - // TODO once common.L{x}Txs parameter pointers is undone, update this lines related to pointers usage - toEthAddr := new(ethCommon.Address) - *toEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr - rqToEthAddr := new(ethCommon.Address) - *rqToEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr - tx := common.PoolL2Tx{ - FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: toIdx, - ToEthAddr: toEthAddr, - ToBJJ: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), - TokenID: inst.TokenID, - Amount: big.NewInt(int64(inst.Amount)), - Fee: common.FeeSelector(inst.Fee), - Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, - State: common.PoolL2TxStatePending, - Timestamp: time.Now(), - BatchNum: nil, - RqToEthAddr: rqToEthAddr, - RqToBJJ: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), - Type: common.TxTypeTransfer, - } - nTx, err := common.NewPoolL2Tx(&tx) - if err != nil { - panic(err) - } - tx = *nTx - // perform signature and set it to tx.Signature - toSign, err := tx.HashToSign() - if err != nil { - panic(err) - } - sig := tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign) - tx.Signature = sig - - txs = append(txs, tx) - case common.TxTypeExit: - tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ - // TODO once common.L{x}Txs parameter pointers is undone, update this lines related to pointers usage - toIdx := new(common.Idx) - *toIdx = common.Idx(1) // as is an Exit - tx := common.PoolL2Tx{ - FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: toIdx, // as is an Exit - TokenID: inst.TokenID, - Amount: big.NewInt(int64(inst.Amount)), - Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, - Type: common.TxTypeExit, - } - txs = append(txs, tx) - default: - log.Fatalf("instruction type unrecognized: %s", inst.Type) - } - } - - return txs -} - -// generateKeys generates BabyJubJub & Address keys for the given list of -// account names in a deterministic way. This means, that for the same given -// 'accNames' in a certain order, the keys will be always the same. -func (tc *TestContext) generateKeys(accNames []string) map[string]*Account { - acc := make(map[string]*Account) - for i := 1; i < len(accNames)+1; i++ { - if _, ok := tc.accounts[accNames[i-1]]; ok { - // account already created - continue - } - // babyjubjub key - var sk babyjub.PrivateKey - copy(sk[:], []byte(strconv.Itoa(i))) // only for testing - - // eth address - var key ecdsa.PrivateKey - key.D = big.NewInt(int64(i)) // only for testing - key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes()) - key.Curve = ethCrypto.S256() - addr := ethCrypto.PubkeyToAddress(key.PublicKey) - - a := Account{ - BJJ: &sk, - Addr: addr, - Nonce: 0, - } - tc.accounts[accNames[i-1]] = &a - } - return acc -} diff --git a/test/utils.go b/test/utils.go deleted file mode 100644 index 25678fc..0000000 --- a/test/utils.go +++ /dev/null @@ -1,12 +0,0 @@ -package test - -import "github.com/hermeznetwork/hermez-node/common" - -func extendTokenIDs(o, n []common.TokenID) []common.TokenID { - - return o -} -func extendAccounts(o, n map[string]*Account) map[string]*Account { - - return o -} diff --git a/txselector/txselector_test.go b/txselector/txselector_test.go index 187ff94..9cda9c1 100644 --- a/txselector/txselector_test.go +++ b/txselector/txselector_test.go @@ -1,22 +1,7 @@ package txselector -import ( - "io/ioutil" - "os" - "testing" - "time" - - "github.com/hermeznetwork/hermez-node/common" - dbUtils "github.com/hermeznetwork/hermez-node/db" - "github.com/hermeznetwork/hermez-node/db/historydb" - "github.com/hermeznetwork/hermez-node/db/l2db" - "github.com/hermeznetwork/hermez-node/db/statedb" - "github.com/hermeznetwork/hermez-node/test" - "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - +/* + TODO update transactions generation func initTest(t *testing.T, testSet string) *TxSelector { pass := os.Getenv("POSTGRES_PASS") db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez") @@ -52,30 +37,31 @@ func addTokens(t *testing.T, tokens []common.Token, db *sqlx.DB) { } func TestGetL2TxSelection(t *testing.T) { - txsel := initTest(t, test.SetTest0) + txsel := initTest(t, transakcio.SetPool0) test.CleanL2DB(txsel.l2db.DB()) - // generate test transactions - l1Txs, _, poolL2Txs, tokens := test.GenerateTestTxsFromSet(t, test.SetTest0) + // generate test transactions + l1Txs, _, poolL2Txs, tokens := test.GenerateTestTxsFromSet(t, test.SetTest0) - // add tokens to HistoryDB to avoid breaking FK constrains - addTokens(t, tokens, txsel.l2db.DB()) - // add the first batch of transactions to the TxSelector - addL2Txs(t, txsel, poolL2Txs[0]) + // add tokens to HistoryDB to avoid breaking FK constrains + addTokens(t, tokens, txsel.l2db.DB()) + // add the first batch of transactions to the TxSelector + addL2Txs(t, txsel, poolL2Txs[0]) - _, err := txsel.GetL2TxSelection(0) - assert.Nil(t, err) + _, err := txsel.GetL2TxSelection(0) + assert.Nil(t, err) - _, _, _, err = txsel.GetL1L2TxSelection(0, l1Txs[0]) - assert.Nil(t, err) + _, _, _, err = txsel.GetL1L2TxSelection(0, l1Txs[0]) + assert.Nil(t, err) - // txs, err := txsel.GetL2TxSelection(0) - // assert.Nil(t, err) - // for _, tx := range txs { - // fmt.Println(tx.FromIdx, tx.ToIdx, tx.AbsoluteFee) - // } - // assert.Equal(t, 3, len(txs)) - // assert.Equal(t, uint64(6), txs[0].AbsoluteFee) - // assert.Equal(t, uint64(5), txs[1].AbsoluteFee) - // assert.Equal(t, uint64(4), txs[2].AbsoluteFee) + // txs, err := txsel.GetL2TxSelection(0) + // assert.Nil(t, err) + // for _, tx := range txs { + // fmt.Println(tx.FromIdx, tx.ToIdx, tx.AbsoluteFee) + // } + // assert.Equal(t, 3, len(txs)) + // assert.Equal(t, uint64(6), txs[0].AbsoluteFee) + // assert.Equal(t, uint64(5), txs[1].AbsoluteFee) + // assert.Equal(t, uint64(4), txs[2].AbsoluteFee) } +*/