diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 25733e1fef..759b620b2c 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -276,7 +276,7 @@ func ZetaAccounting(t *testing.T, index string) types.ZetaAccounting { func InboundVote(coinType coin.CoinType, from, to int64) types.MsgVoteInbound { return types.MsgVoteInbound{ - Creator: "", + Creator: Bech32AccAddress().String(), Sender: EthAddress().String(), SenderChainId: Chain(from).ChainId, Receiver: EthAddress().String(), diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index 9056e3aa1d..7bfd0c50de 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -466,7 +466,7 @@ func (ob *Observer) ReadLastTxScannedFromDB() (string, error) { return lastTx.Hash, nil } -// PostVoteInbound posts a vote for the given vote message +// PostVoteInbound posts a vote for the given vote message and returns the ballot. func (ob *Observer) PostVoteInbound( ctx context.Context, msg *crosschaintypes.MsgVoteInbound, @@ -477,19 +477,26 @@ func (ob *Observer) PostVoteInbound( var ( txHash = msg.InboundHash coinType = msg.CoinType - chainID = ob.Chain().ChainId ) - zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound(ctx, gasLimit, retryGasLimit, msg) - + // prepare logger fields lf := map[string]any{ - "inbound.chain_id": chainID, - "inbound.coin_type": coinType.String(), - "inbound.external_tx_hash": txHash, - "inbound.ballot_index": ballot, - "inbound.zeta_tx_hash": zetaHash, + logs.FieldMethod: "PostVoteInbound", + logs.FieldTx: txHash, + logs.FieldCoinType: coinType.String(), + } + + // make sure the message is valid to avoid unnecessary retries + if err := msg.ValidateBasic(); err != nil { + ob.logger.Inbound.Warn().Err(err).Fields(lf).Msg("invalid inbound vote message") + return "", nil } + // post vote to zetacore + zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound(ctx, gasLimit, retryGasLimit, msg) + lf[logs.FieldZetaTx] = zetaHash + lf[logs.FieldBallot] = ballot + switch { case err != nil: ob.logger.Inbound.Error().Err(err).Fields(lf).Msg("inbound detected: error posting vote") diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index 0c53bea35c..44dc296a8a 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strings" "testing" "time" @@ -16,6 +17,7 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/testutil/sample" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/chains/interfaces" @@ -626,6 +628,24 @@ func TestPostVoteInbound(t *testing.T) { require.NoError(t, err) require.Equal(t, "sampleBallotIndex", ballot) }) + + t.Run("should not post vote if message basic validation fails", func(t *testing.T) { + // create observer + ob := createObserver(t, chains.Ethereum, defaultAlertLatency) + + // create mock zetacore client + zetacoreClient := mocks.NewZetacoreClient(t) + ob = ob.WithZetacoreClient(zetacoreClient) + + // create sample message with long Message + msg := sample.InboundVote(coin.CoinType_Gas, chains.Ethereum.ChainId, chains.ZetaChainMainnet.ChainId) + msg.Message = strings.Repeat("1", crosschaintypes.MaxMessageLength+1) + + // post vote inbound + ballot, err := ob.PostVoteInbound(context.TODO(), &msg, 100000) + require.NoError(t, err) + require.Empty(t, ballot) + }) } func TestAlertOnRPCLatency(t *testing.T) { diff --git a/zetaclient/chains/bitcoin/observer/event.go b/zetaclient/chains/bitcoin/observer/event.go index 9ec2defdde..bff127dc25 100644 --- a/zetaclient/chains/bitcoin/observer/event.go +++ b/zetaclient/chains/bitcoin/observer/event.go @@ -153,14 +153,12 @@ func ValidateStandardMemo(memoStd memo.InboundMemo, chainID int64) error { // IsEventProcessable checks if the inbound event is processable func (ob *Observer) IsEventProcessable(event BTCInboundEvent) bool { + logFields := map[string]any{logs.FieldTx: event.TxHash} + switch category := event.Category(); category { case clienttypes.InboundCategoryGood: return true case clienttypes.InboundCategoryDonation: - logFields := map[string]any{ - logs.FieldChain: ob.Chain().ChainId, - logs.FieldTx: event.TxHash, - } ob.Logger().Inbound.Info().Fields(logFields).Msgf("thank you rich folk for your donation!") return false case clienttypes.InboundCategoryRestricted: @@ -168,7 +166,7 @@ func (ob *Observer) IsEventProcessable(event BTCInboundEvent) bool { false, ob.Chain().ChainId, event.TxHash, event.FromAddress, event.ToAddress, "BTC") return false default: - ob.Logger().Inbound.Error().Msgf("unreachable code got InboundProcessability: %v", category) + ob.Logger().Inbound.Error().Fields(logFields).Msgf("unreachable code got InboundCategory: %v", category) return false } } diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index dece5433a4..27f0839856 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -282,21 +282,7 @@ func (ob *Observer) CheckReceiptForBtcTxHash(ctx context.Context, txHash string, return msg.Digest(), nil } - zetaHash, ballot, err := ob.ZetacoreClient().PostVoteInbound( - ctx, - zetacore.PostVoteInboundGasLimit, - zetacore.PostVoteInboundExecutionGasLimit, - msg, - ) - if err != nil { - ob.logger.Inbound.Error().Err(err).Msg("error posting to zetacore") - return "", err - } else if zetaHash != "" { - ob.logger.Inbound.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx hash: %s inbound %s ballot %s fee %v", - zetaHash, txHash, ballot, event.DepositorFee) - } - - return msg.Digest(), nil + return ob.PostVoteInbound(ctx, msg, zetacore.PostVoteInboundExecutionGasLimit) } // FilterAndParseIncomingTx given txs list returned by the "getblock 2" RPC command, return the txs that are relevant to us @@ -364,22 +350,13 @@ func (ob *Observer) GetInboundVoteFromBtcEvent(event *BTCInboundEvent) *crosscha } amountInt := big.NewInt(amountSats) - // create inbound vote message contract V1 for legacy memo or standard memo - var msg *crosschaintypes.MsgVoteInbound + // create inbound vote message contract V1 for legacy memo if event.MemoStd == nil { - msg = ob.NewInboundVoteFromLegacyMemo(event, amountInt) - } else { - msg = ob.NewInboundVoteFromStdMemo(event, amountInt) - } - - // make sure the message is valid before posting to zetacore - err = msg.ValidateBasic() - if err != nil { - ob.Logger().Inbound.Error().Err(err).Fields(lf).Msg("invalid inbound vote message") - return nil + return ob.NewInboundVoteFromLegacyMemo(event, amountInt) } - return msg + // create inbound vote message for standard memo + return ob.NewInboundVoteFromStdMemo(event, amountInt) } // GetBtcEvent returns a valid BTCInboundEvent or nil diff --git a/zetaclient/chains/bitcoin/observer/inbound_test.go b/zetaclient/chains/bitcoin/observer/inbound_test.go index ba005a1303..7ec938aab0 100644 --- a/zetaclient/chains/bitcoin/observer/inbound_test.go +++ b/zetaclient/chains/bitcoin/observer/inbound_test.go @@ -157,9 +157,7 @@ func Test_GetInboundVoteFromBtcEvent(t *testing.T) { // create test observer ob := MockBTCObserver(t, chain, params, nil) - zetacoreClient := mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{ - OperatorAddress: sample.Bech32AccAddress(), - }).WithZetaChain() + zetacoreClient := mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{}).WithZetaChain() ob.WithZetacoreClient(zetacoreClient) // test cases diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index ea43788fd8..8c1a270e80 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -274,7 +274,7 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent } // create inbound vote message - msg := crosschaintypes.NewMsgVoteInbound( + return crosschaintypes.NewMsgVoteInbound( ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), event.Sender, event.SenderChainID, @@ -292,27 +292,16 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent crosschaintypes.ProtocolContractVersion_V1, false, // not relevant for v1 ) - - // make sure the message is valid before posting to zetacore - err = msg.ValidateBasic() - if err != nil { - ob.Logger().Inbound.Error().Err(err).Fields(lf).Msg("invalid inbound vote message") - return nil - } - - return msg } // IsEventProcessable checks if the inbound event is processable func (ob *Observer) IsEventProcessable(event clienttypes.InboundEvent) bool { + logFields := map[string]any{logs.FieldTx: event.TxHash} + switch category := event.Category(); category { case clienttypes.InboundCategoryGood: return true case clienttypes.InboundCategoryDonation: - logFields := map[string]any{ - logs.FieldChain: ob.Chain().ChainId, - logs.FieldTx: event.TxHash, - } ob.Logger().Inbound.Info().Fields(logFields).Msgf("thank you rich folk for your donation!") return false case clienttypes.InboundCategoryRestricted: @@ -320,7 +309,7 @@ func (ob *Observer) IsEventProcessable(event clienttypes.InboundEvent) bool { false, ob.Chain().ChainId, event.TxHash, event.Sender, event.Receiver, event.CoinType.String()) return false default: - ob.Logger().Inbound.Error().Msgf("unreachable code got InboundProcessability: %v", category) + ob.Logger().Inbound.Error().Msgf("unreachable code got InboundCategory: %v", category) return false } } diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index 02c4a95efd..0b118ae55e 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -2,7 +2,6 @@ package observer_test import ( "context" - "strings" "testing" "github.com/stretchr/testify/require" @@ -10,7 +9,6 @@ import ( "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/pkg/constant" "github.com/zeta-chain/node/testutil/sample" - crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/chains/solana/observer" "github.com/zeta-chain/node/zetaclient/config" @@ -110,9 +108,7 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { params := sample.ChainParams(chain.ChainId) params.GatewayAddress = sample.SolanaAddress(t) zetacoreClient := mocks.NewZetacoreClient(t) - zetacoreClient.WithKeys(&keys.Keys{ - OperatorAddress: sample.Bech32AccAddress(), - }).WithZetaChain().WithPostVoteInbound("", "") + zetacoreClient.WithKeys(&keys.Keys{}).WithZetaChain().WithPostVoteInbound("", "") database, err := db.NewFromSqliteInMemory(true) require.NoError(t, err) @@ -155,16 +151,6 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { msg := ob.BuildInboundVoteMsgFromEvent(event) require.Nil(t, msg) }) - - t.Run("should return nil if message basic validation fails", func(t *testing.T) { - // create event with donation memo - sender := sample.SolanaAddress(t) - maxMsgBytes := crosschaintypes.MaxMessageLength / 2 - event := sample.InboundEvent(chain.ChainId, sender, sender, 1280, []byte(strings.Repeat("a", maxMsgBytes+1))) - - msg := ob.BuildInboundVoteMsgFromEvent(event) - require.Nil(t, msg) - }) } func Test_IsEventProcessable(t *testing.T) { diff --git a/zetaclient/logs/fields.go b/zetaclient/logs/fields.go index 78b95fc7e0..58880543af 100644 --- a/zetaclient/logs/fields.go +++ b/zetaclient/logs/fields.go @@ -10,6 +10,9 @@ const ( FieldNonce = "nonce" FieldTx = "tx" FieldCctx = "cctx" + FieldZetaTx = "zeta_tx" + FieldBallot = "ballot" + FieldCoinType = "coin_type" // module names ModNameInbound = "inbound"