lnrpc+walletrpc: add RPCs for new PSBT methods

This commit is contained in:
Oliver Gugger 2020-10-01 16:21:45 +02:00
parent f947576f33
commit 6229609be7
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
7 changed files with 1496 additions and 93 deletions

View File

@ -279,6 +279,12 @@ http:
- selector: walletrpc.WalletKit.LabelTransaction
post: "/v2/wallet/tx/label"
body: "*"
- selector: walletrpc.WalletKit.FundPsbt
post: "/v2/wallet/psbt/fund"
body: "*"
- selector: walletrpc.WalletKit.FinalizePsbt
post: "/v2/wallet/psbt/finalize"
body: "*"
# watchtowerrpc/watchtower.proto
- selector: watchtowerrpc.Watchtower.GetInfo

89
lnrpc/walletrpc/psbt.go Normal file
View File

@ -0,0 +1,89 @@
// +build walletrpc
package walletrpc
import (
"fmt"
"math"
"time"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil/psbt"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/lnwallet"
)
const (
defaultMinConf = 1
defaultMaxConf = math.MaxInt32
)
// utxoLock is a type that contains an outpoint of an UTXO and its lock lease
// information.
type utxoLock struct {
lockID wtxmgr.LockID
outpoint wire.OutPoint
expiration time.Time
}
// verifyInputsUnspent checks that all inputs are contained in the list of
// known, non-locked UTXOs given.
func verifyInputsUnspent(inputs []*wire.TxIn, utxos []*lnwallet.Utxo) error {
// TODO(guggero): Pass in UTXOs as a map to make lookup more efficient.
for idx, txIn := range inputs {
found := false
for _, u := range utxos {
if u.OutPoint == txIn.PreviousOutPoint {
found = true
break
}
}
if !found {
return fmt.Errorf("input %d not found in list of non-"+
"locked UTXO", idx)
}
}
return nil
}
// lockInputs requests a lock lease for all inputs specified in a PSBT packet
// by using the internal, static lock ID of lnd's wallet.
func lockInputs(w lnwallet.WalletController, packet *psbt.Packet) ([]*utxoLock,
error) {
locks := make([]*utxoLock, len(packet.UnsignedTx.TxIn))
for idx, rawInput := range packet.UnsignedTx.TxIn {
lock := &utxoLock{
lockID: LndInternalLockID,
outpoint: rawInput.PreviousOutPoint,
}
expiration, err := w.LeaseOutput(lock.lockID, lock.outpoint)
if err != nil {
// If we run into a problem with locking one output, we
// should try to unlock those that we successfully
// locked so far. If that fails as well, there's not
// much we can do.
for i := 0; i < idx; i++ {
op := locks[i].outpoint
if err := w.ReleaseOutput(
LndInternalLockID, op,
); err != nil {
log.Errorf("could not release the "+
"lock on %v: %v", op, err)
}
}
return nil, fmt.Errorf("could not lease a lock on "+
"UTXO: %v", err)
}
lock.expiration = expiration
locks[idx] = lock
}
return locks, nil
}

View File

