You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

525 lines
20 KiB

package eth
import (
"context"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/hermeznetwork/hermez-node/common"
WithdrawalDelayer "github.com/hermeznetwork/hermez-node/eth/contracts/withdrawdelayer"
"github.com/hermeznetwork/hermez-node/log"
"github.com/hermeznetwork/tracerr"
)
// DepositState is the state of Deposit
type DepositState struct {
Amount *big.Int
DepositTimestamp uint64
}
// WDelayerEventInitialize is the InitializeWithdrawalDelayerEvent event of the
// Smart Contract
type WDelayerEventInitialize struct {
InitialWithdrawalDelay uint64
InitialHermezGovernanceAddress ethCommon.Address
InitialEmergencyCouncil ethCommon.Address
}
// WDelayerVariables returns the WDelayerVariables from the initialize event
func (ei *WDelayerEventInitialize) WDelayerVariables() *common.WDelayerVariables {
return &common.WDelayerVariables{
EthBlockNum: 0,
HermezGovernanceAddress: ei.InitialHermezGovernanceAddress,
EmergencyCouncilAddress: ei.InitialEmergencyCouncil,
WithdrawalDelay: ei.InitialWithdrawalDelay,
EmergencyModeStartingBlock: 0,
EmergencyMode: false,
}
}
// WDelayerEventDeposit is an event of the WithdrawalDelayer Smart Contract
type WDelayerEventDeposit struct {
Owner ethCommon.Address
Token ethCommon.Address
Amount *big.Int
DepositTimestamp uint64
TxHash ethCommon.Hash // Hash of the transaction that generated this event
}
// WDelayerEventWithdraw is an event of the WithdrawalDelayer Smart Contract
type WDelayerEventWithdraw struct {
Owner ethCommon.Address
Token ethCommon.Address
Amount *big.Int
}
// WDelayerEventEmergencyModeEnabled an event of the WithdrawalDelayer Smart Contract
type WDelayerEventEmergencyModeEnabled struct {
}
// WDelayerEventNewWithdrawalDelay an event of the WithdrawalDelayer Smart Contract
type WDelayerEventNewWithdrawalDelay struct {
WithdrawalDelay uint64
}
// WDelayerEventEscapeHatchWithdrawal an event of the WithdrawalDelayer Smart Contract
type WDelayerEventEscapeHatchWithdrawal struct {
Who ethCommon.Address
To ethCommon.Address
Token ethCommon.Address
Amount *big.Int
}
// WDelayerEventNewEmergencyCouncil an event of the WithdrawalDelayer Smart Contract
type WDelayerEventNewEmergencyCouncil struct {
NewEmergencyCouncil ethCommon.Address
}
// WDelayerEventNewHermezGovernanceAddress an event of the WithdrawalDelayer Smart Contract
type WDelayerEventNewHermezGovernanceAddress struct {
NewHermezGovernanceAddress ethCommon.Address
}
// WDelayerEvents is the lis of events in a block of the WithdrawalDelayer Smart Contract
type WDelayerEvents struct {
Deposit []WDelayerEventDeposit
Withdraw []WDelayerEventWithdraw
EmergencyModeEnabled []WDelayerEventEmergencyModeEnabled
NewWithdrawalDelay []WDelayerEventNewWithdrawalDelay
EscapeHatchWithdrawal []WDelayerEventEscapeHatchWithdrawal
NewEmergencyCouncil []WDelayerEventNewEmergencyCouncil
NewHermezGovernanceAddress []WDelayerEventNewHermezGovernanceAddress
}
// NewWDelayerEvents creates an empty WDelayerEvents with the slices initialized.
func NewWDelayerEvents() WDelayerEvents {
return WDelayerEvents{
Deposit: make([]WDelayerEventDeposit, 0),
Withdraw: make([]WDelayerEventWithdraw, 0),
EmergencyModeEnabled: make([]WDelayerEventEmergencyModeEnabled, 0),
NewWithdrawalDelay: make([]WDelayerEventNewWithdrawalDelay, 0),
EscapeHatchWithdrawal: make([]WDelayerEventEscapeHatchWithdrawal, 0),
NewEmergencyCouncil: make([]WDelayerEventNewEmergencyCouncil, 0),
NewHermezGovernanceAddress: make([]WDelayerEventNewHermezGovernanceAddress, 0),
}
}
// WDelayerInterface is the inteface to WithdrawalDelayer Smart Contract
type WDelayerInterface interface {
//
// Smart Contract Methods
//
WDelayerGetHermezGovernanceAddress() (*ethCommon.Address, error)
WDelayerTransferGovernance(newAddress ethCommon.Address) (*types.Transaction, error)
WDelayerClaimGovernance() (*types.Transaction, error)
WDelayerGetEmergencyCouncil() (*ethCommon.Address, error)
WDelayerTransferEmergencyCouncil(newAddress ethCommon.Address) (*types.Transaction, error)
WDelayerClaimEmergencyCouncil() (*types.Transaction, error)
WDelayerIsEmergencyMode() (bool, error)
WDelayerGetWithdrawalDelay() (int64, error)
WDelayerGetEmergencyModeStartingTime() (int64, error)
WDelayerEnableEmergencyMode() (*types.Transaction, error)
WDelayerChangeWithdrawalDelay(newWithdrawalDelay uint64) (*types.Transaction, error)
WDelayerDepositInfo(owner, token ethCommon.Address) (depositInfo DepositState, err error)
WDelayerDeposit(onwer, token ethCommon.Address, amount *big.Int) (*types.Transaction, error)
WDelayerWithdrawal(owner, token ethCommon.Address) (*types.Transaction, error)
WDelayerEscapeHatchWithdrawal(to, token ethCommon.Address, amount *big.Int) (*types.Transaction, error)
WDelayerEventsByBlock(blockNum int64, blockHash *ethCommon.Hash) (*WDelayerEvents, error)
WDelayerConstants() (*common.WDelayerConstants, error)
WDelayerEventInit() (*WDelayerEventInitialize, int64, error)
}
//
// Implementation
//
// WDelayerClient is the implementation of the interface to the WithdrawDelayer Smart Contract in ethereum.
type WDelayerClient struct {
client *EthereumClient
address ethCommon.Address
wdelayer *WithdrawalDelayer.WithdrawalDelayer
contractAbi abi.ABI
opts *bind.CallOpts
}
// NewWDelayerClient creates a new WDelayerClient
func NewWDelayerClient(client *EthereumClient, address ethCommon.Address) (*WDelayerClient, error) {
contractAbi, err := abi.JSON(strings.NewReader(string(WithdrawalDelayer.WithdrawalDelayerABI)))
if err != nil {
return nil, tracerr.Wrap(err)
}
wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(address, client.Client())
if err != nil {
return nil, tracerr.Wrap(err)
}
return &WDelayerClient{
client: client,
address: address,
wdelayer: wdelayer,
contractAbi: contractAbi,
opts: newCallOpts(),
}, nil
}
// WDelayerGetHermezGovernanceAddress is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerGetHermezGovernanceAddress() (hermezGovernanceAddress *ethCommon.Address, err error) {
var _hermezGovernanceAddress ethCommon.Address
if err := c.client.Call(func(ec *ethclient.Client) error {
_hermezGovernanceAddress, err = c.wdelayer.GetHermezGovernanceAddress(c.opts)
return tracerr.Wrap(err)
}); err != nil {
return nil, tracerr.Wrap(err)
}
return &_hermezGovernanceAddress, nil
}
// WDelayerTransferGovernance is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerTransferGovernance(newAddress ethCommon.Address) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.TransferGovernance(auth, newAddress)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed transfer hermezGovernanceAddress: %w", err))
}
return tx, nil
}
// WDelayerClaimGovernance is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerClaimGovernance() (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.ClaimGovernance(auth)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed claim hermezGovernanceAddress: %w", err))
}
return tx, nil
}
// WDelayerGetEmergencyCouncil is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerGetEmergencyCouncil() (emergencyCouncilAddress *ethCommon.Address, err error) {
var _emergencyCouncilAddress ethCommon.Address
if err := c.client.Call(func(ec *ethclient.Client) error {
_emergencyCouncilAddress, err = c.wdelayer.GetEmergencyCouncil(c.opts)
return tracerr.Wrap(err)
}); err != nil {
return nil, tracerr.Wrap(err)
}
return &_emergencyCouncilAddress, nil
}
// WDelayerTransferEmergencyCouncil is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerTransferEmergencyCouncil(newAddress ethCommon.Address) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.TransferEmergencyCouncil(auth, newAddress)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed transfer EmergencyCouncil: %w", err))
}
return tx, nil
}
// WDelayerClaimEmergencyCouncil is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerClaimEmergencyCouncil() (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.ClaimEmergencyCouncil(auth)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed claim EmergencyCouncil: %w", err))
}
return tx, nil
}
// WDelayerIsEmergencyMode is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerIsEmergencyMode() (ermergencyMode bool, err error) {
if err := c.client.Call(func(ec *ethclient.Client) error {
ermergencyMode, err = c.wdelayer.IsEmergencyMode(c.opts)
return tracerr.Wrap(err)
}); err != nil {
return false, tracerr.Wrap(err)
}
return ermergencyMode, nil
}
// WDelayerGetWithdrawalDelay is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerGetWithdrawalDelay() (withdrawalDelay int64, err error) {
var _withdrawalDelay uint64
if err := c.client.Call(func(ec *ethclient.Client) error {
_withdrawalDelay, err = c.wdelayer.GetWithdrawalDelay(c.opts)
return tracerr.Wrap(err)
}); err != nil {
return 0, tracerr.Wrap(err)
}
return int64(_withdrawalDelay), nil
}
// WDelayerGetEmergencyModeStartingTime is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerGetEmergencyModeStartingTime() (emergencyModeStartingTime int64, err error) {
var _emergencyModeStartingTime uint64
if err := c.client.Call(func(ec *ethclient.Client) error {
_emergencyModeStartingTime, err = c.wdelayer.GetEmergencyModeStartingTime(c.opts)
return tracerr.Wrap(err)
}); err != nil {
return 0, tracerr.Wrap(err)
}
return int64(_emergencyModeStartingTime), nil
}
// WDelayerEnableEmergencyMode is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerEnableEmergencyMode() (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.EnableEmergencyMode(auth)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed setting enable emergency mode: %w", err))
}
return tx, nil
}
// WDelayerChangeWithdrawalDelay is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerChangeWithdrawalDelay(newWithdrawalDelay uint64) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.ChangeWithdrawalDelay(auth, newWithdrawalDelay)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed setting withdrawal delay: %w", err))
}
return tx, nil
}
// WDelayerDepositInfo is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerDepositInfo(owner, token ethCommon.Address) (depositInfo DepositState, err error) {
if err := c.client.Call(func(ec *ethclient.Client) error {
amount, depositTimestamp, err := c.wdelayer.DepositInfo(c.opts, owner, token)
depositInfo.Amount = amount
depositInfo.DepositTimestamp = depositTimestamp
return tracerr.Wrap(err)
}); err != nil {
return depositInfo, tracerr.Wrap(err)
}
return depositInfo, nil
}
// WDelayerDeposit is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerDeposit(owner, token ethCommon.Address, amount *big.Int) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.Deposit(auth, owner, token, amount)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed deposit: %w", err))
}
return tx, nil
}
// WDelayerWithdrawal is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerWithdrawal(owner, token ethCommon.Address) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.Withdrawal(auth, owner, token)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed withdrawal: %w", err))
}
return tx, nil
}
// WDelayerEscapeHatchWithdrawal is the interface to call the smart contract function
func (c *WDelayerClient) WDelayerEscapeHatchWithdrawal(to, token ethCommon.Address, amount *big.Int) (tx *types.Transaction, err error) {
if tx, err = c.client.CallAuth(
0,
func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
return c.wdelayer.EscapeHatchWithdrawal(auth, to, token, amount)
},
); err != nil {
return nil, tracerr.Wrap(fmt.Errorf("Failed escapeHatchWithdrawal: %w", err))
}
return tx, nil
}
// WDelayerConstants returns the Constants of the WDelayer Smart Contract
func (c *WDelayerClient) WDelayerConstants() (constants *common.WDelayerConstants, err error) {
constants = new(common.WDelayerConstants)
if err := c.client.Call(func(ec *ethclient.Client) error {
constants.MaxWithdrawalDelay, err = c.wdelayer.MAXWITHDRAWALDELAY(c.opts)
if err != nil {
return tracerr.Wrap(err)
}
constants.MaxEmergencyModeTime, err = c.wdelayer.MAXEMERGENCYMODETIME(c.opts)
if err != nil {
return tracerr.Wrap(err)
}
constants.HermezRollup, err = c.wdelayer.HermezRollupAddress(c.opts)
if err != nil {
return tracerr.Wrap(err)
}
return tracerr.Wrap(err)
}); err != nil {
return constants, tracerr.Wrap(err)
}
return constants, nil
}
var (
logWDelayerDeposit = crypto.Keccak256Hash([]byte("Deposit(address,address,uint192,uint64)"))
logWDelayerWithdraw = crypto.Keccak256Hash([]byte("Withdraw(address,address,uint192)"))
logWDelayerEmergencyModeEnabled = crypto.Keccak256Hash([]byte("EmergencyModeEnabled()"))
logWDelayerNewWithdrawalDelay = crypto.Keccak256Hash([]byte("NewWithdrawalDelay(uint64)"))
logWDelayerEscapeHatchWithdrawal = crypto.Keccak256Hash([]byte("EscapeHatchWithdrawal(address,address,address,uint256)"))
logWDelayerNewEmergencyCouncil = crypto.Keccak256Hash([]byte("NewEmergencyCouncil(address)"))
logWDelayerNewHermezGovernanceAddress = crypto.Keccak256Hash([]byte("NewHermezGovernanceAddress(address)"))
logWDelayerInitialize = crypto.Keccak256Hash([]byte(
"InitializeWithdrawalDelayerEvent(uint64,address,address)"))
)
// WDelayerEventInit returns the initialize event with its corresponding block number
func (c *WDelayerClient) WDelayerEventInit() (*WDelayerEventInitialize, int64, error) {
query := ethereum.FilterQuery{
Addresses: []ethCommon.Address{
c.address,
},
Topics: [][]ethCommon.Hash{{logWDelayerInitialize}},
}
logs, err := c.client.client.FilterLogs(context.Background(), query)
if err != nil {
return nil, 0, tracerr.Wrap(err)
}
if len(logs) != 1 {
return nil, 0, tracerr.Wrap(fmt.Errorf("no event of type InitializeWithdrawalDelayerEvent found"))
}
vLog := logs[0]
if vLog.Topics[0] != logWDelayerInitialize {
return nil, 0, tracerr.Wrap(fmt.Errorf("event is not InitializeWithdrawalDelayerEvent"))
}
var wDelayerInit WDelayerEventInitialize
if err := c.contractAbi.UnpackIntoInterface(&wDelayerInit, "InitializeWithdrawalDelayerEvent",
vLog.Data); err != nil {
return nil, 0, tracerr.Wrap(err)
}
return &wDelayerInit, int64(vLog.BlockNumber), tracerr.Wrap(err)
}
// WDelayerEventsByBlock returns the events in a block that happened in the
// WDelayer Smart Contract.
// To query by blockNum, set blockNum >= 0 and blockHash == nil.
// To query by blockHash, set blockNum == -1 and blockHash != nil.
// If there are no events in that block the result is nil.
func (c *WDelayerClient) WDelayerEventsByBlock(blockNum int64,
blockHash *ethCommon.Hash) (*WDelayerEvents, error) {
var wdelayerEvents WDelayerEvents
var blockNumBigInt *big.Int
if blockNum >= 0 {
blockNumBigInt = big.NewInt(blockNum)
}
query := ethereum.FilterQuery{
BlockHash: blockHash,
FromBlock: blockNumBigInt,
ToBlock: blockNumBigInt,
Addresses: []ethCommon.Address{
c.address,
},
Topics: [][]ethCommon.Hash{},
}
logs, err := c.client.client.FilterLogs(context.Background(), query)
if err != nil {
return nil, tracerr.Wrap(err)
}
if len(logs) == 0 {
return nil, nil
}
for _, vLog := range logs {
if blockHash != nil && vLog.BlockHash != *blockHash {
log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
return nil, tracerr.Wrap(ErrBlockHashMismatchEvent)
}
switch vLog.Topics[0] {
case logWDelayerDeposit:
var deposit WDelayerEventDeposit
err := c.contractAbi.UnpackIntoInterface(&deposit, "Deposit", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
deposit.Owner = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
deposit.Token = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
deposit.TxHash = vLog.TxHash
wdelayerEvents.Deposit = append(wdelayerEvents.Deposit, deposit)
case logWDelayerWithdraw:
var withdraw WDelayerEventWithdraw
err := c.contractAbi.UnpackIntoInterface(&withdraw, "Withdraw", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
withdraw.Token = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
withdraw.Owner = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
wdelayerEvents.Withdraw = append(wdelayerEvents.Withdraw, withdraw)
case logWDelayerEmergencyModeEnabled:
var emergencyModeEnabled WDelayerEventEmergencyModeEnabled
wdelayerEvents.EmergencyModeEnabled = append(wdelayerEvents.EmergencyModeEnabled, emergencyModeEnabled)
case logWDelayerNewWithdrawalDelay:
var withdrawalDelay WDelayerEventNewWithdrawalDelay
err := c.contractAbi.UnpackIntoInterface(&withdrawalDelay, "NewWithdrawalDelay", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
wdelayerEvents.NewWithdrawalDelay = append(wdelayerEvents.NewWithdrawalDelay, withdrawalDelay)
case logWDelayerEscapeHatchWithdrawal:
var escapeHatchWithdrawal WDelayerEventEscapeHatchWithdrawal
err := c.contractAbi.UnpackIntoInterface(&escapeHatchWithdrawal, "EscapeHatchWithdrawal", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
escapeHatchWithdrawal.Who = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
escapeHatchWithdrawal.To = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
escapeHatchWithdrawal.Token = ethCommon.BytesToAddress(vLog.Topics[3].Bytes())
wdelayerEvents.EscapeHatchWithdrawal = append(wdelayerEvents.EscapeHatchWithdrawal, escapeHatchWithdrawal)
case logWDelayerNewEmergencyCouncil:
var emergencyCouncil WDelayerEventNewEmergencyCouncil
err := c.contractAbi.UnpackIntoInterface(&emergencyCouncil, "NewEmergencyCouncil", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
wdelayerEvents.NewEmergencyCouncil = append(wdelayerEvents.NewEmergencyCouncil, emergencyCouncil)
case logWDelayerNewHermezGovernanceAddress:
var governanceAddress WDelayerEventNewHermezGovernanceAddress
err := c.contractAbi.UnpackIntoInterface(&governanceAddress, "NewHermezGovernanceAddress", vLog.Data)
if err != nil {
return nil, tracerr.Wrap(err)
}
wdelayerEvents.NewHermezGovernanceAddress = append(wdelayerEvents.NewHermezGovernanceAddress, governanceAddress)
}
}
return &wdelayerEvents, nil
}