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
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 ethCrypto "github.com/ethereum/go-ethereum/crypto"
12 "github.com/hermeznetwork/hermez-node/common"
13 "github.com/iden3/go-iden3-crypto/babyjub"
14 "github.com/stretchr/testify/require"
15)
16
17// Account contains the data related to a testing account
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, []common.Token) {
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 := 256
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 Amount: big.NewInt(0),
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 tx := common.PoolL2Tx{
100 FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
101 ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx,
102 ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
103 ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
104 TokenID: inst.TokenID,
105 Amount: big.NewInt(int64(inst.Amount)),
106 Fee: common.FeeSelector(inst.Fee),
107 Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
108 State: common.PoolL2TxStatePending,
109 RqToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
110 RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
111 Type: common.TxTypeTransfer,
112 }
113 nTx, err := common.NewPoolL2Tx(&tx)
114 if err != nil {
115 panic(err)
116 }
117 tx = *nTx
118 // perform signature and set it to tx.Signature
119 toSign, err := tx.HashToSign()
120 if err != nil {
121 panic(err)
122 }
123 sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
124 tx.Signature = sig
125
126 accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
127 batchPoolL2Txs = append(batchPoolL2Txs, tx)
128
129 case common.TxTypeExit, common.TxTypeForceExit:
130 tx := common.L1Tx{
131 FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
132 ToIdx: common.Idx(1), // as is an Exit
133 TokenID: inst.TokenID,
134 Amount: big.NewInt(int64(inst.Amount)),
135 Type: common.TxTypeExit,
136 }
137 batchL1Txs = append(batchL1Txs, tx)
138 case TypeNewBatch:
139 l1Txs = append(l1Txs, batchL1Txs)
140 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
141 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
142 batchL1Txs = []common.L1Tx{}
143 batchCoordinatorL1Txs = []common.L1Tx{}
144 batchPoolL2Txs = []common.PoolL2Tx{}
145 default:
146 continue
147 }
148 }
149 l1Txs = append(l1Txs, batchL1Txs)
150 coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
151 poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
152 tokens := []common.Token{}
153 for i := 0; i < len(poolL2Txs); i++ {
154 for j := 0; j < len(poolL2Txs[i]); j++ {
155 id := poolL2Txs[i][j].TokenID
156 found := false
157 for k := 0; k < len(tokens); k++ {
158 if tokens[k].TokenID == id {
159 found = true
160 break
161 }
162 }
163 if !found {
164 tokens = append(tokens, common.Token{
165 TokenID: id,
166 EthBlockNum: 1,
167 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(i*10000 + j))),
168 })
169 }
170 }
171 }
172 return l1Txs, coordinatorL1Txs, poolL2Txs, tokens
173}
174
175// GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of
176// Instructions code
177func GenerateTestTxsFromSet(t *testing.T, set string) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
178 parser := NewParser(strings.NewReader(set))
179 instructions, err := parser.Parse()
180 require.Nil(t, err)
181
182 return GenerateTestTxs(t, instructions)
183}