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