Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4253ff7
lnwire: introduce new explicit ChannelType TLV record
Roasbeef Mar 4, 2021
822a3af
lnwire: add new ChannelType field as TLV record to Open/AcceptChannel
Roasbeef Mar 4, 2021
55aef0b
lnwire: add new feature bits for explicit channel type negotiation
Roasbeef Mar 4, 2021
268990a
rpc: add new commitment_type field to OpenChannelRequest
Roasbeef Mar 4, 2021
459d7dc
lnwire: extend RawFeatureVector with helper methods
wpaulino Jul 13, 2021
de79673
funding: add explicit commitment type negotiation support
wpaulino Jun 9, 2021
ce2918e
funding: use explicit commitment type negotiation when possible
Roasbeef Mar 4, 2021
082c019
lntest: replace `commitType` type with rpc alternative
wpaulino Jun 9, 2021
478f640
lntest: use explicit channel commitment negotiation for multi-hop itests
wpaulino Jun 9, 2021
450f2be
lncli: add channel type flag to openchannel command
wpaulino Jul 30, 2021
0c56b07
docs: add explicit channel negotiation text to release notes
wpaulino Jul 30, 2021
20f3b54
lncfg: add protocol config option for script enforced lease support
wpaulino Jul 13, 2021
dc1dd6e
lnwire+feature: add feature bit for script enforced lease support
wpaulino Jul 13, 2021
067f335
lnwire: add LeaseExpiry custom record for Open+AcceptChannel
wpaulino Jul 28, 2021
9b05627
channeldb: add new ChannelType bit for script enforced leased channels
wpaulino Jul 14, 2021
5c46a9f
funding+lnwallet: support funding new script enforced leased channels
wpaulino Jul 14, 2021
aa58449
input: add scripts for new script enforced lease commitment type
wpaulino Jul 13, 2021
5d4d6ce
input: add witness gen and weight estimates for new commitment type
wpaulino Jul 13, 2021
4e2d599
lnwallet: coalesce different supported output scripts into single method
wpaulino Jul 15, 2021
b9aed84
lnwallet: support transactions and scripts for new commitment type
wpaulino Jul 15, 2021
425242a
input: add new constructor to support CSV and CLTV locked inputs
wpaulino Jul 13, 2021
4550a1c
contractcourt: handle sweeping script-enforced leased channel outputs
wpaulino Jul 14, 2021
c34d074
sweep: remove previous exclusive group upon re-offered inputs
wpaulino Jul 15, 2021
4ab8155
lnrpc: add new CommitmentType for script enforced leased channels
wpaulino Jul 15, 2021
8632820
chanacceptor: include commitment type in channel acceptor requests
wpaulino Jul 15, 2021
ab96e20
itest: update itests to use new script-enforced lease commitment type
wpaulino Jul 13, 2021
ee40e8e
chanbackup: support backup restore of script enforced leased channels
wpaulino Jul 26, 2021
19e01ad
itest: test backup restore of script enforced lease channel type
wpaulino Jul 26, 2021
debc95c
docs: add script enforced channel leases text to release notes
wpaulino Jul 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions breacharbiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2296,6 +2296,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
false, 0,
)
if err != nil {
return nil, nil, nil, err
Expand Down
37 changes: 37 additions & 0 deletions chanacceptor/rpcacceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,42 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
req := newRequest.request
pendingChanID := req.OpenChanMsg.PendingChannelID

// Map the channel commitment type to its RPC
// counterpart.
var commitmentType lnrpc.CommitmentType
if req.OpenChanMsg.ChannelType != nil {
channelFeatures := lnwire.RawFeatureVector(
*req.OpenChanMsg.ChannelType,
)
switch {
case channelFeatures.OnlyContains(
lnwire.ScriptEnforcedLeaseRequired,
lnwire.AnchorsZeroFeeHtlcTxRequired,
lnwire.StaticRemoteKeyRequired,
):
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE

case channelFeatures.OnlyContains(
lnwire.AnchorsZeroFeeHtlcTxRequired,
lnwire.StaticRemoteKeyRequired,
):
commitmentType = lnrpc.CommitmentType_ANCHORS

case channelFeatures.OnlyContains(
lnwire.StaticRemoteKeyRequired,
):
commitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY

case channelFeatures.OnlyContains():
commitmentType = lnrpc.CommitmentType_LEGACY

default:
log.Warnf("Unhandled commitment type "+
"in channel acceptor request: %v",
req.OpenChanMsg.ChannelType)
}
}

acceptRequests[pendingChanID] = newRequest

// A ChannelAcceptRequest has been received, send it to the client.
Expand All @@ -273,6 +309,7 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
CsvDelay: uint32(req.OpenChanMsg.CsvDelay),
MaxAcceptedHtlcs: uint32(req.OpenChanMsg.MaxAcceptedHTLCs),
ChannelFlags: uint32(req.OpenChanMsg.ChannelFlags),
CommitmentType: commitmentType,
}

