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 ae6fd0c6910f5d0d4a572a6216acf46179b6ac2b
parent 0e603abf62c98edf788d8706f96e63d037c19c34
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 14:53:33 +0400

bench: rewrite for the new API

Benchmarks force results fully (nf), use the library's NFData
instances, and share fixtures, including a secret store holding
2^14 - 1 secrets whose next insertion checks every lower bucket.

Diffstat:
Abench/Fixtures.hs | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbench/Main.hs | 544+++++++++++--------------------------------------------------------------------
Mbench/Weight.hs | 512++++++++-----------------------------------------------------------------------
Mppad-bolt3.cabal | 19++++++++++++++-----
4 files changed, 338 insertions(+), 940 deletions(-)

diff --git a/bench/Fixtures.hs b/bench/Fixtures.hs @@ -0,0 +1,203 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} + +module Fixtures ( + Fixtures(..) + , fixtures + , tex + ) where + +import qualified Bitcoin.Prim.Tx as BT +import Control.DeepSeq (NFData, force) +import Control.Exception (evaluate) +import Control.Monad (foldM) +import qualified Crypto.Curve.Secp256k1 as S +import qualified Crypto.Hash.SHA256 as SHA256 +import qualified Data.ByteString as BS +import qualified Data.ByteString.Base16 as B16 +import GHC.Generics (Generic) +import qualified Lightning.Protocol.BOLT1 as BOLT1 +import Lightning.Protocol.BOLT3 + +-- | Shared wNAF context. +tex :: S.Context +tex = S.precompute +{-# NOINLINE tex #-} + +data Fixtures = Fixtures + { fx_secret :: !BOLT1.PerCommitmentSecret + , fx_seed :: !Seed + , fx_basepoint :: !BOLT1.Point + , fx_pcp :: !PerCommitmentPoint + , fx_rbp :: !RevocationBasepoint + , fx_seckey :: !Seckey + , fx_local_bps :: !Basepoints + , fx_remote_bps :: !Basepoints + , fx_local_fund :: !FundingPubkey + , fx_remote_fund :: !FundingPubkey + , fx_store :: !SecretStore + , fx_store_next :: !(SecretIndex, BOLT1.PerCommitmentSecret) + , fx_store_oldest :: !SecretIndex + , fx_ctx_simple :: !CommitmentContext + , fx_ctx_htlcs :: !CommitmentContext + , fx_ctx_anchors :: !CommitmentContext + , fx_commit :: !CommitmentTx + , fx_htlc_ctx :: !HTLCContext + , fx_htlc_tx :: !HTLCTx + , fx_closing :: !ClosingContext + , fx_legacy :: !LegacyClosingContext + , fx_closing_tx :: !ClosingTx + } deriving Generic + +instance NFData Fixtures + +hex :: BS.ByteString -> Maybe BS.ByteString +hex = B16.decode + +pt :: BS.ByteString -> Maybe BOLT1.Point +pt h = hex h >>= BOLT1.point + +-- | Benchmark fixtures, from the BOLT #3 Appendix C parameters, fully +-- evaluated. +fixtures :: IO Fixtures +fixtures = maybe (fail "invalid fixtures") (evaluate . force) build + +build :: Maybe Fixtures +build = do + secret <- BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0]) + sd <- seed (BS.replicate 32 0xff) + basepoint <- pt + "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2" + pcp <- PerCommitmentPoint <$> pt + "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486" + sk <- seckey (BS.pack [0 .. 31]) + + local_pay <- pt + "034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa" + remote_pay <- pt + "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" + local_delayed <- pt + "023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1" + remote_rev <- pt + "02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27" + local_fund <- FundingPubkey <$> pt + "023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb" + remote_fund <- FundingPubkey <$> pt + "030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1" + let local_bps = Basepoints (RevocationBasepoint local_pay) + (PaymentBasepoint local_pay) (DelayedPaymentBasepoint local_delayed) + (HtlcBasepoint local_pay) + remote_bps = Basepoints (RevocationBasepoint remote_rev) + (PaymentBasepoint remote_pay) (DelayedPaymentBasepoint remote_pay) + (HtlcBasepoint remote_pay) + keys <- derive_commitment_keys local_bps local_fund remote_bps remote_fund + pcp + + -- a store holding the 2^14 - 1 most recent secrets; the next index has + -- 14 trailing zeros, so inserting it checks every lower bucket + let n = 16383 :: Int + idxs <- traverse secret_index (take n [0xFFFFFFFFFFFF, 0xFFFFFFFFFFFE ..]) + store <- foldM (\st i -> insert_secret (generate_from_seed sd i) i st) + empty_secret_store idxs + next <- secret_index (0xFFFFFFFFFFFF - fromIntegral n) + oldest <- secret_index 0xFFFFFFFFFFFF + + txid <- hex + "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be" + let outpoint = BT.OutPoint (BT.TxId (BS.reverse txid)) 0 + cn <- commitment_number 42 + dust <- DustLimit <$> BOLT1.satoshi 546 + to_local <- BOLT1.milli_satoshi 6988000000 + to_remote <- BOLT1.milli_satoshi 3000000000 + htlcs <- traverse htlc + [ (HTLCReceived, 1000000, 500, 0x00), (HTLCReceived, 2000000, 501, 0x01) + , (HTLCOffered, 2000000, 502, 0x02), (HTLCOffered, 3000000, 503, 0x03) + , (HTLCReceived, 4000000, 504, 0x04) ] + let ctx_simple = CommitmentContext + { cc_funding_outpoint = outpoint + , cc_commitment_number = cn + , cc_local_payment_bp = PaymentBasepoint local_pay + , cc_remote_payment_bp = PaymentBasepoint remote_pay + , cc_to_self_delay = ToSelfDelay 144 + , cc_dust_limit = dust + , cc_feerate = FeeratePerKw 15000 + , cc_format = StaticRemotekey + , cc_is_funder = True + , cc_to_local_msat = to_local + , cc_to_remote_msat = to_remote + , cc_htlcs = [] + , cc_keys = keys + } + ctx_htlcs = ctx_simple { cc_feerate = FeeratePerKw 644 + , cc_htlcs = htlcs } + ctx_anchors = ctx_htlcs { cc_format = Anchors } + commit <- build_commitment_tx ctx_htlcs + htlc_out <- case htlcs of + (h : _) -> Just h + [] -> Nothing + let htlc_ctx = HTLCContext + { hc_commitment_txid = BT.txid (commitment_to_tx commit) + , hc_output_index = 0 + , hc_htlc = htlc_out + , hc_to_self_delay = ToSelfDelay 144 + , hc_feerate = FeeratePerKw 644 + , hc_format = StaticRemotekey + , hc_revocation_pubkey = ck_revocation_pubkey keys + , hc_local_delayed = ck_local_delayed keys + } + htlc_tx <- build_htlc_tx htlc_ctx + + fee <- BOLT1.satoshi 1000 + let p2wpkh b = Script (BS.pack [0x00, 0x14] <> BS.replicate 20 b) + closing = ClosingContext + { clc_funding_outpoint = outpoint + , clc_closer_msat = to_local + , clc_closee_msat = to_remote + , clc_closer_script = p2wpkh 0x11 + , clc_closee_script = p2wpkh 0x22 + , clc_fee = fee + , clc_locktime = Locktime 0 + , clc_outputs = CloserAndCloseeOutputs + } + legacy = LegacyClosingContext + { lcc_funding_outpoint = outpoint + , lcc_local_msat = to_local + , lcc_remote_msat = to_remote + , lcc_local_script = p2wpkh 0x11 + , lcc_remote_script = p2wpkh 0x22 + , lcc_dust_limit = dust + , lcc_fee = fee + , lcc_is_funder = True + , lcc_omit_local = False + } + closing_tx <- build_closing_tx closing + + pure Fixtures + { fx_secret = secret + , fx_seed = sd + , fx_basepoint = basepoint + , fx_pcp = pcp + , fx_rbp = RevocationBasepoint basepoint + , fx_seckey = sk + , fx_local_bps = local_bps + , fx_remote_bps = remote_bps + , fx_local_fund = local_fund + , fx_remote_fund = remote_fund + , fx_store = store + , fx_store_next = (next, generate_from_seed sd next) + , fx_store_oldest = oldest + , fx_ctx_simple = ctx_simple + , fx_ctx_htlcs = ctx_htlcs + , fx_ctx_anchors = ctx_anchors + , fx_commit = commit + , fx_htlc_ctx = htlc_ctx + , fx_htlc_tx = htlc_tx + , fx_closing = closing + , fx_legacy = legacy + , fx_closing_tx = closing_tx + } + where + htlc (dir, amt, expiry, b) = do + a <- BOLT1.milli_satoshi amt + ph <- BOLT1.payment_hash (SHA256.hash (BS.replicate 32 b)) + pure (HTLC dir a ph (CltvExpiry expiry)) diff --git a/bench/Main.hs b/bench/Main.hs @@ -1,478 +1,82 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - module Main where -import qualified Crypto.Curve.Secp256k1 as S -import Control.DeepSeq (NFData(..)) import Criterion.Main -import Data.Word (Word64) -import qualified Data.ByteString as BS +import Fixtures import Lightning.Protocol.BOLT3 -import Lightning.Protocol.BOLT3.Types - ( Pubkey(..), Point(..) - , PaymentHash(..), PerCommitmentPoint(..) - , PerCommitmentSecret(..) - , CommitmentNumber(..) - ) - --- NFData instances for benchmarking --- (Satoshi, MilliSatoshi, Point, PaymentHash, PerCommitmentSecret --- derive NFData via ppad-bolt1) - -instance NFData Pubkey where - rnf (Pubkey x) = rnf x - -instance NFData PerCommitmentPoint where - rnf (PerCommitmentPoint x) = rnf x - -instance NFData RevocationPubkey where - rnf (RevocationPubkey x) = rnf x - -instance NFData RevocationBasepoint where - rnf (RevocationBasepoint x) = rnf x - -instance NFData ChannelFeatures where - rnf (ChannelFeatures x) = rnf x - -instance NFData FeeratePerKw where - rnf (FeeratePerKw x) = rnf x - -instance NFData DustLimit where - rnf (DustLimit x) = rnf x - -instance NFData CltvExpiry where - rnf (CltvExpiry x) = rnf x - -instance NFData HTLCDirection where - rnf HTLCOffered = () - rnf HTLCReceived = () - -instance NFData HTLC where - rnf (HTLC d a h c) = rnf d `seq` rnf a `seq` rnf h `seq` rnf c - --- Transaction types -instance NFData CommitmentTx where - rnf (CommitmentTx v l i s o f) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf o `seq` rnf f - -instance NFData HTLCTx where - rnf (HTLCTx v l i s ov os) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf ov `seq` rnf os - -instance NFData ClosingTx where - rnf (ClosingTx v l i s o f) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf o `seq` rnf f - --- Output types -instance NFData TxOutput where - rnf (TxOutput v s t) = rnf v `seq` rnf s `seq` rnf t - -instance NFData OutputType where - rnf OutputToLocal = () - rnf OutputToRemote = () - rnf OutputLocalAnchor = () - rnf OutputRemoteAnchor = () - rnf (OutputOfferedHTLC e) = rnf e - rnf (OutputReceivedHTLC e) = rnf e - --- Primitives -instance NFData Script where - rnf (Script bs) = rnf bs - - -instance NFData Sequence where - rnf (Sequence x) = rnf x - -instance NFData Locktime where - rnf (Locktime x) = rnf x - -instance NFData ToSelfDelay where - rnf (ToSelfDelay x) = rnf x - -instance NFData CommitmentNumber where - rnf (CommitmentNumber x) = rnf x - --- Context types -instance NFData CommitmentContext where - rnf ctx = rnf (cc_funding_outpoint ctx) `seq` - rnf (cc_commitment_number ctx) `seq` - rnf (cc_htlcs ctx) `seq` - rnf (cc_keys ctx) - -instance NFData CommitmentKeys where - rnf keys = rnf (ck_revocation_pubkey keys) `seq` - rnf (ck_local_delayed keys) `seq` - rnf (ck_local_htlc keys) `seq` - rnf (ck_remote_htlc keys) - -instance NFData HTLCContext where - rnf ctx = rnf (hc_commitment_txid ctx) `seq` - rnf (hc_htlc ctx) - -instance NFData ClosingContext where - rnf ctx = rnf (clc_funding_outpoint ctx) `seq` - rnf (clc_local_amount ctx) `seq` - rnf (clc_remote_amount ctx) - --- Key types -instance NFData LocalDelayedPubkey where - rnf (LocalDelayedPubkey p) = rnf p - -instance NFData RemoteDelayedPubkey where - rnf (RemoteDelayedPubkey p) = rnf p - -instance NFData LocalHtlcPubkey where - rnf (LocalHtlcPubkey p) = rnf p - -instance NFData RemoteHtlcPubkey where - rnf (RemoteHtlcPubkey p) = rnf p - -instance NFData LocalPubkey where - rnf (LocalPubkey p) = rnf p - -instance NFData RemotePubkey where - rnf (RemotePubkey p) = rnf p - -instance NFData PaymentBasepoint where - rnf (PaymentBasepoint p) = rnf p - -instance NFData DelayedPaymentBasepoint where - rnf (DelayedPaymentBasepoint p) = rnf p - -instance NFData HtlcBasepoint where - rnf (HtlcBasepoint p) = rnf p - -instance NFData FundingPubkey where - rnf (FundingPubkey p) = rnf p - --- Secret storage (SecretStore is a newtype over list) -instance NFData SecretStore where - rnf store = rnf (derive_old_secret 0 store) - --- Validation errors -instance NFData ValidationError where - rnf (InvalidVersion a b) = rnf a `seq` rnf b - rnf (InvalidLocktime a) = rnf a - rnf (InvalidSequence a) = rnf a - rnf InvalidOutputOrdering = () - rnf (DustLimitViolation a b c) = rnf a `seq` rnf b `seq` rnf c - rnf MissingAnchorOutput = () - rnf (InvalidAnchorValue a) = rnf a - rnf (InvalidFee a b) = rnf a `seq` rnf b - rnf (InvalidHTLCLocktime a b) = rnf a `seq` rnf b - rnf (InvalidHTLCSequence a b) = rnf a `seq` rnf b - 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 - ] - , bgroup "secret generation" [ - bench "generate_from_seed (final node)" $ - whnf (generate_from_seed seed) 281474976710655 - , bench "generate_from_seed (first node)" $ - whnf (generate_from_seed seed) 0 - ] - , bgroup "fee calculation" [ - bench "commitment_fee (no anchors, 0 htlcs)" $ - whnf (commitment_fee feerate noAnchors) 0 - , bench "commitment_fee (no anchors, 10 htlcs)" $ - whnf (commitment_fee feerate noAnchors) 10 - , bench "commitment_fee (anchors, 10 htlcs)" $ - whnf (commitment_fee feerate withAnchors) 10 - , bench "htlc_timeout_fee" $ - whnf (htlc_timeout_fee feerate) noAnchors - , bench "htlc_success_fee" $ - whnf (htlc_success_fee feerate) noAnchors - ] - , bgroup "trimming" [ - bench "is_trimmed (offered, not trimmed)" $ - whnf (is_trimmed dust feerate noAnchors) htlcNotTrimmed - , bench "is_trimmed (offered, trimmed)" $ - whnf (is_trimmed dust feerate noAnchors) htlcTrimmed - , bench "htlc_trim_threshold (offered)" $ - whnf (htlc_trim_threshold dust feerate noAnchors) HTLCOffered +main = do + fx <- fixtures + let (next_idx, next_secret) = fx_store_next fx + keys = ck fx + defaultMain [ + bgroup "keys" [ + bench "derive_per_commitment_point" $ + nf derive_per_commitment_point (fx_secret fx) + , bench "derive_per_commitment_point'" $ + nf (derive_per_commitment_point' tex) (fx_secret fx) + , bench "derive_pubkey" $ + nf (derive_pubkey (fx_basepoint fx)) (fx_pcp fx) + , bench "derive_revocationpubkey" $ + nf (derive_revocationpubkey (fx_rbp fx)) (fx_pcp fx) + , bench "derive_commitment_keys" $ + nf (derive_commitment_keys (fx_local_bps fx) (fx_local_fund fx) + (fx_remote_bps fx) (fx_remote_fund fx)) (fx_pcp fx) + , bench "derive_commitment_keys'" $ + nf (derive_commitment_keys' tex (fx_local_bps fx) (fx_local_fund fx) + (fx_remote_bps fx) (fx_remote_fund fx)) (fx_pcp fx) + , bench "derive_privkey" $ + nf (derive_privkey (fx_seckey fx)) (fx_pcp fx) + , bench "derive_revocationprivkey" $ + nf (derive_revocationprivkey (fx_seckey fx)) (fx_secret fx) + ] + , bgroup "secrets" [ + bench "generate_from_seed (index 2^48 - 1)" $ + nf (generate_from_seed (fx_seed fx)) (fx_store_oldest fx) + , bench "insert_secret (14 lower buckets)" $ + nf (insert_secret next_secret next_idx) (fx_store fx) + , bench "derive_old_secret (index 2^48 - 1)" $ + nf (derive_old_secret (fx_store_oldest fx)) (fx_store fx) + , bench "secret_store . un_secret_store" $ + nf (secret_store . un_secret_store) (fx_store fx) + ] + , bgroup "scripts" [ + bench "funding_script" $ + nf (funding_script (fx_local_fund fx)) (fx_remote_fund fx) + , bench "to_local_script" $ + nf (to_local_script (ck_revocation_pubkey keys) (ToSelfDelay 144)) + (ck_local_delayed keys) + , bench "offered_htlc_script" $ + nf (offered_htlc_script (ck_revocation_pubkey keys) + (ck_remote_htlc keys) (ck_local_htlc keys) (hash fx)) + Anchors + , bench "received_htlc_script" $ + nf (received_htlc_script (ck_revocation_pubkey keys) + (ck_remote_htlc keys) (ck_local_htlc keys) (hash fx) + (CltvExpiry 500)) + Anchors + ] + , bgroup "transactions" [ + bench "build_commitment_tx (no htlcs)" $ + nf build_commitment_tx (fx_ctx_simple fx) + , bench "build_commitment_tx (5 htlcs)" $ + nf build_commitment_tx (fx_ctx_htlcs fx) + , bench "build_commitment_tx (5 htlcs, anchors)" $ + nf build_commitment_tx (fx_ctx_anchors fx) + , bench "encode_commitment_tx (5 htlcs)" $ + nf encode_commitment_tx (fx_commit fx) + , bench "build_htlc_tx" $ + nf build_htlc_tx (fx_htlc_ctx fx) + , bench "encode_htlc_tx" $ + nf encode_htlc_tx (fx_htlc_tx fx) + , bench "build_closing_tx" $ + nf build_closing_tx (fx_closing fx) + , bench "build_legacy_closing_tx" $ + nf build_legacy_closing_tx (fx_legacy fx) + , bench "encode_closing_tx" $ + nf encode_closing_tx (fx_closing_tx fx) + ] ] - , bgroup "tx building" [ - bench "build_commitment_tx (0 htlcs, no anchors)" $ - whnf build_commitment_tx (mkCommitmentContext htlcs0 noAnchors) - , bench "build_commitment_tx (10 htlcs, no anchors)" $ - whnf build_commitment_tx (mkCommitmentContext htlcs10 noAnchors) - , bench "build_commitment_tx (100 htlcs, no anchors)" $ - whnf build_commitment_tx (mkCommitmentContext htlcs100 noAnchors) - , bench "build_commitment_tx (10 htlcs, anchors)" $ - whnf build_commitment_tx (mkCommitmentContext htlcs10 withAnchors) - , bench "build_htlc_timeout_tx" $ - whnf build_htlc_timeout_tx sampleHtlcContext - , bench "build_htlc_success_tx" $ - whnf build_htlc_success_tx sampleHtlcContext - , bench "build_closing_tx" $ - whnf build_closing_tx sampleClosingContext - ] - , bgroup "script generation" [ - bench "funding_script" $ - whnf (funding_script (FundingPubkey samplePubkey1)) - (FundingPubkey samplePubkey2) - , bench "to_local_script" $ - whnf (to_local_script (RevocationPubkey samplePubkey1) - (ToSelfDelay 144)) - (LocalDelayedPubkey samplePubkey2) - , bench "to_remote_script (no anchors)" $ - whnf (to_remote_script (RemotePubkey samplePubkey1)) noAnchors - , bench "to_remote_script (anchors)" $ - whnf (to_remote_script (RemotePubkey samplePubkey1)) withAnchors - , bench "anchor_script" $ - whnf anchor_script (FundingPubkey samplePubkey1) - , bench "offered_htlc_script" $ - whnf (offered_htlc_script (RevocationPubkey samplePubkey1) - (RemoteHtlcPubkey samplePubkey2) - (LocalHtlcPubkey samplePubkey3) - (PaymentHash $ BS.replicate 32 0)) - noAnchors - , bench "received_htlc_script" $ - whnf (received_htlc_script (RevocationPubkey samplePubkey1) - (RemoteHtlcPubkey samplePubkey2) - (LocalHtlcPubkey samplePubkey3) - (PaymentHash $ BS.replicate 32 0) - (CltvExpiry 500000)) - noAnchors - ] - , bgroup "serialization" [ - env (pure $ build_commitment_tx $ mkCommitmentContext htlcs0 noAnchors) - $ \tx -> bench "encode_tx (0 htlcs)" $ whnf encode_tx tx - , env (pure $ build_commitment_tx $ mkCommitmentContext htlcs10 noAnchors) - $ \tx -> bench "encode_tx (10 htlcs)" $ whnf encode_tx tx - , env (pure $ build_commitment_tx $ mkCommitmentContext htlcs100 noAnchors) - $ \tx -> bench "encode_tx (100 htlcs)" $ whnf encode_tx tx - , bench "encode_htlc_tx" $ - whnf encode_htlc_tx (build_htlc_timeout_tx sampleHtlcContext) - , bench "encode_closing_tx" $ - whnf encode_closing_tx (build_closing_tx sampleClosingContext) - ] - , bgroup "parsing" [ - env (pure $ encodeTx0 htlcs0 noAnchors) - $ \bs -> bench "decode_tx (0 htlcs)" $ whnf decode_tx bs - , env (pure $ encodeTx0 htlcs10 noAnchors) - $ \bs -> bench "decode_tx (10 htlcs)" $ whnf decode_tx bs - , env (pure $ encodeTx0 htlcs100 noAnchors) - $ \bs -> bench "decode_tx (100 htlcs)" $ whnf decode_tx bs - ] - , bgroup "validation" [ - env (pure $ build_commitment_tx $ mkCommitmentContext htlcs10 noAnchors) - $ \tx -> bench "validate_commitment_tx (valid)" $ - whnf (validate_commitment_tx dust noAnchors) tx - , env (pure $ build_htlc_timeout_tx sampleHtlcContext) - $ \tx -> bench "validate_htlc_tx" $ - whnf validate_htlc_tx tx - , env (pure $ build_closing_tx sampleClosingContext) - $ \tx -> bench "validate_closing_tx" $ - whnf validate_closing_tx tx - , env (pure $ ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs10 noAnchors) - $ \outs -> bench "validate_output_ordering" $ - whnf validate_output_ordering outs - ] - , bgroup "secret storage" [ - bench "insert_secret (first)" $ - whnf (insert_secret (BS.replicate 32 0xFF) 281474976710655) - empty_store - , env setupFilledStore $ \store -> - bench "derive_old_secret (recent)" $ - whnf (derive_old_secret 281474976710654) store - , env setupFilledStore $ \store -> - bench "derive_old_secret (old)" $ - whnf (derive_old_secret 281474976710600) store - ] - , bgroup "output sorting" [ - env (pure $ ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs10 noAnchors) - $ \outs -> bench "sort_outputs (10)" $ nf sort_outputs outs - , env (pure $ ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs100 noAnchors) - $ \outs -> bench "sort_outputs (100)" $ nf sort_outputs outs - ] - ] where - -- Key derivation test data - basepoint = Point $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, - 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] - - perCommitmentPoint = PerCommitmentPoint $ Point $ BS.pack - [0x02, 0x5f, 0x71, 0x17, 0xa7, 0x81, 0x50, 0xfe, 0x2e, 0xf9, 0x7d, - 0xb7, 0xcf, 0xc8, 0x3b, 0xd5, 0x7b, 0x2e, 0x2c, 0x0d, 0x0d, 0xd2, - 0x5e, 0xaf, 0x46, 0x7a, 0x4a, 0x1c, 0x2a, 0x45, 0xce, 0x14, 0x86] - - revocationBasepoint = RevocationBasepoint $ Point $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 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 - - -- Fee calculation test data - feerate = FeeratePerKw 5000 - noAnchors = ChannelFeatures { cf_option_anchors = False } - withAnchors = ChannelFeatures { cf_option_anchors = True } - - -- Trimming test data - dust = DustLimit (Satoshi 546) - - htlcNotTrimmed = HTLC - { htlc_direction = HTLCOffered - , htlc_amount_msat = MilliSatoshi 5000000 - , htlc_payment_hash = PaymentHash (BS.replicate 32 0) - , htlc_cltv_expiry = CltvExpiry 500000 - } - - htlcTrimmed = HTLC - { htlc_direction = HTLCOffered - , htlc_amount_msat = MilliSatoshi 1000000 - , htlc_payment_hash = PaymentHash (BS.replicate 32 0) - , htlc_cltv_expiry = CltvExpiry 500000 - } - - -- Sample pubkeys - samplePubkey1, samplePubkey2, samplePubkey3 :: Pubkey - samplePubkey1 = Pubkey $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, - 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] - samplePubkey2 = Pubkey $ BS.pack - [0x02, 0x5f, 0x71, 0x17, 0xa7, 0x81, 0x50, 0xfe, 0x2e, 0xf9, 0x7d, - 0xb7, 0xcf, 0xc8, 0x3b, 0xd5, 0x7b, 0x2e, 0x2c, 0x0d, 0x0d, 0xd2, - 0x5e, 0xaf, 0x46, 0x7a, 0x4a, 0x1c, 0x2a, 0x45, 0xce, 0x14, 0x86] - samplePubkey3 = samplePubkey1 - - -- Helper to encode a commitment tx for decode benchmarks - encodeTx0 :: [HTLC] -> ChannelFeatures -> BS.ByteString - encodeTx0 htlcs features = - case encode_tx (build_commitment_tx - (mkCommitmentContext htlcs features)) of - Nothing -> BS.empty - Just bs -> bs - - -- Funding outpoint - sampleFundingOutpoint :: OutPoint - sampleFundingOutpoint = OutPoint (TxId $ BS.replicate 32 0x01) 0 - - -- HTLC lists - mkHtlc :: HTLCDirection -> Word64 -> Word64 -> HTLC - mkHtlc dir amtMsat expiry = HTLC - { htlc_direction = dir - , htlc_amount_msat = MilliSatoshi amtMsat - , htlc_payment_hash = PaymentHash (BS.replicate 32 0x00) - , htlc_cltv_expiry = CltvExpiry (fromIntegral expiry) - } - - htlcs0, htlcs10, htlcs100 :: [HTLC] - htlcs0 = [] - htlcs10 = [mkHtlc (if even i then HTLCOffered else HTLCReceived) - (5000000 + i * 100000) (500000 + i) - | i <- [0..9]] - htlcs100 = [mkHtlc (if even i then HTLCOffered else HTLCReceived) - (5000000 + i * 10000) (500000 + i) - | i <- [0..99]] - - -- CommitmentKeys fixture - sampleCommitmentKeys :: CommitmentKeys - sampleCommitmentKeys = CommitmentKeys - { ck_revocation_pubkey = RevocationPubkey samplePubkey1 - , ck_local_delayed = LocalDelayedPubkey samplePubkey1 - , ck_local_htlc = LocalHtlcPubkey samplePubkey1 - , ck_remote_htlc = RemoteHtlcPubkey samplePubkey2 - , ck_local_payment = LocalPubkey samplePubkey1 - , ck_remote_payment = RemotePubkey samplePubkey2 - , ck_local_funding = FundingPubkey samplePubkey1 - , ck_remote_funding = FundingPubkey samplePubkey2 - } - - -- CommitmentContext builder - mkCommitmentContext :: [HTLC] -> ChannelFeatures -> CommitmentContext - mkCommitmentContext htlcs features = CommitmentContext - { cc_funding_outpoint = sampleFundingOutpoint - , cc_commitment_number = CommitmentNumber 42 - , cc_local_payment_bp = - PaymentBasepoint $ Point $ unPubkey samplePubkey1 - , cc_remote_payment_bp = - PaymentBasepoint $ Point $ unPubkey samplePubkey2 - , cc_to_self_delay = ToSelfDelay 144 - , cc_dust_limit = DustLimit (Satoshi 546) - , cc_feerate = FeeratePerKw 5000 - , cc_features = features - , cc_is_funder = True - , cc_to_local_msat = MilliSatoshi 500000000 - , cc_to_remote_msat = MilliSatoshi 500000000 - , cc_htlcs = htlcs - , cc_keys = sampleCommitmentKeys - } - - -- HTLC context - sampleHtlcContext :: HTLCContext - sampleHtlcContext = HTLCContext - { hc_commitment_txid = TxId $ BS.replicate 32 0x01 - , hc_output_index = 0 - , hc_htlc = mkHtlc HTLCOffered 5000000 500000 - , hc_to_self_delay = ToSelfDelay 144 - , hc_feerate = FeeratePerKw 5000 - , hc_features = noAnchors - , hc_revocation_pubkey = RevocationPubkey samplePubkey1 - , hc_local_delayed = LocalDelayedPubkey samplePubkey1 - } - - -- Closing context - sampleClosingContext :: ClosingContext - sampleClosingContext = ClosingContext - { clc_funding_outpoint = sampleFundingOutpoint - , clc_local_amount = Satoshi 500000 - , clc_remote_amount = Satoshi 500000 - , clc_local_script = - Script $ BS.pack [0x00, 0x14] <> BS.replicate 20 0x01 - , clc_remote_script = - Script $ BS.pack [0x00, 0x14] <> BS.replicate 20 0x02 - , clc_local_dust_limit = DustLimit (Satoshi 546) - , clc_remote_dust_limit = DustLimit (Satoshi 546) - , clc_fee = Satoshi 1000 - , clc_is_funder = True - , clc_locktime = Locktime 0 - , clc_funding_script = funding_script (FundingPubkey samplePubkey1) - (FundingPubkey samplePubkey2) - } - - -- Setup for secret storage benchmarks - setupFilledStore :: IO SecretStore - setupFilledStore = do - let secrets = [(generate_from_seed seed i, i) - | i <- [281474976710655, 281474976710654 .. 281474976710600]] - pure $! foldl insertOrFail empty_store secrets - where - insertOrFail store (sec, idx) = - case insert_secret sec idx store of - Just s -> s - Nothing -> store + ck = cc_keys . fx_ctx_simple + hash = htlc_payment_hash . hc_htlc . fx_htlc_ctx diff --git a/bench/Weight.hs b/bench/Weight.hs @@ -1,471 +1,53 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} - 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) +import Fixtures 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) - -instance NFData Pubkey where - rnf (Pubkey x) = rnf x - -instance NFData PerCommitmentPoint where - rnf (PerCommitmentPoint x) = rnf x - -instance NFData RevocationPubkey where - rnf (RevocationPubkey x) = rnf x - -instance NFData RevocationBasepoint where - rnf (RevocationBasepoint x) = rnf x - -instance NFData LocalDelayedPubkey where - rnf (LocalDelayedPubkey p) = rnf p - -instance NFData RemoteDelayedPubkey where - rnf (RemoteDelayedPubkey p) = rnf p - -instance NFData LocalHtlcPubkey where - rnf (LocalHtlcPubkey p) = rnf p - -instance NFData RemoteHtlcPubkey where - rnf (RemoteHtlcPubkey p) = rnf p - -instance NFData LocalPubkey where - rnf (LocalPubkey p) = rnf p - -instance NFData RemotePubkey where - rnf (RemotePubkey p) = rnf p - -instance NFData PaymentBasepoint where - rnf (PaymentBasepoint p) = rnf p - -instance NFData DelayedPaymentBasepoint where - rnf (DelayedPaymentBasepoint p) = rnf p - -instance NFData HtlcBasepoint where - rnf (HtlcBasepoint p) = rnf p - -instance NFData FundingPubkey where - rnf (FundingPubkey p) = rnf p - --- Channel features -instance NFData ChannelFeatures where - rnf (ChannelFeatures x) = rnf x - -instance NFData FeeratePerKw where - rnf (FeeratePerKw x) = rnf x - -instance NFData DustLimit where - rnf (DustLimit x) = rnf x - --- Hash types -instance NFData CltvExpiry where - rnf (CltvExpiry x) = rnf x - --- HTLC types -instance NFData HTLCDirection where - rnf HTLCOffered = () - rnf HTLCReceived = () - -instance NFData HTLC where - rnf (HTLC d a h c) = rnf d `seq` rnf a `seq` rnf h `seq` rnf c - --- Transaction types -instance NFData CommitmentTx where - rnf (CommitmentTx v l i s o f) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf o `seq` rnf f - -instance NFData HTLCTx where - rnf (HTLCTx v l i s ov os) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf ov `seq` rnf os - -instance NFData ClosingTx where - rnf (ClosingTx v l i s o f) = - rnf v `seq` rnf l `seq` rnf i `seq` rnf s `seq` rnf o `seq` rnf f - --- Output types -instance NFData TxOutput where - rnf (TxOutput v s t) = rnf v `seq` rnf s `seq` rnf t - -instance NFData OutputType where - rnf OutputToLocal = () - rnf OutputToRemote = () - rnf OutputLocalAnchor = () - rnf OutputRemoteAnchor = () - rnf (OutputOfferedHTLC e) = rnf e - rnf (OutputReceivedHTLC e) = rnf e - --- Primitives -instance NFData Script where - rnf (Script bs) = rnf bs - - -instance NFData Sequence where - rnf (Sequence x) = rnf x - -instance NFData Locktime where - rnf (Locktime x) = rnf x - -instance NFData ToSelfDelay where - rnf (ToSelfDelay x) = rnf x - -instance NFData CommitmentNumber where - rnf (CommitmentNumber x) = rnf x - --- Context types -instance NFData CommitmentContext where - rnf ctx = rnf (cc_funding_outpoint ctx) `seq` - rnf (cc_commitment_number ctx) `seq` - rnf (cc_htlcs ctx) `seq` - rnf (cc_keys ctx) - -instance NFData CommitmentKeys where - rnf keys = rnf (ck_revocation_pubkey keys) `seq` - rnf (ck_local_delayed keys) `seq` - rnf (ck_local_htlc keys) `seq` - rnf (ck_remote_htlc keys) - -instance NFData HTLCContext where - rnf ctx = rnf (hc_commitment_txid ctx) `seq` - rnf (hc_htlc ctx) - -instance NFData ClosingContext where - rnf ctx = rnf (clc_funding_outpoint ctx) `seq` - rnf (clc_local_amount ctx) `seq` - rnf (clc_remote_amount ctx) - -instance NFData ValidationError where - rnf (InvalidVersion e a) = rnf e `seq` rnf a - rnf (InvalidLocktime lt) = rnf lt - rnf (InvalidSequence sq) = rnf sq - rnf InvalidOutputOrdering = () - rnf (DustLimitViolation i v d) = rnf i `seq` rnf v `seq` rnf d - rnf MissingAnchorOutput = () - rnf (InvalidAnchorValue v) = rnf v - rnf (InvalidFee e a) = rnf e `seq` rnf a - rnf (InvalidHTLCLocktime e a) = rnf e `seq` rnf a - rnf (InvalidHTLCSequence e a) = rnf e `seq` rnf a - rnf NoOutputs = () - rnf (TooManyOutputs n) = rnf n - --- Secret store (opaque, use generic rnf on the list) -instance NFData SecretStore where - rnf ss = ss `seq` () - main :: IO () -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 - - -- Secret generation allocations - func "generate_from_seed (final)" (generate_from_seed seed) 281474976710655 - func "generate_from_seed (first)" (generate_from_seed seed) 0 - - -- Fee calculation allocations - func "commitment_fee (0 htlcs)" (commitment_fee feerate noAnchors) 0 - func "commitment_fee (10 htlcs)" (commitment_fee feerate noAnchors) 10 - func "htlc_timeout_fee" (htlc_timeout_fee feerate) noAnchors - func "htlc_success_fee" (htlc_success_fee feerate) noAnchors - - -- Trimming allocations - func "is_trimmed (not trimmed)" - (is_trimmed dust feerate noAnchors) htlcNotTrimmed - func "is_trimmed (trimmed)" - (is_trimmed dust feerate noAnchors) htlcTrimmed - func "htlc_trim_threshold" - (htlc_trim_threshold dust feerate noAnchors) HTLCOffered - - -- Transaction building allocations - func "build_commitment_tx (0 htlcs, no anchors)" - build_commitment_tx (mkCommitmentContext htlcs0 noAnchors) - func "build_commitment_tx (10 htlcs, no anchors)" - build_commitment_tx (mkCommitmentContext htlcs10 noAnchors) - func "build_commitment_tx (100 htlcs, no anchors)" - build_commitment_tx (mkCommitmentContext htlcs100 noAnchors) - func "build_commitment_tx (10 htlcs, anchors)" - build_commitment_tx (mkCommitmentContext htlcs10 withAnchors) - func "build_htlc_timeout_tx" - build_htlc_timeout_tx sampleHtlcContext - func "build_htlc_success_tx" - build_htlc_success_tx sampleHtlcContext - func "build_closing_tx" - build_closing_tx sampleClosingContext - - -- Script generation allocations - func "funding_script" - (funding_script (FundingPubkey samplePubkey1)) - (FundingPubkey samplePubkey2) - func "to_local_script" - (to_local_script (RevocationPubkey samplePubkey1) - (ToSelfDelay 144)) - (LocalDelayedPubkey samplePubkey2) - func "to_remote_script (no anchors)" - (to_remote_script (RemotePubkey samplePubkey1)) noAnchors - func "to_remote_script (anchors)" - (to_remote_script (RemotePubkey samplePubkey1)) withAnchors - func "anchor_script" - anchor_script (FundingPubkey samplePubkey1) - func "offered_htlc_script" - (offered_htlc_script (RevocationPubkey samplePubkey1) - (RemoteHtlcPubkey samplePubkey2) - (LocalHtlcPubkey samplePubkey3) - (PaymentHash $ BS.replicate 32 0)) - noAnchors - func "received_htlc_script" - (received_htlc_script (RevocationPubkey samplePubkey1) - (RemoteHtlcPubkey samplePubkey2) - (LocalHtlcPubkey samplePubkey3) - (PaymentHash $ BS.replicate 32 0) - (CltvExpiry 500000)) - noAnchors - - -- Serialization allocations - func "encode_tx (0 htlcs)" - encode_tx (build_commitment_tx $ - mkCommitmentContext htlcs0 noAnchors) - func "encode_tx (10 htlcs)" - encode_tx (build_commitment_tx $ - mkCommitmentContext htlcs10 noAnchors) - func "encode_tx (100 htlcs)" - encode_tx (build_commitment_tx $ - mkCommitmentContext htlcs100 noAnchors) - func "encode_htlc_tx" - encode_htlc_tx (build_htlc_timeout_tx sampleHtlcContext) - func "encode_closing_tx" - encode_closing_tx (build_closing_tx sampleClosingContext) - - -- Parsing allocations - func "decode_tx (0 htlcs)" - decode_tx (encodeTx0 htlcs0 noAnchors) - func "decode_tx (10 htlcs)" - decode_tx (encodeTx0 htlcs10 noAnchors) - func "decode_tx (100 htlcs)" - decode_tx (encodeTx0 htlcs100 noAnchors) - - -- Validation allocations - func "validate_commitment_tx (valid)" - (validate_commitment_tx dust noAnchors) - (build_commitment_tx $ mkCommitmentContext htlcs10 noAnchors) - func "validate_htlc_tx" - validate_htlc_tx (build_htlc_timeout_tx sampleHtlcContext) - func "validate_closing_tx" - validate_closing_tx (build_closing_tx sampleClosingContext) - func "validate_output_ordering" - validate_output_ordering (ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs10 noAnchors) - - -- Secret storage allocations - func "insert_secret (first)" - (insert_secret (BS.replicate 32 0xFF) 281474976710655) - empty_store - func "derive_old_secret (recent)" - (derive_old_secret 281474976710654) - filledStore - func "derive_old_secret (old)" - (derive_old_secret 281474976710600) - filledStore - - -- Output sorting allocations - func "sort_outputs (10)" - sort_outputs (ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs10 noAnchors) - func "sort_outputs (100)" - sort_outputs (ctx_outputs $ build_commitment_tx $ - mkCommitmentContext htlcs100 noAnchors) - - where - -- Key derivation test data - basepoint = Point $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, - 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] - - perCommitmentPoint = PerCommitmentPoint $ Point $ BS.pack - [0x02, 0x5f, 0x71, 0x17, 0xa7, 0x81, 0x50, 0xfe, 0x2e, 0xf9, 0x7d, - 0xb7, 0xcf, 0xc8, 0x3b, 0xd5, 0x7b, 0x2e, 0x2c, 0x0d, 0x0d, 0xd2, - 0x5e, 0xaf, 0x46, 0x7a, 0x4a, 0x1c, 0x2a, 0x45, 0xce, 0x14, 0x86] - - revocationBasepoint = RevocationBasepoint $ Point $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 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 - - -- Fee calculation test data - feerate = FeeratePerKw 5000 - noAnchors = ChannelFeatures { cf_option_anchors = False } - withAnchors = ChannelFeatures { cf_option_anchors = True } - - -- Trimming test data - dust = DustLimit (Satoshi 546) - - htlcNotTrimmed = HTLC - { htlc_direction = HTLCOffered - , htlc_amount_msat = MilliSatoshi 5000000 - , htlc_payment_hash = PaymentHash (BS.replicate 32 0) - , htlc_cltv_expiry = CltvExpiry 500000 - } - - htlcTrimmed = HTLC - { htlc_direction = HTLCOffered - , htlc_amount_msat = MilliSatoshi 1000000 - , htlc_payment_hash = PaymentHash (BS.replicate 32 0) - , htlc_cltv_expiry = CltvExpiry 500000 - } - - -- Sample pubkeys - samplePubkey1, samplePubkey2, samplePubkey3 :: Pubkey - samplePubkey1 = Pubkey $ BS.pack - [0x03, 0x6d, 0x6c, 0xaa, 0xc2, 0x48, 0xaf, 0x96, 0xf6, 0xaf, 0xa7, - 0xf9, 0x04, 0xf5, 0x50, 0x25, 0x3a, 0x0f, 0x3e, 0xf3, 0xf5, 0xaa, - 0x2f, 0xe6, 0x83, 0x8a, 0x95, 0xb2, 0x16, 0x69, 0x14, 0x68, 0xe2] - samplePubkey2 = Pubkey $ BS.pack - [0x02, 0x5f, 0x71, 0x17, 0xa7, 0x81, 0x50, 0xfe, 0x2e, 0xf9, 0x7d, - 0xb7, 0xcf, 0xc8, 0x3b, 0xd5, 0x7b, 0x2e, 0x2c, 0x0d, 0x0d, 0xd2, - 0x5e, 0xaf, 0x46, 0x7a, 0x4a, 0x1c, 0x2a, 0x45, 0xce, 0x14, 0x86] - samplePubkey3 = samplePubkey1 - - -- Helper to encode a commitment tx for decode benchmarks - encodeTx0 :: [HTLC] -> ChannelFeatures -> BS.ByteString - encodeTx0 htlcs features = - case encode_tx (build_commitment_tx - (mkCommitmentContext htlcs features)) of - Nothing -> BS.empty - Just bs -> bs - - -- Funding outpoint - sampleFundingOutpoint :: OutPoint - sampleFundingOutpoint = OutPoint (TxId $ BS.replicate 32 0x01) 0 - - -- HTLC builder - mkHtlc :: HTLCDirection -> Word64 -> Word32 -> HTLC - mkHtlc dir amtMsat expiry = HTLC - { htlc_direction = dir - , htlc_amount_msat = MilliSatoshi amtMsat - , htlc_payment_hash = PaymentHash (BS.replicate 32 0x00) - , htlc_cltv_expiry = CltvExpiry expiry - } - - htlcs0, htlcs10, htlcs100 :: [HTLC] - htlcs0 = [] - htlcs10 = [mkHtlc (if even i then HTLCOffered else HTLCReceived) - (5000000 + i * 100000) (500000 + fromIntegral i) - | i <- [0..9]] - htlcs100 = [mkHtlc (if even i then HTLCOffered else HTLCReceived) - (5000000 + i * 10000) (500000 + fromIntegral i) - | i <- [0..99]] - - -- CommitmentKeys - sampleCommitmentKeys :: CommitmentKeys - sampleCommitmentKeys = CommitmentKeys - { ck_revocation_pubkey = RevocationPubkey samplePubkey1 - , ck_local_delayed = LocalDelayedPubkey samplePubkey1 - , ck_local_htlc = LocalHtlcPubkey samplePubkey1 - , ck_remote_htlc = RemoteHtlcPubkey samplePubkey2 - , ck_local_payment = LocalPubkey samplePubkey1 - , ck_remote_payment = RemotePubkey samplePubkey2 - , ck_local_funding = FundingPubkey samplePubkey1 - , ck_remote_funding = FundingPubkey samplePubkey2 - } - - -- CommitmentContext builder - mkCommitmentContext :: [HTLC] -> ChannelFeatures -> CommitmentContext - mkCommitmentContext htlcs features = CommitmentContext - { cc_funding_outpoint = sampleFundingOutpoint - , cc_commitment_number = CommitmentNumber 42 - , cc_local_payment_bp = PaymentBasepoint $ - Point $ unPubkey samplePubkey1 - , cc_remote_payment_bp = PaymentBasepoint $ - Point $ unPubkey samplePubkey2 - , cc_to_self_delay = ToSelfDelay 144 - , cc_dust_limit = DustLimit (Satoshi 546) - , cc_feerate = FeeratePerKw 5000 - , cc_features = features - , cc_is_funder = True - , cc_to_local_msat = MilliSatoshi 500000000 - , cc_to_remote_msat = MilliSatoshi 500000000 - , cc_htlcs = htlcs - , cc_keys = sampleCommitmentKeys - } - - -- HTLC context - sampleHtlcContext :: HTLCContext - sampleHtlcContext = HTLCContext - { hc_commitment_txid = TxId $ BS.replicate 32 0x01 - , hc_output_index = 0 - , hc_htlc = mkHtlc HTLCOffered 5000000 500000 - , hc_to_self_delay = ToSelfDelay 144 - , hc_feerate = FeeratePerKw 5000 - , hc_features = noAnchors - , hc_revocation_pubkey = RevocationPubkey samplePubkey1 - , hc_local_delayed = LocalDelayedPubkey samplePubkey1 - } - - -- Closing context - sampleClosingContext :: ClosingContext - sampleClosingContext = ClosingContext - { clc_funding_outpoint = sampleFundingOutpoint - , clc_local_amount = Satoshi 500000 - , clc_remote_amount = Satoshi 500000 - , clc_local_script = Script $ - BS.pack [0x00, 0x14] <> BS.replicate 20 0x01 - , clc_remote_script = Script $ - BS.pack [0x00, 0x14] <> BS.replicate 20 0x02 - , clc_local_dust_limit = DustLimit (Satoshi 546) - , clc_remote_dust_limit = DustLimit (Satoshi 546) - , clc_fee = Satoshi 1000 - , clc_is_funder = True - , clc_locktime = Locktime 0 - , clc_funding_script = funding_script (FundingPubkey samplePubkey1) - (FundingPubkey samplePubkey2) - } - - -- Secret storage for benchmarks - filledStore :: SecretStore - filledStore = foldl insertOne empty_store [0..99] - where - insertOne store i = - let idx = 281474976710655 - i - sec = BS.replicate 32 (fromIntegral i) - in case insert_secret sec idx store of - Just s -> s - Nothing -> store +main = do + fx <- fixtures + let (next_idx, next_secret) = fx_store_next fx + mainWith $ do + wgroup "keys" $ do + func "derive_per_commitment_point" derive_per_commitment_point + (fx_secret fx) + func "derive_per_commitment_point'" (derive_per_commitment_point' tex) + (fx_secret fx) + func "derive_pubkey" (derive_pubkey (fx_basepoint fx)) (fx_pcp fx) + func "derive_revocationpubkey" (derive_revocationpubkey (fx_rbp fx)) + (fx_pcp fx) + func "derive_commitment_keys" + (derive_commitment_keys (fx_local_bps fx) (fx_local_fund fx) + (fx_remote_bps fx) (fx_remote_fund fx)) + (fx_pcp fx) + func "derive_commitment_keys'" + (derive_commitment_keys' tex (fx_local_bps fx) (fx_local_fund fx) + (fx_remote_bps fx) (fx_remote_fund fx)) + (fx_pcp fx) + func "derive_privkey" (derive_privkey (fx_seckey fx)) (fx_pcp fx) + func "derive_revocationprivkey" + (derive_revocationprivkey (fx_seckey fx)) (fx_secret fx) + wgroup "secrets" $ do + func "generate_from_seed (index 2^48 - 1)" + (generate_from_seed (fx_seed fx)) (fx_store_oldest fx) + func "insert_secret (14 lower buckets)" + (insert_secret next_secret next_idx) (fx_store fx) + func "derive_old_secret (index 2^48 - 1)" + (derive_old_secret (fx_store_oldest fx)) (fx_store fx) + func "secret_store . un_secret_store" + (secret_store . un_secret_store) (fx_store fx) + wgroup "transactions" $ do + func "build_commitment_tx (no htlcs)" build_commitment_tx + (fx_ctx_simple fx) + func "build_commitment_tx (5 htlcs)" build_commitment_tx + (fx_ctx_htlcs fx) + func "build_commitment_tx (5 htlcs, anchors)" build_commitment_tx + (fx_ctx_anchors fx) + func "encode_commitment_tx (5 htlcs)" encode_commitment_tx + (fx_commit fx) + func "build_htlc_tx" build_htlc_tx (fx_htlc_ctx fx) + func "encode_htlc_tx" encode_htlc_tx (fx_htlc_tx fx) + func "build_closing_tx" build_closing_tx (fx_closing fx) + func "build_legacy_closing_tx" build_legacy_closing_tx (fx_legacy fx) + func "encode_closing_tx" encode_closing_tx (fx_closing_tx fx) diff --git a/ppad-bolt3.cabal b/ppad-bolt3.cabal @@ -70,32 +70,41 @@ benchmark bolt3-bench default-language: Haskell2010 hs-source-dirs: bench main-is: Main.hs + other-modules: Fixtures ghc-options: - -rtsopts -O2 -Wall -fno-warn-orphans + -rtsopts -O2 -Wall build-depends: base , bytestring , criterion , deepseq + , ppad-base16 + , ppad-bolt1 , ppad-bolt3 - , ppad-secp256k1 >= 0.5.4 && < 0.6 + , ppad-secp256k1 + , ppad-sha256 + , ppad-tx benchmark bolt3-weigh type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: bench main-is: Weight.hs + other-modules: Fixtures ghc-options: - -rtsopts -O2 -Wall -fno-warn-orphans + -rtsopts -O2 -Wall build-depends: base , bytestring , deepseq + , ppad-base16 + , ppad-bolt1 , ppad-bolt3 - , ppad-secp256k1 >= 0.5.4 && < 0.6 + , ppad-secp256k1 + , ppad-sha256 + , ppad-tx , weigh -