diff --git a/test/lang.go b/test/lang.go index 33de602..a92bbee 100644 --- a/test/lang.go +++ b/test/lang.go @@ -14,6 +14,8 @@ import ( var eof = rune(0) var errof = fmt.Errorf("eof in parseline") var ecomment = fmt.Errorf("comment in parseline") +var enewbatch = fmt.Errorf("newbatch") +var TypeNewBatch common.TxType = "TxTypeNewBatch" const ( ILLEGAL token = iota @@ -226,6 +228,9 @@ func (p *Parser) parseLine() (*Instruction, error) { // A-B (1): 6 // or Withdraw: // A (1) E: 4 + // or NextBatch: + // > and here the comment + c := &Instruction{} tok, lit := p.scanIgnoreWhitespace() if tok == EOF { @@ -235,6 +240,9 @@ func (p *Parser) parseLine() (*Instruction, error) { if lit == "/" { _, _ = p.s.r.ReadString('\n') return nil, ecomment + } else if lit == ">" { + _, _ = p.s.r.ReadString('\n') + return nil, enewbatch } c.From = lit @@ -338,6 +346,12 @@ func (p *Parser) Parse() (Instructions, error) { i++ continue } + if err == enewbatch { + i++ + inst := &Instruction{Type: TypeNewBatch} + instructions.Instructions = append(instructions.Instructions, inst) + continue + } if err != nil { return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.Literal, err.Error()) } diff --git a/test/lang_test.go b/test/lang_test.go index 7a0a65b..57ad346 100644 --- a/test/lang_test.go +++ b/test/lang_test.go @@ -20,6 +20,10 @@ func TestParse(t *testing.T) { // L2 transactions A-B (1): 6 1 B-C (1): 3 1 + + // set new batch, label does not affect + > batch1 + C-A (1): 3 1 A-B (2): 15 1 @@ -34,7 +38,7 @@ func TestParse(t *testing.T) { parser := NewParser(strings.NewReader(s)) instructions, err := parser.Parse() assert.Nil(t, err) - assert.Equal(t, 12, len(instructions.Instructions)) + assert.Equal(t, 13, len(instructions.Instructions)) // assert.Equal(t, 5, len(instructions.Accounts)) fmt.Println(instructions.Accounts) assert.Equal(t, 3, len(instructions.TokenIDs)) @@ -46,12 +50,13 @@ func TestParse(t *testing.T) { } } - assert.Equal(t, "User0 (1): 20", instructions.Instructions[7].Raw()) - assert.Equal(t, "Type: Create&Deposit, From: User0, Amount: 20, TokenID: 1,\n", instructions.Instructions[7].String()) - assert.Equal(t, "User0-User1 (1): 15 1", instructions.Instructions[9].Raw()) - assert.Equal(t, "Type: Transfer, From: User0, To: User1, Amount: 15, Fee: 1, TokenID: 1,\n", instructions.Instructions[9].String()) - assert.Equal(t, "A (1)E: 5", instructions.Instructions[11].Raw()) - assert.Equal(t, "Type: ForceExit, From: A, Amount: 5, TokenID: 1,\n", instructions.Instructions[11].String()) + assert.Equal(t, TypeNewBatch, instructions.Instructions[5].Type) + assert.Equal(t, "User0 (1): 20", instructions.Instructions[8].Raw()) + assert.Equal(t, "Type: Create&Deposit, From: User0, Amount: 20, TokenID: 1,\n", instructions.Instructions[8].String()) + assert.Equal(t, "User0-User1 (1): 15 1", instructions.Instructions[10].Raw()) + assert.Equal(t, "Type: Transfer, From: User0, To: User1, Amount: 15, Fee: 1, TokenID: 1,\n", instructions.Instructions[10].String()) + assert.Equal(t, "A (1)E: 5", instructions.Instructions[12].Raw()) + assert.Equal(t, "Type: ForceExit, From: A, Amount: 5, TokenID: 1,\n", instructions.Instructions[12].String()) } func TestParseErrors(t *testing.T) { diff --git a/test/sets.go b/test/sets.go new file mode 100644 index 0000000..0e6d21a --- /dev/null +++ b/test/sets.go @@ -0,0 +1,169 @@ +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 + +// Set0 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 +` + +// cases to add: +// - L2 tx from B to A, when B still does not have tokens, which will receive from C diff --git a/test/sets_test.go b/test/sets_test.go new file mode 100644 index 0000000..46375d5 --- /dev/null +++ b/test/sets_test.go @@ -0,0 +1,14 @@ +package test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCompileSets(t *testing.T) { + parser := NewParser(strings.NewReader(SetTest0)) + _, err := parser.Parse() + assert.Nil(t, err) +} diff --git a/test/txs.go b/test/txs.go index bbdb9b3..b5e10f5 100644 --- a/test/txs.go +++ b/test/txs.go @@ -49,17 +49,16 @@ func GenerateKeys(t *testing.T, accNames []string) map[string]*Account { // GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the // given Instructions. -func GenerateTestTxs(t *testing.T, instructions Instructions) ([]*common.L1Tx, []*common.PoolL2Tx) { +func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx) { accounts := GenerateKeys(t, instructions.Accounts) + l1CreatedAccounts := make(map[string]*Account) - // debug - // fmt.Println("accounts:") - // for n, a := range accounts { - // fmt.Printf(" %s: bjj:%s - addr:%s\n", n, a.BJJ.Public().String()[:10], a.Addr.Hex()[:10]) - // } - - var l1txs []*common.L1Tx - var l2txs []*common.PoolL2Tx + var batchL1txs []*common.L1Tx + var batchCoordinatorL1txs []*common.L1Tx + var batchL2txs []*common.PoolL2Tx + var l1txs [][]*common.L1Tx + var coordinatorL1txs [][]*common.L1Tx + var l2txs [][]*common.PoolL2Tx idx := 1 for _, inst := range instructions.Instructions { switch inst.Type { @@ -72,37 +71,81 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([]*common.L1Tx, [ LoadAmount: big.NewInt(int64(inst.Amount)), Type: common.TxTypeCreateAccountDeposit, } - l1txs = append(l1txs, &tx) + batchL1txs = append(batchL1txs, &tx) if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx) + + l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = accounts[idxTokenIDToString(inst.From, inst.TokenID)] idx++ } case common.TxTypeTransfer: + // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account + if _, ok := l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok { + tx := common.L1Tx{ + FromEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, + FromBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), + TokenID: inst.TokenID, + LoadAmount: big.NewInt(int64(inst.Amount)), + Type: common.TxTypeCreateAccountDeposit, + } + accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx) + l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)] + batchCoordinatorL1txs = append(batchCoordinatorL1txs, &tx) + idx++ + } + tx := common.PoolL2Tx{ // TxID: nil, - FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx, - ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, - ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), - TokenID: inst.TokenID, - Amount: big.NewInt(int64(inst.Amount)), - Fee: common.FeeSelector(inst.Fee), - Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, - State: common.PoolL2TxStatePending, - Timestamp: time.Now(), - BatchNum: 0, - Type: common.TxTypeTransfer, + FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, + ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx, + ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, + ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), + TokenID: inst.TokenID, + Amount: big.NewInt(int64(inst.Amount)), + Fee: common.FeeSelector(inst.Fee), + Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, + State: common.PoolL2TxStatePending, + Timestamp: time.Now(), + BatchNum: 0, + RqToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, + RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), + Type: common.TxTypeTransfer, + } + // perform signature and set it to tx.Signature + toSign, err := tx.HashToSign() + if err != nil { + panic(err) } - // TODO once signature function is ready, perform - // signature and set it to tx.Signature + sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign) + tx.Signature = sig accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++ - l2txs = append(l2txs, &tx) + batchL2txs = append(batchL2txs, &tx) + + case common.TxTypeExit, common.TxTypeForceExit: + tx := common.L1Tx{ + FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, + ToIdx: common.Idx(1), // as is an Exit + TokenID: inst.TokenID, + Amount: big.NewInt(int64(inst.Amount)), + Type: common.TxTypeExit, + } + batchL1txs = append(batchL1txs, &tx) + case TypeNewBatch: + l1txs = append(l1txs, batchL1txs) + coordinatorL1txs = append(coordinatorL1txs, batchCoordinatorL1txs) + l2txs = append(l2txs, batchL2txs) + batchL1txs = []*common.L1Tx{} + batchCoordinatorL1txs = []*common.L1Tx{} + batchL2txs = []*common.PoolL2Tx{} default: continue } } + l1txs = append(l1txs, batchL1txs) + coordinatorL1txs = append(coordinatorL1txs, batchCoordinatorL1txs) + l2txs = append(l2txs, batchL2txs) - return l1txs, l2txs + return l1txs, coordinatorL1txs, l2txs } diff --git a/test/txs_test.go b/test/txs_test.go index 91e6310..963dc6c 100644 --- a/test/txs_test.go +++ b/test/txs_test.go @@ -6,7 +6,6 @@ import ( "github.com/hermeznetwork/hermez-node/common" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGenerateTestL2Txs(t *testing.T) { @@ -16,6 +15,7 @@ func TestGenerateTestL2Txs(t *testing.T) { B (1): 5 A-B (1): 6 1 B-C (1): 3 1 + > advance batch C-A (1): 3 1 A-B (1): 1 1 A-B (2): 15 1 @@ -23,29 +23,35 @@ func TestGenerateTestL2Txs(t *testing.T) { User1 (3) : 20 User0-User1 (1): 15 1 User1-User0 (3): 15 1 + B-D (2): 3 1 ` parser := NewParser(strings.NewReader(s)) instructions, err := parser.Parse() assert.Nil(t, err) - l1txs, l2txs := GenerateTestTxs(t, instructions) - require.Equal(t, 5, len(l1txs)) - require.Equal(t, 7, len(l2txs)) + l1txs, coordinatorL1txs, l2txs := GenerateTestTxs(t, instructions) + assert.Equal(t, 2, len(l1txs)) + assert.Equal(t, 3, len(l1txs[0])) + assert.Equal(t, 1, len(coordinatorL1txs[0])) + assert.Equal(t, 2, len(l2txs[0])) + assert.Equal(t, 2, len(l1txs[1])) + assert.Equal(t, 4, len(coordinatorL1txs[1])) + assert.Equal(t, 6, len(l2txs[1])) // l1txs - assert.Equal(t, common.TxTypeCreateAccountDeposit, l1txs[0].Type) - assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[0].FromBJJ.String()) - assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[1].FromBJJ.String()) - assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l1txs[2].FromBJJ.String()) - assert.Equal(t, "b6856a87832b182e5a9a1e738dbcd1f3c728bbc67ea1010aaff563eb5316131b", l1txs[4].FromBJJ.String()) + assert.Equal(t, common.TxTypeCreateAccountDeposit, l1txs[0][0].Type) + assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[0][0].FromBJJ.String()) + assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[0][1].FromBJJ.String()) + assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l1txs[0][2].FromBJJ.String()) + assert.Equal(t, "750a24a874a81c6c6f8aaa168ff2ee88c58263fee9ddd96d9717bcffc809b027", l1txs[1][1].FromBJJ.String()) // l2txs - assert.Equal(t, common.TxTypeTransfer, l2txs[0].Type) - assert.Equal(t, common.Idx(1), l2txs[0].FromIdx) - assert.Equal(t, common.Idx(3), l2txs[0].ToIdx) - assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l2txs[0].ToBJJ.String()) - assert.Equal(t, "0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69", l2txs[0].ToEthAddr.Hex()) - assert.Equal(t, common.Nonce(0), l2txs[0].Nonce) - assert.Equal(t, common.Nonce(1), l2txs[3].Nonce) - assert.Equal(t, common.FeeSelector(1), l2txs[0].Fee) + assert.Equal(t, common.TxTypeTransfer, l2txs[0][0].Type) + assert.Equal(t, common.Idx(1), l2txs[0][0].FromIdx) + assert.Equal(t, common.Idx(3), l2txs[0][0].ToIdx) + assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l2txs[0][0].ToBJJ.String()) + assert.Equal(t, "0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69", l2txs[0][0].ToEthAddr.Hex()) + assert.Equal(t, common.Nonce(0), l2txs[0][0].Nonce) + assert.Equal(t, common.Nonce(1), l2txs[1][1].Nonce) + assert.Equal(t, common.FeeSelector(1), l2txs[0][0].Fee) }