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 b7bd940fe55541047bc12fd523880b924df8049e
parent f2031cfb310fda6bbe02150668e084cdabea0eca
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 17 May 2026 18:52:59 -0230

bench: criterion + weigh entries for wNAF derive_* variants

Adds apostrophe-suffixed variants to both bench targets, alongside
their non-wNAF counterparts so the comparison is direct. Also adds
derive_per_commitment_point to the criterion suite (wasn't covered
before).

Results on M-series macOS, -O2:
  derive_pubkey:                148.7 us -> 45.4 us   (3.3x)
  derive_per_commitment_point:  141.2 us -> 37.7 us   (3.7x)

Weigh allocation matches non-wNAF once the precompute table is
forced before mainWith — without that, the one-time ~50 KB ctxArray
build gets billed to whichever apostrophe-suffixed entry happens to
touch tex first.

ppad-secp256k1 added to both bench targets' build-depends.

Diffstat:
Mbench/Main.hs | 20++++++++++++++++++++
Mbench/Weight.hs | 23++++++++++++++++++++++-
Mppad-bolt3.cabal | 2++
3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/bench/Main.hs b/bench/Main.hs @@ -3,6 +3,7 @@ module Main where +import qualified Crypto.Curve.Secp256k1 as S import Control.DeepSeq (NFData(..)) import Criterion.Main import Data.Word (Word64) @@ -11,6 +12,7 @@ import Lightning.Protocol.BOLT3 import Lightning.Protocol.BOLT3.Types ( Pubkey(..), Point(..) , PaymentHash(..), PerCommitmentPoint(..) + , PerCommitmentSecret(..) , CommitmentNumber(..) ) @@ -163,11 +165,25 @@ instance NFData ValidationError where rnf NoOutputs = () rnf (TooManyOutputs a) = rnf a +-- | Precomputed wNAF context, built once and reused across the +-- apostrophe-suffixed benches. NOINLINE keeps it from being +-- re-created on every call site. +tex :: S.Context +tex = S.precompute +{-# NOINLINE tex #-} + main :: IO () main = defaultMain [ bgroup "key derivation" [ bench "derive_pubkey" $ whnf (derive_pubkey basepoint) perCommitmentPoint + , bench "derive_pubkey'" $ + whnf (derive_pubkey' tex basepoint) perCommitmentPoint + , bench "derive_per_commitment_point" $ + whnf derive_per_commitment_point samplePcs + , bench "derive_per_commitment_point'" $ + whnf (derive_per_commitment_point' tex) + samplePcs , bench "derive_revocationpubkey" $ whnf (derive_revocationpubkey revocationBasepoint) perCommitmentPoint ] @@ -313,6 +329,10 @@ main = defaultMain [ 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] + samplePcs :: PerCommitmentSecret + samplePcs = + PerCommitmentSecret (BS.replicate 32 0x01) + -- Secret generation test data seed = BS.replicate 32 0xFF diff --git a/bench/Weight.hs b/bench/Weight.hs @@ -3,6 +3,7 @@ module Main where +import qualified Crypto.Curve.Secp256k1 as S import Control.DeepSeq (NFData(..)) import qualified Data.ByteString as BS import Data.Word (Word32, Word64) @@ -10,10 +11,17 @@ import Lightning.Protocol.BOLT3 import Lightning.Protocol.BOLT3.Types ( Pubkey(..), Point(..) , PaymentHash(..), PerCommitmentPoint(..) + , PerCommitmentSecret(..) , CommitmentNumber(..) ) import Weigh +-- | Precomputed wNAF context; reused across every apostrophe- +-- suffixed weigh entry. +tex :: S.Context +tex = S.precompute +{-# NOINLINE tex #-} + -- NFData instances for weigh -- (Satoshi, MilliSatoshi, Point, PaymentHash, PerCommitmentSecret -- derive NFData via ppad-bolt1) @@ -165,11 +173,20 @@ instance NFData SecretStore where rnf ss = ss `seq` () main :: IO () -main = mainWith $ do +main = tex `seq` mainWith $ do + -- 'tex' is forced before mainWith so the one-time wNAF + -- precompute (~50 KB) doesn't get billed to whichever + -- apostrophe-suffixed func happens to evaluate it first. setColumns [Case, Allocated, GCs, Max] -- Key derivation allocations func "derive_pubkey" (derive_pubkey basepoint) perCommitmentPoint + func "derive_pubkey'" + (derive_pubkey' tex basepoint) perCommitmentPoint + func "derive_per_commitment_point" + derive_per_commitment_point samplePcs + func "derive_per_commitment_point'" + (derive_per_commitment_point' tex) samplePcs func "derive_revocationpubkey" (derive_revocationpubkey revocationBasepoint) perCommitmentPoint @@ -306,6 +323,10 @@ main = mainWith $ do 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] + samplePcs :: PerCommitmentSecret + samplePcs = + PerCommitmentSecret (BS.replicate 32 0x01) + -- Secret generation test data seed = BS.replicate 32 0xFF diff --git a/ppad-bolt3.cabal b/ppad-bolt3.cabal @@ -77,6 +77,7 @@ benchmark bolt3-bench , criterion , deepseq , ppad-bolt3 + , ppad-secp256k1 >= 0.5.4 && < 0.6 benchmark bolt3-weigh type: exitcode-stdio-1.0 @@ -92,5 +93,6 @@ benchmark bolt3-weigh , bytestring , deepseq , ppad-bolt3 + , ppad-secp256k1 >= 0.5.4 && < 0.6 , weigh