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