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