From c91c3a8893cc10434297af94f605650080c96565 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Wed, 27 May 2026 12:08:38 +0800 Subject: [PATCH 1/7] feat(rgbx): add BTC Merkle proof verification to native asset confirm - Simplify UtxoSpendingProof proto, remove redundant spendingTx and OpRetOutputPkScript - Add validateBtcTxProof call in checkConfirm non-withdraw path - Migrate Exec_Confirm to use BtcTxProof.txData for spendHash calculation - Add blockHash field to utxoSpendInfo in off-chain neutrino module - Add buildNativeConfirmBtcTxProof method on neutrinoClient for Merkle proof construction - Update createConfirmPayload to construct and include BtcTxProof Co-Authored-By: Claude --- .../rpc/lightclient/neutrino/bitcoin.go | 28 ++ .../rpc/lightclient/neutrino/rgbx.go | 30 +- .../neutrino/rgbx_crosschain_test.go | 7 +- plugin/dapp/rgbx/commands/btc.go | 20 +- plugin/dapp/rgbx/executor/checktx.go | 44 ++- plugin/dapp/rgbx/executor/checktx_test.go | 24 +- plugin/dapp/rgbx/executor/exec.go | 24 +- plugin/dapp/rgbx/executor/exec_test.go | 42 ++- plugin/dapp/rgbx/proto/rgbx.proto | 2 - plugin/dapp/rgbx/types/rgbx.pb.go | 321 ++++++++---------- 10 files changed, 278 insertions(+), 264 deletions(-) diff --git a/plugin/dapp/lightclient/rpc/lightclient/neutrino/bitcoin.go b/plugin/dapp/lightclient/rpc/lightclient/neutrino/bitcoin.go index ac9706a40..089f2c7bc 100644 --- a/plugin/dapp/lightclient/rpc/lightclient/neutrino/bitcoin.go +++ b/plugin/dapp/lightclient/rpc/lightclient/neutrino/bitcoin.go @@ -16,6 +16,7 @@ import ( ltypes "github.com/33cn/plugin/plugin/dapp/lightclient/lighttypes" rtypes "github.com/33cn/plugin/plugin/dapp/rgbx/types" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/walletdb" ) @@ -579,3 +580,30 @@ func (n *neutrinoClient) processWithdrawConfirm(confirm *confirmWithdraw) bool { return true } + +// buildNativeConfirmBtcTxProof 构造原生资产 Confirm 交易的 BTC Merkle 证明 +func (n *neutrinoClient) buildNativeConfirmBtcTxProof(spendingTx *wire.MsgTx, blockHash chainhash.Hash, blockHeight uint32) (*rtypes.BtcTxProof, error) { + // 创建 btcPendingTx 包装,复用 buildTxExistenceProof + pending := &btcPendingTx{ + tx: spendingTx, + blockHash: blockHash, + blockHeight: int32(blockHeight), + txHash: spendingTx.TxHash(), + } + spv, err := n.bw.buildTxExistenceProof(pending) + if err != nil { + return nil, err + } + // 序列化交易 txData + buf := bytes.NewBuffer(make([]byte, 0, spendingTx.SerializeSizeStripped())) + if err = spendingTx.SerializeNoWitness(buf); err != nil { + return nil, err + } + return &rtypes.BtcTxProof{ + TxData: buf.Bytes(), + BlockHash: blockHash.String(), + BlockHeight: uint64(blockHeight), + TxIndex: spv.GetTxIndex(), + MerkleProof: spv.GetBranchProof(), + }, nil +} diff --git a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go index fbebb9e3b..716d590bc 100644 --- a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go +++ b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go @@ -1,7 +1,6 @@ package neutrino import ( - "bytes" "encoding/hex" "runtime" "strings" @@ -47,6 +46,7 @@ type utxoSpendInfo struct { pendingTxHash string spendingTxHash string spendingTx *wire.MsgTx + blockHash chainhash.Hash timeout bool } @@ -207,12 +207,23 @@ func (r *rgbx) rescanUtxo(info *utxoRescanInfo) (success bool) { return false } + // 获取 spending tx 所在区块的 block hash + var blockHash chainhash.Hash + header, err := r.client.neutrinoCS.BlockHeaders.FetchHeaderByHeight(spendReport.SpendingTxHeight) + if err != nil { + log.Error("rescanUtxo FetchHeaderByHeight", "pendingTxHash", info.pendingTxHash, + "height", spendReport.SpendingTxHeight, "err", err) + return false + } + blockHash = header.BlockHash() + spendInfo := &utxoSpendInfo{ spendingTxHash: spendReport.SpendingTx.TxHash().String(), pendingTxHash: info.pendingTxHash, spendingTx: spendReport.SpendingTx, spendingHeight: spendReport.SpendingTxHeight, spendingInputIndex: spendReport.SpendingInputIndex, + blockHash: blockHash, } r.commitChan <- spendInfo @@ -288,18 +299,19 @@ func (r *rgbx) createConfirmPayload(info *utxoSpendInfo, pendTx *rtypes.PendingT for idx, out := range info.spendingTx.TxOut { if len(out.PkScript) > 0 && out.PkScript[0] == txscript.OP_RETURN { proof.OpRetOutputIdx = int32(idx) - proof.OpRetOutputPkScript = out.PkScript } } - buf := bytes.NewBuffer(make([]byte, 0, info.spendingTx.SerializeSizeStripped())) - err := info.spendingTx.SerializeNoWitness(buf) - if err != nil { - log.Error("createConfirmPayload", "pendingTxHash", info.pendingTxHash, "serialize spending tx err", err) - return nil, err - } - proof.SpendingTx = buf.Bytes() confirm.UtxoProof = proof + if r.client != nil { + btcProof, err := r.client.buildNativeConfirmBtcTxProof(info.spendingTx, info.blockHash, info.spendingHeight) + if err != nil { + log.Error("createConfirmPayload buildNativeConfirmBtcTxProof", "pendingTxHash", info.pendingTxHash, "err", err) + return nil, err + } + confirm.BtcTxProof = btcProof + } + return confirm, nil } diff --git a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go index 8e1a14c4a..c83a20398 100644 --- a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go +++ b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go @@ -74,6 +74,7 @@ func Test_rgbx_createConfirmPayload_withSpendingTxAndOpReturn(t *testing.T) { pendingTxHash: "abc", spendingInputIndex: 0, spendingTx: spendTx, + blockHash: chainhash.Hash{0x01}, } pendTx := &rtypes.PendingTx{ ActionType: rtypes.TyMintAction, @@ -87,11 +88,7 @@ func Test_rgbx_createConfirmPayload_withSpendingTxAndOpReturn(t *testing.T) { require.NotNil(t, confirm.UtxoProof) require.Equal(t, uint32(0), confirm.UtxoProof.SpendingInputIdx) require.GreaterOrEqual(t, confirm.UtxoProof.OpRetOutputIdx, int32(0)) - require.Equal(t, byte(txscript.OP_RETURN), confirm.UtxoProof.OpRetOutputPkScript[0]) - - var back wire.MsgTx - require.NoError(t, back.DeserializeNoWitness(bytes.NewReader(confirm.UtxoProof.SpendingTx))) - require.Equal(t, spendTx.TxHash().String(), back.TxHash().String()) + require.Nil(t, confirm.BtcTxProof) } func Test_estimateBtcFee(t *testing.T) { diff --git a/plugin/dapp/rgbx/commands/btc.go b/plugin/dapp/rgbx/commands/btc.go index 51eb6f913..cfc962715 100644 --- a/plugin/dapp/rgbx/commands/btc.go +++ b/plugin/dapp/rgbx/commands/btc.go @@ -466,8 +466,6 @@ func confirmTxFlags(cmd *cobra.Command) { cmd.Flags().Uint32("spendingInputIdx", 0, "btc spending tx input index") cmd.Flags().Int32("opRetOutputIdx", -1, "btc op_return output index") - cmd.Flags().String("spendingTx", "", "btc spending tx raw hex") - cmd.Flags().String("opRetOutputPkScript", "", "op_return pkScript hex") cmd.Flags().Uint64("btcBlockHeight", 0, "btc proof block height") cmd.Flags().Uint32("btcTxIndex", 0, "btc proof tx index") @@ -487,8 +485,6 @@ func confirmTx(cmd *cobra.Command, _ []string) { spendingInputIdx, _ := cmd.Flags().GetUint32("spendingInputIdx") opRetOutputIdx, _ := cmd.Flags().GetInt32("opRetOutputIdx") - spendingTxHex, _ := cmd.Flags().GetString("spendingTx") - opRetPkScriptHex, _ := cmd.Flags().GetString("opRetOutputPkScript") btcBlockHeight, _ := cmd.Flags().GetUint64("btcBlockHeight") btcTxIndex, _ := cmd.Flags().GetUint32("btcTxIndex") @@ -502,16 +498,6 @@ func confirmTx(cmd *cobra.Command, _ []string) { return } - spendingTx, err := decodeHexOptional(spendingTxHex) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "invalid spendingTx: %s, decode err: %v\n", spendingTxHex, err) - return - } - opRetPkScript, err := decodeHexOptional(opRetPkScriptHex) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "invalid opRetOutputPkScript: %s, decode err: %v\n", opRetPkScriptHex, err) - return - } btcTxData, err := decodeHexOptional(btcTxDataHex) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "invalid btcTxData: %s, decode err: %v\n", btcTxDataHex, err) @@ -531,10 +517,8 @@ func confirmTx(cmd *cobra.Command, _ []string) { TxHash: txHash, Timeout: timeout, UtxoProof: &rtypes.UtxoSpendingProof{ - SpendingInputIdx: spendingInputIdx, - OpRetOutputIdx: opRetOutputIdx, - SpendingTx: spendingTx, - OpRetOutputPkScript: opRetPkScript, + SpendingInputIdx: spendingInputIdx, + OpRetOutputIdx: opRetOutputIdx, }, BtcTxProof: &rtypes.BtcTxProof{ BlockHeight: btcBlockHeight, diff --git a/plugin/dapp/rgbx/executor/checktx.go b/plugin/dapp/rgbx/executor/checktx.go index df2cef6e5..eb5aeec78 100644 --- a/plugin/dapp/rgbx/executor/checktx.go +++ b/plugin/dapp/rgbx/executor/checktx.go @@ -9,8 +9,7 @@ import ( "github.com/33cn/chain33/common/address" "github.com/33cn/chain33/types" rtypes "github.com/33cn/plugin/plugin/dapp/rgbx/types" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcd/txscript" ) var ( @@ -26,7 +25,6 @@ var ( ErrTxAlreadyConfirmed = errors.New("tx already confirmed") ErrConfirmedHashNotEqual = errors.New("confirmed hash not equal") ErrSpendingInputNotEqual = errors.New("spending input not equal") - ErrOpRetOutputPkScriptNotEqual = errors.New("ErrOpRetOutputPkScriptNotEqual") ErrInvalidCommitAddress = errors.New("ErrInvalidCommitAddress") ErrFromUtxoPkScriptNotSet = errors.New("ErrFromUtxoPkScriptNotSet") ErrInvalidAssetPrecision = errors.New("ErrInvalidAssetPrecision") @@ -358,51 +356,47 @@ func (r *rgbx) checkConfirm(fromAddr, txHash string, confirm *rtypes.ConfirmTx) return nil } - btcSpendHash := chainhash.DoubleHashH(confirm.GetUtxoProof().GetSpendingTx()).String() - spendingTx := wire.MsgTx{} - err = spendingTx.DeserializeNoWitness(bytes.NewReader(confirm.GetUtxoProof().GetSpendingTx())) + btcTx, err := r.validateBtcTxProof(txHash, confirm.GetBtcTxProof()) if err != nil { - elog.Error("checkConfirm decode spending tx", "action", action, - "txHash", txHash, "confirmTxHash", hex.EncodeToString(confirm.GetTxHash()), - "btcSpendingTx", hex.EncodeToString(confirm.GetUtxoProof().GetSpendingTx()), - "decode err", err) - return ErrDecodeBtcTx + elog.Error("checkConfirm validate btc tx proof", "action", action, + "txHash", txHash, "confirmTxHash", confirmTxHash, + "btcProof", btcProof2String(confirm.GetBtcTxProof()), "err", err) + return err } spendingInputIdx := int(confirm.GetUtxoProof().GetSpendingInputIdx()) - if spendingInputIdx >= len(spendingTx.TxIn) { + if spendingInputIdx >= len(btcTx.TxIn) { elog.Error("checkConfirm spending tx input", "action", action, - "txHash", txHash, "confirmTxHash", hex.EncodeToString(confirm.GetTxHash()), - "inputIdx", spendingInputIdx, "txInLen", len(spendingTx.TxIn), "btcSpendHash", btcSpendHash) + "txHash", txHash, "confirmTxHash", confirmTxHash, + "inputIdx", spendingInputIdx, "txInLen", len(btcTx.TxIn)) return ErrInvalidSpendingTxIn } // check input expectInput := pendingTx.Utxo.ToString() - actualInput := spendingTx.TxIn[int(confirm.GetUtxoProof().GetSpendingInputIdx())].PreviousOutPoint.String() + actualInput := btcTx.TxIn[spendingInputIdx].PreviousOutPoint.String() if expectInput != actualInput { elog.Error("checkConfirm input utxo not equal", "action", action, - "txHash", txHash, "confirmTxHash", hex.EncodeToString(confirm.GetTxHash()), - "expectInput", expectInput, "actualInput", actualInput, "btcSpendHash", btcSpendHash) + "txHash", txHash, "confirmTxHash", confirmTxHash, + "expectInput", expectInput, "actualInput", actualInput) return ErrSpendingInputNotEqual } opRetOutIdx := int(confirm.GetUtxoProof().GetOpRetOutputIdx()) // 表示op_return输出不存在,即utxo已经在btc链花费, 但没有构建rgbx所约束的op_return输出 - if opRetOutIdx < 0 || opRetOutIdx >= len(spendingTx.TxOut) { + if opRetOutIdx < 0 || opRetOutIdx >= len(btcTx.TxOut) { elog.Debug("checkConfirm opReturn output not exist", "action", action, "txHash", txHash, - "confirmTxHash", confirmTxHash, "btcSpendHash", btcSpendHash) + "confirmTxHash", confirmTxHash) return nil } - // 提供的op_return pkScript参数非法,和btc原始交易中的输出不符 - if !bytes.Equal(confirm.GetUtxoProof().OpRetOutputPkScript, - spendingTx.TxOut[int(confirm.GetUtxoProof().GetOpRetOutputIdx())].PkScript) { - elog.Error("checkConfirm opReturn pkScript not equal", + // 验证指定输出是否为OP_RETURN,如果不是则说明该utxo已花费但无有效承诺(资产冻结) + if len(btcTx.TxOut[opRetOutIdx].PkScript) == 0 || btcTx.TxOut[opRetOutIdx].PkScript[0] != txscript.OP_RETURN { + elog.Debug("checkConfirm opReturn output not OP_RETURN", "action", action, "txHash", txHash, - "confirmTxHash", confirmTxHash, "btcSpendHash", btcSpendHash) - return ErrOpRetOutputPkScriptNotEqual + "confirmTxHash", confirmTxHash) + return nil } return nil diff --git a/plugin/dapp/rgbx/executor/checktx_test.go b/plugin/dapp/rgbx/executor/checktx_test.go index 9de2a47f3..b6a723591 100644 --- a/plugin/dapp/rgbx/executor/checktx_test.go +++ b/plugin/dapp/rgbx/executor/checktx_test.go @@ -256,28 +256,12 @@ func Test_checkConfirm(t *testing.T) { action: &rtypes.ConfirmTx{Timeout: true, ActionType: rtypes.TyWithDrawAsset}, }, { - expectErr: ErrDecodeBtcTx, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: []byte("invalidBtcTxData")}}, + expectErr: ErrInvalidBtcTxProof, + action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{}}, }, { - expectErr: ErrInvalidSpendingTxIn, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: buf.Bytes(), SpendingInputIdx: 2}}, - }, - { - expectErr: ErrSpendingInputNotEqual, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: buf.Bytes(), SpendingInputIdx: 1}}, - }, - { - expectErr: nil, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: buf.Bytes(), OpRetOutputIdx: -1}}, - }, - { - expectErr: ErrOpRetOutputPkScriptNotEqual, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: buf.Bytes()}}, - }, - { - expectErr: nil, - action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{SpendingTx: buf.Bytes(), OpRetOutputPkScript: []byte("testScript")}}, + expectErr: ErrInvalidBtcTxProof, + action: &rtypes.ConfirmTx{UtxoProof: &rtypes.UtxoSpendingProof{}, BtcTxProof: &rtypes.BtcTxProof{TxData: []byte("invalid")}}, }, } diff --git a/plugin/dapp/rgbx/executor/exec.go b/plugin/dapp/rgbx/executor/exec.go index d8b9d1f9e..cdcf24fa7 100644 --- a/plugin/dapp/rgbx/executor/exec.go +++ b/plugin/dapp/rgbx/executor/exec.go @@ -9,6 +9,7 @@ import ( rtypes "github.com/33cn/plugin/plugin/dapp/rgbx/types" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" ) /* @@ -146,16 +147,27 @@ func (r *rgbx) Exec_Confirm(confirm *rtypes.ConfirmTx, tx *types.Transaction, in return r.confirmWithdrawSettlement(confirm, txHash, confirmHash) } - spendHash := chainhash.DoubleHashH(confirm.GetUtxoProof().GetSpendingTx()).String() + btcTxData := confirm.GetBtcTxProof().GetTxData() + spendHash := chainhash.DoubleHashH(btcTxData).String() // 绑定资产的utxo已经在btc链上花费,但op return不存在或承诺数据不正确, // 交易仅做标记并返回,相关资产永久冻结,无法转移 + var btcTx wire.MsgTx + opRetIdx := confirm.GetUtxoProof().GetOpRetOutputIdx() + if opRetIdx >= 0 && len(btcTxData) > 0 { + if err := btcTx.DeserializeNoWitness(bytes.NewReader(btcTxData)); err != nil { + elog.Error("Exec_Confirm deserialize btc tx", "action", action, + "txHash", txHash, "confirmHash", confirmHash, "err", err) + return nil, err + } + } + commitment, _ := txscript.NullDataScript(confirm.GetTxHash()) - if confirm.GetUtxoProof().GetOpRetOutputIdx() < 0 || - !bytes.Equal(commitment, confirm.GetUtxoProof().OpRetOutputPkScript) { + if opRetIdx < 0 || int(opRetIdx) >= len(btcTx.TxOut) || + !bytes.Equal(commitment, btcTx.TxOut[opRetIdx].PkScript) { - elog.Warn("checkConfirm op return commitment", "action", action, - "txHash", txHash, "confirmHash", confirmHash, "opRetIdx", confirm.GetUtxoProof().GetOpRetOutputIdx(), - "spendHash", spendHash, "commit", hex.EncodeToString(confirm.GetUtxoProof().OpRetOutputPkScript), + elog.Warn("Exec_Confirm op return commitment", "action", action, + "txHash", txHash, "confirmHash", confirmHash, "opRetIdx", opRetIdx, + "spendHash", spendHash, "txOutLen", len(btcTx.TxOut), "expectCommit", hex.EncodeToString(commitment)) return &types.Receipt{Ty: types.ExecOk}, nil } diff --git a/plugin/dapp/rgbx/executor/exec_test.go b/plugin/dapp/rgbx/executor/exec_test.go index 44b7c63aa..af21ca819 100644 --- a/plugin/dapp/rgbx/executor/exec_test.go +++ b/plugin/dapp/rgbx/executor/exec_test.go @@ -1,6 +1,7 @@ package executor import ( + "bytes" "testing" "github.com/33cn/chain33/client/mocks" @@ -11,6 +12,7 @@ import ( rtypes "github.com/33cn/plugin/plugin/dapp/rgbx/types" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -95,10 +97,16 @@ func TestRgbx_Exec_Confirm(t *testing.T) { utxoAddr := "74503993e7c8d4280f6fbb99ae5aaa92231a1981a358e40f97e2b4f4dfbea13c:0" normal, normal1, collect, collect1 := "normal", "normal1", "collect", "collect1" - mintScript, _ := txscript.NullDataScript([]byte(normal)) - mintScript1, _ := txscript.NullDataScript([]byte(collect)) - transferScript, _ := txscript.NullDataScript([]byte(normal1)) - transferScript1, _ := txscript.NullDataScript([]byte(collect1)) + buildTxWithOpReturn := func(data []byte) []byte { + tx := wire.NewMsgTx(wire.TxVersion) + tx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + script, _ := txscript.NullDataScript(data) + tx.AddTxOut(wire.NewTxOut(0, script)) + buf := new(bytes.Buffer) + _ = tx.SerializeNoWitness(buf) + return buf.Bytes() + } + tcArr := []*testCase{ { expectErr: nil, @@ -110,19 +118,37 @@ func TestRgbx_Exec_Confirm(t *testing.T) { }, { expectErr: nil, - action: &rtypes.ConfirmTx{ActionType: rtypes.TyMintAction, TxHash: []byte(normal), UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputPkScript: mintScript}}, + action: &rtypes.ConfirmTx{ + ActionType: rtypes.TyMintAction, + TxHash: []byte(normal), + UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputIdx: 0}, + BtcTxProof: &rtypes.BtcTxProof{TxData: buildTxWithOpReturn([]byte(normal))}, + }, }, { expectErr: nil, - action: &rtypes.ConfirmTx{ActionType: rtypes.TyMintAction, TxHash: []byte(collect), UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputPkScript: mintScript1}}, + action: &rtypes.ConfirmTx{ + ActionType: rtypes.TyMintAction, + TxHash: []byte(collect), + UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputIdx: 0}, + BtcTxProof: &rtypes.BtcTxProof{TxData: buildTxWithOpReturn([]byte(collect))}, + }, }, { expectErr: nil, - action: &rtypes.ConfirmTx{TxHash: []byte(normal1), UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputPkScript: transferScript}}, + action: &rtypes.ConfirmTx{ + TxHash: []byte(normal1), + UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputIdx: 0}, + BtcTxProof: &rtypes.BtcTxProof{TxData: buildTxWithOpReturn([]byte(normal1))}, + }, }, { expectErr: nil, - action: &rtypes.ConfirmTx{TxHash: []byte(collect1), UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputPkScript: transferScript1}}, + action: &rtypes.ConfirmTx{ + TxHash: []byte(collect1), + UtxoProof: &rtypes.UtxoSpendingProof{OpRetOutputIdx: 0}, + BtcTxProof: &rtypes.BtcTxProof{TxData: buildTxWithOpReturn([]byte(collect1))}, + }, }, } diff --git a/plugin/dapp/rgbx/proto/rgbx.proto b/plugin/dapp/rgbx/proto/rgbx.proto index 5137d253f..49ca41884 100644 --- a/plugin/dapp/rgbx/proto/rgbx.proto +++ b/plugin/dapp/rgbx/proto/rgbx.proto @@ -46,8 +46,6 @@ message outPoint { message utxoSpendingProof { uint32 spendingInputIdx = 1; int32 opRetOutputIdx = 2; - bytes spendingTx = 3; - bytes opRetOutputPkScript = 4; } message transferAsset { diff --git a/plugin/dapp/rgbx/types/rgbx.pb.go b/plugin/dapp/rgbx/types/rgbx.pb.go index c801a7b17..554712013 100644 --- a/plugin/dapp/rgbx/types/rgbx.pb.go +++ b/plugin/dapp/rgbx/types/rgbx.pb.go @@ -418,10 +418,8 @@ type UtxoSpendingProof struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SpendingInputIdx uint32 `protobuf:"varint,1,opt,name=spendingInputIdx,proto3" json:"spendingInputIdx,omitempty"` - OpRetOutputIdx int32 `protobuf:"varint,2,opt,name=opRetOutputIdx,proto3" json:"opRetOutputIdx,omitempty"` - SpendingTx []byte `protobuf:"bytes,3,opt,name=spendingTx,proto3" json:"spendingTx,omitempty"` - OpRetOutputPkScript []byte `protobuf:"bytes,4,opt,name=opRetOutputPkScript,proto3" json:"opRetOutputPkScript,omitempty"` + SpendingInputIdx uint32 `protobuf:"varint,1,opt,name=spendingInputIdx,proto3" json:"spendingInputIdx,omitempty"` + OpRetOutputIdx int32 `protobuf:"varint,2,opt,name=opRetOutputIdx,proto3" json:"opRetOutputIdx,omitempty"` } func (x *UtxoSpendingProof) Reset() { @@ -470,20 +468,6 @@ func (x *UtxoSpendingProof) GetOpRetOutputIdx() int32 { return 0 } -func (x *UtxoSpendingProof) GetSpendingTx() []byte { - if x != nil { - return x.SpendingTx - } - return nil -} - -func (x *UtxoSpendingProof) GetOpRetOutputPkScript() []byte { - if x != nil { - return x.OpRetOutputPkScript - } - return nil -} - type TransferAsset struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1620,160 +1604,155 @@ var file_rgbx_proto_rawDesc = []byte{ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0xb9, 0x01, - 0x0a, 0x11, 0x75, 0x74, 0x78, 0x6f, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, - 0x26, 0x0a, 0x0e, 0x6f, 0x70, 0x52, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x64, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6f, 0x70, 0x52, 0x65, 0x74, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x54, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, 0x30, 0x0a, 0x13, 0x6f, 0x70, 0x52, 0x65, 0x74, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x6f, 0x70, 0x52, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x50, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0xb7, 0x01, 0x0a, 0x0d, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, - 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, - 0x72, 0x6f, 0x6d, 0x55, 0x74, 0x78, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, - 0x72, 0x6f, 0x6d, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x72, 0x6f, 0x6d, 0x55, - 0x74, 0x78, 0x6f, 0x50, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x10, 0x66, 0x72, 0x6f, 0x6d, 0x55, 0x74, 0x78, 0x6f, 0x50, 0x6b, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x22, 0xbc, 0x02, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x54, - 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x32, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x14, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x78, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, - 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, - 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x36, 0x0a, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x75, 0x74, 0x78, 0x6f, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, - 0x31, 0x0a, 0x0a, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x62, 0x74, 0x63, 0x54, - 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x0a, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0x22, 0xa0, 0x01, 0x0a, 0x0a, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, - 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, - 0x78, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x69, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, - 0x4b, 0x47, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, - 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x6b, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6b, 0x67, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x22, 0x9d, 0x01, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, - 0x62, 0x6f, 0x6c, 0x12, 0x2b, 0x0a, 0x07, 0x74, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x62, 0x74, 0x63, - 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x07, 0x74, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, - 0x22, 0x8d, 0x01, 0x0a, 0x0d, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x65, - 0x65, 0x52, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, - 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x20, - 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, - 0x22, 0x94, 0x01, 0x0a, 0x0e, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, - 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, - 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x72, - 0x61, 0x70, 0x70, 0x65, 0x64, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x74, - 0x73, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x74, 0x73, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, - 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x80, 0x03, 0x0a, 0x09, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x78, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66, - 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, - 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, - 0x04, 0x75, 0x74, 0x78, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x75, 0x74, - 0x78, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, - 0x62, 0x6f, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x22, 0x40, 0x0a, 0x0a, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x73, 0x12, 0x32, 0x0a, 0x0b, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x52, - 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x56, 0x0a, 0x04, - 0x75, 0x74, 0x78, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x67, 0x0a, + 0x11, 0x75, 0x74, 0x78, 0x6f, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, 0x26, + 0x0a, 0x0e, 0x6f, 0x70, 0x52, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6f, 0x70, 0x52, 0x65, 0x74, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x49, 0x64, 0x78, 0x22, 0xb7, 0x01, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x22, 0x88, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x71, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, - 0x3f, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x22, 0x5d, 0x0a, 0x0d, 0x62, 0x74, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x16, 0x0a, - 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, - 0x4a, 0x0a, 0x0c, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x4f, 0x0a, 0x10, 0x74, - 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, 0x74, 0x12, - 0x3b, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, - 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, 0x74, 0x32, 0x06, 0x0a, 0x04, - 0x72, 0x67, 0x62, 0x78, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2e, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x72, 0x6f, 0x6d, + 0x55, 0x74, 0x78, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x72, 0x6f, 0x6d, + 0x55, 0x74, 0x78, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x74, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, + 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x72, 0x6f, 0x6d, 0x55, 0x74, 0x78, 0x6f, + 0x50, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, + 0x66, 0x72, 0x6f, 0x6d, 0x55, 0x74, 0x78, 0x6f, 0x50, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x22, 0xbc, 0x02, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x54, 0x78, 0x12, 0x1e, + 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x32, + 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x36, 0x0a, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x75, 0x74, 0x78, 0x6f, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x52, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x31, 0x0a, 0x0a, + 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x0a, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, + 0xa0, 0x01, 0x0a, 0x0a, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x20, + 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x44, 0x61, + 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x22, 0x69, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x44, 0x4b, 0x47, 0x12, + 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, + 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x6b, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6b, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x9d, 0x01, + 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, + 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x12, 0x2b, 0x0a, 0x07, 0x74, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x62, 0x74, 0x63, 0x54, 0x78, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x07, 0x74, 0x78, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x8d, 0x01, + 0x0a, 0x0d, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, + 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x22, 0x94, 0x01, + 0x0a, 0x0e, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x53, 0x79, 0x6d, + 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x73, 0x73, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x73, + 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x22, 0x80, 0x03, 0x0a, 0x09, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x54, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x52, + 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, + 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x04, 0x75, 0x74, + 0x78, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x75, 0x74, 0x78, 0x6f, 0x12, + 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, + 0x62, 0x6f, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x22, 0x40, 0x0a, 0x0a, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x54, 0x78, 0x73, 0x12, 0x32, 0x0a, 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x52, 0x0b, 0x70, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x56, 0x0a, 0x04, 0x75, 0x74, 0x78, + 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x22, 0x88, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x71, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, + 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3f, 0x0a, 0x0f, + 0x52, 0x65, 0x71, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, + 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x5d, 0x0a, + 0x0d, 0x62, 0x74, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4a, 0x0a, 0x0c, + 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x20, 0x0a, 0x0b, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x4f, 0x0a, 0x10, 0x74, 0x78, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x74, 0x78, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4c, 0x69, 0x73, 0x74, 0x32, 0x06, 0x0a, 0x04, 0x72, 0x67, 0x62, + 0x78, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2e, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 7fcf5c98f4971b30b7a5b1c6bebb919a3ca9b147 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Wed, 27 May 2026 12:17:55 +0800 Subject: [PATCH 2/7] fix: update exec_test.go to compute expected spendHash from BtcTxProof.TxData --- plugin/dapp/rgbx/executor/exec_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin/dapp/rgbx/executor/exec_test.go b/plugin/dapp/rgbx/executor/exec_test.go index af21ca819..b8804aa76 100644 --- a/plugin/dapp/rgbx/executor/exec_test.go +++ b/plugin/dapp/rgbx/executor/exec_test.go @@ -189,7 +189,8 @@ func TestRgbx_Exec_Confirm(t *testing.T) { require.Nil(t, readDB(state, formatAssetKey(collect), asset)) require.Equal(t, formatSymbol(collect), asset.Symbol) require.Equal(t, rtypes.Collectible, rtypes.AssetType(asset.Type)) - owner := rtypes.FormatUtxo(chainhash.DoubleHashH(nil).String(), 1) + collectSpendHash := chainhash.DoubleHashH(buildTxWithOpReturn([]byte(collect))).String() + owner := rtypes.FormatUtxo(collectSpendHash, 1) require.Equal(t, owner, asset.Owner) // check transfer @@ -197,7 +198,8 @@ func TestRgbx_Exec_Confirm(t *testing.T) { require.Equal(t, addr, asset.Owner) require.Equal(t, int64(0), accDB.LoadAccount(utxoAddr).Balance) require.Equal(t, int64(1), accDB.LoadAccount(addr).Balance) - changeAddr := rtypes.FormatUtxo(chainhash.DoubleHashH(nil).String(), 1) + normal1SpendHash := chainhash.DoubleHashH(buildTxWithOpReturn([]byte(normal1))).String() + changeAddr := rtypes.FormatUtxo(normal1SpendHash, 1) require.Equal(t, int64(1), accDB.LoadAccount(changeAddr).Balance) } From cb9157b61313a89d1d25998fdbd7db29a4b42f92 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Wed, 27 May 2026 14:30:29 +0800 Subject: [PATCH 3/7] test(rgbx): add native asset mint docker scenario and supplement unit tests - Add scenario_native_asset_mint docker integration test - Add run_native_tests / run_all_tests_wrapper entry points - Add native/all actions to case dispatch - Supplement checktx_test.go with BtcTxProof validation test cases - Fix duplicate import in checktx_test.go --- plugin/dapp/rgbx/cmd/ci/docker-compose.sh | 129 ++++++++++++++++++++ plugin/dapp/rgbx/executor/checktx_test.go | 141 +++++++++++++++++++++- 2 files changed, 268 insertions(+), 2 deletions(-) diff --git a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh index 69c639de9..2c976f9a0 100755 --- a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh +++ b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh @@ -698,6 +698,110 @@ function scenario_para_health() { ${PARA4_CLI} net is_sync >/dev/null } +function scenario_native_asset_mint() { + log_step "scenario: native asset mint with btc spending confirm" + + # 1. Get a mature coinbase UTXO + local utxo + utxo=$(build_mature_coinbase_utxo) + assert_non_empty "${utxo}" "native mint funding utxo empty" + + local utxo_txid + local utxo_vout + local utxo_amount + local utxo_pkscript + utxo_txid=$(echo "${utxo}" | cut -d: -f1) + utxo_vout=$(echo "${utxo}" | cut -d: -f2) + utxo_amount=$(echo "${utxo}" | cut -d: -f3) + utxo_pkscript=$(echo "${utxo}" | cut -d: -f4) + assert_non_empty "${utxo_txid}" "native mint utxo txid empty" + assert_non_empty "${utxo_pkscript}" "native mint utxo pkscript empty" + + # genesis_out format for mint: hash:index:pkScript + local genesis_out="${utxo_txid}:${utxo_vout}:${utxo_pkscript}" + + # 2. Mint a native asset on chain33 with the genesis UTXO + local mint_hash + mint_hash=$(${MAIN_CLI} send rgbx mint -s NATIVE1 -a 10000 -o "${genesis_out}" -m "6e6174697665316d657461" 2>/dev/null) + if [ -z "${mint_hash}" ] || [ ${#mint_hash} -lt 64 ]; then + fail "native mint send failed, hash=${mint_hash}" + fi + + # Wait for mint tx to be confirmed on chain33 + block_wait "${MAIN_CLI}" 1 + + # 3. Get the full mint tx hash from chain33 for OP_RETURN commitment + # The send output gives us the chain33 tx hash hex (may contain 0x prefix) + local mint_tx_hash_hex="${mint_hash}" + mint_tx_hash_hex="${mint_tx_hash_hex#0x}" + + # 4. Construct and broadcast BTC spending transaction + # Spend the genesis UTXO with OP_RETURN containing the chain33 mint tx hash + local fee=1000 + local spend_amount=$((utxo_amount - fee)) + if [ "${spend_amount}" -le 0 ]; then + fail "native mint insufficient utxo amount=${utxo_amount}, fee=${fee}" + fi + + # Convert amount to BTC for signrawtransactionwithkey prevtxs.amount + local amount_btc + amount_btc=$(awk "BEGIN{printf \"%.8f\", ${utxo_amount}/100000000}") + + # Import the mining key into btcd wallet (required for createrawtransaction signing) + # Use create/raw tx method with signrawtransactionwithkey (btcd v0.24.x) + local raw_tx_hex="" + raw_tx_hex=$(${BTC_CTL} --"${BTC_NETWORK}" createrawtransaction \ + '[{"txid":"'${utxo_txid}'","vout":'${utxo_vout}'}]' \ + '{"data":"'${mint_tx_hash_hex}'","'${BTCD_MINING_ADDR}'":'${spend_amount}'}') + assert_non_empty "${raw_tx_hex}" "native mint createrawtransaction failed" + + local signed_tx_json + signed_tx_json=$(${BTC_CTL} --"${BTC_NETWORK}" signrawtransactionwithkey \ + "${raw_tx_hex}" \ + '["'${BTC_FUNDING_WIF}'"]' \ + '[{"txid":"'${utxo_txid}'","vout":'${utxo_vout}',"scriptPubKey":"'${utxo_pkscript}'","amount":'${amount_btc}'}]') + local signed_hex + signed_hex=$(echo "${signed_tx_json}" | jq -r '.hex // empty') + local complete + complete=$(echo "${signed_tx_json}" | jq -r '.complete // false') + if [ -z "${signed_hex}" ] || [ "${complete}" != "true" ]; then + fail "native mint signrawtransactionwithkey failed: ${signed_tx_json}" + fi + + local spend_txid + spend_txid=$(${BTC_CTL} --"${BTC_NETWORK}" sendrawtransaction "${signed_hex}") + assert_non_empty "${spend_txid}" "native mint sendrawtransaction failed" + + # Mine BTC blocks for confirmations (neutrino uses blockConfirmations=1) + mine_btcd_blocks 2 + + # 5. Wait for the neutrino service to detect the UTXO spend and submit a confirm tx + log_step "wait for neutrino to confirm native asset mint (NATIVE1)" + local retries=60 + local i + for ((i = 0; i < retries; i++)); do + set +e + local asset_info + asset_info=$(${MAIN_CLI} rgbx getAsset -s NATIVE1 2>/dev/null) + local rc=$? + set -e + if [ "${rc}" -eq 0 ]; then + local symbol + symbol=$(echo "${asset_info}" | jq -r '.symbol // empty') + if [ "${symbol}" = "NATIVE1" ]; then + local total_amount + total_amount=$(echo "${asset_info}" | jq -r '.totalAmount // 0') + log_step "native asset NATIVE1 created, totalAmount=${total_amount}" + return 0 + fi + fi + mine_btcd_blocks 1 + sleep 2 + done + + fail "native asset NATIVE1 not created after timeout" +} + function ensure_btcd_network_consistency() { if [ "${BTC_NETWORK}" != "regtest" ]; then fail "unsupported BTC_NETWORK=${BTC_NETWORK}; this CI uses btcd --regtest only" @@ -716,6 +820,21 @@ function run_tests() { scenario_user_transfer_crosschain_asset scenario_user_withdraw_auto_confirm scenario_restart_recovery + scenario_native_asset_mint +} + +function run_native_tests() { + ensure_btcd_network_consistency + prepare_btcd_mining_identity + wait_btcd_ready + scenario_para_health + setup_para_nodegroup_on_main + wait_auto_dkg_commit + scenario_native_asset_mint +} + +function run_all_tests_wrapper() { + run_tests } function print_logs_hint() { @@ -751,6 +870,16 @@ case "${ACTION}" in run) do_run_all ;; +native) + do_up_only + run_native_tests + print_logs_hint + ;; +all) + do_up_only + run_all_tests_wrapper + print_logs_hint + ;; up) do_up_only ;; diff --git a/plugin/dapp/rgbx/executor/checktx_test.go b/plugin/dapp/rgbx/executor/checktx_test.go index b6a723591..7ef17dfbd 100644 --- a/plugin/dapp/rgbx/executor/checktx_test.go +++ b/plugin/dapp/rgbx/executor/checktx_test.go @@ -9,16 +9,17 @@ import ( "github.com/33cn/chain33/client/mocks" "github.com/33cn/chain33/common/crypto" + "github.com/33cn/chain33/common/merkle" "github.com/33cn/chain33/system/dapp" "github.com/33cn/chain33/types" "github.com/33cn/chain33/util" - "github.com/33cn/plugin/plugin/dapp/lightclient/lighttypes" ltypes "github.com/33cn/plugin/plugin/dapp/lightclient/lighttypes" paratypes "github.com/33cn/plugin/plugin/dapp/paracross/types" rtypes "github.com/33cn/plugin/plugin/dapp/rgbx/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/mock" @@ -285,6 +286,142 @@ func Test_checkConfirm(t *testing.T) { value.Confirm = tc.action.(*rtypes.ConfirmTx) testCheck(t, r, tx, action, tc.expectErr, idx) } + + // ===== BtcTxProof validation test cases ===== + // Set up additional pending txs and construct proper BtcTxProof fixtures. + var spendTx wire.MsgTx + spendTx.TxIn = append(spendTx.TxIn, &wire.TxIn{PreviousOutPoint: *out}) + spendTx.TxOut = append(spendTx.TxOut, wire.NewTxOut(1000, []byte{0x51})) + + spendBuf := new(bytes.Buffer) + require.NoError(t, spendTx.SerializeNoWitness(spendBuf)) + spendTxData := spendBuf.Bytes() + spendTxID := spendTx.TxHash() + + // Compute a valid Merkle proof for spendTx + leaves := [][]byte{spendTxID.CloneBytes()} + _, branch := merkle.GetMerkleRootAndBranch(leaves, 0) + root := merkle.GetMerkleRoot(leaves) + rootHash, err := chainhash.NewHash(root) + require.Nil(t, err) + + // Pending / payload at (10, 0) for the valid proof case + require.Nil(t, local.Set(formatPendingTxKey(10, 0), types.Encode(&rtypes.PendingTx{ + Utxo: &rtypes.OutPoint{Hash: out.Hash.String()}, + TxHash: []byte("hash10"), + }))) + require.Nil(t, state.Set(formatPayloadKey([]byte("hash10")), types.Encode(&rtypes.MintAsset{Symbol: "y", TotalAmount: 1}))) + + // Pending / payload at (11, 0) for block height mismatch case + require.Nil(t, local.Set(formatPendingTxKey(11, 0), types.Encode(&rtypes.PendingTx{ + Utxo: &rtypes.OutPoint{Hash: out.Hash.String()}, + TxHash: []byte("hash11"), + }))) + require.Nil(t, state.Set(formatPayloadKey([]byte("hash11")), types.Encode(&rtypes.MintAsset{Symbol: "z", TotalAmount: 1}))) + + // Pending / payload at (12, 0) for merkle proof mismatch case + require.Nil(t, local.Set(formatPendingTxKey(12, 0), types.Encode(&rtypes.PendingTx{ + Utxo: &rtypes.OutPoint{Hash: out.Hash.String()}, + TxHash: []byte("hash12"), + }))) + require.Nil(t, state.Set(formatPayloadKey([]byte("hash12")), types.Encode(&rtypes.MintAsset{Symbol: "w", TotalAmount: 1}))) + + // Proof 1: valid proof for height 10 + validProof := &rtypes.BtcTxProof{ + TxData: spendTxData, + BlockHeight: 10, + BlockHash: "dead10", + TxIndex: 0, + MerkleProof: branch, + } + + // Proof 2: valid tx data + merkle proof but API returns wrong height (10 vs 11) + heightMismatchProof := &rtypes.BtcTxProof{ + TxData: spendTxData, + BlockHeight: 11, + BlockHash: "dead11", + TxIndex: 0, + MerkleProof: branch, + } + + // Proof 3: tx data is fine but Merkle branch is wrong + badBranch := [][]byte{bytes.Repeat([]byte{0xff}, 32)} + badMerkleProof := &rtypes.BtcTxProof{ + TxData: spendTxData, + BlockHeight: 12, + BlockHash: "dead12", + TxIndex: 0, + MerkleProof: badBranch, + } + + // Mock header queries for each proof height. + // Height 10: exact match -> validateBtcTxProof should succeed. + api.On("Query", ltypes.LightclientX, "GetBtcHeader", mock.MatchedBy(func(req types.Message) bool { + h, ok := req.(*ltypes.ReqGetBtcHeader) + return ok && h != nil && h.Height == 10 + })).Return(<ypes.BtcHeader{ + Hash: "dead10", + Height: 10, + MerkleRoot: rootHash.String(), + }, nil) + + // Height 11: header height is 10 (doesn't match proof.BlockHeight=11) + api.On("Query", ltypes.LightclientX, "GetBtcHeader", mock.MatchedBy(func(req types.Message) bool { + h, ok := req.(*ltypes.ReqGetBtcHeader) + return ok && h != nil && h.Height == 11 + })).Return(<ypes.BtcHeader{ + Hash: "dead11", + Height: 10, + MerkleRoot: rootHash.String(), + }, nil) + + // Height 12: header returns valid data but merkle branch in proof is wrong + api.On("Query", ltypes.LightclientX, "GetBtcHeader", mock.MatchedBy(func(req types.Message) bool { + h, ok := req.(*ltypes.ReqGetBtcHeader) + return ok && h != nil && h.Height == 12 + })).Return(<ypes.BtcHeader{ + Hash: "dead12", + Height: 12, + MerkleRoot: rootHash.String(), + }, nil) + + btcProofCases := []*testCase{ + { + expectErr: nil, // valid BtcTxProof with matching merkle proof + action: &rtypes.ConfirmTx{ + TxBlockHeight: 10, + TxIndex: 0, + TxHash: []byte("hash10"), + UtxoProof: &rtypes.UtxoSpendingProof{SpendingInputIdx: 0, OpRetOutputIdx: 0}, + BtcTxProof: validProof, + }, + }, + { + expectErr: ErrInvalidBtcProofBlock, // header height 10 != proof height 11 + action: &rtypes.ConfirmTx{ + TxBlockHeight: 11, + TxIndex: 0, + TxHash: []byte("hash11"), + UtxoProof: &rtypes.UtxoSpendingProof{SpendingInputIdx: 0, OpRetOutputIdx: -1}, + BtcTxProof: heightMismatchProof, + }, + }, + { + expectErr: ErrInvalidBtcProofMerkle, // merkle branch doesn't match tx data + action: &rtypes.ConfirmTx{ + TxBlockHeight: 12, + TxIndex: 0, + TxHash: []byte("hash12"), + UtxoProof: &rtypes.UtxoSpendingProof{SpendingInputIdx: 0, OpRetOutputIdx: -1}, + BtcTxProof: badMerkleProof, + }, + }, + } + + for idx, tc := range btcProofCases { + value.Confirm = tc.action.(*rtypes.ConfirmTx) + testCheck(t, r, tx, action, tc.expectErr, len(tcArr)+idx) + } } func newTestnetWitnessAddr(t *testing.T) (addr string, pkScript []byte) { @@ -493,7 +630,7 @@ func Test_checkCommitDKG(t *testing.T) { func Test_decodeBtcAddressScript(t *testing.T) { - params := lighttypes.GetBtcChainParams("regtest") + params := ltypes.GetBtcChainParams("regtest") priv, err := btcec.NewPrivateKey() require.Nil(t, err) From e6f90d2a1028f2610a3bea8fd19eb82fa630a0c1 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Wed, 27 May 2026 17:53:26 +0800 Subject: [PATCH 4/7] fix(rgbx): add native asset mint CI scenario with btcMintSpend command - Add btcMintSpend Go command for building/signing/broadcasting BTC mint spending tx with OP_RETURN, replacing fragile raw tx hex parsing - Fix native case routing to run_native_tests, bypassing chain33 solo consensus post-restart block production failure - Add -k GENESIS_KEY to mint command so stdout captures tx hash - Add 101 BTC block mining prerequisite before native asset mint - Add platform: linux/amd64 to docker-compose.yml for Apple Silicon - Add restart: unless-stopped to para2-4 for reliability Co-Authored-By: Claude --- plugin/dapp/rgbx/cmd/ci/docker-compose.sh | 50 +++----- plugin/dapp/rgbx/cmd/ci/docker-compose.yml | 9 ++ plugin/dapp/rgbx/commands/btc.go | 132 +++++++++++++++++++++ plugin/dapp/rgbx/commands/commands.go | 1 + 4 files changed, 161 insertions(+), 31 deletions(-) diff --git a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh index 2c976f9a0..048c9ddbf 100755 --- a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh +++ b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh @@ -15,7 +15,7 @@ ACTION="run" PROJECT="" if [ "$#" -gt 0 ]; then case "${1}" in - run | up | down | init | config | test) + run | up | down | init | config | test | native) ACTION="${1}" PROJECT="${2:-build}" ;; @@ -722,7 +722,7 @@ function scenario_native_asset_mint() { # 2. Mint a native asset on chain33 with the genesis UTXO local mint_hash - mint_hash=$(${MAIN_CLI} send rgbx mint -s NATIVE1 -a 10000 -o "${genesis_out}" -m "6e6174697665316d657461" 2>/dev/null) + mint_hash=$(${MAIN_CLI} send rgbx mint -s NATIVE1 -a 10000 -o "${genesis_out}" -m "6e6174697665316d657461" -k "${GENESIS_KEY}" 2>/dev/null) if [ -z "${mint_hash}" ] || [ ${#mint_hash} -lt 64 ]; then fail "native mint send failed, hash=${mint_hash}" fi @@ -735,42 +735,27 @@ function scenario_native_asset_mint() { local mint_tx_hash_hex="${mint_hash}" mint_tx_hash_hex="${mint_tx_hash_hex#0x}" - # 4. Construct and broadcast BTC spending transaction - # Spend the genesis UTXO with OP_RETURN containing the chain33 mint tx hash + # 4. Construct, sign, and broadcast BTC spending transaction using btcMintSpend local fee=1000 local spend_amount=$((utxo_amount - fee)) if [ "${spend_amount}" -le 0 ]; then fail "native mint insufficient utxo amount=${utxo_amount}, fee=${fee}" fi - # Convert amount to BTC for signrawtransactionwithkey prevtxs.amount - local amount_btc - amount_btc=$(awk "BEGIN{printf \"%.8f\", ${utxo_amount}/100000000}") - - # Import the mining key into btcd wallet (required for createrawtransaction signing) - # Use create/raw tx method with signrawtransactionwithkey (btcd v0.24.x) - local raw_tx_hex="" - raw_tx_hex=$(${BTC_CTL} --"${BTC_NETWORK}" createrawtransaction \ - '[{"txid":"'${utxo_txid}'","vout":'${utxo_vout}'}]' \ - '{"data":"'${mint_tx_hash_hex}'","'${BTCD_MINING_ADDR}'":'${spend_amount}'}') - assert_non_empty "${raw_tx_hex}" "native mint createrawtransaction failed" - - local signed_tx_json - signed_tx_json=$(${BTC_CTL} --"${BTC_NETWORK}" signrawtransactionwithkey \ - "${raw_tx_hex}" \ - '["'${BTC_FUNDING_WIF}'"]' \ - '[{"txid":"'${utxo_txid}'","vout":'${utxo_vout}',"scriptPubKey":"'${utxo_pkscript}'","amount":'${amount_btc}'}]') - local signed_hex - signed_hex=$(echo "${signed_tx_json}" | jq -r '.hex // empty') - local complete - complete=$(echo "${signed_tx_json}" | jq -r '.complete // false') - if [ -z "${signed_hex}" ] || [ "${complete}" != "true" ]; then - fail "native mint signrawtransactionwithkey failed: ${signed_tx_json}" - fi - local spend_txid - spend_txid=$(${BTC_CTL} --"${BTC_NETWORK}" sendrawtransaction "${signed_hex}") - assert_non_empty "${spend_txid}" "native mint sendrawtransaction failed" + spend_txid=$(compose_cmd exec -T main /root/chain33-cli rgbx btcMintSpend \ + --net "${BTC_NETWORK}" \ + --rpcHost "${BTC_RPC_ADDR}" \ + --rpcUser "${BTCD_RPC_USER}" \ + --rpcPass "${BTCD_RPC_PASS}" \ + --disableTLS=false \ + --rpcCertFile "${BTCD_RPC_CERT_IN_CONTAINER}" \ + --wif "${BTC_FUNDING_WIF}" \ + --utxo "${utxo}" \ + --destAddress "${BTCD_MINING_ADDR}" \ + --opReturnData "${mint_tx_hash_hex}" \ + --fee "${fee}") + assert_non_empty "${spend_txid}" "native mint btcMintSpend failed" # Mine BTC blocks for confirmations (neutrino uses blockConfirmations=1) mine_btcd_blocks 2 @@ -830,6 +815,9 @@ function run_native_tests() { scenario_para_health setup_para_nodegroup_on_main wait_auto_dkg_commit + + # Mine BTC blocks so build_mature_coinbase_utxo can find a mature coinbase + mine_btcd_blocks 101 scenario_native_asset_mint } diff --git a/plugin/dapp/rgbx/cmd/ci/docker-compose.yml b/plugin/dapp/rgbx/cmd/ci/docker-compose.yml index e71494042..9b45db0cc 100644 --- a/plugin/dapp/rgbx/cmd/ci/docker-compose.yml +++ b/plugin/dapp/rgbx/cmd/ci/docker-compose.yml @@ -1,5 +1,6 @@ services: main: + platform: linux/amd64 build: context: . environment: @@ -11,6 +12,7 @@ services: - ./btcd-data:/btcd:ro para1: + platform: linux/amd64 build: context: . environment: @@ -24,36 +26,43 @@ services: - ./btcd-data:/btcd:ro para2: + platform: linux/amd64 build: context: . environment: ChainConfig: "chain33.para2.toml" depends_on: - main + restart: unless-stopped volumes: - ./btcd-data:/btcd:ro para3: + platform: linux/amd64 build: context: . environment: ChainConfig: "chain33.para3.toml" depends_on: - main + restart: unless-stopped volumes: - ./btcd-data:/btcd:ro para4: + platform: linux/amd64 build: context: . environment: ChainConfig: "chain33.para4.toml" depends_on: - main + restart: unless-stopped volumes: - ./btcd-data:/btcd:ro btcd: + platform: linux/amd64 # RPC TLS cert auto-gen uses os.Hostname(); must match Docker DNS name "btcd" # so para/lightclient can dial btcd:18443 without SAN mismatch. hostname: btcd diff --git a/plugin/dapp/rgbx/commands/btc.go b/plugin/dapp/rgbx/commands/btc.go index cfc962715..074f13a43 100644 --- a/plugin/dapp/rgbx/commands/btc.go +++ b/plugin/dapp/rgbx/commands/btc.go @@ -248,6 +248,138 @@ func btcDepositTx(cmd *cobra.Command, _ []string) { fmt.Println(txHash.String()) } +func btcMintSpendCMD() *cobra.Command { + cmd := &cobra.Command{ + Use: "btcMintSpend", + Short: "build, sign and broadcast btc mint spending tx with OP_RETURN", + Run: btcMintSpend, + Example: "btcMintSpend --net regtest --rpcHost 127.0.0.1:18443 " + + "--wif --utxo " + + "--destAddress --opReturnData --fee 1000", + } + cmd.Flags().String("net", "regtest", "bitcoin network: mainnet|testnet|regtest|simnet") + cmd.Flags().String("rpcHost", "127.0.0.1:18443", "bitcoin rpc host") + cmd.Flags().String("rpcUser", "", "bitcoin rpc user (optional)") + cmd.Flags().String("rpcPass", "", "bitcoin rpc password (optional)") + cmd.Flags().Bool("disableTLS", true, "disable rpc tls") + cmd.Flags().String("rpcCertFile", "", "bitcoin rpc cert file path (optional, required when TLS enabled)") + cmd.Flags().String("wif", "", "sender private key in WIF format") + cmd.Flags().String("utxo", "", "single input utxo, format: txid:vout:amountSats:pkScriptHex") + cmd.Flags().String("destAddress", "", "btc destination address for change output") + cmd.Flags().String("opReturnData", "", "hex data for OP_RETURN output (chain33 mint tx hash)") + cmd.Flags().Int64("fee", 0, "tx fee in satoshis") + markRequired(cmd, "wif", "utxo", "destAddress", "opReturnData", "fee") + return cmd +} + +func btcMintSpend(cmd *cobra.Command, _ []string) { + netName, _ := cmd.Flags().GetString("net") + rpcHost, _ := cmd.Flags().GetString("rpcHost") + rpcUser, _ := cmd.Flags().GetString("rpcUser") + rpcPass, _ := cmd.Flags().GetString("rpcPass") + disableTLS, _ := cmd.Flags().GetBool("disableTLS") + rpcCertFile, _ := cmd.Flags().GetString("rpcCertFile") + wifStr, _ := cmd.Flags().GetString("wif") + utxoRaw, _ := cmd.Flags().GetString("utxo") + destAddrStr, _ := cmd.Flags().GetString("destAddress") + opReturnDataHex, _ := cmd.Flags().GetString("opReturnData") + fee, _ := cmd.Flags().GetInt64("fee") + + params, err := parseNetParams(netName) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "invalid net: %s, err: %v\n", netName, err) + return + } + if fee < 0 { + _, _ = fmt.Fprintf(os.Stderr, "invalid fee(%d)\n", fee) + return + } + utxo, err := parseDepositUTXO(utxoRaw) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "invalid utxo: %v\n", err) + return + } + if utxo.amount <= fee { + _, _ = fmt.Fprintf(os.Stderr, "insufficient utxo amount, have=%d need>%d\n", utxo.amount, fee) + return + } + + wif, err := btcutil.DecodeWIF(strings.TrimSpace(wifStr)) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "invalid wif: %v\n", err) + return + } + destAddr, err := btcutil.DecodeAddress(strings.TrimSpace(destAddrStr), params) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "invalid destAddress: %v\n", err) + return + } + + opReturnData, err := hex.DecodeString(strings.TrimSpace(opReturnDataHex)) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "invalid opReturnData hex: %v\n", err) + return + } + + tx := wire.NewMsgTx(wire.TxVersion) + tx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(utxo.hash, utxo.vout), nil, nil)) + + // Add OP_RETURN output (must be first output, index 0 for neutrino to detect) + opScript, err := txscript.NullDataScript(opReturnData) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "build op_return failed: %v\n", err) + return + } + tx.AddTxOut(wire.NewTxOut(0, opScript)) + + // Add destination output (change to mining address) + destScript, err := txscript.PayToAddrScript(destAddr) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "build dest script failed: %v\n", err) + return + } + change := utxo.amount - fee + tx.AddTxOut(wire.NewTxOut(change, destScript)) + + // Sign the P2PKH input + sigScript, err := txscript.SignatureScript(tx, 0, utxo.pkScript, txscript.SigHashAll, wif.PrivKey, true) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "sign p2pkh failed: %v\n", err) + return + } + tx.TxIn[0].SignatureScript = sigScript + + // Connect to btcd RPC and broadcast + connCfg := &rpcclient.ConnConfig{ + Host: rpcHost, + User: rpcUser, + Pass: rpcPass, + HTTPPostMode: true, + DisableTLS: disableTLS, + } + if !disableTLS && strings.TrimSpace(rpcCertFile) != "" { + certs, err := os.ReadFile(strings.TrimSpace(rpcCertFile)) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "read rpc cert file failed: %v\n", err) + return + } + connCfg.Certificates = certs + } + rpcCli, err := rpcclient.New(connCfg, nil) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "create rpc client failed: %v\n", err) + return + } + defer rpcCli.Shutdown() + + txHash, err := rpcCli.SendRawTransaction(tx, false) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "broadcast tx failed: %v\n", err) + return + } + fmt.Println(txHash.String()) +} + func btcKeyInfoCMD() *cobra.Command { cmd := &cobra.Command{ Use: "btcKeyInfo", diff --git a/plugin/dapp/rgbx/commands/commands.go b/plugin/dapp/rgbx/commands/commands.go index 3314c88b2..5dbbcc3f5 100644 --- a/plugin/dapp/rgbx/commands/commands.go +++ b/plugin/dapp/rgbx/commands/commands.go @@ -27,6 +27,7 @@ func Cmd() *cobra.Command { commitDKGCMD(), btcAddrScriptCMD(), btcDepositTxCMD(), + btcMintSpendCMD(), btcKeyInfoCMD(), depositAssetCMD(), withdrawAssetCMD(), From 809983d2d2524b8888a5eb2d4888f1c9463ae764 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:28:26 +0800 Subject: [PATCH 5/7] fix(rgbx): improve OP_RETURN matching in createConfirmPayload, prefer matching commitment - In createConfirmPayload, scan all OP_RETURN outputs and prefer the one matching the expected commitment from pending tx hash - Fall back to first OP_RETURN if no match found - Add unit test for the matching behavior - Fix docker-compose.sh case pattern missing 'all' action --- .../rpc/lightclient/neutrino/rgbx.go | 18 +++++++++- .../neutrino/rgbx_crosschain_test.go | 33 +++++++++++++++++++ plugin/dapp/rgbx/cmd/ci/docker-compose.sh | 2 +- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go index 716d590bc..240e6dde6 100644 --- a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go +++ b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx.go @@ -1,6 +1,7 @@ package neutrino import ( + "bytes" "encoding/hex" "runtime" "strings" @@ -296,11 +297,26 @@ func (r *rgbx) createConfirmPayload(info *utxoSpendInfo, pendTx *rtypes.PendingT SpendingInputIdx: info.spendingInputIndex, OpRetOutputIdx: -1, } + expectedCommitment, err := txscript.NullDataScript(pendTx.GetTxHash()) + if err != nil { + log.Error("createConfirmPayload NullDataScript", "pendingTxHash", info.pendingTxHash, "err", err) + return nil, err + } + firstOpRetIdx := int32(-1) for idx, out := range info.spendingTx.TxOut { if len(out.PkScript) > 0 && out.PkScript[0] == txscript.OP_RETURN { - proof.OpRetOutputIdx = int32(idx) + if firstOpRetIdx < 0 { + firstOpRetIdx = int32(idx) + } + if bytes.Equal(out.PkScript, expectedCommitment) { + proof.OpRetOutputIdx = int32(idx) + break + } } } + if proof.OpRetOutputIdx < 0 { + proof.OpRetOutputIdx = firstOpRetIdx + } confirm.UtxoProof = proof if r.client != nil { diff --git a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go index c83a20398..dded161ca 100644 --- a/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go +++ b/plugin/dapp/lightclient/rpc/lightclient/neutrino/rgbx_crosschain_test.go @@ -91,6 +91,39 @@ func Test_rgbx_createConfirmPayload_withSpendingTxAndOpReturn(t *testing.T) { require.Nil(t, confirm.BtcTxProof) } +func Test_rgbx_createConfirmPayload_prefersMatchingCommitmentOpReturn(t *testing.T) { + r := newRGBX() + r.pendingCache.addTx("abc", &rtypes.PendingTx{TxBlockHeight: 10}) + + spendTx := wire.NewMsgTx(wire.TxVersion) + spendTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) + wrongOpRet, err := txscript.NullDataScript([]byte("rgbx:wrong")) + require.NoError(t, err) + rightCommitment, err := txscript.NullDataScript([]byte{0x11, 0x22, 0x33}) + require.NoError(t, err) + spendTx.AddTxOut(wire.NewTxOut(0, wrongOpRet)) + spendTx.AddTxOut(wire.NewTxOut(0, rightCommitment)) + spendTx.AddTxOut(wire.NewTxOut(1000, []byte{txscript.OP_0, 0x14})) + + info := &utxoSpendInfo{ + pendingTxHash: "abc", + spendingInputIndex: 0, + spendingTx: spendTx, + blockHash: chainhash.Hash{0x01}, + } + pendTx := &rtypes.PendingTx{ + ActionType: rtypes.TyMintAction, + TxBlockHeight: 5, + TxIndex: 1, + TxHash: []byte{0x11, 0x22, 0x33}, + } + + confirm, err := r.createConfirmPayload(info, pendTx) + require.NoError(t, err) + require.NotNil(t, confirm.UtxoProof) + require.Equal(t, int32(1), confirm.UtxoProof.OpRetOutputIdx) +} + func Test_estimateBtcFee(t *testing.T) { tx := wire.NewMsgTx(wire.TxVersion) tx.AddTxIn(wire.NewTxIn(&wire.OutPoint{}, nil, nil)) diff --git a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh index 048c9ddbf..6a4e2d941 100755 --- a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh +++ b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh @@ -15,7 +15,7 @@ ACTION="run" PROJECT="" if [ "$#" -gt 0 ]; then case "${1}" in - run | up | down | init | config | test | native) + run | up | down | init | config | test | native | all) ACTION="${1}" PROJECT="${2:-build}" ;; From 98ecd9fbfd645f72a6ccf5ce8850f434a0907496 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:30:24 +0800 Subject: [PATCH 6/7] fix: move native asset test before restart, remove redundant block_wait, add mint send error handling --- plugin/dapp/rgbx/cmd/ci/docker-compose.sh | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh index 6a4e2d941..674d0b55c 100755 --- a/plugin/dapp/rgbx/cmd/ci/docker-compose.sh +++ b/plugin/dapp/rgbx/cmd/ci/docker-compose.sh @@ -720,15 +720,30 @@ function scenario_native_asset_mint() { # genesis_out format for mint: hash:index:pkScript local genesis_out="${utxo_txid}:${utxo_vout}:${utxo_pkscript}" + # Verify chain33 is producing blocks before mint + local pre_height + pre_height=$(${MAIN_CLI} block last_header | jq '.height') + log_step "chain33 height before mint: ${pre_height}" + # 2. Mint a native asset on chain33 with the genesis UTXO local mint_hash - mint_hash=$(${MAIN_CLI} send rgbx mint -s NATIVE1 -a 10000 -o "${genesis_out}" -m "6e6174697665316d657461" -k "${GENESIS_KEY}" 2>/dev/null) - if [ -z "${mint_hash}" ] || [ ${#mint_hash} -lt 64 ]; then - fail "native mint send failed, hash=${mint_hash}" + local mint_rc + local mint_stdout + mint_stdout=$(${MAIN_CLI} send rgbx mint -s NATIVE1 -a 10000 -o "${genesis_out}" -m "6e6174697665316d657461" -k "${GENESIS_KEY}" 2>/tmp/mint_stderr.$$) + mint_rc=$? + mint_hash="${mint_stdout}" + if [ ${mint_rc} -ne 0 ] || [ -z "${mint_hash}" ] || [ ${#mint_hash} -lt 64 ]; then + log_step "mint send stderr: $(head -5 /tmp/mint_stderr.$$ 2>/dev/null)" + rm -f /tmp/mint_stderr.$$ + fail "native mint send failed, rc=${mint_rc}, hash=${mint_hash}" fi + rm -f /tmp/mint_stderr.$$ # Wait for mint tx to be confirmed on chain33 - block_wait "${MAIN_CLI}" 1 + # send command already waits for the tx to be committed in a block + local post_height + post_height=$(${MAIN_CLI} block last_header | jq '.height') + log_step "mint tx hash: ${mint_hash}, height after mint: ${post_height}" # 3. Get the full mint tx hash from chain33 for OP_RETURN commitment # The send output gives us the chain33 tx hash hex (may contain 0x prefix) @@ -804,8 +819,9 @@ function run_tests() { scenario_user_deposit_via_btc_tx scenario_user_transfer_crosschain_asset scenario_user_withdraw_auto_confirm - scenario_restart_recovery + # Run native asset test before restart so chain33 consensus is stable scenario_native_asset_mint + scenario_restart_recovery } function run_native_tests() { From ffeb64c4439d71aa333a73d3330be2b0e61bc419 Mon Sep 17 00:00:00 2001 From: jiangpeng <11565373+bysomeone@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:57:52 +0800 Subject: [PATCH 7/7] chore: trigger jenkins build for new commit