if err := r.send(chanAcceptReq); err != nil {
Expand Down
41 changes: 40 additions & 1 deletion chanbackup/single.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ const (
// AnchorsZeroFeeHtlcTxCommitVersion is a version that denotes this
// channel is using the zero-fee second-level anchor commitment format.
AnchorsZeroFeeHtlcTxCommitVersion = 3

// ScriptEnforcedLeaseVersion is a version that denotes this channel is
// using the zero-fee second-level anchor commitment format along with
// an additional CLTV requirement of the channel lease maturity on any
// commitment and HTLC outputs that pay directly to the channel
// initiator.
ScriptEnforcedLeaseVersion = 4
)

// Single is a static description of an existing channel that can be used for
Expand Down Expand Up @@ -116,6 +123,16 @@ type Single struct {
// ShaChainRootDesc describes how to derive the private key that was
// used as the shachain root for this channel.
ShaChainRootDesc keychain.KeyDescriptor

// LeaseExpiry represents the absolute expiration as a height of the
// chain of a channel lease that is applied to every output that pays
// directly to the channel initiator in addition to the usual CSV
// requirement.
//
// NOTE: This field will only be present for the following versions:
//
// - ScriptEnforcedLeaseVersion
LeaseExpiry uint32
}

// NewSingle creates a new static channel backup based on an existing open
Expand Down Expand Up @@ -177,6 +194,10 @@ func NewSingle(channel *channeldb.OpenChannel,
}

switch {
case channel.ChanType.HasLeaseExpiration():
single.Version = ScriptEnforcedLeaseVersion
single.LeaseExpiry = channel.ThawHeight

case channel.ChanType.ZeroHtlcTxFee():
single.Version = AnchorsZeroFeeHtlcTxCommitVersion

Expand All @@ -203,6 +224,7 @@ func (s *Single) Serialize(w io.Writer) error {
case TweaklessCommitVersion:
case AnchorsCommitVersion:
case AnchorsZeroFeeHtlcTxCommitVersion:
case ScriptEnforcedLeaseVersion:
default:
return fmt.Errorf("unable to serialize w/ unknown "+
"version: %v", s.Version)
Expand Down Expand Up @@ -264,6 +286,12 @@ func (s *Single) Serialize(w io.Writer) error {
); err != nil {
return err
}
if s.Version == ScriptEnforcedLeaseVersion {
err := lnwire.WriteElements(&singleBytes, s.LeaseExpiry)
if err != nil {
return err
}
}

return lnwire.WriteElements(
w,
Expand Down Expand Up @@ -363,6 +391,7 @@ func (s *Single) Deserialize(r io.Reader) error {
case TweaklessCommitVersion:
case AnchorsCommitVersion:
case AnchorsZeroFeeHtlcTxCommitVersion:
case ScriptEnforcedLeaseVersion:
default:
return fmt.Errorf("unable to de-serialize w/ unknown "+
"version: %v", s.Version)
Expand Down Expand Up @@ -456,8 +485,18 @@ func (s *Single) Deserialize(r io.Reader) error {
return err
}
s.ShaChainRootDesc.KeyLocator.Family = keychain.KeyFamily(shaKeyFam)
err = lnwire.ReadElements(r, &s.ShaChainRootDesc.KeyLocator.Index)
if err != nil {
return err
}

if s.Version == ScriptEnforcedLeaseVersion {
if err := lnwire.ReadElement(r, &s.LeaseExpiry); err != nil {
return err
}
}

return lnwire.ReadElements(r, &s.ShaChainRootDesc.KeyLocator.Index)
return nil
}

// UnpackFromReader is similar to Deserialize method, but it expects the passed
Expand Down
16 changes: 11 additions & 5 deletions chanbackup/single_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,7 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
isInitiator = true
}

chanType := channeldb.SingleFunderBit
if rand.Int63()%2 == 0 {
chanType = channeldb.SingleFunderTweaklessBit
}
chanType := channeldb.ChannelType(rand.Intn(8))

return &channeldb.OpenChannel{
ChainHash: chainHash,
Expand All @@ -137,6 +134,7 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
ShortChannelID: lnwire.NewShortChanIDFromInt(
uint64(rand.Int63()),
),
ThawHeight: rand.Uint32(),
IdentityPub: pub,
LocalChanCfg: channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{
Expand Down Expand Up @@ -243,6 +241,13 @@ func TestSinglePackUnpack(t *testing.T) {
valid: true,
},

// The new script enforced channel lease version should
// pack/unpack with no problem.
{
version: ScriptEnforcedLeaseVersion,
valid: true,
},

// A non-default version, atm this should result in a failure.
{
version: 99,
Expand Down Expand Up @@ -293,8 +298,9 @@ func TestSinglePackUnpack(t *testing.T) {
t.Fatalf("unable to serialize single: %v", err)
}

// Mutate the version byte to an unknown version.
rawBytes := rawSingle.Bytes()
rawBytes[0] ^= 5
Comment thread
wpaulino marked this conversation as resolved.
Outdated
rawBytes[0] = ^uint8(0)

newReader := bytes.NewReader(rawBytes)
err = unpackedSingle.Deserialize(newReader)
Expand Down
26 changes: 19 additions & 7 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ const (
// ZeroHtlcTxFeeBit indicates that the channel should use zero-fee
// second-level HTLC transactions.
ZeroHtlcTxFeeBit ChannelType = 1 << 5

// LeaseExpirationBit indicates that the channel has been leased for a
// period of time, constraining every output that pays to the channel
// initiator with an additional CLTV of the lease maturity.
LeaseExpirationBit ChannelType = 1 << 6
Comment thread
wpaulino marked this conversation as resolved.
Outdated
)

// IsSingleFunder returns true if the channel type if one of the known single
Expand Down Expand Up @@ -301,6 +306,11 @@ func (c ChannelType) IsFrozen() bool {
return c&FrozenBit == FrozenBit
}

// HasLeaseExpiration returns true if the channel originated from a lease.
func (c ChannelType) HasLeaseExpiration() bool {
return c&LeaseExpirationBit == LeaseExpirationBit
}

// ChannelConstraints represents a set of constraints meant to allow a node to
// limit their exposure, enact flow control and ensure that all HTLCs are
// economically relevant. This struct will be mirrored for both sides of the
Expand Down Expand Up @@ -1363,7 +1373,7 @@ func putOpenChannel(chanBucket kvdb.RwBucket, channel *OpenChannel) error {

// Next, if this is a frozen channel, we'll add in the axillary
// information we need to store.
if channel.ChanType.IsFrozen() {
if channel.ChanType.IsFrozen() || channel.ChanType.HasLeaseExpiration() {
err := storeThawHeight(
chanBucket, channel.ThawHeight,
)
Expand Down Expand Up @@ -1404,7 +1414,7 @@ func fetchOpenChannel(chanBucket kvdb.RBucket,

// Next, if this is a frozen channel, we'll add in the axillary
// information we need to store.
if channel.ChanType.IsFrozen() {
if channel.ChanType.IsFrozen() || channel.ChanType.HasLeaseExpiration() {
thawHeight, err := fetchThawHeight(chanBucket)
if err != nil {
return nil, fmt.Errorf("unable to store thaw "+
Expand Down Expand Up @@ -2871,7 +2881,7 @@ func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary,

// We'll also remove the channel from the frozen channel bucket
// if we need to.
if c.ChanType.IsFrozen() {
if c.ChanType.IsFrozen() || c.ChanType.HasLeaseExpiration() {
err := deleteThawHeight(chanBucket)
if err != nil {
return err
Expand Down Expand Up @@ -3071,13 +3081,14 @@ func (c *OpenChannel) RemoteRevocationStore() (shachain.Store, error) {
// channel is not frozen, then 0 is returned.
func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
// Only frozen channels have a thaw height.
if !c.ChanType.IsFrozen() {
if !c.ChanType.IsFrozen() && !c.ChanType.HasLeaseExpiration() {
Comment thread
wpaulino marked this conversation as resolved.
Outdated
return 0, nil
}

// If the channel's thaw height is below the absolute threshold, then
// it's interpreted as a relative height to the chain's current height.
if c.ThawHeight < AbsoluteThawHeightThreshold {
// If the channel has the frozen bit set and it's thaw height is below
// the absolute threshold, then it's interpreted as a relative height to
// the chain's current height.
if c.ChanType.IsFrozen() && c.ThawHeight < AbsoluteThawHeightThreshold {
// We'll only known of the channel's short ID once it's
// confirmed.
if c.IsPending {
Expand All @@ -3086,6 +3097,7 @@ func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
}
return c.ShortChannelID.BlockHeight + c.ThawHeight, nil
}

return c.ThawHeight, nil
}

Expand Down
7 changes: 7 additions & 0 deletions chanrestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) (
chanType |= channeldb.AnchorOutputsBit
chanType |= channeldb.SingleFunderTweaklessBit

case chanbackup.ScriptEnforcedLeaseVersion:
chanType = channeldb.LeaseExpirationBit
chanType |= channeldb.ZeroHtlcTxFeeBit
chanType |= channeldb.AnchorOutputsBit
chanType |= channeldb.SingleFunderTweaklessBit

default:
return nil, fmt.Errorf("unknown Single version: %v", err)
}
Expand All @@ -172,6 +178,7 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) (
RemoteCurrentRevocation: backup.RemoteNodePub,
RevocationStore: shachain.NewRevocationStore(),
RevocationProducer: shaChainProducer,
ThawHeight: backup.LeaseExpiry,
},
}

Expand Down
22 changes: 22 additions & 0 deletions cmd/lncli/cmd_open_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Signed base64 encoded PSBT or hex encoded raw wire TX (or path to text file): `
// the user from choosing a large file by accident and running into out
// of memory issues or other weird errors.
psbtMaxFileSize = 1024 * 1024

channelTypeTweakless = "tweakless"
channelTypeAnchors = "anchors"
)

// TODO(roasbeef): change default number of confirmations
Expand Down Expand Up @@ -200,6 +203,12 @@ var openChannelCommand = cli.Command{
Usage: "(optional) the maximum value in msat that " +
"can be pending within the channel at any given time",
},
cli.StringFlag{
Name: "channel_type",
Usage: fmt.Sprintf("(optional) the type of channel to "+
"propose to the remote peer (%q, %q)",
channelTypeTweakless, channelTypeAnchors),
},
},
Action: actionDecorator(openChannel),
}
Expand Down Expand Up @@ -307,6 +316,19 @@ func openChannel(ctx *cli.Context) error {

req.Private = ctx.Bool("private")

// Parse the channel type and map it to its RPC representation.
channelType := ctx.String("channel_type")
switch channelType {
case "":
break
case channelTypeTweakless:
req.CommitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
case channelTypeAnchors:
req.CommitmentType = lnrpc.CommitmentType_ANCHORS
default:
return fmt.Errorf("unsupported channel type %v", channelType)
}

// PSBT funding is a more involved, interactive process that is too
// large to also fit into this already long function.
if ctx.Bool("psbt") {
Expand Down
7 changes: 7 additions & 0 deletions contractcourt/anchor_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ func (c *anchorResolver) IsResolved() bool {
return c.resolved
}

// SupplementState allows the user of a ContractResolver to supplement it with
// state required for the proper resolution of a contract.
//
// NOTE: Part of the ContractResolver interface.
func (c *anchorResolver) SupplementState(_ *channeldb.OpenChannel) {
}

// report returns a report on the resolution state of the contract.
func (c *anchorResolver) report() *ContractReport {
c.reportLock.Lock()
Expand Down
6 changes: 6 additions & 0 deletions contractcourt/chain_arbitrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
report,
)
},
FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) {
return c.chanSource.FetchHistoricalChannel(&chanPoint)
},
}

// The final component needed is an arbitrator log that the arbitrator
Expand Down Expand Up @@ -558,6 +561,9 @@ func (c *ChainArbitrator) Start() error {
tx, c.cfg.ChainHash, &chanPoint, report,
)
},
FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) {
return c.chanSource.FetchHistoricalChannel(&chanPoint)
},
}
chanLog, err := newBoltArbitratorLog(
c.chanSource.Backend, arbCfg, c.cfg.ChainHash, chanPoint,
Expand Down
Loading