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