arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/eth/rollup.go
eth/rollup.goRaw
  1package eth
  2
  3import (
  4	"context"
  5	"fmt"
  6	"math/big"
  7	"strings"
  8
  9	"github.com/ethereum/go-ethereum"
 10	"github.com/ethereum/go-ethereum/accounts/abi"
 11	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/ethereum/go-ethereum/core/types"
 14	"github.com/ethereum/go-ethereum/crypto"
 15	"github.com/ethereum/go-ethereum/ethclient"
 16	"github.com/hermeznetwork/hermez-node/common"
 17	Hermez "github.com/hermeznetwork/hermez-node/eth/contracts/hermez"
 18	HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
 19	"github.com/hermeznetwork/hermez-node/log"
 20	"github.com/iden3/go-iden3-crypto/babyjub"
 21)
 22
 23// QueueStruct is the queue of L1Txs for a batch
 24type QueueStruct struct {
 25	L1TxQueue    []common.L1Tx
 26	TotalL1TxFee *big.Int
 27}
 28
 29// NewQueueStruct creates a new clear QueueStruct.
 30func NewQueueStruct() *QueueStruct {
 31	return &QueueStruct{
 32		L1TxQueue:    make([]common.L1Tx, 0),
 33		TotalL1TxFee: big.NewInt(0),
 34	}
 35}
 36
 37// RollupState represents the state of the Rollup in the Smart Contract
 38type RollupState struct {
 39	StateRoot *big.Int
 40	ExitRoots []*big.Int
 41	// ExitNullifierMap       map[[256 / 8]byte]bool
 42	ExitNullifierMap       map[int64]map[int64]bool // batchNum -> idx -> bool
 43	TokenList              []ethCommon.Address
 44	TokenMap               map[ethCommon.Address]bool
 45	MapL1TxQueue           map[int64]*QueueStruct
 46	LastL1L2Batch          int64
 47	CurrentToForgeL1TxsNum int64
 48	LastToForgeL1TxsNum    int64
 49	CurrentIdx             int64
 50}
 51
 52// RollupEventL1UserTx is an event of the Rollup Smart Contract
 53type RollupEventL1UserTx struct {
 54	// ToForgeL1TxsNum int64 // QueueIndex       *big.Int
 55	// Position        int   // TransactionIndex *big.Int
 56	L1UserTx common.L1Tx
 57}
 58
 59// RollupEventL1UserTxAux is an event of the Rollup Smart Contract
 60type RollupEventL1UserTxAux struct {
 61	ToForgeL1TxsNum uint64 // QueueIndex       *big.Int
 62	Position        uint8  // TransactionIndex *big.Int
 63	L1UserTx        []byte
 64}
 65
 66// RollupEventAddToken is an event of the Rollup Smart Contract
 67type RollupEventAddToken struct {
 68	TokenAddress ethCommon.Address
 69	TokenID      uint32
 70}
 71
 72// RollupEventForgeBatch is an event of the Rollup Smart Contract
 73type RollupEventForgeBatch struct {
 74	BatchNum int64
 75	// Sender    ethCommon.Address
 76	EthTxHash ethCommon.Hash
 77}
 78
 79// RollupEventUpdateForgeL1L2BatchTimeout is an event of the Rollup Smart Contract
 80type RollupEventUpdateForgeL1L2BatchTimeout struct {
 81	NewForgeL1L2BatchTimeout int64
 82}
 83
 84// RollupEventUpdateFeeAddToken is an event of the Rollup Smart Contract
 85type RollupEventUpdateFeeAddToken struct {
 86	NewFeeAddToken *big.Int
 87}
 88
 89// RollupEventWithdraw is an event of the Rollup Smart Contract
 90type RollupEventWithdraw struct {
 91	Idx             uint64
 92	NumExitRoot     uint64
 93	InstantWithdraw bool
 94	TxHash          ethCommon.Hash // Hash of the transaction that generated this event
 95}
 96
 97// RollupEvents is the list of events in a block of the Rollup Smart Contract
 98type RollupEvents struct {
 99	L1UserTx                    []RollupEventL1UserTx
100	AddToken                    []RollupEventAddToken
101	ForgeBatch                  []RollupEventForgeBatch
102	UpdateForgeL1L2BatchTimeout []RollupEventUpdateForgeL1L2BatchTimeout
103	UpdateFeeAddToken           []RollupEventUpdateFeeAddToken
104	Withdraw                    []RollupEventWithdraw
105}
106
107// NewRollupEvents creates an empty RollupEvents with the slices initialized.
108func NewRollupEvents() RollupEvents {
109	return RollupEvents{
110		L1UserTx:                    make([]RollupEventL1UserTx, 0),
111		AddToken:                    make([]RollupEventAddToken, 0),
112		ForgeBatch:                  make([]RollupEventForgeBatch, 0),
113		UpdateForgeL1L2BatchTimeout: make([]RollupEventUpdateForgeL1L2BatchTimeout, 0),
114		UpdateFeeAddToken:           make([]RollupEventUpdateFeeAddToken, 0),
115		Withdraw:                    make([]RollupEventWithdraw, 0),
116	}
117}
118
119// RollupForgeBatchArgs are the arguments to the ForgeBatch function in the Rollup Smart Contract
120type RollupForgeBatchArgs struct {
121	NewLastIdx            int64
122	NewStRoot             *big.Int
123	NewExitRoot           *big.Int
124	L1CoordinatorTxs      []common.L1Tx
125	L1CoordinatorTxsAuths [][]byte // Authorization for accountCreations for each L1CoordinatorTx
126	L2TxsData             []common.L2Tx
127	FeeIdxCoordinator     []common.Idx
128	// Circuit selector
129	VerifierIdx uint8
130	L1Batch     bool
131	ProofA      [2]*big.Int
132	ProofB      [2][2]*big.Int
133	ProofC      [2]*big.Int
134}
135
136// RollupForgeBatchArgsAux are the arguments to the ForgeBatch function in the Rollup Smart Contract
137type RollupForgeBatchArgsAux struct {
138	NewLastIdx             *big.Int
139	NewStRoot              *big.Int
140	NewExitRoot            *big.Int
141	EncodedL1CoordinatorTx []byte
142	L2TxsData              []byte
143	FeeIdxCoordinator      []byte
144	// Circuit selector
145	VerifierIdx uint8
146	L1Batch     bool
147	ProofA      [2]*big.Int
148	ProofB      [2][2]*big.Int
149	ProofC      [2]*big.Int
150}
151
152// RollupInterface is the inteface to to Rollup Smart Contract
153type RollupInterface interface {
154	//
155	// Smart Contract Methods
156	//
157
158	// Public Functions
159
160	RollupForgeBatch(*RollupForgeBatchArgs) (*types.Transaction, error)
161	RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (*types.Transaction, error)
162
163	RollupWithdrawMerkleProof(babyPubKey *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (*types.Transaction, error)
164	RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error)
165
166	RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error)
167	RollupL1UserTxERC20Permit(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error)
168
169	// Governance Public Functions
170	RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (*types.Transaction, error)
171	RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (*types.Transaction, error)
172
173	// Viewers
174	RollupRegisterTokensCount() (*big.Int, error)
175	RollupLastForgedBatch() (int64, error)
176
177	//
178	// Smart Contract Status
179	//
180
181	RollupConstants() (*common.RollupConstants, error)
182	RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error)
183	RollupForgeBatchArgs(ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error)
184}
185
186//
187// Implementation
188//
189
190// RollupClient is the implementation of the interface to the Rollup Smart Contract in ethereum.
191type RollupClient struct {
192	client      *EthereumClient
193	address     ethCommon.Address
194	tokenHEZCfg TokenConfig
195	hermez      *Hermez.Hermez
196	tokenHEZ    *HEZ.HEZ
197	contractAbi abi.ABI
198}
199
200// NewRollupClient creates a new RollupClient
201func NewRollupClient(client *EthereumClient, address ethCommon.Address, tokenHEZCfg TokenConfig) (*RollupClient, error) {
202	contractAbi, err := abi.JSON(strings.NewReader(string(Hermez.HermezABI)))
203	if err != nil {
204		return nil, err
205	}
206	hermez, err := Hermez.NewHermez(address, client.Client())
207	if err != nil {
208		return nil, err
209	}
210	tokenHEZ, err := HEZ.NewHEZ(tokenHEZCfg.Address, client.Client())
211	if err != nil {
212		return nil, err
213	}
214	return &RollupClient{
215		client:      client,
216		address:     address,
217		tokenHEZCfg: tokenHEZCfg,
218		hermez:      hermez,
219		tokenHEZ:    tokenHEZ,
220		contractAbi: contractAbi,
221	}, nil
222}
223
224// RollupForgeBatch is the interface to call the smart contract function
225func (c *RollupClient) RollupForgeBatch(args *RollupForgeBatchArgs) (tx *types.Transaction, err error) {
226	if tx, err = c.client.CallAuth(
227		1000000, //nolint:gomnd
228		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
229			rollupConst, err := c.RollupConstants()
230			if err != nil {
231				return nil, err
232			}
233			nLevels := rollupConst.Verifiers[args.VerifierIdx].NLevels
234			lenBytes := nLevels / 8 //nolint:gomnd
235			newLastIdx := big.NewInt(int64(args.NewLastIdx))
236			var l1CoordinatorBytes []byte
237			for i := 0; i < len(args.L1CoordinatorTxs); i++ {
238				l1 := args.L1CoordinatorTxs[i]
239				bytesl1, err := l1.BytesCoordinatorTx(args.L1CoordinatorTxsAuths[i])
240				if err != nil {
241					return nil, err
242				}
243				l1CoordinatorBytes = append(l1CoordinatorBytes, bytesl1[:]...)
244			}
245			var l2DataBytes []byte
246			for i := 0; i < len(args.L2TxsData); i++ {
247				l2 := args.L2TxsData[i]
248				bytesl2, err := l2.Bytes(uint32(nLevels))
249				if err != nil {
250					return nil, err
251				}
252				l2DataBytes = append(l2DataBytes, bytesl2[:]...)
253			}
254			var feeIdxCoordinator []byte
255			if len(args.FeeIdxCoordinator) > common.RollupConstMaxFeeIdxCoordinator {
256				return nil, fmt.Errorf("len(args.FeeIdxCoordinator) > %v",
257					common.RollupConstMaxFeeIdxCoordinator)
258			}
259			for i := 0; i < common.RollupConstMaxFeeIdxCoordinator; i++ {
260				feeIdx := common.Idx(0)
261				if i < len(args.FeeIdxCoordinator) {
262					feeIdx = args.FeeIdxCoordinator[i]
263				}
264				bytesFeeIdx, err := feeIdx.Bytes()
265				if err != nil {
266					return nil, err
267				}
268				feeIdxCoordinator = append(feeIdxCoordinator, bytesFeeIdx[len(bytesFeeIdx)-int(lenBytes):]...)
269			}
270			return c.hermez.ForgeBatch(auth, newLastIdx, args.NewStRoot, args.NewExitRoot, l1CoordinatorBytes, l2DataBytes, feeIdxCoordinator, args.VerifierIdx, args.L1Batch, args.ProofA, args.ProofB, args.ProofC)
271		},
272	); err != nil {
273		return nil, fmt.Errorf("Failed forge batch: %w", err)
274	}
275	return tx, nil
276}
277
278// RollupAddToken is the interface to call the smart contract function.
279// `feeAddToken` is the amount of HEZ tokens that will be paid to add the
280// token.  `feeAddToken` must match the public value of the smart contract.
281func (c *RollupClient) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (tx *types.Transaction, err error) {
282	if tx, err = c.client.CallAuth(
283		0,
284		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
285			owner := c.client.account.Address
286			spender := c.address
287			nonce, err := c.tokenHEZ.Nonces(nil, owner)
288			if err != nil {
289				return nil, err
290			}
291			tokenName := c.tokenHEZCfg.Name
292			tokenAddr := c.tokenHEZCfg.Address
293			chainid, _ := c.client.Client().ChainID(context.Background())
294			digest, _ := createPermitDigest(tokenAddr, owner, spender, chainid, feeAddToken, nonce, deadline, tokenName)
295			signature, _ := c.client.ks.SignHash(*c.client.account, digest)
296			permit := createPermit(owner, spender, feeAddToken, deadline, digest, signature)
297
298			return c.hermez.AddToken(auth, tokenAddress, permit)
299		},
300	); err != nil {
301		return nil, fmt.Errorf("Failed add Token %w", err)
302	}
303	return tx, nil
304}
305
306// RollupWithdrawMerkleProof is the interface to call the smart contract function
307func (c *RollupClient) RollupWithdrawMerkleProof(fromBJJ *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (tx *types.Transaction, err error) {
308	if tx, err = c.client.CallAuth(
309		0,
310		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
311			pkCompL := fromBJJ.Compress()
312			pkCompB := common.SwapEndianness(pkCompL[:])
313			babyPubKey := new(big.Int).SetBytes(pkCompB)
314			numExitRootB := big.NewInt(numExitRoot)
315			idxBig := big.NewInt(idx)
316			return c.hermez.WithdrawMerkleProof(auth, tokenID, amount, babyPubKey, numExitRootB, siblings, idxBig, instantWithdraw)
317		},
318	); err != nil {
319		return nil, fmt.Errorf("Failed update WithdrawMerkleProof: %w", err)
320	}
321	return tx, nil
322}
323
324// RollupWithdrawCircuit is the interface to call the smart contract function
325func (c *RollupClient) RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error) {
326	log.Error("TODO")
327	return nil, errTODO
328}
329
330// RollupL1UserTxERC20ETH is the interface to call the smart contract function
331func (c *RollupClient) RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
332	if tx, err = c.client.CallAuth(
333		0,
334		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
335			pkCompL := fromBJJ.Compress()
336			pkCompB := common.SwapEndianness(pkCompL[:])
337			babyPubKey := new(big.Int).SetBytes(pkCompB)
338			fromIdxBig := big.NewInt(fromIdx)
339			toIdxBig := big.NewInt(toIdx)
340			loadAmountF, err := common.NewFloat16(loadAmount)
341			if err != nil {
342				return nil, err
343			}
344			amountF, err := common.NewFloat16(amount)
345			if err != nil {
346				return nil, err
347			}
348			if tokenID == 0 {
349				auth.Value = loadAmount
350			}
351			var permit []byte
352			return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(loadAmountF),
353				uint16(amountF), tokenID, toIdxBig, permit)
354		},
355	); err != nil {
356		return nil, fmt.Errorf("Failed add L1 Tx ERC20/ETH: %w", err)
357	}
358	return tx, nil
359}
360
361// RollupL1UserTxERC20Permit is the interface to call the smart contract function
362func (c *RollupClient) RollupL1UserTxERC20Permit(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error) {
363	if tx, err = c.client.CallAuth(
364		0,
365		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
366			pkCompL := fromBJJ.Compress()
367			pkCompB := common.SwapEndianness(pkCompL[:])
368			babyPubKey := new(big.Int).SetBytes(pkCompB)
369			fromIdxBig := big.NewInt(fromIdx)
370			toIdxBig := big.NewInt(toIdx)
371			loadAmountF, err := common.NewFloat16(loadAmount)
372			if err != nil {
373				return nil, err
374			}
375			amountF, err := common.NewFloat16(amount)
376			if err != nil {
377				return nil, err
378			}
379			if tokenID == 0 {
380				auth.Value = loadAmount
381			}
382			owner := c.client.account.Address
383			spender := c.address
384			nonce, err := c.tokenHEZ.Nonces(nil, owner)
385			if err != nil {
386				return nil, err
387			}
388			tokenName := c.tokenHEZCfg.Name
389			tokenAddr := c.tokenHEZCfg.Address
390			chainid, _ := c.client.Client().ChainID(context.Background())
391			digest, _ := createPermitDigest(tokenAddr, owner, spender, chainid, amount, nonce, deadline, tokenName)
392			signature, _ := c.client.ks.SignHash(*c.client.account, digest)
393			permit := createPermit(owner, spender, amount, deadline, digest, signature)
394			return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(loadAmountF),
395				uint16(amountF), tokenID, toIdxBig, permit)
396		},
397	); err != nil {
398		return nil, fmt.Errorf("Failed add L1 Tx ERC20Permit: %w", err)
399	}
400	return tx, nil
401}
402
403// RollupRegisterTokensCount is the interface to call the smart contract function
404func (c *RollupClient) RollupRegisterTokensCount() (registerTokensCount *big.Int, err error) {
405	if err := c.client.Call(func(ec *ethclient.Client) error {
406		registerTokensCount, err = c.hermez.RegisterTokensCount(nil)
407		return err
408	}); err != nil {
409		return nil, err
410	}
411	return registerTokensCount, nil
412}
413
414// RollupLastForgedBatch is the interface to call the smart contract function
415func (c *RollupClient) RollupLastForgedBatch() (lastForgedBatch int64, err error) {
416	if err := c.client.Call(func(ec *ethclient.Client) error {
417		_lastForgedBatch, err := c.hermez.LastForgedBatch(nil)
418		lastForgedBatch = int64(_lastForgedBatch)
419		return err
420	}); err != nil {
421		return 0, err
422	}
423	return lastForgedBatch, nil
424}
425
426// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
427func (c *RollupClient) RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (tx *types.Transaction, err error) {
428	if tx, err = c.client.CallAuth(
429		0,
430		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
431			return c.hermez.UpdateForgeL1L2BatchTimeout(auth, uint8(newForgeL1L2BatchTimeout))
432		},
433	); err != nil {
434		return nil, fmt.Errorf("Failed update ForgeL1L2BatchTimeout: %w", err)
435	}
436	return tx, nil
437}
438
439// RollupUpdateFeeAddToken is the interface to call the smart contract function
440func (c *RollupClient) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
441	if tx, err = c.client.CallAuth(
442		0,
443		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
444			return c.hermez.UpdateFeeAddToken(auth, newFeeAddToken)
445		},
446	); err != nil {
447		return nil, fmt.Errorf("Failed update FeeAddToken: %w", err)
448	}
449	return tx, nil
450}
451
452// RollupConstants returns the Constants of the Rollup Smart Contract
453func (c *RollupClient) RollupConstants() (rollupConstants *common.RollupConstants, err error) {
454	rollupConstants = new(common.RollupConstants)
455	if err := c.client.Call(func(ec *ethclient.Client) error {
456		absoluteMaxL1L2BatchTimeout, err := c.hermez.ABSOLUTEMAXL1L2BATCHTIMEOUT(nil)
457		if err != nil {
458			return err
459		}
460		rollupConstants.AbsoluteMaxL1L2BatchTimeout = int64(absoluteMaxL1L2BatchTimeout)
461		rollupConstants.TokenHEZ, err = c.hermez.TokenHEZ(nil)
462		if err != nil {
463			return err
464		}
465		for i := int64(0); i < int64(common.LenVerifiers); i++ {
466			var newRollupVerifier common.RollupVerifierStruct
467			rollupVerifier, err := c.hermez.RollupVerifiers(nil, big.NewInt(i))
468			if err != nil {
469				return err
470			}
471			newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
472			newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
473			rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
474		}
475		rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(nil)
476		if err != nil {
477			return err
478		}
479		rollupConstants.HermezGovernanceDAOAddress, err = c.hermez.HermezGovernanceDAOAddress(nil)
480		if err != nil {
481			return err
482		}
483		rollupConstants.SafetyAddress, err = c.hermez.SafetyAddress(nil)
484		if err != nil {
485			return err
486		}
487		rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(nil)
488		return err
489	}); err != nil {
490		return nil, err
491	}
492	return rollupConstants, nil
493}
494
495var (
496	logHermezL1UserTxEvent               = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint64,uint8,bytes)"))
497	logHermezAddToken                    = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
498	logHermezForgeBatch                  = crypto.Keccak256Hash([]byte("ForgeBatch(uint64)"))
499	logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
500	logHermezUpdateFeeAddToken           = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
501	logHermezWithdrawEvent               = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint48,bool)"))
502)
503
504// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
505func (c *RollupClient) RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error) {
506	var rollupEvents RollupEvents
507	var blockHash *ethCommon.Hash
508
509	query := ethereum.FilterQuery{
510		FromBlock: big.NewInt(blockNum),
511		ToBlock:   big.NewInt(blockNum),
512		Addresses: []ethCommon.Address{
513			c.address,
514		},
515		BlockHash: nil,
516		Topics:    [][]ethCommon.Hash{},
517	}
518	logs, err := c.client.client.FilterLogs(context.Background(), query)
519	if err != nil {
520		return nil, nil, err
521	}
522	if len(logs) > 0 {
523		blockHash = &logs[0].BlockHash
524	}
525	for _, vLog := range logs {
526		if vLog.BlockHash != *blockHash {
527			log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
528			return nil, nil, ErrBlockHashMismatchEvent
529		}
530		switch vLog.Topics[0] {
531		case logHermezL1UserTxEvent:
532			var L1UserTxAux RollupEventL1UserTxAux
533			var L1UserTx RollupEventL1UserTx
534			err := c.contractAbi.Unpack(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
535			if err != nil {
536				return nil, nil, err
537			}
538			L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
539			if err != nil {
540				return nil, nil, err
541			}
542			toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
543			L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
544			L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
545			L1Tx.UserOrigin = true
546			L1UserTx.L1UserTx = *L1Tx
547			rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
548		case logHermezAddToken:
549			var addToken RollupEventAddToken
550			err := c.contractAbi.Unpack(&addToken, "AddToken", vLog.Data)
551			if err != nil {
552				return nil, nil, err
553			}
554			addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
555			rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
556		case logHermezForgeBatch:
557			var forgeBatch RollupEventForgeBatch
558			forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
559			forgeBatch.EthTxHash = vLog.TxHash
560			// forgeBatch.Sender = vLog.Address
561			rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
562		case logHermezUpdateForgeL1L2BatchTimeout:
563			var updateForgeL1L2BatchTimeout struct {
564				NewForgeL1L2BatchTimeout uint8
565			}
566			err := c.contractAbi.Unpack(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
567			if err != nil {
568				return nil, nil, err
569			}
570			rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
571				RollupEventUpdateForgeL1L2BatchTimeout{
572					NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
573				})
574		case logHermezUpdateFeeAddToken:
575			var updateFeeAddToken RollupEventUpdateFeeAddToken
576			err := c.contractAbi.Unpack(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
577			if err != nil {
578				return nil, nil, err
579			}
580			rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
581		case logHermezWithdrawEvent:
582			var withdraw RollupEventWithdraw
583			withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
584			withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
585			instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
586			if instantWithdraw == 1 {
587				withdraw.InstantWithdraw = true
588			}
589			withdraw.TxHash = vLog.TxHash
590			rollupEvents.Withdraw = append(rollupEvents.Withdraw, withdraw)
591		}
592	}
593	return &rollupEvents, blockHash, nil
594}
595
596// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
597// Rollup Smart Contract in the given transaction, and the sender address.
598func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
599	tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
600	if err != nil {
601		return nil, nil, err
602	}
603	txData := tx.Data()
604	method, err := c.contractAbi.MethodById(txData[:4])
605	if err != nil {
606		return nil, nil, err
607	}
608	receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
609	if err != nil {
610		return nil, nil, err
611	}
612	sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
613	if err != nil {
614		return nil, nil, err
615	}
616	var aux RollupForgeBatchArgsAux
617	if err := method.Inputs.Unpack(&aux, txData[4:]); err != nil {
618		return nil, nil, err
619	}
620	rollupForgeBatchArgs := RollupForgeBatchArgs{
621		L1Batch:               aux.L1Batch,
622		NewExitRoot:           aux.NewExitRoot,
623		NewLastIdx:            aux.NewLastIdx.Int64(),
624		NewStRoot:             aux.NewStRoot,
625		ProofA:                aux.ProofA,
626		ProofB:                aux.ProofB,
627		ProofC:                aux.ProofC,
628		VerifierIdx:           aux.VerifierIdx,
629		L1CoordinatorTxs:      []common.L1Tx{},
630		L1CoordinatorTxsAuths: [][]byte{},
631		L2TxsData:             []common.L2Tx{},
632		FeeIdxCoordinator:     []common.Idx{},
633	}
634	numTxsL1 := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
635	for i := 0; i < numTxsL1; i++ {
636		bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
637		var signature []byte
638		v := bytesL1Coordinator[0]
639		s := bytesL1Coordinator[1:33]
640		r := bytesL1Coordinator[33:65]
641		signature = append(signature, r[:]...)
642		signature = append(signature, s[:]...)
643		signature = append(signature, v)
644		l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator)
645		if err != nil {
646			return nil, nil, err
647		}
648		rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
649		rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
650	}
651	rollupConsts, err := c.RollupConstants()
652	if err != nil {
653		return nil, nil, err
654	}
655	nLevels := rollupConsts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
656	lenL2TxsBytes := int((nLevels/8)*2 + 2 + 1)
657	numTxsL2 := len(aux.L2TxsData) / lenL2TxsBytes
658	for i := 0; i < numTxsL2; i++ {
659		l2Tx, err := common.L2TxFromBytes(aux.L2TxsData[i*lenL2TxsBytes:(i+1)*lenL2TxsBytes], int(nLevels))
660		if err != nil {
661			return nil, nil, err
662		}
663		rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
664	}
665	lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
666	numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
667	for i := 0; i < numFeeIdxCoordinator; i++ {
668		var paddedFeeIdx [6]byte
669		// TODO: This check is not necessary: the first case will always work.  Test it before removing the if.
670		if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
671			copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
672		} else {
673			copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
674		}
675		feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
676		if err != nil {
677			return nil, nil, err
678		}
679		if feeIdxCoordinator != common.Idx(0) {
680			rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
681		}
682	}
683	return &rollupForgeBatchArgs, &sender, nil
684}