bolt3

Lightning transaction and script formats, per BOLT #3 (docs.ppad.tech/bolt3).
git clone git://git.ppad.tech/bolt3.git
Log | Files | Refs | README | LICENSE

commit acfdcfd23aa99407cfcf1f03aa3c637edd030a32
parent f7f15ccf1061b984962a1bef9b235a910be93098
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 17 May 2026 18:43:23 -0230

add wNAF-suffixed derive_* variants

Mirrors the apostrophe convention in ppad-secp256k1: each function
takes a precomputed Context as its first argument and uses
sign-friendly base-point multiplication via mul_wnaf. Callers thread
a single Context across many derivations.

Added:
  derive_per_commitment_point'
  derive_pubkey'                      -- highest leverage (5x per
                                         deriveCommitmentKeys)
  derive_localpubkey'
  derive_local_htlcpubkey'
  derive_remote_htlcpubkey'
  derive_local_delayedpubkey'
  derive_remote_delayedpubkey'

derive_revocationpubkey' is deliberately omitted: its two scalar
mults are basepoint * scalar (not G * scalar), so the wNAF table
built by S.precompute doesn't apply. The non-prime variant remains
the only option there.

Diffstat:
Mlib/Lightning/Protocol/BOLT3.hs | 7+++++++
Mlib/Lightning/Protocol/BOLT3/Keys.hs | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+), 0 deletions(-)

