diff --git a/common/chain.go b/common/chain.go index 63cb26b213..010e899258 100644 --- a/common/chain.go +++ b/common/chain.go @@ -102,6 +102,14 @@ func IsEVMChain(chainID int64) bool { chainID == 137 // polygon mainnet } +func IsHeaderSupportedEvmChain(chainID int64) bool { + return chainID == 5 || // Goerli + chainID == 97 || // BSC testnet + chainID == 1337 || // eth privnet + chainID == 1 || // eth mainnet + chainID == 56 // bsc mainnet +} + func (chain Chain) IsKlaytnChain() bool { return chain.ChainId == 1001 } diff --git a/testutil/network/genesis_state.go b/testutil/network/genesis_state.go index 334c258746..ff1596e88d 100644 --- a/testutil/network/genesis_state.go +++ b/testutil/network/genesis_state.go @@ -105,9 +105,12 @@ func AddObserverData(t *testing.T, genesisState map[string]json.RawMessage, code state.Params.BallotMaturityBlocks = 3 state.Keygen = &observerTypes.Keygen{BlockNumber: 10, GranteePubkeys: []string{}} crosschainFlags := &observerTypes.CrosschainFlags{ - IsInboundEnabled: true, - IsOutboundEnabled: true, + IsInboundEnabled: true, + IsOutboundEnabled: true, + GasPriceIncreaseFlags: &observerTypes.DefaultGasPriceIncreaseFlags, + BlockHeaderVerificationFlags: &observerTypes.DefaultBlockHeaderVerificationFlags, } + nullify.Fill(&crosschainFlags) state.CrosschainFlags = crosschainFlags diff --git a/testutil/sample/common.go b/testutil/sample/common.go index 77daaf09b0..a2d3c18dd7 100644 --- a/testutil/sample/common.go +++ b/testutil/sample/common.go @@ -1,7 +1,12 @@ package sample import ( + "context" + "math/big" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rlp" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/zeta-chain/zetacore/common" ) @@ -22,3 +27,18 @@ func PubKeySet() *common.PubKeySet { } return &pubKeySet } + +func EthHeader() (headerRLP []byte, err error) { + url := "https://rpc.ankr.com/eth_goerli" + client, err := ethclient.Dial(url) + if err != nil { + return + } + bn := int64(9889649) + block, err := client.BlockByNumber(context.Background(), big.NewInt(bn)) + if err != nil { + return + } + headerRLP, _ = rlp.EncodeToBytes(block.Header()) + return +} diff --git a/x/observer/genesis.go b/x/observer/genesis.go index 8c00a1bd4b..8e9303df78 100644 --- a/x/observer/genesis.go +++ b/x/observer/genesis.go @@ -39,8 +39,18 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) k.SetParams(ctx, params) // Set if defined + + crosschainFlags := types.DefaultCrosschainFlags() if genState.CrosschainFlags != nil { - k.SetCrosschainFlags(ctx, *genState.CrosschainFlags) + crosschainFlags.IsOutboundEnabled = genState.CrosschainFlags.IsOutboundEnabled + crosschainFlags.IsInboundEnabled = genState.CrosschainFlags.IsInboundEnabled + if genState.CrosschainFlags.BlockHeaderVerificationFlags != nil { + crosschainFlags.BlockHeaderVerificationFlags = genState.CrosschainFlags.BlockHeaderVerificationFlags + } + if genState.CrosschainFlags.GasPriceIncreaseFlags != nil { + crosschainFlags.GasPriceIncreaseFlags = genState.CrosschainFlags.GasPriceIncreaseFlags + } + k.SetCrosschainFlags(ctx, *crosschainFlags) } else { k.SetCrosschainFlags(ctx, *types.DefaultCrosschainFlags()) } diff --git a/x/observer/genesis_test.go b/x/observer/genesis_test.go index 6919685a52..30b9260821 100644 --- a/x/observer/genesis_test.go +++ b/x/observer/genesis_test.go @@ -31,7 +31,7 @@ func TestGenesis(t *testing.T) { sample.NodeAccount(), sample.NodeAccount(), }, - CrosschainFlags: sample.CrosschainFlags(), + CrosschainFlags: types.DefaultCrosschainFlags(), Keygen: sample.Keygen(t), LastObserverCount: sample.LastObserverCount(1000), CoreParamsList: sample.CoreParamsList(), diff --git a/x/observer/keeper/msg_server_add_block_header.go b/x/observer/keeper/msg_server_add_block_header.go index cfc3e32bcd..83f8c97dc0 100644 --- a/x/observer/keeper/msg_server_add_block_header.go +++ b/x/observer/keeper/msg_server_add_block_header.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "fmt" cosmoserrors "cosmossdk.io/errors" @@ -20,6 +21,20 @@ func (k msgServer) AddBlockHeader(goCtx context.Context, msg *types.MsgAddBlockH return nil, types.ErrNotAuthorizedPolicy } + crosschainFlags, found := k.GetCrosschainFlags(ctx) + if !found { + return nil, fmt.Errorf("crosschain flags not found") + } + if crosschainFlags.BlockHeaderVerificationFlags == nil { + return nil, fmt.Errorf("block header verification flags not found") + } + if common.IsBitcoinChain(msg.ChainId) && !crosschainFlags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled { + return nil, cosmoserrors.Wrapf(types.ErrBlockHeaderVerficationDisabled, "proof verification not enabled for bitcoin ,chain id: %d", msg.ChainId) + } + if common.IsEVMChain(msg.ChainId) && !crosschainFlags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled { + return nil, cosmoserrors.Wrapf(types.ErrBlockHeaderVerficationDisabled, "proof verification not enabled for evm ,chain id: %d", msg.ChainId) + } + // add vote to ballot ballot, _, err := k.FindBallot(ctx, msg.Digest(), chain, types.ObservationType_InBoundTx) if err != nil { @@ -37,7 +52,7 @@ func (k msgServer) AddBlockHeader(goCtx context.Context, msg *types.MsgAddBlockH /** * Vote finalized, add block header to store */ - _, found := k.GetBlockHeader(ctx, msg.BlockHash) + _, found = k.GetBlockHeader(ctx, msg.BlockHash) if found { hashString, err := common.HashToString(msg.ChainId, msg.BlockHash) if err != nil { diff --git a/x/observer/keeper/msg_server_add_block_header_test.go b/x/observer/keeper/msg_server_add_block_header_test.go new file mode 100644 index 0000000000..db77cac8ab --- /dev/null +++ b/x/observer/keeper/msg_server_add_block_header_test.go @@ -0,0 +1,96 @@ +//go:build TESTNET +// +build TESTNET + +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/common" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/keeper" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgServer_AddBlockHeader(t *testing.T) { + header, err := sample.EthHeader() + assert.NoError(t, err) + observerChain := common.GoerliChain() + observerAddress := sample.AccAddress() + // Add tests for btc headers : https://github.com/zeta-chain/node/issues/1336 + tt := []struct { + name string + msg *types.MsgAddBlockHeader + IsEthTypeChainEnabled bool + IsBtcTypeChainEnabled bool + wantErr require.ErrorAssertionFunc + }{ + { + name: "success submit eth header", + msg: &types.MsgAddBlockHeader{ + Creator: observerAddress, + ChainId: common.GoerliChain().ChainId, + BlockHash: sample.Bytes(), + Height: 1, + Header: common.NewEthereumHeader(header), + }, + IsEthTypeChainEnabled: true, + IsBtcTypeChainEnabled: true, + wantErr: require.NoError, + }, + { + name: "failure submit eth header eth disabled", + msg: &types.MsgAddBlockHeader{ + Creator: observerAddress, + ChainId: common.GoerliChain().ChainId, + BlockHash: sample.Bytes(), + Height: 1, + Header: common.NewEthereumHeader(header), + }, + IsEthTypeChainEnabled: false, + IsBtcTypeChainEnabled: true, + wantErr: func(t require.TestingT, err error, i ...interface{}) { + assert.ErrorIs(t, err, types.ErrBlockHeaderVerficationDisabled) + }, + }, + { + name: "failure submit eth header eth disabled", + msg: &types.MsgAddBlockHeader{ + Creator: sample.AccAddress(), + ChainId: common.GoerliChain().ChainId, + BlockHash: sample.Bytes(), + Height: 1, + Header: common.NewEthereumHeader(header), + }, + IsEthTypeChainEnabled: false, + IsBtcTypeChainEnabled: true, + wantErr: func(t require.TestingT, err error, i ...interface{}) { + assert.ErrorIs(t, err, types.ErrNotAuthorizedPolicy) + }, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + k, ctx := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + k.SetObserverMapper(ctx, &types.ObserverMapper{ + ObserverChain: &observerChain, + ObserverList: []string{observerAddress}, + }) + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsInboundEnabled: true, + IsOutboundEnabled: true, + GasPriceIncreaseFlags: nil, + BlockHeaderVerificationFlags: &types.BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: tc.IsEthTypeChainEnabled, + IsBtcTypeChainEnabled: tc.IsBtcTypeChainEnabled, + }, + }) + _, err := srv.AddBlockHeader(ctx, tc.msg) + tc.wantErr(t, err) + }) + } +} diff --git a/x/observer/migrations/v4/migrate_test.go b/x/observer/migrations/v4/migrate_test.go index 98f4439403..2e420749b9 100644 --- a/x/observer/migrations/v4/migrate_test.go +++ b/x/observer/migrations/v4/migrate_test.go @@ -25,6 +25,6 @@ func TestMigrateStore(t *testing.T) { assert.NoError(t, err) flags, found := k.GetCrosschainFlags(ctx) assert.True(t, found) - assert.False(t, flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) - assert.False(t, flags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled) + assert.True(t, flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) + assert.True(t, flags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled) } diff --git a/x/observer/types/crosschain_flags.go b/x/observer/types/crosschain_flags.go index 4dccfbc8c4..bc3db1e8f8 100644 --- a/x/observer/types/crosschain_flags.go +++ b/x/observer/types/crosschain_flags.go @@ -18,15 +18,17 @@ var DefaultGasPriceIncreaseFlags = GasPriceIncreaseFlags{ GasPriceIncreaseMax: 500, } +var DefaultBlockHeaderVerificationFlags = BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: true, + IsBtcTypeChainEnabled: true, +} + // DefaultCrosschainFlags returns the default crosschain flags used when not defined func DefaultCrosschainFlags() *CrosschainFlags { return &CrosschainFlags{ - IsInboundEnabled: true, - IsOutboundEnabled: true, - GasPriceIncreaseFlags: &DefaultGasPriceIncreaseFlags, - BlockHeaderVerificationFlags: &BlockHeaderVerificationFlags{ - IsEthTypeChainEnabled: false, - IsBtcTypeChainEnabled: false, - }, + IsInboundEnabled: true, + IsOutboundEnabled: true, + GasPriceIncreaseFlags: &DefaultGasPriceIncreaseFlags, + BlockHeaderVerificationFlags: &DefaultBlockHeaderVerificationFlags, } } diff --git a/x/observer/types/errors.go b/x/observer/types/errors.go index 3767712e34..c4659faa33 100644 --- a/x/observer/types/errors.go +++ b/x/observer/types/errors.go @@ -25,9 +25,10 @@ var ( ErrKeygenCompleted = errorsmod.Register(ModuleName, 1115, "keygen already completed") ErrNotAuthorized = errorsmod.Register(ModuleName, 1116, "not authorized") - ErrBlockHeaderNotFound = errorsmod.Register(ModuleName, 1117, "block header not found") - ErrUnrecognizedBlockHeader = errorsmod.Register(ModuleName, 1118, "unrecognized block header") - ErrBlockAlreadyExist = errorsmod.Register(ModuleName, 1119, "block already exists") - ErrNoParentHash = errorsmod.Register(ModuleName, 1120, "no parent hash") - ErrInvalidTimestamp = errorsmod.Register(ModuleName, 1121, "invalid timestamp") + ErrBlockHeaderNotFound = errorsmod.Register(ModuleName, 1117, "block header not found") + ErrUnrecognizedBlockHeader = errorsmod.Register(ModuleName, 1118, "unrecognized block header") + ErrBlockAlreadyExist = errorsmod.Register(ModuleName, 1119, "block already exists") + ErrNoParentHash = errorsmod.Register(ModuleName, 1120, "no parent hash") + ErrInvalidTimestamp = errorsmod.Register(ModuleName, 1121, "invalid timestamp") + ErrBlockHeaderVerficationDisabled = errorsmod.Register(ModuleName, 1122, "block header verification is disabled") ) diff --git a/x/observer/types/messages_add_block_header.go b/x/observer/types/messages_add_block_header.go index cfa2867128..f7094a002c 100644 --- a/x/observer/types/messages_add_block_header.go +++ b/x/observer/types/messages_add_block_header.go @@ -52,7 +52,7 @@ func (msg *MsgAddBlockHeader) ValidateBasic() error { return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, err.Error()) } - if common.IsEthereumChain(msg.ChainId) || common.IsBitcoinChain(msg.ChainId) { + if common.IsHeaderSupportedEvmChain(msg.ChainId) || common.IsBitcoinChain(msg.ChainId) { if len(msg.BlockHash) != 32 { return cosmoserrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid block hash length (%d)", len(msg.BlockHash)) }