hermez-node/test/txs.go
test/txs.goRaw
1package test
2
3import (
4 "crypto/ecdsa"
5 "math/big"
6 "strconv"
7 "strings"
8 "testing"
9 "time"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 ethCrypto "github.com/ethereum/go-ethereum/crypto"
13 "github.com/hermeznetwork/hermez-node/common"
14 "github.com/iden3/go-iden3-crypto/babyjub"
15 "github.com/stretchr/testify/require"
16)
17
18type Account struct {
19 BJJ *babyjub.PrivateKey
20 Addr ethCommon.Address
21 Idx common.Idx
22 Nonce common.Nonce
23}
24
25// GenerateKeys generates BabyJubJub & Address keys for the given list of
26// account names in a deterministic way. This means, that for the same given
27// 'accNames' the keys will be always the same.
28func GenerateKeys(t *testing.T, accNames []string) map[string]*Account {
29 acc := make(map[string]*Account)
30 for i := 1; i < len(accNames)+1; i++ {
31 // babyjubjub key
32 var sk babyjub.PrivateKey
33 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
34
35 // eth address
36 var key ecdsa.PrivateKey
37 key.D = big.NewInt(int64(i)) // only for testing
38 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
39 key.Curve = ethCrypto.S256()
40 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
41
42 a := Account{
43 BJJ: &sk,
44 Addr: addr,
45 Nonce: 0,
46 }
47 acc[accNames[i-1]] = &a
48 }
49 return acc
50}
51
52// GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the
53// given Instructions.
54func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx) {
55 accounts := GenerateKeys(t, instructions.Accounts)
56 l1CreatedAccounts := make(map[string]*Account)
57
58 var batchL1Txs []*common.L1Tx
59 var batchCoordinatorL1Txs []*common.L1Tx
60 var batchPoolL2Txs []*common.PoolL2Tx
61 var l1Txs [][]*common.L1Tx
62 var coordinatorL1Txs [][]*common.L1Tx
63 var poolL2Txs [][]*common.PoolL2Tx
64 idx := 1
65 for _, inst := range instructions.Instructions {
66 switch inst.Type {
67 case common.TxTypeCreateAccountDeposit:
68 tx := common.L1Tx{
69 // TxID
70 FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
71 FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
72 TokenID: inst.TokenID,
73 LoadAmount: big.NewInt(int64(inst.Amount)),
74 Type: common.TxTypeCreateAccountDeposit,
75 }
76 batchL1Txs = append(batchL1Txs, &tx)
77 if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
78 accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
79
80 l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = accounts[idxTokenIDToString(inst.From, inst.TokenID)]
81 idx++
82 }
83 case common.TxTypeTransfer:
84 // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account
85 if _, ok := l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok {
86 tx := common.L1Tx{
87 FromEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
88 FromBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
89 TokenID: inst.TokenID,
90 LoadAmount: big.NewInt(int64(inst.Amount)),
91 Type: common.TxTypeCreateAccountDeposit,
92 }
93 accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx)
94 l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)]
95 batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, &tx)
96 idx++
97 }
98
99 tx := common.PoolL2Tx{
100 // TxID: nil,
101 FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
102 ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx,
103 ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
104 ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
105 TokenID: inst.TokenID,
106 Amount: big.NewInt(int64(inst.Amount)),
107 Fee: common.FeeSelector(inst.Fee),
108 Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
109 State: common.PoolL2TxStatePending,
110 Timestamp: time.Now(),
111 BatchNum: 0,
112 RqToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
113 RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
114 Type: common.TxTypeTransfer,
115 }
116 // perform signature and set it to tx.Signature
117 toSign, err := tx.HashToSign()
118 if err != nil {
119 panic(err)
120 }
121 sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
122 tx.Signature = sig
123
124 accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
125 batchPoolL2Txs = append(batchPoolL2Txs, &tx)
126
127 case common.TxTypeExit, common.TxTypeForceExit:
128 tx := common.L1Tx{
129 FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
130 ToIdx: common.Idx(1), // as is an Exit
131 TokenID: inst.TokenID,
132 Amount: big.NewInt(int64(inst.Amount)),
133 Type: common.TxTypeExit,
134 }
135 batchL1Txs = append(batchL1Txs, &tx)
136 case TypeNewBatch:
137 l1Txs = append(l1Txs, batchL1Txs)
138 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
139 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
140 batchL1Txs = []*common.L1Tx{}
141 batchCoordinatorL1Txs = []*common.L1Tx{}
142 batchPoolL2Txs = []*common.PoolL2Tx{}
143 default:
144 continue
145 }
146 }
147 l1Txs = append(l1Txs, batchL1Txs)
148 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
149 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
150
151 return l1Txs, coordinatorL1Txs, poolL2Txs
152}
153
154func GenerateTestTxsFromSet(t *testing.T, set string) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx) {
155 parser := NewParser(strings.NewReader(set))
156 instructions, err := parser.Parse()
157 require.Nil(t, err)
158
159 return GenerateTestTxs(t, instructions)
160}