From 0213563bb8674cf8d79a5599f4e6bbc887ca4cdc Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Wed, 31 Jan 2024 21:28:09 -0500 Subject: [PATCH 01/19] fn: update helper funcs for option and either --- fn/either.go | 22 ++++++++++++++++++++++ fn/option.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) 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. From 117e19bdf634be31a2a0558d678ed30d8e20e916 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 12 Jan 2024 17:54:02 -0500 Subject: [PATCH 02/19] asset: add en/decode support for taptree nodes --- asset/asset.go | 171 ++++++++++++++++++++++++++++++++++++++++++++ asset/asset_test.go | 171 +++++++++++++++++++++++++++++++++++++++++++- asset/encoding.go | 136 +++++++++++++++++++++++++++++++++++ 3 files changed, 476 insertions(+), 2 deletions(-) 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..73fd72162 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,131 @@ 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, fmt.Errorf("invalid tapscript branch data, not 2" + + "elements") + } + + 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 +} From db7c1de851193a038d31304cd1d27ef8c6f9074e Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 12 Jan 2024 17:55:47 -0500 Subject: [PATCH 03/19] asset+tapgarden: TapscriptTreeManager interface In this commit, we introduce the TapscriptTreeManager. This interface stores a tapscript tree associated with a root hash, which could be a stored alongside a batch key, asset script key, or group key. --- asset/encoding.go | 3 +-- asset/interface.go | 37 +++++++++++++++++++++++++++++++++++++ asset/mock.go | 5 +++-- internal/test/helpers.go | 11 +++++------ tapgarden/planter.go | 4 ++++ 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/asset/encoding.go b/asset/encoding.go index 73fd72162..281f8bce4 100644 --- a/asset/encoding.go +++ b/asset/encoding.go @@ -681,8 +681,7 @@ func EncodeTapBranchNodes(branch TapBranchNodes) [][]byte { func DecodeTapBranchNodes(branchData [][]byte) (*TapBranchNodes, error) { if len(branchData) != 2 { - return nil, fmt.Errorf("invalid tapscript branch data, not 2" + - "elements") + return nil, ErrInvalidTapBranch } left, right := branchData[0], branchData[1] 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/internal/test/helpers.go b/internal/test/helpers.go index 6db79f95b..f5a356206 100644 --- a/internal/test/helpers.go +++ b/internal/test/helpers.go @@ -393,19 +393,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/tapgarden/planter.go b/tapgarden/planter.go index e88d566cf..13d4d985e 100644 --- a/tapgarden/planter.go +++ b/tapgarden/planter.go @@ -34,6 +34,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. From 12f369624317c473a8403aa8a4079b85c5d7b299 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Mon, 5 Feb 2024 11:14:12 -0500 Subject: [PATCH 04/19] tapdb: add new tapscript tree tables and queries This commit adds three new tables that enable us to store tapscript trees, with deduplication of tapscript leaves. A tree can also be stored as two TapHashes representing a TapBranch, allowing for externally managed tapscript trees. These tables are not intended to be modified with the basic queries added in this commit, but wrapper functions in the next commit. --- tapdb/assets_store.go | 14 ++ tapdb/sqlc/assets.sql.go | 161 ++++++++++++++++++ .../000016_tapscript_trees.down.sql | 4 + .../migrations/000016_tapscript_trees.up.sql | 43 +++++ tapdb/sqlc/models.go | 18 ++ tapdb/sqlc/querier.go | 8 + tapdb/sqlc/queries/assets.sql | 79 +++++++++ 7 files changed, 327 insertions(+) create mode 100644 tapdb/sqlc/migrations/000016_tapscript_trees.down.sql create mode 100644 tapdb/sqlc/migrations/000016_tapscript_trees.up.sql 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..c75474d77 100644 --- a/tapdb/sqlc/assets.sql.go +++ b/tapdb/sqlc/assets.sql.go @@ -436,6 +436,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 @@ -1580,6 +1624,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 +2680,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..d52644541 --- /dev/null +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql @@ -0,0 +1,4 @@ +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; \ 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..2be8285d8 --- /dev/null +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql @@ -0,0 +1,43 @@ +-- 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..0a519c5fd 100644 --- a/tapdb/sqlc/models.go +++ b/tapdb/sqlc/models.go @@ -304,6 +304,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..8277b6b54 100644 --- a/tapdb/sqlc/querier.go +++ b/tapdb/sqlc/querier.go @@ -30,6 +30,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 +78,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 +170,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..d14a2e1eb 100644 --- a/tapdb/sqlc/queries/assets.sql +++ b/tapdb/sqlc/queries/assets.sql @@ -795,6 +795,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 From f4634c5768b5ae289fff9a3da49925bfd48aadd5 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 9 Feb 2024 03:28:28 -0500 Subject: [PATCH 05/19] tapdb: add tapscript sibling to batch table --- tapdb/asset_minting.go | 9 +++++ tapdb/sqlc/assets.sql.go | 35 +++++++++++++++++-- .../000016_tapscript_trees.down.sql | 3 +- .../migrations/000016_tapscript_trees.up.sql | 5 +++ tapdb/sqlc/models.go | 1 + tapdb/sqlc/querier.go | 1 + tapdb/sqlc/queries/assets.sql | 12 +++++++ 7 files changed, 62 insertions(+), 4 deletions(-) diff --git a/tapdb/asset_minting.go b/tapdb/asset_minting.go index e1d81ecf1..672cd114f 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 @@ -177,6 +181,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 diff --git a/tapdb/sqlc/assets.sql.go b/tapdb/sqlc/assets.sql.go index c75474d77..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 @@ -1364,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 @@ -1379,6 +1404,7 @@ type FetchMintingBatchRow struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte KeyID int64 RawKey []byte KeyFamily int32 @@ -1396,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, @@ -1405,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 @@ -1420,6 +1447,7 @@ type FetchMintingBatchesByInverseStateRow struct { GenesisID sql.NullInt64 HeightHint int32 CreationTimeUnix time.Time + TapscriptSibling []byte KeyID int64 RawKey []byte KeyFamily int32 @@ -1443,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, diff --git a/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql b/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql index d52644541..2ba9ffbe9 100644 --- a/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.down.sql @@ -1,4 +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; \ No newline at end of file +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 index 2be8285d8..4c0bd0021 100644 --- a/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql +++ b/tapdb/sqlc/migrations/000016_tapscript_trees.up.sql @@ -1,3 +1,8 @@ +-- 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 ( diff --git a/tapdb/sqlc/models.go b/tapdb/sqlc/models.go index 0a519c5fd..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 { diff --git a/tapdb/sqlc/querier.go b/tapdb/sqlc/querier.go index 8277b6b54..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 diff --git a/tapdb/sqlc/queries/assets.sql b/tapdb/sqlc/queries/assets.sql index d14a2e1eb..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 From 9160bfaa913acd663da8c3bfca8836f5da7a5eef Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Mon, 5 Feb 2024 11:24:16 -0500 Subject: [PATCH 06/19] tapdb: MintingStore supports TapscriptTreeManager In this commit, we expose helpers to upsert and delete tapscript trees. These are used to add support for tapscript trees during minting. --- internal/test/helpers.go | 16 ++ tapdb/asset_minting.go | 120 +++++++++++ tapdb/asset_minting_test.go | 391 ++++++++++++++++++++++++++++++++++++ tapdb/assets_common.go | 115 +++++++++++ 4 files changed, 642 insertions(+) diff --git a/internal/test/helpers.go b/internal/test/helpers.go index f5a356206..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 { diff --git a/tapdb/asset_minting.go b/tapdb/asset_minting.go index 672cd114f..353342e7a 100644 --- a/tapdb/asset_minting.go +++ b/tapdb/asset_minting.go @@ -137,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 @@ -1220,6 +1224,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..9e6c7b5c0 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" @@ -152,6 +153,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 @@ -1189,6 +1330,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 +} From a6e7f53490776c84ca48b24b7c170253f61eb5a0 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 12 Jan 2024 18:01:04 -0500 Subject: [PATCH 07/19] tapgarden: add tapscript tree arg to finalize call --- tapgarden/interface.go | 14 +++++- tapgarden/planter.go | 103 ++++++++++++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 20 deletions(-) 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/planter.go b/tapgarden/planter.go index 13d4d985e..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" @@ -127,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), @@ -362,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 @@ -374,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, @@ -500,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) @@ -605,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 @@ -676,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", @@ -740,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") From 491737d019db1a4a001dfeaf606098a689907771 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Mon, 22 Jan 2024 21:22:30 -0500 Subject: [PATCH 08/19] proof: add tapscript sibling as a minting blob opt --- proof/mint.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) 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) From 3e103a376fefd4d9255df50926890a9392bf9d48 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Tue, 20 Feb 2024 18:42:57 -0500 Subject: [PATCH 09/19] commitment: remove vers check in commitment checks In this commit, we remove an overly strict version check when asserting that a Tapscript script is not a Taproot Asset Commitment. All versions, including unknown future versions, should be rejected in this context. --- commitment/tap.go | 3 --- 1 file changed, 3 deletions(-) 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[:], From 13f60738d90588b1352dfc94eb496f5fccc6b9e7 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Mon, 22 Jan 2024 21:20:02 -0500 Subject: [PATCH 10/19] commitment: refactor tapscript preimage validation In this commit, we update how tapscript preimages are validated before use. We define safe constructors for tapscript preimages, and require their use by making the raw preimage bytes private. We also add checks when decoding a preimage to further prevent improper construction. --- commitment/encoding.go | 8 +- commitment/taproot.go | 256 +++++++++++++++++++++++-------------- commitment/taproot_test.go | 82 +++++++++--- 3 files changed, 230 insertions(+), 116 deletions(-) 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/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( From a030f57637e9d79fab42bc1c689d2e042c872044 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Wed, 21 Feb 2024 17:55:52 -0500 Subject: [PATCH 11/19] proof: update handling of tapscript preimages --- proof/records.go | 32 +++++++++++++++++++++------- proof/taproot.go | 55 ++++++++++++++++++------------------------------ 2 files changed, 45 insertions(+), 42 deletions(-) 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 } From f6dd14a08492ea5a083b46bba7d9d7faff86fd6a Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Wed, 21 Feb 2024 17:59:56 -0500 Subject: [PATCH 12/19] address: update handling of tapscript preimages --- address/address.go | 4 ++-- address/records.go | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) 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/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, From 2d3d1e12f718a204ae5a718190479918631aa084 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Wed, 21 Feb 2024 17:57:26 -0500 Subject: [PATCH 13/19] multi: update test handling of tapscript preimages --- address/address_test.go | 8 ++++-- address/mock.go | 4 ++- itest/psbt_test.go | 5 ++-- itest/round_trip_send_test.go | 3 ++- proof/mock.go | 8 +++--- proof/proof_test.go | 47 ++++++++++++++++------------------- tappsbt/mock.go | 5 ++-- tapsend/send_test.go | 8 +++--- 8 files changed, 48 insertions(+), 40 deletions(-) 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/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/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/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/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, From 8856f68196b18da56f73177a336293bac9c3f46a Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Tue, 20 Feb 2024 19:03:58 -0500 Subject: [PATCH 14/19] tapgarden: propogate taptree sibling for batch key --- tapgarden/batch.go | 46 +++++++++++++++++++++-- tapgarden/caretaker.go | 79 +++++++++++++++++++++++++++++++++++++-- tapgarden/mock.go | 39 +++++++++++++++++++ tapgarden/planter_test.go | 27 ++++++++----- 4 files changed, 173 insertions(+), 18 deletions(-) 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/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_test.go b/tapgarden/planter_test.go index aaa7ffc6a..1b630f35d 100644 --- a/tapgarden/planter_test.go +++ b/tapgarden/planter_test.go @@ -66,6 +66,8 @@ type mintingTestHarness struct { store tapgarden.MintingStore + treeStore *tapgarden.FallibleTapscriptTreeMgr + keyRing *tapgarden.MockKeyRing genSigner *tapgarden.MockGenSigner @@ -96,10 +98,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 +130,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, @@ -277,7 +282,7 @@ func (t *mintingTestHarness) assertFinalizeBatch(wg *sync.WaitGroup, // progressCaretaker uses the mock interfaces to progress a caretaker from start // to TX confirmation. func (t *mintingTestHarness) progressCaretaker( - seedlings []*tapgarden.Seedling) func() { + seedlings []*tapgarden.Seedling, batchSibling *chainhash.Hash) func() { // Assert that the caretaker has requested a genesis TX to be funded. _ = t.assertGenesisTxFunded() @@ -295,7 +300,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 +631,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 *chainhash.Hash) { + t.Helper() // Ensure that a request to finalize the PSBt has come across. @@ -650,7 +657,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 +798,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 +900,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 +1036,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 @@ -1137,7 +1144,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - _ = t.progressCaretaker(seedlings) + _ = t.progressCaretaker(seedlings, nil) caretakerCount++ t.assertFinalizeBatch(&wg, respChan, "") @@ -1161,7 +1168,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - sendConfNtfn := t.progressCaretaker(seedlings) + sendConfNtfn := t.progressCaretaker(seedlings, nil) caretakerCount++ // Trigger the confirmation event, which should cause the caretaker to @@ -1191,7 +1198,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - sendConfNtfn = t.progressCaretaker(seedlings) + sendConfNtfn = t.progressCaretaker(seedlings, nil) sendConfNtfn() t.assertFinalizeBatch(&wg, respChan, "") From 28a5ccc6262fe4fa0f8c3b62caebb3c9d1fcf6a7 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 9 Feb 2024 03:40:48 -0500 Subject: [PATCH 15/19] tapdb: update minting queries to set batch sibling --- tapdb/asset_minting.go | 55 +++++++++++++++++++++++---- tapdb/asset_minting_test.go | 75 +++++++++++++++++++++++++++++-------- 2 files changed, 106 insertions(+), 24 deletions(-) diff --git a/tapdb/asset_minting.go b/tapdb/asset_minting.go index 353342e7a..78a22e02c 100644 --- a/tapdb/asset_minting.go +++ b/tapdb/asset_minting.go @@ -312,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. @@ -862,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, @@ -929,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) { @@ -1019,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. @@ -1087,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, }) diff --git a/tapdb/asset_minting_test.go b/tapdb/asset_minting_test.go index 9e6c7b5c0..a719f3e89 100644 --- a/tapdb/asset_minting_test.go +++ b/tapdb/asset_minting_test.go @@ -67,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) @@ -329,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 @@ -384,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) @@ -394,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) @@ -740,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, @@ -756,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)) @@ -769,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, } } @@ -812,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 @@ -825,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) @@ -849,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. From 8169ee4674378b09614d3d8d969247a8f67bd117 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 9 Feb 2024 03:54:48 -0500 Subject: [PATCH 16/19] taprpc: add tapscript sibling to finalize call --- rpcserver.go | 3 +- taprpc/mintrpc/mint.pb.go | 255 +++--- taprpc/mintrpc/mint.proto | 16 + taprpc/mintrpc/mint.swagger.json | 45 + taprpc/taprootassets.pb.go | 1367 +++++++++++++++++------------- taprpc/taprootassets.proto | 20 + 6 files changed, 1029 insertions(+), 677 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 36075d352..8ccc24615 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1097,7 +1097,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, ) 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; } From f5fef0facba970dc459228358171c298589c3009 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 12 Jan 2024 18:08:36 -0500 Subject: [PATCH 17/19] rpc: add tapscript sibling to FinalizeBatch --- rpcserver.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 8ccc24615..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) } @@ -2917,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 ( From 96c5357cb7d047e7b618a8edb913bb0d34f5882a Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Tue, 23 Jan 2024 02:01:12 -0500 Subject: [PATCH 18/19] tapgarden: test minting with batch tapscript tree --- tapgarden/planter_test.go | 194 +++++++++++++++++++++++++++++++++++--- 1 file changed, 183 insertions(+), 11 deletions(-) diff --git a/tapgarden/planter_test.go b/tapgarden/planter_test.go index 1b630f35d..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" ) @@ -244,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() @@ -252,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, @@ -263,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() @@ -273,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, batchSibling *chainhash.Hash) 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() @@ -632,7 +649,7 @@ func (t *mintingTestHarness) assertSeedlingsMatchSprouts( // assertGenesisPsbtFinalized asserts that a request to finalize the genesis // transaction has been requested by a caretaker. func (t *mintingTestHarness) assertGenesisPsbtFinalized( - sibling *chainhash.Hash) { + sibling *commitment.TapscriptPreimage) { t.Helper() @@ -1115,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( @@ -1141,7 +1158,7 @@ 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, nil) @@ -1165,7 +1182,7 @@ 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, nil) @@ -1187,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) @@ -1195,7 +1212,7 @@ 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, nil) @@ -1208,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 { @@ -1238,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 From 35c8156a2aee848db0bce1ad05d9bf82beec2722 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 9 Feb 2024 04:05:11 -0500 Subject: [PATCH 19/19] itest: add script path spend of minting output --- itest/assertions.go | 25 +++++++ itest/assets_test.go | 136 +++++++++++++++++++++++++++++++++++++ itest/test_list_on_test.go | 4 ++ itest/utils.go | 72 ++++++++++++-------- tapcfg/server.go | 1 + 5 files changed, 211 insertions(+), 27 deletions(-) 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/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/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{},