From 2ffa2c9770039b6d319251edce2f5b2fff598099 Mon Sep 17 00:00:00 2001 From: bendanzhentan <455462586@qq.com> Date: Tue, 21 Nov 2023 09:07:38 +0800 Subject: [PATCH] apply review suggestions 1. add SignerConfig for signer and gas price 2. reduce duplicated code in ProveWithdrawalTransaction and FinalizeWithdrawalTransaction 3. fix type definition ContractAddress 4. add ci workflow build.yaml --- .github/workflows/build.yaml | 41 ++++++++++++ core/config.go | 37 +++++++++++ core/processor.go | 125 ++++++++++++----------------------- core/types.go | 2 +- 4 files changed, 120 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..24b7144 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,41 @@ +name: Build + +on: + push: + # branches: [ main ] + # pull_request: + # branches: [ main ] + +jobs: + + 'build-go': + name: make build-go + runs-on: ubuntu-latest + steps: + + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ^1.21 + + - name: Check out + uses: actions/checkout@v3 + + - name: Make build-go + run: make build-go + + 'build-solidity': + name: make build-solidity + runs-on: ubuntu-latest + steps: + + - name: Check out + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Make build-solidity + run: cd contracts && forge build diff --git a/core/config.go b/core/config.go index 1b6fae5..b726b3e 100644 --- a/core/config.go +++ b/core/config.go @@ -1,9 +1,15 @@ package core import ( + "crypto/ecdsa" "errors" + "fmt" "os" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/BurntSushi/toml" "github.com/ethereum-optimism/optimism/indexer/config" "github.com/ethereum/go-ethereum/log" @@ -19,6 +25,7 @@ type Config struct { RPCs config.RPCsConfig `toml:"rpcs"` DB config.DBConfig `toml:"db"` L1Contracts config.L1Contracts `toml:"l1-contracts"` + Signer SignerConfig `toml:"signer"` } type MiscConfig struct { @@ -29,6 +36,11 @@ type MiscConfig struct { LogFilterBlockRange int64 `toml:"log-filter-block-range"` } +type SignerConfig struct { + Privkey string `toml:"privkey"` + GasPrice int64 `toml:"gas-price"` +} + // LoadConfig loads the `bot.toml` config file from a given path func LoadConfig(log log.Logger, path string) (Config, error) { log.Debug("loading config", "path", path) @@ -61,6 +73,31 @@ func LoadConfig(log log.Logger, path string) (Config, error) { return conf, errors.New("challenge-time-window must be set") } + if _, _, err = conf.SignerKeyPair(); err != nil { + return conf, err + } + if conf.Signer.GasPrice == 0 { + return conf, errors.New("gas-price must be set") + } + log.Info("loaded config") return conf, nil } + +func (c *Config) SignerKeyPair() (*ecdsa.PrivateKey, *common.Address, error) { + privkey, err := crypto.HexToECDSA(c.Signer.Privkey) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse privkey: %w", err) + } + + pubKey := privkey.Public() + pubKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) + if !ok { + return nil, nil, errors.New("failed to cast public key to ECDSA") + } + + pubKeyBytes := crypto.FromECDSAPub(pubKeyECDSA) + pubKeyHash := crypto.Keccak256(pubKeyBytes[1:])[12:] + address := common.HexToAddress(hexutil.Encode(pubKeyHash)) + return privkey, &address, nil +} diff --git a/core/processor.go b/core/processor.go index eafa67b..cd70923 100644 --- a/core/processor.go +++ b/core/processor.go @@ -2,21 +2,17 @@ package core import ( "context" - "crypto/ecdsa" "errors" "fmt" - "math/big" - "os" - "github.com/ethereum-optimism/optimism/indexer/config" "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "math/big" ) type Processor struct { @@ -25,7 +21,7 @@ type Processor struct { L1Client *ClientExt L2Client *ClientExt - L1Contracts config.L1Contracts + cfg Config L2Contracts config.L2Contracts } @@ -38,17 +34,10 @@ func NewProcessor( log = log.New("processor", "Processor") l2Contracts := config.L2ContractsFromPredeploys() - return &Processor{log, l1Client, l2Client, cfg.L1Contracts, l2Contracts} + return &Processor{log, l1Client, l2Client, cfg, l2Contracts} } -func (b *Processor) ProveWithdrawalTransaction(botDelegatedWithdrawToEvent *L2ContractEvent) error { - receipt, err := b.L2Client.TransactionReceipt(context.Background(), common.HexToHash(botDelegatedWithdrawToEvent.TransactionHash)) - if err != nil { - return err - } - - l2BlockNumber := receipt.BlockNumber - +func (b *Processor) toWithdrawal(botDelegatedWithdrawToEvent *L2ContractEvent, receipt *types.Receipt) (*bindings.TypesWithdrawalTransaction, error) { // Events flow: // // event[i-5]: WithdrawalInitiated @@ -58,7 +47,7 @@ func (b *Processor) ProveWithdrawalTransaction(botDelegatedWithdrawToEvent *L2Co // event[i-1]: SentMessageExtension1 // event[i] : L2StandardBridgeBot.WithdrawTo if botDelegatedWithdrawToEvent.LogIndex < 5 || len(receipt.Logs) < 5 { - return fmt.Errorf("invalid botDelegatedWithdrawToEvent: %v", botDelegatedWithdrawToEvent) + return nil, fmt.Errorf("invalid botDelegatedWithdrawToEvent: %v", botDelegatedWithdrawToEvent) } messagePassedLog := receipt.Logs[botDelegatedWithdrawToEvent.LogIndex-3] @@ -67,16 +56,31 @@ func (b *Processor) ProveWithdrawalTransaction(botDelegatedWithdrawToEvent *L2Co sentMessageEvent, err := b.toL2CrossDomainMessengerSentMessageExtension1(sentMessageLog, sentMessageExtension1Log) if err != nil { - return err + return nil, err } messagePassedEvent, err := b.toMessagePassed(messagePassedLog) if err != nil { - return err + return nil, err } withdrawalTx, err := b.toLowLevelMessage(sentMessageEvent, messagePassedEvent) if err != nil { - return fmt.Errorf("toLowLevelMessage err: %v", err) + return nil, fmt.Errorf("toLowLevelMessage err: %v", err) + } + + return withdrawalTx, nil +} + +func (b *Processor) ProveWithdrawalTransaction(botDelegatedWithdrawToEvent *L2ContractEvent) error { + receipt, err := b.L2Client.TransactionReceipt(context.Background(), common.HexToHash(botDelegatedWithdrawToEvent.TransactionHash)) + if err != nil { + return err + } + + l2BlockNumber := receipt.BlockNumber + withdrawalTx, err := b.toWithdrawal(botDelegatedWithdrawToEvent, receipt) + if err != nil { + return fmt.Errorf("toWithdrawal err: %v", err) } hash, err := b.hashWithdrawal(withdrawalTx) @@ -125,33 +129,22 @@ func (b *Processor) ProveWithdrawalTransaction(botDelegatedWithdrawToEvent *L2Co return err } - // Retrieve the private key from the environment variable OPBNB_BRIDGE_BOT_PRIVKEY - botPrivkey, err := crypto.HexToECDSA(os.Getenv("OPBNB_BRIDGE_BOT_PRIVKEY")) + gasPrice := big.NewInt(b.cfg.Signer.GasPrice) + signerPrivkey, signerAddress, err := b.cfg.SignerKeyPair() if err != nil { - return fmt.Errorf("invalid $OPBNB_BRIDGE_BOT_PRIVKEY: %w", err) - } - - pubKey := botPrivkey.Public() - pubKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) - if !ok { - return errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + return err } - pubKeyBytes := crypto.FromECDSAPub(pubKeyECDSA) - pubKeyHash := crypto.Keccak256(pubKeyBytes[1:])[12:] - fromAddress := common.HexToAddress(hexutil.Encode(pubKeyHash)) - - const gasPrice = 9000000000 // 9 GWei optimismPortalTransactor, _ := bindings.NewOptimismPortalTransactor( - b.L1Contracts.OptimismPortalProxy, + b.cfg.L1Contracts.OptimismPortalProxy, b.L1Client, ) signedTx, err := optimismPortalTransactor.ProveWithdrawalTransaction( &bind.TransactOpts{ - From: fromAddress, - GasPrice: big.NewInt(gasPrice), + From: *signerAddress, + GasPrice: gasPrice, Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - return types.SignTx(tx, types.NewEIP155Signer(l1ChainId), botPrivkey) + return types.SignTx(tx, types.NewEIP155Signer(l1ChainId), signerPrivkey) }, }, *withdrawalTx, @@ -174,34 +167,9 @@ func (b *Processor) FinalizeMessage(botDelegatedWithdrawToEvent *L2ContractEvent return err } - // Events flow: - // - // event[i-5]: WithdrawalInitiated - // event[i-4]: ETHBridgeInitiated - // event[i-3]: MessagePassed - // event[i-2]: SentMessage - // event[i-1]: SentMessageExtension1 - // event[i] : L2StandardBridgeBot.WithdrawTo - if botDelegatedWithdrawToEvent.LogIndex < 5 || len(receipt.Logs) < 5 { - return fmt.Errorf("invalid botDelegatedWithdrawToEvent: %v", botDelegatedWithdrawToEvent) - } - - messagePassedLog := receipt.Logs[botDelegatedWithdrawToEvent.LogIndex-3] - sentMessageLog := receipt.Logs[botDelegatedWithdrawToEvent.LogIndex-2] - sentMessageExtension1Log := receipt.Logs[botDelegatedWithdrawToEvent.LogIndex-1] - - sentMessageEvent, err := b.toL2CrossDomainMessengerSentMessageExtension1(sentMessageLog, sentMessageExtension1Log) - if err != nil { - return err - } - messagePassedEvent, err := b.toMessagePassed(messagePassedLog) - if err != nil { - return err - } - - withdrawalTx, err := b.toLowLevelMessage(sentMessageEvent, messagePassedEvent) + withdrawalTx, err := b.toWithdrawal(botDelegatedWithdrawToEvent, receipt) if err != nil { - return fmt.Errorf("toLowLevelMessage err: %v", err) + return fmt.Errorf("toWithdrawal err: %v", err) } l1ChainId, err := b.L1Client.ChainID(context.Background()) @@ -209,33 +177,22 @@ func (b *Processor) FinalizeMessage(botDelegatedWithdrawToEvent *L2ContractEvent return err } - // Retrieve the private key from the environment variable OPBNB_BRIDGE_BOT_PRIVKEY - botPrivkey, err := crypto.HexToECDSA(os.Getenv("OPBNB_BRIDGE_BOT_PRIVKEY")) + gasPrice := big.NewInt(b.cfg.Signer.GasPrice) + signerPrivkey, signerAddress, err := b.cfg.SignerKeyPair() if err != nil { - return fmt.Errorf("invalid $OPBNB_BRIDGE_BOT_PRIVKEY: %w", err) - } - - pubKey := botPrivkey.Public() - pubKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) - if !ok { - return errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + return err } - pubKeyBytes := crypto.FromECDSAPub(pubKeyECDSA) - pubKeyHash := crypto.Keccak256(pubKeyBytes[1:])[12:] - fromAddress := common.HexToAddress(hexutil.Encode(pubKeyHash)) - - const gasPrice = 9000000000 // 9 GWei optimismPortalTransactor, _ := bindings.NewOptimismPortalTransactor( - b.L1Contracts.OptimismPortalProxy, + b.cfg.L1Contracts.OptimismPortalProxy, b.L1Client, ) signedTx, err := optimismPortalTransactor.FinalizeWithdrawalTransaction( &bind.TransactOpts{ - From: fromAddress, - GasPrice: big.NewInt(gasPrice), + From: *signerAddress, + GasPrice: gasPrice, Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - return types.SignTx(tx, types.NewEIP155Signer(l1ChainId), botPrivkey) + return types.SignTx(tx, types.NewEIP155Signer(l1ChainId), signerPrivkey) }, }, *withdrawalTx, @@ -492,7 +449,7 @@ func (b *Processor) getMessagePassedMessagesFromReceipt(receipt *types.Receipt) func (b *Processor) getL2OutputAfter(l2BlockNumber *big.Int) (*big.Int, *bindings.TypesOutputProposal, error) { l2OutputOracleCaller, err := bindings.NewL2OutputOracleCaller( - b.L1Contracts.L2OutputOracleProxy, + b.cfg.L1Contracts.L2OutputOracleProxy, b.L1Client, ) if err != nil { @@ -535,7 +492,7 @@ func (b *Processor) toLowLevelMessage( withdrawalTx := bindings.TypesWithdrawalTransaction{ Nonce: messagePassedEvent.Nonce, Sender: b.L2Contracts.L2CrossDomainMessenger, - Target: b.L1Contracts.L1CrossDomainMessengerProxy, + Target: b.cfg.L1Contracts.L1CrossDomainMessengerProxy, Value: sentMessageEvent.Value, GasLimit: messagePassedEvent.GasLimit, Data: relayMessageCalldata, diff --git a/core/types.go b/core/types.go index 989f999..fcd70f4 100644 --- a/core/types.go +++ b/core/types.go @@ -8,7 +8,7 @@ type L2ContractEvent struct { ID uint `gorm:"primarykey"` BlockTime int64 `gorm:"type:integer;not null;index:idx_l2_contract_events_block_time"` BlockHash string `gorm:"type:varchar(256);not null;uniqueIndex:idx_l2_contract_events_block_hash_log_index_key,priority:1;"` - ContractAddress string `gorm:"type:varchar(256);not nul"` + ContractAddress string `gorm:"type:varchar(256);not null"` TransactionHash string `gorm:"type:varchar(256);not null"` LogIndex int `gorm:"type:integer;not null;uniqueIndex:idx_l2_contract_events_block_hash_log_index_key,priority:2"` EventSignature string `gorm:"type:varchar(256);not null"`