From e56dda05bbb186c9b49ecdc7023b25f206db9684 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 8 Aug 2025 16:32:39 -0500 Subject: [PATCH 1/3] print error message detail when memo decoding failed --- ...est_bitcoin_deposit_invalid_memo_revert.go | 40 ++++++++++++++++--- zetaclient/chains/bitcoin/observer/inbound.go | 2 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/e2e/e2etests/test_bitcoin_deposit_invalid_memo_revert.go b/e2e/e2etests/test_bitcoin_deposit_invalid_memo_revert.go index cb180f9658..1c364252bd 100644 --- a/e2e/e2etests/test_bitcoin_deposit_invalid_memo_revert.go +++ b/e2e/e2etests/test_bitcoin_deposit_invalid_memo_revert.go @@ -5,6 +5,7 @@ import ( "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/memo" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) @@ -20,7 +21,7 @@ func TestBitcoinDepositInvalidMemoRevert(r *runner.E2ERunner, args []string) { txHash, err := r.SendToTSSWithMemo(0.1, nil) require.NoError(r, err) - // wait for the cctx to be mined + // wait for the cctx to be reverted cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit without memo") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) @@ -31,20 +32,49 @@ func TestBitcoinDepositInvalidMemoRevert(r *runner.E2ERunner, args []string) { txHash, err = r.SendToTSSWithMemo(0.1, []byte{}) require.NoError(r, err) - // wait for the cctx to be mined + // wait for the cctx to be reverted cctx = utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit empty memo") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) require.EqualValues(r, crosschaintypes.InboundStatus_INVALID_MEMO, cctx.InboundParams.Status) // CASE 3 - // make a deposit with an invalid memo - txHash, err = r.SendToTSSWithMemo(0.1, []byte("invalid memo")) + // make a deposit with an invalid legacy memo + txHash, err = r.SendToTSSWithMemo(0.1, []byte("invalid legacy memo")) require.NoError(r, err) - // wait for the cctx to be mined + // wait for the cctx to be reverted cctx = utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit invalid memo") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) require.EqualValues(r, crosschaintypes.InboundStatus_INVALID_MEMO, cctx.InboundParams.Status) + + // CASE 4 + // make a deposit with an invalid standard memo + memo := &memo.InboundMemo{ + Header: memo.Header{ + Version: 0, + EncodingFmt: memo.EncodingFmtCompactShort, + OpCode: memo.OpCodeDepositAndCall, + }, + FieldsV0: memo.FieldsV0{ + Receiver: r.TestDAppV2ZEVMAddr, + Payload: []byte("payload is not allowed"), + }, + } + + // modify the op code to 0b0000 (deposit), so payload won't be allowed + memoBytes, err := memo.EncodeToBytes() + require.NoError(r, err) + memoBytes[2] = 0x00 + + // deposit to TSS + txHash, err = r.SendToTSSWithMemo(0.1, memoBytes) + require.NoError(r, err) + + // wait for the cctx to be reverted + cctx = utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit invalid standard memo") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) + require.EqualValues(r, crosschaintypes.InboundStatus_INVALID_MEMO, cctx.InboundParams.Status) } diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index 4d56664c7b..c5736ede2c 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -197,7 +197,7 @@ func (ob *Observer) GetInboundVoteFromBtcEvent(event *BTCInboundEvent) *crosscha // if the memo is invalid, we set the status in the event, the inbound will be observed as invalid err := event.DecodeMemoBytes(ob.Chain().ChainId) if err != nil { - ob.Logger().Inbound.Info().Fields(lf).Msgf("invalid memo bytes: %s", hex.EncodeToString(event.MemoBytes)) + ob.Logger().Inbound.Error().Err(err).Fields(lf).Msgf("invalid memo: %s", hex.EncodeToString(event.MemoBytes)) event.Status = crosschaintypes.InboundStatus_INVALID_MEMO } From 980548731c1e12e7a364470b8bf3aa23000bc20f Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 8 Aug 2025 16:41:04 -0500 Subject: [PATCH 2/3] add changelog entry --- changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index 469f9bdefc..a36ee2fb1d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # CHANGELOG +## UNRELEASED + +### Fixes + +* [4090](https://github.com/zeta-chain/node/pull/4090) - print error message in detail if unable to decode Bitcoin memo + ## v33.0.0 ### Features From 70d519715ce86d52561b8bcdd4d6fffa9a907f43 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 8 Aug 2025 16:53:09 -0500 Subject: [PATCH 3/3] keep invalid memo log on Info level --- zetaclient/chains/bitcoin/observer/inbound.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index c5736ede2c..7afc6d670d 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -197,7 +197,7 @@ func (ob *Observer) GetInboundVoteFromBtcEvent(event *BTCInboundEvent) *crosscha // if the memo is invalid, we set the status in the event, the inbound will be observed as invalid err := event.DecodeMemoBytes(ob.Chain().ChainId) if err != nil { - ob.Logger().Inbound.Error().Err(err).Fields(lf).Msgf("invalid memo: %s", hex.EncodeToString(event.MemoBytes)) + ob.Logger().Inbound.Info().Err(err).Fields(lf).Msgf("invalid memo: %s", hex.EncodeToString(event.MemoBytes)) event.Status = crosschaintypes.InboundStatus_INVALID_MEMO }