forked from lightninglabs/taproot-assets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
psbt_channel_funder.go
148 lines (128 loc) · 4.45 KB
/
psbt_channel_funder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package taprootassets
import (
"bytes"
"context"
"fmt"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/taproot-assets/tapchannel"
"github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/routing/route"
)
const (
// CustomChannelRemoteReserve is the custom channel minimum remote
// reserve that we'll use for our channels.
CustomChannelRemoteReserve = 1062
)
// LndPbstChannelFunder is an implementation of the tapchannel.ChannelFunder
// interface that uses lnd to carry out the PSBT funding process.
type LndPbstChannelFunder struct {
lnd *lndclient.LndServices
}
// NewLndPbstChannelFunder creates a new LndPbstChannelFunder instance.
func NewLndPbstChannelFunder(lnd *lndclient.LndServices) *LndPbstChannelFunder {
return &LndPbstChannelFunder{
lnd: lnd,
}
}
// assetChanIntent is a concrete implementation of the
// tapchannel.AssetChanIntent.
type assetChanIntent struct {
psbtTemplate *psbt.Packet
lnd *lndclient.LndServices
tempPID funding.PendingChanID
}
// FundingPsbt is the original PsbtTemplate, plus the P2TR funding output
// that'll create the channel.
func (a *assetChanIntent) FundingPsbt() (*psbt.Packet, error) {
return a.psbtTemplate, nil
}
// BindPsbt accepts a new *unsigned* PSBT with any additional inputs or outputs
// (for change) added. This PSBT is still unsigned. This step performs final
// verification to ensure the PSBT is crafted in a manner that'll properly open
// the channel once broadcaster.
func (a *assetChanIntent) BindPsbt(ctx context.Context,
finalPSBT *psbt.Packet) error {
var psbtBuf bytes.Buffer
if err := finalPSBT.Serialize(&psbtBuf); err != nil {
return fmt.Errorf("unable to serialize base PSBT: %w", err)
}
_, err := a.lnd.Client.FundingStateStep(
ctx, &lnrpc.FundingTransitionMsg{
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
PsbtVerify: &lnrpc.FundingPsbtVerify{
PendingChanId: a.tempPID[:],
FundedPsbt: psbtBuf.Bytes(),
SkipFinalize: true,
},
},
},
)
return err
}
// OpenChannel attempts to open a new asset holding private channel using the
// backing lnd node. The PSBT flow is by default. An AssetChanIntent is
// returned that includes the updated PSBT template that includes the funding
// output. Once all other inputs+outputs have been added, then BindPsbt should
// be called to progress the funding process. Afterward, the funding
// transaction should be signed+broadcast.
//
// NOTE: This is part of the tapchannel.ChannelFunder interface.
func (l *LndPbstChannelFunder) OpenChannel(ctx context.Context,
req tapchannel.OpenChanReq) (tapchannel.AssetChanIntent, error) {
var psbtBuf bytes.Buffer
if req.PsbtTemplate != nil {
err := req.PsbtTemplate.Serialize(&psbtBuf)
if err != nil {
return nil, fmt.Errorf("unable to serialize base "+
"PSBT: %w", err)
}
}
// We'll map our high level params into a request for a: private,
// taproot channel, that uses the PSBT funding flow.
taprootCommitType := lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
openChanStream, errChan, err := l.lnd.Client.OpenChannelStream(
ctx, route.NewVertex(&req.PeerPub), req.ChanAmt, req.PushAmt,
true, lndclient.WithCommitmentType(&taprootCommitType),
lndclient.WithFundingShim(&lnrpc.FundingShim{
Shim: &lnrpc.FundingShim_PsbtShim{
PsbtShim: &lnrpc.PsbtShim{
PendingChanId: req.TempPID[:],
NoPublish: true,
BasePsbt: psbtBuf.Bytes(),
},
},
}),
lndclient.WithRemoteReserve(CustomChannelRemoteReserve),
)
if err != nil {
return nil, fmt.Errorf("unable to open channel with "+
"lnd: %w", err)
}
// With our request extended, we'll now wait for the initial response
// sent after the responder sends AcceptChannel.
select {
case resp := <-openChanStream:
// Assert that we have a PSBT response from the node.
if resp.PsbtFund == nil {
return nil, fmt.Errorf("expected PSBT funding response")
}
fundingPSBT, err := psbt.NewFromRawBytes(
bytes.NewReader(resp.PsbtFund.Psbt), false,
)
if err != nil {
return nil, fmt.Errorf("unable to parse PSBT: %w", err)
}
return &assetChanIntent{
psbtTemplate: fundingPSBT,
lnd: l.lnd,
tempPID: req.TempPID,
}, nil
case err := <-errChan:
return nil, err
}
}
// A compile-time check to ensure that LndPbstChannelFunder fully implements
// the tapchannel.PsbtChannelFunder interface.
var _ tapchannel.PsbtChannelFunder = (*LndPbstChannelFunder)(nil)