From aa0c680e18f8f17cc03051a3271d7f474a93d85a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 8 Apr 2024 19:47:05 -0700 Subject: [PATCH] lnwallet: add new AuxSigner interface to mirror SigPool In this commit, we add a new aux signer interface that's meant to mirror the SigPool. If present, this'll be used to (maybe) obtain signatures for second level HTLCs for certain classes of custom channels. --- lnwallet/aux_signer.go | 245 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 lnwallet/aux_signer.go diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go new file mode 100644 index 000000000..a724f20cb --- /dev/null +++ b/lnwallet/aux_signer.go @@ -0,0 +1,245 @@ +package lnwallet + +import ( + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// AuxHtlcDescriptor is a struct that contains the information needed to sign or +// verify an HTLC for custom channels. +type AuxHtlcDescriptor struct { + // ChanID is the ChannelID of the LightningChannel that this + // paymentDescriptor belongs to. We track this here so we can + // reconstruct the Messages that this paymentDescriptor is built from. + ChanID lnwire.ChannelID + + // RHash is the payment hash for this HTLC. The HTLC can be settled iff + // the preimage to this hash is presented. + RHash PaymentHash + + // Timeout is the absolute timeout in blocks, after which this HTLC + // expires. + Timeout uint32 + + // Amount is the HTLC amount in milli-satoshis. + Amount lnwire.MilliSatoshi + + // HtlcIndex is the index within the main update log for this HTLC. + // Entries within the log of type Add will have this field populated, + // as other entries will point to the entry via this counter. + // + // NOTE: This field will only be populated if EntryType is Add. + HtlcIndex uint64 + + // ParentIndex is the HTLC index of the entry that this update settles + // or times out. + // + // NOTE: This field will only be populated if EntryType is Fail or + // Settle. + ParentIndex uint64 + + // EntryType denotes the exact type of the paymentDescriptor. In the + // case of a Timeout, or Settle type, then the Parent field will point + // into the log to the HTLC being modified. + EntryType updateType + + // CustomRecords also stores the set of optional custom records that + // may have been attached to a sent HTLC. + CustomRecords lnwire.CustomRecords + + // addCommitHeight[Remote|Local] encodes the height of the commitment + // which included this HTLC on either the remote or local commitment + // chain. This value is used to determine when an HTLC is fully + // "locked-in". + addCommitHeightRemote uint64 + addCommitHeightLocal uint64 + + // removeCommitHeight[Remote|Local] encodes the height of the + // commitment which removed the parent pointer of this + // paymentDescriptor either due to a timeout or a settle. Once both + // these heights are below the tail of both chains, the log entries can + // safely be removed. + removeCommitHeightRemote uint64 + removeCommitHeightLocal uint64 +} + +// AddHeight returns the height at which the HTLC was added to the commitment +// chain. The height is returned based on the chain the HTLC is being added to +// (local or remote chain). +func (a *AuxHtlcDescriptor) AddHeight( + whoseCommitChain lntypes.ChannelParty) uint64 { + + if whoseCommitChain.IsRemote() { + return a.addCommitHeightRemote + } + + return a.addCommitHeightLocal +} + +// RemoveHeight returns the height at which the HTLC was removed from the +// commitment chain. The height is returned based on the chain the HTLC is being +// removed from (local or remote chain). +func (a *AuxHtlcDescriptor) RemoveHeight( + whoseCommitChain lntypes.ChannelParty) uint64 { + + if whoseCommitChain.IsRemote() { + return a.removeCommitHeightRemote + } + + return a.removeCommitHeightLocal +} + +// newAuxHtlcDescriptor creates a new AuxHtlcDescriptor from a payment +// descriptor. +func newAuxHtlcDescriptor(p *paymentDescriptor) AuxHtlcDescriptor { + return AuxHtlcDescriptor{ + ChanID: p.ChanID, + RHash: p.RHash, + Timeout: p.Timeout, + Amount: p.Amount, + HtlcIndex: p.HtlcIndex, + ParentIndex: p.ParentIndex, + EntryType: p.EntryType, + CustomRecords: p.CustomRecords.Copy(), + addCommitHeightRemote: p.addCommitHeightRemote, + addCommitHeightLocal: p.addCommitHeightLocal, + removeCommitHeightRemote: p.removeCommitHeightRemote, + removeCommitHeightLocal: p.removeCommitHeightLocal, + } +} + +// BaseAuxJob is a struct that contains the common fields that are shared among +// the aux sign/verify jobs. +type BaseAuxJob struct { + // OutputIndex is the output index of the HTLC on the commitment + // transaction being signed. + // + // NOTE: If the output is dust from the PoV of the commitment chain, + // then this value will be -1. + OutputIndex int32 + + // KeyRing is the commitment key ring that contains the keys needed to + // generate the second level HTLC signatures. + KeyRing CommitmentKeyRing + + // HTLC is the HTLC that is being signed or verified. + HTLC AuxHtlcDescriptor + + // Incoming is a boolean that indicates if the HTLC is incoming or + // outgoing. + Incoming bool + + // CommitBlob is the commitment transaction blob that contains the aux + // information for this channel. + CommitBlob fn.Option[tlv.Blob] + + // HtlcLeaf is the aux tap leaf that corresponds to the HTLC being + // signed/verified. + HtlcLeaf input.AuxTapLeaf +} + +// AuxSigJob is a struct that contains all the information needed to sign an +// HTLC for custom channels. +type AuxSigJob struct { + // SignDesc is the sign desc for this HTLC. + SignDesc input.SignDescriptor + + BaseAuxJob + + // Resp is a channel that will be used to send the result of the sign + // job. + Resp chan AuxSigJobResp + + // Cancel is a channel that should be closed if the caller wishes to + // abandon all pending sign jobs part of a single batch. + Cancel chan struct{} +} + +// NewAuxSigJob creates a new AuxSigJob. +func NewAuxSigJob(sigJob SignJob, keyRing CommitmentKeyRing, incoming bool, + htlc AuxHtlcDescriptor, commitBlob fn.Option[tlv.Blob], + htlcLeaf input.AuxTapLeaf, cancelChan chan struct{}) AuxSigJob { + + return AuxSigJob{ + SignDesc: sigJob.SignDesc, + BaseAuxJob: BaseAuxJob{ + OutputIndex: sigJob.OutputIndex, + KeyRing: keyRing, + HTLC: htlc, + Incoming: incoming, + CommitBlob: commitBlob, + HtlcLeaf: htlcLeaf, + }, + Resp: make(chan AuxSigJobResp, 1), + Cancel: cancelChan, + } +} + +// AuxSigJobResp is a struct that contains the result of a sign job. +type AuxSigJobResp struct { + // SigBlob is the signature blob that was generated for the HTLC. This + // is an opaque TLV field that may contain the signature and other data. + SigBlob fn.Option[tlv.Blob] + + // HtlcIndex is the index of the HTLC that was signed. + HtlcIndex uint64 + + // Err is the error that occurred when executing the specified + // signature job. In the case that no error occurred, this value will + // be nil. + Err error +} + +// AuxVerifyJob is a struct that contains all the information needed to verify +// an HTLC for custom channels. +type AuxVerifyJob struct { + // SigBlob is the signature blob that was generated for the HTLC. This + // is an opaque TLV field that may contain the signature and other data. + SigBlob fn.Option[tlv.Blob] + + BaseAuxJob +} + +// NewAuxVerifyJob creates a new AuxVerifyJob. +func NewAuxVerifyJob(sig fn.Option[tlv.Blob], keyRing CommitmentKeyRing, + incoming bool, htlc AuxHtlcDescriptor, commitBlob fn.Option[tlv.Blob], + htlcLeaf input.AuxTapLeaf) AuxVerifyJob { + + return AuxVerifyJob{ + SigBlob: sig, + BaseAuxJob: BaseAuxJob{ + KeyRing: keyRing, + HTLC: htlc, + Incoming: incoming, + CommitBlob: commitBlob, + HtlcLeaf: htlcLeaf, + }, + } +} + +// AuxSigner is an interface that is used to sign and verify HTLCs for custom +// channels. It is similar to the existing SigPool, but uses opaque blobs to +// shuffle around signature information and other metadata. +type AuxSigner interface { + // SubmitSecondLevelSigBatch takes a batch of aux sign jobs and + // processes them asynchronously. + SubmitSecondLevelSigBatch(chanState AuxChanState, commitTx *wire.MsgTx, + sigJob []AuxSigJob) error + + // PackSigs takes a series of aux signatures and packs them into a + // single blob that can be sent alongside the CommitSig messages. + PackSigs([]fn.Option[tlv.Blob]) fn.Result[fn.Option[tlv.Blob]] + + // UnpackSigs takes a packed blob of signatures and returns the + // original signatures for each HTLC, keyed by HTLC index. + UnpackSigs(fn.Option[tlv.Blob]) fn.Result[[]fn.Option[tlv.Blob]] + + // VerifySecondLevelSigs attempts to synchronously verify a batch of aux + // sig jobs. + VerifySecondLevelSigs(chanState AuxChanState, commitTx *wire.MsgTx, + verifyJob []AuxVerifyJob) error +}