diff --git a/address/address.go b/address/address.go index 74cf35101..f7bb52516 100644 --- a/address/address.go +++ b/address/address.go @@ -196,9 +196,9 @@ func New(version Version, genesis asset.Genesis, groupKey *btcec.PublicKey, // We can only use a tapscript sibling that is not a Taproot Asset // commitment. if tapscriptSibling != nil { - if err := tapscriptSibling.VerifyNoCommitment(); err != nil { + if _, err := tapscriptSibling.TapHash(); err != nil { return nil, errors.New("address: tapscript sibling " + - "is a Taproot Asset commitment") + "is invalid") } } diff --git a/address/address_test.go b/address/address_test.go index 1ac1789ca..314b4bcce 100644 --- a/address/address_test.go +++ b/address/address_test.go @@ -48,11 +48,15 @@ func randAddress(t *testing.T, net *ChainParams, v Version, groupPubKey, amount = test.RandInt[uint64]() } - var tapscriptSibling *commitment.TapscriptPreimage + var ( + tapscriptSibling *commitment.TapscriptPreimage + err error + ) if sibling { - tapscriptSibling = commitment.NewPreimageFromLeaf( + tapscriptSibling, err = commitment.NewPreimageFromLeaf( txscript.NewBaseTapLeaf([]byte("not a valid script")), ) + require.NoError(t, err) } scriptKey := *pubKey diff --git a/address/mock.go b/address/mock.go index 478c358e3..f6d7b7935 100644 --- a/address/mock.go +++ b/address/mock.go @@ -70,9 +70,11 @@ func RandAddr(t testing.TB, params *ChainParams, groupPubKey = &groupInfo.GroupPubKey groupWitness = groupInfo.Witness - tapscriptSibling = commitment.NewPreimageFromLeaf( + var err error + tapscriptSibling, err = commitment.NewPreimageFromLeaf( txscript.NewBaseTapLeaf([]byte("not a valid script")), ) + require.NoError(t, err) } tapAddr, err := New( diff --git a/address/records.go b/address/records.go index ac4cc2d77..77e48c1bb 100644 --- a/address/records.go +++ b/address/records.go @@ -1,6 +1,7 @@ package address import ( + "bytes" "net/url" "github.com/btcsuite/btcd/btcec/v2" @@ -86,8 +87,14 @@ func newAddressTapscriptSiblingRecord( tapscriptSibling **commitment.TapscriptPreimage) tlv.Record { sizeFunc := func() uint64 { - // 1 byte for the type, and then the pre-image itself. - return 1 + uint64(len((*tapscriptSibling).SiblingPreimage)) + var buf bytes.Buffer + err := commitment.TapscriptPreimageEncoder( + &buf, tapscriptSibling, &[8]byte{}, + ) + if err != nil { + panic(err) + } + return uint64(len(buf.Bytes())) } return tlv.MakeDynamicRecord( addrTapscriptSiblingType, tapscriptSibling, sizeFunc, diff --git a/asset/asset.go b/asset/asset.go index dab13565e..3e477ce82 100644 --- a/asset/asset.go +++ b/asset/asset.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "math" "reflect" "strings" "time" @@ -22,6 +23,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/mssmt" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -464,6 +466,175 @@ const ( ScriptV0 ScriptVersion = 0 ) +// TapscriptTreeNodes represents the two supported ways to define a tapscript +// tree to be used as a sibling for a Taproot Asset commitment, an asset group +// key, or an asset script key. This type is used for interfacing with the DB, +// not for supplying in a proof or key derivation. The inner fields are mutually +// exclusive. +type TapscriptTreeNodes struct { + // leaves is created from an ordered list of TapLeaf objects and + // represents a Tapscript tree. + leaves *TapLeafNodes + + // branch is created from a TapBranch and represents the tapHashes of + // the child nodes of a TapBranch. + branch *TapBranchNodes +} + +// GetLeaves returns an Option containing a copy of the internal TapLeafNodes, +// if it exists. +func GetLeaves(ttn TapscriptTreeNodes) fn.Option[TapLeafNodes] { + return fn.MaybeSome(ttn.leaves) +} + +// GetBranch returns an Option containing a copy of the internal TapBranchNodes, +// if it exists. +func GetBranch(ttn TapscriptTreeNodes) fn.Option[TapBranchNodes] { + return fn.MaybeSome(ttn.branch) +} + +// FromBranch creates a TapscriptTreeNodes object from a TapBranchNodes object. +func FromBranch(tbn TapBranchNodes) TapscriptTreeNodes { + return TapscriptTreeNodes{ + branch: &tbn, + } +} + +// FromLeaves creates a TapscriptTreeNodes object from a TapLeafNodes object. +func FromLeaves(tln TapLeafNodes) TapscriptTreeNodes { + return TapscriptTreeNodes{ + leaves: &tln, + } +} + +// CheckTapLeafSanity asserts that a TapLeaf script is smaller than the maximum +// witness size, and that the TapLeaf version is Tapscript v0. +func CheckTapLeafSanity(leaf *txscript.TapLeaf) error { + if leaf == nil { + return fmt.Errorf("leaf cannot be nil") + } + + if leaf.LeafVersion != txscript.BaseLeafVersion { + return fmt.Errorf("tapleaf version %d not supported", + leaf.LeafVersion) + } + + if len(leaf.Script) == 0 { + return fmt.Errorf("tapleaf script is empty") + } + + if len(leaf.Script) >= blockchain.MaxBlockWeight { + return fmt.Errorf("tapleaf script too large") + } + + return nil +} + +// TapLeafNodes represents an ordered list of TapLeaf objects, that have been +// checked for their script version and size. These leaves can be stored to and +// loaded from the DB. +type TapLeafNodes struct { + v []txscript.TapLeaf +} + +// TapTreeNodesFromLeaves sanity checks an ordered list of TapLeaf objects and +// constructs a TapscriptTreeNodes object if all leaves are valid. +func TapTreeNodesFromLeaves(leaves []txscript.TapLeaf) (*TapscriptTreeNodes, + error) { + + err := CheckTapLeavesSanity(leaves) + if err != nil { + return nil, err + } + + nodes := TapscriptTreeNodes{ + leaves: &TapLeafNodes{ + v: leaves, + }, + } + + return &nodes, nil +} + +// CheckTapLeavesSanity asserts that a slice of TapLeafs is below the maximum +// size, and that each leaf passes a sanity check for script version and size. +func CheckTapLeavesSanity(leaves []txscript.TapLeaf) error { + if len(leaves) == 0 { + return fmt.Errorf("no leaves given") + } + + // The maximum number of leaves we will allow for a Tapscript tree we + // store is 2^15 - 1. To use a larger tree, create a TapscriptTreeNodes + // object from a TapBranch instead. + if len(leaves) > math.MaxInt16 { + return fmt.Errorf("tapleaf count larger than %d", + math.MaxInt16) + } + + // Reject any leaf not using the initial Tapscript version, or with a + // script size above the maximum blocksize. + for i := range leaves { + err := CheckTapLeafSanity(&leaves[i]) + if err != nil { + return err + } + } + + return nil +} + +// ToLeaves returns the TapLeaf slice inside a TapLeafNodes object. +func ToLeaves(l TapLeafNodes) []txscript.TapLeaf { + return append([]txscript.TapLeaf{}, l.v...) +} + +// LeafNodesRootHash returns the root hash of a Tapscript tree built from the +// TapLeaf nodes in a TapLeafNodes object. +func LeafNodesRootHash(l TapLeafNodes) chainhash.Hash { + return txscript.AssembleTaprootScriptTree(l.v...).RootNode.TapHash() +} + +// TapBranchNodesLen is the length of a TapBranch represented as a byte arrray. +const TapBranchNodesLen = 64 + +// TapBranchNodes represents the tapHashes of the child nodes of a TapBranch. +// These tapHashes can be stored to and loaded from the DB. +type TapBranchNodes struct { + left [chainhash.HashSize]byte + right [chainhash.HashSize]byte +} + +// TapTreeNodesFromBranch creates a TapscriptTreeNodes object from a TapBranch. +func TapTreeNodesFromBranch(branch txscript.TapBranch) TapscriptTreeNodes { + return TapscriptTreeNodes{ + branch: &TapBranchNodes{ + left: branch.Left().TapHash(), + right: branch.Right().TapHash(), + }, + } +} + +// ToBranch returns an encoded TapBranchNodes object. +func ToBranch(b TapBranchNodes) [][]byte { + return EncodeTapBranchNodes(b) +} + +// BranchNodesRootHash returns the root hash of a Tapscript tree built from the +// tapHashes stored in a TapBranchNodes object. +func BranchNodesRootHash(b TapBranchNodes) chainhash.Hash { + return NewTapBranchHash(b.left, b.right) +} + +// NewTapBranchHash takes the raw tap hashes of the left and right nodes and +// hashes them into a branch. +func NewTapBranchHash(l, r chainhash.Hash) chainhash.Hash { + if bytes.Compare(l[:], r[:]) > 0 { + l, r = r, l + } + + return *chainhash.TaggedHash(chainhash.TagTapBranch, l[:], r[:]) +} + // AssetGroup holds information about an asset group, including the genesis // information needed re-tweak the raw key. type AssetGroup struct { diff --git a/asset/asset_test.go b/asset/asset_test.go index b413fd2b9..74bc145cc 100644 --- a/asset/asset_test.go +++ b/asset/asset_test.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "testing" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/txscript" @@ -31,8 +32,10 @@ var ( "821525f66a4a85ea8b71e482a74f382d2ce5ebeee8fdb2172f47" + "7df4900d310536c0", ) - sig, _ = schnorr.ParseSignature(sigBytes) - sigWitness = wire.TxWitness{sig.Serialize()} + sig, _ = schnorr.ParseSignature(sigBytes) + sigWitness = wire.TxWitness{sig.Serialize()} + unsupportedTapLeafVersion = txscript.TapscriptLeafVersion(0xf0) + testTapLeafScript = []byte{99, 88, 77, 66, 55, 44} generatedTestVectorName = "asset_tlv_encoding_generated.json" @@ -498,6 +501,170 @@ func TestAssetEncoding(t *testing.T) { test.WriteTestVectors(t, generatedTestVectorName, testVectors) } +// TestTapLeafEncoding asserts that we can properly encode and decode tapLeafs +// through their TLV serialization, and that invalid tapLeafs are rejected. +func TestTapLeafEncoding(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + leaf *txscript.TapLeaf + valid bool + }{ + { + name: "nil leaf", + leaf: nil, + valid: false, + }, + { + name: "empty script", + leaf: fn.Ptr(txscript.NewBaseTapLeaf([]byte{})), + valid: false, + }, + { + name: "large leaf script", + leaf: fn.Ptr(txscript.NewBaseTapLeaf( + test.RandBytes(blockchain.MaxBlockWeight), + )), + valid: true, + }, + { + name: "random script with unknown version", + leaf: fn.Ptr(txscript.NewTapLeaf( + unsupportedTapLeafVersion, testTapLeafScript, + )), + valid: true, + }, + } + + for _, testCase := range tests { + tc := testCase + + t.Run(tc.name, func(t *testing.T) { + leafBytes, err := EncodeTapLeaf(tc.leaf) + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + return + } + + leaf, err := DecodeTapLeaf(leafBytes) + require.NoError(t, err) + + require.Equal(t, tc.leaf.LeafVersion, leaf.LeafVersion) + require.Equal(t, tc.leaf.Script, leaf.Script) + }) + } +} + +// TestTapBranchEncodings asserts that we can properly encode and decode +// tapBranches, and that invalid slices of byte slices are rejected. +func TestTapBranchEncoding(t *testing.T) { + tests := []struct { + name string + branchData [][]byte + valid bool + }{ + { + name: "empty branch", + branchData: [][]byte{}, + valid: false, + }, + { + name: "branch with invalid child", + branchData: [][]byte{ + pubKeyBytes, + hashBytes2[:], + }, + valid: false, + }, + { + name: "valid branch", + branchData: [][]byte{ + hashBytes1[:], + hashBytes2[:], + }, + valid: true, + }, + } + + for _, testCase := range tests { + tc := testCase + + t.Run(tc.name, func(t *testing.T) { + branch, err := DecodeTapBranchNodes(tc.branchData) + + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + return + } + + branchBytes := EncodeTapBranchNodes(*branch) + require.Equal(t, tc.branchData, branchBytes) + }) + } +} + +// TestTapLeafSanity assserts that we reject tapLeafs that fail our sanity +// checks, and accept valid tapLeafs. +func TestTapLeafSanity(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + leaf *txscript.TapLeaf + sane bool + }{ + { + name: "nil leaf", + leaf: nil, + sane: false, + }, + { + name: "unsupported version", + leaf: fn.Ptr(txscript.NewTapLeaf( + unsupportedTapLeafVersion, testTapLeafScript, + )), + sane: false, + }, + { + name: "empty script", + leaf: fn.Ptr(txscript.NewBaseTapLeaf([]byte{})), + sane: false, + }, + { + name: "large leaf script", + leaf: fn.Ptr(txscript.NewBaseTapLeaf( + test.RandBytes(blockchain.MaxBlockWeight), + )), + sane: false, + }, + { + name: "valid tapleaf", + leaf: fn.Ptr( + txscript.NewBaseTapLeaf(testTapLeafScript), + ), + sane: true, + }, + } + + for _, testCase := range tests { + tc := testCase + + t.Run(tc.name, func(t *testing.T) { + err := CheckTapLeafSanity(tc.leaf) + if tc.sane { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + // TestAssetIsBurn asserts that the IsBurn method is correct. func TestAssetIsBurn(t *testing.T) { root := testRootAsset.Copy() diff --git a/asset/encoding.go b/asset/encoding.go index 9e31468c6..281f8bce4 100644 --- a/asset/encoding.go +++ b/asset/encoding.go @@ -11,11 +11,19 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/mssmt" "github.com/lightningnetwork/lnd/tlv" ) +// TLV types for TapLeaf encode/decode. +const ( + typeTapLeafVersion tlv.Type = 1 + typeTapLeafScript tlv.Type = 2 +) + var ( // ErrTooManyInputs is returned when an asset TLV atempts to reference // too many inputs. @@ -664,3 +672,130 @@ func LeafDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error { } return tlv.NewTypeForEncodingErr(val, "Asset") } + +func EncodeTapBranchNodes(branch TapBranchNodes) [][]byte { + return [][]byte{ + bytes.Clone(branch.left[:]), bytes.Clone(branch.right[:]), + } +} + +func DecodeTapBranchNodes(branchData [][]byte) (*TapBranchNodes, error) { + if len(branchData) != 2 { + return nil, ErrInvalidTapBranch + } + + left, right := branchData[0], branchData[1] + + // Given data must be 32 bytes long to be a valid TapHash. + if len(left) != chainhash.HashSize || len(right) != chainhash.HashSize { + return nil, fmt.Errorf("invalid tapbranch taphash length") + } + + var leftHash, rightHash [chainhash.HashSize]byte + copy(leftHash[:], left) + copy(rightHash[:], right) + + return &TapBranchNodes{ + left: leftHash, + right: rightHash, + }, nil +} + +func EncodeTapLeafNodes(leaves TapLeafNodes) ([][]byte, error) { + innerLeaves := ToLeaves(leaves) + + return fn.MapErr(innerLeaves, func(l txscript.TapLeaf) ([]byte, error) { + return EncodeTapLeaf(&l) + }) +} + +func DecodeTapLeafNodes(leafData [][]byte) (*TapLeafNodes, error) { + if len(leafData) == 0 { + return nil, fmt.Errorf("no tapleaves provided") + } + + orderedLeaves := make([]txscript.TapLeaf, len(leafData)) + for i, leafBytes := range leafData { + leaf, err := DecodeTapLeaf(leafBytes) + if err != nil { + return nil, err + } + + orderedLeaves[i] = *leaf + } + + // The tapLeaf decoder is less strict than the TapLeafNodes type. Check + // that all leaves meet the restrictions for TapLeafNodes. + err := CheckTapLeavesSanity(orderedLeaves) + if err != nil { + return nil, err + } + + return &TapLeafNodes{ + v: orderedLeaves, + }, nil +} + +// The following TapLeaf {en,de}coders are a duplicate of those used in +// btcwallet. Specifically, the inner loop logic for handling []TapLeaf objects. +// https://github.com/btcsuite/btcwallet/blob/master/waddrmgr/tlv.go#L160 +// The {en,de}coders here omit the extra size prefix for the leaf TLV used in +// btcwallet. This duplication is needed until we update btcwallet to export +// these methods. + +// EncodeTapLeaf encodes a TapLeaf into a byte slice containing a TapLeaf TLV +// record, prefixed with a varint indicating the length of the record. +func EncodeTapLeaf(leaf *txscript.TapLeaf) ([]byte, error) { + if leaf == nil { + return nil, fmt.Errorf("cannot encode nil tapleaf") + } + if len(leaf.Script) == 0 { + return nil, fmt.Errorf("tapleaf script is empty") + } + + leafVersion := uint8(leaf.LeafVersion) + tlvStream, err := tlv.NewStream( + tlv.MakePrimitiveRecord(typeTapLeafVersion, &leafVersion), + tlv.MakePrimitiveRecord(typeTapLeafScript, &leaf.Script), + ) + if err != nil { + return nil, err + } + + var leafTLVBytes bytes.Buffer + err = tlvStream.Encode(&leafTLVBytes) + if err != nil { + return nil, err + } + + return leafTLVBytes.Bytes(), nil +} + +// DecodeTapLeaf decodes a byte slice containing a TapLeaf TLV record prefixed +// with a varint indicating the length of the record. +func DecodeTapLeaf(leafData []byte) (*txscript.TapLeaf, error) { + var ( + leafVersion uint8 + script []byte + ) + + tlvStream, err := tlv.NewStream( + tlv.MakePrimitiveRecord(typeTapLeafVersion, &leafVersion), + tlv.MakePrimitiveRecord(typeTapLeafScript, &script), + ) + if err != nil { + return nil, err + } + + err = tlvStream.Decode(bytes.NewReader(leafData)) + if err != nil { + return nil, err + } + + leaf := txscript.TapLeaf{ + LeafVersion: txscript.TapscriptLeafVersion(leafVersion), + Script: script, + } + + return &leaf, nil +} diff --git a/asset/interface.go b/asset/interface.go index acd8d5465..6ebb8c811 100644 --- a/asset/interface.go +++ b/asset/interface.go @@ -1,7 +1,11 @@ package asset import ( + "context" + "errors" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/lndclient" ) @@ -24,3 +28,36 @@ type GenesisTxBuilder interface { // output is used to create a group witness for the grouped asset. BuildGenesisTx(newAsset *Asset) (*wire.MsgTx, *wire.TxOut, error) } + +// TapscriptTreeManager is used to persist a Tapscript tree, represented as +// either a slice of TapLeafs or a TapBranch. After a tree is stored, it can be +// referenced by its root hash. This root hash can be stored as a tweak for keys +// such as a batch internal key, group key, or asset script key. +type TapscriptTreeManager interface { + // StoreTapscriptTree persists a Tapscript tree given a validated set of + // TapLeafs or a TapBranch. If the store succeeds, the root hash of the + // Tapscript tree is returned. + StoreTapscriptTree(ctx context.Context, + treeNodes TapscriptTreeNodes) (*chainhash.Hash, error) + + // LoadTapscriptTree loads the Tapscript tree with the given root hash, + // and decodes the tree into a TapscriptTreeNodes object. + LoadTapscriptTree(ctx context.Context, + rootHash chainhash.Hash) (*TapscriptTreeNodes, error) + + // DeleteTapscriptTree deletes the Tapscript tree with the given root + // hash. + DeleteTapscriptTree(ctx context.Context, rootHash chainhash.Hash) error +} + +var ( + // ErrTreeNotFound is returned when a TapscriptTreeManager attempts to + // load a Tapscript tree, but found no tree nodes. + ErrTreeNotFound = errors.New("tapscript tree not found") + + // ErrInvalidTapBranch is returned when decoding a slice of byte slices + // to a TapBranch, and there are not exactly two slices. + ErrInvalidTapBranch = errors.New( + "tapscript tree branch must be 2 nodes", + ) +) diff --git a/asset/mock.go b/asset/mock.go index 185b48dfe..7630335ae 100644 --- a/asset/mock.go +++ b/asset/mock.go @@ -380,11 +380,12 @@ func AssetCustomGroupKey(t *testing.T, useHashLock, BIP86, keySpend, // Derive a tapscipt root using the default tapscript tree used for // testing, but use a signature as a witness. case keySpend: - tapRootHash := test.BuildTapscriptTreeNoReveal( + treeRootChildren := test.BuildTapscriptTreeNoReveal( t, groupSinglyTweakedKey, ) + treeTapHash := treeRootChildren.TapHash() - groupReq.TapscriptRoot = tapRootHash + groupReq.TapscriptRoot = treeTapHash[:] groupKey, err = DeriveCustomGroupKey( genSigner, &genBuilder, groupReq, nil, nil, ) diff --git a/commitment/encoding.go b/commitment/encoding.go index 6e9fbae64..630c0becf 100644 --- a/commitment/encoding.go +++ b/commitment/encoding.go @@ -141,12 +141,12 @@ func TapscriptPreimageEncoder(w io.Writer, val any, buf *[8]byte) error { if t, ok := val.(**TapscriptPreimage); ok { // We'll encode the pre-image as 1 byte for the type of the // pre-image, and then the pre-image itself. - siblingType := uint8((*t).SiblingType) + siblingType := uint8((*t).siblingType) if err := tlv.EUint8(w, &siblingType, buf); err != nil { return err } - return tlv.EVarBytes(w, &(*t).SiblingPreimage, buf) + return tlv.EVarBytes(w, &(*t).siblingPreimage, buf) } return tlv.NewTypeForEncodingErr(val, "*TapscriptPreimage") @@ -175,10 +175,10 @@ func TapscriptPreimageDecoder(r io.Reader, val any, buf *[8]byte, return err } - preimage.SiblingType = TapscriptPreimageType(siblingType) + preimage.siblingType = TapscriptPreimageType(siblingType) // Now we'll read out the pre-image itself. - err = tlv.DVarBytes(r, &preimage.SiblingPreimage, buf, l-1) + err = tlv.DVarBytes(r, &preimage.siblingPreimage, buf, l-1) if err != nil { return err } diff --git a/commitment/tap.go b/commitment/tap.go index ffdca10e8..69672ce14 100644 --- a/commitment/tap.go +++ b/commitment/tap.go @@ -313,9 +313,6 @@ func IsTaprootAssetCommitmentScript(script []byte) bool { if len(script) != TaprootAssetCommitmentScriptSize { return false } - if script[0] != byte(asset.V0) { - return false - } return bytes.Equal( script[1:1+len(TaprootAssetsMarker)], TaprootAssetsMarker[:], diff --git a/commitment/taproot.go b/commitment/taproot.go index 3de0cbbac..1c4f0014e 100644 --- a/commitment/taproot.go +++ b/commitment/taproot.go @@ -8,6 +8,8 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" ) const ( @@ -37,6 +39,12 @@ var ( "invalid tapscript preimage length", ) + // ErrInvalidTapscriptPreimageType is an error returned when a tapscript + // preimage has an unknown type that is not leaf nor branch. + ErrInvalidTapscriptPreimageType = errors.New( + "unknown tapscript preimage type", + ) + // ErrPreimageIsTapCommitment is an error returned when a tapscript // preimage is a valid Taproot Asset commitment. ErrPreimageIsTapCommitment = errors.New( @@ -66,51 +74,152 @@ func (t TapscriptPreimageType) String() string { return "BranchPreimage" default: - return fmt.Sprintf("UnKnownSiblingType(%d)", t) + return fmt.Sprintf("UnknownSiblingType(%d)", t) } } // TapscriptPreimage wraps a pre-image byte slice with a type byte that self // identifies what type of pre-image it is. type TapscriptPreimage struct { - // SiblingPreimage is the pre-image itself. This will be either 32 or - // 64 bytes. - SiblingPreimage []byte + // SiblingPreimage is the pre-image itself. This will be 64 bytes if + // representing a TapBranch, or any size under 4 MBytes if representing + // a TapLeaf. + siblingPreimage []byte // SiblingType is the type of the pre-image. - SiblingType TapscriptPreimageType + siblingType TapscriptPreimageType } // NewPreimageFromLeaf creates a new TapscriptPreimage from a single tap leaf. -func NewPreimageFromLeaf(leaf txscript.TapLeaf) *TapscriptPreimage { - // The leaf encoding is: leafVersion || compactSizeof(script) || - // script, where compactSizeof returns the compact size needed to - // encode the value. +func NewPreimageFromLeaf(leaf txscript.TapLeaf) (*TapscriptPreimage, error) { + // Check the leaf size and version, and assert that the leaf script is + // not a Taproot Asset Commitment. + err := asset.CheckTapLeafSanity(&leaf) + if err != nil { + return nil, err + } + + if IsTaprootAssetCommitmentScript(leaf.Script) { + return nil, ErrPreimageIsTapCommitment + } + + // The leaf encoding is: leafVersion || compactSizeof(script) || script, + // where compactSizeof returns the compact size needed to encode the + // value. var encodedLeaf bytes.Buffer _ = encodedLeaf.WriteByte(byte(leaf.LeafVersion)) _ = wire.WriteVarBytes(&encodedLeaf, 0, leaf.Script) return &TapscriptPreimage{ - SiblingPreimage: encodedLeaf.Bytes(), - SiblingType: LeafPreimage, + siblingPreimage: encodedLeaf.Bytes(), + siblingType: LeafPreimage, + }, nil +} + +// NewLeafFromPreimage sanity checks a TapscriptPreimage and decodes it into a +// TapLeaf. +func NewLeafFromPreimage(preimage TapscriptPreimage) (*txscript.TapLeaf, + error) { + + // The preimage must be a TapLeaf. + if preimage.Type() != LeafPreimage { + return nil, ErrInvalidTapscriptPreimageType + } + + // Remove the leaf version and script size prefix from the preimage. + // The prefix is at least 2 bytes long, and if it's missing then this + // preimage was not created correctly. + if len(preimage.siblingPreimage) < 2 { + return nil, ErrInvalidTapscriptPreimageLen + } + + // The script is encoded with a leading VarByte that indicates its total + // length. + version := txscript.TapscriptLeafVersion(preimage.siblingPreimage[0]) + remaining := preimage.siblingPreimage[1:] + script, err := wire.ReadVarBytes( + bytes.NewReader(remaining), 0, uint32(len(remaining)), "script", + ) + if err != nil { + return nil, fmt.Errorf("error decoding leaf pre-image: %w", err) } + + // The script must not be a Taproot Asset Commitment. + if IsTaprootAssetCommitmentScript(script) { + return nil, ErrPreimageIsTapCommitment + } + + return fn.Ptr(txscript.NewTapLeaf(version, script)), nil } // NewPreimageFromBranch creates a new TapscriptPreimage from a tap branch. -func NewPreimageFromBranch(branch txscript.TapBranch) *TapscriptPreimage { +func NewPreimageFromBranch(branch txscript.TapBranch) TapscriptPreimage { + leftHash := branch.Left().TapHash() + rightHash := branch.Right().TapHash() + branchBytes := bytes.Join([][]byte{leftHash[:], rightHash[:]}, nil) + + return TapscriptPreimage{ + siblingPreimage: branchBytes, + siblingType: BranchPreimage, + } +} + +// TapTreeToSibling constucts a taproot sibling hash from Tapscript tree nodes, +// to be used with a TapCommitment tree root to derive a tapscript root. This +// could be multiple TapLeaf objects, or a representation of a TapBranch. +func NewPreimageFromTapscriptTreeNodes( + tn asset.TapscriptTreeNodes) (*TapscriptPreimage, error) { + var ( - encodedBranch bytes.Buffer - leftHash = branch.Left().TapHash() - rightHash = branch.Right().TapHash() + preimage *TapscriptPreimage + preimageErr error ) - _, _ = encodedBranch.Write(leftHash[:]) - _, _ = encodedBranch.Write(rightHash[:]) - return &TapscriptPreimage{ - SiblingPreimage: encodedBranch.Bytes(), - SiblingType: BranchPreimage, + asset.GetLeaves(tn).WhenSome(func(tln asset.TapLeafNodes) { + leaves := asset.ToLeaves(tln) + + // Check that none of the leaves are a Taproot Asset Commitment. + badLeaves := fn.Any(leaves, func(leaf txscript.TapLeaf) bool { + return IsTaprootAssetCommitmentScript(leaf.Script) + }) + if badLeaves { + preimageErr = ErrPreimageIsTapCommitment + return + } + + switch len(leaves) { + case 1: + // If we only have one leaf, our preimage is just the + // encoded leaf. + preimage, preimageErr = NewPreimageFromLeaf(leaves[0]) + + default: + // Make a branch from the leaves. + tree := txscript.AssembleTaprootScriptTree(leaves...) + branch := txscript.NewTapBranch( + tree.RootNode.Left(), tree.RootNode.Right(), + ) + preimage = fn.Ptr(NewPreimageFromBranch(branch)) + } + }) + + asset.GetBranch(tn).WhenSome(func(tbn asset.TapBranchNodes) { + branch := asset.ToBranch(tbn) + preimage = &TapscriptPreimage{ + siblingPreimage: bytes.Join(branch, nil), + siblingType: BranchPreimage, + } + }) + + if preimageErr != nil { + return nil, preimageErr } + if preimage == nil { + return nil, fmt.Errorf("malformed tapscript tree nodes") + } + + return preimage, nil } // IsEmpty returns true if the sibling pre-image is empty. @@ -119,7 +228,12 @@ func (t *TapscriptPreimage) IsEmpty() bool { return true } - return len(t.SiblingPreimage) == 0 + return len(t.siblingPreimage) == 0 +} + +// Type returns the preimage type. +func (t *TapscriptPreimage) Type() TapscriptPreimageType { + return TapscriptPreimageType(t.siblingType) } // TapHash returns the tap hash of this preimage according to its type. @@ -128,33 +242,35 @@ func (t *TapscriptPreimage) TapHash() (*chainhash.Hash, error) { return nil, ErrInvalidEmptyTapscriptPreimage } - switch t.SiblingType { - // The sibling is actually a leaf pre-image, so we'll verify that it - // isn't a Taproot Asset commitment, and then hash it with the - // commitment to obtain our root. + switch t.siblingType { + // The sibling is a leaf pre-image, so we'll verify that it isn't a + // Taproot Asset commitment, and then compute its TapHash. case LeafPreimage: - return TapLeafHash(t.SiblingPreimage) + leaf, err := NewLeafFromPreimage(*t) + if err != nil { + return nil, err + } - // The sibling is actually a branch pre-image, so we'll verify that the - // branch pre-image is 64-bytes (the two 32-byte hashes of the left - // and right nodes), and then derive the key from that. + return fn.Ptr(leaf.TapHash()), nil + + // The sibling is a branch pre-image, so we'll verify that the pre-image + // is 64-bytes (the two 32-byte hashes of the left and right nodes), + // and then derive the TapHash from that. case BranchPreimage: - return TapBranchHash(t.SiblingPreimage) + if len(t.siblingPreimage) != tapBranchPreimageLen { + return nil, ErrInvalidTapscriptPreimageLen + } - default: - return nil, fmt.Errorf("unknown sibling type: <%d>", - t.SiblingType) - } -} + var left, right chainhash.Hash + left = (chainhash.Hash)(t.siblingPreimage[:chainhash.HashSize]) + right = (chainhash.Hash)(t.siblingPreimage[chainhash.HashSize:]) -// VerifyNoCommitment verifies that the preimage is not a Taproot Asset -// commitment. -func (t *TapscriptPreimage) VerifyNoCommitment() error { - if IsTaprootAssetCommitmentScript(t.SiblingPreimage) { - return ErrPreimageIsTapCommitment - } + return fn.Ptr(asset.NewTapBranchHash(left, right)), nil - return nil + default: + return nil, fmt.Errorf("%w: %d", + ErrInvalidTapscriptPreimageType, t.siblingType) + } } // MaybeDecodeTapscriptPreimage returns the decoded preimage and hash of the @@ -179,6 +295,8 @@ func MaybeDecodeTapscriptPreimage(encoded []byte) (*TapscriptPreimage, "preimage: %w", err) } + // Validate the correctness of the decoded preimage and compute its + // TapHash. tapHash, err := preimage.TapHash() if err != nil { return nil, nil, fmt.Errorf("error deriving tap hash "+ @@ -214,57 +332,3 @@ func MaybeEncodeTapscriptPreimage(t *TapscriptPreimage) ([]byte, return b.Bytes(), tapHash, nil } - -// NewTapBranchHash takes the raw tap hashes of the left and right nodes and -// hashes them into a branch. -func NewTapBranchHash(l, r chainhash.Hash) chainhash.Hash { - if bytes.Compare(l[:], r[:]) > 0 { - l, r = r, l - } - - return *chainhash.TaggedHash(chainhash.TagTapBranch, l[:], r[:]) -} - -// TapBranchHash computes the TapHash of a TapBranch node from its preimage -// if possible, otherwise an error is returned. -func TapBranchHash(preimage []byte) (*chainhash.Hash, error) { - // Empty preimage or leaf preimage, return typed error. - if len(preimage) != tapBranchPreimageLen { - return nil, ErrInvalidTapscriptPreimageLen - } - - left := (*chainhash.Hash)(preimage[:chainhash.HashSize]) - right := (*chainhash.Hash)(preimage[chainhash.HashSize:]) - h := NewTapBranchHash(*left, *right) - - return &h, nil -} - -// TapLeafHash computes the TapHash of a TapLeaf node from its preimage -// if possible, otherwise an error is returned. -func TapLeafHash(preimage []byte) (*chainhash.Hash, error) { - // Empty preimage. - if len(preimage) == 0 { - return nil, ErrInvalidEmptyTapscriptPreimage - } - - // Enforce that it is not including another Taproot Asset commitment. - if bytes.Contains(preimage, TaprootAssetsMarker[:]) { - return nil, ErrInvalidTaprootProof - } - - version := txscript.TapscriptLeafVersion(preimage[0]) - - // The script is encoded with a leading VarByte that indicates its total - // length. - remaining := preimage[1:] - script, err := wire.ReadVarBytes( - bytes.NewReader(remaining), 0, uint32(len(remaining)), "script", - ) - if err != nil { - return nil, fmt.Errorf("error decoding leaf pre-image: %w", err) - } - - h := txscript.NewTapLeaf(version, script).TapHash() - return &h, nil -} diff --git a/commitment/taproot_test.go b/commitment/taproot_test.go index 9f2e4dfb5..88d3e52ff 100644 --- a/commitment/taproot_test.go +++ b/commitment/taproot_test.go @@ -1,11 +1,16 @@ package commitment import ( + "bytes" + "encoding/binary" "encoding/hex" "testing" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/internal/test" "github.com/stretchr/testify/require" ) @@ -31,6 +36,20 @@ func TestTapscriptPreimage(t *testing.T) { branch := txscript.NewTapBranch(leaf1, leaf2) branchHash := branch.TapHash() + // Create a random byte slice with the same structure as a Taproot + // Asset commitment root, that can be used in a TapLeaf. + randTapCommitmentRoot := func(version asset.Version) []byte { + var dummyRootSum [8]byte + binary.BigEndian.PutUint64( + dummyRootSum[:], test.RandInt[uint64](), + ) + dummyRootHashParts := [][]byte{ + {byte(version)}, TaprootAssetsMarker[:], + fn.ByteSlice(test.RandHash()), dummyRootSum[:], + } + return bytes.Join(dummyRootHashParts, nil) + } + testCases := []struct { name string makePreimage func(t *testing.T) *TapscriptPreimage @@ -43,11 +62,11 @@ func TestTapscriptPreimage(t *testing.T) { name: "invalid type", makePreimage: func(t *testing.T) *TapscriptPreimage { return &TapscriptPreimage{ - SiblingType: 123, + siblingType: 123, } }, expectedType: 123, - expectedName: "UnKnownSiblingType(123)", + expectedName: "UnknownSiblingType(123)", expectedEmpty: true, expectedHashErr: ErrInvalidEmptyTapscriptPreimage.Error(), }, { @@ -63,29 +82,64 @@ func TestTapscriptPreimage(t *testing.T) { name: "empty branch pre-image", makePreimage: func(t *testing.T) *TapscriptPreimage { return &TapscriptPreimage{ - SiblingType: BranchPreimage, + siblingType: BranchPreimage, } }, expectedType: BranchPreimage, expectedName: "BranchPreimage", expectedEmpty: true, expectedHashErr: ErrInvalidEmptyTapscriptPreimage.Error(), + }, { + name: "invalid size leaf pre-image", + makePreimage: func(t *testing.T) *TapscriptPreimage { + return &TapscriptPreimage{ + siblingPreimage: []byte("b"), + } + }, + expectedType: LeafPreimage, + expectedName: "LeafPreimage", + expectedEmpty: false, + expectedHashErr: ErrInvalidTapscriptPreimageLen.Error(), }, { name: "invalid size branch pre-image", makePreimage: func(t *testing.T) *TapscriptPreimage { return &TapscriptPreimage{ - SiblingType: BranchPreimage, - SiblingPreimage: []byte("too short"), + siblingType: BranchPreimage, + siblingPreimage: []byte("too short"), } }, expectedType: BranchPreimage, expectedName: "BranchPreimage", expectedEmpty: false, expectedHashErr: ErrInvalidTapscriptPreimageLen.Error(), + }, { + name: "tap commitment leaf pre-image", + makePreimage: func(t *testing.T) *TapscriptPreimage { + tapCommitmentRoot := randTapCommitmentRoot(asset.V0) + var encodedLeaf bytes.Buffer + + _ = encodedLeaf.WriteByte( + byte(txscript.BaseLeafVersion), + ) + _ = wire.WriteVarBytes( + &encodedLeaf, 0, tapCommitmentRoot, + ) + + return &TapscriptPreimage{ + siblingType: LeafPreimage, + siblingPreimage: encodedLeaf.Bytes(), + } + }, + expectedType: LeafPreimage, + expectedName: "LeafPreimage", + expectedEmpty: false, + expectedHashErr: ErrPreimageIsTapCommitment.Error(), }, { name: "valid leaf pre-image", makePreimage: func(t *testing.T) *TapscriptPreimage { - return NewPreimageFromLeaf(leaf1) + preimage, err := NewPreimageFromLeaf(leaf1) + require.NoError(t, err) + return preimage }, expectedType: LeafPreimage, expectedName: "LeafPreimage", @@ -94,7 +148,7 @@ func TestTapscriptPreimage(t *testing.T) { }, { name: "valid branch pre-image", makePreimage: func(t *testing.T) *TapscriptPreimage { - return NewPreimageFromBranch(branch) + return fn.Ptr(NewPreimageFromBranch(branch)) }, expectedType: BranchPreimage, expectedName: "BranchPreimage", @@ -108,10 +162,10 @@ func TestTapscriptPreimage(t *testing.T) { t.Run(tc.name, func(tt *testing.T) { preimage := tc.makePreimage(tt) - require.Equal(tt, tc.expectedType, preimage.SiblingType) + require.Equal(tt, tc.expectedType, preimage.siblingType) require.Equal( tt, tc.expectedName, - preimage.SiblingType.String(), + preimage.siblingType.String(), ) require.Equal(tt, tc.expectedEmpty, preimage.IsEmpty()) @@ -125,10 +179,6 @@ func TestTapscriptPreimage(t *testing.T) { require.NoError(tt, err) require.Equal(tt, tc.expectedHash, hash) - - // Make sure that the preimage is detected as not being - // a Taproot Asset commitment. - require.NoError(tt, preimage.VerifyNoCommitment()) }) } } @@ -150,9 +200,9 @@ func TestMaybeEncodeTapscriptPreimage(t *testing.T) { preImage := []byte("hash locks are cool") siblingLeaf := test.ScriptHashLock(t, preImage) - encodedPreimage, _, err := MaybeEncodeTapscriptPreimage( - NewPreimageFromLeaf(siblingLeaf), - ) + leafPreimage, err := NewPreimageFromLeaf(siblingLeaf) + require.NoError(t, err) + encodedPreimage, _, err := MaybeEncodeTapscriptPreimage(leafPreimage) require.NoError(t, err) require.Equal( diff --git a/fn/either.go b/fn/either.go index 6bd6bd309..5be440a3d 100644 --- a/fn/either.go +++ b/fn/either.go @@ -35,3 +35,25 @@ func (e Either[L, R]) IsLeft() bool { func (e Either[L, R]) IsRight() bool { return e.right.IsSome() } + +// MapLeft maps the left value of the Either to a new value. +func MapLeft[L any, R any, O any](f func(L) O) func(Either[L, R]) Option[O] { + return func(e Either[L, R]) Option[O] { + if e.IsLeft() { + return MapOption(f)(e.left) + } + + return None[O]() + } +} + +// MapRight maps the right value of the Either to a new value. +func MapRight[L any, R any, O any](f func(R) O) func(Either[L, R]) Option[O] { + return func(e Either[L, R]) Option[O] { + if e.IsRight() { + return MapOption(f)(e.right) + } + + return None[O]() + } +} diff --git a/fn/option.go b/fn/option.go index 741ef241f..7aaa9894b 100644 --- a/fn/option.go +++ b/fn/option.go @@ -17,13 +17,24 @@ func Some[A any](a A) Option[A] { } } -// None trivially constructs an empty option +// None trivially constructs an empty option. // // None : Option[A]. func None[A any]() Option[A] { return Option[A]{} } +// MaybeSome constructs an option from a pointer. +// +// MaybeSome : *A -> Option[A]. +func MaybeSome[A any](a *A) Option[A] { + if a == nil { + return None[A]() + } + + return Some[A](*a) +} + // ElimOption is the universal Option eliminator. It can be used to safely // handle all possible values inside the Option by supplying two continuations. // @@ -48,6 +59,33 @@ func (o Option[A]) UnwrapOr(a A) A { return a } +// UnwrapPtr is used to extract a reference to a value from an option, and we +// supply an empty pointer in the case when the Option is empty. +func (o Option[A]) UnwrapToPtr() *A { + var v *A + o.WhenSome(func(a A) { + v = &a + }) + + return v +} + +// UnwrapOrFunc is used to extract a value from an option, and we supply a +// thunk to be evaluated in the case when the Option is empty. +func (o Option[A]) UnwrapOrFunc(f func() A) A { + return ElimOption(o, f, func(a A) A { return a }) +} + +// UnwrapOrFuncErr is used to extract a value from an option, and we supply a +// thunk to be evaluated in the case when the Option is empty. +func (o Option[A]) UnwrapOrFuncErr(f func() (A, error)) (A, error) { + if o.isSome { + return o.some, nil + } + + return f() +} + // WhenSome is used to conditionally perform a side-effecting function that // accepts a value of the type that parameterizes the option. If this function // performs no side effects, WhenSome is useless. @@ -117,6 +155,19 @@ func MapOption[A, B any](f func(A) B) func(Option[A]) Option[B] { } } +// MapOptionZ transforms a pure function A -> B into one that will operate +// inside the Option context. Unlike MapOption, this function will return the +// default/zero argument of the return type if the Option is empty. +func MapOptionZ[A, B any](o Option[A], f func(A) B) B { + var zero B + + if o.IsNone() { + return zero + } + + return f(o.some) +} + // LiftA2Option transforms a pure function (A, B) -> C into one that will // operate in an Option context. For the returned function, if either of its // arguments are None, then the result will be None. diff --git a/internal/test/helpers.go b/internal/test/helpers.go index 6db79f95b..b7a4fcb07 100644 --- a/internal/test/helpers.go +++ b/internal/test/helpers.go @@ -358,6 +358,22 @@ func RandTxWitnesses(t testing.TB) wire.TxWitness { return w } +func RandTapLeaf(customScriptLen *int) txscript.TapLeaf { + scriptLen := 500 + + // Ensure that we never have an empty script. + randScriptLen := RandIntn(scriptLen) + if randScriptLen == 0 { + randScriptLen = 1 + } + + if customScriptLen != nil { + randScriptLen = *customScriptLen + } + + return txscript.NewBaseTapLeaf(RandBytes(randScriptLen)) +} + // ScriptHashLock returns a simple bitcoin script that locks the funds to a hash // lock of the given preimage. func ScriptHashLock(t *testing.T, preimage []byte) txscript.TapLeaf { @@ -393,19 +409,18 @@ func ReadTestDataFile(t *testing.T, fileName string) string { } // BuildTapscriptTree builds a Tapscript tree with two leaves, a hash lock -// script and a signature verification script. It returns only the tapscript -// tree root. +// script and a signature verification script. func BuildTapscriptTreeNoReveal(t *testing.T, - internalKey *btcec.PublicKey) []byte { + internalKey *btcec.PublicKey) txscript.TapBranch { hashLockWitness := []byte("foobar") hashLockLeaf := ScriptHashLock(t, hashLockWitness) sigLeaf := ScriptSchnorrSig(t, internalKey) tree := txscript.AssembleTaprootScriptTree(hashLockLeaf, sigLeaf) - rootHash := tree.RootNode.TapHash() - - return rootHash[:] + return txscript.NewTapBranch( + tree.RootNode.Left(), tree.RootNode.Right(), + ) } // BuildTapscriptTree builds a Tapscript tree with two leaves, a hash lock diff --git a/itest/assertions.go b/itest/assertions.go index 219c18ab1..9bb51fc81 100644 --- a/itest/assertions.go +++ b/itest/assertions.go @@ -17,6 +17,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/proof" "github.com/lightninglabs/taproot-assets/taprpc" @@ -1562,6 +1563,30 @@ func AssertAssetsMinted(t *testing.T, return assetList } +func AssertGenesisOutput(t *testing.T, output *taprpc.ManagedUtxo, + sibling commitment.TapscriptPreimage) { + + // Fetch the encoded tapscript sibling from an anchored asset, and check + // it against the expected sibling. + require.True(t, len(output.Assets) > 1) + rpcSibling := output.Assets[0].ChainAnchor.TapscriptSibling + require.True(t, fn.All(output.Assets, func(a *taprpc.Asset) bool { + return bytes.Equal(a.ChainAnchor.TapscriptSibling, rpcSibling) + })) + encodedSibling, siblingHash, err := commitment. + MaybeEncodeTapscriptPreimage(&sibling) + require.NoError(t, err) + require.Equal(t, encodedSibling, rpcSibling) + + // We should be able to recompute a merkle root from the tapscript + // sibling hash and the Taproot Asset Commitment root that matches what + // is stored in the managed output. + expectedMerkleRoot := asset.NewTapBranchHash( + (chainhash.Hash)(output.TaprootAssetRoot), *siblingHash, + ) + require.Equal(t, expectedMerkleRoot[:], output.MerkleRoot) +} + func AssertAssetBalances(t *testing.T, client taprpc.TaprootAssetsClient, simpleAssets, issuableAssets []*taprpc.Asset) { diff --git a/itest/assets_test.go b/itest/assets_test.go index 556c1c142..644f328dc 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -1,19 +1,28 @@ package itest import ( + "bytes" "context" "crypto/tls" "net/http" "time" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/internal/test" "github.com/lightninglabs/taproot-assets/proof" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/lightninglabs/taproot-assets/taprpc/tapdevrpc" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "golang.org/x/net/http2" ) @@ -334,3 +343,130 @@ func testMintAssetNameCollisionError(t *harnessTest) { collideAssetName := rpcCollideAsset[0].AssetGenesis.Name require.Equal(t.t, commonAssetName, collideAssetName) } + +// testMintAssetsWithTapscriptSibling tests that a batch of assets can be minted +// with a tapscript sibling, and that the genesis output from that mint can be +// spend via the script path. +func testMintAssetsWithTapscriptSibling(t *harnessTest) { + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + // Build the tapscript tree. + sigLockPrivKey := test.RandPrivKey(t.t) + hashLockPreimage := []byte("foobar") + hashLockLeaf := test.ScriptHashLock(t.t, hashLockPreimage) + sigLeaf := test.ScriptSchnorrSig(t.t, sigLockPrivKey.PubKey()) + siblingTree := txscript.AssembleTaprootScriptTree(hashLockLeaf, sigLeaf) + + siblingBranch := txscript.NewTapBranch( + siblingTree.RootNode.Left(), siblingTree.RootNode.Right(), + ) + siblingPreimage := commitment.NewPreimageFromBranch(siblingBranch) + typedBranch := asset.TapTreeNodesFromBranch(siblingBranch) + rawBranch := fn.MapOptionZ(asset.GetBranch(typedBranch), asset.ToBranch) + require.Len(t.t, rawBranch, 2) + siblingReq := mintrpc.FinalizeBatchRequest_Branch{ + Branch: &taprpc.TapBranch{ + LeftTaphash: rawBranch[0], + RightTaphash: rawBranch[1], + }, + } + + rpcSimpleAssets := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, t.tapd, simpleAssets, + WithSiblingBranch(siblingReq), + ) + rpcIssuableAssets := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, t.tapd, issuableAssets, + ) + + AssertAssetBalances(t.t, t.tapd, rpcSimpleAssets, rpcIssuableAssets) + + // Filter the managed UTXOs to select the genesis UTXO with the + // tapscript sibling. + utxos, err := t.tapd.ListUtxos(ctxt, &taprpc.ListUtxosRequest{}) + require.NoError(t.t, err) + + utxoWithTapSibling := func(utxo *taprpc.ManagedUtxo) bool { + return !bytes.Equal(utxo.TaprootAssetRoot, utxo.MerkleRoot) + } + mintingOutputWithSibling := fn.Filter( + maps.Values(utxos.ManagedUtxos), utxoWithTapSibling, + ) + require.Len(t.t, mintingOutputWithSibling, 1) + genesisWithSibling := mintingOutputWithSibling[0] + + // Verify that all assets anchored in the output with the tapscript + // sibling have the correct sibling preimage. Also verify that the final + // tweak used for the genesis output is derived from the tapscript + // sibling created above and the batch Taproot Asset commitment. + AssertGenesisOutput(t.t, genesisWithSibling, siblingPreimage) + + // Extract the fields needed to construct a script path spend, which + // includes the Taproot Asset commitment root, the final tap tweak, and + // the internal key. + mintTapTweak := genesisWithSibling.MerkleRoot + mintTapTreeRoot := genesisWithSibling.TaprootAssetRoot + mintInternalKey, err := btcec.ParsePubKey( + genesisWithSibling.InternalKey, + ) + require.NoError(t.t, err) + + mintOutputKey := txscript.ComputeTaprootOutputKey( + mintInternalKey, mintTapTweak, + ) + mintOutputKeyIsOdd := mintOutputKey.SerializeCompressed()[0] == 0x03 + siblingScriptHash := sigLeaf.TapHash() + + // Build the control block and witness. + inclusionProof := bytes.Join( + [][]byte{siblingScriptHash[:], mintTapTreeRoot}, nil, + ) + hashLockControlBlock := txscript.ControlBlock{ + InternalKey: mintInternalKey, + OutputKeyYIsOdd: mintOutputKeyIsOdd, + LeafVersion: txscript.BaseLeafVersion, + InclusionProof: inclusionProof, + } + hashLockControlBlockBytes, err := hashLockControlBlock.ToBytes() + require.NoError(t.t, err) + + hashLockWitness := wire.TxWitness{ + hashLockPreimage, hashLockLeaf.Script, hashLockControlBlockBytes, + } + + // Make a non-tap output from Bob to use in a TX spending Alice's + // genesis UTXO. + burnOutput := MakeOutput( + t, t.lndHarness.Bob, lnrpc.AddressType_TAPROOT_PUBKEY, 500, + ) + + // Construct and publish the TX. + genesisOutpoint, err := wire.NewOutPointFromString( + genesisWithSibling.OutPoint, + ) + require.NoError(t.t, err) + + burnTx := wire.MsgTx{ + Version: 2, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: *genesisOutpoint, + Witness: hashLockWitness, + }}, + TxOut: []*wire.TxOut{burnOutput}, + } + + var burnTxBuf bytes.Buffer + require.NoError(t.t, burnTx.Serialize(&burnTxBuf)) + t.lndHarness.Bob.RPC.PublishTransaction(&walletrpc.Transaction{ + TxHex: burnTxBuf.Bytes(), + }) + + // Bob should detect the TX, and the resulting confirmed UTXO once + // a new block is mined. + t.lndHarness.Miner.AssertNumTxsInMempool(1) + t.lndHarness.AssertNumUTXOsUnconfirmed(t.lndHarness.Bob, 1) + t.lndHarness.MineBlocksAndAssertNumTxes(1, 1) + t.lndHarness.AssertNumUTXOsWithConf(t.lndHarness.Bob, 1, 1, 1) +} diff --git a/itest/psbt_test.go b/itest/psbt_test.go index e0f89ec83..90f298685 100644 --- a/itest/psbt_test.go +++ b/itest/psbt_test.go @@ -744,8 +744,9 @@ func testPsbtInteractiveTapscriptSibling(t *harnessTest) { preImage := []byte("hash locks are cool") siblingLeaf := test.ScriptHashLock(t.t, preImage) - preimage := commitment.NewPreimageFromLeaf(siblingLeaf) - vPkt.Outputs[0].AnchorOutputTapscriptSibling = preimage + siblingPreimage, err := commitment.NewPreimageFromLeaf(siblingLeaf) + require.NoError(t.t, err) + vPkt.Outputs[0].AnchorOutputTapscriptSibling = siblingPreimage // Next, we'll attempt to complete a transfer with PSBTs from alice to // bob, using the partial amount. diff --git a/itest/round_trip_send_test.go b/itest/round_trip_send_test.go index cc199d663..96bfd0183 100644 --- a/itest/round_trip_send_test.go +++ b/itest/round_trip_send_test.go @@ -54,7 +54,8 @@ func testRoundTripSend(t *harnessTest) { hashLockPreimage := []byte("hash locks are cool") scriptLeaf := test.ScriptHashLock(t.t, hashLockPreimage) - sibling := commitment.NewPreimageFromLeaf(scriptLeaf) + sibling, err := commitment.NewPreimageFromLeaf(scriptLeaf) + require.NoError(t.t, err) siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage(sibling) require.NoError(t.t, err) diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index 26859c095..e5bbe67ae 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -17,6 +17,10 @@ var testCases = []*testCase{ name: "asset name collision raises mint error", test: testMintAssetNameCollisionError, }, + { + name: "mint assets with tap sibling", + test: testMintAssetsWithTapscriptSibling, + }, { name: "addresses", test: testAddresses, diff --git a/itest/utils.go b/itest/utils.go index 8cf11cbed..1e2c2e865 100644 --- a/itest/utils.go +++ b/itest/utils.go @@ -9,7 +9,6 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/fn" @@ -130,6 +129,23 @@ type UTXORequest struct { Amount int64 } +// MakeOutput creates a new TXO from a given output type and amount. +func MakeOutput(t *harnessTest, wallet *node.HarnessNode, + addrType lnrpc.AddressType, amount int64) *wire.TxOut { + + addrResp := wallet.RPC.NewAddress(&lnrpc.NewAddressRequest{ + Type: addrType, + }) + addr, err := btcutil.DecodeAddress( + addrResp.Address, harnessNetParams, + ) + require.NoError(t.t, err) + + addrScript := t.lndHarness.PayToAddrScript(addr) + + return wire.NewTxOut(amount, addrScript) +} + // SetNodeUTXOs sets the wallet state for the given node wallet to a set of // UTXOs of a specific type and value. func SetNodeUTXOs(t *harnessTest, wallet *node.HarnessNode, @@ -146,28 +162,9 @@ func SetNodeUTXOs(t *harnessTest, wallet *node.HarnessNode, // Build TXOs from the UTXO requests, which will be used by the miner // to build a TX. - makeOutputs := func(req *UTXORequest) *wire.TxOut { - addrResp := wallet.RPC.NewAddress( - &lnrpc.NewAddressRequest{ - Type: req.Type, - }, - ) - - addr, err := btcutil.DecodeAddress( - addrResp.Address, t.lndHarness.Miner.ActiveNet, - ) - require.NoError(t.t, err) - - addrScript, err := txscript.PayToAddrScript(addr) - require.NoError(t.t, err) - - return &wire.TxOut{ - PkScript: addrScript, - Value: req.Amount, - } - } - - aliceOutputs := fn.Map(reqs, makeOutputs) + aliceOutputs := fn.Map(reqs, func(r *UTXORequest) *wire.TxOut { + return MakeOutput(t, wallet, r.Type, r.Amount) + }) _ = t.lndHarness.Miner.SendOutputsWithoutChange(aliceOutputs, feeRate) t.lndHarness.MineBlocksAndAssertNumTxes(1, 1) @@ -195,7 +192,9 @@ func ResetNodeWallet(t *harnessTest, wallet *node.HarnessNode) { type MintOption func(*MintOptions) type MintOptions struct { - mintingTimeout time.Duration + mintingTimeout time.Duration + siblingBranch *mintrpc.FinalizeBatchRequest_Branch + siblingFullTree *mintrpc.FinalizeBatchRequest_FullTree } func DefaultMintOptions() *MintOptions { @@ -210,6 +209,18 @@ func WithMintingTimeout(timeout time.Duration) MintOption { } } +func WithSiblingBranch(branch mintrpc.FinalizeBatchRequest_Branch) MintOption { + return func(options *MintOptions) { + options.siblingBranch = &branch + } +} + +func WithSiblingTree(tree mintrpc.FinalizeBatchRequest_FullTree) MintOption { + return func(options *MintOptions) { + options.siblingFullTree = &tree + } +} + // MintAssetUnconfirmed is a helper function that mints a batch of assets and // waits until the minting transaction is in the mempool but does not mine a // block. @@ -234,10 +245,17 @@ func MintAssetUnconfirmed(t *testing.T, minerClient *rpcclient.Client, require.Len(t, assetResp.PendingBatch.Assets, idx+1) } + finalizeReq := &mintrpc.FinalizeBatchRequest{} + + if options.siblingBranch != nil { + finalizeReq.BatchSibling = options.siblingBranch + } + if options.siblingFullTree != nil { + finalizeReq.BatchSibling = options.siblingFullTree + } + // Instruct the daemon to finalize the batch. - batchResp, err := tapClient.FinalizeBatch( - ctxt, &mintrpc.FinalizeBatchRequest{}, - ) + batchResp, err := tapClient.FinalizeBatch(ctxt, finalizeReq) require.NoError(t, err) require.NotEmpty(t, batchResp.Batch) require.Len(t, batchResp.Batch.Assets, len(assetRequests)) diff --git a/proof/mint.go b/proof/mint.go index 561aba0a8..881ce506c 100644 --- a/proof/mint.go +++ b/proof/mint.go @@ -193,7 +193,8 @@ type MintingBlobOption func(*mintingBlobOpts) // mintingBlobOpts is a set of options that can be used to modify the final // proof files created. type mintingBlobOpts struct { - metaReveals map[asset.SerializedKey]*MetaReveal + metaReveals map[asset.SerializedKey]*MetaReveal + tapSiblingPreimage *commitment.TapscriptPreimage } // defaultMintingBlobOpts returns the default set of options for creating a @@ -214,6 +215,17 @@ func WithAssetMetaReveals( } } +// WithSiblingPreimage is a MintingBlobOption that allows the caller to provide +// a tapscript sibling preimage to be used when building the initial minting +// blob. +func WithSiblingPreimage( + sibling *commitment.TapscriptPreimage) MintingBlobOption { + + return func(o *mintingBlobOpts) { + o.tapSiblingPreimage = sibling + } +} + // NewMintingBlobs takes a set of minting parameters, and produces a series of // serialized proof files, which proves the creation/existence of each of the // assets within the batch. @@ -304,6 +316,14 @@ func committedProofs(baseProof *Proof, tapTreeRoot *commitment.TapCommitment, // then encode that as a proof file blob in the blobs map. assets := tapTreeRoot.CommittedAssets() proofs := make(AssetProofs, len(assets)) + + // If a sibling preimage was provided for this Tap commitment, we'll + // need to include it with every inclusion proof. + var batchSiblingPreimage *commitment.TapscriptPreimage + if opts.tapSiblingPreimage != nil { + batchSiblingPreimage = opts.tapSiblingPreimage + } + for idx := range assets { // First, we'll copy over the base proof and also set the asset // within the proof itself. @@ -325,11 +345,9 @@ func committedProofs(baseProof *Proof, tapTreeRoot *commitment.TapCommitment, // With the merkle proof obtained, we can now set that in the // main inclusion proof. // - // NOTE: We don't add a TapSiblingPreimage here since we assume - // that this minting output ONLY commits to the Taproot Asset - // commitment. assetProof.InclusionProof.CommitmentProof = &CommitmentProof{ - Proof: *assetMerkleProof, + Proof: *assetMerkleProof, + TapSiblingPreimage: batchSiblingPreimage, } scriptKey := asset.ToSerialized(newAsset.ScriptKey.PubKey) diff --git a/proof/mock.go b/proof/mock.go index 2c51401e1..a45e3bd6b 100644 --- a/proof/mock.go +++ b/proof/mock.go @@ -76,8 +76,10 @@ func RandProof(t testing.TB, genesis asset.Genesis, leaf1 := txscript.NewBaseTapLeaf([]byte{1}) leaf2 := txscript.NewBaseTapLeaf([]byte{2}) - testLeafPreimage := commitment.NewPreimageFromLeaf(leaf1) - testLeafPreimage2 := commitment.NewPreimageFromLeaf(leaf2) + testLeafPreimage, err := commitment.NewPreimageFromLeaf(leaf1) + require.NoError(t, err) + testLeafPreimage2, err := commitment.NewPreimageFromLeaf(leaf2) + require.NoError(t, err) testBranchPreimage := commitment.NewPreimageFromBranch( txscript.NewTapBranch(leaf1, leaf2), ) @@ -112,7 +114,7 @@ func RandProof(t testing.TB, genesis asset.Genesis, InternalKey: test.RandPubKey(t), CommitmentProof: nil, TapscriptProof: &TapscriptProof{ - TapPreimage1: testBranchPreimage, + TapPreimage1: &testBranchPreimage, TapPreimage2: testLeafPreimage2, Bip86: true, }, diff --git a/proof/proof_test.go b/proof/proof_test.go index 1ea959e4b..ca3703cb3 100644 --- a/proof/proof_test.go +++ b/proof/proof_test.go @@ -371,9 +371,12 @@ func TestGenesisProofVerification(t *testing.T) { scriptInternalKey := test.RandPrivKey(t).PubKey() leaf1 := test.ScriptHashLock(t, []byte("foobar")) leaf2 := test.ScriptSchnorrSig(t, scriptInternalKey) + testLeafPreimage, err := commitment.NewPreimageFromLeaf(leaf1) + require.NoError(t, err) // The order doesn't matter here as they are sorted before hashing. branch := txscript.NewTapBranch(leaf1, leaf2) + testBranchPreimage := commitment.NewPreimageFromBranch(branch) amount := uint64(5000) testCases := []struct { @@ -402,20 +405,16 @@ func TestGenesisProofVerification(t *testing.T) { assetVersion: asset.V1, }, { - name: "collectible with leaf preimage", - assetType: asset.Collectible, - tapscriptPreimage: commitment.NewPreimageFromLeaf( - leaf1, - ), - noMetaHash: true, + name: "collectible with leaf preimage", + assetType: asset.Collectible, + tapscriptPreimage: testLeafPreimage, + noMetaHash: true, }, { - name: "collectible with branch preimage", - assetType: asset.Collectible, - tapscriptPreimage: commitment.NewPreimageFromBranch( - branch, - ), - noMetaHash: true, + name: "collectible with branch preimage", + assetType: asset.Collectible, + tapscriptPreimage: &testBranchPreimage, + noMetaHash: true, }, { name: "normal genesis", @@ -431,22 +430,18 @@ func TestGenesisProofVerification(t *testing.T) { assetVersion: asset.V1, }, { - name: "normal with leaf preimage", - assetType: asset.Normal, - amount: &amount, - tapscriptPreimage: commitment.NewPreimageFromLeaf( - leaf1, - ), - noMetaHash: true, + name: "normal with leaf preimage", + assetType: asset.Normal, + amount: &amount, + tapscriptPreimage: testLeafPreimage, + noMetaHash: true, }, { - name: "normal with branch preimage", - assetType: asset.Normal, - amount: &amount, - tapscriptPreimage: commitment.NewPreimageFromBranch( - branch, - ), - noMetaHash: true, + name: "normal with branch preimage", + assetType: asset.Normal, + amount: &amount, + tapscriptPreimage: &testBranchPreimage, + noMetaHash: true, }, { name: "normal asset with a meta reveal", diff --git a/proof/records.go b/proof/records.go index a84a8cfc6..13f9f8dd7 100644 --- a/proof/records.go +++ b/proof/records.go @@ -224,8 +224,14 @@ func CommitmentProofTapSiblingPreimageRecord( preimage **commitment.TapscriptPreimage) tlv.Record { sizeFunc := func() uint64 { - // 1 byte for the type, and then the pre-image itself. - return 1 + uint64(len((*preimage).SiblingPreimage)) + var buf bytes.Buffer + err := commitment.TapscriptPreimageEncoder( + &buf, preimage, &[8]byte{}, + ) + if err != nil { + panic(err) + } + return uint64(len(buf.Bytes())) } return tlv.MakeDynamicRecord( CommitmentProofTapSiblingPreimageType, preimage, sizeFunc, @@ -238,10 +244,15 @@ func TapscriptProofTapPreimage1Record( preimage **commitment.TapscriptPreimage) tlv.Record { sizeFunc := func() uint64 { - // 1 byte for the type, and then the pre-image itself. - return 1 + uint64(len((*preimage).SiblingPreimage)) + var buf bytes.Buffer + err := commitment.TapscriptPreimageEncoder( + &buf, preimage, &[8]byte{}, + ) + if err != nil { + panic(err) + } + return uint64(len(buf.Bytes())) } - return tlv.MakeDynamicRecord( TapscriptProofTapPreimage1, preimage, sizeFunc, commitment.TapscriptPreimageEncoder, @@ -253,10 +264,15 @@ func TapscriptProofTapPreimage2Record( preimage **commitment.TapscriptPreimage) tlv.Record { sizeFunc := func() uint64 { - // 1 byte for the type, and then the pre-image itself. - return 1 + uint64(len((*preimage).SiblingPreimage)) + var buf bytes.Buffer + err := commitment.TapscriptPreimageEncoder( + &buf, preimage, &[8]byte{}, + ) + if err != nil { + panic(err) + } + return uint64(len(buf.Bytes())) } - return tlv.MakeDynamicRecord( TapscriptProofTapPreimage2, preimage, sizeFunc, commitment.TapscriptPreimageEncoder, diff --git a/proof/taproot.go b/proof/taproot.go index 6ce6ab7a5..620fe1e2d 100644 --- a/proof/taproot.go +++ b/proof/taproot.go @@ -99,12 +99,12 @@ type TapscriptProof struct { // EncodeRecords returns the encoding records for TapscriptProof. func (p TapscriptProof) EncodeRecords() []tlv.Record { records := make([]tlv.Record, 0, 3) - if p.TapPreimage1 != nil && len(p.TapPreimage1.SiblingPreimage) > 0 { + if p.TapPreimage1 != nil && !p.TapPreimage1.IsEmpty() { records = append(records, TapscriptProofTapPreimage1Record( &p.TapPreimage1, )) } - if p.TapPreimage2 != nil && len(p.TapPreimage2.SiblingPreimage) > 0 { + if p.TapPreimage2 != nil && !p.TapPreimage2.IsEmpty() { records = append(records, TapscriptProofTapPreimage2Record( &p.TapPreimage2, )) @@ -373,23 +373,20 @@ func (p TapscriptProof) DeriveTaprootKeys(internalKey *btcec.PublicKey) ( // hashes. In this case, the tapscript tree has two elements, with both // of them being leaves. case !p.TapPreimage1.IsEmpty() && !p.TapPreimage2.IsEmpty() && - p.TapPreimage1.SiblingType == commitment.LeafPreimage && - p.TapPreimage2.SiblingType == commitment.LeafPreimage: + p.TapPreimage1.Type() == commitment.LeafPreimage && + p.TapPreimage2.Type() == commitment.LeafPreimage: - leafHash1, err := commitment.TapLeafHash( - p.TapPreimage1.SiblingPreimage, - ) + leafHash1, err := p.TapPreimage1.TapHash() if err != nil { return nil, err } - leafHash2, err := commitment.TapLeafHash( - p.TapPreimage2.SiblingPreimage, - ) + + leafHash2, err := p.TapPreimage2.TapHash() if err != nil { return nil, err } - rootHash := commitment.NewTapBranchHash(*leafHash1, *leafHash2) + rootHash := asset.NewTapBranchHash(*leafHash1, *leafHash2) tapscriptRoot = rootHash[:] // Two pre-images are specified, with both of the pre-images being a @@ -397,23 +394,19 @@ func (p TapscriptProof) DeriveTaprootKeys(internalKey *btcec.PublicKey) ( // we just care that these are actually branches and the hash up // correctly. case !p.TapPreimage1.IsEmpty() && !p.TapPreimage2.IsEmpty() && - p.TapPreimage1.SiblingType == commitment.BranchPreimage && - p.TapPreimage2.SiblingType == commitment.BranchPreimage: + p.TapPreimage1.Type() == commitment.BranchPreimage && + p.TapPreimage2.Type() == commitment.BranchPreimage: - branch1, err := commitment.TapBranchHash( - p.TapPreimage1.SiblingPreimage, - ) + branch1, err := p.TapPreimage1.TapHash() if err != nil { return nil, err } - branch2, err := commitment.TapBranchHash( - p.TapPreimage2.SiblingPreimage, - ) + branch2, err := p.TapPreimage2.TapHash() if err != nil { return nil, err } - rootHash := commitment.NewTapBranchHash(*branch1, *branch2) + rootHash := asset.NewTapBranchHash(*branch1, *branch2) tapscriptRoot = rootHash[:] // Two pre-images are specified, with one of them being a leaf and the @@ -421,33 +414,27 @@ func (p TapscriptProof) DeriveTaprootKeys(internalKey *btcec.PublicKey) ( // tree. We'll verify the first sibling is a leaf, and the other is // actually a branch. case !p.TapPreimage1.IsEmpty() && !p.TapPreimage2.IsEmpty() && - p.TapPreimage1.SiblingType == commitment.LeafPreimage && - p.TapPreimage2.SiblingType == commitment.BranchPreimage: + p.TapPreimage1.Type() == commitment.LeafPreimage && + p.TapPreimage2.Type() == commitment.BranchPreimage: - leafHash, err := commitment.TapLeafHash( - p.TapPreimage1.SiblingPreimage, - ) + leafHash, err := p.TapPreimage1.TapHash() if err != nil { return nil, err } - branchHash, err := commitment.TapBranchHash( - p.TapPreimage2.SiblingPreimage, - ) + branchHash, err := p.TapPreimage2.TapHash() if err != nil { return nil, err } - rootHash := commitment.NewTapBranchHash(*leafHash, *branchHash) + rootHash := asset.NewTapBranchHash(*leafHash, *branchHash) tapscriptRoot = rootHash[:] // Only a single pre-image was specified, and the pre-image is a leaf. - case !p.TapPreimage1.IsEmpty() && - p.TapPreimage1.SiblingType == commitment.BranchPreimage: + case !p.TapPreimage1.IsEmpty() && p.TapPreimage2.IsEmpty() && + p.TapPreimage1.Type() == commitment.LeafPreimage: - tapHash, err := commitment.TapLeafHash( - p.TapPreimage1.SiblingPreimage, - ) + tapHash, err := p.TapPreimage1.TapHash() if err != nil { return nil, err } diff --git a/rpcserver.go b/rpcserver.go index 36075d352..92ba5ee7c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -18,6 +18,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" @@ -515,12 +516,44 @@ func (r *rpcServer) FinalizeBatch(_ context.Context, req *mintrpc.FinalizeBatchRequest) (*mintrpc.FinalizeBatchResponse, error) { + var batchSibling *asset.TapscriptTreeNodes + feeRate, err := checkFeeRateSanity(req.FeeRate) if err != nil { return nil, err } + feeRateOpt := fn.MaybeSome(feeRate) + + batchTapscriptTree := req.GetFullTree() + batchTapBranch := req.GetBranch() + + switch { + case batchTapscriptTree != nil && batchTapBranch != nil: + return nil, fmt.Errorf("cannot specify both tapscript tree " + + "and tapscript tree branches") + + case batchTapscriptTree != nil: + batchSibling, err = marshalTapscriptFullTree(batchTapscriptTree) + if err != nil { + return nil, fmt.Errorf("invalid tapscript tree: %w", + err) + } + + case batchTapBranch != nil: + batchSibling, err = marshalTapscriptBranch(batchTapBranch) + if err != nil { + return nil, fmt.Errorf("invalid tapscript branch: %w", + err) + } + } + tapTreeOpt := fn.MaybeSome(batchSibling) - batch, err := r.cfg.AssetMinter.FinalizeBatch(feeRate) + batch, err := r.cfg.AssetMinter.FinalizeBatch( + tapgarden.FinalizeParams{ + FeeRate: feeRateOpt, + SiblingTapTree: tapTreeOpt, + }, + ) if err != nil { return nil, fmt.Errorf("unable to finalize batch: %w", err) } @@ -1097,7 +1130,8 @@ func (r *rpcServer) NewAddr(ctx context.Context, return nil, err } - // Was there a tapscript sibling preimage specified? + // Was there a tapscript sibling preimage specified? If so, decode it + // and check that it is not a Taproot Asset Commitment. tapscriptSibling, _, err := commitment.MaybeDecodeTapscriptPreimage( req.TapscriptSibling, ) @@ -2916,6 +2950,29 @@ func marshalBatchState(batch *tapgarden.MintingBatch) (mintrpc.BatchState, } } +func marshalTapscriptFullTree(tree *taprpc.TapscriptFullTree) ( + *asset.TapscriptTreeNodes, error) { + + rpcLeaves := tree.GetAllLeaves() + leaves := fn.Map(rpcLeaves, func(l *taprpc.TapLeaf) txscript.TapLeaf { + return txscript.NewBaseTapLeaf(l.Script) + }) + + return asset.TapTreeNodesFromLeaves(leaves) +} + +func marshalTapscriptBranch(branch *taprpc.TapBranch) (*asset.TapscriptTreeNodes, + error) { + + branchData := [][]byte{branch.LeftTaphash, branch.RightTaphash} + tapBranch, err := asset.DecodeTapBranchNodes(branchData) + if err != nil { + return nil, err + } + + return fn.Ptr(asset.FromBranch(*tapBranch)), nil +} + // UnmarshalScriptKey parses the RPC script key into the native counterpart. func UnmarshalScriptKey(rpcKey *taprpc.ScriptKey) (*asset.ScriptKey, error) { var ( diff --git a/tapcfg/server.go b/tapcfg/server.go index 13117bb3b..25d1acc53 100644 --- a/tapcfg/server.go +++ b/tapcfg/server.go @@ -332,6 +332,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, Wallet: walletAnchor, ChainBridge: chainBridge, Log: assetMintingStore, + TreeStore: assetMintingStore, KeyRing: keyRing, GenSigner: virtualTxSigner, GenTxBuilder: &tapscript.GroupTxBuilder{}, diff --git a/tapdb/asset_minting.go b/tapdb/asset_minting.go index e1d81ecf1..78a22e02c 100644 --- a/tapdb/asset_minting.go +++ b/tapdb/asset_minting.go @@ -60,6 +60,10 @@ type ( // AssetGroupKey is used to insert a new asset key group into the DB. AssetGroupKey = sqlc.UpsertAssetGroupKeyParams + // BatchTapSiblingUpdate is used to update a batch with the root hash + // of a tapscript sibling associated with it. + BatchTapSiblingUpdate = sqlc.BindMintingBatchWithTapSiblingParams + // BatchChainUpdate is used to update a batch with the minting // transaction associated with it. BatchChainUpdate = sqlc.BindMintingBatchWithTxParams @@ -133,6 +137,10 @@ type PendingAssetStore interface { // GroupStore houses the methods related to querying asset groups. GroupStore + // TapscriptTreeStore houses the methods related to storing, fetching, + // and deleting tapscript trees. + TapscriptTreeStore + // NewMintingBatch creates a new minting batch. NewMintingBatch(ctx context.Context, arg MintingBatchInit) error @@ -177,6 +185,11 @@ type PendingAssetStore interface { FetchSeedlingByID(ctx context.Context, seedlingID int64) (AssetSeedling, error) + // BindMintingBatchWithTapSibling adds a tapscript tree root hash to an + // existing batch. + BindMintingBatchWithTapSibling(ctx context.Context, + arg BatchTapSiblingUpdate) error + // BindMintingBatchWithTx adds the minting transaction to an existing // batch. BindMintingBatchWithTx(ctx context.Context, arg BatchChainUpdate) error @@ -299,6 +312,21 @@ func (a *AssetMintingStore) CommitMintingBatch(ctx context.Context, "batch: %w", err) } + // With the batch key and batch itself inserted, we can insert + // the batch tapscript sibling if present. + newBatchSibling := newBatch.TapSibling() + if newBatchSibling != nil { + tapSibling := BatchTapSiblingUpdate{ + RawKey: rawBatchKey, + TapscriptSibling: newBatchSibling, + } + err = q.BindMintingBatchWithTapSibling(ctx, tapSibling) + if err != nil { + return fmt.Errorf("unable to insert batch "+ + "sibling: %w", err) + } + } + // Now that our minting batch is in place, which references the // internal key inserted above, we can create the set of new // seedlings. We insert group anchors before other assets. @@ -849,6 +877,15 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore, batch.UpdateState(batchState) + if len(dbBatch.TapscriptSibling) != 0 { + batchSibling, err := chainhash.NewHash(dbBatch.TapscriptSibling) + if err != nil { + return nil, err + } + + batch.UpdateTapSibling(batchSibling) + } + if dbBatch.MintingTxPsbt != nil { genesisPkt, err := psbt.NewFromRawBytes( bytes.NewReader(dbBatch.MintingTxPsbt), false, @@ -916,6 +953,22 @@ func (a *AssetMintingStore) UpdateBatchState(ctx context.Context, }) } +// CommitBatchTapSibling updates the tapscript sibling of a batch based on the +// batch key. +func (a *AssetMintingStore) CommitBatchTapSibling(ctx context.Context, + batchKey *btcec.PublicKey, batchSibling *chainhash.Hash) error { + + siblingUpdate := BatchTapSiblingUpdate{ + RawKey: batchKey.SerializeCompressed(), + TapscriptSibling: batchSibling[:], + } + + var writeTxOpts AssetStoreTxOptions + return a.db.ExecTx(ctx, &writeTxOpts, func(q PendingAssetStore) error { + return q.BindMintingBatchWithTapSibling(ctx, siblingUpdate) + }) +} + // encodeOutpoint encodes the outpoint point in Bitcoin wire format, returning // the final result. func encodeOutpoint(outPoint wire.OutPoint) ([]byte, error) { @@ -1006,7 +1059,8 @@ func (a *AssetMintingStore) AddSproutsToBatch(ctx context.Context, // root manually? func (a *AssetMintingStore) CommitSignedGenesisTx(ctx context.Context, batchKey *btcec.PublicKey, genesisPkt *tapgarden.FundedPsbt, - anchorOutputIndex uint32, merkleRoot []byte) error { + anchorOutputIndex uint32, merkleRoot, tapTreeRoot []byte, + tapSibling []byte) error { // The managed UTXO we'll insert only contains the raw tx of the // genesis packet, so we'll extract that now. @@ -1074,13 +1128,11 @@ func (a *AssetMintingStore) CommitSignedGenesisTx(ctx context.Context, // batch, we'll create a new managed UTXO for this batch as // this is where all the assets will be anchored within. utxoID, err := q.UpsertManagedUTXO(ctx, RawManagedUTXO{ - RawKey: rawBatchKey, - Outpoint: anchorOutpoint, - AmtSats: anchorOutput.Value, - // When minting, we never have a tapscript sibling, so - // the TaprootAssetRoot root is always equal to the - // merkle root. - TaprootAssetRoot: merkleRoot, + RawKey: rawBatchKey, + Outpoint: anchorOutpoint, + AmtSats: anchorOutput.Value, + TaprootAssetRoot: tapTreeRoot, + TapscriptSibling: tapSibling, MerkleRoot: merkleRoot, TxnID: chainTXID, }) @@ -1211,6 +1263,122 @@ func (a *AssetMintingStore) FetchGroupByGroupKey(ctx context.Context, return dbGroup, nil } +// StoreTapscriptTree persists a Tapscript tree given a validated set of +// TapLeafs or a TapBranch. If the store succeeds, the root hash of the +// Tapscript tree is returned. +func (a *AssetMintingStore) StoreTapscriptTree(ctx context.Context, + treeNodes asset.TapscriptTreeNodes) (*chainhash.Hash, error) { + + var ( + rootHash chainhash.Hash + isBranch bool + treeNodesBytes [][]byte + err error + ) + + asset.GetLeaves(treeNodes).WhenSome(func(tln asset.TapLeafNodes) { + rootHash = asset.LeafNodesRootHash(tln) + treeNodesBytes, err = asset.EncodeTapLeafNodes(tln) + }) + if err != nil { + return nil, err + } + + // For a TapBranch, we must set isBranch to ensure that the branch data + // will be decoded correctly. + asset.GetBranch(treeNodes).WhenSome(func(tbn asset.TapBranchNodes) { + isBranch = true + rootHash = asset.BranchNodesRootHash(tbn) + treeNodesBytes = asset.EncodeTapBranchNodes(tbn) + }) + + // If no tapscript tree data was encoded, the given tapscript tree was + // malformed. Return before modifying the database. + if len(treeNodesBytes) == 0 { + return nil, fmt.Errorf("unable to encode tapscript tree") + } + + var writeTxOpts AssetStoreTxOptions + err = a.db.ExecTx(ctx, &writeTxOpts, func(a PendingAssetStore) error { + return upsertTapscriptTree( + ctx, a, rootHash[:], isBranch, treeNodesBytes, + ) + }) + if err != nil { + return nil, err + } + + return &rootHash, nil +} + +// LoadTapscriptTree loads the Tapscript tree with the given root hash, and +// decodes the tree into a TapscriptTreeNodes object. +func (a *AssetMintingStore) LoadTapscriptTree(ctx context.Context, + rootHash chainhash.Hash) (*asset.TapscriptTreeNodes, error) { + + var ( + dbTreeNodes []TapscriptTreeNode + err error + ) + + readOpts := NewAssetStoreReadTx() + dbErr := a.db.ExecTx(ctx, &readOpts, func(a PendingAssetStore) error { + dbTreeNodes, err = a.FetchTapscriptTree(ctx, rootHash[:]) + return err + }) + if dbErr != nil { + return nil, dbErr + } + + // The query can return zero nodes without returning an error, so handle + // that case explicitly here. + if len(dbTreeNodes) == 0 { + return nil, asset.ErrTreeNotFound + } + + nodeBytes := fn.Map(dbTreeNodes, func(dbNode TapscriptTreeNode) []byte { + return dbNode.RawNode + }) + + // Each node signals if the tree was stored as a set of leaves or a + // branch, so we can read this flag from any node. + isBranch := dbTreeNodes[0].BranchOnly + if isBranch { + // For a tree stored as a TapBranch, there can only be two nodes + // returned. + if len(dbTreeNodes) != 2 { + return nil, asset.ErrInvalidTapBranch + } + + branchNodes, err := asset.DecodeTapBranchNodes(nodeBytes) + if err != nil { + return nil, err + } + + return fn.Ptr(asset.FromBranch(*branchNodes)), nil + } + + // If the tree was not stored as a branch, it must be a set of leaves. + leafNodes, err := asset.DecodeTapLeafNodes(nodeBytes) + if err != nil { + return nil, err + } + + return fn.Ptr(asset.FromLeaves(*leafNodes)), nil +} + +// DeleteTapscriptTree deletes the Tapscript tree with the given root hash, +// including all nodes and edges. +func (a *AssetMintingStore) DeleteTapscriptTree(ctx context.Context, + rootHash chainhash.Hash) error { + + var writeTxOpts AssetStoreTxOptions + return a.db.ExecTx(ctx, &writeTxOpts, func(a PendingAssetStore) error { + return deleteTapscriptTree(ctx, a, rootHash[:]) + }) +} + // A compile-time assertion to ensure that AssetMintingStore meets the // tapgarden.MintingStore interface. var _ tapgarden.MintingStore = (*AssetMintingStore)(nil) +var _ asset.TapscriptTreeManager = (*AssetMintingStore)(nil) diff --git a/tapdb/asset_minting_test.go b/tapdb/asset_minting_test.go index a39a52e25..a719f3e89 100644 --- a/tapdb/asset_minting_test.go +++ b/tapdb/asset_minting_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -66,11 +67,18 @@ func assertBatchState(t *testing.T, batch *tapgarden.MintingBatch, require.Equal(t, state, batch.State()) } +func assertBatchSibling(t *testing.T, batch *tapgarden.MintingBatch, + sibling chainhash.Hash) { + + require.Equal(t, sibling[:], batch.TapSibling()) +} + func assertBatchEqual(t *testing.T, a, b *tapgarden.MintingBatch) { t.Helper() require.Equal(t, a.CreationTime.Unix(), b.CreationTime.Unix()) require.Equal(t, a.State(), b.State()) + require.Equal(t, a.TapSibling(), b.TapSibling()) require.Equal(t, a.BatchKey, b.BatchKey) require.Equal(t, a.Seedlings, b.Seedlings) require.Equal(t, a.GenesisPacket, b.GenesisPacket) @@ -152,6 +160,146 @@ func storeGroupGenesis(t *testing.T, ctx context.Context, initGen asset.Genesis, } } +// treeFromLeaves generates a tapscript tree in multiple forms from a list of +// tapscript leaves. +func treeFromLeaves(t *testing.T, leaves []txscript.TapLeaf) (chainhash.Hash, + asset.TapscriptTreeNodes, [][]byte) { + + tree, err := asset.TapTreeNodesFromLeaves(leaves) + require.NoError(t, err) + + checkedLeaves := asset.GetLeaves(*tree).UnwrapToPtr() + require.NotNil(t, checkedLeaves) + + treeBytes, err := asset.EncodeTapLeafNodes(*checkedLeaves) + require.NoError(t, err) + + return asset.LeafNodesRootHash(*checkedLeaves), *tree, treeBytes +} + +// treeFromBranch generates a tapscript tree in multiple forms from a set of +// byte slices. +func treeFromBranch(t *testing.T, children [][]byte) (chainhash.Hash, + asset.TapscriptTreeNodes, [][]byte) { + + branch, err := asset.DecodeTapBranchNodes(children) + require.NoError(t, err) + + tree := asset.FromBranch(*branch) + treeBytes := asset.EncodeTapBranchNodes(*branch) + + return asset.BranchNodesRootHash(*branch), tree, treeBytes +} + +// storeTapscriptTreeWrapper wraps a DB transaction that stores a tapscript +// tree. +func storeTapscriptTreeWrapper(t *testing.T, ctx context.Context, isBranch bool, + store *AssetMintingStore, rootHash []byte, nodes [][]byte) error { + + var writeTxOpts AssetStoreTxOptions + return store.db.ExecTx(ctx, &writeTxOpts, + func(q PendingAssetStore) error { + return upsertTapscriptTree( + ctx, q, rootHash, isBranch, nodes, + ) + }) +} + +// fetchTapscriptTreeWrapper wraps a DB transaction that fetches a tapscript +// tree. +func fetchTapscriptTreeWrapper(t *testing.T, ctx context.Context, + rootHash []byte, store *AssetMintingStore) ([]TapscriptTreeNode, + error) { + + var ( + dbTreeNodes []TapscriptTreeNode + err error + ) + + readOpts := NewAssetStoreReadTx() + dbErr := store.db.ExecTx(ctx, &readOpts, + func(q PendingAssetStore) error { + dbTreeNodes, err = q.FetchTapscriptTree(ctx, rootHash) + return err + }) + + return dbTreeNodes, dbErr +} + +// deleteTapscriptTreeWrapper wraps a DB transaction that deletes a tapscript +// tree. +func deleteTapscriptTreeWrapper(t *testing.T, ctx context.Context, + rootHash []byte, store *AssetMintingStore) error { + + var writeTxOpts AssetStoreTxOptions + return store.db.ExecTx(ctx, &writeTxOpts, + func(q PendingAssetStore) error { + return deleteTapscriptTree(ctx, q, rootHash[:]) + }) +} + +// assertTreeDeletion asserts that a tapscript tree has been deleted properly. +func assertTreeDeletion(t *testing.T, ctx context.Context, rootHash []byte, + store *AssetMintingStore) { + + dbTree, err := fetchTapscriptTreeWrapper(t, ctx, rootHash, store) + require.NoError(t, err) + require.Empty(t, dbTree) +} + +// assertStoredTreeEqual asserts that the tapscript tree fetched with a root +// hash matches the expected bytes. +func assertStoredTreeEqual(t *testing.T, ctx context.Context, isBranch bool, + store *AssetMintingStore, rootHash []byte, expected [][]byte) { + + dbTree, err := fetchTapscriptTreeWrapper(t, ctx, rootHash, store) + require.NoError(t, err) + + require.True(t, fn.All(dbTree, func(node TapscriptTreeNode) bool { + return node.BranchOnly == isBranch + })) + dbTreeBytes := fn.Map(dbTree, func(node TapscriptTreeNode) []byte { + return node.RawNode + }) + require.Equal(t, expected, dbTreeBytes) +} + +// storeTapscriptTreeChecked asserts that we can store a tapscript tree, and +// that the root hash returned matches the one calculated from the tree. +func storeTapscriptTreeChecked(t *testing.T, ctx context.Context, + store *AssetMintingStore, tree asset.TapscriptTreeNodes, + hash chainhash.Hash) { + + dbRootHash, err := store.StoreTapscriptTree(ctx, tree) + require.NoError(t, err) + require.True(t, hash.IsEqual(dbRootHash)) +} + +// loadTapscriptTreeChecked asserts that we can load a tapscript tree, and that +// the tapscript tree returned matches the initial tree. +func loadTapscriptTreeChecked(t *testing.T, ctx context.Context, + store *AssetMintingStore, tree asset.TapscriptTreeNodes, + hash chainhash.Hash) { + + dbTree, err := store.LoadTapscriptTree(ctx, hash) + require.NoError(t, err) + require.NotNil(t, dbTree) + require.Equal(t, tree, *dbTree) +} + +// deleteTapscriptTreeChecked asserts that we can delete a tapscript tree, and +// that future attempts to load the deleted tree return the expected error. +func deleteTapscriptTreeChecked(t *testing.T, ctx context.Context, + store *AssetMintingStore, hash chainhash.Hash) { + + err := store.DeleteTapscriptTree(ctx, hash) + require.NoError(t, err) + + dbTree, err := store.LoadTapscriptTree(ctx, hash) + require.Empty(t, dbTree) + require.ErrorIs(t, err, asset.ErrTreeNotFound) +} + // addRandGroupToBatch selects a random seedling, generates an asset genesis to // match that seedling, and stores that genesis so that the seedling can be // minted into an existing group. The seedling is updated with the group key @@ -188,6 +336,22 @@ func addRandGroupToBatch(t *testing.T, store *AssetMintingStore, return genesisAmt, seedlingGroups, group } +// addRandSiblingToBatch generates a random hash and adds it to the given batch. +func addRandSiblingToBatch(t *testing.T, batch *tapgarden.MintingBatch) ( + commitment.TapscriptPreimage, chainhash.Hash) { + + tapSiblingSingleLeaf := test.RandTapLeaf(nil) + siblingPreimage, err := commitment.NewPreimageFromLeaf( + tapSiblingSingleLeaf, + ) + require.NoError(t, err) + tapSibling, err := siblingPreimage.TapHash() + require.NoError(t, err) + batch.UpdateTapSibling(tapSibling) + + return *siblingPreimage, *tapSibling +} + // addMultiAssetGroupToBatch selects a random seedling pair, where neither // seedling is being issued into an existing group, and creates a multi-asset // group. Specifically, one seedling will have emission enabled, and the other @@ -243,6 +407,7 @@ func TestCommitMintingBatchSeedlings(t *testing.T) { // be a reissuance into a specific group. mintingBatch := tapgarden.RandSeedlingMintingBatch(t, numSeedlings) addRandGroupToBatch(t, assetStore, ctx, mintingBatch.Seedlings) + _, randSiblingHash := addRandSiblingToBatch(t, mintingBatch) err := assetStore.CommitMintingBatch(ctx, mintingBatch) require.NoError(t, err) @@ -253,6 +418,7 @@ func TestCommitMintingBatchSeedlings(t *testing.T) { mintingBatches := noError1(t, assetStore.FetchNonFinalBatches, ctx) assertSeedlingBatchLen(t, mintingBatches, 1, numSeedlings) assertBatchEqual(t, mintingBatch, mintingBatches[0]) + assertBatchSibling(t, mintingBatch, randSiblingHash) mintingBatchKeyed, err := assetStore.FetchMintingBatch(ctx, batchKey) require.NoError(t, err) @@ -599,13 +765,16 @@ func TestAddSproutsToBatch(t *testing.T) { } type randAssetCtx struct { - batchKey *btcec.PublicKey - groupKey *btcec.PublicKey - groupGenAmt uint64 - genesisPkt *tapgarden.FundedPsbt - scriptRoot []byte - assetRoot *commitment.TapCommitment - mintingBatch *tapgarden.MintingBatch + batchKey *btcec.PublicKey + groupKey *btcec.PublicKey + groupGenAmt uint64 + genesisPkt *tapgarden.FundedPsbt + assetRoot *commitment.TapCommitment + merkleRoot []byte + scriptRoot []byte + tapSiblingBytes []byte + tapSiblingHash chainhash.Hash + mintingBatch *tapgarden.MintingBatch } func addRandAssets(t *testing.T, ctx context.Context, @@ -615,6 +784,7 @@ func addRandAssets(t *testing.T, ctx context.Context, genAmt, seedlingGroups, group := addRandGroupToBatch( t, assetStore, ctx, mintingBatch.Seedlings, ) + randSibling, randSiblingHash := addRandSiblingToBatch(t, mintingBatch) batchKey := mintingBatch.BatchKey.PubKey require.NoError(t, assetStore.CommitMintingBatch(ctx, mintingBatch)) @@ -628,16 +798,24 @@ func addRandAssets(t *testing.T, ctx context.Context, ctx, batchKey, genesisPacket, assetRoot, )) + merkleRoot := assetRoot.TapscriptRoot(&randSiblingHash) scriptRoot := assetRoot.TapscriptRoot(nil) + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( + &randSibling, + ) + require.NoError(t, err) return randAssetCtx{ - batchKey: batchKey, - groupKey: &group.GroupKey.GroupPubKey, - groupGenAmt: genAmt, - genesisPkt: genesisPacket, - scriptRoot: scriptRoot[:], - assetRoot: assetRoot, - mintingBatch: mintingBatch, + batchKey: batchKey, + groupKey: &group.GroupKey.GroupPubKey, + groupGenAmt: genAmt, + genesisPkt: genesisPacket, + assetRoot: assetRoot, + merkleRoot: merkleRoot[:], + scriptRoot: scriptRoot[:], + tapSiblingBytes: siblingBytes, + tapSiblingHash: randSiblingHash, + mintingBatch: mintingBatch, } } @@ -671,7 +849,8 @@ func TestCommitBatchChainActions(t *testing.T) { // alongside any managed UTXOs. require.NoError(t, assetStore.CommitSignedGenesisTx( ctx, randAssetCtx.batchKey, randAssetCtx.genesisPkt, 2, - randAssetCtx.scriptRoot, + randAssetCtx.merkleRoot, randAssetCtx.scriptRoot, + randAssetCtx.tapSiblingBytes, )) // The batch updated above should be found, with the batch state @@ -684,6 +863,7 @@ func TestCommitBatchChainActions(t *testing.T) { assertPsbtEqual( t, randAssetCtx.genesisPkt, mintingBatches[0].GenesisPacket, ) + assertBatchSibling(t, mintingBatches[0], randAssetCtx.tapSiblingHash) var rawTxBytes bytes.Buffer rawGenTx, err := psbt.Extract(randAssetCtx.genesisPkt.Pkt) @@ -708,7 +888,11 @@ func TestCommitBatchChainActions(t *testing.T) { TxnID: sqlInt64(dbGenTx.TxnID), }) require.NoError(t, err) - require.Equal(t, randAssetCtx.scriptRoot, managedUTXO.MerkleRoot) + require.Equal(t, randAssetCtx.merkleRoot, managedUTXO.MerkleRoot) + require.Equal(t, randAssetCtx.scriptRoot, managedUTXO.TaprootAssetRoot) + require.Equal( + t, randAssetCtx.tapSiblingBytes, managedUTXO.TapscriptSibling, + ) // Next, we'll confirm that all the assets inserted previously now are // able to be queried according to the anchor UTXO primary key. @@ -1189,6 +1373,256 @@ func TestGroupAnchors(t *testing.T) { require.Equal(t, groupAnchors, rawGroupAnchors) } +// TestTapscriptTreeStore tests the functions that use the queries of the +// TapscriptTreeStore interface. +func TestTapscriptTreeStore(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // First, we'll open up a new asset store. We only need the mintingStore + // pointer, as we're only testing the TapscriptTreeStore functionality + // here. + assetStore, _, _ := newAssetStore(t) + + // Now we generate a set of tapLeafs and tapBranches to store. + randLeafCount := 4 + var tapLeaves []txscript.TapLeaf + for i := 0; i < randLeafCount; i++ { + leaf := test.RandTapLeaf(nil) + tapLeaves = append(tapLeaves, leaf) + } + + // Let's add a duplicate tapLeaf as well. + dupeNode := txscript.NewBaseTapLeaf(tapLeaves[1].Script) + tapLeaves = append(tapLeaves, dupeNode) + + branchChildCount := 3 + var tapBranchChildren [][]byte + for i := 0; i < branchChildCount; i++ { + tapBranchChildren = append( + tapBranchChildren, test.RandBytes(chainhash.HashSize), + ) + } + + // Now, let's compute root hashes for the trees we'll load and store. + // We will use 5 trees total, as drawn below. + // + // tree 1: tapLeaves[0] + // tree 2: tapLeaves[:3] (first three nodes) + // tree 3; tapLeaves[:] (five nodes, including a duplicate from tree 2) + // tree 4: tapBranchChildren[:2] (first two branch nodes) + // tree 5: tapBranchChildren[1:] (last two branch nodes) + + tree1Hash, _, tree1 := treeFromLeaves( + t, []txscript.TapLeaf{tapLeaves[0]}, + ) + tree2Hash, _, tree2 := treeFromLeaves(t, tapLeaves[:3]) + tree3Hash, _, tree3 := treeFromLeaves(t, tapLeaves[:]) + tree4Hash, _, tree4 := treeFromBranch(t, tapBranchChildren[:2]) + tree5Hash, _, tree5 := treeFromBranch(t, tapBranchChildren[1:]) + + // Start with the cases where tree insertion should fail. + badRootHashErr := storeTapscriptTreeWrapper( + t, ctx, false, assetStore, tree1Hash[1:], tree1, + ) + require.ErrorContains(t, badRootHashErr, "must be 32 bytes") + + emptyTreeErr := storeTapscriptTreeWrapper( + t, ctx, false, assetStore, tree1Hash[:], nil, + ) + require.ErrorContains(t, emptyTreeErr, "no tapscript tree nodes") + + invalidBranchErr := storeTapscriptTreeWrapper( + t, ctx, true, assetStore, tree4Hash[:], tree3, + ) + require.ErrorContains(t, invalidBranchErr, "must be 2 nodes") + + // Now, let's insert the first tree, and then assert that we can fetch + // and decode an identical tree. + err := storeTapscriptTreeWrapper( + t, ctx, false, assetStore, tree1Hash[:], tree1, + ) + require.NoError(t, err) + + assertStoredTreeEqual(t, ctx, false, assetStore, tree1Hash[:], tree1) + + // If we try to fetch a tree with a different root hash, that will not + // return an error, but the results should be empty. + dbTree2, err := fetchTapscriptTreeWrapper( + t, ctx, tree2Hash[:], assetStore, + ) + require.Empty(t, dbTree2) + require.Nil(t, err) + + // Trying to delete a tree we haven't inserted yet will not err. + err = deleteTapscriptTreeWrapper(t, ctx, tree2Hash[:], assetStore) + require.Nil(t, err) + + // Insert the second tree, which has one node already inserted. + err = storeTapscriptTreeWrapper( + t, ctx, false, assetStore, tree2Hash[:], tree2, + ) + require.NoError(t, err) + + // Fetching both trees should still work. + assertStoredTreeEqual(t, ctx, false, assetStore, tree1Hash[:], tree1) + assertStoredTreeEqual(t, ctx, false, assetStore, tree2Hash[:], tree2) + + // If we delete the first tree, we should still be able to fetch the + // second tree intact. + err = deleteTapscriptTreeWrapper(t, ctx, tree1Hash[:], assetStore) + require.NoError(t, err) + assertTreeDeletion(t, ctx, tree1Hash[:], assetStore) + + assertStoredTreeEqual(t, ctx, false, assetStore, tree2Hash[:], tree2) + + // Let's insert the third tree, which contains a node that's a duplicate + // of an already-inserted node. + err = storeTapscriptTreeWrapper( + t, ctx, false, assetStore, tree3Hash[:], tree3, + ) + require.NoError(t, err) + + // Fetching the second and third trees should succeed. + assertStoredTreeEqual(t, ctx, false, assetStore, tree2Hash[:], tree2) + assertStoredTreeEqual(t, ctx, false, assetStore, tree3Hash[:], tree3) + + // Deleting the third tree should not affect the second tree. + err = deleteTapscriptTreeWrapper(t, ctx, tree3Hash[:], assetStore) + require.NoError(t, err) + assertTreeDeletion(t, ctx, tree1Hash[:], assetStore) + + assertStoredTreeEqual(t, ctx, false, assetStore, tree2Hash[:], tree2) + + // Let's also test handling of tapscript branches. + err = storeTapscriptTreeWrapper( + t, ctx, true, assetStore, tree4Hash[:], tree4, + ) + require.NoError(t, err) + + assertStoredTreeEqual(t, ctx, true, assetStore, tree4Hash[:], tree4) + + // The second tapscript branch shares a node with the first. + err = storeTapscriptTreeWrapper( + t, ctx, true, assetStore, tree5Hash[:], tree5, + ) + require.NoError(t, err) + + assertStoredTreeEqual(t, ctx, true, assetStore, tree4Hash[:], tree4) + assertStoredTreeEqual(t, ctx, true, assetStore, tree5Hash[:], tree5) + + // Deleting the first set of branches should not affect the second. + err = deleteTapscriptTreeWrapper(t, ctx, tree4Hash[:], assetStore) + require.NoError(t, err) + assertTreeDeletion(t, ctx, tree4Hash[:], assetStore) + + assertStoredTreeEqual(t, ctx, true, assetStore, tree5Hash[:], tree5) +} + +// TestTapscriptTreeManager tests the functions that implement the +// TapscriptTreeManager interface. This follows the same actions as +// TestTapscriptTreeStore, but with higher-level functions. +func TestTapscriptTreeManager(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // First, we'll open up a new asset store. We only need the mintingStore + // pointer, as we're only testing the TapscriptTreeStore functionality + // here. + assetStore, _, _ := newAssetStore(t) + + // Now we generate a set of tapLeafs and tapBranches to store. + randLeafCount := 4 + var tapLeaves []txscript.TapLeaf + for i := 0; i < randLeafCount; i++ { + leaf := test.RandTapLeaf(nil) + tapLeaves = append(tapLeaves, leaf) + } + + // Let's add a duplicate tapLeaf as well. + dupeNode := txscript.NewBaseTapLeaf(tapLeaves[1].Script) + tapLeaves = append(tapLeaves, dupeNode) + + branchChildCount := 3 + var tapBranchChildren [][]byte + for i := 0; i < branchChildCount; i++ { + tapBranchChildren = append( + tapBranchChildren, test.RandBytes(chainhash.HashSize), + ) + } + + // Now, let's compute root hashes for the trees we'll load and store. + // We will use 5 trees total, as drawn below. + // + // tree 1: tapLeaves[0] + // tree 2: tapLeaves[:3] (first three nodes) + // tree 3; tapLeaves[:] (five nodes, including a duplicate from tree 2) + // tree 4: tapBranchChildren[:2] (first two branch nodes) + // tree 5: tapBranchChildren[1:] (last two branch nodes) + + tree1Hash, tree1, _ := treeFromLeaves( + t, []txscript.TapLeaf{tapLeaves[0]}, + ) + tree2Hash, tree2, _ := treeFromLeaves(t, tapLeaves[:3]) + tree3Hash, tree3, _ := treeFromLeaves(t, tapLeaves[:]) + tree4Hash, tree4, _ := treeFromBranch(t, tapBranchChildren[:2]) + tree5Hash, tree5, _ := treeFromBranch(t, tapBranchChildren[1:]) + + // Now, let's insert the first tree, and then assert that we can fetch + // and decode an identical tree. + storeTapscriptTreeChecked(t, ctx, assetStore, tree1, tree1Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree1, tree1Hash) + + // If we try to fetch a tree with a different root hash, that will + // return an error. + tree2empty, err := assetStore.LoadTapscriptTree(ctx, tree2Hash) + require.ErrorContains(t, err, "tree not found") + require.Nil(t, tree2empty) + + // Trying to delete a tree we haven't inserted yet will not err. + err = assetStore.DeleteTapscriptTree(ctx, tree2Hash) + require.Nil(t, err) + + // Insert the second tree, which has one node already inserted. + storeTapscriptTreeChecked(t, ctx, assetStore, tree2, tree2Hash) + + // Fetching both trees should still work. + loadTapscriptTreeChecked(t, ctx, assetStore, tree1, tree1Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree2, tree2Hash) + + // If we delete the first tree, we should still be able to fetch the + // second tree intact. + deleteTapscriptTreeChecked(t, ctx, assetStore, tree1Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree2, tree2Hash) + + // Let's insert the third tree, which contains a node that's a duplicate + // of an already-inserted node. + storeTapscriptTreeChecked(t, ctx, assetStore, tree3, tree3Hash) + + // Fetching the second and third trees should succeed. + loadTapscriptTreeChecked(t, ctx, assetStore, tree2, tree2Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree3, tree3Hash) + + // Deleting the third tree should not affect the second tree. + deleteTapscriptTreeChecked(t, ctx, assetStore, tree3Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree2, tree2Hash) + + // Let's also test handling of tapscript branches. + storeTapscriptTreeChecked(t, ctx, assetStore, tree4, tree4Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree4, tree4Hash) + + // The second tapscript branch shares a node with the first. + storeTapscriptTreeChecked(t, ctx, assetStore, tree5, tree5Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree4, tree4Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree5, tree5Hash) + + // Deleting the first set of branches should not affect the second. + deleteTapscriptTreeChecked(t, ctx, assetStore, tree4Hash) + loadTapscriptTreeChecked(t, ctx, assetStore, tree5, tree5Hash) +} + func init() { rand.Seed(time.Now().Unix()) diff --git a/tapdb/assets_common.go b/tapdb/assets_common.go index 34d03c8ed..4b7468441 100644 --- a/tapdb/assets_common.go +++ b/tapdb/assets_common.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/proof" @@ -596,3 +597,117 @@ func maybeUpsertAssetMeta(ctx context.Context, db UpsertAssetStore, return assetMetaID, nil } + +// TapscriptTreeStore houses the methods related to storing, fetching, and +// deleting tapscript trees. +type TapscriptTreeStore interface { + // UpsertTapscriptTreeRootHash inserts a new tapscript tree root hash. + UpsertTapscriptTreeRootHash(ctx context.Context, + rootHash TapscriptTreeRootHash) (int64, error) + + // UpsertTapscriptTreeEdge inserts a new tapscript tree edge that + // references both a tapscript tree node and a root hash. + UpsertTapscriptTreeEdge(ctx context.Context, + edge TapscriptTreeEdge) (int64, error) + + // UpsertTapscriptTreeNode inserts a new tapscript tree node. + UpsertTapscriptTreeNode(ctx context.Context, node []byte) (int64, error) + + // FetchTapscriptTree fetches all child nodes of a tapscript tree root + // hash, which can be used to reassemble the tapscript tree. + FetchTapscriptTree(ctx context.Context, + rootHash []byte) ([]TapscriptTreeNode, error) + + // DeleteTapscriptTreeEdges deletes all edges that reference the given + // root hash. + DeleteTapscriptTreeEdges(ctx context.Context, rootHash []byte) error + + // DeleteTapscriptTreeNodes deletes all nodes that are not referenced by + // any edge. + DeleteTapscriptTreeNodes(ctx context.Context) error + + // DeleteTapscriptTreeRoot deletes a tapscript tree root hash. + DeleteTapscriptTreeRoot(ctx context.Context, rootHash []byte) error +} + +// upsertTapscriptTree inserts a tapscript tree into the database, including the +// nodes and edges needed to reassemble the tree. +func upsertTapscriptTree(ctx context.Context, q TapscriptTreeStore, + rootHash []byte, isBranch bool, nodes [][]byte) error { + + if len(rootHash) != chainhash.HashSize { + return fmt.Errorf("root hash must be 32 bytes") + } + + // Perform final sanity checks on the given tapscript tree nodes. + nodeCount := len(nodes) + switch { + case nodeCount == 0: + return fmt.Errorf("no tapscript tree nodes provided") + + case isBranch && nodeCount != 2: + return asset.ErrInvalidTapBranch + } + + // Insert the root hash first. + treeRoot := TapscriptTreeRootHash{ + RootHash: rootHash, + BranchOnly: isBranch, + } + rootId, err := q.UpsertTapscriptTreeRootHash(ctx, treeRoot) + if err != nil { + return err + } + + // Tree node ordering must be preserved. We'll use the loop counter as + // the node index in the tapscript edges we insert. + for i := 0; i < nodeCount; i++ { + nodeId, err := q.UpsertTapscriptTreeNode(ctx, nodes[i]) + if err != nil { + return err + } + + // Link each node to the root hash inserted earlier. + edge := TapscriptTreeEdge{ + RootHashID: rootId, + NodeIndex: int64(i), + RawNodeID: nodeId, + } + _, err = q.UpsertTapscriptTreeEdge(ctx, edge) + if err != nil { + return err + } + } + + return nil +} + +// deleteTapscriptTree deletes a tapscript tree from the database, including the +// edges and all nodes not referenced by another tapscript tree. +func deleteTapscriptTree(ctx context.Context, q TapscriptTreeStore, + rootHash []byte) error { + + // Delete the tree edges first, as they reference both tree nodes and + // the root hash. + err := q.DeleteTapscriptTreeEdges(ctx, rootHash) + if err != nil { + return err + } + + // Any nodes not referenced by another tapscript tree will now not be + // referenced by any edges. Delete all such nodes. This will only affect + // nodes from this tree, as nodes in a properly inserted tree are + // referenced by at least one edge. + err = q.DeleteTapscriptTreeNodes(ctx) + if err != nil { + return err + } + + // With all edges and nodes deleted, delete the root hash. + err = q.DeleteTapscriptTreeRoot(ctx, rootHash) + if err != nil { + return err + } + + return nil +} diff --git a/tapdb/assets_store.go b/tapdb/assets_store.go index de3c8e21f..ab1167463 100644 --- a/tapdb/assets_store.go +++ b/tapdb/assets_store.go @@ -141,6 +141,20 @@ type ( // QueryProofTransAttemptsParams is a type alias for the params needed // to query the proof transfer attempts log. QueryProofTransAttemptsParams = sqlc.QueryProofTransferAttemptsParams + + // TapscriptTreeRootHash is a type alias for the params needed to insert + // a tapscript tree root hash. + TapscriptTreeRootHash = sqlc.UpsertTapscriptTreeRootHashParams + + // TapscriptTreeEdge is a type alias for the params needed to insert an + // edge that links a tapscript tree node to a root hash, and records + // the order of the node in the tapscript tree. + TapscriptTreeEdge = sqlc.UpsertTapscriptTreeEdgeParams + + // TapscriptTreeNode is a type alias for a tapscript tree node returned + // when fetching a tapscript tree, which includes the serialized node + // and the node index in the tree. + TapscriptTreeNode = sqlc.FetchTapscriptTreeRow ) // ActiveAssetsStore is a sub-set of the main sqlc.Querier interface that diff --git a/tapdb/sqlc/assets.sql.go b/tapdb/sqlc/assets.sql.go index 7fc8eb243..18073a4a8 100644 --- a/tapdb/sqlc/assets.sql.go +++ b/tapdb/sqlc/assets.sql.go @@ -87,7 +87,7 @@ func (q *Queries) AllInternalKeys(ctx context.Context) ([]InternalKey, error) { } const allMintingBatches = `-- name: AllMintingBatches :many -SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, key_id, raw_key, key_family, key_index +SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, tapscript_sibling, key_id, raw_key, key_family, key_index FROM asset_minting_batches JOIN internal_keys ON asset_minting_batches.batch_id = internal_keys.key_id @@ -101,6 +101,7 @@ type AllMintingBatchesRow struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte KeyID int64 RawKey []byte KeyFamily int32 @@ -124,6 +125,7 @@ func (q *Queries) AllMintingBatches(ctx context.Context) ([]AllMintingBatchesRow &i.GenesisID, &i.HeightHint, &i.CreationTimeUnix, + &i.TapscriptSibling, &i.KeyID, &i.RawKey, &i.KeyFamily, @@ -328,6 +330,29 @@ func (q *Queries) AssetsInBatch(ctx context.Context, rawKey []byte) ([]AssetsInB return items, nil } +const bindMintingBatchWithTapSibling = `-- name: BindMintingBatchWithTapSibling :exec +WITH target_batch AS ( + SELECT batch_id + FROM asset_minting_batches batches + JOIN internal_keys keys + ON batches.batch_id = keys.key_id + WHERE keys.raw_key = $1 +) +UPDATE asset_minting_batches +SET tapscript_sibling = $2 +WHERE batch_id IN (SELECT batch_id FROM target_batch) +` + +type BindMintingBatchWithTapSiblingParams struct { + RawKey []byte + TapscriptSibling []byte +} + +func (q *Queries) BindMintingBatchWithTapSibling(ctx context.Context, arg BindMintingBatchWithTapSiblingParams) error { + _, err := q.db.ExecContext(ctx, bindMintingBatchWithTapSibling, arg.RawKey, arg.TapscriptSibling) + return err +} + const bindMintingBatchWithTx = `-- name: BindMintingBatchWithTx :exec WITH target_batch AS ( SELECT batch_id @@ -436,6 +461,50 @@ func (q *Queries) DeleteManagedUTXO(ctx context.Context, outpoint []byte) error return err } +const deleteTapscriptTreeEdges = `-- name: DeleteTapscriptTreeEdges :exec +WITH tree_info AS ( + -- This CTE is used to fetch all edges that link the given tapscript tree + -- root hash to child nodes. + SELECT tapscript_edges.edge_id + FROM tapscript_edges + JOIN tapscript_roots + ON tapscript_edges.root_hash_id = tapscript_roots.root_id + WHERE tapscript_roots.root_hash = $1 +) +DELETE FROM tapscript_edges +WHERE edge_id IN (SELECT edge_id FROM tree_info) +` + +func (q *Queries) DeleteTapscriptTreeEdges(ctx context.Context, rootHash []byte) error { + _, err := q.db.ExecContext(ctx, deleteTapscriptTreeEdges, rootHash) + return err +} + +const deleteTapscriptTreeNodes = `-- name: DeleteTapscriptTreeNodes :exec +DELETE FROM tapscript_nodes +WHERE NOT EXISTS ( + SELECT 1 + FROM tapscript_edges + -- Delete any node that is not referenced by any edge. + WHERE tapscript_edges.raw_node_id = tapscript_nodes.node_id +) +` + +func (q *Queries) DeleteTapscriptTreeNodes(ctx context.Context) error { + _, err := q.db.ExecContext(ctx, deleteTapscriptTreeNodes) + return err +} + +const deleteTapscriptTreeRoot = `-- name: DeleteTapscriptTreeRoot :exec +DELETE FROM tapscript_roots +WHERE root_hash = $1 +` + +func (q *Queries) DeleteTapscriptTreeRoot(ctx context.Context, rootHash []byte) error { + _, err := q.db.ExecContext(ctx, deleteTapscriptTreeRoot, rootHash) + return err +} + const deleteUTXOLease = `-- name: DeleteUTXOLease :exec UPDATE managed_utxos SET lease_owner = NULL, lease_expiry = NULL @@ -1320,7 +1389,7 @@ WITH target_batch AS ( ON batches.batch_id = keys.key_id WHERE keys.raw_key = $1 ) -SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, key_id, raw_key, key_family, key_index +SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, tapscript_sibling, key_id, raw_key, key_family, key_index FROM asset_minting_batches batches JOIN internal_keys keys ON batches.batch_id = keys.key_id @@ -1335,6 +1404,7 @@ type FetchMintingBatchRow struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte KeyID int64 RawKey []byte KeyFamily int32 @@ -1352,6 +1422,7 @@ func (q *Queries) FetchMintingBatch(ctx context.Context, rawKey []byte) (FetchMi &i.GenesisID, &i.HeightHint, &i.CreationTimeUnix, + &i.TapscriptSibling, &i.KeyID, &i.RawKey, &i.KeyFamily, @@ -1361,7 +1432,7 @@ func (q *Queries) FetchMintingBatch(ctx context.Context, rawKey []byte) (FetchMi } const fetchMintingBatchesByInverseState = `-- name: FetchMintingBatchesByInverseState :many -SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, key_id, raw_key, key_family, key_index +SELECT batch_id, batch_state, minting_tx_psbt, change_output_index, genesis_id, height_hint, creation_time_unix, tapscript_sibling, key_id, raw_key, key_family, key_index FROM asset_minting_batches batches JOIN internal_keys keys ON batches.batch_id = keys.key_id @@ -1376,6 +1447,7 @@ type FetchMintingBatchesByInverseStateRow struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte KeyID int64 RawKey []byte KeyFamily int32 @@ -1399,6 +1471,7 @@ func (q *Queries) FetchMintingBatchesByInverseState(ctx context.Context, batchSt &i.GenesisID, &i.HeightHint, &i.CreationTimeUnix, + &i.TapscriptSibling, &i.KeyID, &i.RawKey, &i.KeyFamily, @@ -1580,6 +1653,54 @@ func (q *Queries) FetchSeedlingsForBatch(ctx context.Context, rawKey []byte) ([] return items, nil } +const fetchTapscriptTree = `-- name: FetchTapscriptTree :many +WITH tree_info AS ( + -- This CTE is used to fetch all edges that link the given tapscript tree + -- root hash to child nodes. Each edge also contains the index of the child + -- node in the tapscript tree. + SELECT tapscript_roots.branch_only, tapscript_edges.raw_node_id, + tapscript_edges.node_index + FROM tapscript_roots + JOIN tapscript_edges + ON tapscript_roots.root_id = tapscript_edges.root_hash_id + WHERE tapscript_roots.root_hash = $1 +) +SELECT tree_info.branch_only, tapscript_nodes.raw_node +FROM tapscript_nodes +JOIN tree_info + ON tree_info.raw_node_id = tapscript_nodes.node_id +ORDER BY tree_info.node_index ASC +` + +type FetchTapscriptTreeRow struct { + BranchOnly bool + RawNode []byte +} + +// Sort the nodes by node_index here instead of returning the indices. +func (q *Queries) FetchTapscriptTree(ctx context.Context, rootHash []byte) ([]FetchTapscriptTreeRow, error) { + rows, err := q.db.QueryContext(ctx, fetchTapscriptTree, rootHash) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FetchTapscriptTreeRow + for rows.Next() { + var i FetchTapscriptTreeRow + if err := rows.Scan(&i.BranchOnly, &i.RawNode); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const genesisAssets = `-- name: GenesisAssets :many SELECT gen_asset_id, asset_id, asset_tag, meta_data_id, output_index, asset_type, genesis_point_id FROM genesis_assets @@ -2588,3 +2709,72 @@ func (q *Queries) UpsertScriptKey(ctx context.Context, arg UpsertScriptKeyParams err := row.Scan(&script_key_id) return script_key_id, err } + +const upsertTapscriptTreeEdge = `-- name: UpsertTapscriptTreeEdge :one +INSERT INTO tapscript_edges ( + root_hash_id, node_index, raw_node_id +) VALUES ( + $1, $2, $3 +) ON CONFLICT (root_hash_id, node_index, raw_node_id) + -- This is a NOP, root_hash_id, node_index, and raw_node_id are the unique + -- fields that caused the conflict. + DO UPDATE SET root_hash_id = EXCLUDED.root_hash_id, + node_index = EXCLUDED.node_index, raw_node_id = EXCLUDED.raw_node_id +RETURNING edge_id +` + +type UpsertTapscriptTreeEdgeParams struct { + RootHashID int64 + NodeIndex int64 + RawNodeID int64 +} + +func (q *Queries) UpsertTapscriptTreeEdge(ctx context.Context, arg UpsertTapscriptTreeEdgeParams) (int64, error) { + row := q.db.QueryRowContext(ctx, upsertTapscriptTreeEdge, arg.RootHashID, arg.NodeIndex, arg.RawNodeID) + var edge_id int64 + err := row.Scan(&edge_id) + return edge_id, err +} + +const upsertTapscriptTreeNode = `-- name: UpsertTapscriptTreeNode :one +INSERT INTO tapscript_nodes ( + raw_node +) VALUES ( + $1 +) ON CONFLICT (raw_node) + -- This is a NOP, raw_node is the unique field that caused the conflict. + DO UPDATE SET raw_node = EXCLUDED.raw_node +RETURNING node_id +` + +func (q *Queries) UpsertTapscriptTreeNode(ctx context.Context, rawNode []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, upsertTapscriptTreeNode, rawNode) + var node_id int64 + err := row.Scan(&node_id) + return node_id, err +} + +const upsertTapscriptTreeRootHash = `-- name: UpsertTapscriptTreeRootHash :one +INSERT INTO tapscript_roots ( + root_hash, branch_only +) VALUES ( + $1, $2 +) ON CONFLICT (root_hash) + -- This is a NOP, the root_hash is the unique field that caused the + -- conflict. The tree should be deleted before switching between branch and + -- leaf storage for the same root hash. + DO UPDATE SET root_hash = EXCLUDED.root_hash +RETURNING root_id +` + +type UpsertTapscriptTreeRootHashParams struct { + RootHash []byte + BranchOnly bool +} + +func (q *Queries) UpsertTapscriptTreeRootHash(ctx context.Context, arg UpsertTapscriptTreeRootHashParams) (int64, error) { + row := q.db.QueryRowContext(ctx, upsertTapscriptTreeRootHash, arg.RootHash, arg.BranchOnly) + var root_id int64 + err := row.Scan(&root_id) + return root_id, err +} diff --git a/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql b/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql new file mode 100644 index 000000000..2ba9ffbe9 --- /dev/null +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql @@ -0,0 +1,5 @@ +DROP INDEX IF EXISTS tapscript_edges_unique; +DROP TABLE IF EXISTS tapscript_edges; +DROP TABLE IF EXISTS tapscript_nodes; +DROP TABLE IF EXISTS tapscript_roots; +ALTER TABLE asset_minting_batches DROP COLUMN tapscript_sibling; \ No newline at end of file diff --git a/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql b/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql new file mode 100644 index 000000000..4c0bd0021 --- /dev/null +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql @@ -0,0 +1,48 @@ +-- The tapscript sibling is an optional hash that represents the root hash of a +-- tapscript tree. On batch finalization, this hash is used with the Taproot +-- Asset commitment to create the Taproot output key of the genesis output. +ALTER TABLE asset_minting_batches ADD COLUMN tapscript_sibling BLOB; + +-- This table stores root hashes for tapscript trees, and a flag to ensure that +-- the stored tree nodes are decoded correctly. +CREATE TABLE IF NOT EXISTS tapscript_roots ( + root_id BIGINT PRIMARY KEY, + + -- The root hash of a tapscript tree. + root_hash BLOB NOT NULL UNIQUE CHECK(length(root_hash) = 32), + + -- A flag to record if a tapscript tree was stored as two tapHashes, or + -- a set of tapLeafs. + branch_only BOOLEAN NOT NULL DEFAULT FALSE +); + +-- This table stores tapscript nodes, which are tapHashes or tapLeafs. A node +-- may be included in multiple tapscript trees. +CREATE TABLE IF NOT EXISTS tapscript_nodes ( + node_id BIGINT PRIMARY KEY, + + -- The serialized tapscript node, which may be a tapHash or tapLeaf. + raw_node BLOB NOT NULL UNIQUE +); + +-- This table stores tapscript edges, which link a serialized tapscript node +-- to a tapscript tree root hash and preserve the node ordering in the tree. +CREATE TABLE IF NOT EXISTS tapscript_edges ( + edge_id BIGINT PRIMARY KEY, + + -- The root hash of a tree that includes the referenced tapscript node. + root_hash_id BIGINT NOT NULL REFERENCES tapscript_roots(root_id), + + -- The index of the referenced node in the tapscript tree, which is + -- needed to correctly reconstruct the tapscript tree. + node_index BIGINT NOT NULL, + + -- The tapscript node referenced by this edge. + raw_node_id BIGINT NOT NULL REFERENCES tapscript_nodes(node_id) +); + +-- A leaf can be repeated within a tree, and shared amongst trees, but there can +-- only be one leaf at a given index in a tree. +CREATE UNIQUE INDEX tapscript_edges_unique ON tapscript_edges ( + root_hash_id, node_index, raw_node_id +); diff --git a/tapdb/sqlc/models.go b/tapdb/sqlc/models.go index cebf2d494..5ee64cd12 100644 --- a/tapdb/sqlc/models.go +++ b/tapdb/sqlc/models.go @@ -77,6 +77,7 @@ type AssetMintingBatch struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte } type AssetProof struct { @@ -304,6 +305,24 @@ type ScriptKey struct { Tweak []byte } +type TapscriptEdge struct { + EdgeID int64 + RootHashID int64 + NodeIndex int64 + RawNodeID int64 +} + +type TapscriptNode struct { + NodeID int64 + RawNode []byte +} + +type TapscriptRoot struct { + RootID int64 + RootHash []byte + BranchOnly bool +} + type UniverseEvent struct { EventID int64 EventType string diff --git a/tapdb/sqlc/querier.go b/tapdb/sqlc/querier.go index bc018d91e..e43bf3267 100644 --- a/tapdb/sqlc/querier.go +++ b/tapdb/sqlc/querier.go @@ -19,6 +19,7 @@ type Querier interface { ApplyPendingOutput(ctx context.Context, arg ApplyPendingOutputParams) (int64, error) AssetsByGenesisPoint(ctx context.Context, prevOut []byte) ([]AssetsByGenesisPointRow, error) AssetsInBatch(ctx context.Context, rawKey []byte) ([]AssetsInBatchRow, error) + BindMintingBatchWithTapSibling(ctx context.Context, arg BindMintingBatchWithTapSiblingParams) error BindMintingBatchWithTx(ctx context.Context, arg BindMintingBatchWithTxParams) error ConfirmChainAnchorTx(ctx context.Context, arg ConfirmChainAnchorTxParams) error ConfirmChainTx(ctx context.Context, arg ConfirmChainTxParams) error @@ -30,6 +31,9 @@ type Querier interface { DeleteMultiverseLeaf(ctx context.Context, arg DeleteMultiverseLeafParams) error DeleteNode(ctx context.Context, arg DeleteNodeParams) (int64, error) DeleteRoot(ctx context.Context, namespace string) (int64, error) + DeleteTapscriptTreeEdges(ctx context.Context, rootHash []byte) error + DeleteTapscriptTreeNodes(ctx context.Context) error + DeleteTapscriptTreeRoot(ctx context.Context, rootHash []byte) error DeleteUTXOLease(ctx context.Context, outpoint []byte) error DeleteUniverseEvents(ctx context.Context, namespaceRoot string) error DeleteUniverseLeaves(ctx context.Context, namespace string) error @@ -75,6 +79,8 @@ type Querier interface { FetchSeedlingByID(ctx context.Context, seedlingID int64) (AssetSeedling, error) FetchSeedlingID(ctx context.Context, arg FetchSeedlingIDParams) (int64, error) FetchSeedlingsForBatch(ctx context.Context, rawKey []byte) ([]FetchSeedlingsForBatchRow, error) + // Sort the nodes by node_index here instead of returning the indices. + FetchTapscriptTree(ctx context.Context, rootHash []byte) ([]FetchTapscriptTreeRow, error) FetchTransferInputs(ctx context.Context, transferID int64) ([]FetchTransferInputsRow, error) FetchTransferOutputs(ctx context.Context, transferID int64) ([]FetchTransferOutputsRow, error) FetchUniverseKeys(ctx context.Context, arg FetchUniverseKeysParams) ([]FetchUniverseKeysRow, error) @@ -165,6 +171,9 @@ type Querier interface { UpsertMultiverseRoot(ctx context.Context, arg UpsertMultiverseRootParams) (int64, error) UpsertRootNode(ctx context.Context, arg UpsertRootNodeParams) error UpsertScriptKey(ctx context.Context, arg UpsertScriptKeyParams) (int64, error) + UpsertTapscriptTreeEdge(ctx context.Context, arg UpsertTapscriptTreeEdgeParams) (int64, error) + UpsertTapscriptTreeNode(ctx context.Context, rawNode []byte) (int64, error) + UpsertTapscriptTreeRootHash(ctx context.Context, arg UpsertTapscriptTreeRootHashParams) (int64, error) UpsertUniverseLeaf(ctx context.Context, arg UpsertUniverseLeafParams) error UpsertUniverseRoot(ctx context.Context, arg UpsertUniverseRootParams) (int64, error) } diff --git a/tapdb/sqlc/queries/assets.sql b/tapdb/sqlc/queries/assets.sql index d81d879aa..afa327d55 100644 --- a/tapdb/sqlc/queries/assets.sql +++ b/tapdb/sqlc/queries/assets.sql @@ -467,6 +467,18 @@ UPDATE asset_minting_batches SET minting_tx_psbt = $2, change_output_index = $3, genesis_id = $4 WHERE batch_id IN (SELECT batch_id FROM target_batch); +-- name: BindMintingBatchWithTapSibling :exec +WITH target_batch AS ( + SELECT batch_id + FROM asset_minting_batches batches + JOIN internal_keys keys + ON batches.batch_id = keys.key_id + WHERE keys.raw_key = $1 +) +UPDATE asset_minting_batches +SET tapscript_sibling = $2 +WHERE batch_id IN (SELECT batch_id FROM target_batch); + -- name: UpdateBatchGenesisTx :exec WITH target_batch AS ( SELECT batch_id @@ -795,6 +807,85 @@ SELECT key_family, key_index FROM internal_keys WHERE raw_key = $1; +-- name: UpsertTapscriptTreeRootHash :one +INSERT INTO tapscript_roots ( + root_hash, branch_only +) VALUES ( + $1, $2 +) ON CONFLICT (root_hash) + -- This is a NOP, the root_hash is the unique field that caused the + -- conflict. The tree should be deleted before switching between branch and + -- leaf storage for the same root hash. + DO UPDATE SET root_hash = EXCLUDED.root_hash +RETURNING root_id; + +-- name: UpsertTapscriptTreeNode :one +INSERT INTO tapscript_nodes ( + raw_node +) VALUES ( + $1 +) ON CONFLICT (raw_node) + -- This is a NOP, raw_node is the unique field that caused the conflict. + DO UPDATE SET raw_node = EXCLUDED.raw_node +RETURNING node_id; + +-- name: UpsertTapscriptTreeEdge :one +INSERT INTO tapscript_edges ( + root_hash_id, node_index, raw_node_id +) VALUES ( + $1, $2, $3 +) ON CONFLICT (root_hash_id, node_index, raw_node_id) + -- This is a NOP, root_hash_id, node_index, and raw_node_id are the unique + -- fields that caused the conflict. + DO UPDATE SET root_hash_id = EXCLUDED.root_hash_id, + node_index = EXCLUDED.node_index, raw_node_id = EXCLUDED.raw_node_id +RETURNING edge_id; + +-- name: FetchTapscriptTree :many +WITH tree_info AS ( + -- This CTE is used to fetch all edges that link the given tapscript tree + -- root hash to child nodes. Each edge also contains the index of the child + -- node in the tapscript tree. + SELECT tapscript_roots.branch_only, tapscript_edges.raw_node_id, + tapscript_edges.node_index + FROM tapscript_roots + JOIN tapscript_edges + ON tapscript_roots.root_id = tapscript_edges.root_hash_id + WHERE tapscript_roots.root_hash = @root_hash +) +SELECT tree_info.branch_only, tapscript_nodes.raw_node +FROM tapscript_nodes +JOIN tree_info + ON tree_info.raw_node_id = tapscript_nodes.node_id +-- Sort the nodes by node_index here instead of returning the indices. +ORDER BY tree_info.node_index ASC; + +-- name: DeleteTapscriptTreeEdges :exec +WITH tree_info AS ( + -- This CTE is used to fetch all edges that link the given tapscript tree + -- root hash to child nodes. + SELECT tapscript_edges.edge_id + FROM tapscript_edges + JOIN tapscript_roots + ON tapscript_edges.root_hash_id = tapscript_roots.root_id + WHERE tapscript_roots.root_hash = @root_hash +) +DELETE FROM tapscript_edges +WHERE edge_id IN (SELECT edge_id FROM tree_info); + +-- name: DeleteTapscriptTreeNodes :exec +DELETE FROM tapscript_nodes +WHERE NOT EXISTS ( + SELECT 1 + FROM tapscript_edges + -- Delete any node that is not referenced by any edge. + WHERE tapscript_edges.raw_node_id = tapscript_nodes.node_id +); + +-- name: DeleteTapscriptTreeRoot :exec +DELETE FROM tapscript_roots +WHERE root_hash = @root_hash; + -- name: FetchGenesisByAssetID :one SELECT * FROM genesis_info_view diff --git a/tapgarden/batch.go b/tapgarden/batch.go index 4db82b374..689cddbad 100644 --- a/tapgarden/batch.go +++ b/tapgarden/batch.go @@ -6,6 +6,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -67,6 +68,10 @@ type MintingBatch struct { // to commit to the Taproot Asset commitment above. mintingPubKey *btcec.PublicKey + // tapSibling is an optional root hash of a tapscript tree that will be + // used with the taprootAssetScriptRoot to construct the mintingPubKey. + tapSibling *chainhash.Hash + // taprootAssetScriptRoot is the root hash of the Taproot Asset // commitment. If this is nil, then the mintingPubKey will be as well. taprootAssetScriptRoot []byte @@ -104,7 +109,9 @@ func (m *MintingBatch) validateGroupAnchor(s *Seedling) error { // MintingOutputKey derives the output key that once mined, will commit to the // Taproot asset root, thereby creating the set of included assets. -func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { +func (m *MintingBatch) MintingOutputKey(sibling *commitment.TapscriptPreimage) ( + *btcec.PublicKey, []byte, error) { + if m.mintingPubKey != nil { return m.mintingPubKey, m.taprootAssetScriptRoot, nil } @@ -113,7 +120,21 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { return nil, nil, fmt.Errorf("no asset commitment present") } - taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot(nil) + var ( + siblingHash *chainhash.Hash + err error + ) + + if sibling != nil { + siblingHash, err = sibling.TapHash() + if err != nil { + return nil, nil, err + } + } + + taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot( + siblingHash, + ) m.taprootAssetScriptRoot = taprootAssetScriptRoot[:] m.mintingPubKey = txscript.ComputeTaprootOutputKey( @@ -125,8 +146,10 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { // genesisScript returns the script that should be placed in the minting output // within the genesis transaction. -func (m *MintingBatch) genesisScript() ([]byte, error) { - mintingOutputKey, _, err := m.MintingOutputKey() +func (m *MintingBatch) genesisScript(sibling *commitment.TapscriptPreimage) ( + []byte, error) { + + mintingOutputKey, _, err := m.MintingOutputKey(sibling) if err != nil { return nil, err } @@ -149,3 +172,18 @@ func (m *MintingBatch) State() BatchState { func (m *MintingBatch) UpdateState(state BatchState) { m.batchState.Store(uint32(state)) } + +// TapSibling returns the optional tapscript sibling for the batch, which is a +// root hash of a tapscript tree. +func (m *MintingBatch) TapSibling() []byte { + if m.tapSibling == nil { + return nil + } + + return m.tapSibling.CloneBytes() +} + +// UpdateTapSibling updates the optional tapscript sibling for the batch. +func (m *MintingBatch) UpdateTapSibling(sibling *chainhash.Hash) { + m.tapSibling = sibling +} diff --git a/tapgarden/caretaker.go b/tapgarden/caretaker.go index b9f6d2db9..9f673de71 100644 --- a/tapgarden/caretaker.go +++ b/tapgarden/caretaker.go @@ -679,10 +679,28 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) b.cfg.Batch.RootAssetCommitment = tapCommitment + // Fetch the optional Tapscript sibling for this batch, and + // convert it to a TapscriptPreimage. + var batchSibling *commitment.TapscriptPreimage + if b.cfg.Batch.tapSibling != nil { + tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree( + ctx, *b.cfg.Batch.tapSibling, + ) + if err != nil { + return 0, err + } + + batchSibling, err = commitment. + NewPreimageFromTapscriptTreeNodes(*tapSibling) + if err != nil { + return 0, err + } + } + // With the commitment Taproot Asset root SMT constructed, we'll // map that into the tapscript root we'll insert into the // genesis transaction. - genesisScript, err := b.cfg.Batch.genesisScript() + genesisScript, err := b.cfg.Batch.genesisScript(batchSibling) if err != nil { return 0, fmt.Errorf("unable to create genesis "+ "script: %w", err) @@ -776,18 +794,51 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) // At this point we have a fully signed PSBT packet which'll // create our set of assets once mined. We'll write this to - // disk, then import the public key into the wallet. + // disk, then import the public key into the wallet. The sibling + // here can always be nil as we'll fetch the output key computed + // previously in BatchStateFrozen. // // TODO(roasbeef): re-run during the broadcast phase to ensure // it's fully imported? - mintingOutputKey, tapRoot, err := b.cfg.Batch.MintingOutputKey() + mintingOutputKey, merkleRoot, err := b.cfg.Batch. + MintingOutputKey(nil) if err != nil { return 0, err } + + // To spend this output in the future, we must also commit the + // Taproot Asset commitment root and batch tapscript sibling. + tapCommitmentRoot := b.cfg.Batch.RootAssetCommitment. + TapscriptRoot(nil) + + // Fetch the optional Tapscript sibling for this batch, and + // encode it to bytes. + var siblingBytes []byte + if b.cfg.Batch.tapSibling != nil { + tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree( + ctx, *b.cfg.Batch.tapSibling, + ) + if err != nil { + return 0, err + } + + batchSibling, err := commitment. + NewPreimageFromTapscriptTreeNodes(*tapSibling) + if err != nil { + return 0, err + } + + siblingBytes, _, err = commitment. + MaybeEncodeTapscriptPreimage(batchSibling) + if err != nil { + return 0, err + } + } + err = b.cfg.Log.CommitSignedGenesisTx( ctx, b.cfg.Batch.BatchKey.PubKey, b.cfg.Batch.GenesisPacket, b.anchorOutputIndex, - tapRoot, + merkleRoot, tapCommitmentRoot[:], siblingBytes, ) if err != nil { return 0, fmt.Errorf("unable to commit genesis "+ @@ -976,6 +1027,24 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) groupVerifier := GenGroupVerifier(ctx, b.cfg.Log) groupAnchorVerifier := GenGroupAnchorVerifier(ctx, b.cfg.Log) + // Fetch the optional tapscript sibling for this batch, which + // is needed to construct valid inclusion proofs. + var batchSibling *commitment.TapscriptPreimage + if b.cfg.Batch.tapSibling != nil { + tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree( + ctx, *b.cfg.Batch.tapSibling, + ) + if err != nil { + return 0, err + } + + batchSibling, err = commitment. + NewPreimageFromTapscriptTreeNodes(*tapSibling) + if err != nil { + return 0, err + } + } + // Now that the minting transaction has been confirmed, we'll // need to create the series of proof file blobs for each of // the assets. In case the lnd wallet creates a P2TR change @@ -990,6 +1059,7 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) TxIndex: int(confInfo.TxIndex), OutputIndex: int(b.anchorOutputIndex), InternalKey: b.cfg.Batch.BatchKey.PubKey, + TapscriptSibling: batchSibling, TaprootAssetRoot: batchCommitment, }, GenesisPoint: extractGenesisOutpoint( @@ -1011,6 +1081,7 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) baseProof, headerVerifier, groupVerifier, groupAnchorVerifier, proof.WithAssetMetaReveals(b.cfg.Batch.AssetMetas), + proof.WithSiblingPreimage(batchSibling), ) if err != nil { return 0, fmt.Errorf("unable to construct minting "+ diff --git a/tapgarden/interface.go b/tapgarden/interface.go index a586ca35f..cdd907676 100644 --- a/tapgarden/interface.go +++ b/tapgarden/interface.go @@ -44,7 +44,7 @@ type Planter interface { // FinalizeBatch signals that the asset minter should finalize // the current batch, if one exists. - FinalizeBatch(feeRate *chainfee.SatPerKWeight) (*MintingBatch, error) + FinalizeBatch(params FinalizeParams) (*MintingBatch, error) // CancelBatch signals that the asset minter should cancel the // current batch, if one exists. @@ -169,6 +169,8 @@ func NewBatchState(state uint8) (BatchState, error) { // the process of seeding, planting, and finally maturing taproot assets that are // a part of the batch. type MintingStore interface { + asset.TapscriptTreeManager + // CommitMintingBatch commits a new minting batch to disk, identified // by its batch key. CommitMintingBatch(ctx context.Context, newBatch *MintingBatch) error @@ -215,7 +217,7 @@ type MintingStore interface { // state upon a successful call. CommitSignedGenesisTx(ctx context.Context, batchKey *btcec.PublicKey, genesisTx *FundedPsbt, anchorOutputIndex uint32, - tapRoot []byte) error + merkleRoot, tapTreeRoot, tapSibling []byte) error // MarkBatchConfirmed marks the batch as confirmed on chain. The passed // block location information determines where exactly in the chain the @@ -236,6 +238,14 @@ type MintingStore interface { // key, including the genesis information used to create the group. FetchGroupByGroupKey(ctx context.Context, groupKey *btcec.PublicKey) (*asset.AssetGroup, error) + + // CommitBatchTapSibling adds a tapscript sibling to the batch, + // specified by the sibling root hash. + // + // NOTE: The tapscript tree that defines the batch sibling must already + // be committed to disk. + CommitBatchTapSibling(ctx context.Context, batchKey *btcec.PublicKey, + rootHash *chainhash.Hash) error } // ChainBridge is our bridge to the target chain. It's used to get confirmation diff --git a/tapgarden/mock.go b/tapgarden/mock.go index 831ee8f02..3ff14e347 100644 --- a/tapgarden/mock.go +++ b/tapgarden/mock.go @@ -633,3 +633,42 @@ func (m *MockProofWatcher) DefaultUpdateCallback() proof.UpdateCallback { return nil } } + +type FallibleTapscriptTreeMgr struct { + store MintingStore + FailLoad, FailStore bool +} + +func (mgr FallibleTapscriptTreeMgr) DeleteTapscriptTree(ctx context.Context, + rootHash chainhash.Hash) error { + + return mgr.store.DeleteTapscriptTree(ctx, rootHash) +} + +func (mgr FallibleTapscriptTreeMgr) LoadTapscriptTree(ctx context.Context, + rootHash chainhash.Hash) (*asset.TapscriptTreeNodes, error) { + + if mgr.FailLoad { + return nil, fmt.Errorf("failed to load tapscript tree") + } + + return mgr.store.LoadTapscriptTree(ctx, rootHash) +} + +func (mgr FallibleTapscriptTreeMgr) StoreTapscriptTree(ctx context.Context, + treeNodes asset.TapscriptTreeNodes) (*chainhash.Hash, error) { + + if mgr.FailStore { + return nil, fmt.Errorf("unable to store tapscript tree") + } + + return mgr.store.StoreTapscriptTree(ctx, treeNodes) +} + +func NewFallibleTapscriptTreeMgr(store MintingStore) FallibleTapscriptTreeMgr { + return FallibleTapscriptTreeMgr{ + store: store, + } +} + +var _ asset.TapscriptTreeManager = (*FallibleTapscriptTreeMgr)(nil) diff --git a/tapgarden/planter.go b/tapgarden/planter.go index e88d566cf..d38cda655 100644 --- a/tapgarden/planter.go +++ b/tapgarden/planter.go @@ -8,6 +8,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightninglabs/taproot-assets/asset" @@ -34,6 +35,10 @@ type GardenKit struct { // various states the planter will progress it through. Log MintingStore + // TreeStore provides access to optional tapscript trees used with + // script keys, minting output keys, and group keys. + TreeStore asset.TapscriptTreeManager + // KeyRing is used for obtaining internal keys for the anchor // transaction, as well as script keys for each asset and group keys // for assets created that permit ongoing emission. @@ -123,6 +128,13 @@ type stateParamReq[T, S any] struct { param S } +// FinalizeParams are the options available to change how a batch is finalized, +// and how the genesis TX is constructed. +type FinalizeParams struct { + FeeRate fn.Option[chainfee.SatPerKWeight] + SiblingTapTree fn.Option[asset.TapscriptTreeNodes] +} + func newStateParamReq[T, S any](req reqType, param S) *stateParamReq[T, S] { return &stateParamReq[T, S]{ stateReq: *newStateReq[T](req), @@ -358,7 +370,7 @@ func freezeMintingBatch(ctx context.Context, batchStore MintingStore, batchKey := batch.BatchKey.PubKey - log.Infof("Freezing MintingBatch(key=%x, num_assets=%v", + log.Infof("Freezing MintingBatch(key=%x, num_assets=%v)", batchKey.SerializeCompressed(), len(batch.Seedlings)) // In order to freeze a batch, we need to update the state of the batch @@ -370,6 +382,17 @@ func freezeMintingBatch(ctx context.Context, batchStore MintingStore, ) } +func commitBatchSibling(ctx context.Context, batchStore MintingStore, + batch *MintingBatch, sibling *chainhash.Hash) error { + + batchKey := batch.BatchKey.PubKey + + log.Infof("Committing tapscript sibling hash(batch_key=%x, sibling=%x)", + batchKey.SerializeCompressed(), sibling[:]) + + return batchStore.CommitBatchTapSibling(ctx, batchKey, sibling) +} + // ListBatches returns the single batch specified by the batch key, or the set // of batches not yet finalized on disk. func listBatches(ctx context.Context, batchStore MintingStore, @@ -496,7 +519,14 @@ func (c *ChainPlanter) gardener() { continue } - _, err := c.finalizeBatch(nil) + defaultFeeRate := fn.None[chainfee.SatPerKWeight]() + emptyTapSibling := fn.None[asset.TapscriptTreeNodes]() + + defaultFinalizeParams := FinalizeParams{ + FeeRate: defaultFeeRate, + SiblingTapTree: emptyTapSibling, + } + _, err := c.finalizeBatch(defaultFinalizeParams) if err != nil { c.cfg.ErrChan <- fmt.Errorf("unable to freeze "+ "minting batch: %w", err) @@ -601,20 +631,23 @@ func (c *ChainPlanter) gardener() { batchKeySerial := asset.ToSerialized(batchKey) log.Infof("Finalizing batch %x", batchKeySerial) - feeRate, err := - typedParam[*chainfee.SatPerKWeight](req) + finalizeReqParams, err := + typedParam[FinalizeParams](req) if err != nil { - req.Error(fmt.Errorf("bad fee rate: "+ - "%w", err)) + req.Error(fmt.Errorf("bad finalize "+ + "params: %w", err)) break } - caretaker, err := c.finalizeBatch(*feeRate) + caretaker, err := c.finalizeBatch( + *finalizeReqParams, + ) if err != nil { - c.cfg.ErrChan <- fmt.Errorf("unable "+ - "to freeze minting batch: %w", - err) - continue + freezeErr := fmt.Errorf("unable to "+ + "freeze minting batch: %w", err) + c.cfg.ErrChan <- freezeErr + req.Error(freezeErr) + break } // We now wait for the caretaker to either @@ -672,13 +705,51 @@ func (c *ChainPlanter) gardener() { } // finalizeBatch creates a new caretaker for the batch and starts it. -func (c *ChainPlanter) finalizeBatch( - feeRate *chainfee.SatPerKWeight) (*BatchCaretaker, error) { +func (c *ChainPlanter) finalizeBatch(params FinalizeParams) (*BatchCaretaker, + error) { + + var ( + feeRate *chainfee.SatPerKWeight + rootHash *chainhash.Hash + err error + ) + + ctx, cancel := c.WithCtxQuit() + defer cancel() + // If a tapscript tree was specified for this batch, we'll store it on + // disk. The caretaker we start for this batch will use it when deriving + // the final Taproot output key. + params.FeeRate.WhenSome(func(fr chainfee.SatPerKWeight) { + feeRate = &fr + }) + params.SiblingTapTree.WhenSome(func(tn asset.TapscriptTreeNodes) { + rootHash, err = c.cfg.TreeStore. + StoreTapscriptTree(ctx, tn) + }) + + if err != nil { + return nil, fmt.Errorf("unable to store tapscript "+ + "tree for minting batch: %w", err) + } + + if rootHash != nil { + ctx, cancel = c.WithCtxQuit() + defer cancel() + err = commitBatchSibling( + ctx, c.cfg.Log, c.pendingBatch, rootHash, + ) + if err != nil { + return nil, fmt.Errorf("unable to commit tapscript "+ + "sibling for minting batch %w", err) + } + } + + c.pendingBatch.tapSibling = rootHash // At this point, we have a non-empty batch, so we'll first finalize it // on disk. This means no further seedlings can be added to this batch. - ctx, cancel := c.WithCtxQuit() - err := freezeMintingBatch(ctx, c.cfg.Log, c.pendingBatch) + ctx, cancel = c.WithCtxQuit() + err = freezeMintingBatch(ctx, c.cfg.Log, c.pendingBatch) cancel() if err != nil { return nil, fmt.Errorf("unable to freeze minting batch: %w", @@ -736,10 +807,10 @@ func (c *ChainPlanter) ListBatches(batchKey *btcec.PublicKey) ([]*MintingBatch, } // FinalizeBatch sends a signal to the planter to finalize the current batch. -func (c *ChainPlanter) FinalizeBatch( - feeRate *chainfee.SatPerKWeight) (*MintingBatch, error) { +func (c *ChainPlanter) FinalizeBatch(params FinalizeParams) (*MintingBatch, + error) { - req := newStateParamReq[*MintingBatch](reqTypeFinalizeBatch, feeRate) + req := newStateParamReq[*MintingBatch](reqTypeFinalizeBatch, params) if !fn.SendOrQuit[stateRequest](c.stateReqs, req, c.Quit) { return nil, fmt.Errorf("chain planter shutting down") diff --git a/tapgarden/planter_test.go b/tapgarden/planter_test.go index aaa7ffc6a..c1c9e2e2d 100644 --- a/tapgarden/planter_test.go +++ b/tapgarden/planter_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "database/sql" + "encoding/binary" "encoding/hex" "fmt" "math/rand" @@ -21,6 +22,7 @@ import ( "github.com/davecgh/go-spew/spew" tap "github.com/lightninglabs/taproot-assets" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/internal/test" "github.com/lightninglabs/taproot-assets/proof" @@ -32,6 +34,7 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/ticker" "github.com/stretchr/testify/require" ) @@ -66,6 +69,8 @@ type mintingTestHarness struct { store tapgarden.MintingStore + treeStore *tapgarden.FallibleTapscriptTreeMgr + keyRing *tapgarden.MockKeyRing genSigner *tapgarden.MockGenSigner @@ -96,10 +101,12 @@ func newMintingTestHarness(t *testing.T, store tapgarden.MintingStore, keyRing := tapgarden.NewMockKeyRing() genSigner := tapgarden.NewMockGenSigner(keyRing) + treeMgr := tapgarden.NewFallibleTapscriptTreeMgr(store) return &mintingTestHarness{ T: t, store: store, + treeStore: &treeMgr, ticker: ticker.NewForce(interval), wallet: tapgarden.NewMockWalletAnchor(), chain: tapgarden.NewMockChainBridge(), @@ -126,6 +133,7 @@ func (t *mintingTestHarness) refreshChainPlanter() { Wallet: t.wallet, ChainBridge: t.chain, Log: t.store, + TreeStore: t.treeStore, KeyRing: t.keyRing, GenSigner: t.genSigner, GenTxBuilder: t.genTxBuilder, @@ -239,7 +247,7 @@ type FinalizeBatchResp struct { // finalizeBatch uses the public FinalizeBatch planter call to start a caretaker // for an existing batch. The caller must wait for the planter call to complete. func (t *mintingTestHarness) finalizeBatch(wg *sync.WaitGroup, - respChan chan *FinalizeBatchResp) { + respChan chan *FinalizeBatchResp, params *tapgarden.FinalizeParams) { t.Helper() @@ -247,7 +255,18 @@ func (t *mintingTestHarness) finalizeBatch(wg *sync.WaitGroup, go func() { defer wg.Done() - frozenBatch, finalizeErr := t.planter.FinalizeBatch(nil) + finalizeParams := tapgarden.FinalizeParams{ + FeeRate: fn.None[chainfee.SatPerKWeight](), + SiblingTapTree: fn.None[asset.TapscriptTreeNodes](), + } + + if params != nil { + finalizeParams = *params + } + + frozenBatch, finalizeErr := t.planter.FinalizeBatch( + finalizeParams, + ) resp := &FinalizeBatchResp{ Batch: frozenBatch, Err: finalizeErr, @@ -258,7 +277,8 @@ func (t *mintingTestHarness) finalizeBatch(wg *sync.WaitGroup, } func (t *mintingTestHarness) assertFinalizeBatch(wg *sync.WaitGroup, - respChan chan *FinalizeBatchResp, errString string) { + respChan chan *FinalizeBatchResp, + errString string) *tapgarden.MintingBatch { t.Helper() @@ -268,16 +288,18 @@ func (t *mintingTestHarness) assertFinalizeBatch(wg *sync.WaitGroup, switch { case errString == "": require.NoError(t, finalizeResp.Err) + return finalizeResp.Batch default: require.ErrorContains(t, finalizeResp.Err, errString) + return nil } } // progressCaretaker uses the mock interfaces to progress a caretaker from start // to TX confirmation. -func (t *mintingTestHarness) progressCaretaker( - seedlings []*tapgarden.Seedling) func() { +func (t *mintingTestHarness) progressCaretaker(seedlings []*tapgarden.Seedling, + batchSibling *commitment.TapscriptPreimage) func() { // Assert that the caretaker has requested a genesis TX to be funded. _ = t.assertGenesisTxFunded() @@ -295,7 +317,7 @@ func (t *mintingTestHarness) progressCaretaker( // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(batchSibling) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -626,7 +648,9 @@ func (t *mintingTestHarness) assertSeedlingsMatchSprouts( // assertGenesisPsbtFinalized asserts that a request to finalize the genesis // transaction has been requested by a caretaker. -func (t *mintingTestHarness) assertGenesisPsbtFinalized() { +func (t *mintingTestHarness) assertGenesisPsbtFinalized( + sibling *commitment.TapscriptPreimage) { + t.Helper() // Ensure that a request to finalize the PSBt has come across. @@ -650,7 +674,7 @@ func (t *mintingTestHarness) assertGenesisPsbtFinalized() { // The minting key of the batch should match the public key // that was inserted into the wallet. - batchKey, _, err := pendingBatch.MintingOutputKey() + batchKey, _, err := pendingBatch.MintingOutputKey(sibling) require.NoError(t, err) importedKey, err := fn.RecvOrTimeout( @@ -791,7 +815,7 @@ func testBasicAssetCreation(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -893,7 +917,7 @@ func testMintingTicker(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -1029,7 +1053,7 @@ func testMintingCancelFinalize(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -1108,7 +1132,7 @@ func testFinalizeBatch(t *mintingTestHarness) { ) // Finalize the pending batch to start a caretaker. - t.finalizeBatch(&wg, respChan) + t.finalizeBatch(&wg, respChan, nil) batchCount++ _, err := fn.RecvOrTimeout( @@ -1134,10 +1158,10 @@ func testFinalizeBatch(t *mintingTestHarness) { // caretaker to TX confirmation. The finalize call should report no // error, but the caretaker should propagate the confirmation error to // the shared error channel. - t.finalizeBatch(&wg, respChan) + t.finalizeBatch(&wg, respChan, nil) batchCount++ - _ = t.progressCaretaker(seedlings) + _ = t.progressCaretaker(seedlings, nil) caretakerCount++ t.assertFinalizeBatch(&wg, respChan, "") @@ -1158,10 +1182,10 @@ func testFinalizeBatch(t *mintingTestHarness) { t.chain.EmptyConf(true) // Start a new caretaker that should reach TX broadcast. - t.finalizeBatch(&wg, respChan) + t.finalizeBatch(&wg, respChan, nil) batchCount++ - sendConfNtfn := t.progressCaretaker(seedlings) + sendConfNtfn := t.progressCaretaker(seedlings, nil) caretakerCount++ // Trigger the confirmation event, which should cause the caretaker to @@ -1180,7 +1204,7 @@ func testFinalizeBatch(t *mintingTestHarness) { // If we try to finalize without a pending batch, the finalize call // should return an error. - t.finalizeBatch(&wg, respChan) + t.finalizeBatch(&wg, respChan, nil) t.assertFinalizeBatch(&wg, respChan, "no pending batch") t.assertNumCaretakersActive(caretakerCount) @@ -1188,10 +1212,10 @@ func testFinalizeBatch(t *mintingTestHarness) { seedlings = t.queueInitialBatch(numSeedlings) t.chain.EmptyConf(false) - t.finalizeBatch(&wg, respChan) + t.finalizeBatch(&wg, respChan, nil) batchCount++ - sendConfNtfn = t.progressCaretaker(seedlings) + sendConfNtfn = t.progressCaretaker(seedlings, nil) sendConfNtfn() t.assertFinalizeBatch(&wg, respChan, "") @@ -1201,6 +1225,156 @@ func testFinalizeBatch(t *mintingTestHarness) { t.assertLastBatchState(batchCount, tapgarden.BatchStateFinalized) } +func testFinalizeWithTapscriptTree(t *mintingTestHarness) { + // First, create a new chain planter instance using the supplied test + // harness. + t.refreshChainPlanter() + + // Create an initial batch of 5 seedlings. + const numSeedlings = 5 + seedlings := t.queueInitialBatch(numSeedlings) + + var ( + wg sync.WaitGroup + respChan = make(chan *FinalizeBatchResp, 1) + finalizeReq tapgarden.FinalizeParams + batchCount = 0 + ) + + // Build a standalone tapscript tree object, that matches the tree + // created by other test helpers. + sigLockKey := test.RandPubKey(t) + hashLockWitness := []byte("foobar") + hashLockLeaf := test.ScriptHashLock(t.T, hashLockWitness) + sigLeaf := test.ScriptSchnorrSig(t.T, sigLockKey) + tapTreePreimage, err := asset.TapTreeNodesFromLeaves( + []txscript.TapLeaf{hashLockLeaf, sigLeaf}, + ) + require.NoError(t, err) + + finalizeReq = tapgarden.FinalizeParams{ + SiblingTapTree: fn.Some(*tapTreePreimage), + } + + // Force tapscript tree storage to fail, which should cause batch + // finalization to fail. + t.treeStore.FailStore = true + t.finalizeBatch(&wg, respChan, &finalizeReq) + finalizeErr := <-respChan + require.ErrorContains(t, finalizeErr.Err, "unable to store") + + // Empty the main error channel before reattempting a mint. + select { + case <-t.errChan: + default: + } + + // Allow tapscript tree storage to succeed, but force tapscript tree + // loading to fail. + t.treeStore.FailStore = false + t.treeStore.FailLoad = true + + // Receive all the signals needed to progress the caretaker through + // the batch sprouting, which is when the sibling tapscript tree is + // used. + progressCaretakerToTxSigning := func( + currentSeedlings []*tapgarden.Seedling) { + + _ = t.assertGenesisTxFunded() + + for i := 0; i < len(currentSeedlings); i++ { + t.assertKeyDerived() + + if currentSeedlings[i].EnableEmission { + t.assertKeyDerived() + } + } + } + + // Finalize the batch with a tapscript tree sibling. + t.finalizeBatch(&wg, respChan, &finalizeReq) + batchCount++ + + // The caretaker should fail when computing the Taproot output key. + progressCaretakerToTxSigning(seedlings) + t.assertFinalizeBatch(&wg, respChan, "failed to load tapscript tree") + t.assertLastBatchState(batchCount, tapgarden.BatchStateFrozen) + t.assertNoPendingBatch() + + // Reset the tapscript tree store to not force load or store failures. + t.treeStore.FailStore = false + t.treeStore.FailLoad = false + + // Construct a tapscript tree with a single leaf that has the structure + // of a TapLeaf computed from a TapCommitment. This should be rejected + // by the caretaker, as the genesis TX for the batch should only commit + // to one TapCommitment. + var dummyRootSum [8]byte + binary.BigEndian.PutUint64(dummyRootSum[:], test.RandInt[uint64]()) + dummyRootHashParts := [][]byte{ + {byte(asset.V0)}, commitment.TaprootAssetsMarker[:], + fn.ByteSlice(test.RandHash()), dummyRootSum[:], + } + dummyTapCommitmentRootHash := bytes.Join(dummyRootHashParts, nil) + dummyTapLeaf := txscript.NewBaseTapLeaf(dummyTapCommitmentRootHash) + dummyTapCommitmentPreimage, err := asset.TapTreeNodesFromLeaves( + []txscript.TapLeaf{dummyTapLeaf}, + ) + require.NoError(t, err) + + finalizeReq.SiblingTapTree = fn.Some(*dummyTapCommitmentPreimage) + + // Queue another batch, and try to finalize with a sibling that is also + // a Taproot asset commitment. + seedlings = t.queueInitialBatch(numSeedlings) + t.finalizeBatch(&wg, respChan, &finalizeReq) + batchCount++ + + progressCaretakerToTxSigning(seedlings) + t.assertFinalizeBatch( + &wg, respChan, "preimage is a Taproot Asset commitment", + ) + t.assertNoPendingBatch() + + // Queue another batch, and provide a valid sibling tapscript tree. + seedlings = t.queueInitialBatch(numSeedlings) + finalizeReq.SiblingTapTree = fn.Some(*tapTreePreimage) + t.finalizeBatch(&wg, respChan, &finalizeReq) + batchCount++ + + // Verify that the final genesis TX uses the correct Taproot output key. + treeRootChildren := test.BuildTapscriptTreeNoReveal(t.T, sigLockKey) + siblingPreimage := commitment.NewPreimageFromBranch(treeRootChildren) + sendConfNtfn := t.progressCaretaker(seedlings, &siblingPreimage) + sendConfNtfn() + + // Once the TX is broadcast, the caretaker should run to completion, + // storing issuance proofs and updating the batch state to finalized. + batchWithSibling := t.assertFinalizeBatch(&wg, respChan, "") + require.NotNil(t, batchWithSibling) + t.assertNoError() + t.assertNoPendingBatch() + t.assertNumCaretakersActive(0) + t.assertLastBatchState(batchCount, tapgarden.BatchStateFinalized) + + // Verify that the final minting output key matches what we would derive + // manually. + batchRootCommitment := batchWithSibling.RootAssetCommitment + require.NotNil(t, batchRootCommitment) + siblingHash, err := siblingPreimage.TapHash() + require.NoError(t, err) + batchScriptRoot := batchRootCommitment.TapscriptRoot(siblingHash) + batchOutputKeyExpected := txscript.ComputeTaprootOutputKey( + batchWithSibling.BatchKey.PubKey, batchScriptRoot[:], + ) + batchOutputKey, _, err := batchWithSibling.MintingOutputKey(nil) + require.NoError(t, err) + require.Equal( + t, batchOutputKeyExpected.SerializeCompressed(), + batchOutputKey.SerializeCompressed(), + ) +} + // mintingStoreTestCase is used to programmatically run a series of test cases // that are parametrized based on a fresh minting store. type mintingStoreTestCase struct { @@ -1231,6 +1405,11 @@ var testCases = []mintingStoreTestCase{ interval: minterInterval, testFunc: testFinalizeBatch, }, + { + name: "finalize_with_tapscript_tree", + interval: minterInterval, + testFunc: testFinalizeWithTapscriptTree, + }, } // TestBatchedAssetIssuance runs a test of tests to ensure that the set of diff --git a/tappsbt/mock.go b/tappsbt/mock.go index cf38047f3..6251b7418 100644 --- a/tappsbt/mock.go +++ b/tappsbt/mock.go @@ -80,7 +80,8 @@ func RandPacket(t testing.TB) *VPacket { LeafVersion: txscript.BaseLeafVersion, Script: []byte("not a valid script"), } - testPreimage1 := commitment.NewPreimageFromLeaf(leaf1) + testPreimage1, err := commitment.NewPreimageFromLeaf(leaf1) + require.NoError(t, err) testPreimage2 := commitment.NewPreimageFromBranch( txscript.NewTapBranch(leaf1, leaf1), ) @@ -155,7 +156,7 @@ func RandPacket(t testing.TB) *VPacket { AnchorOutputTaprootBip32Derivation: trBip32Derivations, Asset: testOutputAsset, ScriptKey: testOutputAsset.ScriptKey, - AnchorOutputTapscriptSibling: testPreimage2, + AnchorOutputTapscriptSibling: &testPreimage2, }}, ChainParams: testParams, } diff --git a/taprpc/mintrpc/mint.pb.go b/taprpc/mintrpc/mint.pb.go index b8d489104..5a4b869f6 100644 --- a/taprpc/mintrpc/mint.pb.go +++ b/taprpc/mintrpc/mint.pb.go @@ -524,6 +524,15 @@ type FinalizeBatchRequest struct { ShortResponse bool `protobuf:"varint,1,opt,name=short_response,json=shortResponse,proto3" json:"short_response,omitempty"` // The optional fee rate to use for the minting transaction, in sat/kw. FeeRate uint32 `protobuf:"varint,2,opt,name=fee_rate,json=feeRate,proto3" json:"fee_rate,omitempty"` + // The optional tapscript sibling that will be used when deriving the genesis + // output for the batch. This sibling is a tapscript tree, which allows the + // minter to encumber future transfers of assets in the batch with Tapscript. + // + // Types that are assignable to BatchSibling: + // + // *FinalizeBatchRequest_FullTree + // *FinalizeBatchRequest_Branch + BatchSibling isFinalizeBatchRequest_BatchSibling `protobuf_oneof:"batch_sibling"` } func (x *FinalizeBatchRequest) Reset() { @@ -572,6 +581,46 @@ func (x *FinalizeBatchRequest) GetFeeRate() uint32 { return 0 } +func (m *FinalizeBatchRequest) GetBatchSibling() isFinalizeBatchRequest_BatchSibling { + if m != nil { + return m.BatchSibling + } + return nil +} + +func (x *FinalizeBatchRequest) GetFullTree() *taprpc.TapscriptFullTree { + if x, ok := x.GetBatchSibling().(*FinalizeBatchRequest_FullTree); ok { + return x.FullTree + } + return nil +} + +func (x *FinalizeBatchRequest) GetBranch() *taprpc.TapBranch { + if x, ok := x.GetBatchSibling().(*FinalizeBatchRequest_Branch); ok { + return x.Branch + } + return nil +} + +type isFinalizeBatchRequest_BatchSibling interface { + isFinalizeBatchRequest_BatchSibling() +} + +type FinalizeBatchRequest_FullTree struct { + // An ordered list of TapLeafs, which will be used to construct a + // Tapscript tree. + FullTree *taprpc.TapscriptFullTree `protobuf:"bytes,3,opt,name=full_tree,json=fullTree,proto3,oneof"` +} + +type FinalizeBatchRequest_Branch struct { + // A TapBranch that represents a Tapscript tree managed externally. + Branch *taprpc.TapBranch `protobuf:"bytes,4,opt,name=branch,proto3,oneof"` +} + +func (*FinalizeBatchRequest_FullTree) isFinalizeBatchRequest_BatchSibling() {} + +func (*FinalizeBatchRequest_Branch) isFinalizeBatchRequest_BatchSibling() {} + type FinalizeBatchResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -911,71 +960,79 @@ var file_mintrpc_mint_proto_rawDesc = []byte{ 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x22, 0x58, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x44, 0x0a, 0x15, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, - 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, - 0x68, 0x22, 0x14, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x32, 0x0a, 0x13, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x22, 0x61, 0x0a, 0x10, 0x4c, - 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x24, - 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, - 0x79, 0x53, 0x74, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x44, - 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, - 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x73, 0x2a, 0x88, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, - 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, - 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x52, 0x4f, 0x5a, 0x45, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, - 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4d, - 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, - 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x52, 0x4f, 0x41, 0x44, 0x43, 0x41, 0x53, - 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x05, 0x12, 0x19, - 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x49, - 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x42, 0x41, 0x54, - 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x45, 0x45, 0x44, 0x4c, 0x49, 0x4e, - 0x47, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x07, 0x12, 0x20, 0x0a, - 0x1c, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x50, 0x52, - 0x4f, 0x55, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x32, - 0xaa, 0x02, 0x0a, 0x04, 0x4d, 0x69, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x09, 0x4d, 0x69, 0x6e, 0x74, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x69, 0x6e, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x6e, 0x74, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0d, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, - 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, - 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x6d, 0x69, - 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x38, 0x5a, 0x36, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, - 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x6d, - 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x38, 0x0a, 0x09, + 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x61, 0x70, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x48, 0x00, 0x52, 0x08, 0x66, 0x75, + 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x61, 0x70, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48, 0x00, 0x52, 0x06, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x62, + 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x44, 0x0a, 0x15, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, + 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x32, 0x0a, 0x13, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x61, 0x74, 0x63, + 0x68, 0x4b, 0x65, 0x79, 0x22, 0x61, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x09, 0x62, 0x61, 0x74, 0x63, + 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x53, 0x74, 0x72, 0x42, 0x08, 0x0a, + 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x44, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x2a, 0x88, 0x02, + 0x0a, 0x0a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x13, + 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x16, + 0x0a, 0x12, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x52, + 0x4f, 0x5a, 0x45, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, + 0x03, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, + 0x5f, 0x42, 0x52, 0x4f, 0x41, 0x44, 0x43, 0x41, 0x53, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, + 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x54, 0x43, 0x48, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, + 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x53, 0x45, 0x45, 0x44, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, + 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x07, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x50, 0x52, 0x4f, 0x55, 0x54, 0x5f, 0x43, 0x41, 0x4e, + 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x32, 0xaa, 0x02, 0x0a, 0x04, 0x4d, 0x69, 0x6e, + 0x74, 0x12, 0x42, 0x0a, 0x09, 0x4d, 0x69, 0x6e, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x19, + 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x6e, 0x74, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x69, 0x6e, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x6e, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x19, + 0x2e, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x69, 0x6e, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, + 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x6d, 0x69, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -993,21 +1050,23 @@ func file_mintrpc_mint_proto_rawDescGZIP() []byte { var file_mintrpc_mint_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_mintrpc_mint_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_mintrpc_mint_proto_goTypes = []interface{}{ - (BatchState)(0), // 0: mintrpc.BatchState - (*PendingAsset)(nil), // 1: mintrpc.PendingAsset - (*MintAsset)(nil), // 2: mintrpc.MintAsset - (*MintAssetRequest)(nil), // 3: mintrpc.MintAssetRequest - (*MintAssetResponse)(nil), // 4: mintrpc.MintAssetResponse - (*MintingBatch)(nil), // 5: mintrpc.MintingBatch - (*FinalizeBatchRequest)(nil), // 6: mintrpc.FinalizeBatchRequest - (*FinalizeBatchResponse)(nil), // 7: mintrpc.FinalizeBatchResponse - (*CancelBatchRequest)(nil), // 8: mintrpc.CancelBatchRequest - (*CancelBatchResponse)(nil), // 9: mintrpc.CancelBatchResponse - (*ListBatchRequest)(nil), // 10: mintrpc.ListBatchRequest - (*ListBatchResponse)(nil), // 11: mintrpc.ListBatchResponse - (taprpc.AssetVersion)(0), // 12: taprpc.AssetVersion - (taprpc.AssetType)(0), // 13: taprpc.AssetType - (*taprpc.AssetMeta)(nil), // 14: taprpc.AssetMeta + (BatchState)(0), // 0: mintrpc.BatchState + (*PendingAsset)(nil), // 1: mintrpc.PendingAsset + (*MintAsset)(nil), // 2: mintrpc.MintAsset + (*MintAssetRequest)(nil), // 3: mintrpc.MintAssetRequest + (*MintAssetResponse)(nil), // 4: mintrpc.MintAssetResponse + (*MintingBatch)(nil), // 5: mintrpc.MintingBatch + (*FinalizeBatchRequest)(nil), // 6: mintrpc.FinalizeBatchRequest + (*FinalizeBatchResponse)(nil), // 7: mintrpc.FinalizeBatchResponse + (*CancelBatchRequest)(nil), // 8: mintrpc.CancelBatchRequest + (*CancelBatchResponse)(nil), // 9: mintrpc.CancelBatchResponse + (*ListBatchRequest)(nil), // 10: mintrpc.ListBatchRequest + (*ListBatchResponse)(nil), // 11: mintrpc.ListBatchResponse + (taprpc.AssetVersion)(0), // 12: taprpc.AssetVersion + (taprpc.AssetType)(0), // 13: taprpc.AssetType + (*taprpc.AssetMeta)(nil), // 14: taprpc.AssetMeta + (*taprpc.TapscriptFullTree)(nil), // 15: taprpc.TapscriptFullTree + (*taprpc.TapBranch)(nil), // 16: taprpc.TapBranch } var file_mintrpc_mint_proto_depIdxs = []int32{ 12, // 0: mintrpc.PendingAsset.asset_version:type_name -> taprpc.AssetVersion @@ -1020,21 +1079,23 @@ var file_mintrpc_mint_proto_depIdxs = []int32{ 5, // 7: mintrpc.MintAssetResponse.pending_batch:type_name -> mintrpc.MintingBatch 0, // 8: mintrpc.MintingBatch.state:type_name -> mintrpc.BatchState 1, // 9: mintrpc.MintingBatch.assets:type_name -> mintrpc.PendingAsset - 5, // 10: mintrpc.FinalizeBatchResponse.batch:type_name -> mintrpc.MintingBatch - 5, // 11: mintrpc.ListBatchResponse.batches:type_name -> mintrpc.MintingBatch - 3, // 12: mintrpc.Mint.MintAsset:input_type -> mintrpc.MintAssetRequest - 6, // 13: mintrpc.Mint.FinalizeBatch:input_type -> mintrpc.FinalizeBatchRequest - 8, // 14: mintrpc.Mint.CancelBatch:input_type -> mintrpc.CancelBatchRequest - 10, // 15: mintrpc.Mint.ListBatches:input_type -> mintrpc.ListBatchRequest - 4, // 16: mintrpc.Mint.MintAsset:output_type -> mintrpc.MintAssetResponse - 7, // 17: mintrpc.Mint.FinalizeBatch:output_type -> mintrpc.FinalizeBatchResponse - 9, // 18: mintrpc.Mint.CancelBatch:output_type -> mintrpc.CancelBatchResponse - 11, // 19: mintrpc.Mint.ListBatches:output_type -> mintrpc.ListBatchResponse - 16, // [16:20] is the sub-list for method output_type - 12, // [12:16] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 15, // 10: mintrpc.FinalizeBatchRequest.full_tree:type_name -> taprpc.TapscriptFullTree + 16, // 11: mintrpc.FinalizeBatchRequest.branch:type_name -> taprpc.TapBranch + 5, // 12: mintrpc.FinalizeBatchResponse.batch:type_name -> mintrpc.MintingBatch + 5, // 13: mintrpc.ListBatchResponse.batches:type_name -> mintrpc.MintingBatch + 3, // 14: mintrpc.Mint.MintAsset:input_type -> mintrpc.MintAssetRequest + 6, // 15: mintrpc.Mint.FinalizeBatch:input_type -> mintrpc.FinalizeBatchRequest + 8, // 16: mintrpc.Mint.CancelBatch:input_type -> mintrpc.CancelBatchRequest + 10, // 17: mintrpc.Mint.ListBatches:input_type -> mintrpc.ListBatchRequest + 4, // 18: mintrpc.Mint.MintAsset:output_type -> mintrpc.MintAssetResponse + 7, // 19: mintrpc.Mint.FinalizeBatch:output_type -> mintrpc.FinalizeBatchResponse + 9, // 20: mintrpc.Mint.CancelBatch:output_type -> mintrpc.CancelBatchResponse + 11, // 21: mintrpc.Mint.ListBatches:output_type -> mintrpc.ListBatchResponse + 18, // [18:22] is the sub-list for method output_type + 14, // [14:18] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_mintrpc_mint_proto_init() } @@ -1176,6 +1237,10 @@ func file_mintrpc_mint_proto_init() { } } } + file_mintrpc_mint_proto_msgTypes[5].OneofWrappers = []interface{}{ + (*FinalizeBatchRequest_FullTree)(nil), + (*FinalizeBatchRequest_Branch)(nil), + } file_mintrpc_mint_proto_msgTypes[9].OneofWrappers = []interface{}{ (*ListBatchRequest_BatchKey)(nil), (*ListBatchRequest_BatchKeyStr)(nil), diff --git a/taprpc/mintrpc/mint.proto b/taprpc/mintrpc/mint.proto index d632e02e9..842b78610 100644 --- a/taprpc/mintrpc/mint.proto +++ b/taprpc/mintrpc/mint.proto @@ -177,6 +177,22 @@ message FinalizeBatchRequest { // The optional fee rate to use for the minting transaction, in sat/kw. uint32 fee_rate = 2; + + /* + The optional tapscript sibling that will be used when deriving the genesis + output for the batch. This sibling is a tapscript tree, which allows the + minter to encumber future transfers of assets in the batch with Tapscript. + */ + oneof batch_sibling { + /* + An ordered list of TapLeafs, which will be used to construct a + Tapscript tree. + */ + taprpc.TapscriptFullTree full_tree = 3; + + // A TapBranch that represents a Tapscript tree managed externally. + taprpc.TapBranch branch = 4; + } } message FinalizeBatchResponse { diff --git a/taprpc/mintrpc/mint.swagger.json b/taprpc/mintrpc/mint.swagger.json index a8fe96a1f..53e26f37d 100644 --- a/taprpc/mintrpc/mint.swagger.json +++ b/taprpc/mintrpc/mint.swagger.json @@ -196,6 +196,14 @@ "type": "integer", "format": "int64", "description": "The optional fee rate to use for the minting transaction, in sat/kw." + }, + "full_tree": { + "$ref": "#/definitions/taprpcTapscriptFullTree", + "description": "An ordered list of TapLeafs, which will be used to construct a\nTapscript tree." + }, + "branch": { + "$ref": "#/definitions/taprpcTapBranch", + "description": "A TapBranch that represents a Tapscript tree managed externally." } } }, @@ -423,6 +431,43 @@ ], "default": "ASSET_VERSION_V0", "description": " - ASSET_VERSION_V0: ASSET_VERSION_V0 is the default asset version. This version will include\nthe witness vector in the leaf for a tap commitment.\n - ASSET_VERSION_V1: ASSET_VERSION_V1 is the asset version that leaves out the witness vector\nfrom the MS-SMT leaf encoding." + }, + "taprpcTapBranch": { + "type": "object", + "properties": { + "left_taphash": { + "type": "string", + "format": "byte", + "description": "The TapHash of the left child of the root hash of a Tapscript tree." + }, + "right_taphash": { + "type": "string", + "format": "byte", + "description": "The TapHash of the right child of the root hash of a Tapscript tree." + } + } + }, + "taprpcTapLeaf": { + "type": "object", + "properties": { + "script": { + "type": "string", + "format": "byte", + "description": "The script of the tap leaf." + } + } + }, + "taprpcTapscriptFullTree": { + "type": "object", + "properties": { + "all_leaves": { + "type": "array", + "items": { + "$ref": "#/definitions/taprpcTapLeaf" + }, + "description": "The complete, ordered list of all tap leaves of the tree." + } + } } } } diff --git a/taprpc/taprootassets.pb.go b/taprpc/taprootassets.pb.go index b0df5dd98..41148ccc8 100644 --- a/taprpc/taprootassets.pb.go +++ b/taprpc/taprootassets.pb.go @@ -3124,6 +3124,159 @@ func (x *KeyDescriptor) GetKeyLoc() *KeyLocator { return nil } +type TapscriptFullTree struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The complete, ordered list of all tap leaves of the tree. + AllLeaves []*TapLeaf `protobuf:"bytes,1,rep,name=all_leaves,json=allLeaves,proto3" json:"all_leaves,omitempty"` +} + +func (x *TapscriptFullTree) Reset() { + *x = TapscriptFullTree{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TapscriptFullTree) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TapscriptFullTree) ProtoMessage() {} + +func (x *TapscriptFullTree) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TapscriptFullTree.ProtoReflect.Descriptor instead. +func (*TapscriptFullTree) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{39} +} + +func (x *TapscriptFullTree) GetAllLeaves() []*TapLeaf { + if x != nil { + return x.AllLeaves + } + return nil +} + +type TapLeaf struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The script of the tap leaf. + Script []byte `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` +} + +func (x *TapLeaf) Reset() { + *x = TapLeaf{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TapLeaf) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TapLeaf) ProtoMessage() {} + +func (x *TapLeaf) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TapLeaf.ProtoReflect.Descriptor instead. +func (*TapLeaf) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{40} +} + +func (x *TapLeaf) GetScript() []byte { + if x != nil { + return x.Script + } + return nil +} + +type TapBranch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The TapHash of the left child of the root hash of a Tapscript tree. + LeftTaphash []byte `protobuf:"bytes,1,opt,name=left_taphash,json=leftTaphash,proto3" json:"left_taphash,omitempty"` + // The TapHash of the right child of the root hash of a Tapscript tree. + RightTaphash []byte `protobuf:"bytes,2,opt,name=right_taphash,json=rightTaphash,proto3" json:"right_taphash,omitempty"` +} + +func (x *TapBranch) Reset() { + *x = TapBranch{} + if protoimpl.UnsafeEnabled { + mi := &file_taprootassets_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TapBranch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TapBranch) ProtoMessage() {} + +func (x *TapBranch) ProtoReflect() protoreflect.Message { + mi := &file_taprootassets_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TapBranch.ProtoReflect.Descriptor instead. +func (*TapBranch) Descriptor() ([]byte, []int) { + return file_taprootassets_proto_rawDescGZIP(), []int{41} +} + +func (x *TapBranch) GetLeftTaphash() []byte { + if x != nil { + return x.LeftTaphash + } + return nil +} + +func (x *TapBranch) GetRightTaphash() []byte { + if x != nil { + return x.RightTaphash + } + return nil +} + type DecodeAddrRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3135,7 +3288,7 @@ type DecodeAddrRequest struct { func (x *DecodeAddrRequest) Reset() { *x = DecodeAddrRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[39] + mi := &file_taprootassets_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3148,7 +3301,7 @@ func (x *DecodeAddrRequest) String() string { func (*DecodeAddrRequest) ProtoMessage() {} func (x *DecodeAddrRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[39] + mi := &file_taprootassets_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3161,7 +3314,7 @@ func (x *DecodeAddrRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DecodeAddrRequest.ProtoReflect.Descriptor instead. func (*DecodeAddrRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{39} + return file_taprootassets_proto_rawDescGZIP(), []int{42} } func (x *DecodeAddrRequest) GetAddr() string { @@ -3185,7 +3338,7 @@ type ProofFile struct { func (x *ProofFile) Reset() { *x = ProofFile{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[40] + mi := &file_taprootassets_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3198,7 +3351,7 @@ func (x *ProofFile) String() string { func (*ProofFile) ProtoMessage() {} func (x *ProofFile) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[40] + mi := &file_taprootassets_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3211,7 +3364,7 @@ func (x *ProofFile) ProtoReflect() protoreflect.Message { // Deprecated: Use ProofFile.ProtoReflect.Descriptor instead. func (*ProofFile) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{40} + return file_taprootassets_proto_rawDescGZIP(), []int{43} } func (x *ProofFile) GetRawProofFile() []byte { @@ -3281,7 +3434,7 @@ type DecodedProof struct { func (x *DecodedProof) Reset() { *x = DecodedProof{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[41] + mi := &file_taprootassets_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3294,7 +3447,7 @@ func (x *DecodedProof) String() string { func (*DecodedProof) ProtoMessage() {} func (x *DecodedProof) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[41] + mi := &file_taprootassets_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3307,7 +3460,7 @@ func (x *DecodedProof) ProtoReflect() protoreflect.Message { // Deprecated: Use DecodedProof.ProtoReflect.Descriptor instead. func (*DecodedProof) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{41} + return file_taprootassets_proto_rawDescGZIP(), []int{44} } func (x *DecodedProof) GetProofAtDepth() uint32 { @@ -3414,7 +3567,7 @@ type VerifyProofResponse struct { func (x *VerifyProofResponse) Reset() { *x = VerifyProofResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[42] + mi := &file_taprootassets_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3427,7 +3580,7 @@ func (x *VerifyProofResponse) String() string { func (*VerifyProofResponse) ProtoMessage() {} func (x *VerifyProofResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[42] + mi := &file_taprootassets_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3440,7 +3593,7 @@ func (x *VerifyProofResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyProofResponse.ProtoReflect.Descriptor instead. func (*VerifyProofResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{42} + return file_taprootassets_proto_rawDescGZIP(), []int{45} } func (x *VerifyProofResponse) GetValid() bool { @@ -3480,7 +3633,7 @@ type DecodeProofRequest struct { func (x *DecodeProofRequest) Reset() { *x = DecodeProofRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[43] + mi := &file_taprootassets_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3493,7 +3646,7 @@ func (x *DecodeProofRequest) String() string { func (*DecodeProofRequest) ProtoMessage() {} func (x *DecodeProofRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[43] + mi := &file_taprootassets_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3506,7 +3659,7 @@ func (x *DecodeProofRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DecodeProofRequest.ProtoReflect.Descriptor instead. func (*DecodeProofRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{43} + return file_taprootassets_proto_rawDescGZIP(), []int{46} } func (x *DecodeProofRequest) GetRawProof() []byte { @@ -3548,7 +3701,7 @@ type DecodeProofResponse struct { func (x *DecodeProofResponse) Reset() { *x = DecodeProofResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[44] + mi := &file_taprootassets_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3561,7 +3714,7 @@ func (x *DecodeProofResponse) String() string { func (*DecodeProofResponse) ProtoMessage() {} func (x *DecodeProofResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[44] + mi := &file_taprootassets_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3574,7 +3727,7 @@ func (x *DecodeProofResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DecodeProofResponse.ProtoReflect.Descriptor instead. func (*DecodeProofResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{44} + return file_taprootassets_proto_rawDescGZIP(), []int{47} } func (x *DecodeProofResponse) GetDecodedProof() *DecodedProof { @@ -3597,7 +3750,7 @@ type ExportProofRequest struct { func (x *ExportProofRequest) Reset() { *x = ExportProofRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[45] + mi := &file_taprootassets_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3610,7 +3763,7 @@ func (x *ExportProofRequest) String() string { func (*ExportProofRequest) ProtoMessage() {} func (x *ExportProofRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[45] + mi := &file_taprootassets_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3623,7 +3776,7 @@ func (x *ExportProofRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExportProofRequest.ProtoReflect.Descriptor instead. func (*ExportProofRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{45} + return file_taprootassets_proto_rawDescGZIP(), []int{48} } func (x *ExportProofRequest) GetAssetId() []byte { @@ -3677,7 +3830,7 @@ type AddrEvent struct { func (x *AddrEvent) Reset() { *x = AddrEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[46] + mi := &file_taprootassets_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3690,7 +3843,7 @@ func (x *AddrEvent) String() string { func (*AddrEvent) ProtoMessage() {} func (x *AddrEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[46] + mi := &file_taprootassets_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3703,7 +3856,7 @@ func (x *AddrEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AddrEvent.ProtoReflect.Descriptor instead. func (*AddrEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{46} + return file_taprootassets_proto_rawDescGZIP(), []int{49} } func (x *AddrEvent) GetCreationTimeUnixSeconds() uint64 { @@ -3776,7 +3929,7 @@ type AddrReceivesRequest struct { func (x *AddrReceivesRequest) Reset() { *x = AddrReceivesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[47] + mi := &file_taprootassets_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3789,7 +3942,7 @@ func (x *AddrReceivesRequest) String() string { func (*AddrReceivesRequest) ProtoMessage() {} func (x *AddrReceivesRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[47] + mi := &file_taprootassets_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3802,7 +3955,7 @@ func (x *AddrReceivesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddrReceivesRequest.ProtoReflect.Descriptor instead. func (*AddrReceivesRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{47} + return file_taprootassets_proto_rawDescGZIP(), []int{50} } func (x *AddrReceivesRequest) GetFilterAddr() string { @@ -3831,7 +3984,7 @@ type AddrReceivesResponse struct { func (x *AddrReceivesResponse) Reset() { *x = AddrReceivesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[48] + mi := &file_taprootassets_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3844,7 +3997,7 @@ func (x *AddrReceivesResponse) String() string { func (*AddrReceivesResponse) ProtoMessage() {} func (x *AddrReceivesResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[48] + mi := &file_taprootassets_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3857,7 +4010,7 @@ func (x *AddrReceivesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddrReceivesResponse.ProtoReflect.Descriptor instead. func (*AddrReceivesResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{48} + return file_taprootassets_proto_rawDescGZIP(), []int{51} } func (x *AddrReceivesResponse) GetEvents() []*AddrEvent { @@ -3880,7 +4033,7 @@ type SendAssetRequest struct { func (x *SendAssetRequest) Reset() { *x = SendAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[49] + mi := &file_taprootassets_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3893,7 +4046,7 @@ func (x *SendAssetRequest) String() string { func (*SendAssetRequest) ProtoMessage() {} func (x *SendAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[49] + mi := &file_taprootassets_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3906,7 +4059,7 @@ func (x *SendAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendAssetRequest.ProtoReflect.Descriptor instead. func (*SendAssetRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{49} + return file_taprootassets_proto_rawDescGZIP(), []int{52} } func (x *SendAssetRequest) GetTapAddrs() []string { @@ -3937,7 +4090,7 @@ type PrevInputAsset struct { func (x *PrevInputAsset) Reset() { *x = PrevInputAsset{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[50] + mi := &file_taprootassets_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3950,7 +4103,7 @@ func (x *PrevInputAsset) String() string { func (*PrevInputAsset) ProtoMessage() {} func (x *PrevInputAsset) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[50] + mi := &file_taprootassets_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3963,7 +4116,7 @@ func (x *PrevInputAsset) ProtoReflect() protoreflect.Message { // Deprecated: Use PrevInputAsset.ProtoReflect.Descriptor instead. func (*PrevInputAsset) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{50} + return file_taprootassets_proto_rawDescGZIP(), []int{53} } func (x *PrevInputAsset) GetAnchorPoint() string { @@ -4005,7 +4158,7 @@ type SendAssetResponse struct { func (x *SendAssetResponse) Reset() { *x = SendAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[51] + mi := &file_taprootassets_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4018,7 +4171,7 @@ func (x *SendAssetResponse) String() string { func (*SendAssetResponse) ProtoMessage() {} func (x *SendAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[51] + mi := &file_taprootassets_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4031,7 +4184,7 @@ func (x *SendAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SendAssetResponse.ProtoReflect.Descriptor instead. func (*SendAssetResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{51} + return file_taprootassets_proto_rawDescGZIP(), []int{54} } func (x *SendAssetResponse) GetTransfer() *AssetTransfer { @@ -4050,7 +4203,7 @@ type GetInfoRequest struct { func (x *GetInfoRequest) Reset() { *x = GetInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[52] + mi := &file_taprootassets_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4063,7 +4216,7 @@ func (x *GetInfoRequest) String() string { func (*GetInfoRequest) ProtoMessage() {} func (x *GetInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[52] + mi := &file_taprootassets_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4076,7 +4229,7 @@ func (x *GetInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetInfoRequest.ProtoReflect.Descriptor instead. func (*GetInfoRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{52} + return file_taprootassets_proto_rawDescGZIP(), []int{55} } type GetInfoResponse struct { @@ -4097,7 +4250,7 @@ type GetInfoResponse struct { func (x *GetInfoResponse) Reset() { *x = GetInfoResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[53] + mi := &file_taprootassets_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4110,7 +4263,7 @@ func (x *GetInfoResponse) String() string { func (*GetInfoResponse) ProtoMessage() {} func (x *GetInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[53] + mi := &file_taprootassets_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4123,7 +4276,7 @@ func (x *GetInfoResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetInfoResponse.ProtoReflect.Descriptor instead. func (*GetInfoResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{53} + return file_taprootassets_proto_rawDescGZIP(), []int{56} } func (x *GetInfoResponse) GetVersion() string { @@ -4191,7 +4344,7 @@ type SubscribeSendAssetEventNtfnsRequest struct { func (x *SubscribeSendAssetEventNtfnsRequest) Reset() { *x = SubscribeSendAssetEventNtfnsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[54] + mi := &file_taprootassets_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4204,7 +4357,7 @@ func (x *SubscribeSendAssetEventNtfnsRequest) String() string { func (*SubscribeSendAssetEventNtfnsRequest) ProtoMessage() {} func (x *SubscribeSendAssetEventNtfnsRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[54] + mi := &file_taprootassets_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4217,7 +4370,7 @@ func (x *SubscribeSendAssetEventNtfnsRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use SubscribeSendAssetEventNtfnsRequest.ProtoReflect.Descriptor instead. func (*SubscribeSendAssetEventNtfnsRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{54} + return file_taprootassets_proto_rawDescGZIP(), []int{57} } type SendAssetEvent struct { @@ -4235,7 +4388,7 @@ type SendAssetEvent struct { func (x *SendAssetEvent) Reset() { *x = SendAssetEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[55] + mi := &file_taprootassets_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4248,7 +4401,7 @@ func (x *SendAssetEvent) String() string { func (*SendAssetEvent) ProtoMessage() {} func (x *SendAssetEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[55] + mi := &file_taprootassets_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4261,7 +4414,7 @@ func (x *SendAssetEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SendAssetEvent.ProtoReflect.Descriptor instead. func (*SendAssetEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{55} + return file_taprootassets_proto_rawDescGZIP(), []int{58} } func (m *SendAssetEvent) GetEvent() isSendAssetEvent_Event { @@ -4318,7 +4471,7 @@ type ExecuteSendStateEvent struct { func (x *ExecuteSendStateEvent) Reset() { *x = ExecuteSendStateEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[56] + mi := &file_taprootassets_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4331,7 +4484,7 @@ func (x *ExecuteSendStateEvent) String() string { func (*ExecuteSendStateEvent) ProtoMessage() {} func (x *ExecuteSendStateEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[56] + mi := &file_taprootassets_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4344,7 +4497,7 @@ func (x *ExecuteSendStateEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteSendStateEvent.ProtoReflect.Descriptor instead. func (*ExecuteSendStateEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{56} + return file_taprootassets_proto_rawDescGZIP(), []int{59} } func (x *ExecuteSendStateEvent) GetTimestamp() int64 { @@ -4381,7 +4534,7 @@ type ProofTransferBackoffWaitEvent struct { func (x *ProofTransferBackoffWaitEvent) Reset() { *x = ProofTransferBackoffWaitEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[57] + mi := &file_taprootassets_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4394,7 +4547,7 @@ func (x *ProofTransferBackoffWaitEvent) String() string { func (*ProofTransferBackoffWaitEvent) ProtoMessage() {} func (x *ProofTransferBackoffWaitEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[57] + mi := &file_taprootassets_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4407,7 +4560,7 @@ func (x *ProofTransferBackoffWaitEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ProofTransferBackoffWaitEvent.ProtoReflect.Descriptor instead. func (*ProofTransferBackoffWaitEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{57} + return file_taprootassets_proto_rawDescGZIP(), []int{60} } func (x *ProofTransferBackoffWaitEvent) GetTimestamp() int64 { @@ -4447,7 +4600,7 @@ type SubscribeReceiveAssetEventNtfnsRequest struct { func (x *SubscribeReceiveAssetEventNtfnsRequest) Reset() { *x = SubscribeReceiveAssetEventNtfnsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[58] + mi := &file_taprootassets_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4460,7 +4613,7 @@ func (x *SubscribeReceiveAssetEventNtfnsRequest) String() string { func (*SubscribeReceiveAssetEventNtfnsRequest) ProtoMessage() {} func (x *SubscribeReceiveAssetEventNtfnsRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[58] + mi := &file_taprootassets_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4473,7 +4626,7 @@ func (x *SubscribeReceiveAssetEventNtfnsRequest) ProtoReflect() protoreflect.Mes // Deprecated: Use SubscribeReceiveAssetEventNtfnsRequest.ProtoReflect.Descriptor instead. func (*SubscribeReceiveAssetEventNtfnsRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{58} + return file_taprootassets_proto_rawDescGZIP(), []int{61} } type AssetReceiveCompleteEvent struct { @@ -4492,7 +4645,7 @@ type AssetReceiveCompleteEvent struct { func (x *AssetReceiveCompleteEvent) Reset() { *x = AssetReceiveCompleteEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[59] + mi := &file_taprootassets_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4505,7 +4658,7 @@ func (x *AssetReceiveCompleteEvent) String() string { func (*AssetReceiveCompleteEvent) ProtoMessage() {} func (x *AssetReceiveCompleteEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[59] + mi := &file_taprootassets_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4518,7 +4671,7 @@ func (x *AssetReceiveCompleteEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AssetReceiveCompleteEvent.ProtoReflect.Descriptor instead. func (*AssetReceiveCompleteEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{59} + return file_taprootassets_proto_rawDescGZIP(), []int{62} } func (x *AssetReceiveCompleteEvent) GetTimestamp() int64 { @@ -4557,7 +4710,7 @@ type ReceiveAssetEvent struct { func (x *ReceiveAssetEvent) Reset() { *x = ReceiveAssetEvent{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[60] + mi := &file_taprootassets_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4570,7 +4723,7 @@ func (x *ReceiveAssetEvent) String() string { func (*ReceiveAssetEvent) ProtoMessage() {} func (x *ReceiveAssetEvent) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[60] + mi := &file_taprootassets_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4583,7 +4736,7 @@ func (x *ReceiveAssetEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ReceiveAssetEvent.ProtoReflect.Descriptor instead. func (*ReceiveAssetEvent) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{60} + return file_taprootassets_proto_rawDescGZIP(), []int{63} } func (m *ReceiveAssetEvent) GetEvent() isReceiveAssetEvent_Event { @@ -4643,7 +4796,7 @@ type FetchAssetMetaRequest struct { func (x *FetchAssetMetaRequest) Reset() { *x = FetchAssetMetaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[61] + mi := &file_taprootassets_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4656,7 +4809,7 @@ func (x *FetchAssetMetaRequest) String() string { func (*FetchAssetMetaRequest) ProtoMessage() {} func (x *FetchAssetMetaRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[61] + mi := &file_taprootassets_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4669,7 +4822,7 @@ func (x *FetchAssetMetaRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchAssetMetaRequest.ProtoReflect.Descriptor instead. func (*FetchAssetMetaRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{61} + return file_taprootassets_proto_rawDescGZIP(), []int{64} } func (m *FetchAssetMetaRequest) GetAsset() isFetchAssetMetaRequest_Asset { @@ -4759,7 +4912,7 @@ type BurnAssetRequest struct { func (x *BurnAssetRequest) Reset() { *x = BurnAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[62] + mi := &file_taprootassets_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4772,7 +4925,7 @@ func (x *BurnAssetRequest) String() string { func (*BurnAssetRequest) ProtoMessage() {} func (x *BurnAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[62] + mi := &file_taprootassets_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4785,7 +4938,7 @@ func (x *BurnAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BurnAssetRequest.ProtoReflect.Descriptor instead. func (*BurnAssetRequest) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{62} + return file_taprootassets_proto_rawDescGZIP(), []int{65} } func (m *BurnAssetRequest) GetAsset() isBurnAssetRequest_Asset { @@ -4855,7 +5008,7 @@ type BurnAssetResponse struct { func (x *BurnAssetResponse) Reset() { *x = BurnAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[63] + mi := &file_taprootassets_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4868,7 +5021,7 @@ func (x *BurnAssetResponse) String() string { func (*BurnAssetResponse) ProtoMessage() {} func (x *BurnAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[63] + mi := &file_taprootassets_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4881,7 +5034,7 @@ func (x *BurnAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BurnAssetResponse.ProtoReflect.Descriptor instead. func (*BurnAssetResponse) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{63} + return file_taprootassets_proto_rawDescGZIP(), []int{66} } func (x *BurnAssetResponse) GetBurnTransfer() *AssetTransfer { @@ -4912,7 +5065,7 @@ type OutPoint struct { func (x *OutPoint) Reset() { *x = OutPoint{} if protoimpl.UnsafeEnabled { - mi := &file_taprootassets_proto_msgTypes[64] + mi := &file_taprootassets_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4925,7 +5078,7 @@ func (x *OutPoint) String() string { func (*OutPoint) ProtoMessage() {} func (x *OutPoint) ProtoReflect() protoreflect.Message { - mi := &file_taprootassets_proto_msgTypes[64] + mi := &file_taprootassets_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4938,7 +5091,7 @@ func (x *OutPoint) ProtoReflect() protoreflect.Message { // Deprecated: Use OutPoint.ProtoReflect.Descriptor instead. func (*OutPoint) Descriptor() ([]byte, []int) { - return file_taprootassets_proto_rawDescGZIP(), []int{64} + return file_taprootassets_proto_rawDescGZIP(), []int{67} } func (x *OutPoint) GetTxid() []byte { @@ -5356,380 +5509,392 @@ var file_taprootassets_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, - 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x22, 0x27, - 0x0a, 0x11, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x56, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6f, 0x66, - 0x46, 0x69, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x61, 0x77, 0x5f, 0x70, 0x72, 0x6f, 0x6f, - 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x61, - 0x77, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x65, - 0x6e, 0x65, 0x73, 0x69, 0x73, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, - 0xd7, 0x04, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, - 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x61, 0x74, 0x5f, 0x64, 0x65, 0x70, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x41, - 0x74, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, - 0x12, 0x23, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x05, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x72, 0x65, - 0x76, 0x65, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6d, - 0x65, 0x74, 0x61, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x78, 0x5f, - 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x78, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, - 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x72, - 0x6f, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, - 0x32, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, - 0x6e, 0x75, 0x6d, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, - 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, - 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, - 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x62, 0x75, 0x72, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x69, 0x73, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x3c, 0x0a, 0x0e, 0x67, 0x65, 0x6e, - 0x65, 0x73, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x73, - 0x69, 0x73, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x52, 0x0d, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, - 0x73, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x40, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x22, 0x66, 0x0a, 0x13, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0d, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x52, 0x0c, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x22, 0xb1, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, - 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x61, 0x77, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x61, - 0x74, 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x41, 0x74, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x50, 0x72, - 0x65, 0x76, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x77, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x52, - 0x65, 0x76, 0x65, 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x13, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, - 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x0c, 0x64, 0x65, 0x63, 0x6f, 0x64, - 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x7c, 0x0a, 0x12, 0x45, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, - 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xd0, 0x02, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x12, 0x20, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x04, 0x61, 0x64, - 0x64, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x20, 0x0a, 0x0c, 0x75, 0x74, 0x78, 0x6f, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x75, 0x74, 0x78, 0x6f, 0x41, 0x6d, 0x74, 0x53, 0x61, - 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x69, 0x62, - 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x74, 0x61, 0x70, 0x72, - 0x6f, 0x6f, 0x74, 0x53, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, - 0x61, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, - 0x68, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x74, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, - 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, - 0x12, 0x3c, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, - 0x0a, 0x14, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x4a, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x70, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x70, 0x41, 0x64, 0x64, - 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, - 0x0a, 0x0e, 0x50, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x52, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x22, 0x10, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x9b, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, - 0x0b, 0x6c, 0x6e, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x6e, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x2e, 0x0a, 0x13, 0x6c, 0x6e, 0x64, 0x5f, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6c, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, - 0x64, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x79, 0x6e, - 0x63, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x25, 0x0a, - 0x23, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x15, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x71, 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x77, 0x61, 0x69, 0x74, - 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, - 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x66, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x54, 0x0a, - 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, + 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x22, 0x43, + 0x0a, 0x11, 0x54, 0x61, 0x70, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x54, + 0x72, 0x65, 0x65, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x61, 0x70, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x4c, 0x65, 0x61, + 0x76, 0x65, 0x73, 0x22, 0x21, 0x0a, 0x07, 0x54, 0x61, 0x70, 0x4c, 0x65, 0x61, 0x66, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x70, 0x42, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x65, 0x66, 0x74, 0x5f, 0x74, 0x61, 0x70, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6c, 0x65, 0x66, 0x74, 0x54, + 0x61, 0x70, 0x68, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x5f, + 0x74, 0x61, 0x70, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x54, 0x61, 0x70, 0x68, 0x61, 0x73, 0x68, 0x22, 0x27, 0x0a, 0x11, 0x44, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x22, 0x56, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, + 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x61, 0x77, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x66, + 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x61, 0x77, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x65, 0x6e, 0x65, 0x73, + 0x69, 0x73, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xd7, 0x04, 0x0a, + 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x24, 0x0a, + 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x61, 0x74, 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x41, 0x74, 0x44, 0x65, + 0x70, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x23, 0x0a, + 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x74, + 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x32, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x61, + 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x78, 0x5f, 0x6d, 0x65, 0x72, + 0x6b, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0d, 0x74, 0x78, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x27, + 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, + 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x70, + 0x6c, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x32, 0x0a, 0x15, + 0x6e, 0x75, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6e, 0x75, 0x6d, + 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, + 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x69, + 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x68, 0x61, + 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, + 0x07, 0x69, 0x73, 0x5f, 0x62, 0x75, 0x72, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x69, 0x73, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x3c, 0x0a, 0x0e, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, + 0x73, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x52, + 0x65, 0x76, 0x65, 0x61, 0x6c, 0x52, 0x0d, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x52, 0x65, + 0x76, 0x65, 0x61, 0x6c, 0x12, 0x40, 0x0a, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x22, 0x66, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0d, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x70, + 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x0c, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xb1, + 0x01, 0x0a, 0x12, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x70, 0x72, 0x6f, + 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x61, 0x77, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x61, 0x74, 0x5f, 0x64, + 0x65, 0x70, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x41, 0x74, 0x44, 0x65, 0x70, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x77, 0x69, 0x74, 0x68, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x50, 0x72, 0x65, 0x76, 0x57, + 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, + 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x77, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x22, 0x50, 0x0a, 0x13, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x64, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, + 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x0c, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x7c, 0x0a, 0x12, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x22, 0xd0, 0x02, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, + 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x20, 0x0a, + 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, + 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0c, + 0x75, 0x74, 0x78, 0x6f, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x75, 0x74, 0x78, 0x6f, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x27, + 0x0a, 0x0f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x69, 0x62, 0x6c, 0x69, 0x6e, + 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, + 0x53, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x61, 0x73, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x68, 0x61, 0x73, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x74, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x3c, 0x0a, + 0x0d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x41, 0x0a, 0x14, 0x41, + 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, + 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x4a, + 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x70, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, + 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0e, 0x50, + 0x72, 0x65, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x52, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9b, 0x02, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6e, + 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x6c, 0x6e, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x2e, 0x0a, 0x13, 0x6c, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x6c, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x6c, + 0x69, 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, + 0x6f, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, + 0x79, 0x6e, 0x63, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x25, 0x0a, 0x23, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, + 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x15, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x71, + 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x54, 0x0a, 0x15, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x22, 0xbc, 0x01, 0x0a, 0x1d, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, + 0x3e, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x28, 0x0a, 0x26, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7d, 0x0a, 0x19, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x1d, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x66, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x23, 0x0a, - 0x0d, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x64, 0x64, 0x72, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xf5, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x71, + 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, - 0x70, 0x65, 0x22, 0x28, 0x0a, 0x26, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7d, 0x0a, 0x19, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xf5, 0x01, 0x0a, 0x11, - 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x71, 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x5f, 0x77, 0x61, 0x69, 0x74, - 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, - 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x66, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x64, 0x0a, 0x1c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x19, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x15, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, - 0x00, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x6d, 0x65, - 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x53, 0x74, 0x72, 0x12, 0x24, 0x0a, - 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, - 0x53, 0x74, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xaf, 0x01, 0x0a, - 0x10, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x22, - 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x53, - 0x74, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, - 0x62, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x54, 0x6f, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x65, 0x78, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x84, - 0x01, 0x0a, 0x11, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x52, 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x12, 0x33, 0x0a, 0x0a, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x09, 0x62, 0x75, 0x72, 0x6e, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x41, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, 0x28, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, - 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x42, 0x4c, 0x45, - 0x10, 0x01, 0x2a, 0x39, 0x0a, 0x0d, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4f, 0x50, 0x41, 0x51, 0x55, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, - 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x3a, 0x0a, - 0x0c, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, - 0x10, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x56, - 0x30, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x56, 0x45, 0x52, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x31, 0x10, 0x01, 0x2a, 0xb0, 0x01, 0x0a, 0x0a, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x55, 0x54, 0x50, - 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x10, 0x00, - 0x12, 0x1a, 0x0a, 0x16, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x53, 0x50, 0x4c, 0x49, 0x54, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, - 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x41, 0x53, 0x53, - 0x49, 0x56, 0x45, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, - 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x50, 0x41, 0x53, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x53, 0x50, 0x4c, 0x49, 0x54, 0x5f, 0x52, - 0x4f, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x50, 0x41, 0x53, 0x53, - 0x49, 0x56, 0x45, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x53, 0x10, 0x04, 0x2a, 0xd0, 0x01, 0x0a, - 0x0f, 0x41, 0x64, 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x2a, 0x0a, 0x26, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x41, + 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x57, 0x61, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x64, 0x0a, 0x1c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x19, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0xa6, 0x01, 0x0a, 0x15, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, + 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x08, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x61, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x53, 0x74, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x65, + 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, + 0x42, 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xaf, 0x01, 0x0a, 0x10, 0x42, 0x75, + 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x48, 0x00, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x53, 0x74, 0x72, 0x12, + 0x24, 0x0a, 0x0e, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x62, 0x75, 0x72, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, + 0x6f, 0x42, 0x75, 0x72, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, + 0x78, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x84, 0x01, 0x0a, 0x11, + 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, + 0x0c, 0x62, 0x75, 0x72, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x33, 0x0a, + 0x0a, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x09, 0x62, 0x75, 0x72, 0x6e, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x22, 0x41, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, 0x28, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0f, + 0x0a, 0x0b, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x2a, + 0x39, 0x0a, 0x0d, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x50, + 0x41, 0x51, 0x55, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, 0x41, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x3a, 0x0a, 0x0c, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x53, + 0x53, 0x45, 0x54, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x30, 0x10, 0x00, + 0x12, 0x14, 0x0a, 0x10, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x56, 0x31, 0x10, 0x01, 0x2a, 0xb0, 0x01, 0x0a, 0x0a, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, + 0x16, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x50, 0x4c, + 0x49, 0x54, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x4f, 0x55, 0x54, + 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x49, 0x56, 0x45, + 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x22, + 0x0a, 0x1e, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x41, + 0x53, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x53, 0x50, 0x4c, 0x49, 0x54, 0x5f, 0x52, 0x4f, 0x4f, 0x54, + 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x49, 0x56, 0x45, + 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x53, 0x10, 0x04, 0x2a, 0xd0, 0x01, 0x0a, 0x0f, 0x41, 0x64, + 0x64, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, + 0x19, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x2a, 0x0a, 0x26, + 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, + 0x54, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x41, 0x44, 0x44, 0x52, + 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x52, + 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, + 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, + 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4f, 0x46, + 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, - 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x44, 0x44, 0x52, - 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x52, - 0x4f, 0x4f, 0x46, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1f, - 0x0a, 0x1b, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x04, 0x2a, - 0x52, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x4f, 0x46, 0x5f, 0x54, 0x52, - 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, - 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x4f, 0x46, 0x5f, 0x54, 0x52, 0x41, 0x4e, - 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, - 0x45, 0x10, 0x01, 0x32, 0x86, 0x0b, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x74, 0x78, - 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x4c, 0x69, - 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x49, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, - 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, + 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x52, 0x0a, 0x11, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x4f, 0x46, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, + 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x4f, 0x46, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x10, 0x01, + 0x32, 0x86, 0x0b, 0x0a, 0x0d, 0x54, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, + 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, + 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x4c, 0x69, - 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, - 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, - 0x64, 0x64, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x4e, 0x65, 0x77, - 0x41, 0x64, 0x64, 0x72, 0x12, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, - 0x77, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x74, - 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x35, 0x0a, 0x0a, 0x44, 0x65, - 0x63, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, - 0x72, 0x12, 0x49, 0x0a, 0x0c, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, + 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x74, + 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, + 0x73, 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, + 0x72, 0x12, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, + 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x35, 0x0a, 0x0a, 0x44, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0c, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x49, + 0x0a, 0x0c, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, - 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x11, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x1a, 0x1b, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x44, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x1a, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0x12, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, - 0x65, 0x12, 0x40, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x18, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x18, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x65, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, - 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, - 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x6e, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, - 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x46, 0x65, 0x74, 0x63, - 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x2e, 0x74, 0x61, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x30, 0x5a, 0x2e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, - 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x1a, 0x1b, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x44, 0x65, 0x63, 0x6f, + 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3c, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, + 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x40, + 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x40, 0x0a, 0x09, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x18, 0x2e, + 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x42, 0x75, 0x72, 0x6e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, + 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, + 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x65, 0x6e, 0x64, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x12, 0x2b, + 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, + 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x6e, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x12, 0x2e, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -5745,7 +5910,7 @@ func file_taprootassets_proto_rawDescGZIP() []byte { } var file_taprootassets_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_taprootassets_proto_msgTypes = make([]protoimpl.MessageInfo, 69) +var file_taprootassets_proto_msgTypes = make([]protoimpl.MessageInfo, 72) var file_taprootassets_proto_goTypes = []interface{}{ (AssetType)(0), // 0: taprpc.AssetType (AssetMetaType)(0), // 1: taprpc.AssetMetaType @@ -5792,36 +5957,39 @@ var file_taprootassets_proto_goTypes = []interface{}{ (*ScriptKey)(nil), // 42: taprpc.ScriptKey (*KeyLocator)(nil), // 43: taprpc.KeyLocator (*KeyDescriptor)(nil), // 44: taprpc.KeyDescriptor - (*DecodeAddrRequest)(nil), // 45: taprpc.DecodeAddrRequest - (*ProofFile)(nil), // 46: taprpc.ProofFile - (*DecodedProof)(nil), // 47: taprpc.DecodedProof - (*VerifyProofResponse)(nil), // 48: taprpc.VerifyProofResponse - (*DecodeProofRequest)(nil), // 49: taprpc.DecodeProofRequest - (*DecodeProofResponse)(nil), // 50: taprpc.DecodeProofResponse - (*ExportProofRequest)(nil), // 51: taprpc.ExportProofRequest - (*AddrEvent)(nil), // 52: taprpc.AddrEvent - (*AddrReceivesRequest)(nil), // 53: taprpc.AddrReceivesRequest - (*AddrReceivesResponse)(nil), // 54: taprpc.AddrReceivesResponse - (*SendAssetRequest)(nil), // 55: taprpc.SendAssetRequest - (*PrevInputAsset)(nil), // 56: taprpc.PrevInputAsset - (*SendAssetResponse)(nil), // 57: taprpc.SendAssetResponse - (*GetInfoRequest)(nil), // 58: taprpc.GetInfoRequest - (*GetInfoResponse)(nil), // 59: taprpc.GetInfoResponse - (*SubscribeSendAssetEventNtfnsRequest)(nil), // 60: taprpc.SubscribeSendAssetEventNtfnsRequest - (*SendAssetEvent)(nil), // 61: taprpc.SendAssetEvent - (*ExecuteSendStateEvent)(nil), // 62: taprpc.ExecuteSendStateEvent - (*ProofTransferBackoffWaitEvent)(nil), // 63: taprpc.ProofTransferBackoffWaitEvent - (*SubscribeReceiveAssetEventNtfnsRequest)(nil), // 64: taprpc.SubscribeReceiveAssetEventNtfnsRequest - (*AssetReceiveCompleteEvent)(nil), // 65: taprpc.AssetReceiveCompleteEvent - (*ReceiveAssetEvent)(nil), // 66: taprpc.ReceiveAssetEvent - (*FetchAssetMetaRequest)(nil), // 67: taprpc.FetchAssetMetaRequest - (*BurnAssetRequest)(nil), // 68: taprpc.BurnAssetRequest - (*BurnAssetResponse)(nil), // 69: taprpc.BurnAssetResponse - (*OutPoint)(nil), // 70: taprpc.OutPoint - nil, // 71: taprpc.ListUtxosResponse.ManagedUtxosEntry - nil, // 72: taprpc.ListGroupsResponse.GroupsEntry - nil, // 73: taprpc.ListBalancesResponse.AssetBalancesEntry - nil, // 74: taprpc.ListBalancesResponse.AssetGroupBalancesEntry + (*TapscriptFullTree)(nil), // 45: taprpc.TapscriptFullTree + (*TapLeaf)(nil), // 46: taprpc.TapLeaf + (*TapBranch)(nil), // 47: taprpc.TapBranch + (*DecodeAddrRequest)(nil), // 48: taprpc.DecodeAddrRequest + (*ProofFile)(nil), // 49: taprpc.ProofFile + (*DecodedProof)(nil), // 50: taprpc.DecodedProof + (*VerifyProofResponse)(nil), // 51: taprpc.VerifyProofResponse + (*DecodeProofRequest)(nil), // 52: taprpc.DecodeProofRequest + (*DecodeProofResponse)(nil), // 53: taprpc.DecodeProofResponse + (*ExportProofRequest)(nil), // 54: taprpc.ExportProofRequest + (*AddrEvent)(nil), // 55: taprpc.AddrEvent + (*AddrReceivesRequest)(nil), // 56: taprpc.AddrReceivesRequest + (*AddrReceivesResponse)(nil), // 57: taprpc.AddrReceivesResponse + (*SendAssetRequest)(nil), // 58: taprpc.SendAssetRequest + (*PrevInputAsset)(nil), // 59: taprpc.PrevInputAsset + (*SendAssetResponse)(nil), // 60: taprpc.SendAssetResponse + (*GetInfoRequest)(nil), // 61: taprpc.GetInfoRequest + (*GetInfoResponse)(nil), // 62: taprpc.GetInfoResponse + (*SubscribeSendAssetEventNtfnsRequest)(nil), // 63: taprpc.SubscribeSendAssetEventNtfnsRequest + (*SendAssetEvent)(nil), // 64: taprpc.SendAssetEvent + (*ExecuteSendStateEvent)(nil), // 65: taprpc.ExecuteSendStateEvent + (*ProofTransferBackoffWaitEvent)(nil), // 66: taprpc.ProofTransferBackoffWaitEvent + (*SubscribeReceiveAssetEventNtfnsRequest)(nil), // 67: taprpc.SubscribeReceiveAssetEventNtfnsRequest + (*AssetReceiveCompleteEvent)(nil), // 68: taprpc.AssetReceiveCompleteEvent + (*ReceiveAssetEvent)(nil), // 69: taprpc.ReceiveAssetEvent + (*FetchAssetMetaRequest)(nil), // 70: taprpc.FetchAssetMetaRequest + (*BurnAssetRequest)(nil), // 71: taprpc.BurnAssetRequest + (*BurnAssetResponse)(nil), // 72: taprpc.BurnAssetResponse + (*OutPoint)(nil), // 73: taprpc.OutPoint + nil, // 74: taprpc.ListUtxosResponse.ManagedUtxosEntry + nil, // 75: taprpc.ListGroupsResponse.GroupsEntry + nil, // 76: taprpc.ListBalancesResponse.AssetBalancesEntry + nil, // 77: taprpc.ListBalancesResponse.AssetGroupBalancesEntry } var file_taprootassets_proto_depIdxs = []int32{ 1, // 0: taprpc.AssetMeta.type:type_name -> taprpc.AssetMetaType @@ -5832,19 +6000,19 @@ var file_taprootassets_proto_depIdxs = []int32{ 10, // 5: taprpc.Asset.asset_group:type_name -> taprpc.AssetGroup 8, // 6: taprpc.Asset.chain_anchor:type_name -> taprpc.AnchorInfo 14, // 7: taprpc.Asset.prev_witnesses:type_name -> taprpc.PrevWitness - 56, // 8: taprpc.PrevWitness.prev_id:type_name -> taprpc.PrevInputAsset + 59, // 8: taprpc.PrevWitness.prev_id:type_name -> taprpc.PrevInputAsset 15, // 9: taprpc.PrevWitness.split_commitment:type_name -> taprpc.SplitCommitment 13, // 10: taprpc.SplitCommitment.root_asset:type_name -> taprpc.Asset 13, // 11: taprpc.ListAssetResponse.assets:type_name -> taprpc.Asset 13, // 12: taprpc.ManagedUtxo.assets:type_name -> taprpc.Asset - 71, // 13: taprpc.ListUtxosResponse.managed_utxos:type_name -> taprpc.ListUtxosResponse.ManagedUtxosEntry + 74, // 13: taprpc.ListUtxosResponse.managed_utxos:type_name -> taprpc.ListUtxosResponse.ManagedUtxosEntry 0, // 14: taprpc.AssetHumanReadable.type:type_name -> taprpc.AssetType 2, // 15: taprpc.AssetHumanReadable.version:type_name -> taprpc.AssetVersion 21, // 16: taprpc.GroupedAssets.assets:type_name -> taprpc.AssetHumanReadable - 72, // 17: taprpc.ListGroupsResponse.groups:type_name -> taprpc.ListGroupsResponse.GroupsEntry + 75, // 17: taprpc.ListGroupsResponse.groups:type_name -> taprpc.ListGroupsResponse.GroupsEntry 9, // 18: taprpc.AssetBalance.asset_genesis:type_name -> taprpc.GenesisInfo - 73, // 19: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry - 74, // 20: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry + 76, // 19: taprpc.ListBalancesResponse.asset_balances:type_name -> taprpc.ListBalancesResponse.AssetBalancesEntry + 77, // 20: taprpc.ListBalancesResponse.asset_group_balances:type_name -> taprpc.ListBalancesResponse.AssetGroupBalancesEntry 30, // 21: taprpc.ListTransfersResponse.transfers:type_name -> taprpc.AssetTransfer 31, // 22: taprpc.AssetTransfer.inputs:type_name -> taprpc.TransferInput 33, // 23: taprpc.AssetTransfer.outputs:type_name -> taprpc.TransferOutput @@ -5859,75 +6027,76 @@ var file_taprootassets_proto_depIdxs = []int32{ 2, // 32: taprpc.NewAddrRequest.asset_version:type_name -> taprpc.AssetVersion 44, // 33: taprpc.ScriptKey.key_desc:type_name -> taprpc.KeyDescriptor 43, // 34: taprpc.KeyDescriptor.key_loc:type_name -> taprpc.KeyLocator - 13, // 35: taprpc.DecodedProof.asset:type_name -> taprpc.Asset - 6, // 36: taprpc.DecodedProof.meta_reveal:type_name -> taprpc.AssetMeta - 12, // 37: taprpc.DecodedProof.genesis_reveal:type_name -> taprpc.GenesisReveal - 11, // 38: taprpc.DecodedProof.group_key_reveal:type_name -> taprpc.GroupKeyReveal - 47, // 39: taprpc.VerifyProofResponse.decoded_proof:type_name -> taprpc.DecodedProof - 47, // 40: taprpc.DecodeProofResponse.decoded_proof:type_name -> taprpc.DecodedProof - 70, // 41: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint - 38, // 42: taprpc.AddrEvent.addr:type_name -> taprpc.Addr - 4, // 43: taprpc.AddrEvent.status:type_name -> taprpc.AddrEventStatus - 4, // 44: taprpc.AddrReceivesRequest.filter_status:type_name -> taprpc.AddrEventStatus - 52, // 45: taprpc.AddrReceivesResponse.events:type_name -> taprpc.AddrEvent - 30, // 46: taprpc.SendAssetResponse.transfer:type_name -> taprpc.AssetTransfer - 62, // 47: taprpc.SendAssetEvent.execute_send_state_event:type_name -> taprpc.ExecuteSendStateEvent - 63, // 48: taprpc.SendAssetEvent.proof_transfer_backoff_wait_event:type_name -> taprpc.ProofTransferBackoffWaitEvent - 5, // 49: taprpc.ProofTransferBackoffWaitEvent.transfer_type:type_name -> taprpc.ProofTransferType - 38, // 50: taprpc.AssetReceiveCompleteEvent.address:type_name -> taprpc.Addr - 63, // 51: taprpc.ReceiveAssetEvent.proof_transfer_backoff_wait_event:type_name -> taprpc.ProofTransferBackoffWaitEvent - 65, // 52: taprpc.ReceiveAssetEvent.asset_receive_complete_event:type_name -> taprpc.AssetReceiveCompleteEvent - 30, // 53: taprpc.BurnAssetResponse.burn_transfer:type_name -> taprpc.AssetTransfer - 47, // 54: taprpc.BurnAssetResponse.burn_proof:type_name -> taprpc.DecodedProof - 18, // 55: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo - 22, // 56: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets - 25, // 57: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance - 26, // 58: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance - 7, // 59: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest - 17, // 60: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest - 20, // 61: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest - 24, // 62: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest - 28, // 63: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest - 34, // 64: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest - 36, // 65: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest - 39, // 66: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest - 41, // 67: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest - 45, // 68: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest - 53, // 69: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest - 46, // 70: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile - 49, // 71: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest - 51, // 72: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest - 55, // 73: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest - 68, // 74: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest - 58, // 75: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest - 60, // 76: taprpc.TaprootAssets.SubscribeSendAssetEventNtfns:input_type -> taprpc.SubscribeSendAssetEventNtfnsRequest - 64, // 77: taprpc.TaprootAssets.SubscribeReceiveAssetEventNtfns:input_type -> taprpc.SubscribeReceiveAssetEventNtfnsRequest - 67, // 78: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest - 16, // 79: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse - 19, // 80: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse - 23, // 81: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse - 27, // 82: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse - 29, // 83: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse - 35, // 84: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse - 37, // 85: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse - 40, // 86: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse - 38, // 87: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr - 38, // 88: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr - 54, // 89: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse - 48, // 90: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse - 50, // 91: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse - 46, // 92: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile - 57, // 93: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse - 69, // 94: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse - 59, // 95: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse - 61, // 96: taprpc.TaprootAssets.SubscribeSendAssetEventNtfns:output_type -> taprpc.SendAssetEvent - 66, // 97: taprpc.TaprootAssets.SubscribeReceiveAssetEventNtfns:output_type -> taprpc.ReceiveAssetEvent - 6, // 98: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.AssetMeta - 79, // [79:99] is the sub-list for method output_type - 59, // [59:79] is the sub-list for method input_type - 59, // [59:59] is the sub-list for extension type_name - 59, // [59:59] is the sub-list for extension extendee - 0, // [0:59] is the sub-list for field type_name + 46, // 35: taprpc.TapscriptFullTree.all_leaves:type_name -> taprpc.TapLeaf + 13, // 36: taprpc.DecodedProof.asset:type_name -> taprpc.Asset + 6, // 37: taprpc.DecodedProof.meta_reveal:type_name -> taprpc.AssetMeta + 12, // 38: taprpc.DecodedProof.genesis_reveal:type_name -> taprpc.GenesisReveal + 11, // 39: taprpc.DecodedProof.group_key_reveal:type_name -> taprpc.GroupKeyReveal + 50, // 40: taprpc.VerifyProofResponse.decoded_proof:type_name -> taprpc.DecodedProof + 50, // 41: taprpc.DecodeProofResponse.decoded_proof:type_name -> taprpc.DecodedProof + 73, // 42: taprpc.ExportProofRequest.outpoint:type_name -> taprpc.OutPoint + 38, // 43: taprpc.AddrEvent.addr:type_name -> taprpc.Addr + 4, // 44: taprpc.AddrEvent.status:type_name -> taprpc.AddrEventStatus + 4, // 45: taprpc.AddrReceivesRequest.filter_status:type_name -> taprpc.AddrEventStatus + 55, // 46: taprpc.AddrReceivesResponse.events:type_name -> taprpc.AddrEvent + 30, // 47: taprpc.SendAssetResponse.transfer:type_name -> taprpc.AssetTransfer + 65, // 48: taprpc.SendAssetEvent.execute_send_state_event:type_name -> taprpc.ExecuteSendStateEvent + 66, // 49: taprpc.SendAssetEvent.proof_transfer_backoff_wait_event:type_name -> taprpc.ProofTransferBackoffWaitEvent + 5, // 50: taprpc.ProofTransferBackoffWaitEvent.transfer_type:type_name -> taprpc.ProofTransferType + 38, // 51: taprpc.AssetReceiveCompleteEvent.address:type_name -> taprpc.Addr + 66, // 52: taprpc.ReceiveAssetEvent.proof_transfer_backoff_wait_event:type_name -> taprpc.ProofTransferBackoffWaitEvent + 68, // 53: taprpc.ReceiveAssetEvent.asset_receive_complete_event:type_name -> taprpc.AssetReceiveCompleteEvent + 30, // 54: taprpc.BurnAssetResponse.burn_transfer:type_name -> taprpc.AssetTransfer + 50, // 55: taprpc.BurnAssetResponse.burn_proof:type_name -> taprpc.DecodedProof + 18, // 56: taprpc.ListUtxosResponse.ManagedUtxosEntry.value:type_name -> taprpc.ManagedUtxo + 22, // 57: taprpc.ListGroupsResponse.GroupsEntry.value:type_name -> taprpc.GroupedAssets + 25, // 58: taprpc.ListBalancesResponse.AssetBalancesEntry.value:type_name -> taprpc.AssetBalance + 26, // 59: taprpc.ListBalancesResponse.AssetGroupBalancesEntry.value:type_name -> taprpc.AssetGroupBalance + 7, // 60: taprpc.TaprootAssets.ListAssets:input_type -> taprpc.ListAssetRequest + 17, // 61: taprpc.TaprootAssets.ListUtxos:input_type -> taprpc.ListUtxosRequest + 20, // 62: taprpc.TaprootAssets.ListGroups:input_type -> taprpc.ListGroupsRequest + 24, // 63: taprpc.TaprootAssets.ListBalances:input_type -> taprpc.ListBalancesRequest + 28, // 64: taprpc.TaprootAssets.ListTransfers:input_type -> taprpc.ListTransfersRequest + 34, // 65: taprpc.TaprootAssets.StopDaemon:input_type -> taprpc.StopRequest + 36, // 66: taprpc.TaprootAssets.DebugLevel:input_type -> taprpc.DebugLevelRequest + 39, // 67: taprpc.TaprootAssets.QueryAddrs:input_type -> taprpc.QueryAddrRequest + 41, // 68: taprpc.TaprootAssets.NewAddr:input_type -> taprpc.NewAddrRequest + 48, // 69: taprpc.TaprootAssets.DecodeAddr:input_type -> taprpc.DecodeAddrRequest + 56, // 70: taprpc.TaprootAssets.AddrReceives:input_type -> taprpc.AddrReceivesRequest + 49, // 71: taprpc.TaprootAssets.VerifyProof:input_type -> taprpc.ProofFile + 52, // 72: taprpc.TaprootAssets.DecodeProof:input_type -> taprpc.DecodeProofRequest + 54, // 73: taprpc.TaprootAssets.ExportProof:input_type -> taprpc.ExportProofRequest + 58, // 74: taprpc.TaprootAssets.SendAsset:input_type -> taprpc.SendAssetRequest + 71, // 75: taprpc.TaprootAssets.BurnAsset:input_type -> taprpc.BurnAssetRequest + 61, // 76: taprpc.TaprootAssets.GetInfo:input_type -> taprpc.GetInfoRequest + 63, // 77: taprpc.TaprootAssets.SubscribeSendAssetEventNtfns:input_type -> taprpc.SubscribeSendAssetEventNtfnsRequest + 67, // 78: taprpc.TaprootAssets.SubscribeReceiveAssetEventNtfns:input_type -> taprpc.SubscribeReceiveAssetEventNtfnsRequest + 70, // 79: taprpc.TaprootAssets.FetchAssetMeta:input_type -> taprpc.FetchAssetMetaRequest + 16, // 80: taprpc.TaprootAssets.ListAssets:output_type -> taprpc.ListAssetResponse + 19, // 81: taprpc.TaprootAssets.ListUtxos:output_type -> taprpc.ListUtxosResponse + 23, // 82: taprpc.TaprootAssets.ListGroups:output_type -> taprpc.ListGroupsResponse + 27, // 83: taprpc.TaprootAssets.ListBalances:output_type -> taprpc.ListBalancesResponse + 29, // 84: taprpc.TaprootAssets.ListTransfers:output_type -> taprpc.ListTransfersResponse + 35, // 85: taprpc.TaprootAssets.StopDaemon:output_type -> taprpc.StopResponse + 37, // 86: taprpc.TaprootAssets.DebugLevel:output_type -> taprpc.DebugLevelResponse + 40, // 87: taprpc.TaprootAssets.QueryAddrs:output_type -> taprpc.QueryAddrResponse + 38, // 88: taprpc.TaprootAssets.NewAddr:output_type -> taprpc.Addr + 38, // 89: taprpc.TaprootAssets.DecodeAddr:output_type -> taprpc.Addr + 57, // 90: taprpc.TaprootAssets.AddrReceives:output_type -> taprpc.AddrReceivesResponse + 51, // 91: taprpc.TaprootAssets.VerifyProof:output_type -> taprpc.VerifyProofResponse + 53, // 92: taprpc.TaprootAssets.DecodeProof:output_type -> taprpc.DecodeProofResponse + 49, // 93: taprpc.TaprootAssets.ExportProof:output_type -> taprpc.ProofFile + 60, // 94: taprpc.TaprootAssets.SendAsset:output_type -> taprpc.SendAssetResponse + 72, // 95: taprpc.TaprootAssets.BurnAsset:output_type -> taprpc.BurnAssetResponse + 62, // 96: taprpc.TaprootAssets.GetInfo:output_type -> taprpc.GetInfoResponse + 64, // 97: taprpc.TaprootAssets.SubscribeSendAssetEventNtfns:output_type -> taprpc.SendAssetEvent + 69, // 98: taprpc.TaprootAssets.SubscribeReceiveAssetEventNtfns:output_type -> taprpc.ReceiveAssetEvent + 6, // 99: taprpc.TaprootAssets.FetchAssetMeta:output_type -> taprpc.AssetMeta + 80, // [80:100] is the sub-list for method output_type + 60, // [60:80] is the sub-list for method input_type + 60, // [60:60] is the sub-list for extension type_name + 60, // [60:60] is the sub-list for extension extendee + 0, // [0:60] is the sub-list for field type_name } func init() { file_taprootassets_proto_init() } @@ -6405,7 +6574,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecodeAddrRequest); i { + switch v := v.(*TapscriptFullTree); i { case 0: return &v.state case 1: @@ -6417,7 +6586,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProofFile); i { + switch v := v.(*TapLeaf); i { case 0: return &v.state case 1: @@ -6429,7 +6598,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecodedProof); i { + switch v := v.(*TapBranch); i { case 0: return &v.state case 1: @@ -6441,7 +6610,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyProofResponse); i { + switch v := v.(*DecodeAddrRequest); i { case 0: return &v.state case 1: @@ -6453,7 +6622,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecodeProofRequest); i { + switch v := v.(*ProofFile); i { case 0: return &v.state case 1: @@ -6465,7 +6634,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DecodeProofResponse); i { + switch v := v.(*DecodedProof); i { case 0: return &v.state case 1: @@ -6477,7 +6646,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExportProofRequest); i { + switch v := v.(*VerifyProofResponse); i { case 0: return &v.state case 1: @@ -6489,7 +6658,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddrEvent); i { + switch v := v.(*DecodeProofRequest); i { case 0: return &v.state case 1: @@ -6501,7 +6670,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddrReceivesRequest); i { + switch v := v.(*DecodeProofResponse); i { case 0: return &v.state case 1: @@ -6513,7 +6682,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddrReceivesResponse); i { + switch v := v.(*ExportProofRequest); i { case 0: return &v.state case 1: @@ -6525,7 +6694,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendAssetRequest); i { + switch v := v.(*AddrEvent); i { case 0: return &v.state case 1: @@ -6537,7 +6706,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PrevInputAsset); i { + switch v := v.(*AddrReceivesRequest); i { case 0: return &v.state case 1: @@ -6549,7 +6718,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendAssetResponse); i { + switch v := v.(*AddrReceivesResponse); i { case 0: return &v.state case 1: @@ -6561,7 +6730,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetInfoRequest); i { + switch v := v.(*SendAssetRequest); i { case 0: return &v.state case 1: @@ -6573,7 +6742,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetInfoResponse); i { + switch v := v.(*PrevInputAsset); i { case 0: return &v.state case 1: @@ -6585,7 +6754,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeSendAssetEventNtfnsRequest); i { + switch v := v.(*SendAssetResponse); i { case 0: return &v.state case 1: @@ -6597,7 +6766,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendAssetEvent); i { + switch v := v.(*GetInfoRequest); i { case 0: return &v.state case 1: @@ -6609,7 +6778,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExecuteSendStateEvent); i { + switch v := v.(*GetInfoResponse); i { case 0: return &v.state case 1: @@ -6621,7 +6790,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProofTransferBackoffWaitEvent); i { + switch v := v.(*SubscribeSendAssetEventNtfnsRequest); i { case 0: return &v.state case 1: @@ -6633,7 +6802,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeReceiveAssetEventNtfnsRequest); i { + switch v := v.(*SendAssetEvent); i { case 0: return &v.state case 1: @@ -6645,7 +6814,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AssetReceiveCompleteEvent); i { + switch v := v.(*ExecuteSendStateEvent); i { case 0: return &v.state case 1: @@ -6657,7 +6826,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReceiveAssetEvent); i { + switch v := v.(*ProofTransferBackoffWaitEvent); i { case 0: return &v.state case 1: @@ -6669,7 +6838,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAssetMetaRequest); i { + switch v := v.(*SubscribeReceiveAssetEventNtfnsRequest); i { case 0: return &v.state case 1: @@ -6681,7 +6850,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BurnAssetRequest); i { + switch v := v.(*AssetReceiveCompleteEvent); i { case 0: return &v.state case 1: @@ -6693,7 +6862,7 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BurnAssetResponse); i { + switch v := v.(*ReceiveAssetEvent); i { case 0: return &v.state case 1: @@ -6705,6 +6874,42 @@ func file_taprootassets_proto_init() { } } file_taprootassets_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FetchAssetMetaRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BurnAssetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BurnAssetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_taprootassets_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OutPoint); i { case 0: return &v.state @@ -6721,21 +6926,21 @@ func file_taprootassets_proto_init() { (*ListBalancesRequest_AssetId)(nil), (*ListBalancesRequest_GroupKey)(nil), } - file_taprootassets_proto_msgTypes[55].OneofWrappers = []interface{}{ + file_taprootassets_proto_msgTypes[58].OneofWrappers = []interface{}{ (*SendAssetEvent_ExecuteSendStateEvent)(nil), (*SendAssetEvent_ProofTransferBackoffWaitEvent)(nil), } - file_taprootassets_proto_msgTypes[60].OneofWrappers = []interface{}{ + file_taprootassets_proto_msgTypes[63].OneofWrappers = []interface{}{ (*ReceiveAssetEvent_ProofTransferBackoffWaitEvent)(nil), (*ReceiveAssetEvent_AssetReceiveCompleteEvent)(nil), } - file_taprootassets_proto_msgTypes[61].OneofWrappers = []interface{}{ + file_taprootassets_proto_msgTypes[64].OneofWrappers = []interface{}{ (*FetchAssetMetaRequest_AssetId)(nil), (*FetchAssetMetaRequest_MetaHash)(nil), (*FetchAssetMetaRequest_AssetIdStr)(nil), (*FetchAssetMetaRequest_MetaHashStr)(nil), } - file_taprootassets_proto_msgTypes[62].OneofWrappers = []interface{}{ + file_taprootassets_proto_msgTypes[65].OneofWrappers = []interface{}{ (*BurnAssetRequest_AssetId)(nil), (*BurnAssetRequest_AssetIdStr)(nil), } @@ -6745,7 +6950,7 @@ func file_taprootassets_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_taprootassets_proto_rawDesc, NumEnums: 6, - NumMessages: 69, + NumMessages: 72, NumExtensions: 0, NumServices: 1, }, diff --git a/taprpc/taprootassets.proto b/taprpc/taprootassets.proto index ef85afac5..482492b1c 100644 --- a/taprpc/taprootassets.proto +++ b/taprpc/taprootassets.proto @@ -755,6 +755,26 @@ message KeyDescriptor { KeyLocator key_loc = 2; } +message TapscriptFullTree { + /* + The complete, ordered list of all tap leaves of the tree. + */ + repeated TapLeaf all_leaves = 1; +} + +message TapLeaf { + // The script of the tap leaf. + bytes script = 2; +} + +message TapBranch { + // The TapHash of the left child of the root hash of a Tapscript tree. + bytes left_taphash = 1; + + // The TapHash of the right child of the root hash of a Tapscript tree. + bytes right_taphash = 2; +} + message DecodeAddrRequest { string addr = 1; } diff --git a/tapsend/send_test.go b/tapsend/send_test.go index 646e12e2d..713dd3234 100644 --- a/tapsend/send_test.go +++ b/tapsend/send_test.go @@ -1070,18 +1070,19 @@ var createOutputCommitmentsTestCases = []testCase{{ ) tpl := pkt.Outputs[1] - testPreimage := commitment.NewPreimageFromLeaf( + testPreimage, err := commitment.NewPreimageFromLeaf( txscript.TapLeaf{ LeafVersion: txscript.BaseLeafVersion, Script: []byte("not a valid script"), }, ) + require.NoError(t, err) pkt.Outputs = append(pkt.Outputs, &tappsbt.VOutput{ AnchorOutputIndex: tpl.AnchorOutputIndex, AnchorOutputTapscriptSibling: testPreimage, }) - _, err := tapsend.CreateOutputCommitments(nil, pkt, nil) + _, err = tapsend.CreateOutputCommitments(nil, pkt, nil) return err }, err: tapsend.ErrInvalidAnchorInfo, @@ -2171,9 +2172,10 @@ func TestPayToAddrScript(t *testing.T) { ) // And now the same with an address that has a tapscript sibling. - sibling := commitment.NewPreimageFromLeaf(txscript.NewBaseTapLeaf( + sibling, err := commitment.NewPreimageFromLeaf(txscript.NewBaseTapLeaf( []byte("not a valid script"), )) + require.NoError(t, err) addr2, err := address.New( address.V0, gen, nil, nil, *recipientScriptKey.PubKey, *internalKey, sendAmt, sibling, &address.RegressionNetTap,