@ -1368,6 +1368,400 @@ func (m *LabelTransactionResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_LabelTransactionResponse proto.InternalMessageInfo
type FundPsbtRequest struct {
// Types that are valid to be assigned to Template:
// *FundPsbtRequest_Psbt
// *FundPsbtRequest_Raw
Template isFundPsbtRequest_Template `protobuf_oneof:"template"`
// Types that are valid to be assigned to Fees:
// *FundPsbtRequest_TargetConf
// *FundPsbtRequest_SatPerVbyte
Fees isFundPsbtRequest_Fees `protobuf_oneof:"fees"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FundPsbtRequest) Reset() { *m = FundPsbtRequest{} }
func (m *FundPsbtRequest) String() string { return proto.CompactTextString(m) }
func (*FundPsbtRequest) ProtoMessage() {}
func (*FundPsbtRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{24}
}
func (m *FundPsbtRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FundPsbtRequest.Unmarshal(m, b)
}
func (m *FundPsbtRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FundPsbtRequest.Marshal(b, m, deterministic)
}
func (m *FundPsbtRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_FundPsbtRequest.Merge(m, src)
}
func (m *FundPsbtRequest) XXX_Size() int {
return xxx_messageInfo_FundPsbtRequest.Size(m)
}
func (m *FundPsbtRequest) XXX_DiscardUnknown() {
xxx_messageInfo_FundPsbtRequest.DiscardUnknown(m)
}
var xxx_messageInfo_FundPsbtRequest proto.InternalMessageInfo
type isFundPsbtRequest_Template interface {
isFundPsbtRequest_Template()
}
type FundPsbtRequest_Psbt struct {
Psbt []byte `protobuf:"bytes,1,opt,name=psbt,proto3,oneof"`
}
type FundPsbtRequest_Raw struct {
Raw *TxTemplate `protobuf:"bytes,2,opt,name=raw,proto3,oneof"`
}
func (*FundPsbtRequest_Psbt) isFundPsbtRequest_Template() {}
func (*FundPsbtRequest_Raw) isFundPsbtRequest_Template() {}
func (m *FundPsbtRequest) GetTemplate() isFundPsbtRequest_Template {
if m != nil {
return m.Template
}
return nil
}
func (m *FundPsbtRequest) GetPsbt() []byte {
if x, ok := m.GetTemplate().(*FundPsbtRequest_Psbt); ok {
return x.Psbt
}
return nil
}
func (m *FundPsbtRequest) GetRaw() *TxTemplate {
if x, ok := m.GetTemplate().(*FundPsbtRequest_Raw); ok {
return x.Raw
}
return nil
}
type isFundPsbtRequest_Fees interface {
isFundPsbtRequest_Fees()
}
type FundPsbtRequest_TargetConf struct {
TargetConf uint32 `protobuf:"varint,3,opt,name=target_conf,json=targetConf,proto3,oneof"`
}
type FundPsbtRequest_SatPerVbyte struct {
SatPerVbyte uint32 `protobuf:"varint,4,opt,name=sat_per_vbyte,json=satPerVbyte,proto3,oneof"`
}
func (*FundPsbtRequest_TargetConf) isFundPsbtRequest_Fees() {}
func (*FundPsbtRequest_SatPerVbyte) isFundPsbtRequest_Fees() {}
func (m *FundPsbtRequest) GetFees() isFundPsbtRequest_Fees {
if m != nil {
return m.Fees
}
return nil
}
func (m *FundPsbtRequest) GetTargetConf() uint32 {
if x, ok := m.GetFees().(*FundPsbtRequest_TargetConf); ok {
return x.TargetConf
}
return 0
}
func (m *FundPsbtRequest) GetSatPerVbyte() uint32 {
if x, ok := m.GetFees().(*FundPsbtRequest_SatPerVbyte); ok {
return x.SatPerVbyte
}
return 0
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*FundPsbtRequest) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*FundPsbtRequest_Psbt)(nil),
(*FundPsbtRequest_Raw)(nil),
(*FundPsbtRequest_TargetConf)(nil),
(*FundPsbtRequest_SatPerVbyte)(nil),
}
}
type FundPsbtResponse struct {
//
//The funded but not yet signed 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 lock leases that were acquired for the inputs in the funded PSBT
//packet.
LockedUtxos []*UtxoLease `protobuf:"bytes,3,rep,name=locked_utxos,json=lockedUtxos,proto3" json:"locked_utxos,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FundPsbtResponse) Reset() { *m = FundPsbtResponse{} }
func (m *FundPsbtResponse) String() string { return proto.CompactTextString(m) }
func (*FundPsbtResponse) ProtoMessage() {}
func (*FundPsbtResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{25}
}
func (m *FundPsbtResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FundPsbtResponse.Unmarshal(m, b)
}
func (m *FundPsbtResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FundPsbtResponse.Marshal(b, m, deterministic)
}
func (m *FundPsbtResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_FundPsbtResponse.Merge(m, src)
}
func (m *FundPsbtResponse) XXX_Size() int {
return xxx_messageInfo_FundPsbtResponse.Size(m)
}
func (m *FundPsbtResponse) XXX_DiscardUnknown() {
xxx_messageInfo_FundPsbtResponse.DiscardUnknown(m)
}
var xxx_messageInfo_FundPsbtResponse proto.InternalMessageInfo
func (m *FundPsbtResponse) GetFundedPsbt() []byte {
if m != nil {
return m.FundedPsbt
}
return nil
}
func (m *FundPsbtResponse) GetChangeOutputIndex() int32 {
if m != nil {
return m.ChangeOutputIndex
}
return 0
}
func (m *FundPsbtResponse) GetLockedUtxos() []*UtxoLease {
if m != nil {
return m.LockedUtxos
}
return nil
}
type TxTemplate struct {
//
//An optional list of inputs to use. Every input must be an UTXO known to the
//wallet that has not been locked before. The sum of all inputs must be
//sufficiently greater than the sum of all outputs to pay a miner fee with the
//fee rate specified in the parent message.
//
//If no inputs are specified, coin selection will be performed instead and
//inputs of sufficient value will be added to the resulting PSBT.
Inputs []*lnrpc.OutPoint `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"`
//
//A map of all addresses and the amounts to send to in the funded PSBT.
Outputs map[string]uint64 `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *TxTemplate) Reset() { *m = TxTemplate{} }
func (m *TxTemplate) String() string { return proto.CompactTextString(m) }
func (*TxTemplate) ProtoMessage() {}
func (*TxTemplate) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{26}
}
func (m *TxTemplate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TxTemplate.Unmarshal(m, b)
}
func (m *TxTemplate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_TxTemplate.Marshal(b, m, deterministic)
}
func (m *TxTemplate) XXX_Merge(src proto.Message) {
xxx_messageInfo_TxTemplate.Merge(m, src)
}
func (m *TxTemplate) XXX_Size() int {
return xxx_messageInfo_TxTemplate.Size(m)
}
func (m *TxTemplate) XXX_DiscardUnknown() {
xxx_messageInfo_TxTemplate.DiscardUnknown(m)
}
var xxx_messageInfo_TxTemplate proto.InternalMessageInfo
func (m *TxTemplate) GetInputs() []*lnrpc.OutPoint {
if m != nil {
return m.Inputs
}
return nil
}
func (m *TxTemplate) GetOutputs() map[string]uint64 {
if m != nil {
return m.Outputs
}
return nil
}
type UtxoLease struct {
//
//A 32 byte random ID that identifies the lease.
Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// The identifying outpoint of the output being leased.
Outpoint *lnrpc.OutPoint `protobuf:"bytes,2,opt,name=outpoint,proto3" json:"outpoint,omitempty"`
//
//The absolute expiration of the output lease represented as a unix timestamp.
Expiration uint64 `protobuf:"varint,3,opt,name=expiration,proto3" json:"expiration,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UtxoLease) Reset() { *m = UtxoLease{} }
func (m *UtxoLease) String() string { return proto.CompactTextString(m) }
func (*UtxoLease) ProtoMessage() {}
func (*UtxoLease) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{27}
}
func (m *UtxoLease) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UtxoLease.Unmarshal(m, b)
}
func (m *UtxoLease) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UtxoLease.Marshal(b, m, deterministic)
}
func (m *UtxoLease) XXX_Merge(src proto.Message) {
xxx_messageInfo_UtxoLease.Merge(m, src)
}
func (m *UtxoLease) XXX_Size() int {
return xxx_messageInfo_UtxoLease.Size(m)
}
func (m *UtxoLease) XXX_DiscardUnknown() {
xxx_messageInfo_UtxoLease.DiscardUnknown(m)
}
var xxx_messageInfo_UtxoLease proto.InternalMessageInfo
func (m *UtxoLease) GetId() []byte {
if m != nil {
return m.Id
}
return nil
}
func (m *UtxoLease) GetOutpoint() *lnrpc.OutPoint {
if m != nil {
return m.Outpoint
}
return nil
}
func (m *UtxoLease) GetExpiration() uint64 {
if m != nil {
return m.Expiration
}
return 0
}
type FinalizePsbtRequest struct {
//
//A PSBT that should be signed and finalized. The PSBT must contain all
//required inputs, outputs, UTXO data and partial signatures of all other
//signers.
FundedPsbt []byte `protobuf:"bytes,1,opt,name=funded_psbt,json=fundedPsbt,proto3" json:"funded_psbt,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FinalizePsbtRequest) Reset() { *m = FinalizePsbtRequest{} }
func (m *FinalizePsbtRequest) String() string { return proto.CompactTextString(m) }
func (*FinalizePsbtRequest) ProtoMessage() {}
func (*FinalizePsbtRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{28}
}
func (m *FinalizePsbtRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FinalizePsbtRequest.Unmarshal(m, b)
}
func (m *FinalizePsbtRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FinalizePsbtRequest.Marshal(b, m, deterministic)
}
func (m *FinalizePsbtRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_FinalizePsbtRequest.Merge(m, src)
}
func (m *FinalizePsbtRequest) XXX_Size() int {
return xxx_messageInfo_FinalizePsbtRequest.Size(m)
}
func (m *FinalizePsbtRequest) XXX_DiscardUnknown() {
xxx_messageInfo_FinalizePsbtRequest.DiscardUnknown(m)
}
var xxx_messageInfo_FinalizePsbtRequest proto.InternalMessageInfo
func (m *FinalizePsbtRequest) GetFundedPsbt() []byte {
if m != nil {
return m.FundedPsbt
}
return nil
}
type FinalizePsbtResponse struct {
// The fully signed and finalized transaction in PSBT format.
SignedPsbt []byte `protobuf:"bytes,1,opt,name=signed_psbt,json=signedPsbt,proto3" json:"signed_psbt,omitempty"`
// The fully signed and finalized transaction in the raw wire format.
RawFinalTx []byte `protobuf:"bytes,2,opt,name=raw_final_tx,json=rawFinalTx,proto3" json:"raw_final_tx,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FinalizePsbtResponse) Reset() { *m = FinalizePsbtResponse{} }
func (m *FinalizePsbtResponse) String() string { return proto.CompactTextString(m) }
func (*FinalizePsbtResponse) ProtoMessage() {}
func (*FinalizePsbtResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6cc6942ac78249e5, []int{29}
}
func (m *FinalizePsbtResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FinalizePsbtResponse.Unmarshal(m, b)
}
func (m *FinalizePsbtResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FinalizePsbtResponse.Marshal(b, m, deterministic)
}
func (m *FinalizePsbtResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_FinalizePsbtResponse.Merge(m, src)
}
func (m *FinalizePsbtResponse) XXX_Size() int {
return xxx_messageInfo_FinalizePsbtResponse.Size(m)
}
func (m *FinalizePsbtResponse) XXX_DiscardUnknown() {
xxx_messageInfo_FinalizePsbtResponse.DiscardUnknown(m)
}
var xxx_messageInfo_FinalizePsbtResponse proto.InternalMessageInfo
func (m *FinalizePsbtResponse) GetSignedPsbt() []byte {
if m != nil {
return m.SignedPsbt
}
return nil
}
func (m *FinalizePsbtResponse) GetRawFinalTx() []byte {
if m != nil {
return m.RawFinalTx
}
return nil
}
func init() {
proto.RegisterEnum("walletrpc.WitnessType", WitnessType_name, WitnessType_value)
proto.RegisterType((*ListUnspentRequest)(nil), "walletrpc.ListUnspentRequest")
@ -1395,104 +1789,132 @@ func init() {
proto.RegisterType((*ListSweepsResponse_TransactionIDs)(nil), "walletrpc.ListSweepsResponse.TransactionIDs")
proto.RegisterType((*LabelTransactionRequest)(nil), "walletrpc.LabelTransactionRequest")
proto.RegisterType((*LabelTransactionResponse)(nil), "walletrpc.LabelTransactionResponse")
proto.RegisterType((*FundPsbtRequest)(nil), "walletrpc.FundPsbtRequest")
proto.RegisterType((*FundPsbtResponse)(nil), "walletrpc.FundPsbtResponse")
proto.RegisterType((*TxTemplate)(nil), "walletrpc.TxTemplate")
proto.RegisterMapType((map[string]uint64)(nil), "walletrpc.TxTemplate.OutputsEntry")
proto.RegisterType((*UtxoLease)(nil), "walletrpc.UtxoLease")
proto.RegisterType((*FinalizePsbtRequest)(nil), "walletrpc.FinalizePsbtRequest")
proto.RegisterType((*FinalizePsbtResponse)(nil), "walletrpc.FinalizePsbtResponse")
}
func init() { proto.RegisterFile("walletrpc/walletkit.proto", fileDescriptor_6cc6942ac78249e5) }
var fileDescriptor_6cc6942ac78249e5 = []byte{
// 1460 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x6f, 0x6f, 0x1a, 0x47,
0x13, 0x8f, 0xff, 0x61, 0x98, 0x03, 0x8c, 0x17, 0x6c, 0x13, 0xe2, 0xc4, 0xce, 0x45, 0xcf, 0x53,
0xab, 0x49, 0xb0, 0xea, 0x28, 0x55, 0x92, 0x4a, 0x55, 0x6d, 0x38, 0x0b, 0x0b, 0x0c, 0xce, 0x81,
0x63, 0xa5, 0x7d, 0x71, 0x3a, 0xb8, 0x0d, 0x3e, 0x19, 0xee, 0x2e, 0x7b, 0x4b, 0x38, 0xde, 0xf5,
0x53, 0x54, 0xca, 0x77, 0xe9, 0xa7, 0xe8, 0x27, 0xaa, 0x76, 0xf7, 0x38, 0xf6, 0x00, 0xa7, 0xaa,
0xd4, 0x57, 0xbe, 0x9d, 0xdf, 0xcc, 0x6f, 0x67, 0x67, 0xc6, 0x33, 0x03, 0x3c, 0x1c, 0x9b, 0x83,
0x01, 0xa6, 0xc4, 0xeb, 0x1d, 0x8b, 0xaf, 0x3b, 0x9b, 0x96, 0x3d, 0xe2, 0x52, 0x17, 0xa5, 0x22,
0xa8, 0x94, 0x22, 0x5e, 0x4f, 0x48, 0x4b, 0x05, 0xdf, 0xee, 0x3b, 0x4c, 0x9d, 0xfd, 0xc5, 0x44,
0x48, 0xd5, 0x26, 0xa0, 0x86, 0xed, 0xd3, 0x6b, 0xc7, 0xf7, 0xb0, 0x43, 0x75, 0xfc, 0x79, 0x84,
0x7d, 0x8a, 0x1e, 0x41, 0x6a, 0x68, 0x3b, 0x46, 0xcf, 0x75, 0x3e, 0xf9, 0xc5, 0x95, 0xc3, 0x95,
0xa3, 0x0d, 0x3d, 0x39, 0xb4, 0x9d, 0x0a, 0x3b, 0x73, 0xd0, 0x0c, 0x42, 0x70, 0x35, 0x04, 0xcd,
0x80, 0x83, 0xea, 0x1b, 0xc8, 0xc7, 0xf8, 0x7c, 0xcf, 0x75, 0x7c, 0x8c, 0x9e, 0xc2, 0xc6, 0x88,
0x06, 0x2e, 0x23, 0x5b, 0x3b, 0x52, 0x4e, 0x94, 0xf2, 0x80, 0xb9, 0x52, 0xbe, 0xa6, 0x81, 0xab,
0x0b, 0x44, 0x7d, 0x0f, 0xa8, 0x81, 0x4d, 0x1f, 0xb7, 0x46, 0xd4, 0x1b, 0x45, 0x9e, 0x64, 0x61,
0xd5, 0xb6, 0xb8, 0x0b, 0x69, 0x7d, 0xd5, 0xb6, 0xd0, 0x73, 0x48, 0xba, 0x23, 0xea, 0xb9, 0xb6,
0x43, 0xf9, 0xdd, 0xca, 0xc9, 0x56, 0xc8, 0xd5, 0x1a, 0xd1, 0x2b, 0x26, 0xd6, 0x23, 0x05, 0xf5,
0x35, 0xe4, 0x63, 0x94, 0xa1, 0x33, 0x4f, 0x00, 0x70, 0xe0, 0xd9, 0xc4, 0xa4, 0xb6, 0xeb, 0x70,
0xee, 0x75, 0x5d, 0x92, 0xa8, 0x6d, 0x28, 0xe8, 0x78, 0xf0, 0x1f, 0xfb, 0xb2, 0x07, 0x3b, 0x73,
0xa4, 0xc2, 0x1b, 0xf5, 0x3d, 0x24, 0xea, 0x78, 0xa2, 0xe3, 0xcf, 0xe8, 0x08, 0x72, 0x77, 0x78,
0x62, 0x7c, 0xb2, 0x9d, 0x3e, 0x26, 0x86, 0x47, 0x18, 0xaf, 0x08, 0x7e, 0xf6, 0x0e, 0x4f, 0xce,
0xb9, 0xf8, 0x8a, 0x49, 0xd1, 0x63, 0x00, 0xae, 0x69, 0x0e, 0xed, 0xc1, 0x24, 0xcc, 0x41, 0x8a,
0xe9, 0x70, 0x81, 0x9a, 0x01, 0xe5, 0xd4, 0xb2, 0x48, 0xe8, 0xb7, 0xaa, 0x42, 0x5a, 0x1c, 0xc3,
0xf7, 0x23, 0x58, 0x37, 0x2d, 0x8b, 0x70, 0xee, 0x94, 0xce, 0xbf, 0xd5, 0x77, 0xa0, 0x74, 0x88,
0xe9, 0xf8, 0x66, 0x8f, 0x85, 0x00, 0xed, 0x40, 0x82, 0x06, 0xc6, 0x2d, 0x0e, 0xc2, 0xe7, 0x6e,
0xd0, 0xa0, 0x86, 0x03, 0x54, 0x80, 0x8d, 0x81, 0xd9, 0xc5, 0x03, 0x7e, 0x65, 0x4a, 0x17, 0x07,
0xf5, 0x47, 0xd8, 0xba, 0x1a, 0x75, 0x07, 0xb6, 0x7f, 0x1b, 0x5d, 0xf1, 0x0c, 0x32, 0x9e, 0x10,
0x19, 0x98, 0x10, 0x77, 0x7a, 0x57, 0x3a, 0x14, 0x6a, 0x4c, 0xa6, 0xfe, 0xb9, 0x02, 0xa8, 0x8d,
0x1d, 0x4b, 0x04, 0xc4, 0x9f, 0x86, 0x79, 0x1f, 0xc0, 0x37, 0xa9, 0xe1, 0x61, 0x62, 0xdc, 0x8d,
0xb9, 0xe1, 0x9a, 0x9e, 0xf4, 0x4d, 0x7a, 0x85, 0x49, 0x7d, 0x8c, 0x8e, 0x60, 0xd3, 0x15, 0xfa,
0xc5, 0x55, 0x5e, 0x4b, 0xd9, 0x72, 0x58, 0xd8, 0xe5, 0x4e, 0xd0, 0x1a, 0x51, 0x7d, 0x0a, 0xcf,
0x9c, 0x5d, 0x93, 0x9c, 0x8d, 0x97, 0xf6, 0xfa, 0x5c, 0x69, 0x3f, 0x87, 0x6d, 0x56, 0xb7, 0x96,
0x31, 0x72, 0x98, 0x82, 0x4d, 0x86, 0xd8, 0x2a, 0x6e, 0x1c, 0xae, 0x1c, 0x25, 0xf5, 0x1c, 0x07,
0xae, 0x67, 0x72, 0xf5, 0x05, 0xe4, 0x63, 0xde, 0x87, 0x4f, 0xdf, 0x81, 0x04, 0x31, 0xc7, 0x06,
0x8d, 0x42, 0x47, 0xcc, 0x71, 0x27, 0x50, 0x5f, 0x03, 0xd2, 0x7c, 0x6a, 0x0f, 0x4d, 0x8a, 0xcf,
0x31, 0x9e, 0xbe, 0xf5, 0x00, 0x14, 0x46, 0x68, 0x50, 0x93, 0xf4, 0xf1, 0x34, 0xdb, 0xc0, 0x44,
0x1d, 0x2e, 0x51, 0x5f, 0x41, 0x3e, 0x66, 0x16, 0x5e, 0xf2, 0xcd, 0x18, 0xa9, 0x5f, 0xd7, 0x20,
0x7d, 0x85, 0x1d, 0xcb, 0x76, 0xfa, 0xed, 0x31, 0xc6, 0x5e, 0xac, 0x52, 0x57, 0xfe, 0xa1, 0x52,
0xd1, 0x5b, 0x48, 0x8f, 0x6d, 0xea, 0x60, 0xdf, 0x37, 0xe8, 0xc4, 0xc3, 0x3c, 0xd7, 0xd9, 0x93,
0xdd, 0x72, 0xd4, 0x55, 0xca, 0x37, 0x02, 0xee, 0x4c, 0x3c, 0xac, 0x2b, 0xe3, 0xd9, 0x81, 0xd5,
0xa5, 0x39, 0x74, 0x47, 0x0e, 0x35, 0x7c, 0x93, 0xf2, 0xb8, 0x67, 0xf4, 0x94, 0x90, 0xb4, 0x4d,
0x8a, 0x0e, 0x21, 0x3d, 0xf5, 0xba, 0x3b, 0xa1, 0x98, 0x87, 0x3f, 0xa3, 0x83, 0xf0, 0xfb, 0x6c,
0x42, 0x31, 0x7a, 0x09, 0xa8, 0x4b, 0x5c, 0xd3, 0xea, 0x99, 0x3e, 0x35, 0x4c, 0x4a, 0xf1, 0xd0,
0xa3, 0x3e, 0xcf, 0x40, 0x46, 0xdf, 0x8e, 0x90, 0xd3, 0x10, 0x40, 0x27, 0xb0, 0xe3, 0xe0, 0x80,
0x1a, 0x33, 0x9b, 0x5b, 0x6c, 0xf7, 0x6f, 0x69, 0x31, 0xc1, 0x2d, 0xf2, 0x0c, 0x3c, 0x9b, 0x62,
0x35, 0x0e, 0x31, 0x1b, 0x22, 0xa2, 0x8f, 0x2d, 0x43, 0x0e, 0x7e, 0x52, 0xd8, 0x44, 0x60, 0x25,
0xca, 0x02, 0x7a, 0x05, 0xbb, 0x33, 0x9b, 0xd8, 0x13, 0x52, 0x73, 0x46, 0xed, 0xd9, 0x5b, 0x0a,
0xb0, 0xf1, 0xc9, 0x25, 0x3d, 0x5c, 0xdc, 0xe4, 0x05, 0x24, 0x0e, 0xea, 0x2e, 0x14, 0xe4, 0xd4,
0x4c, 0xab, 0x5e, 0xbd, 0x81, 0x9d, 0x39, 0x79, 0x98, 0xea, 0x9f, 0x21, 0xeb, 0x09, 0xc0, 0xf0,
0x39, 0x12, 0xf6, 0xd0, 0x3d, 0x29, 0x21, 0xb2, 0xa5, 0x9e, 0xf1, 0x64, 0x1e, 0xf5, 0x8f, 0x15,
0xc8, 0x9e, 0x8d, 0x86, 0x9e, 0x54, 0x75, 0xff, 0xaa, 0x1c, 0x0e, 0x40, 0x11, 0x01, 0xe2, 0xc1,
0xe2, 0xd5, 0x90, 0xd1, 0x41, 0x88, 0x58, 0x88, 0x16, 0xb2, 0xba, 0xb6, 0x90, 0xd5, 0x28, 0x12,
0xeb, 0x72, 0x24, 0xb6, 0x61, 0x2b, 0xf2, 0x2b, 0xec, 0x85, 0x2f, 0x61, 0x9b, 0x4d, 0x8f, 0x58,
0x64, 0x50, 0x11, 0x36, 0xbf, 0x60, 0xd2, 0x75, 0x7d, 0xcc, 0x9d, 0x4d, 0xea, 0xd3, 0xa3, 0xfa,
0xfb, 0xaa, 0x98, 0x5e, 0x73, 0x11, 0x6b, 0x40, 0x9e, 0xce, 0x7a, 0x99, 0x61, 0x61, 0x6a, 0xda,
0x03, 0x3f, 0x7c, 0xe9, 0xc3, 0xf0, 0xa5, 0x52, 0xb7, 0xab, 0x0a, 0x85, 0xda, 0x03, 0x1d, 0xd1,
0x05, 0x29, 0xba, 0x81, 0x2d, 0x99, 0xcd, 0xb6, 0xfc, 0xb0, 0xd9, 0xbf, 0x90, 0x12, 0xb0, 0xe8,
0x85, 0x7c, 0xc1, 0x45, 0x95, 0x91, 0x67, 0x25, 0x9a, 0x0b, 0xcb, 0x2f, 0xbd, 0x85, 0x6c, 0x5c,
0x07, 0x7d, 0xb7, 0x78, 0x15, 0xcb, 0x75, 0x6a, 0xde, 0xf4, 0x2c, 0x09, 0x09, 0x51, 0x0b, 0xaa,
0x09, 0x7b, 0x0d, 0xd6, 0xd7, 0x24, 0xa6, 0x69, 0xdc, 0x10, 0xac, 0xd3, 0x20, 0x1a, 0x58, 0xfc,
0x7b, 0x79, 0x03, 0x47, 0xfb, 0x90, 0x72, 0xbf, 0x60, 0x32, 0x26, 0x76, 0x98, 0xbe, 0xa4, 0x3e,
0x13, 0xa8, 0x25, 0x28, 0x2e, 0x5e, 0x21, 0x1e, 0xf9, 0xfd, 0xd7, 0x35, 0x50, 0xa4, 0x6e, 0x80,
0xf2, 0xb0, 0x75, 0xdd, 0xac, 0x37, 0x5b, 0x37, 0x4d, 0xe3, 0xe6, 0xa2, 0xd3, 0xd4, 0xda, 0xed,
0xdc, 0x03, 0x54, 0x84, 0x42, 0xa5, 0x75, 0x79, 0x79, 0xd1, 0xb9, 0xd4, 0x9a, 0x1d, 0xa3, 0x73,
0x71, 0xa9, 0x19, 0x8d, 0x56, 0xa5, 0x9e, 0x5b, 0x41, 0x7b, 0x90, 0x97, 0x90, 0x66, 0xcb, 0xa8,
0x6a, 0x8d, 0xd3, 0x8f, 0xb9, 0x55, 0xb4, 0x03, 0xdb, 0x12, 0xa0, 0x6b, 0x1f, 0x5a, 0x75, 0x2d,
0xb7, 0xc6, 0xf4, 0x6b, 0x9d, 0x46, 0xc5, 0x68, 0x9d, 0x9f, 0x6b, 0xba, 0x56, 0x9d, 0x02, 0xeb,
0xec, 0x0a, 0x0e, 0x9c, 0x56, 0x2a, 0xda, 0x55, 0x67, 0x86, 0x6c, 0xa0, 0xff, 0xc1, 0xd3, 0x98,
0x09, 0xbb, 0xbe, 0x75, 0xdd, 0x31, 0xda, 0x5a, 0xa5, 0xd5, 0xac, 0x1a, 0x0d, 0xed, 0x83, 0xd6,
0xc8, 0x25, 0xd0, 0xff, 0x41, 0x8d, 0x13, 0xb4, 0xaf, 0x2b, 0x15, 0xad, 0xdd, 0x8e, 0xeb, 0x6d,
0xa2, 0x03, 0x78, 0x34, 0xe7, 0xc1, 0x65, 0xab, 0xa3, 0x4d, 0x59, 0x73, 0x49, 0x74, 0x08, 0xfb,
0xf3, 0x9e, 0x70, 0x8d, 0x90, 0x2f, 0x97, 0x42, 0xfb, 0x50, 0xe4, 0x1a, 0x32, 0xf3, 0xd4, 0x5f,
0x40, 0x05, 0xc8, 0x85, 0x91, 0x33, 0xea, 0xda, 0x47, 0xa3, 0x76, 0xda, 0xae, 0xe5, 0x14, 0xf4,
0x08, 0xf6, 0x9a, 0x5a, 0x9b, 0xd1, 0x2d, 0x80, 0xe9, 0xb9, 0x60, 0x9d, 0x36, 0x2b, 0xb5, 0x96,
0x9e, 0xcb, 0x9c, 0xfc, 0xb5, 0x09, 0xa9, 0x1b, 0x5e, 0xa1, 0x75, 0x9b, 0xa2, 0x06, 0x28, 0xd2,
0x62, 0x86, 0x1e, 0xcf, 0x15, 0x6f, 0x7c, 0x01, 0x2c, 0x3d, 0xb9, 0x0f, 0x8e, 0xfe, 0xc5, 0x14,
0x69, 0xb3, 0x8a, 0xb3, 0x2d, 0x2c, 0x4e, 0x71, 0xb6, 0x25, 0x0b, 0x99, 0x0e, 0x99, 0xd8, 0x6e,
0x84, 0x0e, 0x24, 0x83, 0x65, 0xab, 0x58, 0xe9, 0xf0, 0x7e, 0x85, 0x90, 0xf3, 0x1d, 0x64, 0xaa,
0x98, 0xd8, 0x5f, 0x70, 0x13, 0x07, 0xb4, 0x8e, 0x27, 0x68, 0x5b, 0x32, 0x11, 0x0b, 0x57, 0x69,
0x37, 0x5a, 0x1d, 0xea, 0x78, 0x52, 0xc5, 0x7e, 0x8f, 0xd8, 0x1e, 0x75, 0x09, 0x7a, 0x03, 0x29,
0x61, 0xcb, 0xec, 0xf2, 0xb2, 0x52, 0xc3, 0xed, 0x99, 0xd4, 0x25, 0xf7, 0x5a, 0xfe, 0x04, 0x49,
0x76, 0x1f, 0x5b, 0xb7, 0x90, 0x3c, 0x31, 0xa5, 0x75, 0xac, 0xb4, 0xb7, 0x20, 0x0f, 0x5d, 0xae,
0x01, 0x0a, 0xf7, 0x28, 0x79, 0x15, 0x93, 0x69, 0x24, 0x79, 0xa9, 0x24, 0xf7, 0xff, 0xb9, 0xf5,
0xab, 0x01, 0x8a, 0xb4, 0x9a, 0xc4, 0xd2, 0xb3, 0xb8, 0x70, 0xc5, 0xd2, 0xb3, 0x6c, 0xa3, 0x69,
0x80, 0x22, 0xed, 0x20, 0x31, 0xb6, 0xc5, 0x95, 0x26, 0xc6, 0xb6, 0x6c, 0x75, 0xd1, 0x21, 0x13,
0x1b, 0x74, 0xb1, 0x64, 0x2f, 0x1b, 0x8d, 0xb1, 0x64, 0x2f, 0x9f, 0x91, 0xbf, 0xc0, 0x66, 0x38,
0x4a, 0xd0, 0x43, 0x49, 0x39, 0x3e, 0xf6, 0x62, 0x11, 0x9b, 0x9b, 0x3c, 0xe8, 0x02, 0x60, 0xd6,
0xc3, 0xd1, 0xfe, 0x3d, 0xad, 0x5d, 0xf0, 0x3c, 0xfe, 0x66, 0xe3, 0x47, 0xbf, 0x41, 0x6e, 0xbe,
0x5f, 0x22, 0x55, 0x36, 0x59, 0xde, 0xaf, 0x4b, 0xcf, 0xbe, 0xa9, 0x23, 0xc8, 0xcf, 0x7e, 0xf8,
0xf5, 0xb8, 0x6f, 0xd3, 0xdb, 0x51, 0xb7, 0xdc, 0x73, 0x87, 0xc7, 0x03, 0xb6, 0xd1, 0x38, 0xb6,
0xd3, 0x77, 0x30, 0x1d, 0xbb, 0xe4, 0xee, 0x78, 0xe0, 0x58, 0xc7, 0x7c, 0xbe, 0x1d, 0x47, 0x5c,
0xdd, 0x04, 0xff, 0xa5, 0xf7, 0xea, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x49, 0x6e, 0x8a, 0xc5,
0x32, 0x0e, 0x00, 0x00,
// 1793 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xef, 0x6e, 0x22, 0xc9,
0x11, 0x5f, 0x0c, 0xc6, 0x50, 0x80, 0x8d, 0x1b, 0xbc, 0x66, 0x59, 0xef, 0xd9, 0x3b, 0x97, 0xe4,
0x9c, 0xdc, 0x1d, 0x56, 0xbc, 0xba, 0xcb, 0x9e, 0x13, 0x45, 0xb1, 0xf1, 0x58, 0x58, 0x60, 0xf0,
0x35, 0x78, 0xad, 0x4d, 0x3e, 0x8c, 0x06, 0xa6, 0x6d, 0x8f, 0x0c, 0x33, 0x73, 0x33, 0x8d, 0x19,
0xf2, 0x29, 0x4f, 0x11, 0xe9, 0xa4, 0xbc, 0xc3, 0xbd, 0x40, 0x1e, 0x28, 0x8f, 0x11, 0xf5, 0x1f,
0x86, 0x1e, 0xc0, 0x7b, 0x8a, 0x72, 0x9f, 0x4c, 0xd7, 0xaf, 0xea, 0xd7, 0xd5, 0x55, 0x35, 0x5d,
0xd5, 0x86, 0x57, 0x13, 0x73, 0x38, 0x24, 0xd4, 0xf7, 0x06, 0x47, 0xe2, 0xd7, 0xa3, 0x4d, 0x6b,
0x9e, 0xef, 0x52, 0x17, 0x65, 0x23, 0xa8, 0x9a, 0xf5, 0xbd, 0x81, 0x90, 0x56, 0xcb, 0x81, 0x7d,
0xef, 0x30, 0x75, 0xf6, 0x97, 0xf8, 0x42, 0xaa, 0xb5, 0x01, 0xb5, 0xec, 0x80, 0xde, 0x38, 0x81,
0x47, 0x1c, 0x8a, 0xc9, 0x0f, 0x63, 0x12, 0x50, 0xf4, 0x1a, 0xb2, 0x23, 0xdb, 0x31, 0x06, 0xae,
0x73, 0x17, 0x54, 0x12, 0x07, 0x89, 0xc3, 0x75, 0x9c, 0x19, 0xd9, 0x4e, 0x9d, 0xad, 0x39, 0x68,
0x86, 0x12, 0x5c, 0x93, 0xa0, 0x19, 0x72, 0x50, 0x7b, 0x0f, 0xa5, 0x18, 0x5f, 0xe0, 0xb9, 0x4e,
0x40, 0xd0, 0x5b, 0x58, 0x1f, 0xd3, 0xd0, 0x65, 0x64, 0xc9, 0xc3, 0xdc, 0x71, 0xae, 0x36, 0x64,
0xae, 0xd4, 0x6e, 0x68, 0xe8, 0x62, 0x81, 0x68, 0xdf, 0x03, 0x6a, 0x11, 0x33, 0x20, 0x9d, 0x31,
0xf5, 0xc6, 0x91, 0x27, 0x9b, 0xb0, 0x66, 0x5b, 0xdc, 0x85, 0x3c, 0x5e, 0xb3, 0x2d, 0xf4, 0x25,
0x64, 0xdc, 0x31, 0xf5, 0x5c, 0xdb, 0xa1, 0x7c, 0xef, 0xdc, 0xf1, 0x96, 0xe4, 0xea, 0x8c, 0xe9,
0x35, 0x13, 0xe3, 0x48, 0x41, 0xfb, 0x06, 0x4a, 0x31, 0x4a, 0xe9, 0xcc, 0x67, 0x00, 0x24, 0xf4,
0x6c, 0xdf, 0xa4, 0xb6, 0xeb, 0x70, 0xee, 0x14, 0x56, 0x24, 0x5a, 0x17, 0xca, 0x98, 0x0c, 0x7f,
0x61, 0x5f, 0x76, 0x61, 0x67, 0x81, 0x54, 0x78, 0xa3, 0x7d, 0x0f, 0xe9, 0x26, 0x99, 0x62, 0xf2,
0x03, 0x3a, 0x84, 0xe2, 0x23, 0x99, 0x1a, 0x77, 0xb6, 0x73, 0x4f, 0x7c, 0xc3, 0xf3, 0x19, 0xaf,
0x08, 0xfe, 0xe6, 0x23, 0x99, 0x5e, 0x70, 0xf1, 0x35, 0x93, 0xa2, 0x37, 0x00, 0x5c, 0xd3, 0x1c,
0xd9, 0xc3, 0xa9, 0xcc, 0x41, 0x96, 0xe9, 0x70, 0x81, 0x56, 0x80, 0xdc, 0xa9, 0x65, 0xf9, 0xd2,
0x6f, 0x4d, 0x83, 0xbc, 0x58, 0xca, 0xf3, 0x23, 0x48, 0x99, 0x96, 0xe5, 0x73, 0xee, 0x2c, 0xe6,
0xbf, 0xb5, 0x13, 0xc8, 0xf5, 0x7c, 0xd3, 0x09, 0xcc, 0x01, 0x0b, 0x01, 0xda, 0x81, 0x34, 0x0d,
0x8d, 0x07, 0x12, 0xca, 0xe3, 0xae, 0xd3, 0xb0, 0x41, 0x42, 0x54, 0x86, 0xf5, 0xa1, 0xd9, 0x27,
0x43, 0xbe, 0x65, 0x16, 0x8b, 0x85, 0xf6, 0x2d, 0x6c, 0x5d, 0x8f, 0xfb, 0x43, 0x3b, 0x78, 0x88,
0xb6, 0xf8, 0x1c, 0x0a, 0x9e, 0x10, 0x19, 0xc4, 0xf7, 0xdd, 0xd9, 0x5e, 0x79, 0x29, 0xd4, 0x99,
0x4c, 0xfb, 0x77, 0x02, 0x50, 0x97, 0x38, 0x96, 0x08, 0x48, 0x30, 0x0b, 0xf3, 0x1e, 0x40, 0x60,
0x52, 0xc3, 0x23, 0xbe, 0xf1, 0x38, 0xe1, 0x86, 0x49, 0x9c, 0x09, 0x4c, 0x7a, 0x4d, 0xfc, 0xe6,
0x04, 0x1d, 0xc2, 0x86, 0x2b, 0xf4, 0x2b, 0x6b, 0xbc, 0x96, 0x36, 0x6b, 0xb2, 0xb0, 0x6b, 0xbd,
0xb0, 0x33, 0xa6, 0x78, 0x06, 0xcf, 0x9d, 0x4d, 0x2a, 0xce, 0xc6, 0x4b, 0x3b, 0xb5, 0x50, 0xda,
0x5f, 0xc2, 0x36, 0xab, 0x5b, 0xcb, 0x18, 0x3b, 0x4c, 0xc1, 0xf6, 0x47, 0xc4, 0xaa, 0xac, 0x1f,
0x24, 0x0e, 0x33, 0xb8, 0xc8, 0x81, 0x9b, 0xb9, 0x5c, 0xfb, 0x0a, 0x4a, 0x31, 0xef, 0xe5, 0xd1,
0x77, 0x20, 0xed, 0x9b, 0x13, 0x83, 0x46, 0xa1, 0xf3, 0xcd, 0x49, 0x2f, 0xd4, 0xbe, 0x01, 0xa4,
0x07, 0xd4, 0x1e, 0x99, 0x94, 0x5c, 0x10, 0x32, 0x3b, 0xeb, 0x3e, 0xe4, 0x18, 0xa1, 0x41, 0x4d,
0xff, 0x9e, 0xcc, 0xb2, 0x0d, 0x4c, 0xd4, 0xe3, 0x12, 0xed, 0x1d, 0x94, 0x62, 0x66, 0x72, 0x93,
0x4f, 0xc6, 0x48, 0xfb, 0x31, 0x09, 0xf9, 0x6b, 0xe2, 0x58, 0xb6, 0x73, 0xdf, 0x9d, 0x10, 0xe2,
0xc5, 0x2a, 0x35, 0xf1, 0x33, 0x95, 0x8a, 0xbe, 0x83, 0xfc, 0xc4, 0xa6, 0x0e, 0x09, 0x02, 0x83,
0x4e, 0x3d, 0xc2, 0x73, 0xbd, 0x79, 0xfc, 0xb2, 0x16, 0xdd, 0x2a, 0xb5, 0x5b, 0x01, 0xf7, 0xa6,
0x1e, 0xc1, 0xb9, 0xc9, 0x7c, 0xc1, 0xea, 0xd2, 0x1c, 0xb9, 0x63, 0x87, 0x1a, 0x81, 0x49, 0x79,
0xdc, 0x0b, 0x38, 0x2b, 0x24, 0x5d, 0x93, 0xa2, 0x03, 0xc8, 0xcf, 0xbc, 0xee, 0x4f, 0x29, 0xe1,
0xe1, 0x2f, 0x60, 0x10, 0x7e, 0x9f, 0x4d, 0x29, 0x41, 0x5f, 0x03, 0xea, 0xfb, 0xae, 0x69, 0x0d,
0xcc, 0x80, 0x1a, 0x26, 0xa5, 0x64, 0xe4, 0xd1, 0x80, 0x67, 0xa0, 0x80, 0xb7, 0x23, 0xe4, 0x54,
0x02, 0xe8, 0x18, 0x76, 0x1c, 0x12, 0x52, 0x63, 0x6e, 0xf3, 0x40, 0xec, 0xfb, 0x07, 0x5a, 0x49,
0x73, 0x8b, 0x12, 0x03, 0xcf, 0x66, 0x58, 0x83, 0x43, 0xcc, 0xc6, 0x17, 0xd1, 0x27, 0x96, 0xa1,
0x06, 0x3f, 0x23, 0x6c, 0x22, 0xb0, 0x1e, 0x65, 0x01, 0xbd, 0x83, 0x97, 0x73, 0x9b, 0xd8, 0x11,
0xb2, 0x0b, 0x46, 0xdd, 0xf9, 0x59, 0xca, 0xb0, 0x7e, 0xe7, 0xfa, 0x03, 0x52, 0xd9, 0xe0, 0x05,
0x24, 0x16, 0xda, 0x4b, 0x28, 0xab, 0xa9, 0x99, 0x55, 0xbd, 0x76, 0x0b, 0x3b, 0x0b, 0x72, 0x99,
0xea, 0x3f, 0xc3, 0xa6, 0x27, 0x00, 0x23, 0xe0, 0x88, 0xbc, 0x43, 0x77, 0x95, 0x84, 0xa8, 0x96,
0xb8, 0xe0, 0xa9, 0x3c, 0xda, 0x3f, 0x13, 0xb0, 0x79, 0x36, 0x1e, 0x79, 0x4a, 0xd5, 0xfd, 0x4f,
0xe5, 0xb0, 0x0f, 0x39, 0x11, 0x20, 0x1e, 0x2c, 0x5e, 0x0d, 0x05, 0x0c, 0x42, 0xc4, 0x42, 0xb4,
0x94, 0xd5, 0xe4, 0x52, 0x56, 0xa3, 0x48, 0xa4, 0xd4, 0x48, 0x6c, 0xc3, 0x56, 0xe4, 0x97, 0xbc,
0x0b, 0xbf, 0x86, 0x6d, 0xd6, 0x3d, 0x62, 0x91, 0x41, 0x15, 0xd8, 0x78, 0x22, 0x7e, 0xdf, 0x0d,
0x08, 0x77, 0x36, 0x83, 0x67, 0x4b, 0xed, 0x1f, 0x6b, 0xa2, 0x7b, 0x2d, 0x44, 0xac, 0x05, 0x25,
0x3a, 0xbf, 0xcb, 0x0c, 0x8b, 0x50, 0xd3, 0x1e, 0x06, 0xf2, 0xa4, 0xaf, 0xe4, 0x49, 0x95, 0xdb,
0xee, 0x5c, 0x28, 0x34, 0x5e, 0x60, 0x44, 0x97, 0xa4, 0xe8, 0x16, 0xb6, 0x54, 0x36, 0xdb, 0x0a,
0xe4, 0x65, 0xff, 0x95, 0x92, 0x80, 0x65, 0x2f, 0xd4, 0x0d, 0x2e, 0xcf, 0x19, 0xf9, 0xa6, 0x42,
0x73, 0x69, 0x05, 0xd5, 0xef, 0x60, 0x33, 0xae, 0x83, 0xbe, 0x58, 0xde, 0x8a, 0xe5, 0x3a, 0xbb,
0x68, 0x7a, 0x96, 0x81, 0xb4, 0xa8, 0x05, 0xcd, 0x84, 0xdd, 0x16, 0xbb, 0xd7, 0x14, 0xa6, 0x59,
0xdc, 0x10, 0xa4, 0x68, 0x18, 0x35, 0x2c, 0xfe, 0x7b, 0xf5, 0x05, 0x8e, 0xf6, 0x20, 0xeb, 0x3e,
0x11, 0x7f, 0xe2, 0xdb, 0x32, 0x7d, 0x19, 0x3c, 0x17, 0x68, 0x55, 0xa8, 0x2c, 0x6f, 0x21, 0x13,
0xf6, 0x53, 0x02, 0xb6, 0x2e, 0xc6, 0x8e, 0x75, 0x1d, 0xf4, 0xa3, 0x36, 0x59, 0x86, 0x94, 0x17,
0xf4, 0x45, 0x65, 0xe5, 0x1b, 0x2f, 0x30, 0x5f, 0xa1, 0xdf, 0x42, 0xd2, 0x37, 0x27, 0x32, 0x74,
0x3b, 0x4a, 0xe8, 0x7a, 0x61, 0x8f, 0x8c, 0xbc, 0xa1, 0x49, 0x49, 0xe3, 0x05, 0x66, 0x3a, 0xe8,
0x6d, 0xbc, 0xe2, 0x78, 0x3d, 0x35, 0x12, 0xb1, 0x9a, 0xfb, 0x15, 0x14, 0x66, 0x35, 0xf7, 0x34,
0xbf, 0x4a, 0x1a, 0x09, 0x9c, 0x13, 0x65, 0xf7, 0x81, 0x09, 0xcf, 0x00, 0x32, 0x54, 0x72, 0x9f,
0xa5, 0x21, 0x75, 0x47, 0x48, 0xa0, 0xfd, 0x2b, 0x01, 0xc5, 0xb9, 0xc7, 0xb2, 0x62, 0xf6, 0x21,
0x77, 0x37, 0x76, 0x2c, 0x62, 0x19, 0x73, 0xcf, 0x31, 0x08, 0x11, 0x53, 0x44, 0x35, 0x28, 0x0d,
0x1e, 0x4c, 0xe7, 0x9e, 0x18, 0xa2, 0xbb, 0x18, 0xb6, 0x63, 0x91, 0x50, 0x76, 0xde, 0x6d, 0x01,
0x89, 0x46, 0x70, 0xc9, 0x00, 0xf4, 0x07, 0xc8, 0x0f, 0xdd, 0xc1, 0x23, 0xb1, 0x0c, 0x31, 0xf6,
0x24, 0xf9, 0x27, 0x5b, 0x56, 0x8e, 0xcd, 0x46, 0x1f, 0x3e, 0x9c, 0xe0, 0x9c, 0xd0, 0xbc, 0xe1,
0x53, 0xd0, 0x4f, 0x09, 0x80, 0x79, 0x44, 0xd0, 0x17, 0x90, 0xb6, 0x1d, 0xde, 0xec, 0xc4, 0x47,
0xbf, 0xf4, 0x9d, 0x4a, 0x18, 0xfd, 0x69, 0xb1, 0x2d, 0x6a, 0x2b, 0x43, 0x5c, 0x93, 0xdd, 0x4a,
0x77, 0xa8, 0x3f, 0x8d, 0x5a, 0x65, 0xf5, 0x04, 0xf2, 0x2a, 0x80, 0x8a, 0x90, 0x7c, 0x24, 0x53,
0xd9, 0xb4, 0xd9, 0x4f, 0x56, 0x38, 0x4f, 0xe6, 0x70, 0x2c, 0xba, 0x41, 0x0a, 0x8b, 0xc5, 0xc9,
0xda, 0xfb, 0x84, 0xf6, 0x00, 0xd9, 0xe8, 0x2c, 0xff, 0xd7, 0x88, 0xb4, 0x30, 0x97, 0x25, 0x97,
0xe6, 0xb2, 0x6f, 0xa1, 0x74, 0x61, 0x3b, 0xe6, 0xd0, 0xfe, 0x3b, 0x51, 0xeb, 0xed, 0xe7, 0x92,
0xa7, 0x7d, 0x84, 0x72, 0xdc, 0x6e, 0x9e, 0x75, 0x3e, 0x0b, 0xc7, 0x0d, 0x85, 0x88, 0x67, 0xfd,
0x00, 0xf2, 0xac, 0x95, 0xdf, 0x31, 0x63, 0xd6, 0xd0, 0xd7, 0x84, 0x86, 0x6f, 0x4e, 0x38, 0x5f,
0x2f, 0xfc, 0xdd, 0x8f, 0x49, 0xc8, 0x29, 0xdd, 0x10, 0x95, 0x60, 0xeb, 0xa6, 0xdd, 0x6c, 0x77,
0x6e, 0xdb, 0xc6, 0xed, 0x65, 0xaf, 0xad, 0x77, 0xbb, 0xc5, 0x17, 0xa8, 0x02, 0xe5, 0x7a, 0xe7,
0xea, 0xea, 0xb2, 0x77, 0xa5, 0xb7, 0x7b, 0x46, 0xef, 0xf2, 0x4a, 0x37, 0x5a, 0x9d, 0x7a, 0xb3,
0x98, 0x40, 0xbb, 0x50, 0x52, 0x90, 0x76, 0xc7, 0x38, 0xd7, 0x5b, 0xa7, 0x1f, 0x8b, 0x6b, 0x68,
0x07, 0xb6, 0x15, 0x00, 0xeb, 0x1f, 0x3a, 0x4d, 0xbd, 0x98, 0x64, 0xfa, 0x8d, 0x5e, 0xab, 0x6e,
0x74, 0x2e, 0x2e, 0x74, 0xac, 0x9f, 0xcf, 0x80, 0x14, 0xdb, 0x82, 0x03, 0xa7, 0xf5, 0xba, 0x7e,
0xdd, 0x9b, 0x23, 0xeb, 0xe8, 0xd7, 0xf0, 0x36, 0x66, 0xc2, 0xb6, 0xef, 0xdc, 0xf4, 0x8c, 0xae,
0x5e, 0xef, 0xb4, 0xcf, 0x8d, 0x96, 0xfe, 0x41, 0x6f, 0x15, 0xd3, 0xe8, 0x37, 0xa0, 0xc5, 0x09,
0xba, 0x37, 0xf5, 0xba, 0xde, 0xed, 0xc6, 0xf5, 0x36, 0xd0, 0x3e, 0xbc, 0x5e, 0xf0, 0xe0, 0xaa,
0xd3, 0xd3, 0x67, 0xac, 0xc5, 0x0c, 0x3a, 0x80, 0xbd, 0x45, 0x4f, 0xb8, 0x86, 0xe4, 0x2b, 0x66,
0xd1, 0x1e, 0x54, 0xb8, 0x86, 0xca, 0x3c, 0xf3, 0x17, 0x50, 0x19, 0x8a, 0x32, 0x72, 0x46, 0x53,
0xff, 0x68, 0x34, 0x4e, 0xbb, 0x8d, 0x62, 0x0e, 0xbd, 0x86, 0xdd, 0xb6, 0xde, 0x65, 0x74, 0x4b,
0x60, 0x7e, 0x21, 0x58, 0xa7, 0xed, 0x7a, 0xa3, 0x83, 0x8b, 0x85, 0xe3, 0xff, 0x64, 0x20, 0x7b,
0xcb, 0xbf, 0x81, 0xa6, 0x4d, 0x51, 0x0b, 0x72, 0xca, 0xc3, 0x04, 0xbd, 0x59, 0xb8, 0xbc, 0xe3,
0x0f, 0xa0, 0xea, 0x67, 0xcf, 0xc1, 0x51, 0x8b, 0xc9, 0x29, 0x2f, 0x8b, 0x38, 0xdb, 0xd2, 0xc3,
0x21, 0xce, 0xb6, 0xe2, 0x41, 0x82, 0xa1, 0x10, 0x7b, 0x1b, 0xa0, 0x7d, 0xc5, 0x60, 0xd5, 0x53,
0xa4, 0x7a, 0xf0, 0xbc, 0x82, 0xe4, 0x3c, 0x81, 0xc2, 0x39, 0xf1, 0xed, 0x27, 0xd2, 0x26, 0x21,
0x6d, 0x92, 0x29, 0xda, 0x56, 0x4c, 0xc4, 0x83, 0xa3, 0xfa, 0x32, 0x1a, 0x9d, 0x9b, 0x64, 0x7a,
0x4e, 0x82, 0x81, 0x6f, 0x7b, 0xd4, 0xf5, 0xd1, 0x7b, 0xc8, 0x0a, 0x5b, 0x66, 0x57, 0x52, 0x95,
0x5a, 0xee, 0xc0, 0xa4, 0xae, 0xff, 0xac, 0xe5, 0x1f, 0x21, 0xc3, 0xf6, 0x63, 0xcf, 0x0d, 0xa4,
0x4e, 0x8c, 0xca, 0x73, 0xa4, 0xba, 0xbb, 0x24, 0x97, 0x2e, 0x37, 0x00, 0xc9, 0x77, 0x84, 0xfa,
0x14, 0x51, 0x69, 0x14, 0x79, 0xb5, 0xaa, 0xce, 0x3f, 0x0b, 0xcf, 0x8f, 0x16, 0xe4, 0x94, 0xd1,
0x3c, 0x96, 0x9e, 0xe5, 0x07, 0x47, 0x2c, 0x3d, 0xab, 0x26, 0xfa, 0x16, 0xe4, 0x94, 0x19, 0x3c,
0xc6, 0xb6, 0x3c, 0xd2, 0xc7, 0xd8, 0x56, 0x8d, 0xee, 0x18, 0x0a, 0xb1, 0x41, 0x2f, 0x96, 0xec,
0x55, 0xa3, 0x61, 0x2c, 0xd9, 0xab, 0x67, 0xc4, 0xbf, 0xc0, 0x86, 0x1c, 0xa5, 0xd0, 0x2b, 0x45,
0x39, 0x3e, 0xf6, 0xc5, 0x22, 0xb6, 0x30, 0x79, 0xa1, 0x4b, 0x80, 0xf9, 0x0c, 0x83, 0xf6, 0x9e,
0x19, 0x6d, 0x04, 0xcf, 0x9b, 0x4f, 0x0e, 0x3e, 0xe8, 0x6f, 0x50, 0x5c, 0x9c, 0x17, 0x90, 0xda,
0x8d, 0x9e, 0x99, 0x57, 0xaa, 0x9f, 0x7f, 0x52, 0x47, 0x92, 0xd7, 0x21, 0x33, 0xeb, 0xde, 0x48,
0x3d, 0xcf, 0xc2, 0x10, 0x52, 0x7d, 0xbd, 0x12, 0x93, 0x24, 0x1d, 0xc8, 0xab, 0x0d, 0x01, 0xa9,
0x29, 0x5b, 0xd1, 0x61, 0xaa, 0xfb, 0xcf, 0xe2, 0x82, 0xf0, 0xec, 0xf7, 0x7f, 0x3d, 0xba, 0xb7,
0xe9, 0xc3, 0xb8, 0x5f, 0x1b, 0xb8, 0xa3, 0xa3, 0x21, 0x7b, 0x67, 0x38, 0xb6, 0x73, 0xef, 0x10,
0x3a, 0x71, 0xfd, 0xc7, 0xa3, 0xa1, 0x63, 0x1d, 0xf1, 0xae, 0x77, 0x14, 0xf1, 0xf4, 0xd3, 0xfc,
0xff, 0x2f, 0xef, 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x88, 0xe3, 0xa5, 0xe6, 0xc8, 0x11, 0x00,
0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -1599,6 +2021,39 @@ type WalletKitClient interface {
//overwrite the exiting transaction label. Labels must not be empty, and
//cannot exceed 500 characters.
LabelTransaction(ctx context.Context, in *LabelTransactionRequest, opts ...grpc.CallOption) (*LabelTransactionResponse, error)
//
//FundPsbt creates a fully populated PSBT that contains enough inputs to fund
//the outputs specified in the template. There are two ways of specifying a
//template: Either by passing in a PSBT with at least one output declared or
//by passing in a raw TxTemplate message.
//
//If there are no inputs specified in the template, coin selection is
//performed automatically. If the template does contain any inputs, it is
//assumed that full coin selection happened externally and no additional
//inputs are added. If the specified inputs aren't enough to fund the outputs
//with the given fee rate, an error is returned.
//
//After either selecting or verifying the inputs, all input UTXOs are locked
//with an internal app ID.
//
//NOTE: If this method returns without an error, it is the caller's
//responsibility to either spend the locked UTXOs (by finalizing and then
//publishing the transaction) or to unlock/release the locked UTXOs in case of
//an error on the caller's side.
FundPsbt(ctx context.Context, in *FundPsbtRequest, opts ...grpc.CallOption) (*FundPsbtResponse, error)
//
//FinalizePsbt expects a partial transaction with all inputs and outputs fully
//declared and tries to sign all inputs that belong to the wallet. Lnd must be
//the last signer of the transaction. That means, if there are any unsigned
//non-witness inputs or inputs without UTXO information attached or inputs
//without witness data that do not belong to lnd's wallet, this method will
//fail. If no error is returned, the PSBT is ready to be extracted and the
//final TX within to be broadcast.
//
//NOTE: This method does NOT publish the transaction once finalized. It is the
//caller's responsibility to either publish the transaction on success or
//unlock/release any locked UTXOs in case of an error in this method.
FinalizePsbt(ctx context.Context, in *FinalizePsbtRequest, opts ...grpc.CallOption) (*FinalizePsbtResponse, error)
}
type walletKitClient struct {
@ -1726,6 +2181,24 @@ func (c *walletKitClient) LabelTransaction(ctx context.Context, in *LabelTransac
return out, nil
}
func (c *walletKitClient) FundPsbt(ctx context.Context, in *FundPsbtRequest, opts ...grpc.CallOption) (*FundPsbtResponse, error) {
out := new(FundPsbtResponse)
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/FundPsbt", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *walletKitClient) FinalizePsbt(ctx context.Context, in *FinalizePsbtRequest, opts ...grpc.CallOption) (*FinalizePsbtResponse, error) {
out := new(FinalizePsbtResponse)
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/FinalizePsbt", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// WalletKitServer is the server API for WalletKit service.
type WalletKitServer interface {
//
@ -1820,6 +2293,39 @@ type WalletKitServer interface {
//overwrite the exiting transaction label. Labels must not be empty, and
//cannot exceed 500 characters.
LabelTransaction(context.Context, *LabelTransactionRequest) (*LabelTransactionResponse, error)
//
//FundPsbt creates a fully populated PSBT that contains enough inputs to fund
//the outputs specified in the template. There are two ways of specifying a
//template: Either by passing in a PSBT with at least one output declared or
//by passing in a raw TxTemplate message.
//
//If there are no inputs specified in the template, coin selection is
//performed automatically. If the template does contain any inputs, it is
//assumed that full coin selection happened externally and no additional
//inputs are added. If the specified inputs aren't enough to fund the outputs
//with the given fee rate, an error is returned.
//
//After either selecting or verifying the inputs, all input UTXOs are locked
//with an internal app ID.
//
//NOTE: If this method returns without an error, it is the caller's
//responsibility to either spend the locked UTXOs (by finalizing and then
//publishing the transaction) or to unlock/release the locked UTXOs in case of
//an error on the caller's side.
FundPsbt(context.Context, *FundPsbtRequest) (*FundPsbtResponse, error)
//
//FinalizePsbt expects a partial transaction with all inputs and outputs fully
//declared and tries to sign all inputs that belong to the wallet. Lnd must be
//the last signer of the transaction. That means, if there are any unsigned
//non-witness inputs or inputs without UTXO information attached or inputs
//without witness data that do not belong to lnd's wallet, this method will
//fail. If no error is returned, the PSBT is ready to be extracted and the
//final TX within to be broadcast.
//
//NOTE: This method does NOT publish the transaction once finalized. It is the
//caller's responsibility to either publish the transaction on success or
//unlock/release any locked UTXOs in case of an error in this method.
FinalizePsbt(context.Context, *FinalizePsbtRequest) (*FinalizePsbtResponse, error)
}
// UnimplementedWalletKitServer can be embedded to have forward compatible implementations.
@ -1865,6 +2371,12 @@ func (*UnimplementedWalletKitServer) ListSweeps(ctx context.Context, req *ListSw
func (*UnimplementedWalletKitServer) LabelTransaction(ctx context.Context, req *LabelTransactionRequest) (*LabelTransactionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LabelTransaction not implemented")
}
func (*UnimplementedWalletKitServer) FundPsbt(ctx context.Context, req *FundPsbtRequest) (*FundPsbtResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FundPsbt not implemented")
}
func (*UnimplementedWalletKitServer) FinalizePsbt(ctx context.Context, req *FinalizePsbtRequest) (*FinalizePsbtResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FinalizePsbt not implemented")
}
func RegisterWalletKitServer(s *grpc.Server, srv WalletKitServer) {
s.RegisterService(&_WalletKit_serviceDesc, srv)
@ -2104,6 +2616,42 @@ func _WalletKit_LabelTransaction_Handler(srv interface{}, ctx context.Context, d
return interceptor(ctx, in, info, handler)
}
func _WalletKit_FundPsbt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FundPsbtRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WalletKitServer).FundPsbt(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/walletrpc.WalletKit/FundPsbt",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WalletKitServer).FundPsbt(ctx, req.(*FundPsbtRequest))
}
return interceptor(ctx, in, info, handler)
}
func _WalletKit_FinalizePsbt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FinalizePsbtRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WalletKitServer).FinalizePsbt(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/walletrpc.WalletKit/FinalizePsbt",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WalletKitServer).FinalizePsbt(ctx, req.(*FinalizePsbtRequest))
}
return interceptor(ctx, in, info, handler)
}
var _WalletKit_serviceDesc = grpc.ServiceDesc{
ServiceName: "walletrpc.WalletKit",
HandlerType: (*WalletKitServer)(nil),
@ -2160,6 +2708,14 @@ var _WalletKit_serviceDesc = grpc.ServiceDesc{
MethodName: "LabelTransaction",
Handler: _WalletKit_LabelTransaction_Handler,
},
{
MethodName: "FundPsbt",
Handler: _WalletKit_FundPsbt_Handler,
},
{
MethodName: "FinalizePsbt",
Handler: _WalletKit_FinalizePsbt_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "walletrpc/walletkit.proto",

View File

@ -476,6 +476,74 @@ func local_request_WalletKit_LabelTransaction_0(ctx context.Context, marshaler r
}
func request_WalletKit_FundPsbt_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FundPsbtRequest
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.FundPsbt(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_WalletKit_FundPsbt_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FundPsbtRequest
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.FundPsbt(ctx, &protoReq)
return msg, metadata, err
}
func request_WalletKit_FinalizePsbt_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FinalizePsbtRequest
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.FinalizePsbt(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_WalletKit_FinalizePsbt_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FinalizePsbtRequest
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.FinalizePsbt(ctx, &protoReq)
return msg, metadata, err
}
// RegisterWalletKitHandlerServer registers the http handlers for service WalletKit to "mux".
// UnaryRPC :call WalletKitServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -741,6 +809,46 @@ func RegisterWalletKitHandlerServer(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_WalletKit_FundPsbt_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.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_WalletKit_FundPsbt_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WalletKit_FundPsbt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_WalletKit_FinalizePsbt_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.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_WalletKit_FinalizePsbt_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WalletKit_FinalizePsbt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1042,6 +1150,46 @@ func RegisterWalletKitHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("POST", pattern_WalletKit_FundPsbt_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)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_WalletKit_FundPsbt_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WalletKit_FundPsbt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_WalletKit_FinalizePsbt_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)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_WalletKit_FinalizePsbt_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WalletKit_FinalizePsbt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1071,6 +1219,10 @@ var (
pattern_WalletKit_ListSweeps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "sweeps"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WalletKit_LabelTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "tx", "label"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WalletKit_FundPsbt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "psbt", "fund"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_WalletKit_FinalizePsbt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "psbt", "finalize"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
@ -1099,4 +1251,8 @@ var (
forward_WalletKit_ListSweeps_0 = runtime.ForwardResponseMessage
forward_WalletKit_LabelTransaction_0 = runtime.ForwardResponseMessage
forward_WalletKit_FundPsbt_0 = runtime.ForwardResponseMessage
forward_WalletKit_FinalizePsbt_0 = runtime.ForwardResponseMessage
)

View File

@ -128,6 +128,43 @@ service WalletKit {
*/
rpc LabelTransaction (LabelTransactionRequest)
returns (LabelTransactionResponse);
/*
FundPsbt creates a fully populated PSBT that contains enough inputs to fund
the outputs specified in the template. There are two ways of specifying a
template: Either by passing in a PSBT with at least one output declared or
by passing in a raw TxTemplate message.
If there are no inputs specified in the template, coin selection is
performed automatically. If the template does contain any inputs, it is
assumed that full coin selection happened externally and no additional
inputs are added. If the specified inputs aren't enough to fund the outputs
with the given fee rate, an error is returned.
After either selecting or verifying the inputs, all input UTXOs are locked
with an internal app ID.
NOTE: If this method returns without an error, it is the caller's
responsibility to either spend the locked UTXOs (by finalizing and then
publishing the transaction) or to unlock/release the locked UTXOs in case of
an error on the caller's side.
*/
rpc FundPsbt (FundPsbtRequest) returns (FundPsbtResponse);
/*
FinalizePsbt expects a partial transaction with all inputs and outputs fully
declared and tries to sign all inputs that belong to the wallet. Lnd must be
the last signer of the transaction. That means, if there are any unsigned
non-witness inputs or inputs without UTXO information attached or inputs
without witness data that do not belong to lnd's wallet, this method will
fail. If no error is returned, the PSBT is ready to be extracted and the
final TX within to be broadcast.
NOTE: This method does NOT publish the transaction once finalized. It is the
caller's responsibility to either publish the transaction on success or
unlock/release any locked UTXOs in case of an error in this method.
*/
rpc FinalizePsbt (FinalizePsbtRequest) returns (FinalizePsbtResponse);
}
message ListUnspentRequest {
@ -461,3 +498,103 @@ message LabelTransactionRequest {
message LabelTransactionResponse {
}
message FundPsbtRequest {
oneof template {
/*
Use an existing PSBT packet as the template for the funded PSBT.
The packet must contain at least one non-dust output. If one or more
inputs are specified, no coin selection is performed. In that case every
input must be an UTXO known to the wallet that has not been locked
before. The sum of all inputs must be sufficiently greater than the sum
of all outputs to pay a miner fee with the specified fee rate. A change
output is added to the PSBT if necessary.
*/
bytes psbt = 1;
/*
Use the outputs and optional inputs from this raw template.
*/
TxTemplate raw = 2;
}
oneof fees {
/*
The target number of blocks that the transaction should be confirmed in.
*/
uint32 target_conf = 3;
/*
The fee rate, expressed in sat/vbyte, that should be used to spend the
input with.
*/
uint32 sat_per_vbyte = 4;
}
}
message FundPsbtResponse {
/*
The funded but not yet signed PSBT packet.
*/
bytes funded_psbt = 1;
/*
The index of the added change output or -1 if no change was left over.
*/
int32 change_output_index = 2;
/*
The list of lock leases that were acquired for the inputs in the funded PSBT
packet.
*/
repeated UtxoLease locked_utxos = 3;
}
message TxTemplate {
/*
An optional list of inputs to use. Every input must be an UTXO known to the
wallet that has not been locked before. The sum of all inputs must be
sufficiently greater than the sum of all outputs to pay a miner fee with the
fee rate specified in the parent message.
If no inputs are specified, coin selection will be performed instead and
inputs of sufficient value will be added to the resulting PSBT.
*/
repeated lnrpc.OutPoint inputs = 1;
/*
A map of all addresses and the amounts to send to in the funded PSBT.
*/
map<string, uint64> outputs = 2;
}
message UtxoLease {
/*
A 32 byte random ID that identifies the lease.
*/
bytes id = 1;
// The identifying outpoint of the output being leased.
lnrpc.OutPoint outpoint = 2;
/*
The absolute expiration of the output lease represented as a unix timestamp.
*/
uint64 expiration = 3;
}
message FinalizePsbtRequest {
/*
A PSBT that should be signed and finalized. The PSBT must contain all
required inputs, outputs, UTXO data and partial signatures of all other
signers.
*/
bytes funded_psbt = 1;
}
message FinalizePsbtResponse {
// The fully signed and finalized transaction in PSBT format.
bytes signed_psbt = 1;
// The fully signed and finalized transaction in the raw wire format.
bytes raw_final_tx = 2;
}

View File

@ -177,6 +177,74 @@
]
}
},
"/v2/wallet/psbt/finalize": {
"post": {
"summary": "FinalizePsbt expects a partial transaction with all inputs and outputs fully\ndeclared and tries to sign all inputs that belong to the wallet. Lnd must be\nthe last signer of the transaction. That means, if there are any unsigned\nnon-witness inputs or inputs without UTXO information attached or inputs\nwithout witness data that do not belong to lnd's wallet, this method will\nfail. If no error is returned, the PSBT is ready to be extracted and the\nfinal TX within to be broadcast.",
"description": "NOTE: This method does NOT publish the transaction once finalized. It is the\ncaller's responsibility to either publish the transaction on success or\nunlock/release any locked UTXOs in case of an error in this method.",
"operationId": "FinalizePsbt",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/walletrpcFinalizePsbtResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/walletrpcFinalizePsbtRequest"
}
}
],
"tags": [
"WalletKit"
]
}
},
"/v2/wallet/psbt/fund": {
"post": {
"summary": "FundPsbt creates a fully populated PSBT that contains enough inputs to fund\nthe outputs specified in the template. There are two ways of specifying a\ntemplate: Either by passing in a PSBT with at least one output declared or\nby passing in a raw TxTemplate message.",
"description": "If there are no inputs specified in the template, coin selection is\nperformed automatically. If the template does contain any inputs, it is\nassumed that full coin selection happened externally and no additional\ninputs are added. If the specified inputs aren't enough to fund the outputs\nwith the given fee rate, an error is returned.\n\nAfter either selecting or verifying the inputs, all input UTXOs are locked\nwith an internal app ID.\n\nNOTE: If this method returns without an error, it is the caller's\nresponsibility to either spend the locked UTXOs (by finalizing and then\npublishing the transaction) or to unlock/release the locked UTXOs in case of\nan error on the caller's side.",
"operationId": "FundPsbt",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/walletrpcFundPsbtResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/walletrpcFundPsbtRequest"
}
}
],
"tags": [
"WalletKit"
]
}
},
"/v2/wallet/send": {
"post": {
"summary": "SendOutputs is similar to the existing sendmany call in Bitcoind, and\nallows the caller to create a transaction that sends to several outputs at\nonce. This is ideal when wanting to batch create a set of transactions.",
@ -689,6 +757,77 @@
}
}
},
"walletrpcFinalizePsbtRequest": {
"type": "object",
"properties": {
"funded_psbt": {
"type": "string",
"format": "byte",
"description": "A PSBT that should be signed and finalized. The PSBT must contain all\nrequired inputs, outputs, UTXO data and partial signatures of all other\nsigners."
}
}
},
"walletrpcFinalizePsbtResponse": {
"type": "object",
"properties": {
"signed_psbt": {
"type": "string",
"format": "byte",
"description": "The fully signed and finalized transaction in PSBT format."
},
"raw_final_tx": {
"type": "string",
"format": "byte",
"description": "The fully signed and finalized transaction in the raw wire format."
}
}
},
"walletrpcFundPsbtRequest": {
"type": "object",
"properties": {
"psbt": {
"type": "string",
"format": "byte",
"description": "Use an existing PSBT packet as the template for the funded PSBT.\n\nThe packet must contain at least one non-dust output. If one or more\ninputs are specified, no coin selection is performed. In that case every\ninput must be an UTXO known to the wallet that has not been locked\nbefore. The sum of all inputs must be sufficiently greater than the sum\nof all outputs to pay a miner fee with the specified fee rate. A change\noutput is added to the PSBT if necessary."
},
"raw": {
"$ref": "#/definitions/walletrpcTxTemplate",
"description": "Use the outputs and optional inputs from this raw template."
},
"target_conf": {
"type": "integer",
"format": "int64",
"description": "The target number of blocks that the transaction should be confirmed in."
},
"sat_per_vbyte": {
"type": "integer",
"format": "int64",
"description": "The fee rate, expressed in sat/vbyte, that should be used to spend the\ninput with."
}
}
},
"walletrpcFundPsbtResponse": {
"type": "object",
"properties": {
"funded_psbt": {
"type": "string",
"format": "byte",
"description": "The funded but not yet signed 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."
},
"locked_utxos": {
"type": "array",
"items": {
"$ref": "#/definitions/walletrpcUtxoLease"
},
"description": "The list of lock leases that were acquired for the inputs in the funded PSBT\npacket."
}
}
},
"walletrpcKeyReq": {
"type": "object",
"properties": {
@ -914,6 +1053,45 @@
}
}
},
"walletrpcTxTemplate": {
"type": "object",
"properties": {
"inputs": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcOutPoint"
},
"description": "An optional list of inputs to use. Every input must be an UTXO known to the\nwallet that has not been locked before. The sum of all inputs must be\nsufficiently greater than the sum of all outputs to pay a miner fee with the\nfee rate specified in the parent message.\n\nIf no inputs are specified, coin selection will be performed instead and\ninputs of sufficient value will be added to the resulting PSBT."
},
"outputs": {
"type": "object",
"additionalProperties": {
"type": "string",
"format": "uint64"
},
"description": "A map of all addresses and the amounts to send to in the funded PSBT."
}
}
},
"walletrpcUtxoLease": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "byte",
"description": "A 32 byte random ID that identifies the lease."
},
"outpoint": {
"$ref": "#/definitions/lnrpcOutPoint",
"description": "The identifying outpoint of the output being leased."
},
"expiration": {
"type": "string",
"format": "uint64",
"description": "The absolute expiration of the output lease represented as a unix timestamp."
}
}
},
"walletrpcWitnessType": {
"type": "string",
"enum": [

View File

@ -15,6 +15,8 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/psbt"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/input"
@ -114,12 +116,32 @@ var (
Entity: "onchain",
Action: "read",
}},
"/walletrpc.WalletKit/FundPsbt": {{
Entity: "onchain",
Action: "write",
}},
"/walletrpc.WalletKit/FinalizePsbt": {{
Entity: "onchain",
Action: "write",
}},
}
// DefaultWalletKitMacFilename is the default name of the wallet kit
// macaroon that we expect to find via a file handle within the main
// configuration file in this package.
DefaultWalletKitMacFilename = "walletkit.macaroon"
// LndInternalLockID is the binary representation of the SHA256 hash of
// the string "lnd-internal-lock-id" and is used for UTXO lock leases to
// identify that we ourselves are locking an UTXO, for example when
// giving out a funded PSBT. The ID corresponds to the hex value of
// ede19a92ed321a4705f8a1cccc1d4f6182545d4bb4fae08bd5937831b7e38f98.
LndInternalLockID = wtxmgr.LockID{
0xed, 0xe1, 0x9a, 0x92, 0xed, 0x32, 0x1a, 0x47,
0x05, 0xf8, 0xa1, 0xcc, 0xcc, 0x1d, 0x4f, 0x61,
0x82, 0x54, 0x5d, 0x4b, 0xb4, 0xfa, 0xe0, 0x8b,
0xd5, 0x93, 0x78, 0x31, 0xb7, 0xe3, 0x8f, 0x98,
}
)
// ErrZeroLabel is returned when an attempt is made to label a transaction with
@ -830,3 +852,262 @@ func (w *WalletKit) LabelTransaction(ctx context.Context,
err = w.cfg.Wallet.LabelTransaction(*hash, req.Label, req.Overwrite)
return &LabelTransactionResponse{}, err
}
// FundPsbt creates a fully populated PSBT that contains enough inputs to fund
// the outputs specified in the template. There are two ways of specifying a
// template: Either by passing in a PSBT with at least one output declared or
// by passing in a raw TxTemplate message. If there are no inputs specified in
// the template, coin selection is performed automatically. If the template does
// contain any inputs, it is assumed that full coin selection happened
// externally and no additional inputs are added. If the specified inputs aren't
// enough to fund the outputs with the given fee rate, an error is returned.
// After either selecting or verifying the inputs, all input UTXOs are locked
// with an internal app ID.
//
// NOTE: If this method returns without an error, it is the caller's
// responsibility to either spend the locked UTXOs (by finalizing and then
// publishing the transaction) or to unlock/release the locked UTXOs in case of
// an error on the caller's side.
func (w *WalletKit) FundPsbt(_ context.Context,
req *FundPsbtRequest) (*FundPsbtResponse, error) {
var (
err error
packet *psbt.Packet
feeSatPerKW chainfee.SatPerKWeight
locks []*utxoLock
rawPsbt bytes.Buffer
)
// There are two ways a user can specify what we call the template (a
// list of inputs and outputs to use in the PSBT): Either as a PSBT
// packet directly or as a special RPC message. Find out which one the
// user wants to use, they are mutually exclusive.
switch {
// The template is specified as a PSBT. All we have to do is parse it.
case req.GetPsbt() != nil:
r := bytes.NewReader(req.GetPsbt())
packet, err = psbt.NewFromRawBytes(r, false)
if err != nil {
return nil, fmt.Errorf("could not parse PSBT: %v", err)
}
// The template is specified as a RPC message. We need to create a new
// PSBT and copy the RPC information over.
case req.GetRaw() != nil:
tpl := req.GetRaw()
if len(tpl.Outputs) == 0 {
return nil, fmt.Errorf("no outputs specified")
}
txOut := make([]*wire.TxOut, 0, len(tpl.Outputs))
for addrStr, amt := range tpl.Outputs {
addr, err := btcutil.DecodeAddress(
addrStr, w.cfg.ChainParams,
)
if err != nil {
return nil, fmt.Errorf("error parsing address "+
"%s for network %s: %v", addrStr,
w.cfg.ChainParams.Name, err)
}
pkScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, fmt.Errorf("error getting pk "+
"script for address %s: %v", addrStr,
err)
}
txOut = append(txOut, &wire.TxOut{
Value: int64(amt),
PkScript: pkScript,
})
}
txIn := make([]*wire.OutPoint, len(tpl.Inputs))
for idx, in := range tpl.Inputs {
op, err := unmarshallOutPoint(in)
if err != nil {
return nil, fmt.Errorf("error parsing "+
"outpoint: %v", err)
}
txIn[idx] = op
}
sequences := make([]uint32, len(txIn))
packet, err = psbt.New(txIn, txOut, 2, 0, sequences)
if err != nil {
return nil, fmt.Errorf("could not create PSBT: %v", err)
}
default:
return nil, fmt.Errorf("transaction template missing, need " +
"to specify either PSBT or raw TX template")
}
// Determine the desired transaction fee.
switch {
// Estimate the fee by the target number of blocks to confirmation.
case req.GetTargetConf() != 0:
targetConf := req.GetTargetConf()
if targetConf < 2 {
return nil, fmt.Errorf("confirmation target must be " +
"greater than 1")
}
feeSatPerKW, err = w.cfg.FeeEstimator.EstimateFeePerKW(
targetConf,
)
if err != nil {
return nil, fmt.Errorf("could not estimate fee: %v",
err)
}
// Convert the fee to sat/kW from the specified sat/vByte.
case req.GetSatPerVbyte() != 0:
feeSatPerKW = chainfee.SatPerKVByte(
req.GetSatPerVbyte() * 1000,
).FeePerKWeight()
default:
return nil, fmt.Errorf("fee definition missing, need to " +
"specify either target_conf or set_per_vbyte")
}
// The RPC parsing part is now over. Several of the following operations
// require us to hold the global coin selection lock so we do the rest
// of the tasks while holding the lock. The result is a list of locked
// UTXOs.
changeIndex := int32(-1)
err = w.cfg.CoinSelectionLocker.WithCoinSelectLock(func() error {
// In case the user did specify inputs, we need to make sure
// they are known to us, still unspent and not yet locked.
if len(packet.UnsignedTx.TxIn) > 0 {
// Get a list of all unspent witness outputs.
utxos, err := w.cfg.Wallet.ListUnspentWitness(
defaultMinConf, defaultMaxConf,
)
if err != nil {
return err
}
// Validate all inputs against our known list of UTXOs
// now.
err = verifyInputsUnspent(packet.UnsignedTx.TxIn, utxos)
if err != nil {
return err
}
}
// We made sure the input from the user is as sane as possible.
// We can now ask the wallet to fund the TX. This will not yet
// lock any coins but might still change the wallet DB by
// generating a new change address.
changeIndex, err = w.cfg.Wallet.FundPsbt(packet, feeSatPerKW)
if err != nil {
return fmt.Errorf("wallet couldn't fund PSBT: %v", err)
}
// Make sure we can properly serialize the packet. If this goes
// wrong then something isn't right with the inputs and we
// probably shouldn't try to lock any of them.
err = packet.Serialize(&rawPsbt)
if err != nil {
return fmt.Errorf("error serializing funded PSBT: %v",
err)
}
// Now we have obtained a set of coins that can be used to fund
// the TX. Let's lock them to be sure they aren't spent by the
// time the PSBT is published. This is the action we do here
// that could cause an error. Therefore if some of the UTXOs
// cannot be locked, the rollback of the other's locks also
// happens in this function. If we ever need to do more after
// this function, we need to extract the rollback needs to be
// extracted into a defer.
locks, err = lockInputs(w.cfg.Wallet, packet)
if err != nil {
return fmt.Errorf("could not lock inputs: %v", err)
}
return nil
})
if err != nil {
return nil, err
}
// Convert the lock leases to the RPC format.
rpcLocks := make([]*UtxoLease, len(locks))
for idx, lock := range locks {
rpcLocks[idx] = &UtxoLease{
Id: lock.lockID[:],
Outpoint: &lnrpc.OutPoint{
TxidBytes: lock.outpoint.Hash[:],
TxidStr: lock.outpoint.String(),
OutputIndex: lock.outpoint.Index,
},
Expiration: uint64(lock.expiration.Unix()),
}
}
return &FundPsbtResponse{
FundedPsbt: rawPsbt.Bytes(),
ChangeOutputIndex: changeIndex,
LockedUtxos: rpcLocks,
}, nil
}
// FinalizePsbt expects a partial transaction with all inputs and outputs fully
// declared and tries to sign all inputs that belong to the wallet. Lnd must be
// the last signer of the transaction. That means, if there are any unsigned
// non-witness inputs or inputs without UTXO information attached or inputs
// without witness data that do not belong to lnd's wallet, this method will
// fail. If no error is returned, the PSBT is ready to be extracted and the
// final TX within to be broadcast.
//
// NOTE: This method does NOT publish the transaction once finalized. It is the
// caller's responsibility to either publish the transaction on success or
// unlock/release any locked UTXOs in case of an error in this method.
func (w *WalletKit) FinalizePsbt(_ context.Context,
req *FinalizePsbtRequest) (*FinalizePsbtResponse, error) {
// Parse the funded PSBT. No additional checks are required at this
// level as the wallet will perform all of them.
packet, err := psbt.NewFromRawBytes(
bytes.NewReader(req.FundedPsbt), false,
)
if err != nil {
return nil, fmt.Errorf("error parsing PSBT: %v", err)
}
// Let the wallet do the heavy lifting. This will sign all inputs that
// we have the UTXO for. If some inputs can't be signed and don't have
// witness data attached, this will fail.
err = w.cfg.Wallet.FinalizePsbt(packet)
if err != nil {
return nil, fmt.Errorf("error finalizing PSBT: %v", err)
}
var (
finalPsbtBytes bytes.Buffer
finalTxBytes bytes.Buffer
)
// Serialize the finalized PSBT in both the packet and wire format.
err = packet.Serialize(&finalPsbtBytes)
if err != nil {
return nil, fmt.Errorf("error serializing PSBT: %v", err)
}
finalTx, err := psbt.Extract(packet)
if err != nil {
return nil, fmt.Errorf("unable to extract final TX: %v", err)
}
err = finalTx.Serialize(&finalTxBytes)
if err != nil {
return nil, fmt.Errorf("error serializing final TX: %v", err)
}
return &FinalizePsbtResponse{
SignedPsbt: finalPsbtBytes.Bytes(),
RawFinalTx: finalTxBytes.Bytes(),
}, nil
}