rpcserver: implement PSBT funding flow
A PSBT funding flow consists of multiple steps. We add new RPC messages that can trigger the underlying state machine to transition to a new state. We also add new response messages that tell the API user what the current state is.
This commit is contained in:
parent
5a52420ab6
commit
376a747bb2
134
rpcserver.go
134
rpcserver.go
@ -19,10 +19,12 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"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/wallet/txauthor"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
@ -1556,6 +1558,51 @@ func newFundingShimAssembler(chanPointShim *lnrpc.ChanPointShim, initiator bool,
|
||||
), nil
|
||||
}
|
||||
|
||||
// newFundingShimAssembler returns a new fully populated
|
||||
// chanfunding.PsbtAssembler using a FundingShim obtained from an RPC caller.
|
||||
func newPsbtAssembler(req *lnrpc.OpenChannelRequest, normalizedMinConfs int32,
|
||||
psbtShim *lnrpc.PsbtShim, netParams *chaincfg.Params) (
|
||||
chanfunding.Assembler, error) {
|
||||
|
||||
var (
|
||||
packet *psbt.Packet
|
||||
err error
|
||||
)
|
||||
|
||||
// Perform some basic sanity checks to ensure that all the expected
|
||||
// fields are populated and none of the incompatible fields are.
|
||||
if len(psbtShim.PendingChanId) != 32 {
|
||||
return nil, fmt.Errorf("pending chan ID not set")
|
||||
}
|
||||
if normalizedMinConfs != 1 {
|
||||
return nil, fmt.Errorf("setting non-default values for " +
|
||||
"minimum confirmation is not supported for PSBT " +
|
||||
"funding")
|
||||
}
|
||||
if req.SatPerByte != 0 || req.TargetConf != 0 {
|
||||
return nil, fmt.Errorf("specifying fee estimation parameters " +
|
||||
"is not supported for PSBT funding")
|
||||
}
|
||||
|
||||
// The base PSBT is optional. But if it's set, it has to be a valid,
|
||||
// binary serialized PSBT.
|
||||
if len(psbtShim.BasePsbt) > 0 {
|
||||
packet, err = psbt.NewFromRawBytes(
|
||||
bytes.NewReader(psbtShim.BasePsbt), false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing base PSBT: %v",
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// With all the parts assembled, we can now make the canned assembler
|
||||
// to pass into the wallet.
|
||||
return chanfunding.NewPsbtAssembler(
|
||||
btcutil.Amount(req.LocalFundingAmount), packet, netParams,
|
||||
), nil
|
||||
}
|
||||
|
||||
// OpenChannel attempts to open a singly funded channel specified in the
|
||||
// request to a remote peer.
|
||||
func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
|
||||
@ -1675,10 +1722,11 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
|
||||
// If the user has provided a shim, then we'll now augment the based
|
||||
// open channel request with this additional logic.
|
||||
if in.FundingShim != nil {
|
||||
switch {
|
||||
// If we have a chan point shim, then this means the funding
|
||||
// transaction was crafted externally. In this case we only
|
||||
// need to hand a channel point down into the wallet.
|
||||
if in.FundingShim.GetChanPointShim() != nil {
|
||||
case in.FundingShim.GetChanPointShim() != nil:
|
||||
chanPointShim := in.FundingShim.GetChanPointShim()
|
||||
|
||||
// Map the channel point shim into a new
|
||||
@ -1691,6 +1739,25 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we have a PSBT shim, then this means the funding
|
||||
// transaction will be crafted outside of the wallet, once the
|
||||
// funding multisig output script is known. We'll create an
|
||||
// intent that will supervise the multi-step process.
|
||||
case in.FundingShim.GetPsbtShim() != nil:
|
||||
psbtShim := in.FundingShim.GetPsbtShim()
|
||||
|
||||
// Instruct the wallet to use the new
|
||||
// chanfunding.PsbtAssembler to construct the funding
|
||||
// transaction.
|
||||
copy(req.pendingChanID[:], psbtShim.PendingChanId)
|
||||
req.chanFunder, err = newPsbtAssembler(
|
||||
in, minConfs, psbtShim,
|
||||
&r.server.cc.wallet.Cfg.NetParams,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6236,6 +6303,7 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
||||
func (r *rpcServer) FundingStateStep(ctx context.Context,
|
||||
in *lnrpc.FundingTransitionMsg) (*lnrpc.FundingStateStepResp, error) {
|
||||
|
||||
var pendingChanID [32]byte
|
||||
switch {
|
||||
|
||||
// If this is a message to register a new shim that is an external
|
||||
@ -6269,7 +6337,6 @@ func (r *rpcServer) FundingStateStep(ctx context.Context,
|
||||
// Once we receive an incoming funding request that uses this
|
||||
// pending channel ID, then this shim will be dispatched in
|
||||
// place of our regular funding workflow.
|
||||
var pendingChanID [32]byte
|
||||
copy(pendingChanID[:], rpcShimIntent.PendingChanId)
|
||||
err = r.server.cc.wallet.RegisterFundingIntent(
|
||||
pendingChanID, shimIntent,
|
||||
@ -6278,18 +6345,71 @@ func (r *rpcServer) FundingStateStep(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// There is no need to register a PSBT shim before opening the channel,
|
||||
// even though our RPC message structure allows for it. Inform the user
|
||||
// by returning a proper error instead of just doing nothing.
|
||||
case in.GetShimRegister() != nil &&
|
||||
in.GetShimRegister().GetPsbtShim() != nil:
|
||||
|
||||
return nil, fmt.Errorf("PSBT shim must only be sent when " +
|
||||
"opening a channel")
|
||||
|
||||
// If this is a transition to cancel an existing shim, then we'll pass
|
||||
// this message along to the wallet.
|
||||
// this message along to the wallet, informing it that the intent no
|
||||
// longer needs to be considered and should be cleaned up.
|
||||
case in.GetShimCancel() != nil:
|
||||
pid := in.GetShimCancel().PendingChanId
|
||||
|
||||
var pendingChanID [32]byte
|
||||
copy(pendingChanID[:], pid)
|
||||
rpcsLog.Debugf("Canceling funding shim for pending_id=%x",
|
||||
in.GetShimCancel().PendingChanId)
|
||||
|
||||
copy(pendingChanID[:], in.GetShimCancel().PendingChanId)
|
||||
err := r.server.cc.wallet.CancelFundingIntent(pendingChanID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If this is a transition to verify the PSBT for an existing shim,
|
||||
// we'll do so and then store the verified PSBT for later so we can
|
||||
// compare it to the final, signed one.
|
||||
case in.GetPsbtVerify() != nil:
|
||||
rpcsLog.Debugf("Verifying PSBT for pending_id=%x",
|
||||
in.GetPsbtVerify().PendingChanId)
|
||||
|
||||
copy(pendingChanID[:], in.GetPsbtVerify().PendingChanId)
|
||||
packet, err := psbt.NewFromRawBytes(
|
||||
bytes.NewReader(in.GetPsbtVerify().FundedPsbt), false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing psbt: %v", err)
|
||||
}
|
||||
|
||||
err = r.server.cc.wallet.PsbtFundingVerify(
|
||||
pendingChanID, packet,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If this is a transition to finalize the PSBT funding flow, we compare
|
||||
// the final PSBT to the previously verified one and if nothing
|
||||
// unexpected was changed, continue the channel opening process.
|
||||
case in.GetPsbtFinalize() != nil:
|
||||
rpcsLog.Debugf("Finalizing PSBT for pending_id=%x",
|
||||
in.GetPsbtFinalize().PendingChanId)
|
||||
|
||||
copy(pendingChanID[:], in.GetPsbtFinalize().PendingChanId)
|
||||
packet, err := psbt.NewFromRawBytes(
|
||||
bytes.NewReader(in.GetPsbtFinalize().SignedPsbt), false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing psbt: %v", err)
|
||||
}
|
||||
|
||||
err = r.server.cc.wallet.PsbtFundingFinalize(
|
||||
pendingChanID, packet,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(roasbeef): extend PendingChannels to also show shims
|
||||
|
Loading…
Reference in New Issue
Block a user