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