Skip to content

Commit

Permalink
avoid endless rescan when the inbound vote message is invalid
Browse files Browse the repository at this point in the history
  • Loading branch information
ws4charlie committed Nov 19, 2024
1 parent b5c3e03 commit 4653b46
Show file tree
Hide file tree
Showing 10 changed files with 344 additions and 84 deletions.
2 changes: 1 addition & 1 deletion x/crosschain/types/message_vote_inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const MaxMessageLength = 10240
// InboundVoteOption is a function that sets some option on the inbound vote message
type InboundVoteOption func(*MsgVoteInbound)

// WithMemoRevertOptions sets the revert options for inbound vote message
// WithRevertOptions sets the revert options for inbound vote message
func WithRevertOptions(revertOptions RevertOptions) InboundVoteOption {
return func(msg *MsgVoteInbound) {
msg.RevertOptions = revertOptions
Expand Down
31 changes: 9 additions & 22 deletions zetaclient/chains/bitcoin/observer/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,7 @@ import (
"github.com/zeta-chain/node/zetaclient/compliance"
"github.com/zeta-chain/node/zetaclient/config"
"github.com/zeta-chain/node/zetaclient/logs"
)

// InboundProcessability is an enum representing the processability of an inbound
type InboundProcessability int

const (
// InboundProcessabilityGood represents a processable inbound
InboundProcessabilityGood InboundProcessability = iota

// InboundProcessabilityDonation represents a donation inbound
InboundProcessabilityDonation

// InboundProcessabilityComplianceViolation represents a compliance violation
InboundProcessabilityComplianceViolation
clienttypes "github.com/zeta-chain/node/zetaclient/types"
)

// BTCInboundEvent represents an incoming transaction event
Expand Down Expand Up @@ -63,10 +50,10 @@ type BTCInboundEvent struct {
}

// Processability returns the processability of the inbound event
func (event *BTCInboundEvent) Processability() InboundProcessability {
func (event *BTCInboundEvent) Processability() clienttypes.InboundProcessability {
// compliance check on sender and receiver addresses
if config.ContainRestrictedAddress(event.FromAddress, event.ToAddress) {
return InboundProcessabilityComplianceViolation
return clienttypes.InboundProcessabilityComplianceViolation
}

// compliance check on receiver, revert/abort addresses in standard memo
Expand All @@ -76,16 +63,16 @@ func (event *BTCInboundEvent) Processability() InboundProcessability {
event.MemoStd.RevertOptions.RevertAddress,
event.MemoStd.RevertOptions.AbortAddress,
) {
return InboundProcessabilityComplianceViolation
return clienttypes.InboundProcessabilityComplianceViolation
}
}

// donation check
if bytes.Equal(event.MemoBytes, []byte(constant.DonationMessage)) {
return InboundProcessabilityDonation
return clienttypes.InboundProcessabilityDonation
}

return InboundProcessabilityGood
return clienttypes.InboundProcessabilityGood
}

// DecodeMemoBytes decodes the contained memo bytes as either standard or legacy memo
Expand Down Expand Up @@ -168,16 +155,16 @@ func ValidateStandardMemo(memoStd memo.InboundMemo, chainID int64) error {
func (ob *Observer) CheckEventProcessability(event BTCInboundEvent) bool {
// check if the event is processable
switch result := event.Processability(); result {
case InboundProcessabilityGood:
case clienttypes.InboundProcessabilityGood:
return true
case InboundProcessabilityDonation:
case clienttypes.InboundProcessabilityDonation:
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 InboundProcessabilityComplianceViolation:
case clienttypes.InboundProcessabilityComplianceViolation:
compliance.PrintComplianceLog(ob.logger.Inbound, ob.logger.Compliance,
false, ob.Chain().ChainId, event.TxHash, event.FromAddress, event.ToAddress, "BTC")
return false
Expand Down
15 changes: 8 additions & 7 deletions zetaclient/chains/bitcoin/observer/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/zeta-chain/node/zetaclient/keys"
"github.com/zeta-chain/node/zetaclient/testutils"
"github.com/zeta-chain/node/zetaclient/testutils/mocks"
clienttypes "github.com/zeta-chain/node/zetaclient/types"
)

// createTestBtcEvent creates a test BTC inbound event
Expand All @@ -41,7 +42,7 @@ func createTestBtcEvent(
}
}

func Test_CheckProcessability(t *testing.T) {
func Test_Processability(t *testing.T) {
// setup compliance config
cfg := config.Config{
ComplianceConfig: sample.ComplianceConfig(),
Expand All @@ -52,23 +53,23 @@ func Test_CheckProcessability(t *testing.T) {
tests := []struct {
name string
event *observer.BTCInboundEvent
expected observer.InboundProcessability
expected clienttypes.InboundProcessability
}{
{
name: "should return InboundProcessabilityGood for a processable inbound event",
event: &observer.BTCInboundEvent{
FromAddress: "tb1quhassyrlj43qar0mn0k5sufyp6mazmh2q85lr6ex8ehqfhxpzsksllwrsu",
ToAddress: testutils.TSSAddressBTCAthens3,
},
expected: observer.InboundProcessabilityGood,
expected: clienttypes.InboundProcessabilityGood,
},
{
name: "should return InboundProcessabilityComplianceViolation for a restricted sender address",
event: &observer.BTCInboundEvent{
FromAddress: sample.RestrictedBtcAddressTest,
ToAddress: testutils.TSSAddressBTCAthens3,
},
expected: observer.InboundProcessabilityComplianceViolation,
expected: clienttypes.InboundProcessabilityComplianceViolation,
},
{
name: "should return InboundProcessabilityComplianceViolation for a restricted receiver address in standard memo",
Expand All @@ -81,7 +82,7 @@ func Test_CheckProcessability(t *testing.T) {
},
},
},
expected: observer.InboundProcessabilityComplianceViolation,
expected: clienttypes.InboundProcessabilityComplianceViolation,
},
{
name: "should return InboundProcessabilityComplianceViolation for a restricted revert address in standard memo",
Expand All @@ -96,7 +97,7 @@ func Test_CheckProcessability(t *testing.T) {
},
},
},
expected: observer.InboundProcessabilityComplianceViolation,
expected: clienttypes.InboundProcessabilityComplianceViolation,
},
{
name: "should return InboundProcessabilityDonation for a donation inbound event",
Expand All @@ -105,7 +106,7 @@ func Test_CheckProcessability(t *testing.T) {
ToAddress: testutils.TSSAddressBTCAthens3,
MemoBytes: []byte(constant.DonationMessage),
},
expected: observer.InboundProcessabilityDonation,
expected: clienttypes.InboundProcessabilityDonation,
},
}

Expand Down
17 changes: 13 additions & 4 deletions zetaclient/chains/bitcoin/observer/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,22 @@ func (ob *Observer) GetInboundVoteFromBtcEvent(event *BTCInboundEvent) *crosscha
}
amountInt := big.NewInt(amountSats)

// create inbound vote message contract V1 for legacy memo
// create inbound vote message contract V1 for legacy memo or standard memo
var msg *crosschaintypes.MsgVoteInbound
if event.MemoStd == nil {
return ob.NewInboundVoteFromLegacyMemo(event, amountInt)
msg = ob.NewInboundVoteFromLegacyMemo(event, amountInt)
} else {
msg = ob.NewInboundVoteFromStdMemo(event, amountInt)
}

// create inbound vote message for standard memo
return 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 msg
}

// GetBtcEvent returns a valid BTCInboundEvent or nil
Expand Down
4 changes: 3 additions & 1 deletion zetaclient/chains/bitcoin/observer/inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ func Test_GetInboundVoteFromBtcEvent(t *testing.T) {

// create test observer
ob := MockBTCObserver(t, chain, params, nil)
zetacoreClient := mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{}).WithZetaChain()
zetacoreClient := mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{
OperatorAddress: sample.Bech32AccAddress(),
}).WithZetaChain()
ob.WithZetacoreClient(zetacoreClient)

