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 ERC777 "github.com/hermeznetwork/hermez-node/eth/contracts/erc777"
18 Hermez "github.com/hermeznetwork/hermez-node/eth/contracts/hermez"
19 "github.com/hermeznetwork/hermez-node/log"
20 "github.com/iden3/go-iden3-crypto/babyjub"
21 "golang.org/x/crypto/sha3"
22)
23
24const (
25 // RollupConstMaxFeeIdxCoordinator is the maximum number of tokens the
26 // coordinator can use to collect fees (determines the number of tokens
27 // that the coordinator can collect fees from). This value is
28 // determined by the circuit.
29 RollupConstMaxFeeIdxCoordinator = 64
30 // RollupConstReservedIDx First 256 indexes reserved, first user index will be the 256
31 RollupConstReservedIDx = 255
32 // RollupConstExitIDx IDX 1 is reserved for exits
33 RollupConstExitIDx = 1
34 // RollupConstLimitLoadAmount Max load amount allowed (loadAmount: L1 --> L2)
35 RollupConstLimitLoadAmount = (1 << 128)
36 // RollupConstLimitL2TransferAmount Max amount allowed (amount L2 --> L2)
37 RollupConstLimitL2TransferAmount = (1 << 192)
38 // RollupConstLimitTokens Max number of tokens allowed to be registered inside the rollup
39 RollupConstLimitTokens = (1 << 32)
40 // RollupConstL1CoordinatorTotalBytes [4 bytes] token + [32 bytes] babyjub + [65 bytes] compressedSignature
41 RollupConstL1CoordinatorTotalBytes = 101
42 // RollupConstL1UserTotalBytes [20 bytes] fromEthAddr + [32 bytes] fromBjj-compressed + [6 bytes] fromIdx +
43 // [2 bytes] loadAmountFloat16 + [2 bytes] amountFloat16 + [4 bytes] tokenId + [6 bytes] toIdx
44 RollupConstL1UserTotalBytes = 72
45 // RollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch
46 RollupConstMaxL1UserTx = 128
47 // RollupConstMaxL1Tx Maximum L1 transactions allowed to be queued in a batch
48 RollupConstMaxL1Tx = 256
49 // RollupConstInputSHAConstantBytes [6 bytes] lastIdx + [6 bytes] newLastIdx + [32 bytes] stateRoot + [32 bytes] newStRoot + [32 bytes] newExitRoot +
50 // [_MAX_L1_TX * _L1_USER_TOTALBYTES bytes] l1TxsData + totalL2TxsDataLength + feeIdxCoordinatorLength + [2 bytes] chainID =
51 // 18542 bytes + totalL2TxsDataLength + feeIdxCoordinatorLength
52 RollupConstInputSHAConstantBytes = 18542
53 // RollupConstNumBuckets Number of buckets
54 RollupConstNumBuckets = 5
55 // RollupConstMaxWithdrawalDelay max withdrawal delay in seconds
56 RollupConstMaxWithdrawalDelay = 2 * 7 * 24 * 60 * 60
57 // RollupConstExchangeMultiplier exchange multiplier
58 RollupConstExchangeMultiplier = 1e14
59 // LenVerifiers number of Rollup Smart Contract Verifiers
60 LenVerifiers = 1
61)
62
63var (
64 // RollupConstEthAddressInternalOnly This ethereum address is used internally for rollup accounts that don't have ethereum address, only Babyjubjub
65 // This non-ethereum accounts can be created by the coordinator and allow users to have a rollup
66 // account without needing an ethereum address
67 RollupConstEthAddressInternalOnly = ethCommon.HexToAddress("0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF")
68 // RollupConstRfield Modulus zkSNARK
69 RollupConstRfield, _ = new(big.Int).SetString(
70 "21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
71
72 // RollupConstERC1820 ERC1820Registry address
73 RollupConstERC1820 = ethCommon.HexToAddress("0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24")
74
75 // ERC777 tokens signatures
76
77 // RollupConstRecipientInterfaceHash ERC777 recipient interface hash
78 RollupConstRecipientInterfaceHash = crypto.Keccak256([]byte("ERC777TokensRecipient"))
79 // RollupConstPerformL1UserTxSignature the signature of the function that can be called thru an ERC777 `send`
80 RollupConstPerformL1UserTxSignature = crypto.Keccak256([]byte("addL1Transaction(uint256,uint48,uint16,uint16,uint32,uint48)"))
81 // RollupConstAddTokenSignature the signature of the function that can be called thru an ERC777 `send`
82 RollupConstAddTokenSignature = crypto.Keccak256([]byte("addToken(address)"))
83 // RollupConstSendSignature ERC777 Signature
84 RollupConstSendSignature = crypto.Keccak256([]byte("send(address,uint256,bytes)"))
85 // RollupConstERC777Granularity ERC777 Signature
86 RollupConstERC777Granularity = crypto.Keccak256([]byte("granularity()"))
87 // RollupConstWithdrawalDelayerDeposit This constant are used to deposit tokens from ERC77 tokens into withdrawal delayer
88 RollupConstWithdrawalDelayerDeposit = crypto.Keccak256([]byte("deposit(address,address,uint192)"))
89
90 // ERC20 signature
91
92 // RollupConstTransferSignature This constant is used in the _safeTransfer internal method in order to safe GAS.
93 RollupConstTransferSignature = crypto.Keccak256([]byte("transfer(address,uint256)"))
94 // RollupConstTransferFromSignature This constant is used in the _safeTransfer internal method in order to safe GAS.
95 RollupConstTransferFromSignature = crypto.Keccak256([]byte("transferFrom(address,address,uint256)"))
96 // RollupConstApproveSignature This constant is used in the _safeTransfer internal method in order to safe GAS.
97 RollupConstApproveSignature = crypto.Keccak256([]byte("approve(address,uint256)"))
98 // RollupConstERC20Signature ERC20 decimals signature
99 RollupConstERC20Signature = crypto.Keccak256([]byte("decimals()"))
100)
101
102// RollupPublicConstants are the constants of the Rollup Smart Contract
103type RollupPublicConstants struct {
104 AbsoluteMaxL1L2BatchTimeout int64 `json:"absoluteMaxL1L2BatchTimeout"`
105 TokenHEZ ethCommon.Address `json:"tokenHEZ"`
106 Verifiers []RollupVerifierStruct `json:"verifiers"`
107 HermezAuctionContract ethCommon.Address `json:"hermezAuctionContract"`
108 HermezGovernanceDAOAddress ethCommon.Address `json:"hermezGovernanceDAOAddress"`
109 SafetyAddress ethCommon.Address `json:"safetyAddress"`
110 WithdrawDelayerContract ethCommon.Address `json:"withdrawDelayerContract"`
111}
112
113// Bucket are the variables of each Bucket of Rollup Smart Contract
114type Bucket struct {
115 CeilUSD uint64 `json:"ceilUSD"`
116 BlockStamp uint64 `json:"blockStamp"`
117 Withdrawals uint64 `json:"withdrawals"`
118 BlockWithdrawalRate uint64 `json:"blockWithdrawalRate"`
119 MaxWithdrawals uint64 `json:"maxWithdrawals"`
120}
121
122// RollupVariables are the variables of the Rollup Smart Contract
123type RollupVariables struct {
124 FeeAddToken *big.Int `json:"feeAddToken" meddler:"fee_addtoken"`
125 ForgeL1L2BatchTimeout int64 `json:"forgeL1L2BatchTimeout" meddler:"forge_l1l2_timeout"`
126 WithdrawalDelay uint64 `json:"withdrawalDelay" meddler:"withdrawal_delay"`
127 Buckets [RollupConstNumBuckets]Bucket `json:"buckets" meddler:"buckets,json"`
128}
129
130// QueueStruct is the queue of L1Txs for a batch
131type QueueStruct struct {
132 L1TxQueue []common.L1Tx
133 TotalL1TxFee *big.Int
134}
135
136// NewQueueStruct creates a new clear QueueStruct.
137func NewQueueStruct() *QueueStruct {
138 return &QueueStruct{
139 L1TxQueue: make([]common.L1Tx, 0),
140 TotalL1TxFee: big.NewInt(0),
141 }
142}
143
144// RollupVerifierStruct is the information about verifiers of the Rollup Smart Contract
145type RollupVerifierStruct struct {
146 MaxTx int64 `json:"maxTx"`
147 NLevels int64 `json:"nlevels"`
148}
149
150// RollupState represents the state of the Rollup in the Smart Contract
151type RollupState struct {
152 StateRoot *big.Int
153 ExitRoots []*big.Int
154 ExitNullifierMap map[[256 / 8]byte]bool
155 TokenList []ethCommon.Address
156 TokenMap map[ethCommon.Address]bool
157 MapL1TxQueue map[int64]*QueueStruct
158 LastL1L2Batch int64
159 CurrentToForgeL1TxsNum int64
160 LastToForgeL1TxsNum int64
161 CurrentIdx int64
162}
163
164// RollupEventL1UserTx is an event of the Rollup Smart Contract
165type RollupEventL1UserTx struct {
166 // ToForgeL1TxsNum int64 // QueueIndex *big.Int
167 // Position int // TransactionIndex *big.Int
168 L1UserTx common.L1Tx
169}
170
171// RollupEventL1UserTxAux is an event of the Rollup Smart Contract
172type RollupEventL1UserTxAux struct {
173 ToForgeL1TxsNum uint64 // QueueIndex *big.Int
174 Position uint8 // TransactionIndex *big.Int
175 L1UserTx []byte
176}
177
178// RollupEventAddToken is an event of the Rollup Smart Contract
179type RollupEventAddToken struct {
180 TokenAddress ethCommon.Address
181 TokenID uint32
182}
183
184// RollupEventForgeBatch is an event of the Rollup Smart Contract
185type RollupEventForgeBatch struct {
186 BatchNum int64
187 // Sender ethCommon.Address
188 EthTxHash ethCommon.Hash
189}
190
191// RollupEventUpdateForgeL1L2BatchTimeout is an event of the Rollup Smart Contract
192type RollupEventUpdateForgeL1L2BatchTimeout struct {
193 NewForgeL1L2BatchTimeout int64
194}
195
196// RollupEventUpdateFeeAddToken is an event of the Rollup Smart Contract
197type RollupEventUpdateFeeAddToken struct {
198 NewFeeAddToken *big.Int
199}
200
201// RollupEventWithdrawEvent is an event of the Rollup Smart Contract
202type RollupEventWithdrawEvent struct {
203 Idx uint64
204 NumExitRoot uint64
205 InstantWithdraw bool
206}
207
208// RollupEvents is the list of events in a block of the Rollup Smart Contract
209type RollupEvents struct {
210 L1UserTx []RollupEventL1UserTx
211 AddToken []RollupEventAddToken
212 ForgeBatch []RollupEventForgeBatch
213 UpdateForgeL1L2BatchTimeout []RollupEventUpdateForgeL1L2BatchTimeout
214 UpdateFeeAddToken []RollupEventUpdateFeeAddToken
215 WithdrawEvent []RollupEventWithdrawEvent
216}
217
218// NewRollupEvents creates an empty RollupEvents with the slices initialized.
219func NewRollupEvents() RollupEvents {
220 return RollupEvents{
221 L1UserTx: make([]RollupEventL1UserTx, 0),
222 AddToken: make([]RollupEventAddToken, 0),
223 ForgeBatch: make([]RollupEventForgeBatch, 0),
224 UpdateForgeL1L2BatchTimeout: make([]RollupEventUpdateForgeL1L2BatchTimeout, 0),
225 UpdateFeeAddToken: make([]RollupEventUpdateFeeAddToken, 0),
226 WithdrawEvent: make([]RollupEventWithdrawEvent, 0),
227 }
228}
229
230// RollupForgeBatchArgs are the arguments to the ForgeBatch function in the Rollup Smart Contract
231type RollupForgeBatchArgs struct {
232 NewLastIdx int64
233 NewStRoot *big.Int
234 NewExitRoot *big.Int
235 L1CoordinatorTxs []common.L1Tx
236 L1CoordinatorTxsAuths [][]byte // Authorization for accountCreations for each L1CoordinatorTx
237 L2TxsData []common.L2Tx
238 FeeIdxCoordinator []common.Idx
239 // Circuit selector
240 VerifierIdx uint8
241 L1Batch bool
242 ProofA [2]*big.Int
243 ProofB [2][2]*big.Int
244 ProofC [2]*big.Int
245}
246
247// RollupForgeBatchArgsAux are the arguments to the ForgeBatch function in the Rollup Smart Contract
248type RollupForgeBatchArgsAux struct {
249 NewLastIdx *big.Int
250 NewStRoot *big.Int
251 NewExitRoot *big.Int
252 EncodedL1CoordinatorTx []byte
253 L2TxsData []byte
254 FeeIdxCoordinator []byte
255 // Circuit selector
256 VerifierIdx uint8
257 L1Batch bool
258 ProofA [2]*big.Int
259 ProofB [2][2]*big.Int
260 ProofC [2]*big.Int
261}
262
263// RollupInterface is the inteface to to Rollup Smart Contract
264type RollupInterface interface {
265 //
266 // Smart Contract Methods
267 //
268
269 // Public Functions
270
271 RollupForgeBatch(*RollupForgeBatchArgs) (*types.Transaction, error)
272 RollupAddToken(tokenAddress ethCommon.Address, feeAddToken *big.Int) (*types.Transaction, error)
273
274 RollupWithdrawMerkleProof(babyPubKey *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (*types.Transaction, error)
275 RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error)
276
277 RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error)
278 RollupL1UserTxERC777(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error)
279
280 // Governance Public Functions
281 RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (*types.Transaction, error)
282 RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (*types.Transaction, error)
283
284 // Viewers
285 RollupRegisterTokensCount() (*big.Int, error)
286
287 //
288 // Smart Contract Status
289 //
290
291 RollupConstants() (*RollupPublicConstants, error)
292 RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error)
293 RollupForgeBatchArgs(ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error)
294}
295
296//
297// Implementation
298//
299
300// RollupClient is the implementation of the interface to the Rollup Smart Contract in ethereum.
301type RollupClient struct {
302 client *EthereumClient
303 address ethCommon.Address
304 tokenHEZAddress ethCommon.Address
305 hermez *Hermez.Hermez
306 contractAbi abi.ABI
307}
308
309// NewRollupClient creates a new RollupClient
310func NewRollupClient(client *EthereumClient, address ethCommon.Address, tokenHEZAddress ethCommon.Address) (*RollupClient, error) {
311 contractAbi, err := abi.JSON(strings.NewReader(string(Hermez.HermezABI)))
312 if err != nil {
313 return nil, err
314 }
315 hermez, err := Hermez.NewHermez(address, client.Client())
316 if err != nil {
317 return nil, err
318 }
319 return &RollupClient{
320 client: client,
321 address: address,
322 tokenHEZAddress: tokenHEZAddress,
323 hermez: hermez,
324 contractAbi: contractAbi,
325 }, nil
326}
327
328// RollupForgeBatch is the interface to call the smart contract function
329func (c *RollupClient) RollupForgeBatch(args *RollupForgeBatchArgs) (tx *types.Transaction, err error) {
330 if tx, err = c.client.CallAuth(
331 1000000, //nolint:gomnd
332 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
333 rollupConst, err := c.RollupConstants()
334 if err != nil {
335 return nil, err
336 }
337 nLevels := rollupConst.Verifiers[args.VerifierIdx].NLevels
338 lenBytes := nLevels / 8 //nolint:gomnd
339 newLastIdx := big.NewInt(int64(args.NewLastIdx))
340 var l1CoordinatorBytes []byte
341 for i := 0; i < len(args.L1CoordinatorTxs); i++ {
342 l1 := args.L1CoordinatorTxs[i]
343 bytesl1, err := l1.BytesCoordinatorTx(args.L1CoordinatorTxsAuths[i])
344 if err != nil {
345 return nil, err
346 }
347 l1CoordinatorBytes = append(l1CoordinatorBytes, bytesl1[:]...)
348 }
349 var l2DataBytes []byte
350 for i := 0; i < len(args.L2TxsData); i++ {
351 l2 := args.L2TxsData[i]
352 bytesl2, err := l2.Bytes(int(nLevels))
353 if err != nil {
354 return nil, err
355 }
356 l2DataBytes = append(l2DataBytes, bytesl2[:]...)
357 }
358 var feeIdxCoordinator []byte
359 if len(args.FeeIdxCoordinator) > RollupConstMaxFeeIdxCoordinator {
360 return nil, fmt.Errorf("len(args.FeeIdxCoordinator) > %v",
361 RollupConstMaxFeeIdxCoordinator)
362 }
363 for i := 0; i < RollupConstMaxFeeIdxCoordinator; i++ {
364 feeIdx := common.Idx(0)
365 if i < len(args.FeeIdxCoordinator) {
366 feeIdx = args.FeeIdxCoordinator[i]
367 }
368 bytesFeeIdx, err := feeIdx.Bytes()
369 if err != nil {
370 return nil, err
371 }
372 feeIdxCoordinator = append(feeIdxCoordinator, bytesFeeIdx[len(bytesFeeIdx)-int(lenBytes):]...)
373 }
374 return c.hermez.ForgeBatch(auth, newLastIdx, args.NewStRoot, args.NewExitRoot, l1CoordinatorBytes, l2DataBytes, feeIdxCoordinator, args.VerifierIdx, args.L1Batch, args.ProofA, args.ProofB, args.ProofC)
375 },
376 ); err != nil {
377 return nil, fmt.Errorf("Failed forge batch: %w", err)
378 }
379 return tx, nil
380}
381
382// RollupAddToken is the interface to call the smart contract function.
383// `feeAddToken` is the amount of HEZ tokens that will be paid to add the
384// token. `feeAddToken` must match the public value of the smart contract.
385func (c *RollupClient) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken *big.Int) (tx *types.Transaction, err error) {
386 if tx, err = c.client.CallAuth(
387 0,
388 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
389 tokens, err := ERC777.NewERC777(c.tokenHEZAddress, ec)
390 if err != nil {
391 return nil, err
392 }
393 addTokenFnSignature := []byte("addToken(address)")
394 hash := sha3.NewLegacyKeccak256()
395 _, err = hash.Write(addTokenFnSignature)
396 if err != nil {
397 return nil, err
398 }
399 methodID := hash.Sum(nil)[:4]
400 var data []byte
401 data = append(data, methodID...)
402 paddedAddress := ethCommon.LeftPadBytes(tokenAddress.Bytes(), 32)
403 data = append(data, paddedAddress[:]...)
404 return tokens.Send(auth, c.address, feeAddToken, data)
405 },
406 ); err != nil {
407 return nil, fmt.Errorf("Failed add Token %w", err)
408 }
409 return tx, nil
410}
411
412// RollupWithdrawMerkleProof is the interface to call the smart contract function
413func (c *RollupClient) RollupWithdrawMerkleProof(fromBJJ *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (tx *types.Transaction, err error) {
414 if tx, err = c.client.CallAuth(
415 0,
416 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
417 hermez, err := Hermez.NewHermez(c.address, ec)
418 if err != nil {
419 return nil, err
420 }
421 pkCompL := fromBJJ.Compress()
422 pkCompB := common.SwapEndianness(pkCompL[:])
423 babyPubKey := new(big.Int).SetBytes(pkCompB)
424 numExitRootB := big.NewInt(numExitRoot)
425 idxBig := big.NewInt(idx)
426 return hermez.WithdrawMerkleProof(auth, tokenID, amount, babyPubKey, numExitRootB, siblings, idxBig, instantWithdraw)
427 },
428 ); err != nil {
429 return nil, fmt.Errorf("Failed update WithdrawMerkleProof: %w", err)
430 }
431 return tx, nil
432}
433
434// RollupWithdrawCircuit is the interface to call the smart contract function
435func (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) {
436 log.Error("TODO")
437 return nil, errTODO
438}
439
440// RollupL1UserTxERC20ETH is the interface to call the smart contract function
441func (c *RollupClient) RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
442 if tx, err = c.client.CallAuth(
443 0,
444 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
445 hermez, err := Hermez.NewHermez(c.address, ec)
446 if err != nil {
447 return nil, err
448 }
449 pkCompL := fromBJJ.Compress()
450 pkCompB := common.SwapEndianness(pkCompL[:])
451 babyPubKey := new(big.Int).SetBytes(pkCompB)
452 fromIdxBig := big.NewInt(fromIdx)
453 toIdxBig := big.NewInt(toIdx)
454 tokenIDBig := uint32(tokenID)
455 loadAmountF, err := common.NewFloat16(loadAmount)
456 if err != nil {
457 return nil, err
458 }
459 amountF, err := common.NewFloat16(amount)
460 if err != nil {
461 return nil, err
462 }
463 if tokenID == 0 {
464 auth.Value = loadAmount
465 }
466 return hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(loadAmountF), uint16(amountF), tokenIDBig, toIdxBig)
467 },
468 ); err != nil {
469 return nil, fmt.Errorf("Failed add L1 Tx ERC20/ETH: %w", err)
470 }
471 return tx, nil
472}
473
474// RollupL1UserTxERC777 is the interface to call the smart contract function
475func (c *RollupClient) RollupL1UserTxERC777(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
476 if tx, err = c.client.CallAuth(
477 0,
478 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
479 tokens, err := ERC777.NewERC777(c.tokenHEZAddress, ec)
480 if err != nil {
481 return nil, err
482 }
483 addL1TxFnSignature := []byte("addL1Transaction(uint256,uint48,uint16,uint16,uint32,uint48)")
484 hash := sha3.NewLegacyKeccak256()
485 _, err = hash.Write(addL1TxFnSignature)
486 if err != nil {
487 return nil, err
488 }
489 methodID := hash.Sum(nil)[:4]
490 pkCompL := fromBJJ.Compress()
491 pkCompB := common.SwapEndianness(pkCompL[:])
492 paddedAddress := ethCommon.LeftPadBytes(pkCompB, 32)
493 fromIdxB, err := common.Idx(fromIdx).Bytes()
494 if err != nil {
495 return nil, err
496 }
497 paddedFromIdx := ethCommon.LeftPadBytes(fromIdxB[:], 32)
498 loadAmountF, err := common.NewFloat16(loadAmount)
499 if err != nil {
500 return nil, err
501 }
502 paddedLoadAmount := ethCommon.LeftPadBytes(loadAmountF.Bytes(), 32)
503 amountF, err := common.NewFloat16(amount)
504 if err != nil {
505 return nil, err
506 }
507 paddedAmount := ethCommon.LeftPadBytes(amountF.Bytes(), 32)
508 tokenIDB := common.TokenID(tokenID).Bytes()
509 paddedTokenID := ethCommon.LeftPadBytes(tokenIDB, 32)
510 toIdxB, err := common.Idx(toIdx).Bytes()
511 if err != nil {
512 return nil, err
513 }
514 paddedToIdx := ethCommon.LeftPadBytes(toIdxB[:], 32)
515 var data []byte
516 data = append(data, methodID...)
517 data = append(data, paddedAddress[:]...)
518 data = append(data, paddedFromIdx[:]...)
519 data = append(data, paddedLoadAmount[:]...)
520 data = append(data, paddedAmount[:]...)
521 data = append(data, paddedTokenID[:]...)
522 data = append(data, paddedToIdx[:]...)
523 return tokens.Send(auth, c.address, loadAmount, data)
524 },
525 ); err != nil {
526 return nil, fmt.Errorf("Failed add L1 Tx ERC777: %w", err)
527 }
528 return tx, nil
529}
530
531// RollupRegisterTokensCount is the interface to call the smart contract function
532func (c *RollupClient) RollupRegisterTokensCount() (*big.Int, error) {
533 var registerTokensCount *big.Int
534 if err := c.client.Call(func(ec *ethclient.Client) error {
535 hermez, err := Hermez.NewHermez(c.address, ec)
536 if err != nil {
537 return err
538 }
539 registerTokensCount, err = hermez.RegisterTokensCount(nil)
540 return err
541 }); err != nil {
542 return nil, err
543 }
544 return registerTokensCount, nil
545}
546
547// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
548func (c *RollupClient) RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (tx *types.Transaction, err error) {
549 if tx, err = c.client.CallAuth(
550 0,
551 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
552 hermez, err := Hermez.NewHermez(c.address, ec)
553 if err != nil {
554 return nil, err
555 }
556 return hermez.UpdateForgeL1L2BatchTimeout(auth, uint8(newForgeL1L2BatchTimeout))
557 },
558 ); err != nil {
559 return nil, fmt.Errorf("Failed update ForgeL1L2BatchTimeout: %w", err)
560 }
561 return tx, nil
562}
563
564// RollupUpdateFeeAddToken is the interface to call the smart contract function
565func (c *RollupClient) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
566 if tx, err = c.client.CallAuth(
567 0,
568 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
569 hermez, err := Hermez.NewHermez(c.address, ec)
570 if err != nil {
571 return nil, err
572 }
573 return hermez.UpdateFeeAddToken(auth, newFeeAddToken)
574 },
575 ); err != nil {
576 return nil, fmt.Errorf("Failed update FeeAddToken: %w", err)
577 }
578 return tx, nil
579}
580
581// RollupConstants returns the Constants of the Rollup Smart Contract
582func (c *RollupClient) RollupConstants() (rollupConstants *RollupPublicConstants, err error) {
583 rollupConstants = new(RollupPublicConstants)
584 if err := c.client.Call(func(ec *ethclient.Client) error {
585 absoluteMaxL1L2BatchTimeout, err := c.hermez.ABSOLUTEMAXL1L2BATCHTIMEOUT(nil)
586 if err != nil {
587 return err
588 }
589 rollupConstants.AbsoluteMaxL1L2BatchTimeout = int64(absoluteMaxL1L2BatchTimeout)
590 rollupConstants.TokenHEZ, err = c.hermez.TokenHEZ(nil)
591 if err != nil {
592 return err
593 }
594 for i := int64(0); i < int64(LenVerifiers); i++ {
595 var newRollupVerifier RollupVerifierStruct
596 rollupVerifier, err := c.hermez.RollupVerifiers(nil, big.NewInt(i))
597 if err != nil {
598 return err
599 }
600 newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
601 newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
602 rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
603 }
604 rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(nil)
605 if err != nil {
606 return err
607 }
608 rollupConstants.HermezGovernanceDAOAddress, err = c.hermez.HermezGovernanceDAOAddress(nil)
609 if err != nil {
610 return err
611 }
612 rollupConstants.SafetyAddress, err = c.hermez.SafetyAddress(nil)
613 if err != nil {
614 return err
615 }
616 rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(nil)
617 return err
618 }); err != nil {
619 return nil, err
620 }
621 return rollupConstants, nil
622}
623
624var (
625 logHermezL1UserTxEvent = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint64,uint8,bytes)"))
626 logHermezAddToken = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
627 logHermezForgeBatch = crypto.Keccak256Hash([]byte("ForgeBatch(uint64)"))
628 logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
629 logHermezUpdateFeeAddToken = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
630 logHermezWithdrawEvent = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint48,bool)"))
631)
632
633// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
634func (c *RollupClient) RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error) {
635 var rollupEvents RollupEvents
636 var blockHash ethCommon.Hash
637
638 query := ethereum.FilterQuery{
639 FromBlock: big.NewInt(blockNum),
640 ToBlock: big.NewInt(blockNum),
641 Addresses: []ethCommon.Address{
642 c.address,
643 },
644 BlockHash: nil,
645 Topics: [][]ethCommon.Hash{},
646 }
647 logs, err := c.client.client.FilterLogs(context.Background(), query)
648 if err != nil {
649 return nil, nil, err
650 }
651 if len(logs) > 0 {
652 blockHash = logs[0].BlockHash
653 }
654 for _, vLog := range logs {
655 if vLog.BlockHash != blockHash {
656 return nil, nil, ErrBlockHashMismatchEvent
657 }
658 switch vLog.Topics[0] {
659 case logHermezL1UserTxEvent:
660 var L1UserTxAux RollupEventL1UserTxAux
661 var L1UserTx RollupEventL1UserTx
662 err := c.contractAbi.Unpack(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
663 if err != nil {
664 return nil, nil, err
665 }
666 L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
667 if err != nil {
668 return nil, nil, err
669 }
670 toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
671 L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
672 L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
673 L1Tx.UserOrigin = true
674 L1UserTx.L1UserTx = *L1Tx
675 rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
676 case logHermezAddToken:
677 var addToken RollupEventAddToken
678 err := c.contractAbi.Unpack(&addToken, "AddToken", vLog.Data)
679 if err != nil {
680 return nil, nil, err
681 }
682 addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
683 rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
684 case logHermezForgeBatch:
685 var forgeBatch RollupEventForgeBatch
686 forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
687 forgeBatch.EthTxHash = vLog.TxHash
688 // forgeBatch.Sender = vLog.Address
689 rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
690 case logHermezUpdateForgeL1L2BatchTimeout:
691 var updateForgeL1L2BatchTimeout struct {
692 NewForgeL1L2BatchTimeout uint8
693 }
694 err := c.contractAbi.Unpack(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
695 if err != nil {
696 return nil, nil, err
697 }
698 rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
699 RollupEventUpdateForgeL1L2BatchTimeout{
700 NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
701 })
702 case logHermezUpdateFeeAddToken:
703 var updateFeeAddToken RollupEventUpdateFeeAddToken
704 err := c.contractAbi.Unpack(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
705 if err != nil {
706 return nil, nil, err
707 }
708 rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
709 case logHermezWithdrawEvent:
710 var withdraw RollupEventWithdrawEvent
711 withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
712 withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
713 instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
714 if instantWithdraw == 1 {
715 withdraw.InstantWithdraw = true
716 }
717 rollupEvents.WithdrawEvent = append(rollupEvents.WithdrawEvent, withdraw)
718 }
719 }
720 return &rollupEvents, &blockHash, nil
721}
722
723// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
724// Rollup Smart Contract in the given transaction, and the sender address.
725func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
726 tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
727 if err != nil {
728 return nil, nil, err
729 }
730 txData := tx.Data()
731 method, err := c.contractAbi.MethodById(txData[:4])
732 if err != nil {
733 return nil, nil, err
734 }
735 receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
736 if err != nil {
737 return nil, nil, err
738 }
739 sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
740 if err != nil {
741 return nil, nil, err
742 }
743 var aux RollupForgeBatchArgsAux
744 if err := method.Inputs.Unpack(&aux, txData[4:]); err != nil {
745 return nil, nil, err
746 }
747 rollupForgeBatchArgs := RollupForgeBatchArgs{
748 L1Batch: aux.L1Batch,
749 NewExitRoot: aux.NewExitRoot,
750 NewLastIdx: aux.NewLastIdx.Int64(),
751 NewStRoot: aux.NewStRoot,
752 ProofA: aux.ProofA,
753 ProofB: aux.ProofB,
754 ProofC: aux.ProofC,
755 VerifierIdx: aux.VerifierIdx,
756 L1CoordinatorTxs: []common.L1Tx{},
757 L1CoordinatorTxsAuths: [][]byte{},
758 L2TxsData: []common.L2Tx{},
759 FeeIdxCoordinator: []common.Idx{},
760 }
761 numTxsL1 := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
762 for i := 0; i < numTxsL1; i++ {
763 bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
764 var signature []byte
765 v := bytesL1Coordinator[0]
766 s := bytesL1Coordinator[1:33]
767 r := bytesL1Coordinator[33:65]
768 signature = append(signature, r[:]...)
769 signature = append(signature, s[:]...)
770 signature = append(signature, v)
771 l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator)
772 if err != nil {
773 return nil, nil, err
774 }
775 rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
776 rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
777 }
778 rollupConsts, err := c.RollupConstants()
779 if err != nil {
780 return nil, nil, err
781 }
782 nLevels := rollupConsts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
783 lenL2TxsBytes := int((nLevels/8)*2 + 2 + 1)
784 numTxsL2 := len(aux.L2TxsData) / lenL2TxsBytes
785 for i := 0; i < numTxsL2; i++ {
786 l2Tx, err := common.L2TxFromBytes(aux.L2TxsData[i*lenL2TxsBytes:(i+1)*lenL2TxsBytes], int(nLevels))
787 if err != nil {
788 return nil, nil, err
789 }
790 rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
791 }
792 lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
793 numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
794 for i := 0; i < numFeeIdxCoordinator; i++ {
795 var paddedFeeIdx [6]byte
796 // TODO: This check is not necessary: the first case will always work. Test it before removing the if.
797 if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
798 copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
799 } else {
800 copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
801 }
802 feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
803 if err != nil {
804 return nil, nil, err
805 }
806 if feeIdxCoordinator != common.Idx(0) {
807 rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
808 }
809 }
810 return &rollupForgeBatchArgs, &sender, nil
811}