arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}