diff --git a/changelog.md b/changelog.md index da4d082c95..412141d2f8 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ ## Unreleased +## v21.0.0 + ### Features * [2633](https://github.com/zeta-chain/node/pull/2633) - support for stateful precompiled contracts @@ -59,6 +61,7 @@ * [2909](https://github.com/zeta-chain/node/pull/2909) - add legacy messages back to codec for querier backward compatibility * [3018](https://github.com/zeta-chain/node/pull/3018) - support `DepositAndCall` and `WithdrawAndCall` with empty payload * [3030](https://github.com/zeta-chain/node/pull/3030) - Avoid storing invalid Solana gateway address in the `SetGatewayAddress` +* [3047](https://github.com/zeta-chain/node/pull/3047) - wrong block hash in subscribe new heads ## v20.0.0 diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index a91118168a..60759d7e0a 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -404,8 +404,12 @@ func (b *Backend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Heade ) } - ethHeader := rpctypes.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) - return ethHeader, nil + validatorAccount, err := GetValidatorAccount(&resBlock.Block.Header, b.queryClient) + if err != nil { + return nil, err + } + + return rpctypes.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee, validatorAccount), nil } // HeaderByHash returns the block header identified by hash. @@ -440,8 +444,12 @@ func (b *Backend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) ) } - ethHeader := rpctypes.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee) - return ethHeader, nil + validatorAccount, err := GetValidatorAccount(&resBlock.Block.Header, b.queryClient) + if err != nil { + return nil, err + } + + return rpctypes.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFee, validatorAccount), nil } // BlockBloom query block bloom filter from block results @@ -613,7 +621,12 @@ func (b *Backend) EthBlockFromTendermintBlock( ) } - ethHeader := rpctypes.EthHeaderFromTendermint(block.Header, bloom, baseFee) + validatorAccount, err := GetValidatorAccount(&resBlock.Block.Header, b.queryClient) + if err != nil { + return nil, err + } + + ethHeader := rpctypes.EthHeaderFromTendermint(block.Header, bloom, baseFee, validatorAccount) msgs, additionals := b.EthMsgsFromTendermintBlock(resBlock, blockRes) txs := []*ethtypes.Transaction{} diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go index 5da81d03c9..8fdea01533 100644 --- a/rpc/backend/blocks_test.go +++ b/rpc/backend/blocks_test.go @@ -1191,6 +1191,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { var expResultBlock *tmrpctypes.ResultBlock _, bz := suite.buildEthereumTx() + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) testCases := []struct { name string @@ -1218,6 +1219,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { height := blockNum.Int64() client := suite.backend.clientCtx.Client.(*mocks.Client) RegisterBlockNotFound(client, height) + }, false, }, @@ -1245,6 +1247,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFeeError(queryClient) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1260,6 +1263,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1275,6 +1279,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1287,7 +1292,12 @@ func (suite *BackendTestSuite) TestHeaderByNumber() { header, err := suite.backend.HeaderByNumber(tc.blockNumber) if tc.expPass { - expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) + expHeader := ethrpc.EthHeaderFromTendermint( + expResultBlock.Block.Header, + ethtypes.Bloom{}, + tc.baseFee, + validator, + ) suite.Require().NoError(err) suite.Require().Equal(expHeader, header) } else { @@ -1303,6 +1313,7 @@ func (suite *BackendTestSuite) TestHeaderByHash() { _, bz := suite.buildEthereumTx() block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) testCases := []struct { name string @@ -1355,6 +1366,7 @@ func (suite *BackendTestSuite) TestHeaderByHash() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFeeError(queryClient) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1370,6 +1382,7 @@ func (suite *BackendTestSuite) TestHeaderByHash() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1385,6 +1398,7 @@ func (suite *BackendTestSuite) TestHeaderByHash() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, true, }, @@ -1397,7 +1411,12 @@ func (suite *BackendTestSuite) TestHeaderByHash() { header, err := suite.backend.HeaderByHash(tc.hash) if tc.expPass { - expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) + expHeader := ethrpc.EthHeaderFromTendermint( + expResultBlock.Block.Header, + ethtypes.Bloom{}, + tc.baseFee, + validator, + ) suite.Require().NoError(err) suite.Require().Equal(expHeader, header) } else { @@ -1410,6 +1429,7 @@ func (suite *BackendTestSuite) TestHeaderByHash() { func (suite *BackendTestSuite) TestEthBlockByNumber() { msgEthereumTx, bz := suite.buildEthereumTx() emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) testCases := []struct { name string @@ -1453,12 +1473,14 @@ func (suite *BackendTestSuite) TestEthBlockByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) baseFee := sdk.NewInt(1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, ethtypes.NewBlock( ethrpc.EthHeaderFromTendermint( emptyBlock.Header, ethtypes.Bloom{}, sdk.NewInt(1).BigInt(), + validator, ), []*ethtypes.Transaction{}, nil, @@ -1479,12 +1501,14 @@ func (suite *BackendTestSuite) TestEthBlockByNumber() { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) baseFee := sdk.NewInt(1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, ethtypes.NewBlock( ethrpc.EthHeaderFromTendermint( emptyBlock.Header, ethtypes.Bloom{}, sdk.NewInt(1).BigInt(), + validator, ), []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, nil, @@ -1520,6 +1544,7 @@ func (suite *BackendTestSuite) TestEthBlockByNumber() { func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() { msgEthereumTx, bz := suite.buildEthereumTx() emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) testCases := []struct { name string @@ -1543,12 +1568,14 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() { func(baseFee sdkmath.Int, blockNum int64) { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, ethtypes.NewBlock( ethrpc.EthHeaderFromTendermint( emptyBlock.Header, ethtypes.Bloom{}, sdk.NewInt(1).BigInt(), + validator, ), []*ethtypes.Transaction{}, nil, @@ -1578,12 +1605,14 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() { func(baseFee sdkmath.Int, blockNum int64) { queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, ethtypes.NewBlock( ethrpc.EthHeaderFromTendermint( emptyBlock.Header, ethtypes.Bloom{}, sdk.NewInt(1).BigInt(), + validator, ), []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, nil, @@ -1657,6 +1686,8 @@ func (suite *BackendTestSuite) TestEthAndSyntheticEthBlockByNumber() { msgEthereumTx, _ := suite.buildEthereumTx() realTx := suite.signAndEncodeEthTx(msgEthereumTx) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) + suite.backend.indexer = nil client := suite.backend.clientCtx.Client.(*mocks.Client) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) @@ -1664,6 +1695,7 @@ func (suite *BackendTestSuite) TestEthAndSyntheticEthBlockByNumber() { RegisterBlock(client, 1, []tmtypes.Tx{realTx, tx}) RegisterBlockResultsWithTxResults(client, 1, []*types.ResponseDeliverTx{{}, &txRes}) RegisterBaseFee(queryClient, sdk.NewInt(1)) + RegisterValidatorAccount(queryClient, validator) // only real should be returned block, err := suite.backend.EthBlockByNumber(1) diff --git a/rpc/backend/call_tx_test.go b/rpc/backend/call_tx_test.go index ec92db089b..7d8d95275e 100644 --- a/rpc/backend/call_tx_test.go +++ b/rpc/backend/call_tx_test.go @@ -24,6 +24,7 @@ func (suite *BackendTestSuite) TestResend() { gasPrice := new(hexutil.Big) toAddr := tests.GenerateAddress() chainID := (*hexutil.Big)(suite.backend.chainID) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) callArgs := evmtypes.TransactionArgs{ From: nil, To: &toAddr, @@ -69,6 +70,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFeeDisabled(queryClient) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -91,6 +93,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -110,6 +113,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFeeDisabled(queryClient) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -163,6 +167,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -186,6 +191,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -210,6 +216,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterParams(queryClient, &header, 1) RegisterParamsWithoutHeader(queryClient, 1) RegisterUnconfirmedTxsError(client, nil) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -238,6 +245,7 @@ func (suite *BackendTestSuite) TestResend() { RegisterParams(queryClient, &header, 1) RegisterParamsWithoutHeader(queryClient, 1) RegisterUnconfirmedTxsEmpty(client, nil) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ Nonce: &txNonce, @@ -448,6 +456,7 @@ func (suite *BackendTestSuite) TestDoCall() { func (suite *BackendTestSuite) TestGasPrice() { defaultGasPrice := (*hexutil.Big)(big.NewInt(1)) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) testCases := []struct { name string @@ -467,6 +476,7 @@ func (suite *BackendTestSuite) TestGasPrice() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, sdk.NewInt(1)) + RegisterValidatorAccount(queryClient, validator) }, defaultGasPrice, true, @@ -483,6 +493,7 @@ func (suite *BackendTestSuite) TestGasPrice() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, sdk.NewInt(1)) + RegisterValidatorAccount(queryClient, validator) }, defaultGasPrice, false, diff --git a/rpc/backend/sign_tx_test.go b/rpc/backend/sign_tx_test.go index 1483b0b5e0..ee82303b0e 100644 --- a/rpc/backend/sign_tx_test.go +++ b/rpc/backend/sign_tx_test.go @@ -31,6 +31,7 @@ func (suite *BackendTestSuite) TestSendTransaction() { from := common.BytesToAddress(priv.PubKey().Address().Bytes()) nonce := hexutil.Uint64(1) baseFee := sdk.NewInt(1) + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) callArgsDefault := evmtypes.TransactionArgs{ From: &from, To: &toAddr, @@ -82,6 +83,7 @@ func (suite *BackendTestSuite) TestSendTransaction() { RegisterBlock(client, 1, nil) RegisterBlockResults(client, 1) RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) }, evmtypes.TransactionArgs{ From: &from, @@ -115,6 +117,7 @@ func (suite *BackendTestSuite) TestSendTransaction() { txBytes, err := txEncoder(tx) suite.Require().NoError(err) RegisterBroadcastTxError(client, txBytes) + RegisterValidatorAccount(queryClient, validator) }, callArgsDefault, common.Hash{}, @@ -142,6 +145,7 @@ func (suite *BackendTestSuite) TestSendTransaction() { txBytes, err := txEncoder(tx) suite.Require().NoError(err) RegisterBroadcastTx(client, txBytes) + RegisterValidatorAccount(queryClient, validator) }, callArgsDefault, hash, diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index bada8750c6..e297053fef 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -26,6 +26,7 @@ import ( "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/proto/tendermint/crypto" tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/ethereum/go-ethereum/common" @@ -318,3 +319,16 @@ func GetHexProofs(proof *crypto.ProofOps) []string { } return proofs } + +func GetValidatorAccount(header *tmtypes.Header, qc *types.QueryClient) (sdk.AccAddress, error) { + res, err := qc.ValidatorAccount( + types.ContextWithHeight(header.Height), + &evmtypes.QueryValidatorAccountRequest{ + ConsAddress: sdk.ConsAddress(header.ProposerAddress).String(), + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to get validator account %w", err) + } + return sdk.AccAddressFromBech32(res.AccountAddress) +} diff --git a/rpc/namespaces/ethereum/eth/filters/api.go b/rpc/namespaces/ethereum/eth/filters/api.go index 7b6d3d9ff3..83a262b01f 100644 --- a/rpc/namespaces/ethereum/eth/filters/api.go +++ b/rpc/namespaces/ethereum/eth/filters/api.go @@ -27,11 +27,13 @@ import ( tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/rpc" evmtypes "github.com/zeta-chain/ethermint/x/evm/types" + "github.com/zeta-chain/node/rpc/backend" "github.com/zeta-chain/node/rpc/types" ) @@ -81,12 +83,13 @@ type filter struct { // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various // information related to the Ethereum protocol such as blocks, transactions and logs. type PublicFilterAPI struct { - logger log.Logger - clientCtx client.Context - backend Backend - events *EventSystem - filtersMu sync.Mutex - filters map[rpc.ID]*filter + logger log.Logger + clientCtx client.Context + backend Backend + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter + queryClient *types.QueryClient } // NewPublicAPI returns a new PublicFilterAPI instance. @@ -98,11 +101,12 @@ func NewPublicAPI( ) *PublicFilterAPI { logger = logger.With("api", "filter") api := &PublicFilterAPI{ - logger: logger, - clientCtx: clientCtx, - backend: backend, - filters: make(map[rpc.ID]*filter), - events: NewEventSystem(logger, tmWSClient), + logger: logger, + clientCtx: clientCtx, + backend: backend, + filters: make(map[rpc.ID]*filter), + events: NewEventSystem(logger, tmWSClient), + queryClient: types.NewQueryClient(clientCtx), } go api.timeoutLoop() @@ -368,9 +372,35 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er baseFee := types.BaseFeeFromEvents(data.ResultBeginBlock.Events) + validatorAccount, err := backend.GetValidatorAccount(&data.Header, api.queryClient) + if err != nil { + api.logger.Error("failed to get validator account", "err", err) + continue + } + // TODO: fetch bloom from events - header := types.EthHeaderFromTendermint(data.Header, ethtypes.Bloom{}, baseFee) - err = notifier.Notify(rpcSub.ID, header) + header := types.EthHeaderFromTendermint(data.Header, ethtypes.Bloom{}, baseFee, validatorAccount) + + var enc types.Header + enc.ParentHash = header.ParentHash + enc.UncleHash = header.UncleHash + enc.Coinbase = header.Coinbase.Hex() + enc.Root = header.Root + enc.TxHash = header.TxHash + enc.ReceiptHash = header.ReceiptHash + enc.Bloom = header.Bloom + enc.Difficulty = (*hexutil.Big)(header.Difficulty) + enc.Number = (*hexutil.Big)(header.Number) + enc.GasLimit = hexutil.Uint64(header.GasLimit) + enc.GasUsed = hexutil.Uint64(header.GasUsed) + enc.Time = hexutil.Uint64(header.Time) + enc.Extra = header.Extra + enc.MixDigest = header.MixDigest + enc.Nonce = header.Nonce + enc.BaseFee = (*hexutil.Big)(header.BaseFee) + enc.Hash = common.BytesToHash(data.Header.Hash()) + + err = notifier.Notify(rpcSub.ID, enc) if err != nil { api.logger.Debug("failed to notify", "error", err.Error()) } diff --git a/rpc/types/block.go b/rpc/types/block.go index cb2c873709..ea1b872bcc 100644 --- a/rpc/types/block.go +++ b/rpc/types/block.go @@ -27,6 +27,7 @@ import ( grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/spf13/cast" ethermint "github.com/zeta-chain/ethermint/types" "google.golang.org/grpc/metadata" @@ -210,3 +211,26 @@ func (bnh *BlockNumberOrHash) decodeFromString(input string) error { } return nil } + +// https://github.com/ethereum/go-ethereum/blob/release/1.11/core/types/gen_header_json.go#L18 +type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + // update string avoid lost checksumed miner after MarshalText + Coinbase string `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom ethtypes.Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce ethtypes.BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + // overwrite rlpHash + Hash common.Hash `json:"hash"` +} diff --git a/rpc/types/utils.go b/rpc/types/utils.go index addce9521a..67ee50133a 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -26,6 +26,7 @@ import ( tmrpcclient "github.com/cometbft/cometbft/rpc/client" tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -61,7 +62,12 @@ func RawTxToEthTx(clientCtx client.Context, txBz tmtypes.Tx) ([]*evmtypes.MsgEth // EthHeaderFromTendermint is an util function that returns an Ethereum Header // from a tendermint Header. -func EthHeaderFromTendermint(header tmtypes.Header, bloom ethtypes.Bloom, baseFee *big.Int) *ethtypes.Header { +func EthHeaderFromTendermint( + header tmtypes.Header, + bloom ethtypes.Bloom, + baseFee *big.Int, + miner sdk.AccAddress, +) *ethtypes.Header { txHash := ethtypes.EmptyRootHash if len(header.DataHash) == 0 { txHash = common.BytesToHash(header.DataHash) @@ -70,7 +76,7 @@ func EthHeaderFromTendermint(header tmtypes.Header, bloom ethtypes.Bloom, baseFe return ðtypes.Header{ ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()), UncleHash: ethtypes.EmptyUncleHash, - Coinbase: common.BytesToAddress(header.ProposerAddress), + Coinbase: common.BytesToAddress(miner), Root: common.BytesToHash(header.AppHash), TxHash: txHash, ReceiptHash: ethtypes.EmptyRootHash, diff --git a/rpc/websockets.go b/rpc/websockets.go index c977c41acc..7d60ae62a3 100644 --- a/rpc/websockets.go +++ b/rpc/websockets.go @@ -32,15 +32,16 @@ import ( tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/pkg/errors" evmtypes "github.com/zeta-chain/ethermint/x/evm/types" + "github.com/zeta-chain/node/rpc/backend" "github.com/zeta-chain/node/rpc/ethereum/pubsub" rpcfilters "github.com/zeta-chain/node/rpc/namespaces/ethereum/eth/filters" "github.com/zeta-chain/node/rpc/types" @@ -378,18 +379,21 @@ func (s *websocketsServer) tcpGetAndSendResponse(wsConn *wsConn, mb []byte) erro // pubSubAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec type pubSubAPI struct { - events *rpcfilters.EventSystem - logger log.Logger - clientCtx client.Context + events *rpcfilters.EventSystem + logger log.Logger + clientCtx client.Context + queryClient *types.QueryClient } // newPubSubAPI creates an instance of the ethereum PubSub API. func newPubSubAPI(clientCtx client.Context, logger log.Logger, tmWSClient *rpcclient.WSClient) *pubSubAPI { logger = logger.With("module", "websocket-client") + types.NewQueryClient(clientCtx) return &pubSubAPI{ - events: rpcfilters.NewEventSystem(logger, tmWSClient), - logger: logger, - clientCtx: clientCtx, + events: rpcfilters.NewEventSystem(logger, tmWSClient), + logger: logger, + clientCtx: clientCtx, + queryClient: types.NewQueryClient(clientCtx), } } @@ -423,9 +427,6 @@ func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn, subID rpc.ID) (pubsub.Un return nil, errors.Wrap(err, "error creating block filter") } - // TODO: use events - baseFee := big.NewInt(params.InitialBaseFee) - go func() { headersCh := sub.Event() errCh := sub.Err() @@ -442,7 +443,34 @@ func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn, subID rpc.ID) (pubsub.Un continue } - header := types.EthHeaderFromTendermint(data.Header, ethtypes.Bloom{}, baseFee) + validatorAccount, err := backend.GetValidatorAccount(&data.Header, api.queryClient) + if err != nil { + api.logger.Error("failed to get validator account", "err", err) + continue + } + + baseFee := types.BaseFeeFromEvents(data.ResultBeginBlock.Events) + + header := types.EthHeaderFromTendermint(data.Header, ethtypes.Bloom{}, baseFee, validatorAccount) + + var enc types.Header + enc.ParentHash = header.ParentHash + enc.UncleHash = header.UncleHash + enc.Coinbase = header.Coinbase.Hex() + enc.Root = header.Root + enc.TxHash = header.TxHash + enc.ReceiptHash = header.ReceiptHash + enc.Bloom = header.Bloom + enc.Difficulty = (*hexutil.Big)(header.Difficulty) + enc.Number = (*hexutil.Big)(header.Number) + enc.GasLimit = hexutil.Uint64(header.GasLimit) + enc.GasUsed = hexutil.Uint64(header.GasUsed) + enc.Time = hexutil.Uint64(header.Time) + enc.Extra = header.Extra + enc.MixDigest = header.MixDigest + enc.Nonce = header.Nonce + enc.BaseFee = (*hexutil.Big)(header.BaseFee) + enc.Hash = common.BytesToHash(data.Header.Hash()) // write to ws conn res := &SubscriptionNotification{ @@ -450,7 +478,7 @@ func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn, subID rpc.ID) (pubsub.Un Method: "eth_subscription", Params: &SubscriptionResult{ Subscription: subID, - Result: header, + Result: enc, }, }