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	"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}