From 60a90271ac1a1ae7767cdbb2a7a50418f280a7b5 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:48 +0100 Subject: [PATCH 01/20] mod+itest: update to lnd version with FundPsbt update, fix itest This commit updates the lnd dependency to the version that offers the new FundPsbt update. Something also changed in the lnd or btcd re-org logic, which requires us to skipt one of the re-org itests. --- fn/errors.go | 10 +++ go.mod | 60 +++++++-------- go.sum | 143 ++++++++++++++---------------------- itest/re-org_test.go | 11 ++- itest/utils.go | 6 +- tapgarden/re-org_watcher.go | 19 ++++- 6 files changed, 117 insertions(+), 132 deletions(-) diff --git a/fn/errors.go b/fn/errors.go index 9fdc785b6..535e8a6fa 100644 --- a/fn/errors.go +++ b/fn/errors.go @@ -34,3 +34,13 @@ func IsCanceled(err error) bool { return false } + +// IsRpcErr returns true if the given error is a gRPC error with the given +// candidate error as the cause. +func IsRpcErr(err error, candidate error) bool { + if err == nil { + return false + } + + return strings.Contains(err.Error(), candidate.Error()) +} diff --git a/go.mod b/go.mod index 6e0733ac2..c8f510de6 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/lightninglabs/taproot-assets go 1.21 require ( - github.com/btcsuite/btcd v0.23.5-0.20230905170901-80f5a0ffdf36 + github.com/btcsuite/btcd v0.24.1-0.20240309015614-f0ec9fbccec8 github.com/btcsuite/btcd/btcec/v2 v2.3.2 - github.com/btcsuite/btcd/btcutil v1.1.4-0.20230904040416-d4f519f5dc05 + github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/btcutil/psbt v1.1.8 - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcwallet v0.16.10-0.20231017144732-e3ff37491e9c - github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 - github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 + github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14 + github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 + github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 github.com/caddyserver/certmagic v0.17.2 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 @@ -26,22 +26,22 @@ require ( github.com/lib/pq v1.10.7 github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030 github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 - github.com/lightninglabs/lndclient v0.17.0-4 - github.com/lightninglabs/neutrino/cache v1.1.1 - github.com/lightningnetwork/lnd v0.17.1-beta + github.com/lightninglabs/lndclient v1.0.1-0.20240301093332-e39221e5a641 + github.com/lightninglabs/neutrino/cache v1.1.2 + github.com/lightningnetwork/lnd v0.17.0-beta.rc6.0.20240301195848-f61761277f14 github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/ticker v1.1.1 - github.com/lightningnetwork/lnd/tlv v1.1.1 + github.com/lightningnetwork/lnd/tlv v1.2.3 github.com/lightningnetwork/lnd/tor v1.1.2 github.com/ory/dockertest/v3 v3.10.0 github.com/prometheus/client_golang v1.14.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.9 - golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 - golang.org/x/net v0.17.0 - golang.org/x/sync v0.3.0 - golang.org/x/term v0.13.0 + golang.org/x/exp v0.0.0-20231226003508-02704c960a9b + golang.org/x/net v0.19.0 + golang.org/x/sync v0.5.0 + golang.org/x/term v0.15.0 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 @@ -59,11 +59,10 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/siphash v1.0.1 // indirect - github.com/andybalholm/brotli v1.0.3 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect - github.com/btcsuite/btcwallet/walletdb v1.4.0 // indirect - github.com/btcsuite/btcwallet/wtxmgr v1.5.0 // indirect + github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 // indirect + github.com/btcsuite/btcwallet/walletdb v1.4.1 // indirect + github.com/btcsuite/btcwallet/wtxmgr v1.5.1 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect @@ -77,12 +76,11 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/lru v1.0.0 // indirect github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v20.10.24+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fergusstrange/embedded-postgres v1.10.0 // indirect + github.com/fergusstrange/embedded-postgres v1.25.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -92,7 +90,7 @@ require ( github.com/google/btree v1.0.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -117,31 +115,28 @@ require ( github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/lightning-node-connect v0.2.5-alpha // indirect github.com/lightninglabs/neutrino v0.16.0 // indirect github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f // indirect + github.com/lightningnetwork/lnd/fn v1.0.4 // indirect github.com/lightningnetwork/lnd/healthcheck v1.2.3 // indirect - github.com/lightningnetwork/lnd/kvdb v1.4.4 // indirect + github.com/lightningnetwork/lnd/kvdb v1.4.5 // indirect github.com/lightningnetwork/lnd/queue v1.1.1 // indirect github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mholt/acmez v1.0.4 // indirect - github.com/mholt/archiver/v3 v3.5.0 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect - github.com/nwaples/rardecode v1.1.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.5 // indirect - github.com/pierrec/lz4/v4 v4.1.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -158,7 +153,6 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 // indirect - github.com/ulikunitz/xz v0.5.11 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -185,11 +179,11 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.23.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.16.0 // indirect google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect diff --git a/go.sum b/go.sum index 1083c8010..a4a62eee1 100644 --- a/go.sum +++ b/go.sum @@ -63,9 +63,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -75,44 +72,38 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd v0.23.5-0.20230905170901-80f5a0ffdf36 h1:g/UbZ6iSzcUH9kEvC+rB8UBCqahmt69e8y6nCegczbg= -github.com/btcsuite/btcd v0.23.5-0.20230905170901-80f5a0ffdf36/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.1-0.20240309015614-f0ec9fbccec8 h1:BfVdcMHp6uxkl3g3vNfqN96O4uV72UTaxB/21s1CBrY= +github.com/btcsuite/btcd v0.24.1-0.20240309015614-f0ec9fbccec8/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= -github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= -github.com/btcsuite/btcd/btcutil v1.1.4-0.20230904040416-d4f519f5dc05 h1:aemxF+69pT9sYC5E6Qj71zQVHcF72m0BNcVhCl3/thU= -github.com/btcsuite/btcd/btcutil v1.1.4-0.20230904040416-d4f519f5dc05/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.10-0.20231017144732-e3ff37491e9c h1:+7tbYEUj0TYYIvuvE9YP+x5dU3FT/8J6Qh8d5YvQwrE= -github.com/btcsuite/btcwallet v0.16.10-0.20231017144732-e3ff37491e9c/go.mod h1:WSKhOJWUmUOHKCKEzdt+jWAHFAE/t4RqVbCwL2pEdiU= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGHjGyV5xTqsXi+ZQ715UU= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= -github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ= -github.com/btcsuite/btcwallet/walletdb v1.4.0/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2cL8vl6av4SU= -github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4= +github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14 h1:GnTInK5UIkDJw9alXR4JeOmKmodJu/5qYknZWqp1Sk0= +github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14/go.mod h1:3EItwIQcrXGkcQlO1OginQ3Ab8YgE8kxka9hjgFuWxM= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc= +github.com/btcsuite/btcwallet/walletdb v1.4.1 h1:NGIGoxx3trpaWqmdOeuhju7KJKp5UM96mQL21idF6RY= +github.com/btcsuite/btcwallet/walletdb v1.4.1/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ= +github.com/btcsuite/btcwallet/wtxmgr v1.5.1 h1:2yXhMGa4DNz16Mi0e8dVoiFXKOznXlxiGLhB3hKj2uA= +github.com/btcsuite/btcwallet/wtxmgr v1.5.1/go.mod h1:tO4FBSdann0xg/Jtm0grV7t1DzpQMK8nThYVtvSJo/8= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= @@ -193,16 +184,13 @@ github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32Paq github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= -github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= -github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= -github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -215,8 +203,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg= -github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c= +github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= +github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= @@ -313,7 +301,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -354,8 +341,8 @@ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0 h1:mdLirNAJBxnGgyB6pjZLcs6ue/6eZGBui6gXspfq4ks= @@ -478,17 +465,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -507,7 +488,6 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -521,33 +501,34 @@ github.com/lightninglabs/lightning-node-connect v0.2.5-alpha h1:ZRVChwczFXK0CEbx github.com/lightninglabs/lightning-node-connect v0.2.5-alpha/go.mod h1:A9Pof9fETkH+F67BnOmrBDThPKstqp73wlImWOZvTXQ= github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 h1:Er1miPZD2XZwcfE4xoS5AILqP1mj7kqnhbBSxW9BDxY= github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4= -github.com/lightninglabs/lndclient v0.17.0-4 h1:2nU7kMYctGmx6NCpbWAwRt3mmlsSzb6HI41WODReYzA= -github.com/lightninglabs/lndclient v0.17.0-4/go.mod h1:oVQUAYEeuYUaoxCWjUBaa/uF0kiqEJJjEaiMhEXHH7Y= +github.com/lightninglabs/lndclient v1.0.1-0.20240301093332-e39221e5a641 h1:gOzlAcE1EL8prHCISjDpFD1tvb9HDxtLEdTVD+ebuc0= +github.com/lightninglabs/lndclient v1.0.1-0.20240301093332-e39221e5a641/go.mod h1:FuD4SXEL3t4J5bu04O+N+k4UcPjsUvGAaT9csWfovbw= github.com/lightninglabs/neutrino v0.16.0 h1:YNTQG32fPR/Zg0vvJVI65OBH8l3U18LSXXtX91hx0q0= github.com/lightninglabs/neutrino v0.16.0/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= -github.com/lightninglabs/neutrino/cache v1.1.1 h1:TllWOSlkABhpgbWJfzsrdUaDH2fBy/54VSIB4vVqV8M= -github.com/lightninglabs/neutrino/cache v1.1.1/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= +github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= +github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display h1:pRdza2wleRN1L2fJXd6ZoQ9ZegVFTAb2bOQfruJPKcY= github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f h1:Pua7+5TcFEJXIIZ1I2YAUapmbcttmLj4TTi786bIi3s= github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= -github.com/lightningnetwork/lnd v0.17.1-beta h1:ya5pXahwrEJ9Sj9TQu0rIsOr1dMK8myuFMslZlmVmhA= -github.com/lightningnetwork/lnd v0.17.1-beta/go.mod h1:wgnCM0tlwxUDZ9y7CeBkXtBJkaY45+R8A3XAgsFS0MA= +github.com/lightningnetwork/lnd v0.17.0-beta.rc6.0.20240301195848-f61761277f14 h1:pFMYLyq4YdqJalPtgBFh/y9EPpGZ2HKYivkzx+ux/Ew= +github.com/lightningnetwork/lnd v0.17.0-beta.rc6.0.20240301195848-f61761277f14/go.mod h1:g9O3HEKKv6tCvgx3L7FYpMP+qtzvPt6wuyKNHuRCcNA= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= -github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= +github.com/lightningnetwork/lnd/fn v1.0.4 h1:n4iGRRoS+XHqNbOrsXIvweps/QfWk+moO7FiYPPFI8k= +github.com/lightningnetwork/lnd/fn v1.0.4/go.mod h1:K9gbvdl5z4XmRcqWUVqvvVcuRKtmq9BNQ+cWYlk+vjw= github.com/lightningnetwork/lnd/healthcheck v1.2.3 h1:oqhOOy8WmIEa6RBkYKC0mmYZkhl8T2kGD97n9jpML8o= github.com/lightningnetwork/lnd/healthcheck v1.2.3/go.mod h1:eDxH3dEwV9DeBW/6inrmlVh1qBOFV0AI14EEPnGt9gc= -github.com/lightningnetwork/lnd/kvdb v1.4.4 h1:bCv63rVCvzqj1BkagN/EWTov6NDDgYEG/t0z2HepRMk= -github.com/lightningnetwork/lnd/kvdb v1.4.4/go.mod h1:9SuaIqMA9ugrVkdvgQkYXa8CAKYNYd4vsEYORP4V698= +github.com/lightningnetwork/lnd/kvdb v1.4.5 h1:wwX3hbFTsnxEIL5X2Pszq1o3Fd2OZGdyWIMr9QrMxL8= +github.com/lightningnetwork/lnd/kvdb v1.4.5/go.mod h1:oaGL6R/qwazM7hPurg8jSPYsWw3cGEOt6YJDs5TUNos= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= -github.com/lightningnetwork/lnd/tlv v1.1.1 h1:BW1u9+uHLRA9sm+8FBkAg1H9rPjrj3S9KvXYiCYjQWk= -github.com/lightningnetwork/lnd/tlv v1.1.1/go.mod h1:292dSXpZ+BNnSJFjS1qvHden9LEbulmECglSgfg+4lw= +github.com/lightningnetwork/lnd/tlv v1.2.3 h1:If5ibokA/UoCBGuCKaY6Vn2SJU0l9uAbehCnhTZjEP8= +github.com/lightningnetwork/lnd/tlv v1.2.3/go.mod h1:zDkmqxOczP6LaLTvSFDQ1SJUfHcQRCMKFj93dn3eMB8= github.com/lightningnetwork/lnd/tor v1.1.2 h1:3zv9z/EivNFaMF89v3ciBjCS7kvCj4ZFG7XvD2Qq0/k= github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= @@ -570,8 +551,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80= github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY= -github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= -github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= @@ -593,9 +572,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 h1:62uLwA3l2JMH84liO4ZhnjTH5PjFyCYxbHLgXPaJMtI= github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= -github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -622,9 +598,6 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= -github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -699,7 +672,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -717,10 +689,6 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -747,7 +715,6 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= @@ -829,8 +796,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -841,8 +808,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -867,8 +834,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -917,8 +884,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -942,8 +909,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1015,14 +982,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1033,8 +1000,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1093,8 +1060,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/itest/re-org_test.go b/itest/re-org_test.go index 21e29a79e..bf03c4525 100644 --- a/itest/re-org_test.go +++ b/itest/re-org_test.go @@ -171,8 +171,10 @@ func testReOrgSend(t *harnessTest) { miner.AssertTxInBlock(initialBlock, sendTXID) t.Logf("Send TX %v mined in block %v", sendTXID, initialBlockHash) - // We now generate the re-org. + // We now generate the re-org. That should put the minting TX back into + // the mempool. generateReOrg(t.t, t.lndHarness, tempMiner, 3, 2) + t.lndHarness.Miner.AssertNumTxsInMempool(1) // This should have caused a reorg, and Alice should sync to the longer // chain, where the funding transaction is not confirmed. @@ -285,8 +287,10 @@ func testReOrgMintAndSend(t *harnessTest) { miner.AssertTxInBlock(initialBlock, sendTXID) t.Logf("Send TX %v mined in block %v", sendTXID, initialBlockHash) - // We now generate the re-org. + // We now generate the re-org. That should put the minting and send TX + // back into the mempool. generateReOrg(t.t, t.lndHarness, tempMiner, 4, 2) + t.lndHarness.Miner.AssertNumTxsInMempool(2) // This should have caused a reorg, and Alice should sync to the longer // chain, where the funding transaction is not confirmed. @@ -371,8 +375,7 @@ func generateReOrg(t *testing.T, lnd *lntest.HarnessTest, tempMiner *lntest.HarnessMiner, depth uint32, expectedDelta int32) { // Now we generate a longer chain with the temp miner. - _, err := tempMiner.Client.Generate(depth) - require.NoError(t, err, "unable to generate blocks") + tempMiner.MineEmptyBlocks(int(depth)) // Ensure the chain lengths are what we expect, with the temp miner // being 2 blocks ahead. diff --git a/itest/utils.go b/itest/utils.go index 2733cdd62..cdac52ecf 100644 --- a/itest/utils.go +++ b/itest/utils.go @@ -127,8 +127,8 @@ func MineBlocks(t *testing.T, client *rpcclient.Client, var blockHashes []*chainhash.Hash - switch backend { - case rpcclient.BitcoindPost19: + switch backend.(type) { + case rpcclient.BitcoindVersion: addr, err := btcutil.DecodeAddress( regtestMiningAddr, regtestParams, ) @@ -139,7 +139,7 @@ func MineBlocks(t *testing.T, client *rpcclient.Client, ) require.NoError(t, err) - case rpcclient.Btcd: + case rpcclient.BtcdVersion: blockHashes, err = client.Generate(num) require.NoError(t, err) diff --git a/tapgarden/re-org_watcher.go b/tapgarden/re-org_watcher.go index 29b6d15aa..31921c227 100644 --- a/tapgarden/re-org_watcher.go +++ b/tapgarden/re-org_watcher.go @@ -278,12 +278,17 @@ func (w *ReOrgWatcher) waitForConf(ctx context.Context, txHash chainhash.Hash, // in a new block in the re-organized chain. case err := <-errChan: - if !fn.IsCanceled(err) { + if !fn.IsRpcErr( + err, + chainntnfs.ErrChainNotifierShuttingDown, + ) && !fn.IsCanceled(err) { + w.reportErr(fmt.Errorf("error while "+ "waiting for conf: %w", err)) - return } + return + case <-ctx.Done(): if !fn.IsCanceled(ctx.Err()) { log.Warnf("Stopping to watch TX %v "+ @@ -464,8 +469,14 @@ func (w *ReOrgWatcher) watchTransactions() { } case err := <-blockErr: - w.reportErr(fmt.Errorf("unable to receive new block "+ - "notifications: %w", err)) + if !fn.IsRpcErr( + err, chainntnfs.ErrChainNotifierShuttingDown, + ) && !fn.IsCanceled(err) { + + w.reportErr(fmt.Errorf("unable to receive "+ + "new block notifications: %w", err)) + } + return case <-w.Quit: From d706465394a01ca36032d4fd4eedb9aed29307c5 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:49 +0100 Subject: [PATCH 02/20] tapfreighter+tapsend: add PrepareAnchoringTemplate function --- tapfreighter/chain_porter.go | 15 -------- tapfreighter/wallet.go | 2 +- tapsend/send.go | 70 +++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index de3e1555d..87a7cce03 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -12,7 +12,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "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/proof" @@ -786,20 +785,6 @@ func (p *ChainPorter) importLocalAddresses(ctx context.Context, return nil } -// createDummyOutput creates a new Bitcoin transaction output that is later -// used to embed a Taproot Asset commitment. -func createDummyOutput() *wire.TxOut { - // The dummy PkScript is the same size as an encoded P2TR output. - newOutput := wire.TxOut{ - Value: int64(tapsend.DummyAmtSats), - PkScript: append( - []byte{txscript.OP_1, txscript.OP_DATA_32}, - make([]byte, 32)..., - ), - } - return &newOutput -} - // stateStep attempts to step through the state machine to complete a Taproot // Asset transfer. func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index ea08231cc..0d3f77523 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -1547,7 +1547,7 @@ func adjustFundedPsbt(fPkt *tapsend.FundedPsbt, anchorInputValue int64) { // Overwrite the existing change output, and restore in at the // highest-index output. - fPkt.Pkt.UnsignedTx.TxOut[changeIndex] = createDummyOutput() + fPkt.Pkt.UnsignedTx.TxOut[changeIndex] = tapsend.CreateDummyOutput() fPkt.Pkt.UnsignedTx.TxOut[maxOutputIndex].PkScript = changeOutput.PkScript fPkt.Pkt.UnsignedTx.TxOut[maxOutputIndex].Value = changeOutput.Value diff --git a/tapsend/send.go b/tapsend/send.go index f05d85606..33daaab34 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -111,9 +111,10 @@ var ( var ( // GenesisDummyScript is a dummy script that we'll use to fund the // initial PSBT packet that'll create initial set of assets. It's the - // same size as a encoded P2TR output and has a valid P2TR prefix. + // same size as an encoded P2TR output and has a valid P2TR prefix. GenesisDummyScript = append( - []byte{txscript.OP_1, 0x20}, bytes.Repeat([]byte{0x00}, 32)..., + []byte{txscript.OP_1, txscript.OP_DATA_32}, + bytes.Repeat([]byte{0x00}, 32)..., ) ) @@ -122,11 +123,10 @@ var ( func CreateDummyOutput() *wire.TxOut { // The dummy PkScript is the same size as an encoded P2TR output and has // a valid P2TR prefix. - newOutput := wire.TxOut{ + return &wire.TxOut{ Value: int64(DummyAmtSats), PkScript: bytes.Clone(GenesisDummyScript), } - return &newOutput } // AssetGroupQuerier is an interface that allows us to query for asset groups by @@ -1021,6 +1021,68 @@ func CreateAnchorTx(vPackets []*tappsbt.VPacket) (*psbt.Packet, error) { return spendPkt, nil } +// PrepareAnchoringTemplate creates a BTC level PSBT packet that can be used to +// anchor the given virtual packets. The PSBT packet is created with the +// necessary inputs and outputs to anchor the virtual packets, but without any +// signatures. The main difference to CreateAnchorTx is that this function +// populates the inputs with the witness UTXO and derivation path information. +func PrepareAnchoringTemplate( + vPackets []*tappsbt.VPacket) (*psbt.Packet, error) { + + btcPacket, err := CreateAnchorTx(vPackets) + if err != nil { + return nil, err + } + + // Populate input information now. We add the witness UTXO and + // derivation path information for each asset input. Since the virtual + // transactions might refer to the same BTC level input, we need to make + // sure we don't add any duplicate inputs. + for pIdx := range vPackets { + for iIdx := range vPackets[pIdx].Inputs { + vIn := vPackets[pIdx].Inputs[iIdx] + anchor := vIn.Anchor + + if HasInput(btcPacket.UnsignedTx, vIn.PrevID.OutPoint) { + continue + } + + btcPacket.Inputs = append(btcPacket.Inputs, psbt.PInput{ + WitnessUtxo: &wire.TxOut{ + Value: int64(anchor.Value), + PkScript: anchor.PkScript, + }, + SighashType: anchor.SigHashType, + Bip32Derivation: anchor.Bip32Derivation, + TaprootBip32Derivation: anchor.TrBip32Derivation, + TaprootInternalKey: schnorr.SerializePubKey( + anchor.InternalKey, + ), + TaprootMerkleRoot: anchor.MerkleRoot, + }) + btcPacket.UnsignedTx.TxIn = append( + btcPacket.UnsignedTx.TxIn, &wire.TxIn{ + PreviousOutPoint: vIn.PrevID.OutPoint, + }, + ) + } + } + + return btcPacket, nil +} + +// HasInput returns true if the given transaction has an input that spends the +// given outpoint. +func HasInput(tx *wire.MsgTx, outpoint wire.OutPoint) bool { + for _, in := range tx.TxIn { + if in.PreviousOutPoint == outpoint { + return true + } + } + + return false +} + // UpdateTaprootOutputKeys updates a PSBT with outputs embedding TapCommitments // involved in an asset send. The sender must attach the Bitcoin input holding // the corresponding Taproot Asset input asset to this PSBT before finalizing From 297723877b20ca24e8281e7e56e360833c970be6 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:50 +0100 Subject: [PATCH 03/20] proof: add encoding/decoding convenience funcs --- proof/proof.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/proof/proof.go b/proof/proof.go index 4d04ba68f..4902b7797 100644 --- a/proof/proof.go +++ b/proof/proof.go @@ -466,3 +466,41 @@ func SparseDecode(r io.Reader, records ...tlv.Record) error { return proofStream.Decode(r) } + +// Encode encodes a proof into a byte slice. +func Encode(p *Proof) ([]byte, error) { + var b bytes.Buffer + if err := p.Encode(&b); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +// Decode decodes a proof from a byte slice. +func Decode(blob []byte) (*Proof, error) { + var p Proof + err := p.Decode(bytes.NewReader(blob)) + if err != nil { + return nil, err + } + return &p, nil +} + +// EncodeFile encodes a proof file into a byte slice. +func EncodeFile(f *File) ([]byte, error) { + var b bytes.Buffer + if err := f.Encode(&b); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +// DecodeFile decodes a proof file from a byte slice. +func DecodeFile(blob []byte) (*File, error) { + var f File + err := f.Decode(bytes.NewReader(blob)) + if err != nil { + return nil, err + } + return &f, nil +} From f08c9b888677ebf6f80f8bac5fbee26e02b2e622 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:52 +0100 Subject: [PATCH 04/20] rpcserver: use convenience methods, fix code style --- rpcserver.go | 110 ++++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 9ef74b402..9d60e6fd8 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "errors" "fmt" + "io" "math" "net/http" "strings" @@ -771,13 +772,11 @@ func (r *rpcServer) marshalChainAsset(ctx context.Context, a *asset.ChainAsset, var anchorTxBytes []byte if a.AnchorTx != nil { - var anchorTxBuf bytes.Buffer - err := a.AnchorTx.Serialize(&anchorTxBuf) + anchorTxBytes, err = serialize(a.AnchorTx) if err != nil { return nil, fmt.Errorf("unable to serialize anchor "+ "tx: %w", err) } - anchorTxBytes = anchorTxBuf.Bytes() } rpcAsset.ChainAnchor = &taprpc.AnchorInfo{ @@ -939,7 +938,7 @@ func (r *rpcServer) ListGroups(ctx context.Context, return nil, err } - asset := &taprpc.AssetHumanReadable{ + rpcAsset := &taprpc.AssetHumanReadable{ Id: a.ID[:], Version: assetVersion, Amount: a.Amount, @@ -958,7 +957,7 @@ func (r *rpcServer) ListGroups(ctx context.Context, } groupsWithAssets[groupKey].Assets = append( - groupsWithAssets[groupKey].Assets, asset, + groupsWithAssets[groupKey].Assets, rpcAsset, ) } @@ -1255,8 +1254,7 @@ func (r *rpcServer) VerifyProof(ctx context.Context, return nil, fmt.Errorf("invalid proof file: %w", err) } - var proofFile proof.File - err := proofFile.Decode(bytes.NewReader(req.RawProofFile)) + proofFile, err := proof.DecodeFile(req.RawProofFile) if err != nil { return nil, fmt.Errorf("unable to decode proof file: %w", err) } @@ -1297,21 +1295,17 @@ func (r *rpcServer) VerifyProof(ctx context.Context, func (r *rpcServer) DecodeProof(ctx context.Context, req *taprpc.DecodeProofRequest) (*taprpc.DecodeProofResponse, error) { - var ( - proofReader = bytes.NewReader(req.RawProof) - rpcProof *taprpc.DecodedProof - ) + var rpcProof *taprpc.DecodedProof switch { case proof.IsSingleProof(req.RawProof): - var p proof.Proof - err := p.Decode(proofReader) + p, err := proof.Decode(req.RawProof) if err != nil { return nil, fmt.Errorf("unable to decode proof: %w", err) } rpcProof, err = r.marshalProof( - ctx, &p, req.WithPrevWitnesses, req.WithMetaReveal, + ctx, p, req.WithPrevWitnesses, req.WithMetaReveal, ) if err != nil { return nil, fmt.Errorf("unable to marshal proof: %w", @@ -1325,8 +1319,8 @@ func (r *rpcServer) DecodeProof(ctx context.Context, return nil, fmt.Errorf("invalid proof file: %w", err) } - var proofFile proof.File - if err := proofFile.Decode(proofReader); err != nil { + proofFile, err := proof.DecodeFile(req.RawProof) + if err != nil { return nil, fmt.Errorf("unable to decode proof file: "+ "%w", err) } @@ -1588,8 +1582,7 @@ func (r *rpcServer) ImportProof(ctx context.Context, // We need to parse the proof file and extract the last proof, so we can // get the locator that is required for storage. - var proofFile proof.File - err := proofFile.Decode(bytes.NewReader(req.ProofFile)) + proofFile, err := proof.DecodeFile(req.ProofFile) if err != nil { return nil, fmt.Errorf("unable to decode proof file: %w", err) } @@ -1773,15 +1766,17 @@ func (r *rpcServer) FundVirtualPsbt(ctx context.Context, "specified") } - var b bytes.Buffer - if err := fundedVPkt.VPacket.Serialize(&b); err != nil { + var err error + response := &wrpc.FundVirtualPsbtResponse{ + ChangeOutputIndex: 0, + } + + response.FundedPsbt, err = serialize(fundedVPkt.VPacket) + if err != nil { return nil, fmt.Errorf("error serializing packet: %w", err) } - return &wrpc.FundVirtualPsbtResponse{ - FundedPsbt: b.Bytes(), - ChangeOutputIndex: 0, - }, nil + return response, nil } // SignVirtualPsbt signs the inputs of a virtual transaction and prepares the @@ -1794,9 +1789,7 @@ func (r *rpcServer) SignVirtualPsbt(ctx context.Context, return nil, fmt.Errorf("request cannot be nil") } - vPkt, err := tappsbt.NewFromRawBytes( - bytes.NewReader(req.FundedPsbt), false, - ) + vPkt, err := tappsbt.Decode(req.FundedPsbt) if err != nil { return nil, fmt.Errorf("error decoding packet: %w", err) } @@ -1843,13 +1836,13 @@ func (r *rpcServer) SignVirtualPsbt(ctx context.Context, return nil, fmt.Errorf("error signing packet: %w", err) } - var b bytes.Buffer - if err := vPkt.Serialize(&b); err != nil { + signedPsbtBytes, err := serialize(vPkt) + if err != nil { return nil, fmt.Errorf("error serializing packet: %w", err) } return &wrpc.SignVirtualPsbtResponse{ - SignedPsbt: b.Bytes(), + SignedPsbt: signedPsbtBytes, SignedInputs: signedInputs, }, nil } @@ -1871,9 +1864,7 @@ func (r *rpcServer) AnchorVirtualPsbts(ctx context.Context, return nil, fmt.Errorf("only one virtual PSBT supported") } - vPacket, err := tappsbt.NewFromRawBytes( - bytes.NewReader(req.VirtualPsbts[0]), false, - ) + vPacket, err := tappsbt.Decode(req.VirtualPsbts[0]) if err != nil { return nil, fmt.Errorf("error decoding packet: %w", err) } @@ -2426,14 +2417,13 @@ func (r *rpcServer) BurnAsset(ctx context.Context, vOut := fundResp.VPacket.Outputs[idx] tOut := resp.Outputs[idx] if vOut.Asset.IsBurn() { - var p proof.Proof - err = p.Decode(bytes.NewReader(tOut.ProofSuffix)) + p, err := proof.Decode(tOut.ProofSuffix) if err != nil { return nil, fmt.Errorf("error decoding "+ "burn proof: %w", err) } - burnProof, err = r.marshalProof(ctx, &p, true, false) + burnProof, err = r.marshalProof(ctx, p, true, false) if err != nil { return nil, fmt.Errorf("error decoding "+ "burn proof: %w", err) @@ -4023,10 +4013,10 @@ func (r *rpcServer) QueryProof(ctx context.Context, func unmarshalAssetLeaf(leaf *unirpc.AssetLeaf) (*universe.Leaf, error) { // We'll just pull the asset details from the serialized issuance proof // itself. - var assetProof proof.Proof - if err := assetProof.Decode( - bytes.NewReader(leaf.Proof), - ); err != nil { + var proofAsset asset.Asset + assetRecord := proof.AssetLeafRecord(&proofAsset) + err := proof.SparseDecode(bytes.NewReader(leaf.Proof), assetRecord) + if err != nil { return nil, err } @@ -4035,12 +4025,12 @@ func unmarshalAssetLeaf(leaf *unirpc.AssetLeaf) (*universe.Leaf, error) { return &universe.Leaf{ GenesisWithGroup: universe.GenesisWithGroup{ - Genesis: assetProof.Asset.Genesis, - GroupKey: assetProof.Asset.GroupKey, + Genesis: proofAsset.Genesis, + GroupKey: proofAsset.GroupKey, }, RawProof: leaf.Proof, - Asset: &assetProof.Asset, - Amt: assetProof.Asset.Amount, + Asset: &proofAsset, + Amt: proofAsset.Amount, }, nil } @@ -4133,8 +4123,8 @@ func (r *rpcServer) Info(ctx context.Context, // unmarshalUniverseSyncType maps an RPC universe sync type into a concrete // type. -func unmarshalUniverseSyncType(req unirpc.UniverseSyncMode) ( - universe.SyncType, error) { +func unmarshalUniverseSyncType( + req unirpc.UniverseSyncMode) (universe.SyncType, error) { switch req { case unirpc.UniverseSyncMode_SYNC_FULL: @@ -4149,7 +4139,9 @@ func unmarshalUniverseSyncType(req unirpc.UniverseSyncMode) ( } // unmarshalSyncTargets maps an RPC sync target into a concrete type. -func unmarshalSyncTargets(targets []*unirpc.SyncTarget) ([]universe.Identifier, error) { +func unmarshalSyncTargets( + targets []*unirpc.SyncTarget) ([]universe.Identifier, error) { + uniIDs := make([]universe.Identifier, 0, len(targets)) for _, target := range targets { uniID, err := UnmarshalUniID(target.Id) @@ -4254,8 +4246,8 @@ func (r *rpcServer) SyncUniverse(ctx context.Context, return r.marshalUniverseDiff(ctx, universeDiff) } -func marshalUniverseServer(server universe.ServerAddr, -) *unirpc.UniverseFederationServer { +func marshalUniverseServer( + server universe.ServerAddr) *unirpc.UniverseFederationServer { return &unirpc.UniverseFederationServer{ Host: server.HostStr(), @@ -4264,7 +4256,7 @@ func marshalUniverseServer(server universe.ServerAddr, } // ListFederationServers lists the set of servers that make up the federation -// of the local Universe server. This servers are used to push out new proofs, +// of the local Universe server. These servers are used to push out new proofs, // and also periodically call sync new proofs from the remote server. func (r *rpcServer) ListFederationServers(ctx context.Context, _ *unirpc.ListFederationServersRequest, @@ -4501,8 +4493,7 @@ func (r *rpcServer) ProveAssetOwnership(ctx context.Context, return nil, fmt.Errorf("cannot fetch proof: %w", err) } - proofFile := &proof.File{} - err = proofFile.Decode(bytes.NewReader(proofBlob)) + proofFile, err := proof.DecodeFile(proofBlob) if err != nil { return nil, fmt.Errorf("cannot decode proof: %w", err) } @@ -4559,8 +4550,7 @@ func (r *rpcServer) VerifyAssetOwnership(ctx context.Context, return nil, fmt.Errorf("a valid proof must be specified") } - p := &proof.Proof{} - err := p.Decode(bytes.NewReader(req.ProofWithWitness)) + p, err := proof.Decode(req.ProofWithWitness) if err != nil { return nil, fmt.Errorf("cannot decode proof file: %w", err) } @@ -5120,3 +5110,15 @@ func (r *rpcServer) SubscribeRfqEventNtfns( } } } + +// serialize is a helper function that serializes a serializable object into a +// byte slice. +func serialize(s interface{ Serialize(io.Writer) error }) ([]byte, error) { + var b bytes.Buffer + err := s.Serialize(&b) + if err != nil { + return nil, err + } + + return b.Bytes(), nil +} From 8cc32f36ee7ba79e3d6f4f263654810d462f8836 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:53 +0100 Subject: [PATCH 05/20] multi: add input proofs for passive assets Since we'll want to be able to sign passive assets through RPC as well, we need the full input proofs to be present on passive assets. --- tapfreighter/wallet.go | 88 ++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index 0d3f77523..d760d18c0 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -262,6 +262,7 @@ func (f *AssetWallet) FundAddressSend(ctx context.Context, // passiveAssetVPacket creates a virtual packet for the given passive asset. func passiveAssetVPacket(params *address.ChainParams, passiveAsset *asset.Asset, anchorPoint wire.OutPoint, anchorOutputIndex uint32, + inputProof *proof.Proof, internalKey *keychain.KeyDescriptor) *tappsbt.VPacket { // Specify virtual input. @@ -273,8 +274,16 @@ func passiveAssetVPacket(params *address.ChainParams, passiveAsset *asset.Asset, inputAsset.ScriptKey.PubKey, ), } + + inputAnchorIdx := inputProof.InclusionProof.OutputIndex + anchorOut := inputProof.AnchorTx.TxOut[inputAnchorIdx] vInput := tappsbt.VInput{ PrevID: inputPrevId, + Anchor: tappsbt.Anchor{ + Value: btcutil.Amount(anchorOut.Value), + PkScript: anchorOut.PkScript, + }, + Proof: inputProof, } // Specify virtual output. @@ -751,32 +760,12 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, // We'll also include an inclusion proof for the input asset in // the virtual transaction. With that a signer can verify that // the asset was actually committed to in the anchor output. - assetID := assetInput.Asset.ID() - proofLocator := proof.Locator{ - AssetID: &assetID, - ScriptKey: *assetInput.Asset.ScriptKey.PubKey, - OutPoint: &assetInput.AnchorPoint, - } - if assetInput.Asset.GroupKey != nil { - proofLocator.GroupKey = &assetInput.Asset.GroupKey.GroupPubKey - } - inputProofBlob, err := f.cfg.AssetProofs.FetchProof( - ctx, proofLocator, + inputProof, err := f.fetchInputProof( + ctx, assetInput.Asset, assetInput.AnchorPoint, ) if err != nil { - return nil, fmt.Errorf("cannot fetch proof for input "+ - "asset: %w", err) - } - inputProofFile := &proof.File{} - err = inputProofFile.Decode(bytes.NewReader(inputProofBlob)) - if err != nil { - return nil, fmt.Errorf("cannot decode proof for input "+ - "asset: %w", err) - } - inputProof, err := inputProofFile.LastProof() - if err != nil { - return nil, fmt.Errorf("cannot get last proof for "+ - "input asset: %w", err) + return nil, fmt.Errorf("error fetching input proof: %w", + err) } tapscriptSiblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( @@ -825,6 +814,42 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, return inputCommitments, nil } +// fetchInputProof fetches the proof for the given asset input from the archive. +func (f *AssetWallet) fetchInputProof(ctx context.Context, + inputAsset *asset.Asset, anchorPoint wire.OutPoint) (*proof.Proof, + error) { + + assetID := inputAsset.ID() + proofLocator := proof.Locator{ + AssetID: &assetID, + ScriptKey: *inputAsset.ScriptKey.PubKey, + OutPoint: &anchorPoint, + } + if inputAsset.GroupKey != nil { + proofLocator.GroupKey = &inputAsset.GroupKey.GroupPubKey + } + inputProofBlob, err := f.cfg.AssetProofs.FetchProof( + ctx, proofLocator, + ) + if err != nil { + return nil, fmt.Errorf("cannot fetch proof for input "+ + "asset: %w", err) + } + inputProofFile := &proof.File{} + err = inputProofFile.Decode(bytes.NewReader(inputProofBlob)) + if err != nil { + return nil, fmt.Errorf("cannot decode proof for input "+ + "asset: %w", err) + } + inputProof, err := inputProofFile.LastProof() + if err != nil { + return nil, fmt.Errorf("cannot get last proof for "+ + "input asset: %w", err) + } + + return inputProof, nil +} + // SignVirtualPacketOptions is a set of functional options that allow callers to // further modify the virtual packet signing process. type SignVirtualPacketOptions struct { @@ -1200,11 +1225,20 @@ func (f *AssetWallet) CreatePassiveAssets(ctx context.Context, for tapKey := range passiveCommitments { passiveCommitment := passiveCommitments[tapKey] for _, passiveAsset := range passiveCommitment.Assets() { + inputProof, err := f.fetchInputProof( + ctx, passiveAsset, prevID.OutPoint, + ) + if err != nil { + return nil, fmt.Errorf("error "+ + "fetching input proof: %w", err) + } + passiveAssets = append( passiveAssets, passiveAssetVPacket( f.cfg.ChainParams, passiveAsset, prevID.OutPoint, - anchorOutIdx, anchorOutDesc, + anchorOutIdx, inputProof, + anchorOutDesc, ), ) } @@ -1221,9 +1255,7 @@ func (f *AssetWallet) SignPassiveAssets( // Sign all the passive assets virtual packets. for idx := range passiveAssets { passiveAsset := passiveAssets[idx] - _, err := f.SignVirtualPacket( - passiveAsset, SkipInputProofVerify(), - ) + _, err := f.SignVirtualPacket(passiveAsset) if err != nil { return fmt.Errorf("unable to sign passive asset "+ "virtual packet: %w", err) From 64d486e8a14e9dc09ce96f113dafc3fd93feef33 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:54 +0100 Subject: [PATCH 06/20] rpcserver+taprpc: return passive assets in FundVirtualPsbts In the new RPC flow that's going to be added in the following commits, the user will need to sign the passive assets manually. We return the passive assets in the funding call so the user can actually do that. For any existing flow these passive assets can just be ignored as the signing still happens automatically under the hood. --- rpcserver.go | 22 +- taprpc/assetwalletrpc/assetwallet.pb.go | 361 +++++++++--------- taprpc/assetwalletrpc/assetwallet.proto | 16 +- .../assetwalletrpc/assetwallet.swagger.json | 10 +- 4 files changed, 236 insertions(+), 173 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 9d60e6fd8..864f7cb37 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1766,10 +1766,30 @@ func (r *rpcServer) FundVirtualPsbt(ctx context.Context, "specified") } - var err error + // Extract the passive assets that are needed for the fully RPC driven + // flow. + passivePackets, err := r.cfg.AssetWallet.CreatePassiveAssets( + ctx, []*tappsbt.VPacket{fundedVPkt.VPacket}, + fundedVPkt.InputCommitments, + ) + if err != nil { + return nil, fmt.Errorf("error creating passive assets: %w", err) + } + + // Serialize the active and passive packets into the response now. response := &wrpc.FundVirtualPsbtResponse{ + PassiveAssetPsbts: make([][]byte, len(passivePackets)), ChangeOutputIndex: 0, } + for idx := range passivePackets { + response.PassiveAssetPsbts[idx], err = serialize( + passivePackets[idx], + ) + if err != nil { + return nil, fmt.Errorf("error serializing passive "+ + "packet: %w", err) + } + } response.FundedPsbt, err = serialize(fundedVPkt.VPacket) if err != nil { diff --git a/taprpc/assetwalletrpc/assetwallet.pb.go b/taprpc/assetwalletrpc/assetwallet.pb.go index 572e42f94..3c7b03e18 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.go @@ -113,10 +113,21 @@ type FundVirtualPsbtResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The funded but not yet signed PSBT packet. + // The funded but not yet signed virtual PSBT packet. FundedPsbt []byte `protobuf:"bytes,1,opt,name=funded_psbt,json=fundedPsbt,proto3" json:"funded_psbt,omitempty"` // The index of the added change output or -1 if no change was left over. ChangeOutputIndex int32 `protobuf:"varint,2,opt,name=change_output_index,json=changeOutputIndex,proto3" json:"change_output_index,omitempty"` + // The list of passive virtual transactions that are anchored in the same BTC + // level anchor transaction inputs as the funded "active" asset above. These + // assets can be ignored when using the AnchorVirtualPsbts RPC, since they are + // retrieved, signed and committed automatically in that method. But the + // passive assets have to be included in the CommitVirtualPsbts RPC which is + // used when custom BTC level anchor transactions are created. + // The main difference to the "active" asset above is that the passive assets + // will not get their own entry in the transfer table of the database, since + // they are just carried along and not directly affected by the direct user + // action. + PassiveAssetPsbts [][]byte `protobuf:"bytes,3,rep,name=passive_asset_psbts,json=passiveAssetPsbts,proto3" json:"passive_asset_psbts,omitempty"` } func (x *FundVirtualPsbtResponse) Reset() { @@ -165,6 +176,13 @@ func (x *FundVirtualPsbtResponse) GetChangeOutputIndex() int32 { return 0 } +func (x *FundVirtualPsbtResponse) GetPassiveAssetPsbts() [][]byte { + if x != nil { + return x.PassiveAssetPsbts + } + return nil +} + type TxTemplate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1136,179 +1154,182 @@ var file_assetwalletrpc_assetwallet_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x03, 0x72, 0x61, 0x77, 0x42, 0x0a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x17, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, - 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, - 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, - 0xc7, 0x01, 0x0a, 0x0a, 0x54, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2e, - 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x61, 0x74, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x17, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x70, + 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x73, 0x62, 0x74, 0x73, + 0x22, 0xc7, 0x01, 0x0a, 0x0a, 0x54, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, + 0x2e, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x72, 0x65, 0x76, 0x49, 0x64, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, + 0x4a, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, + 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x52, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x65, 0x0a, 0x06, 0x50, 0x72, + 0x65, 0x76, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x01, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, + 0x69, 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, 0x22, 0x39, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, + 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, + 0x75, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x22, 0x5f, 0x0a, 0x17, + 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x0c, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x40, 0x0a, + 0x19, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, + 0x62, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x22, + 0x37, 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, + 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, + 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x53, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x35, 0x0a, + 0x14, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, + 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, + 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, + 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, + 0x3c, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x54, 0x0a, + 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x4b, 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, + 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, + 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 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, 0x4b, 0x0a, + 0x1b, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, + 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x1b, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, + 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, + 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x3f, 0x0a, 0x1c, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, + 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, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, + 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x82, 0x08, 0x0a, 0x0b, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x62, 0x0a, 0x0f, 0x46, + 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, + 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x62, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, + 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, + 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, + 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, + 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 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, + 0x62, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, + 0x65, 0x79, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x65, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x72, 0x65, 0x76, 0x49, 0x64, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x4a, - 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x52, - 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, - 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x52, 0x65, - 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x65, 0x0a, 0x06, 0x50, 0x72, 0x65, - 0x76, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x01, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, - 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, - 0x22, 0x39, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, - 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x75, - 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x22, 0x5f, 0x0a, 0x17, 0x53, - 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x40, 0x0a, 0x19, - 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, - 0x52, 0x0c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x22, 0x37, - 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, - 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, - 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x53, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, - 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x14, - 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, - 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, - 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x3c, - 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x54, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, - 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x74, - 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 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, 0x4b, 0x0a, 0x1b, - 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, - 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, - 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x1b, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, - 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, - 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x3f, 0x0a, 0x1c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, - 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 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, - 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, - 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x82, 0x08, 0x0a, 0x0b, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x62, 0x0a, 0x0f, 0x46, 0x75, - 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, - 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, - 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, - 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, - 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, - 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, - 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 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, 0x62, - 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, - 0x79, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x65, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, - 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x14, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, - 0x12, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, - 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x26, + 0x12, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, + 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x14, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 0x12, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, + 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, - 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, + 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, + 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/taprpc/assetwalletrpc/assetwallet.proto b/taprpc/assetwalletrpc/assetwallet.proto index c4d21d34b..8f0d99c13 100644 --- a/taprpc/assetwalletrpc/assetwallet.proto +++ b/taprpc/assetwalletrpc/assetwallet.proto @@ -105,7 +105,7 @@ message FundVirtualPsbtRequest { message FundVirtualPsbtResponse { /* - The funded but not yet signed PSBT packet. + The funded but not yet signed virtual PSBT packet. */ bytes funded_psbt = 1; @@ -113,6 +113,20 @@ message FundVirtualPsbtResponse { The index of the added change output or -1 if no change was left over. */ int32 change_output_index = 2; + + /* + The list of passive virtual transactions that are anchored in the same BTC + level anchor transaction inputs as the funded "active" asset above. These + assets can be ignored when using the AnchorVirtualPsbts RPC, since they are + retrieved, signed and committed automatically in that method. But the + passive assets have to be included in the CommitVirtualPsbts RPC which is + used when custom BTC level anchor transactions are created. + The main difference to the "active" asset above is that the passive assets + will not get their own entry in the transfer table of the database, since + they are just carried along and not directly affected by the direct user + action. + */ + repeated bytes passive_asset_psbts = 3; } message TxTemplate { diff --git a/taprpc/assetwalletrpc/assetwallet.swagger.json b/taprpc/assetwalletrpc/assetwallet.swagger.json index 6ba3c19b3..3e6e66cab 100644 --- a/taprpc/assetwalletrpc/assetwallet.swagger.json +++ b/taprpc/assetwalletrpc/assetwallet.swagger.json @@ -382,12 +382,20 @@ "funded_psbt": { "type": "string", "format": "byte", - "description": "The funded but not yet signed PSBT packet." + "description": "The funded but not yet signed virtual PSBT packet." }, "change_output_index": { "type": "integer", "format": "int32", "description": "The index of the added change output or -1 if no change was left over." + }, + "passive_asset_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The list of passive virtual transactions that are anchored in the same BTC\nlevel anchor transaction inputs as the funded \"active\" asset above. These\nassets can be ignored when using the AnchorVirtualPsbts RPC, since they are\nretrieved, signed and committed automatically in that method. But the\npassive assets have to be included in the CommitVirtualPsbts RPC which is\nused when custom BTC level anchor transactions are created.\nThe main difference to the \"active\" asset above is that the passive assets\nwill not get their own entry in the transfer table of the database, since\nthey are just carried along and not directly affected by the direct user\naction." } } }, From b24d2cd2134604f3ae140811c495c42734116919 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:55 +0100 Subject: [PATCH 07/20] tapsend+tapfreighter: move anchor script generation With this commit we refactor the code for generating the pk script for an anchor output and re-use it for both creating new outputs as well as validating input scripts. We also remove some unnecessary code and fix a typo. --- tapfreighter/wallet.go | 105 +++-------------------------------------- tapsend/send.go | 103 +++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 121 deletions(-) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index d760d18c0..d63f408a6 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -12,7 +12,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" - "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/wallet/txrules" @@ -548,14 +547,6 @@ func (f *AssetWallet) fundPacketWithInputs(ctx context.Context, return nil, err } - // Gather Taproot Asset commitments from the selected anchored assets. - var selectedTapCommitments []*commitment.TapCommitment - for _, selectedCommitment := range selectedCommitments { - selectedTapCommitments = append( - selectedTapCommitments, selectedCommitment.Commitment, - ) - } - fullValue, err := tapsend.ValidateInputs( inputCommitments, assetType, fundDesc, ) @@ -661,7 +652,7 @@ func (f *AssetWallet) fundPacketWithInputs(ctx context.Context, // of input versions. We need to set this now as in // PrepareOutputAssets locators are created which includes the // version from the vOut. If we don't set it here, a v1 asset - // spent that beocmes change will be a v0 if combined with such + // spent that becomes change will be a v0 if combined with such // inputs. // // TODO(roasbeef): remove as not needed? @@ -740,9 +731,11 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, internalKey, f.cfg.ChainParams.HDCoinType, ) - anchorPkScript, anchorMerkleRoot, err := inputAnchorPkScript( - assetInput, - ) + anchorPkScript, anchorMerkleRoot, _, err := + tapsend.AnchorOutputScript( + internalKey.PubKey, assetInput.TapscriptSibling, + assetInput.Commitment, + ) if err != nil { return nil, fmt.Errorf("cannot calculate input asset "+ "pk script: %w", err) @@ -792,7 +785,7 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, Value: assetInput.AnchorOutputValue, PkScript: anchorPkScript, InternalKey: internalKey.PubKey, - MerkleRoot: anchorMerkleRoot, + MerkleRoot: anchorMerkleRoot[:], TapscriptSibling: tapscriptSiblingBytes, Bip32Derivation: []*psbt.Bip32Derivation{ inBip32Derivation, @@ -1479,90 +1472,6 @@ func (f *AssetWallet) FetchInternalKeyLocator(ctx context.Context, return f.cfg.AddrBook.FetchInternalKeyLocator(ctx, rawKey) } -// inputAnchorPkScript returns the top-level Taproot output script of the input -// anchor output as well as the Taproot Asset script root of the output (the -// Taproot tweak). -func inputAnchorPkScript(assetInput *AnchoredCommitment) ([]byte, []byte, - error) { - - // If any of the assets were received non-interactively, then the - // Taproot Asset tree of the input anchor output was built with asset - // leaves that had empty SplitCommitments. We need to replicate this - // here as well. - inputCommitment, err := trimSplitWitnesses(assetInput.Commitment) - if err != nil { - return nil, nil, fmt.Errorf("unable to trim split "+ - "witnesses: %w", err) - } - - // Decode the Tapscript sibling preimage if there was one, so we can - // arrive at the correct merkle root hash. - var siblingHash *chainhash.Hash - if assetInput.TapscriptSibling != nil { - siblingHash, err = assetInput.TapscriptSibling.TapHash() - if err != nil { - return nil, nil, err - } - } - - merkleRoot := inputCommitment.TapscriptRoot(siblingHash) - anchorPubKey := txscript.ComputeTaprootOutputKey( - assetInput.InternalKey.PubKey, merkleRoot[:], - ) - - pkScript, err := tapscript.PayToTaprootScript(anchorPubKey) - return pkScript, merkleRoot[:], err -} - -// trimSplitWitnesses returns a copy of the input commitment in which all assets -// with a split commitment witness have their SplitCommitment field set to nil. -func trimSplitWitnesses( - original *commitment.TapCommitment) (*commitment.TapCommitment, - error) { - - // If the input asset was received non-interactively, then the Taproot - // Asset tree of the input anchor output was built with asset leaves - // that had empty SplitCommitments. However, the SplitCommitment field - // was populated when the transfer of the input asset was verified. - // To recompute the correct output script, we need to build a Taproot - // Asset tree from the input asset without any SplitCommitment. - tapCommitmentCopy, err := original.Copy() - if err != nil { - return nil, err - } - - allAssets := tapCommitmentCopy.CommittedAssets() - for _, inputAsset := range allAssets { - inputAssetCopy := inputAsset.Copy() - - // Assets received via non-interactive split should have one - // witness, with an empty PrevID and a SplitCommitment present. - if inputAssetCopy.HasSplitCommitmentWitness() && - *inputAssetCopy.PrevWitnesses[0].PrevID == asset.ZeroPrevID { - - inputAssetCopy.PrevWitnesses[0].SplitCommitment = nil - - // Build the new Taproot Asset tree by first updating - // the asset commitment tree with the new asset leaf, - // and then the top-level Taproot Asset tree. - inputCommitments := tapCommitmentCopy.Commitments() - inputCommitmentKey := inputAssetCopy.TapCommitmentKey() - inputAssetTree := inputCommitments[inputCommitmentKey] - err = inputAssetTree.Upsert(inputAssetCopy) - if err != nil { - return nil, err - } - - err = tapCommitmentCopy.Upsert(inputAssetTree) - if err != nil { - return nil, err - } - } - } - - return tapCommitmentCopy, nil -} - // adjustFundedPsbt takes a funded PSBT which may have used BIP-0069 sorting, // and creates a new one with outputs shuffled such that the change output is // the last output. diff --git a/tapsend/send.go b/tapsend/send.go index 33daaab34..0f3b2b90f 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -1115,27 +1115,11 @@ func UpdateTaprootOutputKeys(btcPacket *psbt.Packet, vPkt *tappsbt.VPacket, return err } - // Prepare the anchor output's tapscript sibling, if there is - // one. We assume (and checked in an earlier step) that each - // virtual output declares the same tapscript sibling if - // multiple virtual outputs are committed to the same anchor - // output index. - var ( - siblingPreimage = vOut.AnchorOutputTapscriptSibling - siblingHash *chainhash.Hash - ) - if siblingPreimage != nil { - siblingHash, err = siblingPreimage.TapHash() - if err != nil { - return fmt.Errorf("unable to get sibling "+ - "hash: %w", err) - } - } - - // Create the scripts corresponding to the receiver's - // TapCommitment. - script, err := tapscript.PayToAddrScript( - *internalKey, siblingHash, *anchorCommitment, + // We have all the information now to calculate the actual + // commitment output script. + script, merkleRoot, taprootAssetRoot, err := AnchorOutputScript( + internalKey, vOut.AnchorOutputTapscriptSibling, + anchorCommitment, ) if err != nil { return err @@ -1146,8 +1130,6 @@ func UpdateTaprootOutputKeys(btcPacket *psbt.Packet, vPkt *tappsbt.VPacket, // Also set some additional fields in the PSBT output to make // it easier to create the transfer database entry later. - merkleRoot := anchorCommitment.TapscriptRoot(siblingHash) - taprootAssetRoot := anchorCommitment.TapscriptRoot(nil) btcOut.Unknowns = tappsbt.AddCustomField( btcOut.Unknowns, tappsbt.PsbtKeyTypeOutputTaprootMerkleRoot, @@ -1163,6 +1145,81 @@ func UpdateTaprootOutputKeys(btcPacket *psbt.Packet, vPkt *tappsbt.VPacket, return nil } +// AnchorOutputScript creates the anchor output script given an internal key, +// tapscript sibling and the TapCommitment. It also returns the merkle root and +// taproot asset root for the commitment. +func AnchorOutputScript(internalKey *btcec.PublicKey, + siblingPreimage *commitment.TapscriptPreimage, + anchorCommitment *commitment.TapCommitment) ([]byte, chainhash.Hash, + chainhash.Hash, error) { + + var emptyHash chainhash.Hash + + // If any of the assets were received non-interactively, then the + // Taproot Asset tree of the input anchor output was built with asset + // leaves that had empty SplitCommitments. We need to replicate this + // here as well. + trimmedCommitment, err := trimSplitWitnesses(anchorCommitment) + if err != nil { + return nil, emptyHash, emptyHash, fmt.Errorf("unable to trim "+ + "split witnesses: %w", err) + } + + // Decode the Tapscript sibling preimage if there was one, so we can + // arrive at the correct merkle root hash. + _, siblingHash, err := commitment.MaybeEncodeTapscriptPreimage( + siblingPreimage, + ) + if err != nil { + return nil, emptyHash, emptyHash, err + } + + // Create the scripts corresponding to the receiver's TapCommitment. + script, err := tapscript.PayToAddrScript( + *internalKey, siblingHash, *trimmedCommitment, + ) + if err != nil { + return nil, emptyHash, emptyHash, err + } + + // Also set some additional fields in the PSBT output to make it easier + // to create the transfer database entry later. + merkleRoot := trimmedCommitment.TapscriptRoot(siblingHash) + taprootAssetRoot := trimmedCommitment.TapscriptRoot(nil) + + return script, merkleRoot, taprootAssetRoot, nil +} + +// trimSplitWitnesses returns a copy of the commitment in which all assets with +// a split commitment witness have their SplitCommitment field set to nil. +func trimSplitWitnesses( + c *commitment.TapCommitment) (*commitment.TapCommitment, error) { + + // If the input asset was received non-interactively, then the Taproot + // Asset tree of the input anchor output was built with asset leaves + // that had empty SplitCommitments. However, the SplitCommitment field + // was populated when the transfer of the input asset was verified. + // To recompute the correct output script, we need to build a Taproot + // Asset tree from the input asset without any SplitCommitment. + originalAssets := c.CommittedAssets() + assetCopies := make([]*asset.Asset, len(originalAssets)) + for idx, originalAsset := range originalAssets { + assetCopy := originalAsset.Copy() + + // Assets received via non-interactive split should have one + // witness, with an empty PrevID and a SplitCommitment present. + if assetCopy.HasSplitCommitmentWitness() && + *assetCopy.PrevWitnesses[0].PrevID == asset.ZeroPrevID { + + assetCopy.PrevWitnesses[0].SplitCommitment = nil + } + + assetCopies[idx] = assetCopy + } + + return commitment.FromAssets(assetCopies...) +} + // interactiveFullValueSend returns true (and the index of the recipient output) // if there is exactly one output that spends the input fully and interactively // (when discarding any potential passive asset anchor outputs). From 90f63dadba2da89be2d2e1e558824a4aae17b4be Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:56 +0100 Subject: [PATCH 08/20] tapsend: refactor and extend packet validation Because we now give users way more control over the construction and commitment of virtual packets into a BTC anchor transaction, we cannot rely on everything being set correctly anymore. To avoid things being committed incorrectly, we create rigorous virtual packet input and output validation functions that make sure everything is ready to be committed to the chain. --- tapsend/send.go | 466 +++++++++++++++++++++++-- tapsend/send_test.go | 806 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1239 insertions(+), 33 deletions(-) diff --git a/tapsend/send.go b/tapsend/send.go index 0f3b2b90f..b9fb28093 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -101,11 +101,26 @@ var ( "send: Taproot Asset commitment not found", ) - // ErrInvalidAnchorInfo is an error returned when the anchor output + // ErrInvalidAnchorOutputInfo is an error returned when the anchor output // information on a virtual transaction output is invalid. - ErrInvalidAnchorInfo = errors.New( + ErrInvalidAnchorOutputInfo = errors.New( "send: invalid anchor output info", ) + + // ErrInvalidAnchorInputInfo is an error returned when the anchor input + // information on a virtual transaction input is invalid. + ErrInvalidAnchorInputInfo = errors.New( + "send: invalid anchor input info", + ) + + // ErrAssetMissing is an error returned when an asset is missing from a + // virtual transaction. + ErrAssetMissing = errors.New("asset missing") + + // ErrAssetNotSigned is an error returned when an asset is not signed + // in a virtual transaction that was already committed to an anchor + // output. + ErrAssetNotSigned = errors.New("asset not signed") ) var ( @@ -859,6 +874,15 @@ func CreateOutputCommitments( // index. outputCommitments := make(tappsbt.OutputCommitments) + // We require all outputs that reference the same anchor output to be + // identical, otherwise some assumptions in the code below don't hold. + if err := AssertInputAnchorsEqual(packets); err != nil { + return nil, err + } + if err := AssertOutputAnchorsEqual(packets); err != nil { + return nil, err + } + // And now we commit each packet to the respective anchor output // commitments. for _, vPkt := range packets { @@ -888,12 +912,6 @@ func commitPacket(vPkt *tappsbt.VPacket, } } - // We require all outputs that reference the same anchor output to be - // identical, otherwise some assumptions in the code below don't hold. - if err := assertAnchorsEqual(vPkt); err != nil { - return err - } - for idx := range outputs { vOut := outputs[idx] anchorOutputIdx := vOut.AnchorOutputIndex @@ -1249,39 +1267,88 @@ func interactiveFullValueSend(totalInputAmount uint64, return recipientIndex, fullValueInteractiveSend } -// assertAnchorsEqual makes sure that the anchor output information for each -// output of the virtual packet that anchors to the same BTC level output is -// identical. -func assertAnchorsEqual(vPkt *tappsbt.VPacket) error { - deDupMap := make(map[uint32]*tappsbt.Anchor) - for idx := range vPkt.Outputs { - vOut := vPkt.Outputs[idx] - +// AssertOutputAnchorsEqual makes sure that the anchor output information for +// each output of the virtual packets that anchors to the same BTC level output +// is identical. +func AssertOutputAnchorsEqual(packets []*tappsbt.VPacket) error { + // comparableAnchor is a helper function that returns an Anchor struct + // from a virtual output comparing exactly the fields we require to be + // equal across all virtual outputs for the same anchor output index. + comparableAnchor := func(o *tappsbt.VOutput) (*tappsbt.Anchor, error) { siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( - vOut.AnchorOutputTapscriptSibling, + o.AnchorOutputTapscriptSibling, ) if err != nil { - return fmt.Errorf("unable to encode tapscript "+ + return nil, fmt.Errorf("unable to encode tapscript "+ "preimage: %w", err) } - outAnchor := &tappsbt.Anchor{ - InternalKey: vOut.AnchorOutputInternalKey, + + // A virtual output only has certain anchor information set, so + // we can only construct a partial Anchor struct from it. + return &tappsbt.Anchor{ + InternalKey: o.AnchorOutputInternalKey, TapscriptSibling: siblingBytes, - Bip32Derivation: vOut.AnchorOutputBip32Derivation, - TrBip32Derivation: vOut.AnchorOutputTaprootBip32Derivation, - } + Bip32Derivation: o.AnchorOutputBip32Derivation, + TrBip32Derivation: o.AnchorOutputTaprootBip32Derivation, + }, nil + } - anchor, ok := deDupMap[vOut.AnchorOutputIndex] - if !ok { - deDupMap[vOut.AnchorOutputIndex] = outAnchor - continue + outDeDupMap := make(map[uint32]*tappsbt.Anchor) + for _, vPkt := range packets { + for idx := range vPkt.Outputs { + vOut := vPkt.Outputs[idx] + + outAnchor, err := comparableAnchor(vOut) + if err != nil { + return err + } + + anchor, ok := outDeDupMap[vOut.AnchorOutputIndex] + if !ok { + outDeDupMap[vOut.AnchorOutputIndex] = outAnchor + + continue + } + + if !reflect.DeepEqual(anchor, outAnchor) { + return fmt.Errorf("%w: anchor output "+ + "information for output %d is not "+ + "identical to previous with same "+ + "anchor output index", + ErrInvalidAnchorOutputInfo, idx) + } } + } - if !reflect.DeepEqual(anchor, outAnchor) { - return fmt.Errorf("%w: anchor output information for "+ - "output %d is not identical to previous "+ - "with same anchor output index", - ErrInvalidAnchorInfo, idx) + return nil +} + +// AssertInputAnchorsEqual makes sure that the anchor input information for +// each input of the virtual packets that anchors to the same BTC level input +// is identical. +func AssertInputAnchorsEqual(packets []*tappsbt.VPacket) error { + inDeDupMap := make(map[wire.OutPoint]*tappsbt.Anchor) + for _, vPkt := range packets { + for idx := range vPkt.Inputs { + vIn := vPkt.Inputs[idx] + + inAnchor := &vIn.Anchor + op := vIn.PrevID.OutPoint + + anchor, ok := inDeDupMap[op] + if !ok { + inDeDupMap[op] = inAnchor + + continue + } + + if !reflect.DeepEqual(anchor, inAnchor) { + return fmt.Errorf("%w: anchor input "+ + "information for input %v is not "+ + "identical to previous with same "+ + "anchor outpoint", + ErrInvalidAnchorInputInfo, op) + } } } @@ -1317,3 +1384,338 @@ func LogCommitment(prefix string, idx int, a.Amount, a.Version, a.SplitCommitmentRoot != nil) } } + +// ValidateAnchorOutputs checks that the anchor outputs of the virtual packets +// are valid and consistent with the provided BTC level PSBT packet. When +// calling this function, everything must be ready to be signed on the BTC +// level, meaning all asset level information must be final (including the +// asset signatures for ASSET_VERSION_V0 assets). If the updateDatabaseHints +// flag is set, the function will also make sure the extra information (merkle +// root and taproot asset root) is set in the PSBT packet's outputs. That info +// is required to create the transfer output database entry. +func ValidateAnchorOutputs(anchorPacket *psbt.Packet, + packets []*tappsbt.VPacket, updateDatabaseHints bool) error { + + // We first make sure the "static" anchor information in the virtual + // packets is consistent with the provided BTC level PSBT packet. + var ( + numAnchorOutputs = uint32(len(anchorPacket.Outputs)) + outputAssets = make(map[uint32][]*asset.Asset) + outputSiblings = make( + map[uint32]*commitment.TapscriptPreimage, + ) + ) + for _, vPkt := range packets { + for idx := range vPkt.Outputs { + vOut := vPkt.Outputs[idx] + + // The anchor output index must be within the bounds of + // the BTC level PSBT packet. + if vOut.AnchorOutputIndex >= numAnchorOutputs { + return fmt.Errorf("%w: anchor output index %d "+ + "is invalid", ErrInvalidOutputIndexes, + vOut.AnchorOutputIndex) + } + + // The internal key must be set for each output. + if vOut.AnchorOutputInternalKey == nil { + return fmt.Errorf("%w: anchor output internal "+ + "key missing", + ErrInvalidAnchorOutputInfo) + } + + // The tapscript sibling must be a valid tapscript + // preimage. + siblingPreimage := vOut.AnchorOutputTapscriptSibling + _, _, err := commitment.MaybeEncodeTapscriptPreimage( + siblingPreimage, + ) + if err != nil { + return fmt.Errorf("error parsing anchor "+ + "output tapscript sibling: %w", err) + } + + btcOut := anchorPacket.Outputs[vOut.AnchorOutputIndex] + if !bytes.Equal( + btcOut.TaprootInternalKey, + schnorr.SerializePubKey( + vOut.AnchorOutputInternalKey, + ), + ) { + + return fmt.Errorf("%w: internal key mismatch", + ErrInvalidAnchorOutputInfo) + } + + if !reflect.DeepEqual( + btcOut.Bip32Derivation, + vOut.AnchorOutputBip32Derivation, + ) { + + return fmt.Errorf("%w: bip32 derivation "+ + "mismatch", ErrInvalidAnchorOutputInfo) + } + + if !reflect.DeepEqual( + btcOut.TaprootBip32Derivation, + vOut.AnchorOutputTaprootBip32Derivation, + ) { + + return fmt.Errorf("%w: taproot bip32 "+ + "derivation mismatch", + ErrInvalidAnchorOutputInfo) + } + + // We also need to check that the asset is either signed + // or a segregated witness asset (ASSET_VERSION_V1). + if vOut.Asset == nil { + return ErrAssetMissing + } + if vOut.Asset.Version != asset.V1 { + witnesses, err := vOut.PrevWitnesses() + if err != nil { + return fmt.Errorf("error getting "+ + "witnesses: %w", err) + } + + if len(witnesses) == 0 { + return ErrAssetNotSigned + } + + if len(witnesses[0].TxWitness) == 0 { + return ErrAssetNotSigned + } + } + + // All tests passed so far, we can add the asset to the + // list of assets for this anchor output. + outputAssets[vOut.AnchorOutputIndex] = append( + outputAssets[vOut.AnchorOutputIndex], + vOut.Asset, + ) + outputSiblings[vOut.AnchorOutputIndex] = siblingPreimage + } + } + + // We can now go through each anchor output that will carry assets and + // check that we arrive at the correct script. + for anchorIdx, assets := range outputAssets { + anchorCommitment, err := commitment.FromAssets(assets...) + if err != nil { + return fmt.Errorf("unable to create commitment from "+ + "output assets: %w", err) + } + + anchorOut := &anchorPacket.Outputs[anchorIdx] + anchorTxOut := anchorPacket.UnsignedTx.TxOut[anchorIdx] + internalKey, err := schnorr.ParsePubKey( + anchorOut.TaprootInternalKey, + ) + if err != nil { + return fmt.Errorf("error parsing anchor output "+ + "internal key: %w", err) + } + + LogCommitment( + "Output", int(anchorIdx), anchorCommitment, internalKey, + nil, nil, + ) + + script, merkleRoot, taprootAssetRoot, err := AnchorOutputScript( + internalKey, outputSiblings[anchorIdx], + anchorCommitment, + ) + if err != nil { + return fmt.Errorf("unable to create anchor output "+ + "script: %w", err) + } + + // This is the most important check. This ensures that the + // assets in the outputs of the virtual transactions match + // exactly the assets that are committed to in the anchor + // output script. + if !bytes.Equal(anchorTxOut.PkScript, script) { + return fmt.Errorf("%w: anchor output script mismatch "+ + "for anchor output index %d", + ErrInvalidAnchorOutputInfo, anchorIdx) + } + + // If we don't need to update the database hints, we can stop + // here. + if !updateDatabaseHints { + continue + } + + // The caller requested to update the merkle root and Taproot + // Asset root in the PSBT packet, so we do that now. + anchorOut.Unknowns = tappsbt.AddCustomField( + anchorOut.Unknowns, + tappsbt.PsbtKeyTypeOutputTaprootMerkleRoot, + merkleRoot[:], + ) + anchorOut.Unknowns = tappsbt.AddCustomField( + anchorOut.Unknowns, + tappsbt.PsbtKeyTypeOutputAssetRoot, + taprootAssetRoot[:], + ) + } + + return nil +} + +// ValidateAnchorInputs checks that the anchor inputs of the virtual packets +// are valid and consistent with the provided BTC level PSBT packet. +func ValidateAnchorInputs(anchorPacket *psbt.Packet, + packets []*tappsbt.VPacket) error { + + // We first make sure the "static" anchor information in the virtual + // packets is consistent with the provided BTC level PSBT packet. + var ( + inputAssets = make(map[wire.OutPoint][]*asset.Asset) + inputScripts = make(map[wire.OutPoint][]byte) + inputAnchors = make(map[wire.OutPoint]tappsbt.Anchor) + inputSiblings = make( + map[wire.OutPoint]*commitment.TapscriptPreimage, + ) + inputAnchorIndex = make(map[wire.OutPoint]uint32) + ) + for _, vPkt := range packets { + for idx := range vPkt.Inputs { + vIn := vPkt.Inputs[idx] + + outpoint := vIn.PrevID.OutPoint + if !HasInput(anchorPacket.UnsignedTx, outpoint) { + return fmt.Errorf("%w: prev ID outpoint %v "+ + "not found in anchor TX", + ErrInvalidAnchorInputInfo, outpoint) + } + + // The internal key must be set for each input. + if vIn.Anchor.InternalKey == nil { + return fmt.Errorf("%w: internal key missing", + ErrInvalidAnchorInputInfo) + } + + var anchorIn *psbt.PInput + for i, txIn := range anchorPacket.UnsignedTx.TxIn { + if txIn.PreviousOutPoint == outpoint { + anchorIn = &anchorPacket.Inputs[i] + inputAnchorIndex[outpoint] = uint32(i) + + break + } + } + + // Shouldn't happen as we validated above, but just in + // case. + if anchorIn == nil { + return fmt.Errorf("%w: input %v not found in "+ + "anchor TX", ErrInvalidAnchorInputInfo, + outpoint) + } + + // We can only check if the two internal keys are equal + // if the transaction isn't signed yet. If it is, then + // the PSBT library no longer serializes some fields. + if len(anchorIn.FinalScriptWitness) == 0 { + // Make sure we can parse the internal key. + _, err := schnorr.ParsePubKey( + anchorIn.TaprootInternalKey, + ) + if err != nil { + return fmt.Errorf("%w, error parsing "+ + "internal key: %s", + ErrInvalidAnchorInputInfo, + err.Error()) + } + + // But only compare the x-coordinate of the + // actual key. + if !bytes.Equal( + anchorIn.TaprootInternalKey, + schnorr.SerializePubKey( + vIn.Anchor.InternalKey, + ), + ) { + + return fmt.Errorf("%w: internal key "+ + "mismatch", + ErrInvalidAnchorInputInfo) + } + } + + sibling, _, err := commitment.MaybeDecodeTapscriptPreimage( + vIn.Anchor.TapscriptSibling, + ) + if err != nil { + return fmt.Errorf("%w: error parsing anchor "+ + "input tapscript sibling: %s", + ErrInvalidAnchorInputInfo, err.Error()) + } + + // The witness UTXO information is also required to get + // the correct script for the input. + if anchorIn.WitnessUtxo == nil { + return fmt.Errorf("%w: witness UTXO missing", + ErrInvalidAnchorInputInfo) + } + + // All tests passed so far, we can add the asset to the + // list of assets for this anchor input. + inputAssets[outpoint] = append( + inputAssets[outpoint], vIn.Asset(), + ) + inputSiblings[outpoint] = sibling + inputScripts[outpoint] = anchorIn.WitnessUtxo.PkScript + inputAnchors[outpoint] = vIn.Anchor + } + } + + // We can now go through each anchor input that contains assets being + // spent and check that we arrive at the correct script. + for anchorOutpoint, assets := range inputAssets { + anchorCommitment, err := commitment.FromAssets(assets...) + if err != nil { + return fmt.Errorf("unable to create commitment from "+ + "output assets: %w", err) + } + + anchorIdx := inputAnchorIndex[anchorOutpoint] + anchorIn := inputAnchors[anchorOutpoint] + internalKey := anchorIn.InternalKey + + LogCommitment( + "Input", int(anchorIdx), anchorCommitment, internalKey, + nil, nil, + ) + + script, merkleRoot, _, err := AnchorOutputScript( + internalKey, inputSiblings[anchorOutpoint], + anchorCommitment, + ) + if err != nil { + return fmt.Errorf("unable to create anchor output "+ + "script: %w", err) + } + + // This is the most important check. This ensures that the + // assets in the outputs of the virtual transactions match + // exactly the assets that are committed to in the anchor + // output script. + anchorScript := inputScripts[anchorOutpoint] + if !bytes.Equal(anchorScript, script) { + return fmt.Errorf("%w: anchor input script mismatch "+ + "for anchor outpoint %v", + ErrInvalidAnchorInputInfo, anchorOutpoint) + } + + // The merkle root should also match. + if !bytes.Equal(anchorIn.MerkleRoot, merkleRoot[:]) { + return fmt.Errorf("%w: merkle root mismatch for "+ + "anchor outpoint %v", ErrInvalidAnchorInputInfo, + anchorOutpoint) + } + } + + return nil +} diff --git a/tapsend/send_test.go b/tapsend/send_test.go index 4b4e939c6..a63cf4660 100644 --- a/tapsend/send_test.go +++ b/tapsend/send_test.go @@ -13,6 +13,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" tap "github.com/lightninglabs/taproot-assets" @@ -1081,7 +1082,7 @@ var createOutputCommitmentsTestCases = []testCase{{ ) return err }, - err: tapsend.ErrInvalidAnchorInfo, + err: tapsend.ErrInvalidAnchorOutputInfo, }, { name: "non-interactive collectible with group key", f: func(t *testing.T) error { @@ -2068,3 +2069,806 @@ func sendCommitment(t *testing.T, a *asset.Asset, sendAmt btcutil.Amount, TreeRoot: root, } } + +// TestAssertOutputAnchorsEqual tests that invalid output anchor information in +// virtual packets are detected correctly. +func TestAssertOutputAnchorsEqual(t *testing.T) { + packetWithOutputs := func(outs ...*tappsbt.VOutput) *tappsbt.VPacket { + return &tappsbt.VPacket{ + Outputs: outs, + } + } + + var ( + key1 = test.RandPubKey(t) + key2 = test.RandPubKey(t) + ) + + testCases := []struct { + name string + packets []*tappsbt.VPacket + expectedErr error + }{ + { + name: "valid, different anchor output index in same " + + "packet", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }, &tappsbt.VOutput{ + AnchorOutputIndex: 1, + }), + }, + }, + { + name: "valid, identical empty anchors", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }, &tappsbt.VOutput{ + AnchorOutputIndex: 0, + }), + }, + }, + { + name: "valid, identical empty anchors in two packets", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }), + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }), + }, + }, + { + name: "valid, different anchor output index in two " + + "packets", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }), + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 1, + }), + }, + }, + { + name: "invalid, different key", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + }, &tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key2, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + }, + { + name: "invalid, different key in two packets", + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + }), + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key2, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tapsend.AssertOutputAnchorsEqual(tc.packets) + require.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +// TestAssertInputAnchorsEqual tests that invalid input anchor information in +// virtual packets are detected correctly. +func TestAssertInputAnchorsEqual(t *testing.T) { + packetWithInputs := func(ins ...*tappsbt.VInput) *tappsbt.VPacket { + return &tappsbt.VPacket{ + Inputs: ins, + } + } + + var ( + op1 = wire.OutPoint{Hash: chainhash.Hash{1}, Index: 0} + op2 = wire.OutPoint{Hash: chainhash.Hash{2}, Index: 1} + key1 = test.RandPubKey(t) + key2 = test.RandPubKey(t) + ) + + testCases := []struct { + name string + packets []*tappsbt.VPacket + expectedErr error + }{ + { + name: "valid, different anchor inputs in same packet", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }, &tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op2, + }, + }), + }, + }, + { + name: "valid, identical empty anchors", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }, &tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }), + }, + }, + { + name: "valid, identical empty anchors in two packets", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }), + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }), + }, + }, + { + name: "valid, different anchor inputs in two packets", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }), + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op2, + }, + }), + }, + }, + { + name: "invalid, different key", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + }, + }, &tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key2, + }, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + }, + { + name: "invalid, different key in two packets", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + }, + }), + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key2, + }, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + }, + { + name: "invalid, different pk script in two packets", + packets: []*tappsbt.VPacket{ + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + MerkleRoot: []byte("foo"), + }, + }), + packetWithInputs(&tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + MerkleRoot: []byte("bar"), + }, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tapsend.AssertInputAnchorsEqual(tc.packets) + require.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +// TestAssertOutputAnchorsEqual tests that invalid output anchor information in +// virtual packets are detected correctly. +func TestValidateAnchorOutputs(t *testing.T) { + psbtWithOutputs := func(outs ...psbt.POutput) *psbt.Packet { + return &psbt.Packet{ + Outputs: outs, + } + } + withTxOuts := func(p *psbt.Packet, txOuts ...*wire.TxOut) *psbt.Packet { + p.UnsignedTx = &wire.MsgTx{TxOut: txOuts} + return p + } + packetWithOutputs := func(outs ...*tappsbt.VOutput) *tappsbt.VPacket { + return &tappsbt.VPacket{ + Outputs: outs, + } + } + + sibling, err := commitment.NewPreimageFromLeaf(txscript.NewBaseTapLeaf( + []byte("not a valid script"), + )) + require.NoError(t, err) + + var ( + key1 = test.RandPubKey(t) + key2 = test.RandPubKey(t) + asset1 = asset.RandAsset(t, asset.RandAssetType(t)) + vOutSibling = &tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + Asset: asset1, + AnchorOutputTapscriptSibling: sibling, + } + vOutNoSibling = &tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + Asset: asset1, + } + keyRoot = tappsbt.PsbtKeyTypeOutputTaprootMerkleRoot + ) + asset1Commitment, err := commitment.FromAssets(asset1) + require.NoError(t, err) + + scriptSibling, rootSibling, _, err := tapsend.AnchorOutputScript( + key1, sibling, asset1Commitment, + ) + require.NoError(t, err) + scriptNoSibling, rootNoSibling, _, err := tapsend.AnchorOutputScript( + key1, nil, asset1Commitment, + ) + require.NoError(t, err) + + testCases := []struct { + name string + anchor *psbt.Packet + packets []*tappsbt.VPacket + expectedErr error + errSubstring string + expectedRoot *chainhash.Hash + }{ + { + name: "invalid, wrong anchor output index", + anchor: &psbt.Packet{ + Outputs: nil, + }, + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 1, + }), + }, + expectedErr: tapsend.ErrInvalidOutputIndexes, + errSubstring: "output index 1 is invalid", + }, + { + name: "invalid, missing anchor internal key", + anchor: psbtWithOutputs(psbt.POutput{}), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + errSubstring: "internal key missing", + }, + { + name: "invalid, different anchor internal key", + anchor: psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key2, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + errSubstring: "internal key mismatch", + }, + { + name: "invalid, different bip32 derivation", + anchor: psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + Bip32Derivation: []*psbt.Bip32Derivation{ + {}, + {}, + }, + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + }), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + errSubstring: "bip32 derivation", + }, + { + name: "invalid, asset missing", + anchor: psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + }), + }, + expectedErr: tapsend.ErrAssetMissing, + }, + { + name: "invalid, asset has no witness", + anchor: psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + Asset: &asset.Asset{}, + }), + }, + expectedErr: tapsend.ErrAssetNotSigned, + }, + { + name: "invalid, asset not signed", + anchor: psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(&tappsbt.VOutput{ + AnchorOutputIndex: 0, + AnchorOutputInternalKey: key1, + Asset: &asset.Asset{ + PrevWitnesses: []asset.Witness{ + {}, + }, + }, + }), + }, + expectedErr: tapsend.ErrAssetNotSigned, + }, + { + name: "invalid, invalid script", + anchor: withTxOuts(psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), &wire.TxOut{ + PkScript: bytes.Repeat([]byte{0x00}, 32), + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(vOutNoSibling), + }, + expectedErr: tapsend.ErrInvalidAnchorOutputInfo, + errSubstring: "output script mismatch for anchor", + }, + { + name: "valid, no sibling", + anchor: withTxOuts(psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), &wire.TxOut{ + PkScript: scriptNoSibling, + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(vOutNoSibling), + }, + expectedRoot: &rootNoSibling, + }, + { + name: "valid, with sibling", + anchor: withTxOuts(psbtWithOutputs(psbt.POutput{ + TaprootInternalKey: schnorr.SerializePubKey( + key1, + ), + }), &wire.TxOut{ + PkScript: scriptSibling, + }), + packets: []*tappsbt.VPacket{ + packetWithOutputs(vOutSibling), + }, + expectedRoot: &rootSibling, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tapsend.ValidateAnchorOutputs( + tc.anchor, tc.packets, true, + ) + require.ErrorIs(t, err, tc.expectedErr) + + if tc.expectedErr != nil { + require.ErrorContains(t, err, tc.errSubstring) + } + + if tc.expectedRoot != nil { + require.NoError(t, err) + root := tappsbt.ExtractCustomField( + tc.anchor.Outputs[0].Unknowns, keyRoot, + ) + require.Equal(t, tc.expectedRoot[:], root) + } + }) + } +} + +// TestValidateAnchorInputs tests that invalid input anchor information in +// virtual packets are detected correctly. +func TestValidateAnchorInputs(t *testing.T) { + psbtWithInputs := func(ins ...psbt.PInput) *psbt.Packet { + return &psbt.Packet{ + Inputs: ins, + } + } + withTxIns := func(p *psbt.Packet, txIns ...*wire.TxIn) *psbt.Packet { + p.UnsignedTx = &wire.MsgTx{TxIn: txIns} + return p + } + packetWithInput := func(in tappsbt.VInput, + a *asset.Asset) *tappsbt.VPacket { + + vPkt := &tappsbt.VPacket{ + ChainParams: &address.RegressionNetTap, + Inputs: []*tappsbt.VInput{ + &in, + }, + } + if a != nil { + vPkt.SetInputAsset(0, a) + } + + return vPkt + } + + sibling, err := commitment.NewPreimageFromLeaf(txscript.NewBaseTapLeaf( + []byte("not a valid script"), + )) + require.NoError(t, err) + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage(sibling) + require.NoError(t, err) + + var ( + op1 = wire.OutPoint{Hash: chainhash.Hash{1}, Index: 0} + op2 = wire.OutPoint{Hash: chainhash.Hash{2}, Index: 1} + key1 = test.RandPubKey(t) + key2 = test.RandPubKey(t) + key1Bytes = schnorr.SerializePubKey(key1) + asset1 = asset.RandAsset(t, asset.RandAssetType(t)) + vInSibling = tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + TapscriptSibling: siblingBytes, + }, + } + vInNoSibling = tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + }, + } + ) + asset1Commitment, err := commitment.FromAssets(asset1) + require.NoError(t, err) + + scriptSibling, rootSibling, _, err := tapsend.AnchorOutputScript( + key1, sibling, asset1Commitment, + ) + require.NoError(t, err) + scriptNoSibling, rootNoSibling, _, err := tapsend.AnchorOutputScript( + key1, nil, asset1Commitment, + ) + require.NoError(t, err) + + packetSibling := packetWithInput(vInSibling, asset1) + packetSibling.Inputs[0].Anchor.MerkleRoot = rootSibling[:] + + packetNoSibling := packetWithInput(vInNoSibling, asset1) + packetNoSibling.Inputs[0].Anchor.MerkleRoot = rootNoSibling[:] + + testCases := []struct { + name string + anchor *psbt.Packet + packets []*tappsbt.VPacket + expectedErr error + errSubstring string + }{ + { + name: "invalid, empty inputs", + anchor: withTxIns(psbtWithInputs()), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op2, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "prev ID outpoint", + }, + { + name: "invalid, wrong inputs", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{}), &wire.TxIn{}, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op2, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "prev ID outpoint", + }, + { + name: "invalid, missing internal key", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{}), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "internal key missing", + }, + { + name: "invalid, invalid internal key", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: []byte("invalid"), + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key2, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "error parsing internal key", + }, + { + name: "invalid, wrong internal key", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key2, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "internal key mismatch", + }, + { + name: "invalid, invalid sibling preimage", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + TapscriptSibling: []byte( + "invalid", + ), + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "error parsing anchor input tapscript " + + "sibling", + }, + { + name: "invalid, missing utxo info", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(tappsbt.VInput{ + PrevID: asset.PrevID{ + OutPoint: op1, + }, + Anchor: tappsbt.Anchor{ + InternalKey: key1, + TapscriptSibling: siblingBytes, + }, + }, nil), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "witness UTXO missing", + }, + { + name: "invalid, incorrect pk script", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + WitnessUtxo: &wire.TxOut{ + PkScript: []byte("invalid"), + }, + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(vInSibling, asset1), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "anchor input script mismatch", + }, + { + name: "invalid, invalid merkle root", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + WitnessUtxo: &wire.TxOut{ + PkScript: scriptSibling, + }, + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetWithInput(vInSibling, asset1), + }, + expectedErr: tapsend.ErrInvalidAnchorInputInfo, + errSubstring: "merkle root mismatch for anchor", + }, + { + name: "valid, no sibling", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + WitnessUtxo: &wire.TxOut{ + PkScript: scriptNoSibling, + }, + TaprootMerkleRoot: rootNoSibling[:], + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetNoSibling, + }, + }, + { + name: "valid, with sibling", + anchor: withTxIns( + psbtWithInputs(psbt.PInput{ + TaprootInternalKey: key1Bytes, + WitnessUtxo: &wire.TxOut{ + PkScript: scriptSibling, + }, + TaprootMerkleRoot: rootSibling[:], + }), &wire.TxIn{ + PreviousOutPoint: op1, + }, + ), + packets: []*tappsbt.VPacket{ + packetSibling, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tapsend.ValidateAnchorInputs( + tc.anchor, tc.packets, + ) + require.ErrorIs(t, err, tc.expectedErr) + + if tc.expectedErr != nil { + require.ErrorContains(t, err, tc.errSubstring) + } + }) + } +} From ff9af1106d68eb96b5cbba06271b0bd2a5bfd79e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:57 +0100 Subject: [PATCH 09/20] commitment: fix panic with Delete on empty commitment Deleting an asset from an empty commitment should be a no-op and not result in a panic. This commit avoids the panic by returning early because the tree for an empty commitment is nil. --- commitment/asset.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/commitment/asset.go b/commitment/asset.go index 510e35444..f8fed4d14 100644 --- a/commitment/asset.go +++ b/commitment/asset.go @@ -279,6 +279,11 @@ func (c *AssetCommitment) Delete(oldAsset *asset.Asset) error { return ErrAssetTypeMismatch } + // Deleting from an empty tree is a no-op. + if len(c.assets) == 0 { + return nil + } + key := oldAsset.AssetCommitmentKey() // TODO(bhandras): thread the context through. From ce6a9f6ef6973a990b78f7546c889deb34d16be6 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:28:59 +0100 Subject: [PATCH 10/20] tapfreighter: allow internal key to be unknown If we want to have the ability to encumber the BTC level outputs by a MuSig2 internal key and/or have a Tapscript sibling, we need to allow for the internal key to be unknown to the wallet. Otherwise we can't choose the NUMS pub key as the internal key if we wanted, for example, to only allow a script spend on the BTC level. --- tapfreighter/wallet.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index d63f408a6..e3db4b61a 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -718,13 +718,6 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, // now we just put this check in place. assetInput := eligibleCommitments[idx] internalKey := assetInput.InternalKey - if !f.cfg.KeyRing.IsLocalKey(ctx, internalKey) { - return nil, fmt.Errorf("invalid internal key family "+ - "for selected input, not known to lnd: "+ - "key=%x, fam=%v, idx=%v", - internalKey.PubKey.SerializeCompressed(), - internalKey.Family, internalKey.Index) - } inBip32Derivation, inTrBip32Derivation := tappsbt.Bip32DerivationFromKeyDesc( From 7e7dc19cb47f56b0611eb8dd312120495cab5c8a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:00 +0100 Subject: [PATCH 11/20] tapsend+tapfreighter: move asset pruning We no longer prune any tombstone or burn assets from the input commitment, since we'll need them later when committing all assets on-chain to validate that we can fully reconstruct the input TAP commitment tree. So we keep the purgeable assets in the input commitment and only filter them before creating the passive assets (which operates on a copy anyway, so they stay in the original input commitment). --- tapfreighter/wallet.go | 181 ++++++++--------------------------------- tapsend/send.go | 53 ++++++++++++ 2 files changed, 85 insertions(+), 149 deletions(-) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index e3db4b61a..af771a5a0 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -20,7 +20,6 @@ import ( "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/fn" - "github.com/lightninglabs/taproot-assets/mssmt" "github.com/lightninglabs/taproot-assets/proof" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/tapscript" @@ -500,14 +499,14 @@ func (f *AssetWallet) hasOtherAssets(inputCommitments tappsbt.InputCommitments, for idx := range inputCommitments { tapCommitment := inputCommitments[idx] - passiveCommitments, err := removeActiveCommitments( + passiveCommitments, err := tapsend.RemovePacketsFromCommitment( tapCommitment, vPackets, ) if err != nil { return false, err } - if len(passiveCommitments) > 0 { + if len(passiveCommitments.CommittedAssets()) > 0 { return true, nil } } @@ -582,17 +581,6 @@ func (f *AssetWallet) fundPacketWithInputs(ctx context.Context, } } - // We now also need to remove all tombstone or burns from our active - // commitments. - for idx := range inputCommitments { - inputCommitments[idx], err = pruneTombstonesAndBurns( - inputCommitments[idx], - ) - if err != nil { - return nil, err - } - } - // We expect some change back, or have passive assets to commit to, so // let's make sure we create a transfer output. var changeOut *tappsbt.VOutput @@ -964,118 +952,6 @@ func verifyInclusionProof(vIn *tappsbt.VInput) error { return nil } -// pruneTombstonesAndBurns removes all tombstones and burns from the active -// input commitment. -func pruneTombstonesAndBurns( - inputCommitment *commitment.TapCommitment) (*commitment.TapCommitment, - error) { - - committedAssets := inputCommitment.CommittedAssets() - committedAssets = fn.Filter(committedAssets, func(a *asset.Asset) bool { - return !a.IsUnSpendable() && !a.IsBurn() - }) - - return commitment.FromAssets(committedAssets...) -} - -// removeActiveCommitments removes all active commitments from the given input -// commitment and only returns a tree of passive commitments. -func removeActiveCommitments(inputCommitment *commitment.TapCommitment, - vPackets []*tappsbt.VPacket) (commitment.AssetCommitments, error) { - - // Gather passive assets found in the commitment. This creates a copy of - // the commitment map, so we can remove things freely. - passiveCommitments := inputCommitment.Commitments() - - // removeAsset is a helper function that removes the given asset from - // the passed asset commitment and updates the top level Taproot Asset - // commitment with the new asset commitment, if that still contains any - // assets. - removeAsset := func(assetCommitment *commitment.AssetCommitment, - toRemove *asset.Asset, tapKey [32]byte) error { - - // We need to make a copy in order to not modify the original - // commitment, as the above call to get all commitments just - // creates a new slice, but we still have a pointer to the - // original asset commitment. - var err error - assetCommitment, err = assetCommitment.Copy() - if err != nil { - return fmt.Errorf("unable to copy asset commitment: %w", - err) - } - - // Now we can remove the asset from the commitment. - err = assetCommitment.Delete(toRemove) - if err != nil { - return fmt.Errorf("unable to delete asset "+ - "commitment: %w", err) - } - - // Since we're not returning the root Taproot Asset commitment - // but a map of all asset commitments, we need to prune the - // asset commitment manually if it is empty now. - rootHash := assetCommitment.TreeRoot.NodeHash() - if rootHash == mssmt.EmptyTreeRootHash { - delete(passiveCommitments, tapKey) - - return nil - } - - // There are other leaves of this asset in our asset tree, let's - // now update our passive commitment map so these will be - // carried along. - passiveCommitments[tapKey] = assetCommitment - - return nil - } - - // First, we remove any tombstones or burns that might be in the - // commitment. We needed to select them from the DB to arrive at the - // correct input Taproot Asset tree but can now remove them for good as - // they are no longer relevant and don't need to be carried over to the - // next tree. - for tapKey := range passiveCommitments { - assetCommitment := passiveCommitments[tapKey] - committedAssets := assetCommitment.Assets() - - for assetKey := range committedAssets { - committedAsset := committedAssets[assetKey] - if committedAsset.IsUnSpendable() || - committedAsset.IsBurn() { - - err := removeAsset( - assetCommitment, committedAsset, tapKey, - ) - if err != nil { - return nil, fmt.Errorf("unable to "+ - "delete asset: %w", err) - } - } - } - } - - // Remove input assets (the assets being spent) from list of assets to - // re-sign. - for _, vPkt := range vPackets { - for _, vIn := range vPkt.Inputs { - key := vIn.Asset().TapCommitmentKey() - assetCommitment, ok := passiveCommitments[key] - if !ok { - continue - } - - err := removeAsset(assetCommitment, vIn.Asset(), key) - if err != nil { - return nil, fmt.Errorf("unable to "+ - "delete asset: %w", err) - } - } - } - - return passiveCommitments, nil -} - // determinePassiveAssetAnchorOutput determines the best anchor output to attach // passive assets to. If no suitable output is found, a new anchor output is // created. @@ -1189,49 +1065,56 @@ func (f *AssetWallet) CreatePassiveAssets(ctx context.Context, } // Gather passive assets found in each input Taproot Asset commitment. - var passiveAssets []*tappsbt.VPacket + var passivePackets []*tappsbt.VPacket for prevID := range inputCommitments { tapCommitment := inputCommitments[prevID] // Each virtual input is associated with a distinct Taproot // Asset commitment. Therefore, each input may be associated // with a distinct set of passive assets. - passiveCommitments, err := removeActiveCommitments( + passiveCommitments, err := tapsend.RemovePacketsFromCommitment( tapCommitment, activePackets, ) if err != nil { return nil, err } - if len(passiveCommitments) == 0 { + + prunedAssets := tapsend.ExtractUnSpendable(passiveCommitments) + passiveCommitments, err = tapsend.RemoveAssetsFromCommitment( + passiveCommitments, prunedAssets, + ) + if err != nil { + return nil, err + } + + passiveAssets := passiveCommitments.CommittedAssets() + if len(passiveAssets) == 0 { continue } // When there are left over passive assets, we need to create // packets for them as well. - for tapKey := range passiveCommitments { - passiveCommitment := passiveCommitments[tapKey] - for _, passiveAsset := range passiveCommitment.Assets() { - inputProof, err := f.fetchInputProof( - ctx, passiveAsset, prevID.OutPoint, - ) - if err != nil { - return nil, fmt.Errorf("error "+ - "fetching input proof: %w", err) - } - - passiveAssets = append( - passiveAssets, passiveAssetVPacket( - f.cfg.ChainParams, - passiveAsset, prevID.OutPoint, - anchorOutIdx, inputProof, - anchorOutDesc, - ), - ) + for _, passiveAsset := range passiveAssets { + inputProof, err := f.fetchInputProof( + ctx, passiveAsset, prevID.OutPoint, + ) + if err != nil { + return nil, fmt.Errorf("error "+ + "fetching input proof: %w", err) } + + passivePackets = append( + passivePackets, passiveAssetVPacket( + f.cfg.ChainParams, + passiveAsset, prevID.OutPoint, + anchorOutIdx, inputProof, + anchorOutDesc, + ), + ) } } - return passiveAssets, nil + return passivePackets, nil } // SignPassiveAssets signs the given passive asset packets. diff --git a/tapsend/send.go b/tapsend/send.go index b9fb28093..10adab092 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -23,6 +23,7 @@ import ( "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/tapscript" "github.com/lightningnetwork/lnd/input" @@ -1355,6 +1356,58 @@ func AssertInputAnchorsEqual(packets []*tappsbt.VPacket) error { return nil } +// ExtractUnSpendable extracts all tombstones and burns from the active input +// commitment. +func ExtractUnSpendable(c *commitment.TapCommitment) []*asset.Asset { + return fn.Filter(c.CommittedAssets(), func(a *asset.Asset) bool { + return a.IsUnSpendable() || a.IsBurn() + }) +} + +// RemoveAssetsFromCommitment removes all assets from the given commitment and +// only returns a tree of the remaining commitments. +func RemoveAssetsFromCommitment(c *commitment.TapCommitment, + assets []*asset.Asset) (*commitment.TapCommitment, error) { + + // Gather remaining assets ot the commitment. This creates a copy of + // the commitment map, so we can remove things freely. + remainingCommitments, err := c.Copy() + if err != nil { + return nil, fmt.Errorf("unable to copy commitment: %w", err) + } + + // Remove input assets (the assets being spent) from list of assets to + // re-sign. + for _, a := range assets { + assetCommitment, ok := remainingCommitments.Commitment(a) + if !ok { + continue + } + + err = assetCommitment.Delete(a) + if err != nil { + return nil, fmt.Errorf("unable to delete asset: %w", + err) + } + } + + return remainingCommitments, nil +} + +// RemovePacketsFromCommitment removes all assets within the virtual +// transactions from the given commitment. +func RemovePacketsFromCommitment(c *commitment.TapCommitment, + packets []*tappsbt.VPacket) (*commitment.TapCommitment, error) { + + var activeAssets []*asset.Asset + fn.ForEach(packets, func(vPkt *tappsbt.VPacket) { + for _, vIn := range vPkt.Inputs { + activeAssets = append(activeAssets, vIn.Asset()) + } + }) + return RemoveAssetsFromCommitment(c, activeAssets) +} + // LogCommitment logs the given Taproot Asset commitment to the log as a trace // message. This is a no-op if the log level is not set to trace. func LogCommitment(prefix string, idx int, From d78ba83624fbc597c4d138c6525f02d82a284ad0 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:01 +0100 Subject: [PATCH 12/20] tapsend+tapfreighter: refactor and unify packet creation With this commit we simplify, unify and clean up the virtual packet creation. Now we can use the same logic for both active and passive packets. --- tapfreighter/wallet.go | 223 +++++++++++++++++++++++------------------ tapsend/send.go | 24 ++--- 2 files changed, 135 insertions(+), 112 deletions(-) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index af771a5a0..1c9eda7f5 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -257,43 +257,60 @@ func (f *AssetWallet) FundAddressSend(ctx context.Context, return fundedVPkt, nil } -// passiveAssetVPacket creates a virtual packet for the given passive asset. -func passiveAssetVPacket(params *address.ChainParams, passiveAsset *asset.Asset, - anchorPoint wire.OutPoint, anchorOutputIndex uint32, - inputProof *proof.Proof, - internalKey *keychain.KeyDescriptor) *tappsbt.VPacket { +// createPassivePacket creates a virtual packet for the given passive asset. +func createPassivePacket(params *address.ChainParams, passiveAsset *asset.Asset, + activePackets []*tappsbt.VPacket, anchorOutputIndex uint32, + anchorOutputInternalKey keychain.KeyDescriptor, prevOut wire.OutPoint, + inputProof *proof.Proof) (*tappsbt.VPacket, error) { // Specify virtual input. inputAsset := passiveAsset.Copy() - inputPrevId := asset.PrevID{ - OutPoint: anchorPoint, - ID: inputAsset.ID(), - ScriptKey: asset.ToSerialized( - inputAsset.ScriptKey.PubKey, - ), - } - - inputAnchorIdx := inputProof.InclusionProof.OutputIndex - anchorOut := inputProof.AnchorTx.TxOut[inputAnchorIdx] vInput := tappsbt.VInput{ - PrevID: inputPrevId, - Anchor: tappsbt.Anchor{ - Value: btcutil.Amount(anchorOut.Value), - PkScript: anchorOut.PkScript, - }, Proof: inputProof, + PInput: psbt.PInput{ + SighashType: txscript.SigHashDefault, + }, + } + + // Passive assets by definition are in the same anchor input as some of + // the active assets. So to avoid needing to reconstruct the anchor here + // again, we just copy the anchor of an active packet. + for _, activePacket := range activePackets { + for idx := range activePacket.Inputs { + if activePacket.Inputs[idx].PrevID.OutPoint == prevOut { + vInput.Anchor = activePacket.Inputs[idx].Anchor + + vInput.PrevID = asset.PrevID{ + OutPoint: prevOut, + ID: inputAsset.ID(), + ScriptKey: asset.ToSerialized( + inputAsset.ScriptKey.PubKey, + ), + } + + break + } + } + } + + // If we didn't find the anchor in an active packet, something + // definitely went wrong. + var emptyPrevID asset.PrevID + if vInput.PrevID == emptyPrevID { + return nil, fmt.Errorf("unable to find anchor for passive "+ + "asset %v", passiveAsset.ID()) } // Specify virtual output. outputAsset := passiveAsset.Copy() - // Clear the split commitment root, as we'll be transferring the - // whole asset. + // Clear the split commitment root, as we'll be transferring the whole + // asset. outputAsset.SplitCommitmentRoot = nil // Clear the output asset witness data. We'll be creating a new witness. outputAsset.PrevWitnesses = []asset.Witness{{ - PrevID: &inputPrevId, + PrevID: &vInput.PrevID, }} vOutput := tappsbt.VOutput{ @@ -311,7 +328,7 @@ func passiveAssetVPacket(params *address.ChainParams, passiveAsset *asset.Asset, } // Set output internal key. - vOutput.SetAnchorInternalKey(*internalKey, params.HDCoinType) + vOutput.SetAnchorInternalKey(anchorOutputInternalKey, params.HDCoinType) // Create VPacket. vPacket := &tappsbt.VPacket{ @@ -324,7 +341,7 @@ func passiveAssetVPacket(params *address.ChainParams, passiveAsset *asset.Asset, // not needed for the re-anchoring process. vPacket.SetInputAsset(0, inputAsset) - return vPacket + return vPacket, nil } // FundPacket funds a virtual transaction, selecting assets to spend in order to @@ -705,31 +722,6 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, // first place, as we won't be able to spend it anyway. But for // now we just put this check in place. assetInput := eligibleCommitments[idx] - internalKey := assetInput.InternalKey - - inBip32Derivation, inTrBip32Derivation := - tappsbt.Bip32DerivationFromKeyDesc( - internalKey, f.cfg.ChainParams.HDCoinType, - ) - - anchorPkScript, anchorMerkleRoot, _, err := - tapsend.AnchorOutputScript( - internalKey.PubKey, assetInput.TapscriptSibling, - assetInput.Commitment, - ) - if err != nil { - return nil, fmt.Errorf("cannot calculate input asset "+ - "pk script: %w", err) - } - - // Add some trace logging for easier debugging of what we expect - // to be in the commitment we spend (we did the same when - // creating the output, so differences should be apparent when - // debugging). - tapsend.LogCommitment( - "Input", idx, assetInput.Commitment, internalKey.PubKey, - anchorPkScript, anchorMerkleRoot[:], - ) // We'll also include an inclusion proof for the input asset in // the virtual transaction. With that a signer can verify that @@ -742,52 +734,90 @@ func (f *AssetWallet) setVPacketInputs(ctx context.Context, err) } - tapscriptSiblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( - assetInput.TapscriptSibling, + // Create the virtual packet input including the chain anchor + // information. + err = createAndSetInput( + vPkt, idx, f.cfg.ChainParams, assetInput, inputProof, ) if err != nil { - return nil, fmt.Errorf("cannot encode tapscript "+ - "sibling: %w", err) - } - - // At this point, we have a valid "coin" to spend in the - // commitment, so we'll add the relevant information to the - // virtual TX's input. - prevID := asset.PrevID{ - OutPoint: assetInput.AnchorPoint, - ID: assetInput.Asset.ID(), - ScriptKey: asset.ToSerialized( - assetInput.Asset.ScriptKey.PubKey, - ), - } - vPkt.Inputs[idx] = &tappsbt.VInput{ - PrevID: prevID, - Anchor: tappsbt.Anchor{ - Value: assetInput.AnchorOutputValue, - PkScript: anchorPkScript, - InternalKey: internalKey.PubKey, - MerkleRoot: anchorMerkleRoot[:], - TapscriptSibling: tapscriptSiblingBytes, - Bip32Derivation: []*psbt.Bip32Derivation{ - inBip32Derivation, - }, - TrBip32Derivation: []*psbt.TaprootBip32Derivation{ - inTrBip32Derivation, - }, - }, - Proof: inputProof, - PInput: psbt.PInput{ - SighashType: txscript.SigHashDefault, - }, + return nil, fmt.Errorf("unable to create and set "+ + "input: %w", err) } - vPkt.SetInputAsset(idx, assetInput.Asset) + prevID := vPkt.Inputs[idx].PrevID inputCommitments[prevID] = assetInput.Commitment } return inputCommitments, nil } +// createAndSetInput creates a virtual packet input for the given asset input +// and sets it on the given virtual packet. +func createAndSetInput(vPkt *tappsbt.VPacket, idx int, + params *address.ChainParams, assetInput *AnchoredCommitment, + inputProof *proof.Proof) error { + + internalKey := assetInput.InternalKey + derivation, trDerivation := tappsbt.Bip32DerivationFromKeyDesc( + internalKey, params.HDCoinType, + ) + + anchorPkScript, anchorMerkleRoot, _, err := tapsend.AnchorOutputScript( + internalKey.PubKey, assetInput.TapscriptSibling, + assetInput.Commitment, + ) + if err != nil { + return fmt.Errorf("cannot calculate input asset pk script: %w", + err) + } + + // Add some trace logging for easier debugging of what we expect to be + // in the commitment we spend (we did the same when creating the output, + // so differences should be apparent when debugging). + tapsend.LogCommitment( + "Input", idx, assetInput.Commitment, internalKey.PubKey, + anchorPkScript, anchorMerkleRoot[:], + ) + + tapscriptSiblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( + assetInput.TapscriptSibling, + ) + if err != nil { + return fmt.Errorf("cannot encode tapscript sibling: %w", err) + } + + // At this point, we have a valid "coin" to spend in the commitment, so + // we'll add the relevant information to the virtual TX's input. + prevID := asset.PrevID{ + OutPoint: assetInput.AnchorPoint, + ID: assetInput.Asset.ID(), + ScriptKey: asset.ToSerialized( + assetInput.Asset.ScriptKey.PubKey, + ), + } + vPkt.Inputs[idx] = &tappsbt.VInput{ + PrevID: prevID, + Anchor: tappsbt.Anchor{ + Value: assetInput.AnchorOutputValue, + PkScript: anchorPkScript, + InternalKey: internalKey.PubKey, + MerkleRoot: anchorMerkleRoot[:], + TapscriptSibling: tapscriptSiblingBytes, + Bip32Derivation: []*psbt.Bip32Derivation{derivation}, + TrBip32Derivation: []*psbt.TaprootBip32Derivation{ + trDerivation, + }, + }, + Proof: inputProof, + PInput: psbt.PInput{ + SighashType: txscript.SigHashDefault, + }, + } + vPkt.SetInputAsset(idx, assetInput.Asset) + + return nil +} + // fetchInputProof fetches the proof for the given asset input from the archive. func (f *AssetWallet) fetchInputProof(ctx context.Context, inputAsset *asset.Asset, anchorPoint wire.OutPoint) (*proof.Proof, @@ -1099,18 +1129,21 @@ func (f *AssetWallet) CreatePassiveAssets(ctx context.Context, ctx, passiveAsset, prevID.OutPoint, ) if err != nil { - return nil, fmt.Errorf("error "+ - "fetching input proof: %w", err) + return nil, fmt.Errorf("error fetching input "+ + "proof: %w", err) } - passivePackets = append( - passivePackets, passiveAssetVPacket( - f.cfg.ChainParams, - passiveAsset, prevID.OutPoint, - anchorOutIdx, inputProof, - anchorOutDesc, - ), + passivePacket, err := createPassivePacket( + f.cfg.ChainParams, passiveAsset, activePackets, + anchorOutIdx, *anchorOutDesc, prevID.OutPoint, + inputProof, ) + if err != nil { + return nil, fmt.Errorf("unable to create "+ + "passive packet: %w", err) + } + + passivePackets = append(passivePackets, passivePacket) } } diff --git a/tapsend/send.go b/tapsend/send.go index 10adab092..8cbc9f695 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -921,7 +921,10 @@ func commitPacket(vPkt *tappsbt.VPacket, return fmt.Errorf("output %d is missing asset", idx) } - committedAsset := vOut.Asset + sendTapCommitment, err := commitment.FromAssets(vOut.Asset) + if err != nil { + return fmt.Errorf("error committing assets: %w", err) + } // Because the receiver of this output might be receiving // through an address (non-interactive), we need to blank out @@ -932,23 +935,10 @@ func commitPacket(vPkt *tappsbt.VPacket, // send. We do the same even for interactive sends to not need // to distinguish between the two cases in the proof file // itself. - if vOut.Type == tappsbt.TypeSimple { - committedAsset = committedAsset.Copy() - committedAsset.PrevWitnesses[0].SplitCommitment = nil - } - - // Create the two levels of commitments for the output. - sendCommitment, err := commitment.NewAssetCommitment( - committedAsset, - ) + sendTapCommitment, err = trimSplitWitnesses(sendTapCommitment) if err != nil { - return err - } - sendTapCommitment, err := commitment.NewTapCommitment( - sendCommitment, - ) - if err != nil { - return err + return fmt.Errorf("error trimming split witnesses: %w", + err) } // Merge the finished TAP level commitment with the existing From f8946c45b5f06a68f5662949158b34cee80a36b8 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:02 +0100 Subject: [PATCH 13/20] multi: add CommitVirtualPsbts RPC --- go.mod | 2 +- perms/perms.go | 4 + rpcserver.go | 522 ++++++++++- taprpc/assetwalletrpc/assetwallet.pb.go | 827 +++++++++++++----- taprpc/assetwalletrpc/assetwallet.pb.gw.go | 81 ++ taprpc/assetwalletrpc/assetwallet.pb.json.go | 25 + taprpc/assetwalletrpc/assetwallet.proto | 111 +++ .../assetwalletrpc/assetwallet.swagger.json | 116 +++ taprpc/assetwalletrpc/assetwallet.yaml | 4 + taprpc/assetwalletrpc/assetwallet_grpc.pb.go | 44 + tapsend/send.go | 12 +- tapsend/send_test.go | 2 +- 12 files changed, 1481 insertions(+), 269 deletions(-) diff --git a/go.mod b/go.mod index c8f510de6..0483047b6 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14 github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 + github.com/btcsuite/btcwallet/wtxmgr v1.5.1 github.com/caddyserver/certmagic v0.17.2 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 @@ -62,7 +63,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 // indirect github.com/btcsuite/btcwallet/walletdb v1.4.1 // indirect - github.com/btcsuite/btcwallet/wtxmgr v1.5.1 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect diff --git a/perms/perms.go b/perms/perms.go index 356f2dfd2..79b193934 100644 --- a/perms/perms.go +++ b/perms/perms.go @@ -100,6 +100,10 @@ var ( Entity: "assets", Action: "write", }}, + "/assetwalletrpc.AssetWallet/CommitVirtualPsbts": {{ + Entity: "assets", + Action: "write", + }}, "/assetwalletrpc.AssetWallet/NextInternalKey": {{ Entity: "assets", Action: "write", diff --git a/rpcserver.go b/rpcserver.go index 864f7cb37..b01afcec0 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -21,8 +21,10 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/wtxmgr" "github.com/davecgh/go-spew/spew" proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/neutrino/cache/lru" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" @@ -48,6 +50,8 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/verrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" @@ -64,6 +68,21 @@ var ( // ServerMaxMsgReceiveSize is the largest message our server will // receive. ServerMaxMsgReceiveSize = grpc.MaxRecvMsgSize(lnrpc.MaxGrpcMsgSize) + + // P2TRChangeType is the type of change address that should be used for + // funding PSBTs, as we'll always want to use P2TR change addresses. + P2TRChangeType = walletrpc.ChangeAddressType_CHANGE_ADDRESS_TYPE_P2TR + + // fundPsbtCoinSelectVersion is the version of lnd that enabled better + // coin selection support in the FundPsbt RPC call. + fundPsbtCoinSelectVersion = &verrpc.Version{ + AppMajor: 0, + AppMinor: 17, + AppPatch: 99, + BuildTags: []string{ + "signrpc", "walletrpc", "chainrpc", "invoicesrpc", + }, + } ) const ( @@ -1927,6 +1946,405 @@ func (r *rpcServer) AnchorVirtualPsbts(ctx context.Context, }, nil } +// CommitVirtualPsbts creates the output commitments and proofs for the given +// virtual transactions by committing them to the BTC level anchor transaction. +// In addition, the BTC level anchor transaction is funded and prepared up to +// the point where it is ready to be signed. +func (r *rpcServer) CommitVirtualPsbts(ctx context.Context, + req *wrpc.CommitVirtualPsbtsRequest) (*wrpc.CommitVirtualPsbtsResponse, + error) { + + // For this call we require `lnd` to be at least v0.17.99-beta (which + // will become v0.18.0-beta eventually) as we need the new coin + // selection mode in the FundPsbt call. + fundPsbtCoinSelectNotSupportedErr := fmt.Errorf("connected lnd "+ + "version %v does not support advanced coin selection in the "+ + "FundPsbt RPC, need at least v%d.%d.%d for this call", + r.cfg.Lnd.Version.Version, fundPsbtCoinSelectVersion.AppMajor, + fundPsbtCoinSelectVersion.AppMinor, + fundPsbtCoinSelectVersion.AppPatch) + verErr := lndclient.AssertVersionCompatible( + r.cfg.Lnd.Version, fundPsbtCoinSelectVersion, + ) + if verErr != nil { + return nil, fundPsbtCoinSelectNotSupportedErr + } + + if len(req.VirtualPsbts) == 0 { + return nil, fmt.Errorf("no virtual PSBTs specified") + } + + pkt, err := psbt.NewFromRawBytes(bytes.NewReader(req.AnchorPsbt), false) + if err != nil { + return nil, fmt.Errorf("error decoding packet: %w", err) + } + + activePackets, err := decodeVirtualPackets(req.VirtualPsbts) + if err != nil { + return nil, fmt.Errorf("error decoding active packets: %w", err) + } + + passivePackets, err := decodeVirtualPackets(req.PassiveAssetPsbts) + if err != nil { + return nil, fmt.Errorf("error decoding passive packets: %w", + err) + } + + // Make sure the assets given fully satisfy the input commitments. + allPackets := append([]*tappsbt.VPacket{}, activePackets...) + allPackets = append(allPackets, passivePackets...) + err = r.validateInputAssets(ctx, pkt, allPackets) + if err != nil { + return nil, fmt.Errorf("error validating input assets: %w", err) + } + + // We're ready to attempt to fund the transaction now. For that we first + // need to re-serialize our packet. + packetBytes, err := serialize(pkt) + if err != nil { + return nil, fmt.Errorf("error serializing packet: %w", err) + } + + // The change output and fee parameters of this RPC are identical to the + // walletrpc.FundPsbt, so we just map them 1:1 and let lnd do the + // validation. + coinSelect := &walletrpc.PsbtCoinSelect{ + Psbt: packetBytes, + } + fundRequest := &walletrpc.FundPsbtRequest{ + Template: &walletrpc.FundPsbtRequest_CoinSelect{ + CoinSelect: coinSelect, + }, + MinConfs: 1, + ChangeType: P2TRChangeType, + } + + // Unfortunately we can't use the same RPC types, so we have to do a + // 1:1 mapping to the walletrpc types for the anchor change output and + // fee "oneof" fields. + type existingIndex = walletrpc.PsbtCoinSelect_ExistingOutputIndex + switch change := req.AnchorChangeOutput.(type) { + case *wrpc.CommitVirtualPsbtsRequest_ExistingOutputIndex: + coinSelect.ChangeOutput = &existingIndex{ + ExistingOutputIndex: change.ExistingOutputIndex, + } + + case *wrpc.CommitVirtualPsbtsRequest_Add: + coinSelect.ChangeOutput = &walletrpc.PsbtCoinSelect_Add{ + Add: change.Add, + } + + default: + return nil, fmt.Errorf("unknown change output type") + } + + switch fee := req.Fees.(type) { + case *wrpc.CommitVirtualPsbtsRequest_TargetConf: + fundRequest.Fees = &walletrpc.FundPsbtRequest_TargetConf{ + TargetConf: fee.TargetConf, + } + + case *wrpc.CommitVirtualPsbtsRequest_SatPerVbyte: + fundRequest.Fees = &walletrpc.FundPsbtRequest_SatPerVbyte{ + SatPerVbyte: fee.SatPerVbyte, + } + + default: + return nil, fmt.Errorf("unknown fee type") + } + + lndWallet := r.cfg.Lnd.WalletKit + fundedPacket, changeIndex, lockedUTXO, err := lndWallet.FundPsbt( + ctx, fundRequest, + ) + if err != nil { + return nil, fmt.Errorf("error funding packet: %w", err) + } + + // Validate the actual fee rate of the transaction. + txFees, err := fundedPacket.GetTxFee() + if err != nil { + return nil, fmt.Errorf("error calculating transaction fees: %w", + err) + } + + lockedOutpoints := fn.Map( + lockedUTXO, func(utxo *walletrpc.UtxoLease) wire.OutPoint { + var hash chainhash.Hash + copy(hash[:], utxo.Outpoint.TxidBytes) + return wire.OutPoint{ + Hash: hash, + Index: utxo.Outpoint.OutputIndex, + } + }, + ) + + // From now on, if we error out, we need to make sure we unlock the + // UTXOs that lnd just locked for us. + success := false + defer func() { + if !success { + for idx, utxo := range lockedUTXO { + var lockID wtxmgr.LockID + copy(lockID[:], utxo.Id) + + op := lockedOutpoints[idx] + err := lndWallet.ReleaseOutput(ctx, lockID, op) + if err != nil { + rpcsLog.Errorf("Error unlocking lnd "+ + "UTXO %v: %v", op, err) + } + } + } + }() + + // We can now update the anchor outputs as we have the final + // commitments. + outputCommitments, err := tapsend.CreateOutputCommitments(allPackets) + if err != nil { + return nil, fmt.Errorf("unable to create new output "+ + "commitments: %w", err) + } + + for _, vPkt := range allPackets { + err = tapsend.UpdateTaprootOutputKeys( + fundedPacket, vPkt, outputCommitments, + ) + if err != nil { + return nil, fmt.Errorf("error updating taproot output "+ + "keys: %w", err) + } + } + + // We're done creating the output commitments, we can now create the + // transition proof suffixes. + fundingPacket := &tapsend.AnchorTransaction{ + FundedPsbt: &tapsend.FundedPsbt{ + Pkt: fundedPacket, + ChangeOutputIndex: changeIndex, + ChainFees: int64(txFees), + LockedUTXOs: lockedOutpoints, + }, + FinalTx: fundedPacket.UnsignedTx, + ChainFees: int64(txFees), + } + for idx := range allPackets { + vPkt := allPackets[idx] + + for vOutIdx := range vPkt.Outputs { + proofSuffix, err := tapsend.CreateProofSuffix( + fundingPacket, vPkt, outputCommitments, + vOutIdx, allPackets, + ) + if err != nil { + return nil, fmt.Errorf("unable to create "+ + "proof suffix for output %d of vPSBT "+ + "%d: %w", vOutIdx, idx, err) + } + + vPkt.Outputs[vOutIdx].ProofSuffix = proofSuffix + } + } + + // We can now prepare the full answer, beginning with the serialized + // final packet. + response := &wrpc.CommitVirtualPsbtsResponse{ + ChangeOutputIndex: changeIndex, + } + + response.AnchorPsbt, err = serialize(fundedPacket) + if err != nil { + return nil, fmt.Errorf("error serializing packet: %w", err) + } + + // Serialize the final active and passive virtual packets. + response.VirtualPsbts, err = encodeVirtualPackets(activePackets) + if err != nil { + return nil, fmt.Errorf("error encoding active packets: %w", err) + } + response.PassiveAssetPsbts, err = encodeVirtualPackets(passivePackets) + if err != nil { + return nil, fmt.Errorf("error encoding passive packets: %w", + err) + } + + // And finally, we need to also return the locked UTXOs. We just return + // the outpoint, as any additional information can be fetched from the + // lnd wallet directly (we don't want to create pass-through RPCs for + // all those methods). + response.LndLockedUtxos = make([]*taprpc.OutPoint, len(lockedOutpoints)) + for idx := range lockedOutpoints { + response.LndLockedUtxos[idx] = &taprpc.OutPoint{ + Txid: lockedOutpoints[idx].Hash[:], + OutputIndex: lockedOutpoints[idx].Index, + } + } + + // We were successful, let's cancel the UTXO release in the defer. + success = true + + return response, nil +} + +// validateInputAssets makes sure that the input assets are correct and their +// combined commitments match the inputs of the BTC level anchor transaction. +func (r *rpcServer) validateInputAssets(ctx context.Context, + btcPkt *psbt.Packet, vPackets []*tappsbt.VPacket) error { + + // Make sure we decorate all asset inputs with the correct internal key + // derivation path (if it's indeed a key this daemon owns). + for idx := range btcPkt.Inputs { + pIn := &btcPkt.Inputs[idx] + + // We only care about asset inputs which always specify a + // Taproot merkle root. + if len(pIn.TaprootMerkleRoot) == 0 { + continue + } + + // We can't query the internal key if there is none specified. + if len(pIn.TaprootInternalKey) != schnorr.PubKeyBytesLen { + continue + } + + // If we already have the derivation info, we can skip this + // input. + if len(pIn.TaprootBip32Derivation) > 0 { + continue + } + + // Let's query our node for the internal key information now. + internalKey, keyLocator, err := r.querySchnorrInternalKey( + ctx, pIn.TaprootInternalKey, + ) + + switch { + case errors.Is(err, address.ErrInternalKeyNotFound): + // If the internal key is not known, we can't add the + // derivation info. Most likely this asset input is not + // owned by this daemon but another party. + continue + + case err != nil: + return fmt.Errorf("error querying internal key: "+ + "%w", err) + } + + keyDesc := keychain.KeyDescriptor{ + PubKey: internalKey, + KeyLocator: keyLocator, + } + derivation, trDerivation := tappsbt.Bip32DerivationFromKeyDesc( + keyDesc, r.cfg.ChainParams.HDCoinType, + ) + pIn.Bip32Derivation = []*psbt.Bip32Derivation{derivation} + pIn.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{ + trDerivation, + } + } + + // We also want to make sure we actually have the assets that are being + // spent in our database. We fetch the input commitments of all packets + // to asset that. And while we're doing that, we also extract all the + // pruned assets that are not re-created in the outputs, which we need + // for the final validation. We de-duplicate the pruned assets in a + // temporary map keyed by input outpoint and the asset commitment key. + purgedAssetsDeDup := make(map[wire.OutPoint]map[[32]byte]*asset.Asset) + for _, vPkt := range vPackets { + for _, vIn := range vPkt.Inputs { + inputAsset := vIn.Asset() + outpoint := vIn.PrevID.OutPoint + + input, err := r.cfg.AssetStore.FetchCommitment( + ctx, inputAsset.ID(), outpoint, + inputAsset.GroupKey, &inputAsset.ScriptKey, + true, + ) + if err != nil { + // If we can't fetch the input commitment, it + // means this input asset isn't ours. We cannot + // find out if there were any purged assets in + // the commitment, so we just rely on all assets + // being present. If some purged assets are + // missing, then the anchor input equality check + // further down will fail. + rpcsLog.Warnf("Could not fetch input "+ + "commitment for outpoint %v: %v", + outpoint, err) + + continue + } + + assetsToPurge := tapsend.ExtractUnSpendable( + input.Commitment, + ) + for _, a := range assetsToPurge { + key := a.AssetCommitmentKey() + if purgedAssetsDeDup[outpoint] == nil { + purgedAssetsDeDup[outpoint] = make( + map[[32]byte]*asset.Asset, + ) + } + + purgedAssetsDeDup[outpoint][key] = a + } + } + } + + // With the assets de-duplicated by their asset commitment key, we can + // now collect them grouped by input outpoint. + purgedAssets := make(map[wire.OutPoint][]*asset.Asset) + for outpoint, assets := range purgedAssetsDeDup { + for key := range assets { + purgedAssets[outpoint] = append( + purgedAssets[outpoint], assets[key], + ) + } + } + + // At this point all the virtual packet inputs and outputs should fully + // match the BTC level anchor transaction. Version 0 assets should also + // be signed now. + if err := tapsend.AssertInputAnchorsEqual(vPackets); err != nil { + return fmt.Errorf("input anchors don't match: %w", err) + } + if err := tapsend.AssertOutputAnchorsEqual(vPackets); err != nil { + return fmt.Errorf("output anchors don't match: %w", err) + } + err := tapsend.ValidateAnchorInputs(btcPkt, vPackets, purgedAssets) + if err != nil { + return fmt.Errorf("error validating anchor inputs: %w", err) + } + + // Now that we know the packet inputs match the anchored assets, we can + // also check that we don't inflate or deflate the asset supply with the + // active and passive assets. We explicitly don't look at the pruned + // assets. We have already made sure that the total sum of all inputs + // that was committed to in the input anchor is accounted for with the + // active, passive and pruned assets. Now we just need to make sure the + // active and passive transfers don't create or destroy assets. + inputBalances := make(map[asset.ID]uint64) + outputBalances := make(map[asset.ID]uint64) + for _, vPkt := range vPackets { + for _, vIn := range vPkt.Inputs { + inputBalances[vIn.Asset().ID()] += vIn.Asset().Amount + } + for _, vOut := range vPkt.Outputs { + outputBalances[vOut.Asset.ID()] += vOut.Asset.Amount + } + } + for assetID, inputBalance := range inputBalances { + outputBalance := outputBalances[assetID] + if inputBalance != outputBalance { + return fmt.Errorf("input and output balances don't "+ + "match for asset %x: %d != %d", assetID[:], + inputBalance, outputBalance) + } + } + + return nil +} + // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can @@ -2014,34 +2432,11 @@ func (r *rpcServer) QueryInternalKey(ctx context.Context, } case len(req.InternalKey) == 32: - internalKey, err = schnorr.ParsePubKey(req.InternalKey) - if err != nil { - return nil, fmt.Errorf("error parsing internal key: %w", - err) - } - - keyLocator, err = r.cfg.AssetWallet.FetchInternalKeyLocator( - ctx, internalKey, + internalKey, keyLocator, err = r.querySchnorrInternalKey( + ctx, req.InternalKey, ) - - switch { - // If the key can't be found with the even parity, we'll try - // the odd parity. - case errors.Is(err, address.ErrInternalKeyNotFound): - internalKey = tapscript.FlipParity(internalKey) - - keyLocator, err = r.cfg.AssetWallet.FetchInternalKeyLocator( - ctx, internalKey, - ) - if err != nil { - return nil, fmt.Errorf("error fetching "+ - "internal key: %w", err) - } - - // For any other error from above, we'll return it to the user. - case err != nil: - return nil, fmt.Errorf("error fetching internal key: "+ - "%w", err) + if err != nil { + return nil, err } default: @@ -2056,6 +2451,47 @@ func (r *rpcServer) QueryInternalKey(ctx context.Context, }, nil } +// querySchnorrInternalKey returns the key descriptor for the given internal +// key. This is a special method for the case where the key is a Schnorr key, +// and we need to try both parities. +func (r *rpcServer) querySchnorrInternalKey(ctx context.Context, + schnorrKey []byte) (*btcec.PublicKey, keychain.KeyLocator, error) { + + var keyLocator keychain.KeyLocator + + internalKey, err := schnorr.ParsePubKey(schnorrKey) + if err != nil { + return nil, keyLocator, fmt.Errorf("error parsing internal "+ + "key: %w", err) + } + + keyLocator, err = r.cfg.AssetWallet.FetchInternalKeyLocator( + ctx, internalKey, + ) + + switch { + // If the key can't be found with the even parity, we'll try + // the odd parity. + case errors.Is(err, address.ErrInternalKeyNotFound): + internalKey = tapscript.FlipParity(internalKey) + + keyLocator, err = r.cfg.AssetWallet.FetchInternalKeyLocator( + ctx, internalKey, + ) + if err != nil { + return nil, keyLocator, fmt.Errorf("error fetching "+ + "internal key: %w", err) + } + + // For any other error from above, we'll return it to the user. + case err != nil: + return nil, keyLocator, fmt.Errorf("error fetching internal "+ + "key: %w", err) + } + + return internalKey, keyLocator, nil +} + // QueryScriptKey returns the full script key descriptor for the given tweaked // script key. func (r *rpcServer) QueryScriptKey(ctx context.Context, @@ -5142,3 +5578,35 @@ func serialize(s interface{ Serialize(io.Writer) error }) ([]byte, error) { return b.Bytes(), nil } + +// decodeVirtualPackets decodes a slice of raw virtual packet bytes into a slice +// of virtual packets. +func decodeVirtualPackets(rawPackets [][]byte) ([]*tappsbt.VPacket, error) { + packets := make([]*tappsbt.VPacket, len(rawPackets)) + for idx := range rawPackets { + var err error + packets[idx], err = tappsbt.Decode(rawPackets[idx]) + if err != nil { + return nil, fmt.Errorf("error decoding virtual packet "+ + "at index %d: %w", idx, err) + } + } + + return packets, nil +} + +// encodeVirtualPackets encodes a slice of virtual packets into a slice of raw +// virtual packet bytes. +func encodeVirtualPackets(packets []*tappsbt.VPacket) ([][]byte, error) { + rawPackets := make([][]byte, len(packets)) + for idx := range packets { + var err error + rawPackets[idx], err = tappsbt.Encode(packets[idx]) + if err != nil { + return nil, fmt.Errorf("error serializing packet: %w", + err) + } + } + + return rawPackets, nil +} diff --git a/taprpc/assetwalletrpc/assetwallet.pb.go b/taprpc/assetwalletrpc/assetwallet.pb.go index 3c7b03e18..fa4dcc266 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.go @@ -469,6 +469,279 @@ func (x *AnchorVirtualPsbtsRequest) GetVirtualPsbts() [][]byte { return nil } +type CommitVirtualPsbtsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of virtual transactions that should be mapped to the given BTC + // level anchor transaction template. The virtual transactions are expected to + // be signed (or use ASSET_VERSION_V1 with segregated witness to allow for + // signing after committing) and ready to be committed to the anchor + // transaction. + VirtualPsbts [][]byte `protobuf:"bytes,1,rep,name=virtual_psbts,json=virtualPsbts,proto3" json:"virtual_psbts,omitempty"` + // The list of passive virtual transactions that are anchored in the same BTC + // level anchor transaction inputs as the "active" assets above. These can be + // obtained by calling FundVirtualPsbt and using the passive assets returned. + // The virtual transactions are expected to be signed (or use ASSET_VERSION_V1 + // with segregated witness to allow for signing after committing) and ready to + // be committed to the anchor transaction. + // The main difference to the "active" assets above is that the passive assets + // will not get their own entry in the transfer table of the database, since + // they are just carried along and not directly affected by the direct user + // action. + PassiveAssetPsbts [][]byte `protobuf:"bytes,2,rep,name=passive_asset_psbts,json=passiveAssetPsbts,proto3" json:"passive_asset_psbts,omitempty"` + // The template of the BTC level anchor transaction that the virtual + // transactions should be mapped to. The template is expected to already + // contain all asset related inputs and outputs corresponding to the virtual + // transactions given above. This can be achieved by using + // tapfreighter.PrepareAnchoringTemplate for example. + AnchorPsbt []byte `protobuf:"bytes,3,opt,name=anchor_psbt,json=anchorPsbt,proto3" json:"anchor_psbt,omitempty"` + // Types that are assignable to AnchorChangeOutput: + // + // *CommitVirtualPsbtsRequest_ExistingOutputIndex + // *CommitVirtualPsbtsRequest_Add + AnchorChangeOutput isCommitVirtualPsbtsRequest_AnchorChangeOutput `protobuf_oneof:"anchor_change_output"` + // Types that are assignable to Fees: + // + // *CommitVirtualPsbtsRequest_TargetConf + // *CommitVirtualPsbtsRequest_SatPerVbyte + Fees isCommitVirtualPsbtsRequest_Fees `protobuf_oneof:"fees"` +} + +func (x *CommitVirtualPsbtsRequest) Reset() { + *x = CommitVirtualPsbtsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CommitVirtualPsbtsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitVirtualPsbtsRequest) ProtoMessage() {} + +func (x *CommitVirtualPsbtsRequest) ProtoReflect() protoreflect.Message { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[7] + 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 CommitVirtualPsbtsRequest.ProtoReflect.Descriptor instead. +func (*CommitVirtualPsbtsRequest) Descriptor() ([]byte, []int) { + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{7} +} + +func (x *CommitVirtualPsbtsRequest) GetVirtualPsbts() [][]byte { + if x != nil { + return x.VirtualPsbts + } + return nil +} + +func (x *CommitVirtualPsbtsRequest) GetPassiveAssetPsbts() [][]byte { + if x != nil { + return x.PassiveAssetPsbts + } + return nil +} + +func (x *CommitVirtualPsbtsRequest) GetAnchorPsbt() []byte { + if x != nil { + return x.AnchorPsbt + } + return nil +} + +func (m *CommitVirtualPsbtsRequest) GetAnchorChangeOutput() isCommitVirtualPsbtsRequest_AnchorChangeOutput { + if m != nil { + return m.AnchorChangeOutput + } + return nil +} + +func (x *CommitVirtualPsbtsRequest) GetExistingOutputIndex() int32 { + if x, ok := x.GetAnchorChangeOutput().(*CommitVirtualPsbtsRequest_ExistingOutputIndex); ok { + return x.ExistingOutputIndex + } + return 0 +} + +func (x *CommitVirtualPsbtsRequest) GetAdd() bool { + if x, ok := x.GetAnchorChangeOutput().(*CommitVirtualPsbtsRequest_Add); ok { + return x.Add + } + return false +} + +func (m *CommitVirtualPsbtsRequest) GetFees() isCommitVirtualPsbtsRequest_Fees { + if m != nil { + return m.Fees + } + return nil +} + +func (x *CommitVirtualPsbtsRequest) GetTargetConf() uint32 { + if x, ok := x.GetFees().(*CommitVirtualPsbtsRequest_TargetConf); ok { + return x.TargetConf + } + return 0 +} + +func (x *CommitVirtualPsbtsRequest) GetSatPerVbyte() uint64 { + if x, ok := x.GetFees().(*CommitVirtualPsbtsRequest_SatPerVbyte); ok { + return x.SatPerVbyte + } + return 0 +} + +type isCommitVirtualPsbtsRequest_AnchorChangeOutput interface { + isCommitVirtualPsbtsRequest_AnchorChangeOutput() +} + +type CommitVirtualPsbtsRequest_ExistingOutputIndex struct { + // Use the existing output within the anchor PSBT with the specified + // index as the change output. Any leftover change will be added to the + // already specified amount of that output. To add a new change output to + // the PSBT, set the "add" field below instead. + ExistingOutputIndex int32 `protobuf:"varint,4,opt,name=existing_output_index,json=existingOutputIndex,proto3,oneof"` +} + +type CommitVirtualPsbtsRequest_Add struct { + // Add a new P2TR change output to the PSBT if required. + Add bool `protobuf:"varint,5,opt,name=add,proto3,oneof"` +} + +func (*CommitVirtualPsbtsRequest_ExistingOutputIndex) isCommitVirtualPsbtsRequest_AnchorChangeOutput() { +} + +func (*CommitVirtualPsbtsRequest_Add) isCommitVirtualPsbtsRequest_AnchorChangeOutput() {} + +type isCommitVirtualPsbtsRequest_Fees interface { + isCommitVirtualPsbtsRequest_Fees() +} + +type CommitVirtualPsbtsRequest_TargetConf struct { + // The target number of blocks that the transaction should be confirmed in. + TargetConf uint32 `protobuf:"varint,6,opt,name=target_conf,json=targetConf,proto3,oneof"` +} + +type CommitVirtualPsbtsRequest_SatPerVbyte struct { + // The fee rate, expressed in sat/vbyte, that should be used to fund the + // BTC level anchor transaction. + SatPerVbyte uint64 `protobuf:"varint,7,opt,name=sat_per_vbyte,json=satPerVbyte,proto3,oneof"` +} + +func (*CommitVirtualPsbtsRequest_TargetConf) isCommitVirtualPsbtsRequest_Fees() {} + +func (*CommitVirtualPsbtsRequest_SatPerVbyte) isCommitVirtualPsbtsRequest_Fees() {} + +type CommitVirtualPsbtsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The funded BTC level anchor transaction with all outputs updated to commit + // to the virtual transactions given. The transaction is ready to be signed, + // unless some of the asset inputs don't belong to this daemon, in which case + // the anchor input derivation info must be added to those inputs first. + AnchorPsbt []byte `protobuf:"bytes,1,opt,name=anchor_psbt,json=anchorPsbt,proto3" json:"anchor_psbt,omitempty"` + // The updated virtual transactions that now contain the state transition + // proofs for being committed to the BTC level anchor transaction above. If the + // assets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet + // signed, then the proofs need to be updated to include the witness before + // they become fully valid. + VirtualPsbts [][]byte `protobuf:"bytes,2,rep,name=virtual_psbts,json=virtualPsbts,proto3" json:"virtual_psbts,omitempty"` + // The updated passive virtual transactions that were committed to the same BTC + // level anchor transaction as the "active" virtual transactions given. If the + // assets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet + // signed, then the proofs need to be updated to include the witness before + // they become fully valid. + PassiveAssetPsbts [][]byte `protobuf:"bytes,4,rep,name=passive_asset_psbts,json=passiveAssetPsbts,proto3" json:"passive_asset_psbts,omitempty"` + // The index of the (added) change output or -1 if no change was left over. + ChangeOutputIndex int32 `protobuf:"varint,5,opt,name=change_output_index,json=changeOutputIndex,proto3" json:"change_output_index,omitempty"` + // The list of UTXO lock leases that were acquired for the inputs in the funded + // PSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked, + // inputs that were already present in the PSBT are not locked. + LndLockedUtxos []*taprpc.OutPoint `protobuf:"bytes,6,rep,name=lnd_locked_utxos,json=lndLockedUtxos,proto3" json:"lnd_locked_utxos,omitempty"` +} + +func (x *CommitVirtualPsbtsResponse) Reset() { + *x = CommitVirtualPsbtsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CommitVirtualPsbtsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitVirtualPsbtsResponse) ProtoMessage() {} + +func (x *CommitVirtualPsbtsResponse) ProtoReflect() protoreflect.Message { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[8] + 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 CommitVirtualPsbtsResponse.ProtoReflect.Descriptor instead. +func (*CommitVirtualPsbtsResponse) Descriptor() ([]byte, []int) { + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{8} +} + +func (x *CommitVirtualPsbtsResponse) GetAnchorPsbt() []byte { + if x != nil { + return x.AnchorPsbt + } + return nil +} + +func (x *CommitVirtualPsbtsResponse) GetVirtualPsbts() [][]byte { + if x != nil { + return x.VirtualPsbts + } + return nil +} + +func (x *CommitVirtualPsbtsResponse) GetPassiveAssetPsbts() [][]byte { + if x != nil { + return x.PassiveAssetPsbts + } + return nil +} + +func (x *CommitVirtualPsbtsResponse) GetChangeOutputIndex() int32 { + if x != nil { + return x.ChangeOutputIndex + } + return 0 +} + +func (x *CommitVirtualPsbtsResponse) GetLndLockedUtxos() []*taprpc.OutPoint { + if x != nil { + return x.LndLockedUtxos + } + return nil +} + type NextInternalKeyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -480,7 +753,7 @@ type NextInternalKeyRequest struct { func (x *NextInternalKeyRequest) Reset() { *x = NextInternalKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[7] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -493,7 +766,7 @@ func (x *NextInternalKeyRequest) String() string { func (*NextInternalKeyRequest) ProtoMessage() {} func (x *NextInternalKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[7] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -506,7 +779,7 @@ func (x *NextInternalKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NextInternalKeyRequest.ProtoReflect.Descriptor instead. func (*NextInternalKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{7} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{9} } func (x *NextInternalKeyRequest) GetKeyFamily() uint32 { @@ -527,7 +800,7 @@ type NextInternalKeyResponse struct { func (x *NextInternalKeyResponse) Reset() { *x = NextInternalKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[8] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -540,7 +813,7 @@ func (x *NextInternalKeyResponse) String() string { func (*NextInternalKeyResponse) ProtoMessage() {} func (x *NextInternalKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[8] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -553,7 +826,7 @@ func (x *NextInternalKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NextInternalKeyResponse.ProtoReflect.Descriptor instead. func (*NextInternalKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{8} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{10} } func (x *NextInternalKeyResponse) GetInternalKey() *taprpc.KeyDescriptor { @@ -574,7 +847,7 @@ type NextScriptKeyRequest struct { func (x *NextScriptKeyRequest) Reset() { *x = NextScriptKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -587,7 +860,7 @@ func (x *NextScriptKeyRequest) String() string { func (*NextScriptKeyRequest) ProtoMessage() {} func (x *NextScriptKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -600,7 +873,7 @@ func (x *NextScriptKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NextScriptKeyRequest.ProtoReflect.Descriptor instead. func (*NextScriptKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{9} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{11} } func (x *NextScriptKeyRequest) GetKeyFamily() uint32 { @@ -621,7 +894,7 @@ type NextScriptKeyResponse struct { func (x *NextScriptKeyResponse) Reset() { *x = NextScriptKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -634,7 +907,7 @@ func (x *NextScriptKeyResponse) String() string { func (*NextScriptKeyResponse) ProtoMessage() {} func (x *NextScriptKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -647,7 +920,7 @@ func (x *NextScriptKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NextScriptKeyResponse.ProtoReflect.Descriptor instead. func (*NextScriptKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{10} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{12} } func (x *NextScriptKeyResponse) GetScriptKey() *taprpc.ScriptKey { @@ -670,7 +943,7 @@ type QueryInternalKeyRequest struct { func (x *QueryInternalKeyRequest) Reset() { *x = QueryInternalKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -683,7 +956,7 @@ func (x *QueryInternalKeyRequest) String() string { func (*QueryInternalKeyRequest) ProtoMessage() {} func (x *QueryInternalKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -696,7 +969,7 @@ func (x *QueryInternalKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryInternalKeyRequest.ProtoReflect.Descriptor instead. func (*QueryInternalKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{11} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{13} } func (x *QueryInternalKeyRequest) GetInternalKey() []byte { @@ -717,7 +990,7 @@ type QueryInternalKeyResponse struct { func (x *QueryInternalKeyResponse) Reset() { *x = QueryInternalKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -730,7 +1003,7 @@ func (x *QueryInternalKeyResponse) String() string { func (*QueryInternalKeyResponse) ProtoMessage() {} func (x *QueryInternalKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -743,7 +1016,7 @@ func (x *QueryInternalKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryInternalKeyResponse.ProtoReflect.Descriptor instead. func (*QueryInternalKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{12} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{14} } func (x *QueryInternalKeyResponse) GetInternalKey() *taprpc.KeyDescriptor { @@ -767,7 +1040,7 @@ type QueryScriptKeyRequest struct { func (x *QueryScriptKeyRequest) Reset() { *x = QueryScriptKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -780,7 +1053,7 @@ func (x *QueryScriptKeyRequest) String() string { func (*QueryScriptKeyRequest) ProtoMessage() {} func (x *QueryScriptKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -793,7 +1066,7 @@ func (x *QueryScriptKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryScriptKeyRequest.ProtoReflect.Descriptor instead. func (*QueryScriptKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{13} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{15} } func (x *QueryScriptKeyRequest) GetTweakedScriptKey() []byte { @@ -814,7 +1087,7 @@ type QueryScriptKeyResponse struct { func (x *QueryScriptKeyResponse) Reset() { *x = QueryScriptKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -827,7 +1100,7 @@ func (x *QueryScriptKeyResponse) String() string { func (*QueryScriptKeyResponse) ProtoMessage() {} func (x *QueryScriptKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -840,7 +1113,7 @@ func (x *QueryScriptKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryScriptKeyResponse.ProtoReflect.Descriptor instead. func (*QueryScriptKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{14} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{16} } func (x *QueryScriptKeyResponse) GetScriptKey() *taprpc.ScriptKey { @@ -863,7 +1136,7 @@ type ProveAssetOwnershipRequest struct { func (x *ProveAssetOwnershipRequest) Reset() { *x = ProveAssetOwnershipRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -876,7 +1149,7 @@ func (x *ProveAssetOwnershipRequest) String() string { func (*ProveAssetOwnershipRequest) ProtoMessage() {} func (x *ProveAssetOwnershipRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -889,7 +1162,7 @@ func (x *ProveAssetOwnershipRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ProveAssetOwnershipRequest.ProtoReflect.Descriptor instead. func (*ProveAssetOwnershipRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{15} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{17} } func (x *ProveAssetOwnershipRequest) GetAssetId() []byte { @@ -924,7 +1197,7 @@ type ProveAssetOwnershipResponse struct { func (x *ProveAssetOwnershipResponse) Reset() { *x = ProveAssetOwnershipResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -937,7 +1210,7 @@ func (x *ProveAssetOwnershipResponse) String() string { func (*ProveAssetOwnershipResponse) ProtoMessage() {} func (x *ProveAssetOwnershipResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -950,7 +1223,7 @@ func (x *ProveAssetOwnershipResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProveAssetOwnershipResponse.ProtoReflect.Descriptor instead. func (*ProveAssetOwnershipResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{16} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{18} } func (x *ProveAssetOwnershipResponse) GetProofWithWitness() []byte { @@ -971,7 +1244,7 @@ type VerifyAssetOwnershipRequest struct { func (x *VerifyAssetOwnershipRequest) Reset() { *x = VerifyAssetOwnershipRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -984,7 +1257,7 @@ func (x *VerifyAssetOwnershipRequest) String() string { func (*VerifyAssetOwnershipRequest) ProtoMessage() {} func (x *VerifyAssetOwnershipRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -997,7 +1270,7 @@ func (x *VerifyAssetOwnershipRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyAssetOwnershipRequest.ProtoReflect.Descriptor instead. func (*VerifyAssetOwnershipRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{17} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{19} } func (x *VerifyAssetOwnershipRequest) GetProofWithWitness() []byte { @@ -1018,7 +1291,7 @@ type VerifyAssetOwnershipResponse struct { func (x *VerifyAssetOwnershipResponse) Reset() { *x = VerifyAssetOwnershipResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1031,7 +1304,7 @@ func (x *VerifyAssetOwnershipResponse) String() string { func (*VerifyAssetOwnershipResponse) ProtoMessage() {} func (x *VerifyAssetOwnershipResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1044,7 +1317,7 @@ func (x *VerifyAssetOwnershipResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyAssetOwnershipResponse.ProtoReflect.Descriptor instead. func (*VerifyAssetOwnershipResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{18} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{20} } func (x *VerifyAssetOwnershipResponse) GetValidProof() bool { @@ -1066,7 +1339,7 @@ type RemoveUTXOLeaseRequest struct { func (x *RemoveUTXOLeaseRequest) Reset() { *x = RemoveUTXOLeaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1079,7 +1352,7 @@ func (x *RemoveUTXOLeaseRequest) String() string { func (*RemoveUTXOLeaseRequest) ProtoMessage() {} func (x *RemoveUTXOLeaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1092,7 +1365,7 @@ func (x *RemoveUTXOLeaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveUTXOLeaseRequest.ProtoReflect.Descriptor instead. func (*RemoveUTXOLeaseRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{19} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{21} } func (x *RemoveUTXOLeaseRequest) GetOutpoint() *taprpc.OutPoint { @@ -1111,7 +1384,7 @@ type RemoveUTXOLeaseResponse struct { func (x *RemoveUTXOLeaseResponse) Reset() { *x = RemoveUTXOLeaseResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1124,7 +1397,7 @@ func (x *RemoveUTXOLeaseResponse) String() string { func (*RemoveUTXOLeaseResponse) ProtoMessage() {} func (x *RemoveUTXOLeaseResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1137,7 +1410,7 @@ func (x *RemoveUTXOLeaseResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveUTXOLeaseResponse.ProtoReflect.Descriptor instead. func (*RemoveUTXOLeaseResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{20} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{22} } var File_assetwalletrpc_assetwallet_proto protoreflect.FileDescriptor @@ -1197,139 +1470,182 @@ var file_assetwalletrpc_assetwallet_proto_rawDesc = []byte{ 0x62, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x22, - 0x37, 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, - 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, - 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x53, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, - 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x35, 0x0a, - 0x14, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, - 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, - 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, - 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, - 0x3c, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x54, 0x0a, - 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x4b, 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, - 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, - 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x65, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 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, 0x4b, 0x0a, - 0x1b, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, - 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, - 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x1b, 0x56, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, - 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x3f, 0x0a, 0x1c, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, - 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, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, - 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x82, 0x08, 0x0a, 0x0b, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x62, 0x0a, 0x0f, 0x46, - 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, + 0xc4, 0x02, 0x0a, 0x19, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, + 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x11, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x73, 0x62, + 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x70, 0x73, 0x62, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x50, + 0x73, 0x62, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x48, 0x00, 0x52, 0x13, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x64, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x03, 0x61, 0x64, 0x64, 0x12, 0x21, 0x0a, + 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x01, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x61, 0x74, 0x50, 0x65, + 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x42, 0x16, 0x0a, 0x14, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x06, + 0x0a, 0x04, 0x66, 0x65, 0x65, 0x73, 0x22, 0xfe, 0x01, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, + 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6e, 0x63, 0x68, + 0x6f, 0x72, 0x50, 0x73, 0x62, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x76, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, + 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x70, 0x73, 0x62, + 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x3a, 0x0a, 0x10, 0x6c, + 0x6e, 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, + 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x6c, 0x6e, 0x64, 0x4c, 0x6f, 0x63, 0x6b, + 0x65, 0x64, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x37, 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, + 0x22, 0x53, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x14, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, + 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x3c, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x54, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, + 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, + 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 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, 0x4b, 0x0a, 0x1b, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, + 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, + 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x1b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, + 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, + 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, + 0x3f, 0x0a, 0x1c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, + 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, + 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, + 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 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, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0xef, 0x08, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x12, 0x62, 0x0a, 0x0f, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, - 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x62, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, - 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, - 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, - 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 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, - 0x62, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, - 0x65, 0x79, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x65, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, - 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x61, 0x73, 0x73, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, + 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, + 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, + 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, + 0x50, 0x73, 0x62, 0x74, 0x73, 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, 0x6b, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4e, 0x65, 0x78, 0x74, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x13, 0x50, 0x72, 0x6f, - 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, - 0x12, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x14, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x12, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, - 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, - 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, - 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, - 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, + 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, + 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, + 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, + 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, + 0x0a, 0x14, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, + 0x65, 0x61, 0x73, 0x65, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, + 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1344,7 +1660,7 @@ func file_assetwalletrpc_assetwallet_proto_rawDescGZIP() []byte { return file_assetwalletrpc_assetwallet_proto_rawDescData } -var file_assetwalletrpc_assetwallet_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_assetwalletrpc_assetwallet_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_assetwalletrpc_assetwallet_proto_goTypes = []interface{}{ (*FundVirtualPsbtRequest)(nil), // 0: assetwalletrpc.FundVirtualPsbtRequest (*FundVirtualPsbtResponse)(nil), // 1: assetwalletrpc.FundVirtualPsbtResponse @@ -1353,62 +1669,67 @@ var file_assetwalletrpc_assetwallet_proto_goTypes = []interface{}{ (*SignVirtualPsbtRequest)(nil), // 4: assetwalletrpc.SignVirtualPsbtRequest (*SignVirtualPsbtResponse)(nil), // 5: assetwalletrpc.SignVirtualPsbtResponse (*AnchorVirtualPsbtsRequest)(nil), // 6: assetwalletrpc.AnchorVirtualPsbtsRequest - (*NextInternalKeyRequest)(nil), // 7: assetwalletrpc.NextInternalKeyRequest - (*NextInternalKeyResponse)(nil), // 8: assetwalletrpc.NextInternalKeyResponse - (*NextScriptKeyRequest)(nil), // 9: assetwalletrpc.NextScriptKeyRequest - (*NextScriptKeyResponse)(nil), // 10: assetwalletrpc.NextScriptKeyResponse - (*QueryInternalKeyRequest)(nil), // 11: assetwalletrpc.QueryInternalKeyRequest - (*QueryInternalKeyResponse)(nil), // 12: assetwalletrpc.QueryInternalKeyResponse - (*QueryScriptKeyRequest)(nil), // 13: assetwalletrpc.QueryScriptKeyRequest - (*QueryScriptKeyResponse)(nil), // 14: assetwalletrpc.QueryScriptKeyResponse - (*ProveAssetOwnershipRequest)(nil), // 15: assetwalletrpc.ProveAssetOwnershipRequest - (*ProveAssetOwnershipResponse)(nil), // 16: assetwalletrpc.ProveAssetOwnershipResponse - (*VerifyAssetOwnershipRequest)(nil), // 17: assetwalletrpc.VerifyAssetOwnershipRequest - (*VerifyAssetOwnershipResponse)(nil), // 18: assetwalletrpc.VerifyAssetOwnershipResponse - (*RemoveUTXOLeaseRequest)(nil), // 19: assetwalletrpc.RemoveUTXOLeaseRequest - (*RemoveUTXOLeaseResponse)(nil), // 20: assetwalletrpc.RemoveUTXOLeaseResponse - nil, // 21: assetwalletrpc.TxTemplate.RecipientsEntry - (*taprpc.OutPoint)(nil), // 22: taprpc.OutPoint - (*taprpc.KeyDescriptor)(nil), // 23: taprpc.KeyDescriptor - (*taprpc.ScriptKey)(nil), // 24: taprpc.ScriptKey - (*taprpc.SendAssetResponse)(nil), // 25: taprpc.SendAssetResponse + (*CommitVirtualPsbtsRequest)(nil), // 7: assetwalletrpc.CommitVirtualPsbtsRequest + (*CommitVirtualPsbtsResponse)(nil), // 8: assetwalletrpc.CommitVirtualPsbtsResponse + (*NextInternalKeyRequest)(nil), // 9: assetwalletrpc.NextInternalKeyRequest + (*NextInternalKeyResponse)(nil), // 10: assetwalletrpc.NextInternalKeyResponse + (*NextScriptKeyRequest)(nil), // 11: assetwalletrpc.NextScriptKeyRequest + (*NextScriptKeyResponse)(nil), // 12: assetwalletrpc.NextScriptKeyResponse + (*QueryInternalKeyRequest)(nil), // 13: assetwalletrpc.QueryInternalKeyRequest + (*QueryInternalKeyResponse)(nil), // 14: assetwalletrpc.QueryInternalKeyResponse + (*QueryScriptKeyRequest)(nil), // 15: assetwalletrpc.QueryScriptKeyRequest + (*QueryScriptKeyResponse)(nil), // 16: assetwalletrpc.QueryScriptKeyResponse + (*ProveAssetOwnershipRequest)(nil), // 17: assetwalletrpc.ProveAssetOwnershipRequest + (*ProveAssetOwnershipResponse)(nil), // 18: assetwalletrpc.ProveAssetOwnershipResponse + (*VerifyAssetOwnershipRequest)(nil), // 19: assetwalletrpc.VerifyAssetOwnershipRequest + (*VerifyAssetOwnershipResponse)(nil), // 20: assetwalletrpc.VerifyAssetOwnershipResponse + (*RemoveUTXOLeaseRequest)(nil), // 21: assetwalletrpc.RemoveUTXOLeaseRequest + (*RemoveUTXOLeaseResponse)(nil), // 22: assetwalletrpc.RemoveUTXOLeaseResponse + nil, // 23: assetwalletrpc.TxTemplate.RecipientsEntry + (*taprpc.OutPoint)(nil), // 24: taprpc.OutPoint + (*taprpc.KeyDescriptor)(nil), // 25: taprpc.KeyDescriptor + (*taprpc.ScriptKey)(nil), // 26: taprpc.ScriptKey + (*taprpc.SendAssetResponse)(nil), // 27: taprpc.SendAssetResponse } var file_assetwalletrpc_assetwallet_proto_depIdxs = []int32{ 2, // 0: assetwalletrpc.FundVirtualPsbtRequest.raw:type_name -> assetwalletrpc.TxTemplate 3, // 1: assetwalletrpc.TxTemplate.inputs:type_name -> assetwalletrpc.PrevId - 21, // 2: assetwalletrpc.TxTemplate.recipients:type_name -> assetwalletrpc.TxTemplate.RecipientsEntry - 22, // 3: assetwalletrpc.PrevId.outpoint:type_name -> taprpc.OutPoint - 23, // 4: assetwalletrpc.NextInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor - 24, // 5: assetwalletrpc.NextScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey - 23, // 6: assetwalletrpc.QueryInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor - 24, // 7: assetwalletrpc.QueryScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey - 22, // 8: assetwalletrpc.ProveAssetOwnershipRequest.outpoint:type_name -> taprpc.OutPoint - 22, // 9: assetwalletrpc.RemoveUTXOLeaseRequest.outpoint:type_name -> taprpc.OutPoint - 0, // 10: assetwalletrpc.AssetWallet.FundVirtualPsbt:input_type -> assetwalletrpc.FundVirtualPsbtRequest - 4, // 11: assetwalletrpc.AssetWallet.SignVirtualPsbt:input_type -> assetwalletrpc.SignVirtualPsbtRequest - 6, // 12: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:input_type -> assetwalletrpc.AnchorVirtualPsbtsRequest - 7, // 13: assetwalletrpc.AssetWallet.NextInternalKey:input_type -> assetwalletrpc.NextInternalKeyRequest - 9, // 14: assetwalletrpc.AssetWallet.NextScriptKey:input_type -> assetwalletrpc.NextScriptKeyRequest - 11, // 15: assetwalletrpc.AssetWallet.QueryInternalKey:input_type -> assetwalletrpc.QueryInternalKeyRequest - 13, // 16: assetwalletrpc.AssetWallet.QueryScriptKey:input_type -> assetwalletrpc.QueryScriptKeyRequest - 15, // 17: assetwalletrpc.AssetWallet.ProveAssetOwnership:input_type -> assetwalletrpc.ProveAssetOwnershipRequest - 17, // 18: assetwalletrpc.AssetWallet.VerifyAssetOwnership:input_type -> assetwalletrpc.VerifyAssetOwnershipRequest - 19, // 19: assetwalletrpc.AssetWallet.RemoveUTXOLease:input_type -> assetwalletrpc.RemoveUTXOLeaseRequest - 1, // 20: assetwalletrpc.AssetWallet.FundVirtualPsbt:output_type -> assetwalletrpc.FundVirtualPsbtResponse - 5, // 21: assetwalletrpc.AssetWallet.SignVirtualPsbt:output_type -> assetwalletrpc.SignVirtualPsbtResponse - 25, // 22: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:output_type -> taprpc.SendAssetResponse - 8, // 23: assetwalletrpc.AssetWallet.NextInternalKey:output_type -> assetwalletrpc.NextInternalKeyResponse - 10, // 24: assetwalletrpc.AssetWallet.NextScriptKey:output_type -> assetwalletrpc.NextScriptKeyResponse - 12, // 25: assetwalletrpc.AssetWallet.QueryInternalKey:output_type -> assetwalletrpc.QueryInternalKeyResponse - 14, // 26: assetwalletrpc.AssetWallet.QueryScriptKey:output_type -> assetwalletrpc.QueryScriptKeyResponse - 16, // 27: assetwalletrpc.AssetWallet.ProveAssetOwnership:output_type -> assetwalletrpc.ProveAssetOwnershipResponse - 18, // 28: assetwalletrpc.AssetWallet.VerifyAssetOwnership:output_type -> assetwalletrpc.VerifyAssetOwnershipResponse - 20, // 29: assetwalletrpc.AssetWallet.RemoveUTXOLease:output_type -> assetwalletrpc.RemoveUTXOLeaseResponse - 20, // [20:30] is the sub-list for method output_type - 10, // [10:20] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 23, // 2: assetwalletrpc.TxTemplate.recipients:type_name -> assetwalletrpc.TxTemplate.RecipientsEntry + 24, // 3: assetwalletrpc.PrevId.outpoint:type_name -> taprpc.OutPoint + 24, // 4: assetwalletrpc.CommitVirtualPsbtsResponse.lnd_locked_utxos:type_name -> taprpc.OutPoint + 25, // 5: assetwalletrpc.NextInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor + 26, // 6: assetwalletrpc.NextScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey + 25, // 7: assetwalletrpc.QueryInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor + 26, // 8: assetwalletrpc.QueryScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey + 24, // 9: assetwalletrpc.ProveAssetOwnershipRequest.outpoint:type_name -> taprpc.OutPoint + 24, // 10: assetwalletrpc.RemoveUTXOLeaseRequest.outpoint:type_name -> taprpc.OutPoint + 0, // 11: assetwalletrpc.AssetWallet.FundVirtualPsbt:input_type -> assetwalletrpc.FundVirtualPsbtRequest + 4, // 12: assetwalletrpc.AssetWallet.SignVirtualPsbt:input_type -> assetwalletrpc.SignVirtualPsbtRequest + 6, // 13: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:input_type -> assetwalletrpc.AnchorVirtualPsbtsRequest + 7, // 14: assetwalletrpc.AssetWallet.CommitVirtualPsbts:input_type -> assetwalletrpc.CommitVirtualPsbtsRequest + 9, // 15: assetwalletrpc.AssetWallet.NextInternalKey:input_type -> assetwalletrpc.NextInternalKeyRequest + 11, // 16: assetwalletrpc.AssetWallet.NextScriptKey:input_type -> assetwalletrpc.NextScriptKeyRequest + 13, // 17: assetwalletrpc.AssetWallet.QueryInternalKey:input_type -> assetwalletrpc.QueryInternalKeyRequest + 15, // 18: assetwalletrpc.AssetWallet.QueryScriptKey:input_type -> assetwalletrpc.QueryScriptKeyRequest + 17, // 19: assetwalletrpc.AssetWallet.ProveAssetOwnership:input_type -> assetwalletrpc.ProveAssetOwnershipRequest + 19, // 20: assetwalletrpc.AssetWallet.VerifyAssetOwnership:input_type -> assetwalletrpc.VerifyAssetOwnershipRequest + 21, // 21: assetwalletrpc.AssetWallet.RemoveUTXOLease:input_type -> assetwalletrpc.RemoveUTXOLeaseRequest + 1, // 22: assetwalletrpc.AssetWallet.FundVirtualPsbt:output_type -> assetwalletrpc.FundVirtualPsbtResponse + 5, // 23: assetwalletrpc.AssetWallet.SignVirtualPsbt:output_type -> assetwalletrpc.SignVirtualPsbtResponse + 27, // 24: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:output_type -> taprpc.SendAssetResponse + 8, // 25: assetwalletrpc.AssetWallet.CommitVirtualPsbts:output_type -> assetwalletrpc.CommitVirtualPsbtsResponse + 10, // 26: assetwalletrpc.AssetWallet.NextInternalKey:output_type -> assetwalletrpc.NextInternalKeyResponse + 12, // 27: assetwalletrpc.AssetWallet.NextScriptKey:output_type -> assetwalletrpc.NextScriptKeyResponse + 14, // 28: assetwalletrpc.AssetWallet.QueryInternalKey:output_type -> assetwalletrpc.QueryInternalKeyResponse + 16, // 29: assetwalletrpc.AssetWallet.QueryScriptKey:output_type -> assetwalletrpc.QueryScriptKeyResponse + 18, // 30: assetwalletrpc.AssetWallet.ProveAssetOwnership:output_type -> assetwalletrpc.ProveAssetOwnershipResponse + 20, // 31: assetwalletrpc.AssetWallet.VerifyAssetOwnership:output_type -> assetwalletrpc.VerifyAssetOwnershipResponse + 22, // 32: assetwalletrpc.AssetWallet.RemoveUTXOLease:output_type -> assetwalletrpc.RemoveUTXOLeaseResponse + 22, // [22:33] is the sub-list for method output_type + 11, // [11:22] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_assetwalletrpc_assetwallet_proto_init() } @@ -1502,7 +1823,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextInternalKeyRequest); i { + switch v := v.(*CommitVirtualPsbtsRequest); i { case 0: return &v.state case 1: @@ -1514,7 +1835,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextInternalKeyResponse); i { + switch v := v.(*CommitVirtualPsbtsResponse); i { case 0: return &v.state case 1: @@ -1526,7 +1847,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextScriptKeyRequest); i { + switch v := v.(*NextInternalKeyRequest); i { case 0: return &v.state case 1: @@ -1538,7 +1859,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextScriptKeyResponse); i { + switch v := v.(*NextInternalKeyResponse); i { case 0: return &v.state case 1: @@ -1550,7 +1871,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryInternalKeyRequest); i { + switch v := v.(*NextScriptKeyRequest); i { case 0: return &v.state case 1: @@ -1562,7 +1883,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryInternalKeyResponse); i { + switch v := v.(*NextScriptKeyResponse); i { case 0: return &v.state case 1: @@ -1574,7 +1895,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryScriptKeyRequest); i { + switch v := v.(*QueryInternalKeyRequest); i { case 0: return &v.state case 1: @@ -1586,7 +1907,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryScriptKeyResponse); i { + switch v := v.(*QueryInternalKeyResponse); i { case 0: return &v.state case 1: @@ -1598,7 +1919,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProveAssetOwnershipRequest); i { + switch v := v.(*QueryScriptKeyRequest); i { case 0: return &v.state case 1: @@ -1610,7 +1931,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProveAssetOwnershipResponse); i { + switch v := v.(*QueryScriptKeyResponse); i { case 0: return &v.state case 1: @@ -1622,7 +1943,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyAssetOwnershipRequest); i { + switch v := v.(*ProveAssetOwnershipRequest); i { case 0: return &v.state case 1: @@ -1634,7 +1955,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyAssetOwnershipResponse); i { + switch v := v.(*ProveAssetOwnershipResponse); i { case 0: return &v.state case 1: @@ -1646,7 +1967,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveUTXOLeaseRequest); i { + switch v := v.(*VerifyAssetOwnershipRequest); i { case 0: return &v.state case 1: @@ -1658,6 +1979,30 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VerifyAssetOwnershipResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_assetwalletrpc_assetwallet_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveUTXOLeaseRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_assetwalletrpc_assetwallet_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveUTXOLeaseResponse); i { case 0: return &v.state @@ -1674,13 +2019,19 @@ func file_assetwalletrpc_assetwallet_proto_init() { (*FundVirtualPsbtRequest_Psbt)(nil), (*FundVirtualPsbtRequest_Raw)(nil), } + file_assetwalletrpc_assetwallet_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*CommitVirtualPsbtsRequest_ExistingOutputIndex)(nil), + (*CommitVirtualPsbtsRequest_Add)(nil), + (*CommitVirtualPsbtsRequest_TargetConf)(nil), + (*CommitVirtualPsbtsRequest_SatPerVbyte)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_assetwalletrpc_assetwallet_proto_rawDesc, NumEnums: 0, - NumMessages: 22, + NumMessages: 24, NumExtensions: 0, NumServices: 1, }, diff --git a/taprpc/assetwalletrpc/assetwallet.pb.gw.go b/taprpc/assetwalletrpc/assetwallet.pb.gw.go index 9abd65fba..c2a55bb1e 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.gw.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.gw.go @@ -133,6 +133,40 @@ func local_request_AssetWallet_AnchorVirtualPsbts_0(ctx context.Context, marshal } +func request_AssetWallet_CommitVirtualPsbts_0(ctx context.Context, marshaler runtime.Marshaler, client AssetWalletClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CommitVirtualPsbtsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CommitVirtualPsbts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AssetWallet_CommitVirtualPsbts_0(ctx context.Context, marshaler runtime.Marshaler, server AssetWalletServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CommitVirtualPsbtsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CommitVirtualPsbts(ctx, &protoReq) + return msg, metadata, err + +} + func request_AssetWallet_NextInternalKey_0(ctx context.Context, marshaler runtime.Marshaler, client AssetWalletClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq NextInternalKeyRequest var metadata runtime.ServerMetadata @@ -482,6 +516,29 @@ func RegisterAssetWalletHandlerServer(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_AssetWallet_CommitVirtualPsbts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/assetwalletrpc.AssetWallet/CommitVirtualPsbts", runtime.WithHTTPPathPattern("/v1/taproot-assets/wallet/virtual-psbt/commit")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AssetWallet_CommitVirtualPsbts_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AssetWallet_CommitVirtualPsbts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AssetWallet_NextInternalKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -744,6 +801,26 @@ func RegisterAssetWalletHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_AssetWallet_CommitVirtualPsbts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/assetwalletrpc.AssetWallet/CommitVirtualPsbts", runtime.WithHTTPPathPattern("/v1/taproot-assets/wallet/virtual-psbt/commit")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AssetWallet_CommitVirtualPsbts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AssetWallet_CommitVirtualPsbts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AssetWallet_NextInternalKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -894,6 +971,8 @@ var ( pattern_AssetWallet_AnchorVirtualPsbts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "virtual-psbt", "anchor"}, "")) + pattern_AssetWallet_CommitVirtualPsbts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "virtual-psbt", "commit"}, "")) + pattern_AssetWallet_NextInternalKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "internal-key", "next"}, "")) pattern_AssetWallet_NextScriptKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "script-key", "next"}, "")) @@ -916,6 +995,8 @@ var ( forward_AssetWallet_AnchorVirtualPsbts_0 = runtime.ForwardResponseMessage + forward_AssetWallet_CommitVirtualPsbts_0 = runtime.ForwardResponseMessage + forward_AssetWallet_NextInternalKey_0 = runtime.ForwardResponseMessage forward_AssetWallet_NextScriptKey_0 = runtime.ForwardResponseMessage diff --git a/taprpc/assetwalletrpc/assetwallet.pb.json.go b/taprpc/assetwalletrpc/assetwallet.pb.json.go index c5c17fbc4..34ee84be4 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.json.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.json.go @@ -96,6 +96,31 @@ func RegisterAssetWalletJSONCallbacks(registry map[string]func(ctx context.Conte callback(string(respBytes), nil) } + registry["assetwalletrpc.AssetWallet.CommitVirtualPsbts"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &CommitVirtualPsbtsRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewAssetWalletClient(conn) + resp, err := client.CommitVirtualPsbts(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + registry["assetwalletrpc.AssetWallet.NextInternalKey"] = func(ctx context.Context, conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { diff --git a/taprpc/assetwalletrpc/assetwallet.proto b/taprpc/assetwalletrpc/assetwallet.proto index 8f0d99c13..9e1fe341a 100644 --- a/taprpc/assetwalletrpc/assetwallet.proto +++ b/taprpc/assetwalletrpc/assetwallet.proto @@ -31,6 +31,15 @@ service AssetWallet { rpc AnchorVirtualPsbts (AnchorVirtualPsbtsRequest) returns (taprpc.SendAssetResponse); + /* + CommitVirtualPsbts creates the output commitments and proofs for the given + virtual transactions by committing them to the BTC level anchor transaction. + In addition, the BTC level anchor transaction is funded and prepared up to + the point where it is ready to be signed. + */ + rpc CommitVirtualPsbts (CommitVirtualPsbtsRequest) + returns (CommitVirtualPsbtsResponse); + /* NextInternalKey derives the next internal key for the given key family and stores it as an internal key in the database to make sure it is identified @@ -194,6 +203,108 @@ message AnchorVirtualPsbtsRequest { repeated bytes virtual_psbts = 1; } +message CommitVirtualPsbtsRequest { + /* + The list of virtual transactions that should be mapped to the given BTC + level anchor transaction template. The virtual transactions are expected to + be signed (or use ASSET_VERSION_V1 with segregated witness to allow for + signing after committing) and ready to be committed to the anchor + transaction. + */ + repeated bytes virtual_psbts = 1; + + /* + The list of passive virtual transactions that are anchored in the same BTC + level anchor transaction inputs as the "active" assets above. These can be + obtained by calling FundVirtualPsbt and using the passive assets returned. + The virtual transactions are expected to be signed (or use ASSET_VERSION_V1 + with segregated witness to allow for signing after committing) and ready to + be committed to the anchor transaction. + The main difference to the "active" assets above is that the passive assets + will not get their own entry in the transfer table of the database, since + they are just carried along and not directly affected by the direct user + action. + */ + repeated bytes passive_asset_psbts = 2; + + /* + The template of the BTC level anchor transaction that the virtual + transactions should be mapped to. The template is expected to already + contain all asset related inputs and outputs corresponding to the virtual + transactions given above. This can be achieved by using + tapfreighter.PrepareAnchoringTemplate for example. + */ + bytes anchor_psbt = 3; + + oneof anchor_change_output { + /* + Use the existing output within the anchor PSBT with the specified + index as the change output. Any leftover change will be added to the + already specified amount of that output. To add a new change output to + the PSBT, set the "add" field below instead. + */ + int32 existing_output_index = 4; + + /* + Add a new P2TR change output to the PSBT if required. + */ + bool add = 5; + } + + oneof fees { + /* + The target number of blocks that the transaction should be confirmed in. + */ + uint32 target_conf = 6; + + /* + The fee rate, expressed in sat/vbyte, that should be used to fund the + BTC level anchor transaction. + */ + uint64 sat_per_vbyte = 7; + } +} + +message CommitVirtualPsbtsResponse { + /* + The funded BTC level anchor transaction with all outputs updated to commit + to the virtual transactions given. The transaction is ready to be signed, + unless some of the asset inputs don't belong to this daemon, in which case + the anchor input derivation info must be added to those inputs first. + */ + bytes anchor_psbt = 1; + + /* + The updated virtual transactions that now contain the state transition + proofs for being committed to the BTC level anchor transaction above. If the + assets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet + signed, then the proofs need to be updated to include the witness before + they become fully valid. + */ + repeated bytes virtual_psbts = 2; + + /* + The updated passive virtual transactions that were committed to the same BTC + level anchor transaction as the "active" virtual transactions given. If the + assets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet + signed, then the proofs need to be updated to include the witness before + they become fully valid. + */ + repeated bytes passive_asset_psbts = 4; + + /* + The index of the (added) change output or -1 if no change was left over. + */ + int32 change_output_index = 5; + + /* + The list of UTXO lock leases that were acquired for the inputs in the funded + PSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked, + inputs that were already present in the PSBT are not locked. + */ + repeated taprpc.OutPoint lnd_locked_utxos = 6; +} + message NextInternalKeyRequest { uint32 key_family = 1; } diff --git a/taprpc/assetwalletrpc/assetwallet.swagger.json b/taprpc/assetwalletrpc/assetwallet.swagger.json index 3e6e66cab..687c5a97e 100644 --- a/taprpc/assetwalletrpc/assetwallet.swagger.json +++ b/taprpc/assetwalletrpc/assetwallet.swagger.json @@ -281,6 +281,39 @@ ] } }, + "/v1/taproot-assets/wallet/virtual-psbt/commit": { + "post": { + "summary": "CommitVirtualPsbts creates the output commitments and proofs for the given\nvirtual transactions by committing them to the BTC level anchor transaction.\nIn addition, the BTC level anchor transaction is funded and prepared up to\nthe point where it is ready to be signed.", + "operationId": "AssetWallet_CommitVirtualPsbts", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/assetwalletrpcCommitVirtualPsbtsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/assetwalletrpcCommitVirtualPsbtsRequest" + } + } + ], + "tags": [ + "AssetWallet" + ] + } + }, "/v1/taproot-assets/wallet/virtual-psbt/fund": { "post": { "summary": "FundVirtualPsbt selects inputs from the available asset commitments to fund\na virtual transaction matching the template.", @@ -362,6 +395,89 @@ } } }, + "assetwalletrpcCommitVirtualPsbtsRequest": { + "type": "object", + "properties": { + "virtual_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The list of virtual transactions that should be mapped to the given BTC\nlevel anchor transaction template. The virtual transactions are expected to\nbe signed (or use ASSET_VERSION_V1 with segregated witness to allow for\nsigning after committing) and ready to be committed to the anchor\ntransaction." + }, + "passive_asset_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The list of passive virtual transactions that are anchored in the same BTC\nlevel anchor transaction inputs as the \"active\" assets above. These can be\nobtained by calling FundVirtualPsbt and using the passive assets returned.\nThe virtual transactions are expected to be signed (or use ASSET_VERSION_V1\nwith segregated witness to allow for signing after committing) and ready to\nbe committed to the anchor transaction.\nThe main difference to the \"active\" assets above is that the passive assets\nwill not get their own entry in the transfer table of the database, since\nthey are just carried along and not directly affected by the direct user\naction." + }, + "anchor_psbt": { + "type": "string", + "format": "byte", + "description": "The template of the BTC level anchor transaction that the virtual\ntransactions should be mapped to. The template is expected to already\ncontain all asset related inputs and outputs corresponding to the virtual\ntransactions given above. This can be achieved by using\ntapfreighter.PrepareAnchoringTemplate for example." + }, + "existing_output_index": { + "type": "integer", + "format": "int32", + "description": "Use the existing output within the anchor PSBT with the specified\nindex as the change output. Any leftover change will be added to the\nalready specified amount of that output. To add a new change output to\nthe PSBT, set the \"add\" field below instead." + }, + "add": { + "type": "boolean", + "description": "Add a new P2TR change output to the PSBT if required." + }, + "target_conf": { + "type": "integer", + "format": "int64", + "description": "The target number of blocks that the transaction should be confirmed in." + }, + "sat_per_vbyte": { + "type": "string", + "format": "uint64", + "description": "The fee rate, expressed in sat/vbyte, that should be used to fund the\nBTC level anchor transaction." + } + } + }, + "assetwalletrpcCommitVirtualPsbtsResponse": { + "type": "object", + "properties": { + "anchor_psbt": { + "type": "string", + "format": "byte", + "description": "The funded BTC level anchor transaction with all outputs updated to commit\nto the virtual transactions given. The transaction is ready to be signed,\nunless some of the asset inputs don't belong to this daemon, in which case\nthe anchor input derivation info must be added to those inputs first." + }, + "virtual_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The updated virtual transactions that now contain the state transition\nproofs for being committed to the BTC level anchor transaction above. If the\nassets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet\nsigned, then the proofs need to be updated to include the witness before\nthey become fully valid." + }, + "passive_asset_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The updated passive virtual transactions that were committed to the same BTC\nlevel anchor transaction as the \"active\" virtual transactions given. If the\nassets in the virtual transaction outputs are ASSET_VERSION_V1 and not yet\nsigned, then the proofs need to be updated to include the witness before\nthey become fully valid." + }, + "change_output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the (added) change output or -1 if no change was left over." + }, + "lnd_locked_utxos": { + "type": "array", + "items": { + "$ref": "#/definitions/taprpcOutPoint" + }, + "description": "The list of UTXO lock leases that were acquired for the inputs in the funded\nPSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked,\ninputs that were already present in the PSBT are not locked." + } + } + }, "assetwalletrpcFundVirtualPsbtRequest": { "type": "object", "properties": { diff --git a/taprpc/assetwalletrpc/assetwallet.yaml b/taprpc/assetwalletrpc/assetwallet.yaml index 74426670a..c50402561 100644 --- a/taprpc/assetwalletrpc/assetwallet.yaml +++ b/taprpc/assetwalletrpc/assetwallet.yaml @@ -15,6 +15,10 @@ http: post: "/v1/taproot-assets/wallet/virtual-psbt/anchor" body: "*" + - selector: assetwalletrpc.AssetWallet.CommitVirtualPsbts + post: "/v1/taproot-assets/wallet/virtual-psbt/commit" + body: "*" + - selector: assetwalletrpc.AssetWallet.NextInternalKey post: "/v1/taproot-assets/wallet/internal-key/next" body: "*" diff --git a/taprpc/assetwalletrpc/assetwallet_grpc.pb.go b/taprpc/assetwalletrpc/assetwallet_grpc.pb.go index c49a0ebcf..3d117d485 100644 --- a/taprpc/assetwalletrpc/assetwallet_grpc.pb.go +++ b/taprpc/assetwalletrpc/assetwallet_grpc.pb.go @@ -31,6 +31,11 @@ type AssetWalletClient interface { // TODO(guggero): Actually implement accepting and merging multiple // transactions. AnchorVirtualPsbts(ctx context.Context, in *AnchorVirtualPsbtsRequest, opts ...grpc.CallOption) (*taprpc.SendAssetResponse, error) + // CommitVirtualPsbts creates the output commitments and proofs for the given + // virtual transactions by committing them to the BTC level anchor transaction. + // In addition, the BTC level anchor transaction is funded and prepared up to + // the point where it is ready to be signed. + CommitVirtualPsbts(ctx context.Context, in *CommitVirtualPsbtsRequest, opts ...grpc.CallOption) (*CommitVirtualPsbtsResponse, error) // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can @@ -97,6 +102,15 @@ func (c *assetWalletClient) AnchorVirtualPsbts(ctx context.Context, in *AnchorVi return out, nil } +func (c *assetWalletClient) CommitVirtualPsbts(ctx context.Context, in *CommitVirtualPsbtsRequest, opts ...grpc.CallOption) (*CommitVirtualPsbtsResponse, error) { + out := new(CommitVirtualPsbtsResponse) + err := c.cc.Invoke(ctx, "/assetwalletrpc.AssetWallet/CommitVirtualPsbts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *assetWalletClient) NextInternalKey(ctx context.Context, in *NextInternalKeyRequest, opts ...grpc.CallOption) (*NextInternalKeyResponse, error) { out := new(NextInternalKeyResponse) err := c.cc.Invoke(ctx, "/assetwalletrpc.AssetWallet/NextInternalKey", in, out, opts...) @@ -176,6 +190,11 @@ type AssetWalletServer interface { // TODO(guggero): Actually implement accepting and merging multiple // transactions. AnchorVirtualPsbts(context.Context, *AnchorVirtualPsbtsRequest) (*taprpc.SendAssetResponse, error) + // CommitVirtualPsbts creates the output commitments and proofs for the given + // virtual transactions by committing them to the BTC level anchor transaction. + // In addition, the BTC level anchor transaction is funded and prepared up to + // the point where it is ready to be signed. + CommitVirtualPsbts(context.Context, *CommitVirtualPsbtsRequest) (*CommitVirtualPsbtsResponse, error) // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can @@ -221,6 +240,9 @@ func (UnimplementedAssetWalletServer) SignVirtualPsbt(context.Context, *SignVirt func (UnimplementedAssetWalletServer) AnchorVirtualPsbts(context.Context, *AnchorVirtualPsbtsRequest) (*taprpc.SendAssetResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AnchorVirtualPsbts not implemented") } +func (UnimplementedAssetWalletServer) CommitVirtualPsbts(context.Context, *CommitVirtualPsbtsRequest) (*CommitVirtualPsbtsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CommitVirtualPsbts not implemented") +} func (UnimplementedAssetWalletServer) NextInternalKey(context.Context, *NextInternalKeyRequest) (*NextInternalKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NextInternalKey not implemented") } @@ -309,6 +331,24 @@ func _AssetWallet_AnchorVirtualPsbts_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _AssetWallet_CommitVirtualPsbts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CommitVirtualPsbtsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssetWalletServer).CommitVirtualPsbts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/assetwalletrpc.AssetWallet/CommitVirtualPsbts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssetWalletServer).CommitVirtualPsbts(ctx, req.(*CommitVirtualPsbtsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AssetWallet_NextInternalKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(NextInternalKeyRequest) if err := dec(in); err != nil { @@ -454,6 +494,10 @@ var AssetWallet_ServiceDesc = grpc.ServiceDesc{ MethodName: "AnchorVirtualPsbts", Handler: _AssetWallet_AnchorVirtualPsbts_Handler, }, + { + MethodName: "CommitVirtualPsbts", + Handler: _AssetWallet_CommitVirtualPsbts_Handler, + }, { MethodName: "NextInternalKey", Handler: _AssetWallet_NextInternalKey_Handler, diff --git a/tapsend/send.go b/tapsend/send.go index 8cbc9f695..e8b079ea7 100644 --- a/tapsend/send.go +++ b/tapsend/send.go @@ -1608,8 +1608,8 @@ func ValidateAnchorOutputs(anchorPacket *psbt.Packet, // ValidateAnchorInputs checks that the anchor inputs of the virtual packets // are valid and consistent with the provided BTC level PSBT packet. -func ValidateAnchorInputs(anchorPacket *psbt.Packet, - packets []*tappsbt.VPacket) error { +func ValidateAnchorInputs(anchorPacket *psbt.Packet, packets []*tappsbt.VPacket, + prunedAssets map[wire.OutPoint][]*asset.Asset) error { // We first make sure the "static" anchor information in the virtual // packets is consistent with the provided BTC level PSBT packet. @@ -1714,6 +1714,14 @@ func ValidateAnchorInputs(anchorPacket *psbt.Packet, } } + // Add the pruned assets to the input assets, as we know they were + // originally present in the input commitment. + for outpoint := range prunedAssets { + inputAssets[outpoint] = append( + inputAssets[outpoint], prunedAssets[outpoint]..., + ) + } + // We can now go through each anchor input that contains assets being // spent and check that we arrive at the correct script. for anchorOutpoint, assets := range inputAssets { diff --git a/tapsend/send_test.go b/tapsend/send_test.go index a63cf4660..162b04896 100644 --- a/tapsend/send_test.go +++ b/tapsend/send_test.go @@ -2862,7 +2862,7 @@ func TestValidateAnchorInputs(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := tapsend.ValidateAnchorInputs( - tc.anchor, tc.packets, + tc.anchor, tc.packets, nil, ) require.ErrorIs(t, err, tc.expectedErr) From 1f0f6711e798da7484ce6b6c0da0aae435a4f2c2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:03 +0100 Subject: [PATCH 14/20] tapfreighter: prepare parcel for logging multiple vPackets We'll want to support multiple virtual transactions as part of a transfer within the porter at some point. For now we just prepare the struct to hold a slice instead of a single packet. And we add a new parcel type that will allow us to publish, log and transfer proofs for a fully externally prepared and anchored transaction. --- tapfreighter/chain_porter.go | 64 ++++++++++-------- tapfreighter/parcel.go | 121 ++++++++++++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 34 deletions(-) diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index 87a7cce03..f9d0f5ced 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -817,7 +817,9 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { "%w", err) } - currentPkg.VirtualPacket = fundSendRes.VPacket + currentPkg.VirtualPackets = []*tappsbt.VPacket{ + fundSendRes.VPacket, + } currentPkg.InputCommitments = fundSendRes.InputCommitments currentPkg.SendState = SendStateVirtualSign @@ -827,18 +829,21 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { // At this point, we have everything we need to sign our _virtual_ // transaction on the Taproot Asset layer. case SendStateVirtualSign: - vPacket := currentPkg.VirtualPacket - receiverScriptKey := vPacket.Outputs[1].ScriptKey.PubKey - log.Infof("Generating Taproot Asset witnesses for send to: %x", - receiverScriptKey.SerializeCompressed()) + vPackets := currentPkg.VirtualPackets // Now we'll use the signer to sign all the inputs for the new // Taproot Asset leaves. The witness data for each input will be // assigned for us. - _, err := p.cfg.AssetWallet.SignVirtualPacket(vPacket) - if err != nil { - return nil, fmt.Errorf("unable to sign and commit "+ - "virtual packet: %w", err) + for idx := range vPackets { + vPkt := vPackets[idx] + + logPacket(vPkt, "Generating Taproot Asset witnesses") + + _, err := p.cfg.AssetWallet.SignVirtualPacket(vPkt) + if err != nil { + return nil, fmt.Errorf("unable to sign and "+ + "commit virtual packet: %w", err) + } } currentPkg.SendState = SendStateAnchorSign @@ -854,14 +859,13 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { // Submit the template PSBT to the wallet for funding. // - // TODO(roasbeef): unlock the input UTXOs of things fail + // TODO(roasbeef): unlock the input UTXOs if things fail var ( feeRate chainfee.SatPerKWeight err error ) // First, use a manual fee rate if specified by the parcel. - // TODO(jhb): Support PSBT flow / PreSignedParcels addrParcel, ok := currentPkg.Parcel.(*AddressParcel) switch { case ok && addrParcel.transferFeeRate != nil: @@ -879,23 +883,20 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { } readableFeeRate := feeRate.FeePerKVByte().String() - log.Infof("sending with fee rate: %v", readableFeeRate) + log.Infof("Sending with fee rate: %v", readableFeeRate) - vPacket := currentPkg.VirtualPacket - firstRecipient, err := vPacket.FirstNonSplitRootOutput() - if err != nil { - return nil, fmt.Errorf("unable to get first "+ - "interactive output: %w", err) + for idx := range currentPkg.VirtualPackets { + vPkt := currentPkg.VirtualPackets[idx] + + logPacket(vPkt, "Constructing new Taproot Asset "+ + "commitments") } - receiverScriptKey := firstRecipient.ScriptKey.PubKey - log.Infof("Constructing new Taproot Asset commitments for "+ - "send to: %x", receiverScriptKey.SerializeCompressed()) // Gather passive assets virtual packets and sign them. wallet := p.cfg.AssetWallet currentPkg.PassiveAssets, err = wallet.CreatePassiveAssets( - ctx, []*tappsbt.VPacket{vPacket}, + ctx, currentPkg.VirtualPackets, currentPkg.InputCommitments, ) if err != nil { @@ -914,7 +915,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { anchorTx, err := wallet.AnchorVirtualTransactions( ctx, &AnchorVTxnsParams{ FeeRate: feeRate, - VPkts: []*tappsbt.VPacket{vPacket}, + VPkts: currentPkg.VirtualPackets, PassiveAssetsVPkts: currentPkg.PassiveAssets, }, ) @@ -958,9 +959,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { // We need to prepare the parcel for storage. parcel, err := ConvertToTransfer( - currentHeight, []*tappsbt.VPacket{ - currentPkg.VirtualPacket, - }, currentPkg.AnchorTx, currentPkg.PassiveAssets, + currentHeight, currentPkg.VirtualPackets, + currentPkg.AnchorTx, currentPkg.PassiveAssets, isLocalKey, ) if err != nil { @@ -1062,6 +1062,20 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { } } +// logPacket logs the virtual packet to the debug log. +func logPacket(vPkt *tappsbt.VPacket, action string) { + firstRecipient, err := vPkt.FirstNonSplitRootOutput() + if err != nil { + // Fall back to the first output if there is no split output + // (probably a full-value send). + firstRecipient = vPkt.Outputs[0] + } + + receiverScriptKey := firstRecipient.ScriptKey.PubKey + log.Infof("%s for send to: %x", action, + receiverScriptKey.SerializeCompressed()) +} + // RegisterSubscriber adds a new subscriber to the set of subscribers that will // be notified of any new events that are broadcast. // diff --git a/tapfreighter/parcel.go b/tapfreighter/parcel.go index a780056aa..e277af411 100644 --- a/tapfreighter/parcel.go +++ b/tapfreighter/parcel.go @@ -107,7 +107,9 @@ type Parcel interface { // kit returns the parcel kit used for delivery. kit() *parcelKit - // Validate validates the parcel. + // Validate validates the parcel. The validation focuses on the + // necessary fields being present in order for the porter not to panic. + // Any business logic validation is assumed to already have happened. Validate() error } @@ -169,7 +171,9 @@ func (p *AddressParcel) kit() *parcelKit { return p.parcelKit } -// Validate validates the parcel. +// Validate validates the parcel. The validation focuses on the necessary fields +// being present in order for the porter not to panic. Any business logic +// validation is assumed to already have happened. func (p *AddressParcel) Validate() error { // We need at least one address to send to in an address parcel. if len(p.destAddrs) < 1 { @@ -225,7 +229,9 @@ func (p *PendingParcel) kit() *parcelKit { return p.parcelKit } -// Validate validates the parcel. +// Validate validates the parcel. The validation focuses on the necessary fields +// being present in order for the porter not to panic. Any business logic +// validation is assumed to already have happened. func (p *PendingParcel) Validate() error { // A pending parcel should have already been validated. return nil @@ -273,7 +279,7 @@ func (p *PreSignedParcel) pkg() *sendPackage { return &sendPackage{ Parcel: p, SendState: SendStateAnchorSign, - VirtualPacket: p.vPkt, + VirtualPackets: []*tappsbt.VPacket{p.vPkt}, InputCommitments: p.inputCommitments, } } @@ -283,9 +289,107 @@ func (p *PreSignedParcel) kit() *parcelKit { return p.parcelKit } -// Validate validates the parcel. +// Validate validates the parcel. The validation focuses on the necessary fields +// being present in order for the porter not to panic. Any business logic +// validation is assumed to already have happened. func (p *PreSignedParcel) Validate() error { - // TODO(ffranr): Add validation where appropriate. + if p.vPkt == nil { + return fmt.Errorf("no virtual transaction in pre-signed parcel") + } + + if len(p.vPkt.Outputs) == 0 { + return fmt.Errorf("no outputs in virtual transaction") + } + + if p.inputCommitments == nil { + return fmt.Errorf("no input commitments in pre-signed parcel") + } + + return nil +} + +// PreAnchoredParcel is a request to log and publish an asset transfer of a +// pre-anchored parcel. All virtual PSBTs and the on-chain BTC level anchor +// transaction must be fully signed and ready to be broadcast. +type PreAnchoredParcel struct { + *parcelKit + + virtualPackets []*tappsbt.VPacket + + passiveAssets []*tappsbt.VPacket + + anchorTx *tapsend.AnchorTransaction +} + +// A compile-time assertion to ensure PreAnchoredParcel implements the Parcel +// interface. +var _ Parcel = (*PreAnchoredParcel)(nil) + +// NewPreAnchoredParcel creates a new PreAnchoredParcel. +func NewPreAnchoredParcel(vPackets []*tappsbt.VPacket, + passiveAssets []*tappsbt.VPacket, + anchorTx *tapsend.AnchorTransaction) *PreAnchoredParcel { + + return &PreAnchoredParcel{ + parcelKit: &parcelKit{ + respChan: make(chan *OutboundParcel, 1), + errChan: make(chan error, 1), + }, + virtualPackets: vPackets, + passiveAssets: passiveAssets, + anchorTx: anchorTx, + } +} + +// pkg returns the send package that should be delivered. +func (p *PreAnchoredParcel) pkg() *sendPackage { + log.Infof("New anchored delivery request with %d packets", + len(p.virtualPackets)) + + // Initialize a package the signed virtual transaction and input + // commitment. + return &sendPackage{ + Parcel: p, + SendState: SendStateLogCommit, + VirtualPackets: p.virtualPackets, + PassiveAssets: p.passiveAssets, + AnchorTx: p.anchorTx, + } +} + +// kit returns the parcel kit used for delivery. +func (p *PreAnchoredParcel) kit() *parcelKit { + return p.parcelKit +} + +// Validate validates the parcel. The validation focuses on the necessary fields +// being present in order for the porter not to panic. Any business logic +// validation is assumed to already have happened. +func (p *PreAnchoredParcel) Validate() error { + if len(p.virtualPackets) == 0 { + return fmt.Errorf("no virtual transactions in pre-anchored " + + "parcel") + } + + for _, vPkt := range p.virtualPackets { + if len(vPkt.Outputs) == 0 { + return fmt.Errorf("no outputs in virtual transaction") + } + } + + if p.anchorTx == nil { + return fmt.Errorf("no anchor transaction in pre-anchored " + + "parcel") + } + + if p.anchorTx.FinalTx == nil { + return fmt.Errorf("no final transaction in anchor transaction") + } + + if p.anchorTx.FundedPsbt == nil { + return fmt.Errorf("no funded PSBT in anchor transaction") + } + return nil } @@ -294,9 +398,8 @@ type sendPackage struct { // SendState is the current send state of this parcel. SendState SendState - // VirtualPacket is the virtual packet that we'll use to construct the - // virtual asset transition transaction. - VirtualPacket *tappsbt.VPacket + // VirtualPackets is the list of virtual packets that should be shipped. + VirtualPackets []*tappsbt.VPacket // InputCommitments is a map from virtual package input index to its // associated Taproot Asset commitment. From f0d1950ef79f32071fba008c6c43353589eb154d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:04 +0100 Subject: [PATCH 15/20] multi: add PublishAndLogTransfer RPC --- perms/perms.go | 4 + rpcserver.go | 106 +++ taprpc/assetwalletrpc/assetwallet.pb.go | 627 +++++++++++------- taprpc/assetwalletrpc/assetwallet.pb.gw.go | 81 +++ taprpc/assetwalletrpc/assetwallet.pb.json.go | 25 + taprpc/assetwalletrpc/assetwallet.proto | 50 +- .../assetwalletrpc/assetwallet.swagger.json | 73 +- taprpc/assetwalletrpc/assetwallet.yaml | 4 + taprpc/assetwalletrpc/assetwallet_grpc.pb.go | 58 +- 9 files changed, 775 insertions(+), 253 deletions(-) diff --git a/perms/perms.go b/perms/perms.go index 79b193934..8ed4fcb98 100644 --- a/perms/perms.go +++ b/perms/perms.go @@ -104,6 +104,10 @@ var ( Entity: "assets", Action: "write", }}, + "/assetwalletrpc.AssetWallet/PublishAndLogTransfer": {{ + Entity: "assets", + Action: "write", + }}, "/assetwalletrpc.AssetWallet/NextInternalKey": {{ Entity: "assets", Action: "write", diff --git a/rpcserver.go b/rpcserver.go index b01afcec0..71312548a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2345,6 +2345,112 @@ func (r *rpcServer) validateInputAssets(ctx context.Context, return nil } +// PublishAndLogTransfer accepts a fully committed and signed anchor transaction +// and publishes it to the Bitcoin network. It also logs the transfer of the +// given active and passive assets in the database and ships any outgoing proofs +// to the counterparties. +func (r *rpcServer) PublishAndLogTransfer(ctx context.Context, + req *wrpc.PublishAndLogRequest) (*taprpc.SendAssetResponse, error) { + + if len(req.VirtualPsbts) == 0 { + return nil, fmt.Errorf("no virtual PSBTs specified") + } + + pkt, err := psbt.NewFromRawBytes(bytes.NewReader(req.AnchorPsbt), false) + if err != nil { + return nil, fmt.Errorf("error decoding packet: %w", err) + } + + activePackets, err := decodeVirtualPackets(req.VirtualPsbts) + if err != nil { + return nil, fmt.Errorf("error decoding active packets: %w", err) + } + + passivePackets, err := decodeVirtualPackets(req.PassiveAssetPsbts) + if err != nil { + return nil, fmt.Errorf("error decoding passive packets: %w", + err) + } + + // Before we commit the transaction to the database, we want to make + // sure everything is in order. We start by validating the inputs. + allPackets := append([]*tappsbt.VPacket{}, activePackets...) + allPackets = append(allPackets, passivePackets...) + err = r.validateInputAssets(ctx, pkt, allPackets) + if err != nil { + return nil, fmt.Errorf("error validating input assets: %w", err) + } + + // And then the outputs as well. + err = tapsend.ValidateAnchorOutputs(pkt, allPackets, true) + if err != nil { + return nil, fmt.Errorf("error validating anchor outputs: %w", + err) + } + + chainFees, err := pkt.GetTxFee() + if err != nil { + return nil, fmt.Errorf("error calculating transaction fees: %w", + err) + } + + // The BTC level transaction must be fully complete, and we must be able + // to extract the final transaction from it. + finalTx, err := psbt.Extract(pkt) + if err != nil { + return nil, fmt.Errorf("error extracting final anchor "+ + "transaction: %w", err) + } + + anchorTx := &tapsend.AnchorTransaction{ + FundedPsbt: &tapsend.FundedPsbt{ + Pkt: pkt, + ChangeOutputIndex: req.ChangeOutputIndex, + ChainFees: int64(chainFees), + LockedUTXOs: make( + []wire.OutPoint, len(req.LndLockedUtxos), + ), + }, + ChainFees: int64(chainFees), + FinalTx: finalTx, + } + for idx, lndOutpoint := range req.LndLockedUtxos { + hash, err := chainhash.NewHash(lndOutpoint.Txid) + if err != nil { + return nil, fmt.Errorf("error parsing txid: %w", err) + } + + anchorTx.FundedPsbt.LockedUTXOs[idx] = wire.OutPoint{ + Hash: *hash, + Index: lndOutpoint.OutputIndex, + } + } + + // We now have everything to ship the pre-anchored parcel using the + // freighter. This will publish the TX, create the transfer database + // entries and ship the proofs to the counterparties. It'll also wait + // for a confirmation and then update the proofs with the block header + // information. + resp, err := r.cfg.ChainPorter.RequestShipment( + tapfreighter.NewPreAnchoredParcel( + activePackets, passivePackets, anchorTx, + ), + ) + if err != nil { + return nil, fmt.Errorf("error requesting delivery: %w", err) + } + + parcel, err := marshalOutboundParcel(resp) + if err != nil { + return nil, fmt.Errorf("error marshaling outbound parcel: %w", + err) + } + + return &taprpc.SendAssetResponse{ + Transfer: parcel, + }, nil +} + // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can diff --git a/taprpc/assetwalletrpc/assetwallet.pb.go b/taprpc/assetwalletrpc/assetwallet.pb.go index fa4dcc266..7e92d3c8f 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.go @@ -742,6 +742,97 @@ func (x *CommitVirtualPsbtsResponse) GetLndLockedUtxos() []*taprpc.OutPoint { return nil } +type PublishAndLogRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The funded BTC level anchor transaction with all outputs updated to commit + // to the virtual transactions given. The transaction is ready to be signed, + // unless some of the asset inputs don't belong to this daemon, in which case + // the anchor input derivation info must be added to those inputs first. + AnchorPsbt []byte `protobuf:"bytes,1,opt,name=anchor_psbt,json=anchorPsbt,proto3" json:"anchor_psbt,omitempty"` + // The updated virtual transactions that contain the state transition proofs + // of being committed to the BTC level anchor transaction above. + VirtualPsbts [][]byte `protobuf:"bytes,2,rep,name=virtual_psbts,json=virtualPsbts,proto3" json:"virtual_psbts,omitempty"` + // The updated passive virtual transactions that contain the state transition + // proofs of being committed to the BTC level anchor transaction above. + PassiveAssetPsbts [][]byte `protobuf:"bytes,3,rep,name=passive_asset_psbts,json=passiveAssetPsbts,proto3" json:"passive_asset_psbts,omitempty"` + // The index of the (added) change output or -1 if no change was left over. + ChangeOutputIndex int32 `protobuf:"varint,4,opt,name=change_output_index,json=changeOutputIndex,proto3" json:"change_output_index,omitempty"` + // The list of UTXO lock leases that were acquired for the inputs in the funded + // PSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked, + // inputs that were already present in the PSBT are not locked. + LndLockedUtxos []*taprpc.OutPoint `protobuf:"bytes,5,rep,name=lnd_locked_utxos,json=lndLockedUtxos,proto3" json:"lnd_locked_utxos,omitempty"` +} + +func (x *PublishAndLogRequest) Reset() { + *x = PublishAndLogRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublishAndLogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishAndLogRequest) ProtoMessage() {} + +func (x *PublishAndLogRequest) ProtoReflect() protoreflect.Message { + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + 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 PublishAndLogRequest.ProtoReflect.Descriptor instead. +func (*PublishAndLogRequest) Descriptor() ([]byte, []int) { + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{9} +} + +func (x *PublishAndLogRequest) GetAnchorPsbt() []byte { + if x != nil { + return x.AnchorPsbt + } + return nil +} + +func (x *PublishAndLogRequest) GetVirtualPsbts() [][]byte { + if x != nil { + return x.VirtualPsbts + } + return nil +} + +func (x *PublishAndLogRequest) GetPassiveAssetPsbts() [][]byte { + if x != nil { + return x.PassiveAssetPsbts + } + return nil +} + +func (x *PublishAndLogRequest) GetChangeOutputIndex() int32 { + if x != nil { + return x.ChangeOutputIndex + } + return 0 +} + +func (x *PublishAndLogRequest) GetLndLockedUtxos() []*taprpc.OutPoint { + if x != nil { + return x.LndLockedUtxos + } + return nil +} + type NextInternalKeyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -753,7 +844,7 @@ type NextInternalKeyRequest struct { func (x *NextInternalKeyRequest) Reset() { *x = NextInternalKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -766,7 +857,7 @@ func (x *NextInternalKeyRequest) String() string { func (*NextInternalKeyRequest) ProtoMessage() {} func (x *NextInternalKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[9] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -779,7 +870,7 @@ func (x *NextInternalKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NextInternalKeyRequest.ProtoReflect.Descriptor instead. func (*NextInternalKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{9} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{10} } func (x *NextInternalKeyRequest) GetKeyFamily() uint32 { @@ -800,7 +891,7 @@ type NextInternalKeyResponse struct { func (x *NextInternalKeyResponse) Reset() { *x = NextInternalKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -813,7 +904,7 @@ func (x *NextInternalKeyResponse) String() string { func (*NextInternalKeyResponse) ProtoMessage() {} func (x *NextInternalKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[10] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -826,7 +917,7 @@ func (x *NextInternalKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NextInternalKeyResponse.ProtoReflect.Descriptor instead. func (*NextInternalKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{10} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{11} } func (x *NextInternalKeyResponse) GetInternalKey() *taprpc.KeyDescriptor { @@ -847,7 +938,7 @@ type NextScriptKeyRequest struct { func (x *NextScriptKeyRequest) Reset() { *x = NextScriptKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -860,7 +951,7 @@ func (x *NextScriptKeyRequest) String() string { func (*NextScriptKeyRequest) ProtoMessage() {} func (x *NextScriptKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[11] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -873,7 +964,7 @@ func (x *NextScriptKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NextScriptKeyRequest.ProtoReflect.Descriptor instead. func (*NextScriptKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{11} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{12} } func (x *NextScriptKeyRequest) GetKeyFamily() uint32 { @@ -894,7 +985,7 @@ type NextScriptKeyResponse struct { func (x *NextScriptKeyResponse) Reset() { *x = NextScriptKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -907,7 +998,7 @@ func (x *NextScriptKeyResponse) String() string { func (*NextScriptKeyResponse) ProtoMessage() {} func (x *NextScriptKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[12] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -920,7 +1011,7 @@ func (x *NextScriptKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NextScriptKeyResponse.ProtoReflect.Descriptor instead. func (*NextScriptKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{12} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{13} } func (x *NextScriptKeyResponse) GetScriptKey() *taprpc.ScriptKey { @@ -943,7 +1034,7 @@ type QueryInternalKeyRequest struct { func (x *QueryInternalKeyRequest) Reset() { *x = QueryInternalKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -956,7 +1047,7 @@ func (x *QueryInternalKeyRequest) String() string { func (*QueryInternalKeyRequest) ProtoMessage() {} func (x *QueryInternalKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[13] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -969,7 +1060,7 @@ func (x *QueryInternalKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryInternalKeyRequest.ProtoReflect.Descriptor instead. func (*QueryInternalKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{13} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{14} } func (x *QueryInternalKeyRequest) GetInternalKey() []byte { @@ -990,7 +1081,7 @@ type QueryInternalKeyResponse struct { func (x *QueryInternalKeyResponse) Reset() { *x = QueryInternalKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1003,7 +1094,7 @@ func (x *QueryInternalKeyResponse) String() string { func (*QueryInternalKeyResponse) ProtoMessage() {} func (x *QueryInternalKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[14] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1016,7 +1107,7 @@ func (x *QueryInternalKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryInternalKeyResponse.ProtoReflect.Descriptor instead. func (*QueryInternalKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{14} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{15} } func (x *QueryInternalKeyResponse) GetInternalKey() *taprpc.KeyDescriptor { @@ -1040,7 +1131,7 @@ type QueryScriptKeyRequest struct { func (x *QueryScriptKeyRequest) Reset() { *x = QueryScriptKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1053,7 +1144,7 @@ func (x *QueryScriptKeyRequest) String() string { func (*QueryScriptKeyRequest) ProtoMessage() {} func (x *QueryScriptKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[15] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1066,7 +1157,7 @@ func (x *QueryScriptKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryScriptKeyRequest.ProtoReflect.Descriptor instead. func (*QueryScriptKeyRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{15} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{16} } func (x *QueryScriptKeyRequest) GetTweakedScriptKey() []byte { @@ -1087,7 +1178,7 @@ type QueryScriptKeyResponse struct { func (x *QueryScriptKeyResponse) Reset() { *x = QueryScriptKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1100,7 +1191,7 @@ func (x *QueryScriptKeyResponse) String() string { func (*QueryScriptKeyResponse) ProtoMessage() {} func (x *QueryScriptKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[16] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1113,7 +1204,7 @@ func (x *QueryScriptKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryScriptKeyResponse.ProtoReflect.Descriptor instead. func (*QueryScriptKeyResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{16} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{17} } func (x *QueryScriptKeyResponse) GetScriptKey() *taprpc.ScriptKey { @@ -1136,7 +1227,7 @@ type ProveAssetOwnershipRequest struct { func (x *ProveAssetOwnershipRequest) Reset() { *x = ProveAssetOwnershipRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1149,7 +1240,7 @@ func (x *ProveAssetOwnershipRequest) String() string { func (*ProveAssetOwnershipRequest) ProtoMessage() {} func (x *ProveAssetOwnershipRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[17] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1162,7 +1253,7 @@ func (x *ProveAssetOwnershipRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ProveAssetOwnershipRequest.ProtoReflect.Descriptor instead. func (*ProveAssetOwnershipRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{17} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{18} } func (x *ProveAssetOwnershipRequest) GetAssetId() []byte { @@ -1197,7 +1288,7 @@ type ProveAssetOwnershipResponse struct { func (x *ProveAssetOwnershipResponse) Reset() { *x = ProveAssetOwnershipResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1210,7 +1301,7 @@ func (x *ProveAssetOwnershipResponse) String() string { func (*ProveAssetOwnershipResponse) ProtoMessage() {} func (x *ProveAssetOwnershipResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[18] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1223,7 +1314,7 @@ func (x *ProveAssetOwnershipResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProveAssetOwnershipResponse.ProtoReflect.Descriptor instead. func (*ProveAssetOwnershipResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{18} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{19} } func (x *ProveAssetOwnershipResponse) GetProofWithWitness() []byte { @@ -1244,7 +1335,7 @@ type VerifyAssetOwnershipRequest struct { func (x *VerifyAssetOwnershipRequest) Reset() { *x = VerifyAssetOwnershipRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1257,7 +1348,7 @@ func (x *VerifyAssetOwnershipRequest) String() string { func (*VerifyAssetOwnershipRequest) ProtoMessage() {} func (x *VerifyAssetOwnershipRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[19] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1270,7 +1361,7 @@ func (x *VerifyAssetOwnershipRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyAssetOwnershipRequest.ProtoReflect.Descriptor instead. func (*VerifyAssetOwnershipRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{19} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{20} } func (x *VerifyAssetOwnershipRequest) GetProofWithWitness() []byte { @@ -1291,7 +1382,7 @@ type VerifyAssetOwnershipResponse struct { func (x *VerifyAssetOwnershipResponse) Reset() { *x = VerifyAssetOwnershipResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1304,7 +1395,7 @@ func (x *VerifyAssetOwnershipResponse) String() string { func (*VerifyAssetOwnershipResponse) ProtoMessage() {} func (x *VerifyAssetOwnershipResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[20] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1317,7 +1408,7 @@ func (x *VerifyAssetOwnershipResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyAssetOwnershipResponse.ProtoReflect.Descriptor instead. func (*VerifyAssetOwnershipResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{20} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{21} } func (x *VerifyAssetOwnershipResponse) GetValidProof() bool { @@ -1339,7 +1430,7 @@ type RemoveUTXOLeaseRequest struct { func (x *RemoveUTXOLeaseRequest) Reset() { *x = RemoveUTXOLeaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1352,7 +1443,7 @@ func (x *RemoveUTXOLeaseRequest) String() string { func (*RemoveUTXOLeaseRequest) ProtoMessage() {} func (x *RemoveUTXOLeaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[21] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1365,7 +1456,7 @@ func (x *RemoveUTXOLeaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveUTXOLeaseRequest.ProtoReflect.Descriptor instead. func (*RemoveUTXOLeaseRequest) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{21} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{22} } func (x *RemoveUTXOLeaseRequest) GetOutpoint() *taprpc.OutPoint { @@ -1384,7 +1475,7 @@ type RemoveUTXOLeaseResponse struct { func (x *RemoveUTXOLeaseResponse) Reset() { *x = RemoveUTXOLeaseResponse{} if protoimpl.UnsafeEnabled { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1397,7 +1488,7 @@ func (x *RemoveUTXOLeaseResponse) String() string { func (*RemoveUTXOLeaseResponse) ProtoMessage() {} func (x *RemoveUTXOLeaseResponse) ProtoReflect() protoreflect.Message { - mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[22] + mi := &file_assetwalletrpc_assetwallet_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1410,7 +1501,7 @@ func (x *RemoveUTXOLeaseResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveUTXOLeaseResponse.ProtoReflect.Descriptor instead. func (*RemoveUTXOLeaseResponse) Descriptor() ([]byte, []int) { - return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{22} + return file_assetwalletrpc_assetwallet_proto_rawDescGZIP(), []int{23} } var File_assetwalletrpc_assetwallet_proto protoreflect.FileDescriptor @@ -1506,146 +1597,168 @@ var file_assetwalletrpc_assetwallet_proto_rawDesc = []byte{ 0x6e, 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x6c, 0x6e, 0x64, 0x4c, 0x6f, 0x63, 0x6b, - 0x65, 0x64, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x37, 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, - 0x22, 0x53, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x35, 0x0a, 0x14, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, - 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x3c, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x54, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x10, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, - 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, - 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 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, 0x4b, 0x0a, 0x1b, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, - 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, - 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x1b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, - 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, - 0x3f, 0x0a, 0x1c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, + 0x65, 0x64, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0xf8, 0x01, 0x0a, 0x14, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x73, 0x68, 0x41, 0x6e, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x50, 0x73, 0x62, + 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, + 0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x11, 0x70, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x3a, 0x0a, 0x10, 0x6c, 0x6e, 0x64, 0x5f, 0x6c, 0x6f, + 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x0e, 0x6c, 0x6e, 0x64, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x22, 0x37, 0x0a, 0x16, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x53, 0x0a, 0x17, 0x4e, + 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, + 0x22, 0x35, 0x0a, 0x14, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, + 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, + 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x15, 0x4e, 0x65, 0x78, 0x74, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, + 0x65, 0x79, 0x22, 0x3c, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, + 0x22, 0x54, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x22, 0x45, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2c, 0x0a, 0x12, 0x74, 0x77, 0x65, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, 0x77, 0x65, + 0x61, 0x6b, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x4a, 0x0a, + 0x16, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x61, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x09, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x1a, 0x50, 0x72, + 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 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, 0x4b, 0x0a, 0x1b, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, - 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, - 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, - 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 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, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x32, 0xef, 0x08, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x12, 0x62, 0x0a, 0x0f, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, - 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, - 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, - 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, - 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, - 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, - 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, - 0x50, 0x73, 0x62, 0x74, 0x73, 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, 0x6b, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, + 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, + 0x6f, 0x66, 0x57, 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x4b, 0x0a, + 0x1b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, + 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x57, + 0x69, 0x74, 0x68, 0x57, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x3f, 0x0a, 0x1c, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x46, 0x0a, 0x16, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x18, 0x01, 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, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, + 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc9, + 0x09, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x62, + 0x0a, 0x0f, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, + 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, + 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x56, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x12, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6e, + 0x63, 0x68, 0x6f, 0x72, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, + 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, 0x6b, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x56, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x58, 0x0a, 0x15, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x41, 0x6e, 0x64, 0x4c, 0x6f, 0x67, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x41, 0x6e, 0x64, 0x4c, 0x6f, 0x67, 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, 0x62, 0x0a, 0x0f, 0x4e, 0x65, 0x78, + 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x2e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, + 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, + 0x0d, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, + 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x10, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4e, 0x65, 0x78, 0x74, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, - 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, - 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, - 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, - 0x0a, 0x14, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, - 0x65, 0x61, 0x73, 0x65, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, - 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x2e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x14, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2b, 0x2e, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x26, 0x2e, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x4c, 0x65, 0x61, + 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 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, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1660,7 +1773,7 @@ func file_assetwalletrpc_assetwallet_proto_rawDescGZIP() []byte { return file_assetwalletrpc_assetwallet_proto_rawDescData } -var file_assetwalletrpc_assetwallet_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_assetwalletrpc_assetwallet_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_assetwalletrpc_assetwallet_proto_goTypes = []interface{}{ (*FundVirtualPsbtRequest)(nil), // 0: assetwalletrpc.FundVirtualPsbtRequest (*FundVirtualPsbtResponse)(nil), // 1: assetwalletrpc.FundVirtualPsbtResponse @@ -1671,65 +1784,69 @@ var file_assetwalletrpc_assetwallet_proto_goTypes = []interface{}{ (*AnchorVirtualPsbtsRequest)(nil), // 6: assetwalletrpc.AnchorVirtualPsbtsRequest (*CommitVirtualPsbtsRequest)(nil), // 7: assetwalletrpc.CommitVirtualPsbtsRequest (*CommitVirtualPsbtsResponse)(nil), // 8: assetwalletrpc.CommitVirtualPsbtsResponse - (*NextInternalKeyRequest)(nil), // 9: assetwalletrpc.NextInternalKeyRequest - (*NextInternalKeyResponse)(nil), // 10: assetwalletrpc.NextInternalKeyResponse - (*NextScriptKeyRequest)(nil), // 11: assetwalletrpc.NextScriptKeyRequest - (*NextScriptKeyResponse)(nil), // 12: assetwalletrpc.NextScriptKeyResponse - (*QueryInternalKeyRequest)(nil), // 13: assetwalletrpc.QueryInternalKeyRequest - (*QueryInternalKeyResponse)(nil), // 14: assetwalletrpc.QueryInternalKeyResponse - (*QueryScriptKeyRequest)(nil), // 15: assetwalletrpc.QueryScriptKeyRequest - (*QueryScriptKeyResponse)(nil), // 16: assetwalletrpc.QueryScriptKeyResponse - (*ProveAssetOwnershipRequest)(nil), // 17: assetwalletrpc.ProveAssetOwnershipRequest - (*ProveAssetOwnershipResponse)(nil), // 18: assetwalletrpc.ProveAssetOwnershipResponse - (*VerifyAssetOwnershipRequest)(nil), // 19: assetwalletrpc.VerifyAssetOwnershipRequest - (*VerifyAssetOwnershipResponse)(nil), // 20: assetwalletrpc.VerifyAssetOwnershipResponse - (*RemoveUTXOLeaseRequest)(nil), // 21: assetwalletrpc.RemoveUTXOLeaseRequest - (*RemoveUTXOLeaseResponse)(nil), // 22: assetwalletrpc.RemoveUTXOLeaseResponse - nil, // 23: assetwalletrpc.TxTemplate.RecipientsEntry - (*taprpc.OutPoint)(nil), // 24: taprpc.OutPoint - (*taprpc.KeyDescriptor)(nil), // 25: taprpc.KeyDescriptor - (*taprpc.ScriptKey)(nil), // 26: taprpc.ScriptKey - (*taprpc.SendAssetResponse)(nil), // 27: taprpc.SendAssetResponse + (*PublishAndLogRequest)(nil), // 9: assetwalletrpc.PublishAndLogRequest + (*NextInternalKeyRequest)(nil), // 10: assetwalletrpc.NextInternalKeyRequest + (*NextInternalKeyResponse)(nil), // 11: assetwalletrpc.NextInternalKeyResponse + (*NextScriptKeyRequest)(nil), // 12: assetwalletrpc.NextScriptKeyRequest + (*NextScriptKeyResponse)(nil), // 13: assetwalletrpc.NextScriptKeyResponse + (*QueryInternalKeyRequest)(nil), // 14: assetwalletrpc.QueryInternalKeyRequest + (*QueryInternalKeyResponse)(nil), // 15: assetwalletrpc.QueryInternalKeyResponse + (*QueryScriptKeyRequest)(nil), // 16: assetwalletrpc.QueryScriptKeyRequest + (*QueryScriptKeyResponse)(nil), // 17: assetwalletrpc.QueryScriptKeyResponse + (*ProveAssetOwnershipRequest)(nil), // 18: assetwalletrpc.ProveAssetOwnershipRequest + (*ProveAssetOwnershipResponse)(nil), // 19: assetwalletrpc.ProveAssetOwnershipResponse + (*VerifyAssetOwnershipRequest)(nil), // 20: assetwalletrpc.VerifyAssetOwnershipRequest + (*VerifyAssetOwnershipResponse)(nil), // 21: assetwalletrpc.VerifyAssetOwnershipResponse + (*RemoveUTXOLeaseRequest)(nil), // 22: assetwalletrpc.RemoveUTXOLeaseRequest + (*RemoveUTXOLeaseResponse)(nil), // 23: assetwalletrpc.RemoveUTXOLeaseResponse + nil, // 24: assetwalletrpc.TxTemplate.RecipientsEntry + (*taprpc.OutPoint)(nil), // 25: taprpc.OutPoint + (*taprpc.KeyDescriptor)(nil), // 26: taprpc.KeyDescriptor + (*taprpc.ScriptKey)(nil), // 27: taprpc.ScriptKey + (*taprpc.SendAssetResponse)(nil), // 28: taprpc.SendAssetResponse } var file_assetwalletrpc_assetwallet_proto_depIdxs = []int32{ 2, // 0: assetwalletrpc.FundVirtualPsbtRequest.raw:type_name -> assetwalletrpc.TxTemplate 3, // 1: assetwalletrpc.TxTemplate.inputs:type_name -> assetwalletrpc.PrevId - 23, // 2: assetwalletrpc.TxTemplate.recipients:type_name -> assetwalletrpc.TxTemplate.RecipientsEntry - 24, // 3: assetwalletrpc.PrevId.outpoint:type_name -> taprpc.OutPoint - 24, // 4: assetwalletrpc.CommitVirtualPsbtsResponse.lnd_locked_utxos:type_name -> taprpc.OutPoint - 25, // 5: assetwalletrpc.NextInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor - 26, // 6: assetwalletrpc.NextScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey - 25, // 7: assetwalletrpc.QueryInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor - 26, // 8: assetwalletrpc.QueryScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey - 24, // 9: assetwalletrpc.ProveAssetOwnershipRequest.outpoint:type_name -> taprpc.OutPoint - 24, // 10: assetwalletrpc.RemoveUTXOLeaseRequest.outpoint:type_name -> taprpc.OutPoint - 0, // 11: assetwalletrpc.AssetWallet.FundVirtualPsbt:input_type -> assetwalletrpc.FundVirtualPsbtRequest - 4, // 12: assetwalletrpc.AssetWallet.SignVirtualPsbt:input_type -> assetwalletrpc.SignVirtualPsbtRequest - 6, // 13: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:input_type -> assetwalletrpc.AnchorVirtualPsbtsRequest - 7, // 14: assetwalletrpc.AssetWallet.CommitVirtualPsbts:input_type -> assetwalletrpc.CommitVirtualPsbtsRequest - 9, // 15: assetwalletrpc.AssetWallet.NextInternalKey:input_type -> assetwalletrpc.NextInternalKeyRequest - 11, // 16: assetwalletrpc.AssetWallet.NextScriptKey:input_type -> assetwalletrpc.NextScriptKeyRequest - 13, // 17: assetwalletrpc.AssetWallet.QueryInternalKey:input_type -> assetwalletrpc.QueryInternalKeyRequest - 15, // 18: assetwalletrpc.AssetWallet.QueryScriptKey:input_type -> assetwalletrpc.QueryScriptKeyRequest - 17, // 19: assetwalletrpc.AssetWallet.ProveAssetOwnership:input_type -> assetwalletrpc.ProveAssetOwnershipRequest - 19, // 20: assetwalletrpc.AssetWallet.VerifyAssetOwnership:input_type -> assetwalletrpc.VerifyAssetOwnershipRequest - 21, // 21: assetwalletrpc.AssetWallet.RemoveUTXOLease:input_type -> assetwalletrpc.RemoveUTXOLeaseRequest - 1, // 22: assetwalletrpc.AssetWallet.FundVirtualPsbt:output_type -> assetwalletrpc.FundVirtualPsbtResponse - 5, // 23: assetwalletrpc.AssetWallet.SignVirtualPsbt:output_type -> assetwalletrpc.SignVirtualPsbtResponse - 27, // 24: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:output_type -> taprpc.SendAssetResponse - 8, // 25: assetwalletrpc.AssetWallet.CommitVirtualPsbts:output_type -> assetwalletrpc.CommitVirtualPsbtsResponse - 10, // 26: assetwalletrpc.AssetWallet.NextInternalKey:output_type -> assetwalletrpc.NextInternalKeyResponse - 12, // 27: assetwalletrpc.AssetWallet.NextScriptKey:output_type -> assetwalletrpc.NextScriptKeyResponse - 14, // 28: assetwalletrpc.AssetWallet.QueryInternalKey:output_type -> assetwalletrpc.QueryInternalKeyResponse - 16, // 29: assetwalletrpc.AssetWallet.QueryScriptKey:output_type -> assetwalletrpc.QueryScriptKeyResponse - 18, // 30: assetwalletrpc.AssetWallet.ProveAssetOwnership:output_type -> assetwalletrpc.ProveAssetOwnershipResponse - 20, // 31: assetwalletrpc.AssetWallet.VerifyAssetOwnership:output_type -> assetwalletrpc.VerifyAssetOwnershipResponse - 22, // 32: assetwalletrpc.AssetWallet.RemoveUTXOLease:output_type -> assetwalletrpc.RemoveUTXOLeaseResponse - 22, // [22:33] is the sub-list for method output_type - 11, // [11:22] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 24, // 2: assetwalletrpc.TxTemplate.recipients:type_name -> assetwalletrpc.TxTemplate.RecipientsEntry + 25, // 3: assetwalletrpc.PrevId.outpoint:type_name -> taprpc.OutPoint + 25, // 4: assetwalletrpc.CommitVirtualPsbtsResponse.lnd_locked_utxos:type_name -> taprpc.OutPoint + 25, // 5: assetwalletrpc.PublishAndLogRequest.lnd_locked_utxos:type_name -> taprpc.OutPoint + 26, // 6: assetwalletrpc.NextInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor + 27, // 7: assetwalletrpc.NextScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey + 26, // 8: assetwalletrpc.QueryInternalKeyResponse.internal_key:type_name -> taprpc.KeyDescriptor + 27, // 9: assetwalletrpc.QueryScriptKeyResponse.script_key:type_name -> taprpc.ScriptKey + 25, // 10: assetwalletrpc.ProveAssetOwnershipRequest.outpoint:type_name -> taprpc.OutPoint + 25, // 11: assetwalletrpc.RemoveUTXOLeaseRequest.outpoint:type_name -> taprpc.OutPoint + 0, // 12: assetwalletrpc.AssetWallet.FundVirtualPsbt:input_type -> assetwalletrpc.FundVirtualPsbtRequest + 4, // 13: assetwalletrpc.AssetWallet.SignVirtualPsbt:input_type -> assetwalletrpc.SignVirtualPsbtRequest + 6, // 14: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:input_type -> assetwalletrpc.AnchorVirtualPsbtsRequest + 7, // 15: assetwalletrpc.AssetWallet.CommitVirtualPsbts:input_type -> assetwalletrpc.CommitVirtualPsbtsRequest + 9, // 16: assetwalletrpc.AssetWallet.PublishAndLogTransfer:input_type -> assetwalletrpc.PublishAndLogRequest + 10, // 17: assetwalletrpc.AssetWallet.NextInternalKey:input_type -> assetwalletrpc.NextInternalKeyRequest + 12, // 18: assetwalletrpc.AssetWallet.NextScriptKey:input_type -> assetwalletrpc.NextScriptKeyRequest + 14, // 19: assetwalletrpc.AssetWallet.QueryInternalKey:input_type -> assetwalletrpc.QueryInternalKeyRequest + 16, // 20: assetwalletrpc.AssetWallet.QueryScriptKey:input_type -> assetwalletrpc.QueryScriptKeyRequest + 18, // 21: assetwalletrpc.AssetWallet.ProveAssetOwnership:input_type -> assetwalletrpc.ProveAssetOwnershipRequest + 20, // 22: assetwalletrpc.AssetWallet.VerifyAssetOwnership:input_type -> assetwalletrpc.VerifyAssetOwnershipRequest + 22, // 23: assetwalletrpc.AssetWallet.RemoveUTXOLease:input_type -> assetwalletrpc.RemoveUTXOLeaseRequest + 1, // 24: assetwalletrpc.AssetWallet.FundVirtualPsbt:output_type -> assetwalletrpc.FundVirtualPsbtResponse + 5, // 25: assetwalletrpc.AssetWallet.SignVirtualPsbt:output_type -> assetwalletrpc.SignVirtualPsbtResponse + 28, // 26: assetwalletrpc.AssetWallet.AnchorVirtualPsbts:output_type -> taprpc.SendAssetResponse + 8, // 27: assetwalletrpc.AssetWallet.CommitVirtualPsbts:output_type -> assetwalletrpc.CommitVirtualPsbtsResponse + 28, // 28: assetwalletrpc.AssetWallet.PublishAndLogTransfer:output_type -> taprpc.SendAssetResponse + 11, // 29: assetwalletrpc.AssetWallet.NextInternalKey:output_type -> assetwalletrpc.NextInternalKeyResponse + 13, // 30: assetwalletrpc.AssetWallet.NextScriptKey:output_type -> assetwalletrpc.NextScriptKeyResponse + 15, // 31: assetwalletrpc.AssetWallet.QueryInternalKey:output_type -> assetwalletrpc.QueryInternalKeyResponse + 17, // 32: assetwalletrpc.AssetWallet.QueryScriptKey:output_type -> assetwalletrpc.QueryScriptKeyResponse + 19, // 33: assetwalletrpc.AssetWallet.ProveAssetOwnership:output_type -> assetwalletrpc.ProveAssetOwnershipResponse + 21, // 34: assetwalletrpc.AssetWallet.VerifyAssetOwnership:output_type -> assetwalletrpc.VerifyAssetOwnershipResponse + 23, // 35: assetwalletrpc.AssetWallet.RemoveUTXOLease:output_type -> assetwalletrpc.RemoveUTXOLeaseResponse + 24, // [24:36] is the sub-list for method output_type + 12, // [12:24] 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 } func init() { file_assetwalletrpc_assetwallet_proto_init() } @@ -1847,7 +1964,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextInternalKeyRequest); i { + switch v := v.(*PublishAndLogRequest); i { case 0: return &v.state case 1: @@ -1859,7 +1976,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextInternalKeyResponse); i { + switch v := v.(*NextInternalKeyRequest); i { case 0: return &v.state case 1: @@ -1871,7 +1988,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextScriptKeyRequest); i { + switch v := v.(*NextInternalKeyResponse); i { case 0: return &v.state case 1: @@ -1883,7 +2000,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NextScriptKeyResponse); i { + switch v := v.(*NextScriptKeyRequest); i { case 0: return &v.state case 1: @@ -1895,7 +2012,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryInternalKeyRequest); i { + switch v := v.(*NextScriptKeyResponse); i { case 0: return &v.state case 1: @@ -1907,7 +2024,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryInternalKeyResponse); i { + switch v := v.(*QueryInternalKeyRequest); i { case 0: return &v.state case 1: @@ -1919,7 +2036,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryScriptKeyRequest); i { + switch v := v.(*QueryInternalKeyResponse); i { case 0: return &v.state case 1: @@ -1931,7 +2048,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryScriptKeyResponse); i { + switch v := v.(*QueryScriptKeyRequest); i { case 0: return &v.state case 1: @@ -1943,7 +2060,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProveAssetOwnershipRequest); i { + switch v := v.(*QueryScriptKeyResponse); i { case 0: return &v.state case 1: @@ -1955,7 +2072,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProveAssetOwnershipResponse); i { + switch v := v.(*ProveAssetOwnershipRequest); i { case 0: return &v.state case 1: @@ -1967,7 +2084,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyAssetOwnershipRequest); i { + switch v := v.(*ProveAssetOwnershipResponse); i { case 0: return &v.state case 1: @@ -1979,7 +2096,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyAssetOwnershipResponse); i { + switch v := v.(*VerifyAssetOwnershipRequest); i { case 0: return &v.state case 1: @@ -1991,7 +2108,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveUTXOLeaseRequest); i { + switch v := v.(*VerifyAssetOwnershipResponse); i { case 0: return &v.state case 1: @@ -2003,6 +2120,18 @@ func file_assetwalletrpc_assetwallet_proto_init() { } } file_assetwalletrpc_assetwallet_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveUTXOLeaseRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_assetwalletrpc_assetwallet_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveUTXOLeaseResponse); i { case 0: return &v.state @@ -2031,7 +2160,7 @@ func file_assetwalletrpc_assetwallet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_assetwalletrpc_assetwallet_proto_rawDesc, NumEnums: 0, - NumMessages: 24, + NumMessages: 25, NumExtensions: 0, NumServices: 1, }, diff --git a/taprpc/assetwalletrpc/assetwallet.pb.gw.go b/taprpc/assetwalletrpc/assetwallet.pb.gw.go index c2a55bb1e..3ff3d186a 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.gw.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.gw.go @@ -167,6 +167,40 @@ func local_request_AssetWallet_CommitVirtualPsbts_0(ctx context.Context, marshal } +func request_AssetWallet_PublishAndLogTransfer_0(ctx context.Context, marshaler runtime.Marshaler, client AssetWalletClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq PublishAndLogRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.PublishAndLogTransfer(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AssetWallet_PublishAndLogTransfer_0(ctx context.Context, marshaler runtime.Marshaler, server AssetWalletServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq PublishAndLogRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.PublishAndLogTransfer(ctx, &protoReq) + return msg, metadata, err + +} + func request_AssetWallet_NextInternalKey_0(ctx context.Context, marshaler runtime.Marshaler, client AssetWalletClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq NextInternalKeyRequest var metadata runtime.ServerMetadata @@ -539,6 +573,29 @@ func RegisterAssetWalletHandlerServer(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_AssetWallet_PublishAndLogTransfer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/assetwalletrpc.AssetWallet/PublishAndLogTransfer", runtime.WithHTTPPathPattern("/v1/taproot-assets/wallet/virtual-psbt/log-transfer")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AssetWallet_PublishAndLogTransfer_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AssetWallet_PublishAndLogTransfer_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AssetWallet_NextInternalKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -821,6 +878,26 @@ func RegisterAssetWalletHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_AssetWallet_PublishAndLogTransfer_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/assetwalletrpc.AssetWallet/PublishAndLogTransfer", runtime.WithHTTPPathPattern("/v1/taproot-assets/wallet/virtual-psbt/log-transfer")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AssetWallet_PublishAndLogTransfer_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AssetWallet_PublishAndLogTransfer_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AssetWallet_NextInternalKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -973,6 +1050,8 @@ var ( pattern_AssetWallet_CommitVirtualPsbts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "virtual-psbt", "commit"}, "")) + pattern_AssetWallet_PublishAndLogTransfer_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "virtual-psbt", "log-transfer"}, "")) + pattern_AssetWallet_NextInternalKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "internal-key", "next"}, "")) pattern_AssetWallet_NextScriptKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "wallet", "script-key", "next"}, "")) @@ -997,6 +1076,8 @@ var ( forward_AssetWallet_CommitVirtualPsbts_0 = runtime.ForwardResponseMessage + forward_AssetWallet_PublishAndLogTransfer_0 = runtime.ForwardResponseMessage + forward_AssetWallet_NextInternalKey_0 = runtime.ForwardResponseMessage forward_AssetWallet_NextScriptKey_0 = runtime.ForwardResponseMessage diff --git a/taprpc/assetwalletrpc/assetwallet.pb.json.go b/taprpc/assetwalletrpc/assetwallet.pb.json.go index 34ee84be4..725df16c6 100644 --- a/taprpc/assetwalletrpc/assetwallet.pb.json.go +++ b/taprpc/assetwalletrpc/assetwallet.pb.json.go @@ -121,6 +121,31 @@ func RegisterAssetWalletJSONCallbacks(registry map[string]func(ctx context.Conte callback(string(respBytes), nil) } + registry["assetwalletrpc.AssetWallet.PublishAndLogTransfer"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &PublishAndLogRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewAssetWalletClient(conn) + resp, err := client.PublishAndLogTransfer(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + registry["assetwalletrpc.AssetWallet.NextInternalKey"] = func(ctx context.Context, conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { diff --git a/taprpc/assetwalletrpc/assetwallet.proto b/taprpc/assetwalletrpc/assetwallet.proto index 9e1fe341a..59e58dc43 100644 --- a/taprpc/assetwalletrpc/assetwallet.proto +++ b/taprpc/assetwalletrpc/assetwallet.proto @@ -23,7 +23,12 @@ service AssetWallet { /* AnchorVirtualPsbts merges and then commits multiple virtual transactions in - a single BTC level anchor transaction. + a single BTC level anchor transaction. This RPC should be used if the BTC + level anchor transaction of the assets to be spent are encumbered by a + normal key and don't require any special spending conditions. For any custom + spending conditions on the BTC level, the two RPCs CommitVirtualPsbts and + PublishAndLogTransfer should be used instead (which in combination do the + same as this RPC but allow for more flexibility). TODO(guggero): Actually implement accepting and merging multiple transactions. @@ -40,6 +45,15 @@ service AssetWallet { rpc CommitVirtualPsbts (CommitVirtualPsbtsRequest) returns (CommitVirtualPsbtsResponse); + /* + PublishAndLogTransfer accepts a fully committed and signed anchor + transaction and publishes it to the Bitcoin network. It also logs the + transfer of the given active and passive assets in the database and ships + any outgoing proofs to the counterparties. + */ + rpc PublishAndLogTransfer (PublishAndLogRequest) + returns (taprpc.SendAssetResponse); + /* NextInternalKey derives the next internal key for the given key family and stores it as an internal key in the database to make sure it is identified @@ -305,6 +319,40 @@ message CommitVirtualPsbtsResponse { repeated taprpc.OutPoint lnd_locked_utxos = 6; } +message PublishAndLogRequest { + /* + The funded BTC level anchor transaction with all outputs updated to commit + to the virtual transactions given. The transaction is ready to be signed, + unless some of the asset inputs don't belong to this daemon, in which case + the anchor input derivation info must be added to those inputs first. + */ + bytes anchor_psbt = 1; + + /* + The updated virtual transactions that contain the state transition proofs + of being committed to the BTC level anchor transaction above. + */ + repeated bytes virtual_psbts = 2; + + /* + The updated passive virtual transactions that contain the state transition + proofs of being committed to the BTC level anchor transaction above. + */ + repeated bytes passive_asset_psbts = 3; + + /* + The index of the (added) change output or -1 if no change was left over. + */ + int32 change_output_index = 4; + + /* + The list of UTXO lock leases that were acquired for the inputs in the funded + PSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked, + inputs that were already present in the PSBT are not locked. + */ + repeated taprpc.OutPoint lnd_locked_utxos = 5; +} + message NextInternalKeyRequest { uint32 key_family = 1; } diff --git a/taprpc/assetwalletrpc/assetwallet.swagger.json b/taprpc/assetwalletrpc/assetwallet.swagger.json index 687c5a97e..823271994 100644 --- a/taprpc/assetwalletrpc/assetwallet.swagger.json +++ b/taprpc/assetwalletrpc/assetwallet.swagger.json @@ -249,7 +249,7 @@ }, "/v1/taproot-assets/wallet/virtual-psbt/anchor": { "post": { - "summary": "AnchorVirtualPsbts merges and then commits multiple virtual transactions in\na single BTC level anchor transaction.", + "summary": "AnchorVirtualPsbts merges and then commits multiple virtual transactions in\na single BTC level anchor transaction. This RPC should be used if the BTC\nlevel anchor transaction of the assets to be spent are encumbered by a\nnormal key and don't require any special spending conditions. For any custom\nspending conditions on the BTC level, the two RPCs CommitVirtualPsbts and\nPublishAndLogTransfer should be used instead (which in combination do the\nsame as this RPC but allow for more flexibility).", "description": "TODO(guggero): Actually implement accepting and merging multiple\ntransactions.", "operationId": "AssetWallet_AnchorVirtualPsbts", "responses": { @@ -347,6 +347,39 @@ ] } }, + "/v1/taproot-assets/wallet/virtual-psbt/log-transfer": { + "post": { + "summary": "PublishAndLogTransfer accepts a fully committed and signed anchor\ntransaction and publishes it to the Bitcoin network. It also logs the\ntransfer of the given active and passive assets in the database and ships\nany outgoing proofs to the counterparties.", + "operationId": "AssetWallet_PublishAndLogTransfer", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/taprpcSendAssetResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/assetwalletrpcPublishAndLogRequest" + } + } + ], + "tags": [ + "AssetWallet" + ] + } + }, "/v1/taproot-assets/wallet/virtual-psbt/sign": { "post": { "summary": "SignVirtualPsbt signs the inputs of a virtual transaction and prepares the\ncommitments of the inputs and outputs.", @@ -593,6 +626,44 @@ } } }, + "assetwalletrpcPublishAndLogRequest": { + "type": "object", + "properties": { + "anchor_psbt": { + "type": "string", + "format": "byte", + "description": "The funded BTC level anchor transaction with all outputs updated to commit\nto the virtual transactions given. The transaction is ready to be signed,\nunless some of the asset inputs don't belong to this daemon, in which case\nthe anchor input derivation info must be added to those inputs first." + }, + "virtual_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The updated virtual transactions that contain the state transition proofs\nof being committed to the BTC level anchor transaction above." + }, + "passive_asset_psbts": { + "type": "array", + "items": { + "type": "string", + "format": "byte" + }, + "description": "The updated passive virtual transactions that contain the state transition\nproofs of being committed to the BTC level anchor transaction above." + }, + "change_output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the (added) change output or -1 if no change was left over." + }, + "lnd_locked_utxos": { + "type": "array", + "items": { + "$ref": "#/definitions/taprpcOutPoint" + }, + "description": "The list of UTXO lock leases that were acquired for the inputs in the funded\nPSBT packet from lnd. Only inputs added to the PSBT by this RPC are locked,\ninputs that were already present in the PSBT are not locked." + } + } + }, "assetwalletrpcQueryInternalKeyResponse": { "type": "object", "properties": { diff --git a/taprpc/assetwalletrpc/assetwallet.yaml b/taprpc/assetwalletrpc/assetwallet.yaml index c50402561..c6d7c6028 100644 --- a/taprpc/assetwalletrpc/assetwallet.yaml +++ b/taprpc/assetwalletrpc/assetwallet.yaml @@ -18,6 +18,10 @@ http: - selector: assetwalletrpc.AssetWallet.CommitVirtualPsbts post: "/v1/taproot-assets/wallet/virtual-psbt/commit" body: "*" + + - selector: assetwalletrpc.AssetWallet.PublishAndLogTransfer + post: "/v1/taproot-assets/wallet/virtual-psbt/log-transfer" + body: "*" - selector: assetwalletrpc.AssetWallet.NextInternalKey post: "/v1/taproot-assets/wallet/internal-key/next" diff --git a/taprpc/assetwalletrpc/assetwallet_grpc.pb.go b/taprpc/assetwalletrpc/assetwallet_grpc.pb.go index 3d117d485..c9b36e5b7 100644 --- a/taprpc/assetwalletrpc/assetwallet_grpc.pb.go +++ b/taprpc/assetwalletrpc/assetwallet_grpc.pb.go @@ -26,7 +26,12 @@ type AssetWalletClient interface { // commitments of the inputs and outputs. SignVirtualPsbt(ctx context.Context, in *SignVirtualPsbtRequest, opts ...grpc.CallOption) (*SignVirtualPsbtResponse, error) // AnchorVirtualPsbts merges and then commits multiple virtual transactions in - // a single BTC level anchor transaction. + // a single BTC level anchor transaction. This RPC should be used if the BTC + // level anchor transaction of the assets to be spent are encumbered by a + // normal key and don't require any special spending conditions. For any custom + // spending conditions on the BTC level, the two RPCs CommitVirtualPsbts and + // PublishAndLogTransfer should be used instead (which in combination do the + // same as this RPC but allow for more flexibility). // // TODO(guggero): Actually implement accepting and merging multiple // transactions. @@ -36,6 +41,11 @@ type AssetWalletClient interface { // In addition, the BTC level anchor transaction is funded and prepared up to // the point where it is ready to be signed. CommitVirtualPsbts(ctx context.Context, in *CommitVirtualPsbtsRequest, opts ...grpc.CallOption) (*CommitVirtualPsbtsResponse, error) + // PublishAndLogTransfer accepts a fully committed and signed anchor + // transaction and publishes it to the Bitcoin network. It also logs the + // transfer of the given active and passive assets in the database and ships + // any outgoing proofs to the counterparties. + PublishAndLogTransfer(ctx context.Context, in *PublishAndLogRequest, opts ...grpc.CallOption) (*taprpc.SendAssetResponse, error) // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can @@ -111,6 +121,15 @@ func (c *assetWalletClient) CommitVirtualPsbts(ctx context.Context, in *CommitVi return out, nil } +func (c *assetWalletClient) PublishAndLogTransfer(ctx context.Context, in *PublishAndLogRequest, opts ...grpc.CallOption) (*taprpc.SendAssetResponse, error) { + out := new(taprpc.SendAssetResponse) + err := c.cc.Invoke(ctx, "/assetwalletrpc.AssetWallet/PublishAndLogTransfer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *assetWalletClient) NextInternalKey(ctx context.Context, in *NextInternalKeyRequest, opts ...grpc.CallOption) (*NextInternalKeyResponse, error) { out := new(NextInternalKeyResponse) err := c.cc.Invoke(ctx, "/assetwalletrpc.AssetWallet/NextInternalKey", in, out, opts...) @@ -185,7 +204,12 @@ type AssetWalletServer interface { // commitments of the inputs and outputs. SignVirtualPsbt(context.Context, *SignVirtualPsbtRequest) (*SignVirtualPsbtResponse, error) // AnchorVirtualPsbts merges and then commits multiple virtual transactions in - // a single BTC level anchor transaction. + // a single BTC level anchor transaction. This RPC should be used if the BTC + // level anchor transaction of the assets to be spent are encumbered by a + // normal key and don't require any special spending conditions. For any custom + // spending conditions on the BTC level, the two RPCs CommitVirtualPsbts and + // PublishAndLogTransfer should be used instead (which in combination do the + // same as this RPC but allow for more flexibility). // // TODO(guggero): Actually implement accepting and merging multiple // transactions. @@ -195,6 +219,11 @@ type AssetWalletServer interface { // In addition, the BTC level anchor transaction is funded and prepared up to // the point where it is ready to be signed. CommitVirtualPsbts(context.Context, *CommitVirtualPsbtsRequest) (*CommitVirtualPsbtsResponse, error) + // PublishAndLogTransfer accepts a fully committed and signed anchor + // transaction and publishes it to the Bitcoin network. It also logs the + // transfer of the given active and passive assets in the database and ships + // any outgoing proofs to the counterparties. + PublishAndLogTransfer(context.Context, *PublishAndLogRequest) (*taprpc.SendAssetResponse, error) // NextInternalKey derives the next internal key for the given key family and // stores it as an internal key in the database to make sure it is identified // as a local key later on when importing proofs. While an internal key can @@ -243,6 +272,9 @@ func (UnimplementedAssetWalletServer) AnchorVirtualPsbts(context.Context, *Ancho func (UnimplementedAssetWalletServer) CommitVirtualPsbts(context.Context, *CommitVirtualPsbtsRequest) (*CommitVirtualPsbtsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CommitVirtualPsbts not implemented") } +func (UnimplementedAssetWalletServer) PublishAndLogTransfer(context.Context, *PublishAndLogRequest) (*taprpc.SendAssetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PublishAndLogTransfer not implemented") +} func (UnimplementedAssetWalletServer) NextInternalKey(context.Context, *NextInternalKeyRequest) (*NextInternalKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NextInternalKey not implemented") } @@ -349,6 +381,24 @@ func _AssetWallet_CommitVirtualPsbts_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _AssetWallet_PublishAndLogTransfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PublishAndLogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AssetWalletServer).PublishAndLogTransfer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/assetwalletrpc.AssetWallet/PublishAndLogTransfer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AssetWalletServer).PublishAndLogTransfer(ctx, req.(*PublishAndLogRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AssetWallet_NextInternalKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(NextInternalKeyRequest) if err := dec(in); err != nil { @@ -498,6 +548,10 @@ var AssetWallet_ServiceDesc = grpc.ServiceDesc{ MethodName: "CommitVirtualPsbts", Handler: _AssetWallet_CommitVirtualPsbts_Handler, }, + { + MethodName: "PublishAndLogTransfer", + Handler: _AssetWallet_PublishAndLogTransfer_Handler, + }, { MethodName: "NextInternalKey", Handler: _AssetWallet_NextInternalKey_Handler, From b582d49d96949b826f4865724031b0769efefc32 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:06 +0100 Subject: [PATCH 16/20] tapfreighter: turn on full validation before logging Now that we have a full validation logic for making sure all assets of the input anchors are properly accounted for and re-created in appropriate outputs, we want to turn on that validation during the normal send logic of the freighter as well to catch potential bugs before publishing any on-chain transaction. --- tapfreighter/chain_porter.go | 20 ++++++++++++++++++++ tapfreighter/parcel.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index f9d0f5ced..b2e54bf88 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "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/proof" @@ -930,6 +931,25 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { // finalization. currentPkg.AnchorTx = anchorTx + // For the final validation, we need to also supply the assets + // that were committed to the input tree but pruned because they + // were burns or tombstones. + prunedAssets := make(map[wire.OutPoint][]*asset.Asset) + for prevID := range currentPkg.InputCommitments { + c := currentPkg.InputCommitments[prevID] + prunedAssets[prevID.OutPoint] = append( + prunedAssets[prevID.OutPoint], + tapsend.ExtractUnSpendable(c)..., + ) + } + + // Make sure everything is ready for the finalization. + err = currentPkg.validateReadyForPublish(prunedAssets) + if err != nil { + return nil, fmt.Errorf("unable to validate send "+ + "package: %w", err) + } + currentPkg.SendState = SendStateLogCommit return ¤tPkg, nil diff --git a/tapfreighter/parcel.go b/tapfreighter/parcel.go index e277af411..f4892fa6f 100644 --- a/tapfreighter/parcel.go +++ b/tapfreighter/parcel.go @@ -652,3 +652,36 @@ func (s *sendPackage) deliverTxBroadcastResp() { s.Parcel.kit().respChan <- s.OutboundPkg } + +// validateReadyForPublish checks that the virtual packets are ready to be +// published to the network. The pruned assets (assets that were present in the +// input TAP tree but are not re-created by any of the active or passive packets +// because they were burns or tombstones) are required to be supplied in order +// for the full input TAP tree to be reconstructed and validated. +func (s *sendPackage) validateReadyForPublish( + prunedAssets map[wire.OutPoint][]*asset.Asset) error { + + // At this point all the virtual packet inputs and outputs should fully + // match the BTC level anchor transaction. Version 0 assets should also + // be signed now. + allPackets := append([]*tappsbt.VPacket{}, s.VirtualPackets...) + allPackets = append(allPackets, s.PassiveAssets...) + if err := tapsend.AssertInputAnchorsEqual(allPackets); err != nil { + return fmt.Errorf("input anchors don't match: %w", err) + } + if err := tapsend.AssertOutputAnchorsEqual(allPackets); err != nil { + return fmt.Errorf("output anchors don't match: %w", err) + } + + btcPkt := s.AnchorTx.FundedPsbt.Pkt + err := tapsend.ValidateAnchorInputs(btcPkt, allPackets, prunedAssets) + if err != nil { + return fmt.Errorf("error validating anchor inputs: %w", err) + } + err = tapsend.ValidateAnchorOutputs(btcPkt, allPackets, true) + if err != nil { + return fmt.Errorf("error validating anchor outputs: %w", err) + } + + return nil +} From 23bc1756aa4585683ed5ec38da7851dafe3da6c7 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:07 +0100 Subject: [PATCH 17/20] multi: unlock lnd's outputs on permanent failure In case we are sure we can't confirm or even publish an on-chain transaction, we release the UTXOs that were locked in lnd's wallet to avoid locking up balance with re-tries. --- tapfreighter/chain_porter.go | 54 ++++++++++++++++++++++++++++++++---- tapgarden/interface.go | 9 +++--- tapgarden/mock.go | 4 ++- wallet_anchor.go | 24 ++++++++++++++-- 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index b2e54bf88..44da5e3ba 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -21,6 +21,7 @@ import ( "github.com/lightninglabs/taproot-assets/tapscript" "github.com/lightninglabs/taproot-assets/tapsend" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -859,8 +860,6 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { defer cancel() // Submit the template PSBT to the wallet for funding. - // - // TODO(roasbeef): unlock the input UTXOs if things fail var ( feeRate chainfee.SatPerKWeight err error @@ -946,6 +945,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { // Make sure everything is ready for the finalization. err = currentPkg.validateReadyForPublish(prunedAssets) if err != nil { + p.unlockInputs(ctx, ¤tPkg) + return nil, fmt.Errorf("unable to validate send "+ "package: %w", err) } @@ -964,6 +965,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { defer cancel() currentHeight, err := p.cfg.ChainBridge.CurrentHeight(ctx) if err != nil { + p.unlockInputs(ctx, ¤tPkg) + return nil, fmt.Errorf("unable to get current height: "+ "%w", err) } @@ -984,6 +987,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { isLocalKey, ) if err != nil { + p.unlockInputs(ctx, ¤tPkg) + return nil, fmt.Errorf("unable to prepare parcel for "+ "storage: %w", err) } @@ -1000,6 +1005,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { time.Now().Add(defaultBroadcastCoinLeaseDuration), ) if err != nil { + p.unlockInputs(ctx, ¤tPkg) + return nil, fmt.Errorf("unable to write send pkg to "+ "disk: %w", err) } @@ -1018,20 +1025,41 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { err := p.importLocalAddresses(ctx, currentPkg.OutboundPkg) if err != nil { + p.unlockInputs(ctx, ¤tPkg) + return nil, fmt.Errorf("unable to import local "+ "addresses: %w", err) } - log.Infof("Broadcasting new transfer tx, txid=%v", - currentPkg.OutboundPkg.AnchorTx.TxHash()) + txHash := currentPkg.OutboundPkg.AnchorTx.TxHash() + log.Infof("Broadcasting new transfer tx, txid=%v", txHash) // With the public key imported, we can now broadcast to the // network. err = p.cfg.ChainBridge.PublishTransaction( ctx, currentPkg.OutboundPkg.AnchorTx, ) - if err != nil { - return nil, err + switch { + case errors.Is(err, lnwallet.ErrDoubleSpend): + // A double spend error means the transaction will never + // make it into the mempool or chain, so we'll never be + // able to confirm it. At this point we should probably + // put the transfer in a failed state and not re-try on + // next startup... But since we don't have that state + // yet, we just return an error here. But what we can do + // is release any fee sponsoring inputs we selected from + // lnd's wallet to avoid locking up balance. + // + // TODO(guggero): Put this transfer into a failed state + // and don't retry on next startup. + p.unlockInputs(ctx, ¤tPkg) + + return nil, fmt.Errorf("unable to broadcast "+ + "transaction %v: %w", txHash, err) + + case err != nil: + return nil, fmt.Errorf("unable to broadcast "+ + "transaction %v: %w", txHash, err) } // With the transaction broadcast, we'll deliver a @@ -1082,6 +1110,20 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { } } +// unlockInputs unlocks the inputs that were locked for the given package. +func (p *ChainPorter) unlockInputs(ctx context.Context, pkg *sendPackage) { + if pkg == nil || pkg.AnchorTx == nil || pkg.AnchorTx.FundedPsbt == nil { + return + } + + for _, op := range pkg.AnchorTx.FundedPsbt.LockedUTXOs { + err := p.cfg.Wallet.UnlockInput(ctx, op) + if err != nil { + log.Warnf("Unable to unlock input %v: %v", op, err) + } + } +} + // logPacket logs the virtual packet to the debug log. func logPacket(vPkt *tappsbt.VPacket, action string) { firstRecipient, err := vPkt.FirstNonSplitRootOutput() diff --git a/tapgarden/interface.go b/tapgarden/interface.go index ceb148713..b1cf3e262 100644 --- a/tapgarden/interface.go +++ b/tapgarden/interface.go @@ -306,11 +306,12 @@ type WalletAnchor interface { // ImportTaprootOutput imports a new public key into the wallet, as a // P2TR output. - ImportTaprootOutput(context.Context, *btcec.PublicKey) (btcutil.Address, error) + ImportTaprootOutput(context.Context, *btcec.PublicKey) (btcutil.Address, + error) - // UnlockInput unlocks the set of target inputs after a batch is - // abandoned. - UnlockInput(context.Context) error + // UnlockInput unlocks the set of target inputs after a batch or send + // transaction is abandoned. + UnlockInput(context.Context, wire.OutPoint) error // ListUnspentImportScripts lists all UTXOs of the imported Taproot // scripts. diff --git a/tapgarden/mock.go b/tapgarden/mock.go index 2f06cb6a2..d98c93271 100644 --- a/tapgarden/mock.go +++ b/tapgarden/mock.go @@ -173,7 +173,9 @@ func (m *MockWalletAnchor) ImportTaprootOutput(ctx context.Context, ) } -func (m *MockWalletAnchor) UnlockInput(_ context.Context) error { +// UnlockInput unlocks the set of target inputs after a batch or send +// transaction is abandoned. +func (m *MockWalletAnchor) UnlockInput(context.Context, wire.OutPoint) error { return nil } diff --git a/wallet_anchor.go b/wallet_anchor.go index ef1003415..2c92e01f6 100644 --- a/wallet_anchor.go +++ b/wallet_anchor.go @@ -129,8 +129,28 @@ func (l *LndRpcWalletAnchor) ImportTaprootOutput(ctx context.Context, return addr, nil } -// UnlockInput unlocks the set of target inputs after a batch is abandoned. -func (l *LndRpcWalletAnchor) UnlockInput(ctx context.Context) error { +// UnlockInput unlocks the set of target inputs after a batch or send +// transaction is abandoned. +func (l *LndRpcWalletAnchor) UnlockInput(ctx context.Context, + op wire.OutPoint) error { + + leases, err := l.lnd.WalletKit.ListLeases(ctx) + if err != nil { + return fmt.Errorf("error listing existing leases: %w", err) + } + + for _, lease := range leases { + if lease.Outpoint == op { + err = l.lnd.WalletKit.ReleaseOutput( + ctx, lease.LockID, lease.Outpoint, + ) + if err != nil { + return fmt.Errorf("error releasing lease: %w", + err) + } + } + } + return nil } From 16c7c3f6c9229171ce731b834e046636db541f1d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:08 +0100 Subject: [PATCH 18/20] itest: add external commitment via PSBT itest --- itest/psbt_test.go | 337 +++++++++++++++++++++++++++++++++++++ itest/test_harness.go | 7 + itest/test_list_on_test.go | 4 + 3 files changed, 348 insertions(+) diff --git a/itest/psbt_test.go b/itest/psbt_test.go index 3b9634306..8615dbb51 100644 --- a/itest/psbt_test.go +++ b/itest/psbt_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/davecgh/go-spew/spew" tap "github.com/lightninglabs/taproot-assets" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" @@ -23,10 +24,17 @@ import ( "github.com/lightninglabs/taproot-assets/tapsend" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) +var ( + feeRateSatPerKVByte chainfee.SatPerKVByte = 2000 +) + // testPsbtScriptHashLockSend tests that we can properly send assets with a hash // lock back and forth between nodes with the use of PSBTs. func testPsbtScriptHashLockSend(t *harnessTest) { @@ -1609,6 +1617,178 @@ func testPsbtSighashNoneInvalid(t *harnessTest) { require.ErrorContains(t.t, err, "unable to verify proof") } +// testPsbtExternalCommit tests the ability to fully customize the BTC level of +// an asset transfer using a PSBT. This exercises the CommitVirtualPsbts and +// PublishAndLogTransfer RPCs. The test case moves some assets into an output +// that has a hash lock tapscript. +func testPsbtExternalCommit(t *harnessTest) { + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + // We mint some grouped assets to use in the test. These assets are + // minted on the default tapd instance that is always created in the + // integration test (connected to lnd "Alice"). + assets := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, t.tapd, + []*mintrpc.MintAssetRequest{ + issuableAssets[0], + // Our "passive" asset. + { + Asset: &mintrpc.MintAsset{ + AssetType: taprpc.AssetType_NORMAL, + Name: "itestbuxx-passive", + AssetMeta: &taprpc.AssetMeta{ + Data: []byte("some metadata"), + }, + Amount: 123, + }, + }, + }, + ) + targetAsset := assets[0] + + var ( + targetAssetGenesis = targetAsset.AssetGenesis + aliceTapd = t.tapd + aliceLnd = t.lndHarness.Alice + bobLnd = t.lndHarness.Bob + ) + + // We create a second tapd node that will be used to simulate a second + // party in the test. This tapd node is connected to lnd "Bob". + bobTapd := setupTapdHarness(t.t, t, bobLnd, t.universeServer) + defer func() { + require.NoError(t.t, bobTapd.stop(!*noDelete)) + }() + + // And now we prepare the hash lock script for the BTC level. + btcTapLeaf := test.ScriptHashLock(t.t, []byte("hash locks are cool")) + + // The actual internal key of the BTC level Taproot output will be the + // provably un-spendable NUMS key. + siblingPreimage, err := commitment.NewPreimageFromLeaf(btcTapLeaf) + require.NoError(t.t, err) + siblingPreimageBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( + siblingPreimage, + ) + require.NoError(t.t, err) + + // We now have everything we need to create the TAP address to receive + // the multisig secured assets. The recipient of the assets is going to + // be the Bob node, but the custody will be shared between Alice and Bob + // on both levels. + const assetsToSend = 1000 + bobAddr, err := bobTapd.NewAddr(ctxt, &taprpc.NewAddrRequest{ + AssetId: targetAssetGenesis.AssetId, + Amt: assetsToSend, + TapscriptSibling: siblingPreimageBytes, + }) + require.NoError(t.t, err) + + // Now we can create our virtual transaction and ask Alice's tapd to + // fund it. + recipients := map[string]uint64{ + bobAddr.Encoded: bobAddr.Amount, + } + fundResp, err := aliceTapd.FundVirtualPsbt( + ctxt, &wrpc.FundVirtualPsbtRequest{ + Template: &wrpc.FundVirtualPsbtRequest_Raw{ + Raw: &wrpc.TxTemplate{ + Recipients: recipients, + }, + }, + }, + ) + require.NoError(t.t, err) + + // We expect a passive asset to be returned. + require.Equal(t.t, 1, len(fundResp.PassiveAssetPsbts)) + + // With the virtual transaction funded, we can simply sign it and the + // passive assets. + activeAsset, err := tappsbt.Decode(fundResp.FundedPsbt) + require.NoError(t.t, err) + + activeAssets := []*tappsbt.VPacket{ + signVirtualPacket(t.t, aliceTapd, activeAsset), + } + + passiveAssets := make( + []*tappsbt.VPacket, len(fundResp.PassiveAssetPsbts), + ) + for idx := range fundResp.PassiveAssetPsbts { + passiveAsset, err := tappsbt.Decode( + fundResp.PassiveAssetPsbts[idx], + ) + require.NoError(t.t, err) + + passiveAssets[idx] = signVirtualPacket( + t.t, aliceTapd, passiveAsset, + ) + } + + allPackets := append([]*tappsbt.VPacket{}, activeAssets...) + allPackets = append(allPackets, passiveAssets...) + btcPacket, err := tapsend.PrepareAnchoringTemplate(allPackets) + require.NoError(t.t, err) + + var commitResp *wrpc.CommitVirtualPsbtsResponse + btcPacket, activeAssets, passiveAssets, commitResp = commitVirtualPsbts( + t.t, aliceTapd, btcPacket, activeAssets, passiveAssets, -1, + ) + + t.Logf("Committed transaction: %v", toJSON(t.t, commitResp)) + + btcPacket = signPacket(t.t, aliceLnd, btcPacket) + btcPacket = finalizePacket(t.t, aliceLnd, btcPacket) + sendResp := logAndPublish( + t.t, aliceTapd, btcPacket, activeAssets, passiveAssets, + commitResp, + ) + + expectedAmounts := []uint64{ + targetAsset.Amount - assetsToSend, assetsToSend, + } + ConfirmAndAssertOutboundTransferWithOutputs( + t.t, t.lndHarness.Miner.Client, aliceTapd, + sendResp, targetAssetGenesis.AssetId, expectedAmounts, + 0, 1, len(expectedAmounts), + ) + + // And now the event should be completed on both sides. + AssertAddrEvent(t.t, bobTapd, bobAddr, 1, statusCompleted) + AssertNonInteractiveRecvComplete(t.t, bobTapd, 1) + AssertBalanceByID( + t.t, bobTapd, targetAssetGenesis.AssetId, assetsToSend, + ) +} + +func signVirtualPacket(t *testing.T, tapd *tapdHarness, + packet *tappsbt.VPacket) *tappsbt.VPacket { + + ctx := context.Background() + ctxt, cancel := context.WithTimeout(ctx, defaultTimeout) + defer cancel() + + rawPacket, err := tappsbt.Encode(packet) + require.NoError(t, err) + + signResp, err := tapd.SignVirtualPsbt( + ctxt, &wrpc.SignVirtualPsbtRequest{ + FundedPsbt: rawPacket, + }, + ) + require.NoError(t, err) + + require.NotEmpty(t, signResp.SignedInputs) + + parsedPacket, err := tappsbt.Decode(signResp.SignedPsbt) + require.NoError(t, err) + + return parsedPacket +} + func deriveKeys(t *testing.T, tapd *tapdHarness) (asset.ScriptKey, keychain.KeyDescriptor) { @@ -1707,3 +1887,160 @@ func sendAssetAndAssert(ctx context.Context, t *harnessTest, alice, // appear in that RPC output). AssertNonInteractiveRecvComplete(t.t, bob, numInTransfers) } + +func commitVirtualPsbts(t *testing.T, funder *tapdHarness, packet *psbt.Packet, + activePackets []*tappsbt.VPacket, passivePackets []*tappsbt.VPacket, + changeOutputIndex int32) (*psbt.Packet, []*tappsbt.VPacket, + []*tappsbt.VPacket, *wrpc.CommitVirtualPsbtsResponse) { + + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + t.Logf("Funding packet: %v\n", spew.Sdump(packet)) + + var buf bytes.Buffer + err := packet.Serialize(&buf) + require.NoError(t, err) + + request := &wrpc.CommitVirtualPsbtsRequest{ + AnchorPsbt: buf.Bytes(), + Fees: &wrpc.CommitVirtualPsbtsRequest_SatPerVbyte{ + SatPerVbyte: uint64(feeRateSatPerKVByte / 1000), + }, + } + + type existingIndex = wrpc.CommitVirtualPsbtsRequest_ExistingOutputIndex + if changeOutputIndex < 0 { + request.AnchorChangeOutput = &wrpc.CommitVirtualPsbtsRequest_Add{ + Add: true, + } + } else { + request.AnchorChangeOutput = &existingIndex{ + ExistingOutputIndex: changeOutputIndex, + } + } + + request.VirtualPsbts = make([][]byte, len(activePackets)) + for idx := range activePackets { + request.VirtualPsbts[idx], err = tappsbt.Encode( + activePackets[idx], + ) + require.NoError(t, err) + } + request.PassiveAssetPsbts = make([][]byte, len(passivePackets)) + for idx := range passivePackets { + request.PassiveAssetPsbts[idx], err = tappsbt.Encode( + passivePackets[idx], + ) + require.NoError(t, err) + } + + // Now we can map the virtual packets to the PSBT. + commitResponse, err := funder.CommitVirtualPsbts(ctxt, request) + require.NoError(t, err) + + fundedPacket, err := psbt.NewFromRawBytes( + bytes.NewReader(commitResponse.AnchorPsbt), false, + ) + require.NoError(t, err) + + activePackets = make( + []*tappsbt.VPacket, len(commitResponse.VirtualPsbts), + ) + for idx := range commitResponse.VirtualPsbts { + activePackets[idx], err = tappsbt.Decode( + commitResponse.VirtualPsbts[idx], + ) + require.NoError(t, err) + } + + passivePackets = make( + []*tappsbt.VPacket, len(commitResponse.PassiveAssetPsbts), + ) + for idx := range commitResponse.PassiveAssetPsbts { + passivePackets[idx], err = tappsbt.Decode( + commitResponse.PassiveAssetPsbts[idx], + ) + require.NoError(t, err) + } + + return fundedPacket, activePackets, passivePackets, commitResponse +} + +func signPacket(t *testing.T, lnd *node.HarnessNode, + pkt *psbt.Packet) *psbt.Packet { + + var buf bytes.Buffer + err := pkt.Serialize(&buf) + require.NoError(t, err) + + signResp := lnd.RPC.SignPsbt(&walletrpc.SignPsbtRequest{ + FundedPsbt: buf.Bytes(), + }) + + signedPacket, err := psbt.NewFromRawBytes( + bytes.NewReader(signResp.SignedPsbt), false, + ) + require.NoError(t, err) + + return signedPacket +} + +func finalizePacket(t *testing.T, lnd *node.HarnessNode, + pkt *psbt.Packet) *psbt.Packet { + + var buf bytes.Buffer + err := pkt.Serialize(&buf) + require.NoError(t, err) + + finalizeResp := lnd.RPC.FinalizePsbt(&walletrpc.FinalizePsbtRequest{ + FundedPsbt: buf.Bytes(), + }) + + signedPacket, err := psbt.NewFromRawBytes( + bytes.NewReader(finalizeResp.SignedPsbt), false, + ) + require.NoError(t, err) + + return signedPacket +} + +func logAndPublish(t *testing.T, tapd *tapdHarness, btcPkt *psbt.Packet, + activeAssets []*tappsbt.VPacket, passiveAssets []*tappsbt.VPacket, + commitResp *wrpc.CommitVirtualPsbtsResponse) *taprpc.SendAssetResponse { + + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + var buf bytes.Buffer + err := btcPkt.Serialize(&buf) + require.NoError(t, err) + + request := &wrpc.PublishAndLogRequest{ + AnchorPsbt: buf.Bytes(), + VirtualPsbts: make([][]byte, len(activeAssets)), + PassiveAssetPsbts: make([][]byte, len(passiveAssets)), + ChangeOutputIndex: commitResp.ChangeOutputIndex, + LndLockedUtxos: commitResp.LndLockedUtxos, + } + + for idx := range activeAssets { + request.VirtualPsbts[idx], err = tappsbt.Encode( + activeAssets[idx], + ) + require.NoError(t, err) + } + for idx := range passiveAssets { + request.PassiveAssetPsbts[idx], err = tappsbt.Encode( + passiveAssets[idx], + ) + require.NoError(t, err) + } + + resp, err := tapd.PublishAndLogTransfer(ctxt, request) + require.NoError(t, err) + + return resp +} diff --git a/itest/test_harness.go b/itest/test_harness.go index 2ed3ef2b8..3d6a50c90 100644 --- a/itest/test_harness.go +++ b/itest/test_harness.go @@ -524,6 +524,13 @@ func formatProtoJSON(resp proto.Message) (string, error) { return string(jsonBytes), nil } +func toJSON(t *testing.T, resp proto.Message) string { + jsonStr, err := formatProtoJSON(resp) + require.NoError(t, err) + + return jsonStr +} + // lndKeyDescToTap converts an lnd key descriptor to a tap key descriptor. func lndKeyDescToTap(lnd keychain.KeyDescriptor) *taprpc.KeyDescriptor { return &taprpc.KeyDescriptor{ diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index 1ae2c631f..416bc4e81 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -189,6 +189,10 @@ var testCases = []*testCase{ name: "psbt sighash none invalid", test: testPsbtSighashNoneInvalid, }, + { + name: "psbt external commit", + test: testPsbtExternalCommit, + }, { name: "multi input psbt single asset id", test: testMultiInputPsbtSingleAssetID, From f7a791f5d8871b0062fb5dab04c75cdf4d808732 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:09 +0100 Subject: [PATCH 19/20] itest: reduce proof courier ack timeout Since we reduced the node startup time delay during itests in a previous commit (a2a95e78) by removing a one second wait, we can now also decrease the wait time for a receiver to send an ACK after a restart as well. This shaves off around 15 seconds of our itests. --- itest/send_test.go | 32 ++++++++++++++++++++++++++------ itest/tapd_harness.go | 2 +- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/itest/send_test.go b/itest/send_test.go index 6ab120c7c..42e2e2243 100644 --- a/itest/send_test.go +++ b/itest/send_test.go @@ -23,6 +23,7 @@ import ( var ( transferTypeSend = taprpc.ProofTransferType_PROOF_TRANSFER_TYPE_SEND + timeoutMargin = 5 * time.Second ) // testBasicSendUnidirectional tests that we can properly send assets back and @@ -60,6 +61,11 @@ func testBasicSendUnidirectional(t *harnessTest) { } timeout := 2 * defaultProofTransferReceiverAckTimeout + + // Allow for some margin for the operations that aren't pure + // waiting on the receiver ACK. + timeout += timeoutMargin + ctx, cancel := context.WithTimeout(ctxb, timeout) defer cancel() assertAssetSendNtfsEvent( @@ -163,6 +169,11 @@ func testRestartReceiverCheckBalance(t *harnessTest) { } timeout := 2 * defaultProofTransferReceiverAckTimeout + + // Allow for some margin for the operations that aren't pure + // waiting on the receiver ACK. + timeout += timeoutMargin + ctx, cancel := context.WithTimeout(ctxb, timeout) defer cancel() assertAssetSendNtfsEvent( @@ -612,8 +623,11 @@ func testReattemptFailedSendHashmailCourier(t *harnessTest) { // Context timeout scales with expected number of events. timeout := time.Duration(expectedEventCount) * defaultProofTransferReceiverAckTimeout - // Add overhead buffer to context timeout. - timeout += 5 * time.Second + + // Allow for some margin for the operations that aren't pure + // waiting on the receiver ACK. + timeout += timeoutMargin + ctx, cancel := context.WithTimeout(ctxb, timeout) defer cancel() @@ -710,8 +724,11 @@ func testReattemptFailedSendUniCourier(t *harnessTest) { // Context timeout scales with expected number of events. timeout := time.Duration(expectedEventCount) * defaultProofTransferReceiverAckTimeout - // Add overhead buffer to context timeout. - timeout += 5 * time.Second + + // Allow for some margin for the operations that aren't pure + // waiting on the receiver ACK. + timeout += timeoutMargin + ctx, cancel := context.WithTimeout(ctxb, timeout) defer cancel() @@ -872,8 +889,11 @@ func testReattemptFailedReceiveUniCourier(t *harnessTest) { // Context timeout scales with expected number of events. timeout := time.Duration(expectedEventCount) * defaultProofTransferReceiverAckTimeout - // Add overhead buffer to context timeout. - timeout += 5 * time.Second + + // Allow for some margin for the operations that aren't pure + // waiting on the receiver ACK. + timeout += timeoutMargin + ctx, cancel := context.WithTimeout(ctxb, timeout) defer cancel() diff --git a/itest/tapd_harness.go b/itest/tapd_harness.go index c6fc4c218..2e891b244 100644 --- a/itest/tapd_harness.go +++ b/itest/tapd_harness.go @@ -78,7 +78,7 @@ const ( // defaultProofTransferReceiverAckTimeout is the default itest specific // timeout we'll use for waiting for a receiver to acknowledge a proof // transfer. - defaultProofTransferReceiverAckTimeout = 5 * time.Second + defaultProofTransferReceiverAckTimeout = 500 * time.Millisecond ) // tapdHarness is a test harness that holds everything that is needed to From 8280b00500a588f0b498f3a2dea55d02d730df1a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 4 Mar 2024 12:29:10 +0100 Subject: [PATCH 20/20] itest: further reduce itest run time We fix some additional issues of long-running (>10 seconds) integration tests. --- itest/assertions.go | 4 ++-- itest/universe_federation_test.go | 4 ++-- itest/universe_pagination_test.go | 10 ++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/itest/assertions.go b/itest/assertions.go index 98abd825e..8234c54e9 100644 --- a/itest/assertions.go +++ b/itest/assertions.go @@ -1717,8 +1717,8 @@ func assertGroups(t *testing.T, client taprpc.TaprootAssetsClient, // assetRoots is a helper method that fetches all roots from a given universe // rpc by scanning for all pages. -func assetRoots(ctx context.Context, - uni unirpc.UniverseClient, pageSize int32) (*unirpc.AssetRootResponse, error) { +func assetRoots(ctx context.Context, uni unirpc.UniverseClient, + pageSize int32) (*unirpc.AssetRootResponse, error) { offset := int32(0) roots := make(map[string]*unirpc.UniverseRoot) diff --git a/itest/universe_federation_test.go b/itest/universe_federation_test.go index 69d7dd712..b4325b850 100644 --- a/itest/universe_federation_test.go +++ b/itest/universe_federation_test.go @@ -14,10 +14,10 @@ import ( // node is offline at the time of the initial sync attempt. func testMintProofRepeatFedSyncAttempt(t *harnessTest) { // Create a new minting node, without hooking it up to any existing - // Universe server. We will also set the sync ticker to 4 second, so + // Universe server. We will also set the sync ticker to 2 second, so // that we can test that the proof push sync is retried and eventually // succeeds after the fed server peer node reappears online. - syncTickerInterval := 4 * time.Second + syncTickerInterval := 2 * time.Second mintingNode := setupTapdHarness( t.t, t, t.lndHarness.Bob, nil, func(params *tapdHarnessParams) { diff --git a/itest/universe_pagination_test.go b/itest/universe_pagination_test.go index 6a718c024..39f1614c7 100644 --- a/itest/universe_pagination_test.go +++ b/itest/universe_pagination_test.go @@ -22,7 +22,7 @@ const ( // testPageSizeSmall is the page size to use when fetching data from the // universe rpc. We use a small page size to test pagination. - testPageSizeSmall = 2 + testPageSizeSmall = 10 // testGroupSize is the size of the asset group we mint in the // testUniversePaginationSimple test. @@ -30,7 +30,7 @@ const ( ) func testUniversePaginationSimple(t *harnessTest) { - mintSize := 255 + mintSize := 50 timeout := defaultWaitTimeout // If we create a second tapd instance and sync the universe state, @@ -211,10 +211,8 @@ func mintBatchAssetsTest( require.NoError(t, err) require.Eventually(t, func() bool { - return AssertUniverseStateEqual( - t, alice, bob, - ) - }, minterTimeout, time.Second) + return AssertUniverseStateEqual(t, alice, bob) + }, minterTimeout, 200*time.Millisecond) } // fetchAllLeafKeys fetches all leaf keys for a given universe ID.