From 52cdb3d5f98ab0cdd4a7280395bc267a6ec41b60 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 22 Sep 2023 17:13:33 +0200 Subject: [PATCH] multi: add simple taproot channel type to order --- auctioneer/client.go | 2 ++ chaninfo/channel_info.go | 3 ++- channel_acceptor.go | 21 +++++++++++++++++--- cmd/pool/order.go | 10 +++++++--- funding/manager.go | 41 +++++++++++++++++++++++++++------------ order/batch.go | 9 ++++++--- order/batch_verifier.go | 29 +++++++++++++++++++++++++++ order/interfaces.go | 10 +++++++--- order/rpc_parse.go | 6 ++++++ poolscript/script.go | 42 ++++++++++++++++++++++++++++++++++++++++ rpcserver.go | 14 ++++++++++++-- 11 files changed, 160 insertions(+), 27 deletions(-) diff --git a/auctioneer/client.go b/auctioneer/client.go index d4b25d9..fcdf5c7 100644 --- a/auctioneer/client.go +++ b/auctioneer/client.go @@ -427,6 +427,8 @@ func (c *Client) SubmitOrder(ctx context.Context, o order.Order, channelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT case order.ChannelTypeScriptEnforced: channelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED + case order.ChannelTypeSimpleTaproot: + channelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SIMPLE_TAPROOT default: return fmt.Errorf("unhandled channel type %v", c) } diff --git a/chaninfo/channel_info.go b/chaninfo/channel_info.go index 637a87b..d3fe523 100644 --- a/chaninfo/channel_info.go +++ b/chaninfo/channel_info.go @@ -151,7 +151,8 @@ func isSupportedBackupVersion(backup *chanbackup.Single) bool { case chanbackup.TweaklessCommitVersion, chanbackup.AnchorsCommitVersion, chanbackup.AnchorsZeroFeeHtlcTxCommitVersion, - chanbackup.ScriptEnforcedLeaseVersion: + chanbackup.ScriptEnforcedLeaseVersion, + chanbackup.SimpleTaprootVersion: return true default: diff --git a/channel_acceptor.go b/channel_acceptor.go index 6ccebd1..53f5970 100644 --- a/channel_acceptor.go +++ b/channel_acceptor.go @@ -155,9 +155,8 @@ func (s *ChannelAcceptor) acceptChannel(_ context.Context, }, nil } - switch *req.CommitmentType { - case lnwallet.CommitmentTypeScriptEnforcedLease: - default: + const expectedType = lnwallet.CommitmentTypeScriptEnforcedLease + if *req.CommitmentType != expectedType { return &lndclient.AcceptorResponse{ Accept: false, Error: "expected script enforced channel " + @@ -165,6 +164,22 @@ func (s *ChannelAcceptor) acceptChannel(_ context.Context, }, nil } + case order.ChannelTypeSimpleTaproot: + if req.CommitmentType == nil { + return &lndclient.AcceptorResponse{ + Accept: false, + Error: "expected explicit channel negotiation", + }, nil + } + + if *req.CommitmentType != lnwallet.CommitmentTypeSimpleTaproot { + return &lndclient.AcceptorResponse{ + Accept: false, + Error: "expected simple taproot channel " + + "commitment type", + }, nil + } + default: log.Warnf("Unhandled channel type %v for bid %v", expectedChanBid.ChannelType, expectedChanBid.Nonce()) diff --git a/cmd/pool/order.go b/cmd/pool/order.go index 62874ad..34e1269 100644 --- a/cmd/pool/order.go +++ b/cmd/pool/order.go @@ -24,6 +24,7 @@ const ( channelTypePeerDependent = "legacy" channelTypeScriptEnforced = "script-enforced" + channelTypeSimpleTaproot = "simple-taproot" auctionTypeInboundLiquidity = "inbound" auctionTypeOutboundLiquidity = "outbound" @@ -151,9 +152,9 @@ var sharedFlags = []cli.Flag{ cli.StringFlag{ Name: "channel_type", Usage: fmt.Sprintf("the type of channel resulting from the "+ - "order being matched (%q, %q)", - channelTypePeerDependent, - channelTypeScriptEnforced), + "order being matched (%q, %q, %q)", + channelTypePeerDependent, channelTypeScriptEnforced, + channelTypeSimpleTaproot), }, cli.StringFlag{ Name: "auction_type", @@ -434,6 +435,9 @@ func parseCommonParams(ctx *cli.Context, blockDuration uint32) (*poolrpc.Order, case channelTypeScriptEnforced: params.ChannelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED + case channelTypeSimpleTaproot: + params.ChannelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SIMPLE_TAPROOT + default: return nil, fmt.Errorf("unknown channel type %q", channelType) } diff --git a/funding/manager.go b/funding/manager.go index bd4b7b0..917d9ff 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -20,6 +20,7 @@ import ( "github.com/lightninglabs/pool/chaninfo" "github.com/lightninglabs/pool/clientdb" "github.com/lightninglabs/pool/order" + "github.com/lightninglabs/pool/poolscript" "github.com/lightninglabs/pool/sidecar" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -355,14 +356,19 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order, } } - _, fundingOutput, err := input.GenFundingPkScript( - ourMultiSigKey.PubKey.SerializeCompressed(), + commitmentType, musig2 := order.DetermineCommitmentType( + ourOrder.Details(), matchedOrder.Order.Details(), + ) + fundingOutput, err := poolscript.FundingOutput( + commitmentType, ourMultiSigKey.PubKey.SerializeCompressed(), matchedOrder.MultiSigKey[:], int64(chanSize), ) if err != nil { return nil, [32]byte{}, err } + log.Debugf("Funding output pkScript: %x", fundingOutput.PkScript) + // Now that we have the funding script, we'll find the output index // within the batch execution transaction. We ignore the first // argument, as earlier during validation, we would've rejected the @@ -378,6 +384,9 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order, OutputIndex: chanOutputIndex, } + log.Debugf("Found funding output at index %v of batch TX %v", + chanOutputIndex, batchTxID.String()) + // With all the components assembled, we'll now create the chan point // shim, and register it so we use the proper funding key when we // receive the marker's incoming funding request. @@ -394,6 +403,7 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order, RemoteKey: matchedOrder.MultiSigKey[:], PendingChanId: pendingChanID[:], ThawHeight: thawHeight, + Musig2: musig2, } return &lnrpc.FundingShim{ @@ -654,10 +664,10 @@ func (m *Manager) BatchChannelSetup( if err != nil { return nil, err } + chanPointShim := fundingShim.GetChanPointShim() chanPoint := wire.OutPoint{ - Hash: batchTxHash, - Index: fundingShim.GetChanPointShim().ChanPoint. - OutputIndex, + Hash: batchTxHash, + Index: chanPointShim.ChanPoint.OutputIndex, } // Some goroutines are already running from previous @@ -689,14 +699,21 @@ func (m *Manager) BatchChannelSetup( // * also other params to set as well chanAmt := matchedOrder.UnitsFilled.ToSatoshis() chanAmt += matchedOrderBid.SelfChanBalance - var commitmentType lnrpc.CommitmentType - switch { - case ourOrder.Details().ChannelType == order.ChannelTypeScriptEnforced: - fallthrough - case matchedOrderBid.Details().ChannelType == order.ChannelTypeScriptEnforced: - commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE - } + + commitmentType, _ := order.DetermineCommitmentType( + ourOrder.Details(), + matchedOrder.Order.Details(), + ) private := matchedOrderBid.UnannouncedChannel + + log.Debugf("Opening channel to node=%x, private=%v, "+ + "chan_point=%v, chan_amt=%v, commit_type=%v, "+ + "musig2=%v, zero_conf=%v, thaw_height=%v", + matchedOrder.NodeKey[:], private, chanPoint, + chanAmt, commitmentType, chanPointShim.Musig2, + matchedOrderBid.ZeroConfChannel, + chanPointShim.ThawHeight) + fundingReq := &lnrpc.OpenChannelRequest{ NodePubkey: matchedOrder.NodeKey[:], LocalFundingAmount: int64(chanAmt), diff --git a/order/batch.go b/order/batch.go index 5823cd3..6712f63 100644 --- a/order/batch.go +++ b/order/batch.go @@ -15,7 +15,6 @@ import ( "github.com/lightninglabs/pool/auctioneerrpc" "github.com/lightninglabs/pool/poolscript" "github.com/lightninglabs/pool/terms" - "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -363,9 +362,13 @@ func ChannelOutput(batchTx *wire.MsgTx, wallet lndclient.WalletKitClient, } // Gather the information we expect to find in the batch TX. + commitType, _ := DetermineCommitmentType( + ourOrder.Details(), otherOrder.Order.Details(), + ) expectedOutputSize := selfChanBalance + otherOrder.UnitsFilled.ToSatoshis() - _, expectedOut, err := input.GenFundingPkScript( - ourKey, otherOrder.MultiSigKey[:], int64(expectedOutputSize), + expectedOut, err := poolscript.FundingOutput( + commitType, ourKey, otherOrder.MultiSigKey[:], + int64(expectedOutputSize), ) if err != nil { return nil, 0, fmt.Errorf("could not create multisig script: "+ diff --git a/order/batch_verifier.go b/order/batch_verifier.go index a5b0862..8a9ba88 100644 --- a/order/batch_verifier.go +++ b/order/batch_verifier.go @@ -9,6 +9,7 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/terms" + "github.com/lightningnetwork/lnd/lnrpc" ) const ( @@ -371,3 +372,31 @@ func (v *batchVerifier) validateChannelOutput(batch *Batch, ourOrder Order, // A compile-time constraint to ensure batchVerifier implements BatchVerifier. var _ BatchVerifier = (*batchVerifier)(nil) + +// DetermineCommitmentType determines the type of channel to open based on our +// order and the one matched to us and also returns whether that channel type +// uses a Musig2 construction or not. +func DetermineCommitmentType(ourOrder, + theirOrder *Kit) (lnrpc.CommitmentType, bool) { + + switch { + // Since everyone needs to be on lnd 0.15.5+ because of the chain sync + // issue, we can safely assume that everyone supports the new script + // enforced type. So if one side indicates they want it, we'll use it. + case ourOrder.ChannelType == ChannelTypeScriptEnforced || + theirOrder.ChannelType == ChannelTypeScriptEnforced: + + return lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, false + + // For Simple Taproot channels, both sides need to activate them, so we + // need to make sure both parties explicitly requested them (which is + // gated by the startup feature check). + case ourOrder.ChannelType == ChannelTypeSimpleTaproot && + theirOrder.ChannelType == ChannelTypeSimpleTaproot: + + return lnrpc.CommitmentType_SIMPLE_TAPROOT, true + + default: + return lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE, false + } +} diff --git a/order/interfaces.go b/order/interfaces.go index bc84cff..b6afda4 100644 --- a/order/interfaces.go +++ b/order/interfaces.go @@ -134,7 +134,7 @@ const ( StateFailed State = 6 ) -// String returns a human readable string representation of the order state. +// String returns a human-readable string representation of the order state. func (s State) String() string { switch s { case StateSubmitted: @@ -186,7 +186,7 @@ const ( // OrderMatchPrepare message was received initially. MatchStatePrepare MatchState = 0 - // MatchStatePrepare is the state an order is in after the + // MatchStateAccepted is the state an order is in after the // OrderMatchPrepare message was processed successfully and the batch // was accepted. MatchStateAccepted MatchState = 1 @@ -205,7 +205,7 @@ const ( MatchStateFinalized MatchState = 4 ) -// String returns a human readable string representation of the match state. +// String returns a human-readable string representation of the match state. func (s MatchState) String() string { switch s { case MatchStatePrepare: @@ -244,6 +244,10 @@ const ( // leased channel in the commitment and HTLC outputs that pay directly // to the channel initiator. ChannelTypeScriptEnforced ChannelType = 1 + + // ChannelTypeSimpleTaproot represents a channel type that uses a + // Pay-To-Taproot funding output. + ChannelTypeSimpleTaproot ChannelType = 2 ) // ChannelAnnouncementConstraints is a numerical type used to denote if the diff --git a/order/rpc_parse.go b/order/rpc_parse.go index abe1a38..37b0e3c 100644 --- a/order/rpc_parse.go +++ b/order/rpc_parse.go @@ -115,6 +115,9 @@ func ParseRPCOrder(version, leaseDuration uint32, case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED: kit.ChannelType = ChannelTypeScriptEnforced + case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SIMPLE_TAPROOT: + kit.ChannelType = ChannelTypeSimpleTaproot + default: return nil, fmt.Errorf("unhandled channel type %v", details.ChannelType) @@ -260,6 +263,9 @@ func ParseRPCServerOrder(version uint32, details *auctioneerrpc.ServerOrder, case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED: kit.ChannelType = ChannelTypeScriptEnforced + case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SIMPLE_TAPROOT: + kit.ChannelType = ChannelTypeSimpleTaproot + default: return nil, nodeKey, nil, multiSigKey, fmt.Errorf("unhandled channel type %v", details.ChannelType) diff --git a/poolscript/script.go b/poolscript/script.go index b7bf661..00b2e0e 100644 --- a/poolscript/script.go +++ b/poolscript/script.go @@ -15,6 +15,7 @@ import ( "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnrpc" ) // Version represents the type of Pool account script that is used for either @@ -824,3 +825,44 @@ func IncludesPreviousOutPoint(tx *wire.MsgTx, output wire.OutPoint) bool { } return false } + +// FundingOutput returns the channel funding output for the given commitment +// type, funding keys and channel size. +func FundingOutput(commitmentType lnrpc.CommitmentType, ourKey, + theirKey []byte, chanSize int64) (*wire.TxOut, error) { + + switch commitmentType { + case lnrpc.CommitmentType_SIMPLE_TAPROOT: + ourPubKey, err := btcec.ParsePubKey(ourKey) + if err != nil { + return nil, err + } + theirPubKey, err := btcec.ParsePubKey(theirKey) + if err != nil { + return nil, err + } + + log.Debugf("Creating P2TR funding script for local key %x and "+ + "remote key %x", ourKey, theirKey) + _, fundingOutput, err := input.GenTaprootFundingScript( + ourPubKey, theirPubKey, chanSize, + ) + if err != nil { + return nil, err + } + + return fundingOutput, nil + + default: + log.Debugf("Creating P2WSH funding script for local key %x "+ + "and remote key %x", ourKey, theirKey) + _, fundingOutput, err := input.GenFundingPkScript( + ourKey, theirKey, chanSize, + ) + if err != nil { + return nil, err + } + + return fundingOutput, nil + } +} diff --git a/rpcserver.go b/rpcserver.go index 7bb6f96..b42c9f0 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -30,7 +30,6 @@ import ( "github.com/lightninglabs/pool/terms" "github.com/lightningnetwork/lnd/chanbackup" lndFunding "github.com/lightningnetwork/lnd/funding" - "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -2318,8 +2317,14 @@ func (s *rpcServer) prepareLeasesResponse(ctx context.Context, ourMultiSigPubKey = t.Recipient.MultiSigPubKey } + commitType, _ := order.DetermineCommitmentType( + ourOrder.Details(), + match.Order.Details(), + ) + chanAmt := match.UnitsFilled.ToSatoshis() - _, chanOutput, err := input.GenFundingPkScript( + chanOutput, err := poolscript.FundingOutput( + commitType, ourMultiSigPubKey.SerializeCompressed(), match.MultiSigKey[:], int64(chanAmt), ) @@ -3334,6 +3339,8 @@ func marshallChannelType( return auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT case order.ChannelTypeScriptEnforced: return auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED + case order.ChannelTypeSimpleTaproot: + return auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SIMPLE_TAPROOT default: return auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_UNKNOWN } @@ -3366,6 +3373,9 @@ func marshallChannelInfo(chanInfos map[wire.OutPoint]*chaninfo.ChannelInfo) ( case chanbackup.ScriptEnforcedLeaseVersion: channelType = auctioneerrpc.ChannelType_SCRIPT_ENFORCED_LEASE + case chanbackup.SimpleTaprootVersion: + channelType = auctioneerrpc.ChannelType_SIMPLE_TAPROOT + default: return nil, fmt.Errorf("unknown channel type: %v", chanInfo.Version)