hermez-node/test/historydb.go
test/historydb.goRaw
1package test
2
3import (
4 "errors"
5 "fmt"
6 "math/big"
7 "time"
8
9 ethCommon "github.com/ethereum/go-ethereum/common"
10 "github.com/hermeznetwork/hermez-node/common"
11 "github.com/iden3/go-iden3-crypto/babyjub"
12 "github.com/iden3/go-merkletree"
13)
14
15// Block0 represents Ethereum's genesis block,
16// which is stored by default at HistoryDB
17var Block0 common.Block = common.Block{
18 Num: 0,
19 Hash: ethCommon.Hash([32]byte{
20 212, 229, 103, 64, 248, 118, 174, 248,
21 192, 16, 184, 106, 64, 213, 245, 103,
22 69, 161, 24, 208, 144, 106, 52, 230,
23 154, 236, 140, 13, 177, 203, 143, 163,
24 }), // 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3
25 Timestamp: time.Date(2015, time.July, 30, 3, 26, 13, 0, time.UTC), // 2015-07-30 03:26:13
26}
27
28// EthToken represents the Ether coin, which is stored by default in the DB
29// with TokenID = 0
30var EthToken common.Token = common.Token{
31 TokenID: 0,
32 Name: "Ether",
33 Symbol: "ETH",
34 Decimals: 18, //nolint:gomnd
35 EthBlockNum: 0,
36 EthAddr: ethCommon.BigToAddress(big.NewInt(0)),
37}
38
39// WARNING: the generators in this file doesn't necessary follow the protocol
40// they are intended to check that the parsers between struct <==> DB are correct
41
42// GenBlocks generates block from, to block numbers. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
43func GenBlocks(from, to int64) []common.Block {
44 var blocks []common.Block
45 for i := from; i < to; i++ {
46 blocks = append(blocks, common.Block{
47 Num: i,
48 //nolint:gomnd
49 Timestamp: time.Now().Add(time.Second * 13).UTC(),
50 Hash: ethCommon.BigToHash(big.NewInt(int64(i))),
51 })
52 }
53 return blocks
54}
55
56// GenTokens generates tokens. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
57func GenTokens(nTokens int, blocks []common.Block) (tokensToAddInDB []common.Token, ethToken common.Token) {
58 tokensToAddInDB = []common.Token{}
59 for i := 1; i < nTokens; i++ {
60 token := common.Token{
61 TokenID: common.TokenID(i),
62 Name: "NAME" + fmt.Sprint(i),
63 Symbol: fmt.Sprint(i),
64 Decimals: uint64(i + 1),
65 EthBlockNum: blocks[i%len(blocks)].Num,
66 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(i))),
67 }
68 tokensToAddInDB = append(tokensToAddInDB, token)
69 }
70 return tokensToAddInDB, common.Token{
71 TokenID: 0,
72 Name: "Ether",
73 Symbol: "ETH",
74 Decimals: 18, //nolint:gomnd
75 EthBlockNum: 0,
76 EthAddr: ethCommon.BigToAddress(big.NewInt(0)),
77 }
78}
79
80// GenBatches generates batches. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
81func GenBatches(nBatches int, blocks []common.Block) []common.Batch {
82 batches := []common.Batch{}
83 collectedFees := make(map[common.TokenID]*big.Int)
84 for i := 0; i < 64; i++ {
85 collectedFees[common.TokenID(i)] = big.NewInt(int64(i))
86 }
87 for i := 0; i < nBatches; i++ {
88 batch := common.Batch{
89 BatchNum: common.BatchNum(i + 1),
90 EthBlockNum: blocks[i%len(blocks)].Num,
91 //nolint:gomnd
92 ForgerAddr: ethCommon.BigToAddress(big.NewInt(6886723)),
93 CollectedFees: collectedFees,
94 StateRoot: big.NewInt(int64(i+1) * 5), //nolint:gomnd
95 //nolint:gomnd
96 NumAccounts: 30,
97 ExitRoot: big.NewInt(int64(i+1) * 16), //nolint:gomnd
98 SlotNum: int64(i),
99 }
100 if i%2 == 0 {
101 toForge := new(int64)
102 *toForge = int64(i + 1)
103 batch.ForgeL1TxsNum = toForge
104 }
105 batches = append(batches, batch)
106 }
107 return batches
108}
109
110// GenAccounts generates accounts. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
111func GenAccounts(totalAccounts, userAccounts int, tokens []common.Token, userAddr *ethCommon.Address, userBjj *babyjub.PublicKey, batches []common.Batch) []common.Account {
112 if totalAccounts < userAccounts {
113 panic("totalAccounts must be greater than userAccounts")
114 }
115 accs := []common.Account{}
116 for i := 256; i < 256+totalAccounts; i++ {
117 var addr ethCommon.Address
118 var pubK *babyjub.PublicKey
119 if i < 256+userAccounts {
120 addr = *userAddr
121 pubK = userBjj
122 } else {
123 addr = ethCommon.BigToAddress(big.NewInt(int64(i)))
124 privK := babyjub.NewRandPrivKey()
125 pubK = privK.Public()
126 }
127 accs = append(accs, common.Account{
128 Idx: common.Idx(i),
129 TokenID: tokens[i%len(tokens)].TokenID,
130 EthAddr: addr,
131 BatchNum: batches[i%len(batches)].BatchNum,
132 PublicKey: pubK,
133 Balance: big.NewInt(int64(i * 10000000)), //nolint:gomnd
134 })
135 }
136 return accs
137}
138
139// GenL1Txs generates L1 txs. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
140func GenL1Txs(
141 fromIdx int,
142 totalTxs, nUserTxs int,
143 userAddr *ethCommon.Address,
144 accounts []common.Account,
145 tokens []common.Token,
146 blocks []common.Block,
147 batches []common.Batch,
148) ([]common.L1Tx, []common.L1Tx) {
149 if totalTxs < nUserTxs {
150 panic("totalTxs must be greater than userTxs")
151 }
152 userTxs := []common.L1Tx{}
153 othersTxs := []common.L1Tx{}
154 _, nextTxsNum := GetNextToForgeNumAndBatch(batches)
155 for i := fromIdx; i < fromIdx+totalTxs; i++ {
156 token := tokens[i%len(tokens)]
157 amount := big.NewInt(int64(i + 1))
158 tx := common.L1Tx{
159 Position: i - fromIdx,
160 UserOrigin: i%2 == 0,
161 TokenID: token.TokenID,
162 Amount: amount,
163 LoadAmount: amount,
164 EthBlockNum: blocks[i%len(blocks)].Num,
165 }
166 if tx.UserOrigin {
167 n := nextTxsNum
168 tx.ToForgeL1TxsNum = &n
169 } else {
170 tx.BatchNum = &batches[i%len(batches)].BatchNum
171 }
172 nTx, err := common.NewL1Tx(&tx)
173 if err != nil {
174 panic(err)
175 }
176 tx = *nTx
177 if !tx.UserOrigin {
178 tx.BatchNum = &batches[i%len(batches)].BatchNum
179 } else if batches[i%len(batches)].ForgeL1TxsNum != nil {
180 // Add already forged txs
181 tx.BatchNum = &batches[i%len(batches)].BatchNum
182 setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs)
183 } else {
184 // Add unforged txs
185 n := nextTxsNum
186 tx.ToForgeL1TxsNum = &n
187 tx.UserOrigin = true
188 setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs)
189 }
190 }
191 return userTxs, othersTxs
192}
193
194// GetNextToForgeNumAndBatch returns the next BatchNum and ForgeL1TxsNum to be added
195func GetNextToForgeNumAndBatch(batches []common.Batch) (common.BatchNum, int64) {
196 batchNum := batches[len(batches)-1].BatchNum + 1
197 var toForgeL1TxsNum int64
198 found := false
199 for i := len(batches) - 1; i >= 0; i-- {
200 if batches[i].ForgeL1TxsNum != nil {
201 toForgeL1TxsNum = *batches[i].ForgeL1TxsNum + 1
202 found = true
203 break
204 }
205 }
206 if !found {
207 panic("toForgeL1TxsNum not found")
208 }
209 return batchNum, toForgeL1TxsNum
210}
211
212func setFromToAndAppend(
213 fromIdx int,
214 tx common.L1Tx,
215 i, nUserTxs int,
216 userAddr *ethCommon.Address,
217 accounts []common.Account,
218 userTxs *[]common.L1Tx,
219 othersTxs *[]common.L1Tx,
220) {
221 if i < fromIdx+nUserTxs {
222 var from, to *common.Account
223 var err error
224 if i%2 == 0 {
225 from, err = randomAccount(i, true, userAddr, accounts)
226 if err != nil {
227 panic(err)
228 }
229 to, err = randomAccount(i, false, userAddr, accounts)
230 if err != nil {
231 panic(err)
232 }
233 } else {
234 from, err = randomAccount(i, false, userAddr, accounts)
235 if err != nil {
236 panic(err)
237 }
238 to, err = randomAccount(i, true, userAddr, accounts)
239 if err != nil {
240 panic(err)
241 }
242 }
243 tx.FromIdx = from.Idx
244 tx.FromEthAddr = from.EthAddr
245 tx.FromBJJ = from.PublicKey
246 tx.ToIdx = to.Idx
247 *userTxs = append(*userTxs, tx)
248 } else {
249 from, err := randomAccount(i, false, userAddr, accounts)
250 if err != nil {
251 panic(err)
252 }
253 to, err := randomAccount(i, false, userAddr, accounts)
254 if err != nil {
255 panic(err)
256 }
257 tx.FromIdx = from.Idx
258 tx.FromEthAddr = from.EthAddr
259 tx.FromBJJ = from.PublicKey
260 tx.ToIdx = to.Idx
261 *othersTxs = append(*othersTxs, tx)
262 }
263}
264
265// GenL2Txs generates L2 txs. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
266func GenL2Txs(
267 fromIdx int,
268 totalTxs, nUserTxs int,
269 userAddr *ethCommon.Address,
270 accounts []common.Account,
271 tokens []common.Token,
272 blocks []common.Block,
273 batches []common.Batch,
274) ([]common.L2Tx, []common.L2Tx) {
275 if totalTxs < nUserTxs {
276 panic("totalTxs must be greater than userTxs")
277 }
278 userTxs := []common.L2Tx{}
279 othersTxs := []common.L2Tx{}
280 for i := fromIdx; i < fromIdx+totalTxs; i++ {
281 amount := big.NewInt(int64(i + 1))
282 fee := common.FeeSelector(i % 256) //nolint:gomnd
283 tx := common.L2Tx{
284 TxID: common.TxID([12]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i)}), // only for testing purposes
285 BatchNum: batches[i%len(batches)].BatchNum,
286 Position: i - fromIdx,
287 Amount: amount,
288 Fee: fee,
289 Nonce: common.Nonce(i + 1),
290 EthBlockNum: blocks[i%len(blocks)].Num,
291 Type: randomTxType(i),
292 }
293 if i < nUserTxs {
294 var from, to *common.Account
295 var err error
296 if i%2 == 0 {
297 from, err = randomAccount(i, true, userAddr, accounts)
298 if err != nil {
299 panic(err)
300 }
301 to, err = randomAccount(i, false, userAddr, accounts)
302 if err != nil {
303 panic(err)
304 }
305 } else {
306 from, err = randomAccount(i, false, userAddr, accounts)
307 if err != nil {
308 panic(err)
309 }
310 to, err = randomAccount(i, true, userAddr, accounts)
311 if err != nil {
312 panic(err)
313 }
314 }
315 tx.FromIdx = from.Idx
316 tx.ToIdx = to.Idx
317 } else {
318 from, err := randomAccount(i, false, userAddr, accounts)
319 if err != nil {
320 panic(err)
321 }
322 to, err := randomAccount(i, false, userAddr, accounts)
323 if err != nil {
324 panic(err)
325 }
326 tx.FromIdx = from.Idx
327 tx.ToIdx = to.Idx
328 }
329
330 if i < nUserTxs {
331 userTxs = append(userTxs, tx)
332 } else {
333 othersTxs = append(othersTxs, tx)
334 }
335 }
336 return userTxs, othersTxs
337}
338
339// GenCoordinators generates coordinators. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
340func GenCoordinators(nCoords int, blocks []common.Block) []common.Coordinator {
341 coords := []common.Coordinator{}
342 for i := 0; i < nCoords; i++ {
343 coords = append(coords, common.Coordinator{
344 EthBlockNum: blocks[i%len(blocks)].Num,
345 Forger: ethCommon.BigToAddress(big.NewInt(int64(i))),
346 Bidder: ethCommon.BigToAddress(big.NewInt(int64(i))),
347 URL: "https://foo.bar",
348 })
349 }
350 return coords
351}
352
353// GenBids generates bids. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
354func GenBids(nBids int, blocks []common.Block, coords []common.Coordinator) []common.Bid {
355 bids := []common.Bid{}
356 for i := 0; i < nBids*2; i = i + 2 { //nolint:gomnd
357 var slotNum int64
358 if i < nBids {
359 slotNum = int64(i)
360 } else {
361 slotNum = int64(i - nBids)
362 }
363 bids = append(bids, common.Bid{
364 SlotNum: slotNum,
365 BidValue: big.NewInt(int64(i)),
366 EthBlockNum: blocks[i%len(blocks)].Num,
367 Bidder: coords[i%len(blocks)].Bidder,
368 })
369 }
370 return bids
371}
372
373// GenExitTree generates an exitTree (as an array of Exits)
374//nolint:gomnd
375func GenExitTree(n int, batches []common.Batch, accounts []common.Account, blocks []common.Block) []common.ExitInfo {
376 exitTree := make([]common.ExitInfo, n)
377 for i := 0; i < n; i++ {
378 exitTree[i] = common.ExitInfo{
379 BatchNum: batches[i%len(batches)].BatchNum,
380 InstantWithdrawn: nil,
381 DelayedWithdrawRequest: nil,
382 DelayedWithdrawn: nil,
383 AccountIdx: accounts[i%len(accounts)].Idx,
384 MerkleProof: &merkletree.CircomVerifierProof{
385 Root: &merkletree.Hash{byte(i), byte(i + 1)},
386 Siblings: []*merkletree.Hash{
387 merkletree.NewHashFromBigInt(big.NewInt(int64(i) * 10)),
388 merkletree.NewHashFromBigInt(big.NewInt(int64(i)*100 + 1)),
389 merkletree.NewHashFromBigInt(big.NewInt(int64(i)*1000 + 2))},
390 OldKey: &merkletree.Hash{byte(i * 1), byte(i*1 + 1)},
391 OldValue: &merkletree.Hash{byte(i * 2), byte(i*2 + 1)},
392 IsOld0: i%2 == 0,
393 Key: &merkletree.Hash{byte(i * 3), byte(i*3 + 1)},
394 Value: &merkletree.Hash{byte(i * 4), byte(i*4 + 1)},
395 Fnc: i % 2,
396 },
397 Balance: big.NewInt(int64(i) * 1000),
398 }
399 if i%2 == 0 {
400 instant := int64(blocks[i%len(blocks)].Num)
401 exitTree[i].InstantWithdrawn = &instant
402 } else if i%3 == 0 {
403 delayedReq := int64(blocks[i%len(blocks)].Num)
404 exitTree[i].DelayedWithdrawRequest = &delayedReq
405 if i%9 == 0 {
406 delayed := int64(blocks[i%len(blocks)].Num)
407 exitTree[i].DelayedWithdrawn = &delayed
408 }
409 }
410 }
411 return exitTree
412}
413
414func randomAccount(seed int, userAccount bool, userAddr *ethCommon.Address, accs []common.Account) (*common.Account, error) {
415 i := seed % len(accs)
416 firstI := i
417 for {
418 acc := accs[i]
419 if userAccount && *userAddr == acc.EthAddr {
420 return &acc, nil
421 }
422 if !userAccount && (userAddr == nil || *userAddr != acc.EthAddr) {
423 return &acc, nil
424 }
425 i++
426 i = i % len(accs)
427 if i == firstI {
428 return &acc, errors.New("Didnt found any account matchinng the criteria")
429 }
430 }
431}
432
433func randomTxType(seed int) common.TxType {
434 //nolint:gomnd
435 switch seed % 11 {
436 case 0:
437 return common.TxTypeExit
438 //nolint:gomnd
439 case 2:
440 return common.TxTypeTransfer
441 //nolint:gomnd
442 case 3:
443 return common.TxTypeDeposit
444 //nolint:gomnd
445 case 4:
446 return common.TxTypeCreateAccountDeposit
447 //nolint:gomnd
448 case 5:
449 return common.TxTypeCreateAccountDepositTransfer
450 //nolint:gomnd
451 case 6:
452 return common.TxTypeDepositTransfer
453 //nolint:gomnd
454 case 7:
455 return common.TxTypeForceTransfer
456 //nolint:gomnd
457 case 8:
458 return common.TxTypeForceExit
459 //nolint:gomnd
460 case 9:
461 return common.TxTypeTransferToEthAddr
462 //nolint:gomnd
463 case 10:
464 return common.TxTypeTransferToBJJ
465 default:
466 return common.TxTypeTransfer
467 }
468}