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