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
18// Account contains the data related to a testing account
19type Account struct {
20 BJJ *babyjub.PrivateKey
21 Addr ethCommon.Address
22 Idx common.Idx
23 Nonce common.Nonce
24}
25
26// GenerateKeys generates BabyJubJub & Address keys for the given list of
27// account names in a deterministic way. This means, that for the same given
28// 'accNames' the keys will be always the same.
29func GenerateKeys(t *testing.T, accNames []string) map[string]*Account {
30 acc := make(map[string]*Account)
31 for i := 1; i < len(accNames)+1; i++ {
32 // babyjubjub key
33 var sk babyjub.PrivateKey
34 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
35
36 // eth address
37 var key ecdsa.PrivateKey
38 key.D = big.NewInt(int64(i)) // only for testing
39 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
40 key.Curve = ethCrypto.S256()
41 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
42
43 a := Account{
44 BJJ: &sk,
45 Addr: addr,
46 Nonce: 0,
47 }
48 acc[accNames[i-1]] = &a
49 }
50 return acc
51}
52
53// GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the
54// given Instructions.
55func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx, []common.Token) {
56 accounts := GenerateKeys(t, instructions.Accounts)
57 l1CreatedAccounts := make(map[string]*Account)
58
59 var batchL1Txs []*common.L1Tx
60 var batchCoordinatorL1Txs []*common.L1Tx
61 var batchPoolL2Txs []*common.PoolL2Tx
62 var l1Txs [][]*common.L1Tx
63 var coordinatorL1Txs [][]*common.L1Tx
64 var poolL2Txs [][]*common.PoolL2Tx
65 idx := 256
66 for _, inst := range instructions.Instructions {
67 switch inst.Type {
68 case common.TxTypeCreateAccountDeposit:
69 tx := common.L1Tx{
70 // TxID
71 FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
72 FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
73 TokenID: inst.TokenID,
74 LoadAmount: big.NewInt(int64(inst.Amount)),
75 Type: common.TxTypeCreateAccountDeposit,
76 }
77 batchL1Txs = append(batchL1Txs, &tx)
78 if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
79 accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
80
81 l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = accounts[idxTokenIDToString(inst.From, inst.TokenID)]
82 idx++
83 }
84 case common.TxTypeTransfer:
85 // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account
86 if _, ok := l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok {
87 tx := common.L1Tx{
88 FromEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
89 FromBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
90 TokenID: inst.TokenID,
91 LoadAmount: big.NewInt(int64(inst.Amount)),
92 Type: common.TxTypeCreateAccountDeposit,
93 }
94 accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx)
95 l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)]
96 batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, &tx)
97 idx++
98 }
99 toIdx := new(common.Idx)
100 *toIdx = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx
101 toEthAddr := new(ethCommon.Address)
102 *toEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
103 rqToEthAddr := new(ethCommon.Address)
104 *rqToEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
105 tx := common.PoolL2Tx{
106 FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
107 ToIdx: toIdx,
108 ToEthAddr: toEthAddr,
109 ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
110 TokenID: inst.TokenID,
111 Amount: big.NewInt(int64(inst.Amount)),
112 Fee: common.FeeSelector(inst.Fee),
113 Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
114 State: common.PoolL2TxStatePending,
115 Timestamp: time.Now(),
116 BatchNum: nil,
117 RqToEthAddr: rqToEthAddr,
118 RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
119 Type: common.TxTypeTransfer,
120 }
121 nTx, err := common.NewPoolL2Tx(&tx)
122 if err != nil {
123 panic(err)
124 }
125 tx = *nTx
126 // perform signature and set it to tx.Signature
127 toSign, err := tx.HashToSign()
128 if err != nil {
129 panic(err)
130 }
131 sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
132 tx.Signature = sig
133
134 accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
135 batchPoolL2Txs = append(batchPoolL2Txs, &tx)
136
137 case common.TxTypeExit, common.TxTypeForceExit:
138 fromIdx := new(common.Idx)
139 *fromIdx = accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx
140 tx := common.L1Tx{
141 FromIdx: fromIdx,
142 ToIdx: common.Idx(1), // as is an Exit
143 TokenID: inst.TokenID,
144 Amount: big.NewInt(int64(inst.Amount)),
145 Type: common.TxTypeExit,
146 }
147 batchL1Txs = append(batchL1Txs, &tx)
148 case TypeNewBatch:
149 l1Txs = append(l1Txs, batchL1Txs)
150 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
151 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
152 batchL1Txs = []*common.L1Tx{}
153 batchCoordinatorL1Txs = []*common.L1Tx{}
154 batchPoolL2Txs = []*common.PoolL2Tx{}
155 default:
156 continue
157 }
158 }
159 l1Txs = append(l1Txs, batchL1Txs)
160 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
161 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
162 tokens := []common.Token{}
163 for i := 0; i < len(poolL2Txs); i++ {
164 for j := 0; j < len(poolL2Txs[i]); j++ {
165 id := poolL2Txs[i][j].TokenID
166 found := false
167 for k := 0; k < len(tokens); k++ {
168 if tokens[k].TokenID == id {
169 found = true
170 break
171 }
172 }
173 if !found {
174 tokens = append(tokens, common.Token{
175 TokenID: id,
176 EthBlockNum: 1,
177 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(i*10000 + j))),
178 })
179 }
180 }
181 }
182 return l1Txs, coordinatorL1Txs, poolL2Txs, tokens
183}
184
185// GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of
186// Instructions code
187func GenerateTestTxsFromSet(t *testing.T, set string) ([][]*common.L1Tx, [][]*common.L1Tx, [][]*common.PoolL2Tx, []common.Token) {
188 parser := NewParser(strings.NewReader(set))
189 instructions, err := parser.Parse()
190 require.Nil(t, err)
191
192 return GenerateTestTxs(t, instructions)
193}