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