// test cases
Expand Down
61 changes: 51 additions & 10 deletions zetaclient/chains/solana/observer/inbound.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package observer

import (
"bytes"
"context"
"encoding/hex"
"fmt"
Expand All @@ -13,12 +12,12 @@ import (
"github.com/rs/zerolog"

"github.com/zeta-chain/node/pkg/coin"
"github.com/zeta-chain/node/pkg/constant"
solanacontracts "github.com/zeta-chain/node/pkg/contracts/solana"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
solanarpc "github.com/zeta-chain/node/zetaclient/chains/solana/rpc"
"github.com/zeta-chain/node/zetaclient/compliance"
zctx "github.com/zeta-chain/node/zetaclient/context"
"github.com/zeta-chain/node/zetaclient/logs"
clienttypes "github.com/zeta-chain/node/zetaclient/types"
"github.com/zeta-chain/node/zetaclient/zetacore"
)
Expand Down Expand Up @@ -256,19 +255,29 @@ func (ob *Observer) FilterInboundEvents(txResult *rpc.GetTransactionResult) ([]*

// BuildInboundVoteMsgFromEvent builds a MsgVoteInbound from an inbound event
func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent) *crosschaintypes.MsgVoteInbound {
// compliance check. Return nil if the inbound contains restricted addresses
if compliance.DoesInboundContainsRestrictedAddress(event, ob.Logger()) {
// prepare logger fields
lf := map[string]any{
logs.FieldModule: logs.ModNameInbound,
logs.FieldMethod: "BuildInboundVoteMsgFromEvent",
logs.FieldChain: ob.Chain().ChainId,
logs.FieldTx: event.TxHash,
}

// decode event memo bytes to get the receiver
err := event.DecodeMemo()
if err != nil {
ob.Logger().Inbound.Info().Fields(lf).Msgf("invalid memo bytes: %s", hex.EncodeToString(event.Memo))
return nil
}

// donation check
if bytes.Equal(event.Memo, []byte(constant.DonationMessage)) {
ob.Logger().Inbound.Info().
Msgf("thank you rich folk for your donation! tx %s chain %d", event.TxHash, event.SenderChainID)
// check if the event is processable
if !ob.CheckEventProcessability(*event) {
return nil
}

return zetacore.GetInboundVoteMessage(
// create inbound vote message
msg := crosschaintypes.NewMsgVoteInbound(
ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(),
event.Sender,
event.SenderChainID,
event.Sender,
Expand All @@ -281,7 +290,39 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent
0,
event.CoinType,
event.Asset,
ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(),
0, // not a smart contract call
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
}

// CheckEventProcessability checks if the inbound event is processable
func (ob *Observer) CheckEventProcessability(event clienttypes.InboundEvent) bool {
switch result := event.Processability(); result {
case clienttypes.InboundProcessabilityGood:
return true
case clienttypes.InboundProcessabilityDonation:
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.InboundProcessabilityComplianceViolation:
compliance.PrintComplianceLog(ob.Logger().Inbound, ob.Logger().Compliance,
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", result)
return false
}
}
Loading

0 comments on commit 4653b46

Please sign in to comment.