From 0f85e3d03bec53aab03a7de43eb8dbb918456684 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:27:56 -0600 Subject: [PATCH 1/4] fix: use the ZEVM address pulled from memo as Receiver in MsgVoteInbound (#3242) * use the ZEVM address decoded from memo as Receiver in MsgVoteInbound * add Receiver check in unit test * add changelog entry * fix unit test --- e2e/e2etests/test_solana_deposit.go | 1 + e2e/e2etests/test_solana_deposit_call.go | 1 + e2e/e2etests/test_spl_deposit.go | 1 + e2e/e2etests/test_spl_deposit_and_call.go | 1 + zetaclient/chains/solana/observer/inbound.go | 6 +++--- zetaclient/chains/solana/observer/inbound_test.go | 8 +++++--- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/e2e/e2etests/test_solana_deposit.go b/e2e/e2etests/test_solana_deposit.go index d46a56c015..eb238c8c58 100644 --- a/e2e/e2etests/test_solana_deposit.go +++ b/e2e/e2etests/test_solana_deposit.go @@ -29,6 +29,7 @@ func TestSolanaDeposit(r *runner.E2ERunner, args []string) { cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "solana_deposit") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + require.Equal(r, cctx.GetCurrentOutboundParam().Receiver, r.EVMAddress().Hex()) // get ERC20 SOL balance after deposit balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) diff --git a/e2e/e2etests/test_solana_deposit_call.go b/e2e/e2etests/test_solana_deposit_call.go index 0d7ef97975..7d64c5a5d6 100644 --- a/e2e/e2etests/test_solana_deposit_call.go +++ b/e2e/e2etests/test_solana_deposit_call.go @@ -29,6 +29,7 @@ func TestSolanaDepositAndCall(r *runner.E2ERunner, args []string) { cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "solana_deposit_and_call") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + require.Equal(r, cctx.GetCurrentOutboundParam().Receiver, contractAddr.Hex()) // check if example contract has been called, bar value should be set to amount utils.MustHaveCalledExampleContractWithMsg(r, contract, depositAmount, data) diff --git a/e2e/e2etests/test_spl_deposit.go b/e2e/e2etests/test_spl_deposit.go index e20ff5879a..7bf1ffa7a9 100644 --- a/e2e/e2etests/test_spl_deposit.go +++ b/e2e/e2etests/test_spl_deposit.go @@ -42,6 +42,7 @@ func TestSPLDeposit(r *runner.E2ERunner, args []string) { cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "solana_deposit_spl") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + require.Equal(r, cctx.GetCurrentOutboundParam().Receiver, r.EVMAddress().Hex()) // verify balances are updated pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized) diff --git a/e2e/e2etests/test_spl_deposit_and_call.go b/e2e/e2etests/test_spl_deposit_and_call.go index 9c15c8c27f..9d311d5906 100644 --- a/e2e/e2etests/test_spl_deposit_and_call.go +++ b/e2e/e2etests/test_spl_deposit_and_call.go @@ -49,6 +49,7 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) { cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "solana_deposit_spl_and_call") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + require.Equal(r, cctx.GetCurrentOutboundParam().Receiver, contractAddr.Hex()) // check if example contract has been called, bar value should be set to amount utils.MustHaveCalledExampleContractWithMsg(r, contract, big.NewInt(int64(amount)), data) diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index fa3edde764..f11c40a920 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -213,7 +213,7 @@ func (ob *Observer) FilterInboundEvents(txResult *rpc.GetTransactionResult) ([]* events = append(events, &clienttypes.InboundEvent{ SenderChainID: ob.Chain().ChainId, Sender: deposit.Sender, - Receiver: deposit.Sender, // receiver is pulled out from memo + Receiver: "", // receiver will be pulled out from memo later TxOrigin: deposit.Sender, Amount: deposit.Amount, Memo: deposit.Memo, @@ -241,7 +241,7 @@ func (ob *Observer) FilterInboundEvents(txResult *rpc.GetTransactionResult) ([]* events = append(events, &clienttypes.InboundEvent{ SenderChainID: ob.Chain().ChainId, Sender: deposit.Sender, - Receiver: deposit.Sender, // receiver is pulled out from memo + Receiver: "", // receiver will be pulled out from memo later TxOrigin: deposit.Sender, Amount: deposit.Amount, Memo: deposit.Memo, @@ -281,7 +281,7 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent event.Sender, event.SenderChainID, event.Sender, - event.Sender, + event.Receiver, ob.ZetacoreClient().Chain().ChainId, cosmosmath.NewUint(event.Amount), hex.EncodeToString(event.Memo), diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index 025d72592a..deb5d4ab51 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -82,7 +82,7 @@ func Test_FilterInboundEvents(t *testing.T) { eventExpected := &clienttypes.InboundEvent{ SenderChainID: chain.ChainId, Sender: sender, - Receiver: sender, + Receiver: "", TxOrigin: sender, Amount: 100000000, Memo: expectedMemo, @@ -124,11 +124,13 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { t.Run("should return vote msg for valid event", func(t *testing.T) { sender := sample.SolanaAddress(t) - memo := sample.EthAddress().Bytes() - event := sample.InboundEvent(chain.ChainId, sender, sender, 1280, []byte(memo)) + receiver := sample.EthAddress() + event := sample.InboundEvent(chain.ChainId, sender, "", 1280, receiver.Bytes()) msg := ob.BuildInboundVoteMsgFromEvent(event) require.NotNil(t, msg) + require.Equal(t, sender, msg.Sender) + require.Equal(t, receiver.Hex(), msg.Receiver) }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { sender := sample.SolanaAddress(t) From dfaa7af73c7bf53d7a17ad50ce6931ad59ca2790 Mon Sep 17 00:00:00 2001 From: skosito Date: Tue, 10 Dec 2024 20:34:08 +0100 Subject: [PATCH 2/4] fix unit test --- zetaclient/chains/solana/observer/inbound.go | 14 +++++++++ zetaclient/types/event.go | 31 ++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index f11c40a920..68de065e9c 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -19,6 +19,7 @@ import ( 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" ) @@ -277,6 +278,19 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent return nil } + // prepare logger fields + lf := map[string]any{ + logs.FieldMethod: "BuildInboundVoteMsgFromEvent", + 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 + } + return zetacore.GetInboundVoteMessage( event.Sender, event.SenderChainID, diff --git a/zetaclient/types/event.go b/zetaclient/types/event.go index a0313236e6..6709b063bd 100644 --- a/zetaclient/types/event.go +++ b/zetaclient/types/event.go @@ -1,7 +1,15 @@ package types import ( + "bytes" + "encoding/hex" + + "github.com/pkg/errors" + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/constant" + "github.com/zeta-chain/node/pkg/crypto" + "github.com/zeta-chain/node/pkg/memo" ) // InboundEvent represents an inbound event @@ -41,3 +49,26 @@ type InboundEvent struct { // Asset is the asset of the inbound Asset string } + +// DecodeMemo decodes the receiver from the memo bytes +func (event *InboundEvent) DecodeMemo() error { + // skip decoding donation tx as it won't go through zetacore + if bytes.Equal(event.Memo, []byte(constant.DonationMessage)) { + return nil + } + + // decode receiver address from memo + parsedAddress, _, err := memo.DecodeLegacyMemoHex(hex.EncodeToString(event.Memo)) + if err != nil { // unreachable code + return errors.Wrap(err, "invalid memo hex") + } + + // ensure the receiver is valid + if crypto.IsEmptyAddress(parsedAddress) { + return errors.New("got empty receiver address from memo") + } + + event.Receiver = parsedAddress.Hex() + + return nil +} From 13aeb00c90b348d19662a915d45af9f0173f1604 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 16 Dec 2024 15:20:45 +0100 Subject: [PATCH 3/4] unit test --- zetaclient/chains/solana/observer/inbound_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index deb5d4ab51..fc1cd83263 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -129,7 +129,6 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { msg := ob.BuildInboundVoteMsgFromEvent(event) require.NotNil(t, msg) - require.Equal(t, sender, msg.Sender) require.Equal(t, receiver.Hex(), msg.Receiver) }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { From 5c1a95d6f1c3e29c7d7de8fa90de0661ab91fbba Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 16 Dec 2024 15:20:55 +0100 Subject: [PATCH 4/4] unit test --- zetaclient/chains/solana/observer/inbound_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index fc1cd83263..deb5d4ab51 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -129,6 +129,7 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { msg := ob.BuildInboundVoteMsgFromEvent(event) require.NotNil(t, msg) + require.Equal(t, sender, msg.Sender) require.Equal(t, receiver.Hex(), msg.Receiver) }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) {