diff --git a/clientcontroller/evm_consumer.go b/clientcontroller/evm_consumer.go index 1bad8d47..e8d8f858 100644 --- a/clientcontroller/evm_consumer.go +++ b/clientcontroller/evm_consumer.go @@ -1,15 +1,16 @@ package clientcontroller import ( + "context" "fmt" + "math/big" - finalitytypes "github.com/babylonchain/babylon/x/finality/types" fpcfg "github.com/babylonchain/finality-provider/finality-provider/config" "github.com/babylonchain/finality-provider/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/rpc" - "github.com/btcsuite/btcd/btcec/v2" + "github.com/ethereum-optimism/optimism/op-bindings/bindings" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" "go.uber.org/zap" ) @@ -20,9 +21,10 @@ import ( var _ ConsumerController = &EVMConsumerController{} type EVMConsumerController struct { - evmClient *rpc.Client - cfg *fpcfg.EVMConfig - logger *zap.Logger + l1Client *ethclient.Client + l2Client *ethclient.Client + cfg *fpcfg.EVMConfig + logger *zap.Logger } func NewEVMConsumerController( @@ -32,12 +34,17 @@ func NewEVMConsumerController( if err := evmCfg.Validate(); err != nil { return nil, fmt.Errorf("invalid config for EVM RPC client: %w", err) } - ec, err := rpc.Dial(evmCfg.RPCAddr) + l1Client, err := ethclient.Dial(evmCfg.RPCL1Addr) + if err != nil { + return nil, fmt.Errorf("failed to connect to the L1 RPC server %s: %w", evmCfg.RPCL1Addr, err) + } + l2Client, err := ethclient.Dial(evmCfg.RPCL2Addr) if err != nil { - return nil, fmt.Errorf("failed to connect to the EVM RPC server %s: %w", evmCfg.RPCAddr, err) + return nil, fmt.Errorf("failed to connect to the L2 RPC server %s: %w", evmCfg.RPCL2Addr, err) } return &EVMConsumerController{ - ec, + l1Client, + l2Client, evmCfg, logger, }, nil @@ -81,37 +88,77 @@ func (ec *EVMConsumerController) QueryFinalityProviderVotingPower(fpPk *btcec.Pu } func (ec *EVMConsumerController) QueryLatestFinalizedBlock() (*types.BlockInfo, error) { - return &types.BlockInfo{ - Height: 0, - Hash: nil, - }, nil + + lastNumber, err := ec.queryLatestFinalizedNumber() + if err != nil { + return nil, fmt.Errorf("can't get latest finalized block number:%s", err) + } + + block, err := ec.QueryBlock(lastNumber) + if err != nil { + return nil, fmt.Errorf("can't get latest finalized block:%s", err) + } + + return block, nil } func (ec *EVMConsumerController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) { - return ec.queryLatestBlocks(sdk.Uint64ToBigEndian(startHeight), 0, finalitytypes.QueriedBlockStatus_ANY, false) -} + if endHeight < startHeight { + return nil, fmt.Errorf("the startHeight %v should not be higher than the endHeight %v", startHeight, endHeight) + } + count := endHeight - startHeight + if count > limit { + count = limit + } -func (ec *EVMConsumerController) queryLatestBlocks(startKey []byte, count uint64, status finalitytypes.QueriedBlockStatus, reverse bool) ([]*types.BlockInfo, error) { var blocks []*types.BlockInfo + for i := 0; i < int(count); i++ { + + block, err := ec.QueryBlock(startHeight) + if err != nil { + return nil, fmt.Errorf("failed to get start block:%s", err) + } + blocks = append(blocks, block) + startHeight++ + + } + return blocks, nil } func (ec *EVMConsumerController) QueryBlock(height uint64) (*types.BlockInfo, error) { - return &types.BlockInfo{ - Height: height, - Hash: nil, - }, nil + number := new(big.Int).SetUint64(height) + + header, err := ec.l2Client.HeaderByNumber(context.Background(), number) + if err != nil { + return nil, fmt.Errorf("failed to get latest block:%s", err) + } + + blockinfo := &types.BlockInfo{ + Height: header.Number.Uint64(), + Hash: header.Hash().Bytes(), + } + + return blockinfo, nil } func (ec *EVMConsumerController) QueryIsBlockFinalized(height uint64) (bool, error) { - /* TODO: implement - 1. get the latest finalized block number from `latestBlockNumber()` in the L1 L2OutputOracle contract - 2. compare the block number with `height` - */ - return false, nil + + lastNumber, err := ec.queryLatestFinalizedNumber() + if err != nil { + return false, fmt.Errorf("can't get latest finalized block:%s", err) + } + + var finalized bool = false + + if height <= lastNumber { + finalized = true + } + + return finalized, nil } func (ec *EVMConsumerController) QueryActivatedHeight() (uint64, error) { @@ -138,14 +185,34 @@ func (ec *EVMConsumerController) QueryActivatedHeight() (uint64, error) { } func (ec *EVMConsumerController) QueryLatestBlockHeight() (uint64, error) { - /* TODO: implement - get the latest L2 block number from a RPC call - */ - return uint64(0), nil + header, err := ec.l2Client.HeaderByNumber(context.Background(), nil) + if err != nil { + return 0, fmt.Errorf("failed to get latest block:%s", err) + } + + return header.Number.Uint64(), nil } func (ec *EVMConsumerController) Close() error { - ec.evmClient.Close() + + ec.l1Client.Close() + ec.l2Client.Close() + return nil } + +func (ec *EVMConsumerController) queryLatestFinalizedNumber() (uint64, error) { + + output, err := bindings.NewL2OutputOracle(common.HexToAddress(ec.cfg.L2OutputOracleAddr), ec.l1Client) + if err != nil { + return 0, fmt.Errorf("failed to instantiate L2OutputOracle contract:%s ", err) + } + + lastNumber, err := output.LatestBlockNumber(nil) + if err != nil { + return 0, fmt.Errorf("failed to get latest finalize block number:%s ", err) + } + + return lastNumber.Uint64(), err +} diff --git a/finality-provider/config/evm.go b/finality-provider/config/evm.go index 68f9714a..2db91716 100644 --- a/finality-provider/config/evm.go +++ b/finality-provider/config/evm.go @@ -10,18 +10,24 @@ const ( ) type EVMConfig struct { - RPCAddr string `long:"rpc-address" description:"address of the rpc server to connect to"` + RPCL1Addr string `long:"rpcl1-address" description:"address of the L1 RPC server to connect to"` + RPCL2Addr string `long:"rpcl2-address" description:"address of the L2 RPC server to connect to"` + L2OutputOracleAddr string `long:"l2outputoracle-address" description:"address of the L2OutputOracle smart contract"` + BSAddr string `long:"bitcoinstacking-address" description:"address of the BitcoinStaking smart contract"` } func DefaultEVMConfig() EVMConfig { return EVMConfig{ - RPCAddr: defaultEVMRPCAddr, + RPCL2Addr: defaultEVMRPCAddr, } } func (cfg *EVMConfig) Validate() error { - if _, err := url.Parse(cfg.RPCAddr); err != nil { - return fmt.Errorf("rpc-addr is not correctly formatted: %w", err) + if _, err := url.Parse(cfg.RPCL1Addr); err != nil { + return fmt.Errorf("rpcl1-addr is not correctly formatted: %w", err) + } + if _, err := url.Parse(cfg.RPCL2Addr); err != nil { + return fmt.Errorf("rpcl2-addr is not correctly formatted: %w", err) } return nil } diff --git a/go.mod b/go.mod index 2e0732dd..f9aa8b07 100644 --- a/go.mod +++ b/go.mod @@ -114,6 +114,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/emicklei/dot v1.6.1 // indirect + github.com/ethereum-optimism/optimism/op-bindings v0.10.14 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -226,7 +227,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -248,6 +249,7 @@ require ( github.com/ulikunitz/xz v0.5.11 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.8 // indirect diff --git a/go.sum b/go.sum index c849d503..cfad2d45 100644 --- a/go.sum +++ b/go.sum @@ -509,6 +509,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/ethereum-optimism/optimism/op-bindings v0.10.14 h1:SMMnMdNb1QIhJDyvk7QMUv+crAP4UHHoSYBOASBDIjM= +github.com/ethereum-optimism/optimism/op-bindings v0.10.14/go.mod h1:9ZSUq/rjlzp3uYyBN4sZmhTc3oZgDVqJ4wrUja7vj6c= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= @@ -565,6 +567,7 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -1180,6 +1183,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1293,6 +1298,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=