diff --git a/lib/Lightning/Protocol/BOLT3.hs b/lib/Lightning/Protocol/BOLT3.hs @@ -146,12 +146,19 @@ module Lightning.Protocol.BOLT3 ( -- * Key derivation , derive_per_commitment_point + , derive_per_commitment_point' , derive_pubkey + , derive_pubkey' , derive_localpubkey + , derive_localpubkey' , derive_local_htlcpubkey + , derive_local_htlcpubkey' , derive_remote_htlcpubkey + , derive_remote_htlcpubkey' , derive_local_delayedpubkey + , derive_local_delayedpubkey' , derive_remote_delayedpubkey + , derive_remote_delayedpubkey' , derive_revocationpubkey -- ** Secret generation diff --git a/lib/Lightning/Protocol/BOLT3/Keys.hs b/lib/Lightning/Protocol/BOLT3/Keys.hs @@ -23,14 +23,21 @@ module Lightning.Protocol.BOLT3.Keys ( -- * Per-commitment point derivation derive_per_commitment_point + , derive_per_commitment_point' -- * Key derivation , derive_pubkey + , derive_pubkey' , derive_localpubkey + , derive_localpubkey' , derive_local_htlcpubkey + , derive_local_htlcpubkey' , derive_remote_htlcpubkey + , derive_remote_htlcpubkey' , derive_local_delayedpubkey + , derive_local_delayedpubkey' , derive_remote_delayedpubkey + , derive_remote_delayedpubkey' -- * Revocation key derivation , derive_revocationpubkey @@ -77,6 +84,24 @@ derive_per_commitment_point (PerCommitmentSecret sec) = do pure $! PerCommitmentPoint (Point bs) {-# INLINE derive_per_commitment_point #-} +-- | As 'derive_per_commitment_point', but takes a precomputed +-- secp256k1 'S.Context' so the @G@-base scalar mult uses wNAF. +-- Caller threads a single context across many calls. +-- +-- >>> let !tex = S.precompute +-- >>> derive_per_commitment_point' tex secret +-- Just (PerCommitmentPoint ...) +derive_per_commitment_point' + :: S.Context + -> PerCommitmentSecret + -> Maybe PerCommitmentPoint +derive_per_commitment_point' tex (PerCommitmentSecret sec) = do + sk <- S.parse_int256 sec + pk <- S.derive_pub' tex sk + let !bs = S.serialize_point pk + pure $! PerCommitmentPoint (Point bs) +{-# INLINE derive_per_commitment_point' #-} + -- Key derivation --------------------------------------------------------- -- | Derive a pubkey from a basepoint and per-commitment point. @@ -106,6 +131,32 @@ derive_pubkey (Point basepointBs) (PerCommitmentPoint (Point pcpBs)) = do pure $! Pubkey bs {-# INLINE derive_pubkey #-} +-- | As 'derive_pubkey', but takes a precomputed secp256k1 +-- 'S.Context'. The @tweak * G@ multiplication uses wNAF; this is +-- the highest-leverage variant since 'derive_pubkey' fires five +-- times per commitment-key derivation. +-- +-- >>> let !tex = S.precompute +-- >>> derive_pubkey' tex basepoint per_commitment_point +-- Just (Pubkey ...) +derive_pubkey' + :: S.Context + -> Point -- ^ basepoint + -> PerCommitmentPoint -- ^ per_commitment_point + -> Maybe Pubkey +derive_pubkey' + tex + (Point basepointBs) + (PerCommitmentPoint (Point pcpBs)) = do + basepoint <- S.parse_point basepointBs + let !h = SHA256.hash (pcpBs <> basepointBs) + tweak <- S.parse_int256 h + tweakPoint <- S.derive_pub' tex tweak + let !result = S.add basepoint tweakPoint + !bs = S.serialize_point result + pure $! Pubkey bs +{-# INLINE derive_pubkey' #-} + -- | Derive localpubkey from payment_basepoint and per_commitment_point. -- -- >>> derive_localpubkey payment_basepoint per_commitment_point @@ -118,6 +169,17 @@ derive_localpubkey (PaymentBasepoint pt) pcp = LocalPubkey <$> derive_pubkey pt pcp {-# INLINE derive_localpubkey #-} +-- | As 'derive_localpubkey', but uses 'derive_pubkey'' internally +-- (wNAF-accelerated). +derive_localpubkey' + :: S.Context + -> PaymentBasepoint + -> PerCommitmentPoint + -> Maybe LocalPubkey +derive_localpubkey' tex (PaymentBasepoint pt) pcp = + LocalPubkey <$> derive_pubkey' tex pt pcp +{-# INLINE derive_localpubkey' #-} + -- | Derive local_htlcpubkey from htlc_basepoint and per_commitment_point. -- -- >>> derive_local_htlcpubkey htlc_basepoint per_commitment_point @@ -130,6 +192,16 @@ derive_local_htlcpubkey (HtlcBasepoint pt) pcp = LocalHtlcPubkey <$> derive_pubkey pt pcp {-# INLINE derive_local_htlcpubkey #-} +-- | As 'derive_local_htlcpubkey', wNAF variant. +derive_local_htlcpubkey' + :: S.Context + -> HtlcBasepoint + -> PerCommitmentPoint + -> Maybe LocalHtlcPubkey +derive_local_htlcpubkey' tex (HtlcBasepoint pt) pcp = + LocalHtlcPubkey <$> derive_pubkey' tex pt pcp +{-# INLINE derive_local_htlcpubkey' #-} + -- | Derive remote_htlcpubkey from htlc_basepoint and per_commitment_point. -- -- >>> derive_remote_htlcpubkey htlc_basepoint per_commitment_point @@ -142,6 +214,16 @@ derive_remote_htlcpubkey (HtlcBasepoint pt) pcp = RemoteHtlcPubkey <$> derive_pubkey pt pcp {-# INLINE derive_remote_htlcpubkey #-} +-- | As 'derive_remote_htlcpubkey', wNAF variant. +derive_remote_htlcpubkey' + :: S.Context + -> HtlcBasepoint + -> PerCommitmentPoint + -> Maybe RemoteHtlcPubkey +derive_remote_htlcpubkey' tex (HtlcBasepoint pt) pcp = + RemoteHtlcPubkey <$> derive_pubkey' tex pt pcp +{-# INLINE derive_remote_htlcpubkey' #-} + -- | Derive local_delayedpubkey from delayed_payment_basepoint and -- per_commitment_point. -- @@ -155,6 +237,16 @@ derive_local_delayedpubkey (DelayedPaymentBasepoint pt) pcp = LocalDelayedPubkey <$> derive_pubkey pt pcp {-# INLINE derive_local_delayedpubkey #-} +-- | As 'derive_local_delayedpubkey', wNAF variant. +derive_local_delayedpubkey' + :: S.Context + -> DelayedPaymentBasepoint + -> PerCommitmentPoint + -> Maybe LocalDelayedPubkey +derive_local_delayedpubkey' tex (DelayedPaymentBasepoint pt) pcp = + LocalDelayedPubkey <$> derive_pubkey' tex pt pcp +{-# INLINE derive_local_delayedpubkey' #-} + -- | Derive remote_delayedpubkey from delayed_payment_basepoint and -- per_commitment_point. -- @@ -168,8 +260,23 @@ derive_remote_delayedpubkey (DelayedPaymentBasepoint pt) pcp = RemoteDelayedPubkey <$> derive_pubkey pt pcp {-# INLINE derive_remote_delayedpubkey #-} +-- | As 'derive_remote_delayedpubkey', wNAF variant. +derive_remote_delayedpubkey' + :: S.Context + -> DelayedPaymentBasepoint + -> PerCommitmentPoint + -> Maybe RemoteDelayedPubkey +derive_remote_delayedpubkey' tex (DelayedPaymentBasepoint pt) pcp = + RemoteDelayedPubkey <$> derive_pubkey' tex pt pcp +{-# INLINE derive_remote_delayedpubkey' #-} + -- Revocation key derivation ---------------------------------------------- +-- Note: no @derive_revocationpubkey'@ is provided. The two scalar +-- mults inside this function are general (basepoint * scalar, not +-- @G * scalar@), so the wNAF table built by 'S.precompute' — which +-- stores multiples of the generator — doesn't apply. + -- | Derive revocationpubkey from revocation_basepoint and -- per_commitment_point. --