packages feed

ppad-bolt3 0.0.1 → 0.1.0

raw patch · 15 files changed

+4770/−4211 lines, 15 filesdep +QuickCheckdep +ppad-base16dep +ppad-bolt1dep −base16-bytestringdep ~deepseqdep ~ppad-secp256k1dep ~ppad-sha256PVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, ppad-base16, ppad-bolt1, ppad-fixed, ppad-tx, tasty-quickcheck

Dependencies removed: base16-bytestring

Dependency ranges changed: deepseq, ppad-secp256k1, ppad-sha256

API changes (from Hackage documentation)

- Lightning.Protocol.BOLT3: ChannelFeatures :: !Bool -> ChannelFeatures
- Lightning.Protocol.BOLT3: CommitmentNumber :: Word64 -> CommitmentNumber
- Lightning.Protocol.BOLT3: DustLimitViolation :: {-# UNPACK #-} !Int -> !Satoshi -> !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3: EmptyInput :: DecodeError
- Lightning.Protocol.BOLT3: InsufficientBytes :: !Int -> !Int -> DecodeError
- Lightning.Protocol.BOLT3: InvalidAnchorValue :: {-# UNPACK #-} !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3: InvalidFee :: {-# UNPACK #-} !Satoshi -> {-# UNPACK #-} !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3: InvalidFlag :: !Word8 -> DecodeError
- Lightning.Protocol.BOLT3: InvalidHTLCLocktime :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3: InvalidHTLCSequence :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3: InvalidLocktime :: {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3: InvalidMarker :: !Word8 -> DecodeError
- Lightning.Protocol.BOLT3: InvalidOutputOrdering :: ValidationError
- Lightning.Protocol.BOLT3: InvalidSequence :: {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3: InvalidVarint :: DecodeError
- Lightning.Protocol.BOLT3: InvalidVersion :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3: LocalPubkey :: Pubkey -> LocalPubkey
- Lightning.Protocol.BOLT3: MilliSatoshi :: Word64 -> MilliSatoshi
- Lightning.Protocol.BOLT3: MissingAnchorOutput :: ValidationError
- Lightning.Protocol.BOLT3: NoOutputs :: ValidationError
- Lightning.Protocol.BOLT3: Outpoint :: {-# UNPACK #-} !TxId -> {-# UNPACK #-} !Word32 -> Outpoint
- Lightning.Protocol.BOLT3: OutputOfferedHTLC :: {-# UNPACK #-} !CltvExpiry -> OutputType
- Lightning.Protocol.BOLT3: OutputReceivedHTLC :: {-# UNPACK #-} !CltvExpiry -> OutputType
- Lightning.Protocol.BOLT3: PaymentHash :: ByteString -> PaymentHash
- Lightning.Protocol.BOLT3: PaymentPreimage :: ByteString -> PaymentPreimage
- Lightning.Protocol.BOLT3: PerCommitmentSecret :: ByteString -> PerCommitmentSecret
- Lightning.Protocol.BOLT3: Point :: ByteString -> Point
- Lightning.Protocol.BOLT3: Pubkey :: ByteString -> Pubkey
- Lightning.Protocol.BOLT3: RawInput :: !Outpoint -> !ByteString -> !Sequence -> RawInput
- Lightning.Protocol.BOLT3: RawOutput :: !Satoshi -> !Script -> RawOutput
- Lightning.Protocol.BOLT3: RawTx :: {-# UNPACK #-} !Word32 -> ![RawInput] -> ![RawOutput] -> ![[ByteString]] -> !Locktime -> RawTx
- Lightning.Protocol.BOLT3: RemoteDelayedPubkey :: Pubkey -> RemoteDelayedPubkey
- Lightning.Protocol.BOLT3: Satoshi :: Word64 -> Satoshi
- Lightning.Protocol.BOLT3: Seckey :: ByteString -> Seckey
- Lightning.Protocol.BOLT3: TooManyOutputs :: {-# UNPACK #-} !Int -> ValidationError
- Lightning.Protocol.BOLT3: TxId :: ByteString -> TxId
- Lightning.Protocol.BOLT3: TxOutput :: {-# UNPACK #-} !Satoshi -> !Script -> !OutputType -> TxOutput
- Lightning.Protocol.BOLT3: Witness :: [ByteString] -> Witness
- Lightning.Protocol.BOLT3: [cc_features] :: CommitmentContext -> !ChannelFeatures
- Lightning.Protocol.BOLT3: [cf_option_anchors] :: ChannelFeatures -> !Bool
- Lightning.Protocol.BOLT3: [ck_local_payment] :: CommitmentKeys -> !LocalPubkey
- Lightning.Protocol.BOLT3: [clc_funding_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3: [clc_is_funder] :: ClosingContext -> !Bool
- Lightning.Protocol.BOLT3: [clc_local_amount] :: ClosingContext -> !Satoshi
- Lightning.Protocol.BOLT3: [clc_local_dust_limit] :: ClosingContext -> !DustLimit
- Lightning.Protocol.BOLT3: [clc_local_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3: [clc_remote_amount] :: ClosingContext -> !Satoshi
- Lightning.Protocol.BOLT3: [clc_remote_dust_limit] :: ClosingContext -> !DustLimit
- Lightning.Protocol.BOLT3: [clc_remote_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3: [cltx_funding_script] :: ClosingTx -> !Script
- Lightning.Protocol.BOLT3: [hc_features] :: HTLCContext -> !ChannelFeatures
- Lightning.Protocol.BOLT3: [outpoint_index] :: Outpoint -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3: [outpoint_txid] :: Outpoint -> {-# UNPACK #-} !TxId
- Lightning.Protocol.BOLT3: [ri_outpoint] :: RawInput -> !Outpoint
- Lightning.Protocol.BOLT3: [ri_script_sig] :: RawInput -> !ByteString
- Lightning.Protocol.BOLT3: [ri_sequence] :: RawInput -> !Sequence
- Lightning.Protocol.BOLT3: [ro_script] :: RawOutput -> !Script
- Lightning.Protocol.BOLT3: [ro_value] :: RawOutput -> !Satoshi
- Lightning.Protocol.BOLT3: [rtx_inputs] :: RawTx -> ![RawInput]
- Lightning.Protocol.BOLT3: [rtx_locktime] :: RawTx -> !Locktime
- Lightning.Protocol.BOLT3: [rtx_outputs] :: RawTx -> ![RawOutput]
- Lightning.Protocol.BOLT3: [rtx_version] :: RawTx -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3: [rtx_witness] :: RawTx -> ![[ByteString]]
- Lightning.Protocol.BOLT3: [txout_script] :: TxOutput -> !Script
- Lightning.Protocol.BOLT3: [txout_type] :: TxOutput -> !OutputType
- Lightning.Protocol.BOLT3: [txout_value] :: TxOutput -> {-# UNPACK #-} !Satoshi
- Lightning.Protocol.BOLT3: [unCltvExpiry] :: CltvExpiry -> Word32
- Lightning.Protocol.BOLT3: [unCommitmentNumber] :: CommitmentNumber -> Word64
- Lightning.Protocol.BOLT3: [unDelayedPaymentBasepoint] :: DelayedPaymentBasepoint -> Point
- Lightning.Protocol.BOLT3: [unDustLimit] :: DustLimit -> Satoshi
- Lightning.Protocol.BOLT3: [unFeeratePerKw] :: FeeratePerKw -> Word32
- Lightning.Protocol.BOLT3: [unFundingPubkey] :: FundingPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unHtlcBasepoint] :: HtlcBasepoint -> Point
- Lightning.Protocol.BOLT3: [unLocalDelayedPubkey] :: LocalDelayedPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unLocalHtlcPubkey] :: LocalHtlcPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unLocalPubkey] :: LocalPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unLocktime] :: Locktime -> Word32
- Lightning.Protocol.BOLT3: [unMilliSatoshi] :: MilliSatoshi -> Word64
- Lightning.Protocol.BOLT3: [unPaymentBasepoint] :: PaymentBasepoint -> Point
- Lightning.Protocol.BOLT3: [unPaymentHash] :: PaymentHash -> ByteString
- Lightning.Protocol.BOLT3: [unPaymentPreimage] :: PaymentPreimage -> ByteString
- Lightning.Protocol.BOLT3: [unPerCommitmentPoint] :: PerCommitmentPoint -> Point
- Lightning.Protocol.BOLT3: [unPerCommitmentSecret] :: PerCommitmentSecret -> ByteString
- Lightning.Protocol.BOLT3: [unPoint] :: Point -> ByteString
- Lightning.Protocol.BOLT3: [unPubkey] :: Pubkey -> ByteString
- Lightning.Protocol.BOLT3: [unRemoteDelayedPubkey] :: RemoteDelayedPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unRemoteHtlcPubkey] :: RemoteHtlcPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unRemotePubkey] :: RemotePubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unRevocationBasepoint] :: RevocationBasepoint -> Point
- Lightning.Protocol.BOLT3: [unRevocationPubkey] :: RevocationPubkey -> Pubkey
- Lightning.Protocol.BOLT3: [unSatoshi] :: Satoshi -> Word64
- Lightning.Protocol.BOLT3: [unScript] :: Script -> ByteString
- Lightning.Protocol.BOLT3: [unSeckey] :: Seckey -> ByteString
- Lightning.Protocol.BOLT3: [unSequence] :: Sequence -> Word32
- Lightning.Protocol.BOLT3: [unToSelfDelay] :: ToSelfDelay -> Word16
- Lightning.Protocol.BOLT3: [unTxId] :: TxId -> ByteString
- Lightning.Protocol.BOLT3: [unWitness] :: Witness -> [ByteString]
- Lightning.Protocol.BOLT3: build_htlc_success_tx :: HTLCContext -> HTLCTx
- Lightning.Protocol.BOLT3: build_htlc_timeout_tx :: HTLCContext -> HTLCTx
- Lightning.Protocol.BOLT3: commitment_weight :: ChannelFeatures -> Word64 -> Word64
- Lightning.Protocol.BOLT3: commitment_weight_anchors :: Word64
- Lightning.Protocol.BOLT3: commitment_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3: data ChannelFeatures
- Lightning.Protocol.BOLT3: data DecodeError
- Lightning.Protocol.BOLT3: data Outpoint
- Lightning.Protocol.BOLT3: data RawInput
- Lightning.Protocol.BOLT3: data RawOutput
- Lightning.Protocol.BOLT3: data RawTx
- Lightning.Protocol.BOLT3: data TxOutput
- Lightning.Protocol.BOLT3: data ValidationError
- Lightning.Protocol.BOLT3: decode_le32 :: ByteString -> Either DecodeError (Word32, ByteString)
- Lightning.Protocol.BOLT3: decode_le64 :: ByteString -> Either DecodeError (Word64, ByteString)
- Lightning.Protocol.BOLT3: decode_outpoint :: ByteString -> Either DecodeError (Outpoint, ByteString)
- Lightning.Protocol.BOLT3: decode_output :: ByteString -> Either DecodeError (RawOutput, ByteString)
- Lightning.Protocol.BOLT3: decode_tx :: ByteString -> Either DecodeError RawTx
- Lightning.Protocol.BOLT3: decode_varint :: ByteString -> Either DecodeError (Word64, ByteString)
- Lightning.Protocol.BOLT3: decode_witness :: ByteString -> Either DecodeError (Witness, ByteString)
- Lightning.Protocol.BOLT3: derive_local_delayedpubkey :: DelayedPaymentBasepoint -> PerCommitmentPoint -> Maybe LocalDelayedPubkey
- Lightning.Protocol.BOLT3: derive_local_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe LocalHtlcPubkey
- Lightning.Protocol.BOLT3: derive_localpubkey :: PaymentBasepoint -> PerCommitmentPoint -> Maybe LocalPubkey
- Lightning.Protocol.BOLT3: derive_remote_delayedpubkey :: DelayedPaymentBasepoint -> PerCommitmentPoint -> Maybe RemoteDelayedPubkey
- Lightning.Protocol.BOLT3: derive_remote_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe RemoteHtlcPubkey
- Lightning.Protocol.BOLT3: derive_secret :: ByteString -> Int -> Word64 -> ByteString
- Lightning.Protocol.BOLT3: empty_store :: SecretStore
- Lightning.Protocol.BOLT3: encode_funding_witness :: ByteString -> ByteString -> Script -> ByteString
- Lightning.Protocol.BOLT3: encode_le32 :: Word32 -> ByteString
- Lightning.Protocol.BOLT3: encode_le64 :: Word64 -> ByteString
- Lightning.Protocol.BOLT3: encode_outpoint :: Outpoint -> ByteString
- Lightning.Protocol.BOLT3: encode_output :: TxOutput -> ByteString
- Lightning.Protocol.BOLT3: encode_tx :: CommitmentTx -> ByteString
- Lightning.Protocol.BOLT3: encode_tx_for_signing :: CommitmentTx -> ByteString
- Lightning.Protocol.BOLT3: encode_varint :: Word64 -> ByteString
- Lightning.Protocol.BOLT3: encode_witness :: Witness -> ByteString
- Lightning.Protocol.BOLT3: has_anchors :: ChannelFeatures -> Bool
- Lightning.Protocol.BOLT3: htlc_output_script :: RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
- Lightning.Protocol.BOLT3: htlc_output_weight :: Word64
- Lightning.Protocol.BOLT3: htlc_output_witness_revoke :: ByteString -> Witness
- Lightning.Protocol.BOLT3: htlc_output_witness_spend :: ByteString -> Witness
- Lightning.Protocol.BOLT3: htlc_success_weight_anchors :: Word64
- Lightning.Protocol.BOLT3: htlc_success_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3: htlc_timeout_weight_anchors :: Word64
- Lightning.Protocol.BOLT3: htlc_timeout_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3: msat_to_sat :: MilliSatoshi -> Satoshi
- Lightning.Protocol.BOLT3: newtype CommitmentNumber
- Lightning.Protocol.BOLT3: newtype LocalPubkey
- Lightning.Protocol.BOLT3: newtype MilliSatoshi
- Lightning.Protocol.BOLT3: newtype PaymentHash
- Lightning.Protocol.BOLT3: newtype PaymentPreimage
- Lightning.Protocol.BOLT3: newtype PerCommitmentSecret
- Lightning.Protocol.BOLT3: newtype Point
- Lightning.Protocol.BOLT3: newtype Pubkey
- Lightning.Protocol.BOLT3: newtype RemoteDelayedPubkey
- Lightning.Protocol.BOLT3: newtype Satoshi
- Lightning.Protocol.BOLT3: newtype Seckey
- Lightning.Protocol.BOLT3: newtype TxId
- Lightning.Protocol.BOLT3: newtype Witness
- Lightning.Protocol.BOLT3: payment_hash :: ByteString -> Maybe PaymentHash
- Lightning.Protocol.BOLT3: payment_preimage :: ByteString -> Maybe PaymentPreimage
- Lightning.Protocol.BOLT3: per_commitment_secret :: ByteString -> Maybe PerCommitmentSecret
- Lightning.Protocol.BOLT3: point :: ByteString -> Maybe Point
- Lightning.Protocol.BOLT3: pubkey :: ByteString -> Maybe Pubkey
- Lightning.Protocol.BOLT3: sat_to_msat :: Satoshi -> MilliSatoshi
- Lightning.Protocol.BOLT3: sort_outputs :: [TxOutput] -> [TxOutput]
- Lightning.Protocol.BOLT3: to_remote_script :: RemotePubkey -> ChannelFeatures -> Script
- Lightning.Protocol.BOLT3: trimmed_htlcs :: DustLimit -> FeeratePerKw -> ChannelFeatures -> [HTLC] -> [HTLC]
- Lightning.Protocol.BOLT3: txid :: ByteString -> Maybe TxId
- Lightning.Protocol.BOLT3: untrimmed_htlcs :: DustLimit -> FeeratePerKw -> ChannelFeatures -> [HTLC] -> [HTLC]
- Lightning.Protocol.BOLT3: validate_anchor_outputs :: [TxOutput] -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_closing_tx :: ClosingTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_commitment_fee :: FeeratePerKw -> ChannelFeatures -> Word64 -> Satoshi -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_commitment_locktime :: Locktime -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_commitment_sequence :: Sequence -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_commitment_tx :: DustLimit -> ChannelFeatures -> CommitmentTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_dust_limits :: DustLimit -> [TxOutput] -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_htlc_fee :: FeeratePerKw -> ChannelFeatures -> HTLCDirection -> Satoshi -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_htlc_success_tx :: ChannelFeatures -> HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_htlc_timeout_tx :: ChannelFeatures -> CltvExpiry -> HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_htlc_tx :: HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_legacy_closing_tx :: ClosingTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3: validate_output_ordering :: [TxOutput] -> Either ValidationError ()
- Lightning.Protocol.BOLT3: witness_script_hash :: Script -> ByteString
- Lightning.Protocol.BOLT3.Decode: EmptyInput :: DecodeError
- Lightning.Protocol.BOLT3.Decode: InsufficientBytes :: !Int -> !Int -> DecodeError
- Lightning.Protocol.BOLT3.Decode: InvalidFlag :: !Word8 -> DecodeError
- Lightning.Protocol.BOLT3.Decode: InvalidMarker :: !Word8 -> DecodeError
- Lightning.Protocol.BOLT3.Decode: InvalidVarint :: DecodeError
- Lightning.Protocol.BOLT3.Decode: RawInput :: !Outpoint -> !ByteString -> !Sequence -> RawInput
- Lightning.Protocol.BOLT3.Decode: RawOutput :: !Satoshi -> !Script -> RawOutput
- Lightning.Protocol.BOLT3.Decode: RawTx :: {-# UNPACK #-} !Word32 -> ![RawInput] -> ![RawOutput] -> ![[ByteString]] -> !Locktime -> RawTx
- Lightning.Protocol.BOLT3.Decode: [ri_outpoint] :: RawInput -> !Outpoint
- Lightning.Protocol.BOLT3.Decode: [ri_script_sig] :: RawInput -> !ByteString
- Lightning.Protocol.BOLT3.Decode: [ri_sequence] :: RawInput -> !Sequence
- Lightning.Protocol.BOLT3.Decode: [ro_script] :: RawOutput -> !Script
- Lightning.Protocol.BOLT3.Decode: [ro_value] :: RawOutput -> !Satoshi
- Lightning.Protocol.BOLT3.Decode: [rtx_inputs] :: RawTx -> ![RawInput]
- Lightning.Protocol.BOLT3.Decode: [rtx_locktime] :: RawTx -> !Locktime
- Lightning.Protocol.BOLT3.Decode: [rtx_outputs] :: RawTx -> ![RawOutput]
- Lightning.Protocol.BOLT3.Decode: [rtx_version] :: RawTx -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Decode: [rtx_witness] :: RawTx -> ![[ByteString]]
- Lightning.Protocol.BOLT3.Decode: data DecodeError
- Lightning.Protocol.BOLT3.Decode: data RawInput
- Lightning.Protocol.BOLT3.Decode: data RawOutput
- Lightning.Protocol.BOLT3.Decode: data RawTx
- Lightning.Protocol.BOLT3.Decode: decode_le32 :: ByteString -> Either DecodeError (Word32, ByteString)
- Lightning.Protocol.BOLT3.Decode: decode_le64 :: ByteString -> Either DecodeError (Word64, ByteString)
- Lightning.Protocol.BOLT3.Decode: decode_outpoint :: ByteString -> Either DecodeError (Outpoint, ByteString)
- Lightning.Protocol.BOLT3.Decode: decode_output :: ByteString -> Either DecodeError (RawOutput, ByteString)
- Lightning.Protocol.BOLT3.Decode: decode_tx :: ByteString -> Either DecodeError RawTx
- Lightning.Protocol.BOLT3.Decode: decode_varint :: ByteString -> Either DecodeError (Word64, ByteString)
- Lightning.Protocol.BOLT3.Decode: decode_witness :: ByteString -> Either DecodeError (Witness, ByteString)
- Lightning.Protocol.BOLT3.Decode: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Decode.DecodeError
- Lightning.Protocol.BOLT3.Decode: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Decode.RawInput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Decode.RawOutput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Decode.RawTx
- Lightning.Protocol.BOLT3.Decode: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Decode.DecodeError
- Lightning.Protocol.BOLT3.Decode: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Decode.RawInput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Decode.RawOutput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Decode.RawTx
- Lightning.Protocol.BOLT3.Decode: instance GHC.Show.Show Lightning.Protocol.BOLT3.Decode.DecodeError
- Lightning.Protocol.BOLT3.Decode: instance GHC.Show.Show Lightning.Protocol.BOLT3.Decode.RawInput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Show.Show Lightning.Protocol.BOLT3.Decode.RawOutput
- Lightning.Protocol.BOLT3.Decode: instance GHC.Show.Show Lightning.Protocol.BOLT3.Decode.RawTx
- Lightning.Protocol.BOLT3.Encode: encode_closing_tx :: ClosingTx -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_funding_witness :: ByteString -> ByteString -> Script -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_htlc_tx :: HTLCTx -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_le32 :: Word32 -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_le64 :: Word64 -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_outpoint :: Outpoint -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_output :: TxOutput -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_tx :: CommitmentTx -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_tx_for_signing :: CommitmentTx -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_varint :: Word64 -> ByteString
- Lightning.Protocol.BOLT3.Encode: encode_witness :: Witness -> ByteString
- Lightning.Protocol.BOLT3.Keys: data SecretStore
- Lightning.Protocol.BOLT3.Keys: derive_local_delayedpubkey :: DelayedPaymentBasepoint -> PerCommitmentPoint -> Maybe LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Keys: derive_local_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Keys: derive_localpubkey :: PaymentBasepoint -> PerCommitmentPoint -> Maybe LocalPubkey
- Lightning.Protocol.BOLT3.Keys: derive_old_secret :: Word64 -> SecretStore -> Maybe ByteString
- Lightning.Protocol.BOLT3.Keys: derive_per_commitment_point :: PerCommitmentSecret -> Maybe PerCommitmentPoint
- Lightning.Protocol.BOLT3.Keys: derive_pubkey :: Point -> PerCommitmentPoint -> Maybe Pubkey
- Lightning.Protocol.BOLT3.Keys: derive_remote_delayedpubkey :: DelayedPaymentBasepoint -> PerCommitmentPoint -> Maybe RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Keys: derive_remote_htlcpubkey :: HtlcBasepoint -> PerCommitmentPoint -> Maybe RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Keys: derive_revocationpubkey :: RevocationBasepoint -> PerCommitmentPoint -> Maybe RevocationPubkey
- Lightning.Protocol.BOLT3.Keys: derive_secret :: ByteString -> Int -> Word64 -> ByteString
- Lightning.Protocol.BOLT3.Keys: empty_store :: SecretStore
- Lightning.Protocol.BOLT3.Keys: generate_from_seed :: ByteString -> Word64 -> ByteString
- Lightning.Protocol.BOLT3.Keys: insert_secret :: ByteString -> Word64 -> SecretStore -> Maybe SecretStore
- Lightning.Protocol.BOLT3.Keys: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Keys.SecretEntry
- Lightning.Protocol.BOLT3.Keys: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Keys.SecretStore
- Lightning.Protocol.BOLT3.Keys: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Keys.SecretEntry
- Lightning.Protocol.BOLT3.Keys: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Keys.SecretStore
- Lightning.Protocol.BOLT3.Keys: instance GHC.Show.Show Lightning.Protocol.BOLT3.Keys.SecretEntry
- Lightning.Protocol.BOLT3.Keys: instance GHC.Show.Show Lightning.Protocol.BOLT3.Keys.SecretStore
- Lightning.Protocol.BOLT3.Keys: obscured_commitment_number :: PaymentBasepoint -> PaymentBasepoint -> CommitmentNumber -> Word64
- Lightning.Protocol.BOLT3.Scripts: anchor_script :: FundingPubkey -> Script
- Lightning.Protocol.BOLT3.Scripts: anchor_witness_anyone :: Witness
- Lightning.Protocol.BOLT3.Scripts: anchor_witness_owner :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: funding_script :: FundingPubkey -> FundingPubkey -> Script
- Lightning.Protocol.BOLT3.Scripts: funding_witness :: ByteString -> ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: htlc_output_script :: RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
- Lightning.Protocol.BOLT3.Scripts: htlc_output_witness_revoke :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: htlc_output_witness_spend :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: offered_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> ChannelFeatures -> Script
- Lightning.Protocol.BOLT3.Scripts: offered_htlc_witness_preimage :: ByteString -> PaymentPreimage -> Witness
- Lightning.Protocol.BOLT3.Scripts: offered_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
- Lightning.Protocol.BOLT3.Scripts: received_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> CltvExpiry -> ChannelFeatures -> Script
- Lightning.Protocol.BOLT3.Scripts: received_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
- Lightning.Protocol.BOLT3.Scripts: received_htlc_witness_timeout :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: to_local_script :: RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
- Lightning.Protocol.BOLT3.Scripts: to_local_witness_revoke :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: to_local_witness_spend :: ByteString -> Witness
- Lightning.Protocol.BOLT3.Scripts: to_p2wsh :: Script -> Script
- Lightning.Protocol.BOLT3.Scripts: to_remote_script :: RemotePubkey -> ChannelFeatures -> Script
- Lightning.Protocol.BOLT3.Scripts: to_remote_witness :: ByteString -> RemotePubkey -> ChannelFeatures -> Witness
- Lightning.Protocol.BOLT3.Scripts: witness_script_hash :: Script -> ByteString
- Lightning.Protocol.BOLT3.Tx: ClosingContext :: !Outpoint -> !Satoshi -> !Satoshi -> !Script -> !Script -> !DustLimit -> !DustLimit -> !Satoshi -> !Bool -> !Locktime -> !Script -> ClosingContext
- Lightning.Protocol.BOLT3.Tx: ClosingTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> ![TxOutput] -> !Script -> ClosingTx
- Lightning.Protocol.BOLT3.Tx: CommitmentContext :: !Outpoint -> !CommitmentNumber -> !PaymentBasepoint -> !PaymentBasepoint -> !ToSelfDelay -> !DustLimit -> !FeeratePerKw -> !ChannelFeatures -> !Bool -> !MilliSatoshi -> !MilliSatoshi -> ![HTLC] -> !CommitmentKeys -> CommitmentContext
- Lightning.Protocol.BOLT3.Tx: CommitmentKeys :: !RevocationPubkey -> !LocalDelayedPubkey -> !LocalHtlcPubkey -> !RemoteHtlcPubkey -> !LocalPubkey -> !RemotePubkey -> !FundingPubkey -> !FundingPubkey -> CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: CommitmentTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> ![TxOutput] -> !Script -> CommitmentTx
- Lightning.Protocol.BOLT3.Tx: HTLCContext :: !TxId -> {-# UNPACK #-} !Word32 -> !HTLC -> !ToSelfDelay -> !FeeratePerKw -> !ChannelFeatures -> !RevocationPubkey -> !LocalDelayedPubkey -> HTLCContext
- Lightning.Protocol.BOLT3.Tx: HTLCTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> !Satoshi -> !Script -> HTLCTx
- Lightning.Protocol.BOLT3.Tx: OutputLocalAnchor :: OutputType
- Lightning.Protocol.BOLT3.Tx: OutputOfferedHTLC :: {-# UNPACK #-} !CltvExpiry -> OutputType
- Lightning.Protocol.BOLT3.Tx: OutputReceivedHTLC :: {-# UNPACK #-} !CltvExpiry -> OutputType
- Lightning.Protocol.BOLT3.Tx: OutputRemoteAnchor :: OutputType
- Lightning.Protocol.BOLT3.Tx: OutputToLocal :: OutputType
- Lightning.Protocol.BOLT3.Tx: OutputToRemote :: OutputType
- Lightning.Protocol.BOLT3.Tx: TxOutput :: {-# UNPACK #-} !Satoshi -> !Script -> !OutputType -> TxOutput
- Lightning.Protocol.BOLT3.Tx: [cc_commitment_number] :: CommitmentContext -> !CommitmentNumber
- Lightning.Protocol.BOLT3.Tx: [cc_dust_limit] :: CommitmentContext -> !DustLimit
- Lightning.Protocol.BOLT3.Tx: [cc_features] :: CommitmentContext -> !ChannelFeatures
- Lightning.Protocol.BOLT3.Tx: [cc_feerate] :: CommitmentContext -> !FeeratePerKw
- Lightning.Protocol.BOLT3.Tx: [cc_funding_outpoint] :: CommitmentContext -> !Outpoint
- Lightning.Protocol.BOLT3.Tx: [cc_htlcs] :: CommitmentContext -> ![HTLC]
- Lightning.Protocol.BOLT3.Tx: [cc_is_funder] :: CommitmentContext -> !Bool
- Lightning.Protocol.BOLT3.Tx: [cc_keys] :: CommitmentContext -> !CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: [cc_local_payment_bp] :: CommitmentContext -> !PaymentBasepoint
- Lightning.Protocol.BOLT3.Tx: [cc_remote_payment_bp] :: CommitmentContext -> !PaymentBasepoint
- Lightning.Protocol.BOLT3.Tx: [cc_to_local_msat] :: CommitmentContext -> !MilliSatoshi
- Lightning.Protocol.BOLT3.Tx: [cc_to_remote_msat] :: CommitmentContext -> !MilliSatoshi
- Lightning.Protocol.BOLT3.Tx: [cc_to_self_delay] :: CommitmentContext -> !ToSelfDelay
- Lightning.Protocol.BOLT3.Tx: [ck_local_delayed] :: CommitmentKeys -> !LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_local_funding] :: CommitmentKeys -> !FundingPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_local_htlc] :: CommitmentKeys -> !LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_local_payment] :: CommitmentKeys -> !LocalPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_remote_funding] :: CommitmentKeys -> !FundingPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_remote_htlc] :: CommitmentKeys -> !RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Tx: [ck_remote_payment] :: CommitmentKeys -> !RemotePubkey
- Lightning.Protocol.BOLT3.Tx: [ck_revocation_pubkey] :: CommitmentKeys -> !RevocationPubkey
- Lightning.Protocol.BOLT3.Tx: [clc_fee] :: ClosingContext -> !Satoshi
- Lightning.Protocol.BOLT3.Tx: [clc_funding_outpoint] :: ClosingContext -> !Outpoint
- Lightning.Protocol.BOLT3.Tx: [clc_funding_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3.Tx: [clc_is_funder] :: ClosingContext -> !Bool
- Lightning.Protocol.BOLT3.Tx: [clc_local_amount] :: ClosingContext -> !Satoshi
- Lightning.Protocol.BOLT3.Tx: [clc_local_dust_limit] :: ClosingContext -> !DustLimit
- Lightning.Protocol.BOLT3.Tx: [clc_local_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3.Tx: [clc_locktime] :: ClosingContext -> !Locktime
- Lightning.Protocol.BOLT3.Tx: [clc_remote_amount] :: ClosingContext -> !Satoshi
- Lightning.Protocol.BOLT3.Tx: [clc_remote_dust_limit] :: ClosingContext -> !DustLimit
- Lightning.Protocol.BOLT3.Tx: [clc_remote_script] :: ClosingContext -> !Script
- Lightning.Protocol.BOLT3.Tx: [cltx_funding_script] :: ClosingTx -> !Script
- Lightning.Protocol.BOLT3.Tx: [cltx_input_outpoint] :: ClosingTx -> !Outpoint
- Lightning.Protocol.BOLT3.Tx: [cltx_input_sequence] :: ClosingTx -> !Sequence
- Lightning.Protocol.BOLT3.Tx: [cltx_locktime] :: ClosingTx -> !Locktime
- Lightning.Protocol.BOLT3.Tx: [cltx_outputs] :: ClosingTx -> ![TxOutput]
- Lightning.Protocol.BOLT3.Tx: [cltx_version] :: ClosingTx -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Tx: [ctx_funding_script] :: CommitmentTx -> !Script
- Lightning.Protocol.BOLT3.Tx: [ctx_input_outpoint] :: CommitmentTx -> !Outpoint
- Lightning.Protocol.BOLT3.Tx: [ctx_input_sequence] :: CommitmentTx -> !Sequence
- Lightning.Protocol.BOLT3.Tx: [ctx_locktime] :: CommitmentTx -> !Locktime
- Lightning.Protocol.BOLT3.Tx: [ctx_outputs] :: CommitmentTx -> ![TxOutput]
- Lightning.Protocol.BOLT3.Tx: [ctx_version] :: CommitmentTx -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Tx: [hc_commitment_txid] :: HTLCContext -> !TxId
- Lightning.Protocol.BOLT3.Tx: [hc_features] :: HTLCContext -> !ChannelFeatures
- Lightning.Protocol.BOLT3.Tx: [hc_feerate] :: HTLCContext -> !FeeratePerKw
- Lightning.Protocol.BOLT3.Tx: [hc_htlc] :: HTLCContext -> !HTLC
- Lightning.Protocol.BOLT3.Tx: [hc_local_delayed] :: HTLCContext -> !LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Tx: [hc_output_index] :: HTLCContext -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Tx: [hc_revocation_pubkey] :: HTLCContext -> !RevocationPubkey
- Lightning.Protocol.BOLT3.Tx: [hc_to_self_delay] :: HTLCContext -> !ToSelfDelay
- Lightning.Protocol.BOLT3.Tx: [htx_input_outpoint] :: HTLCTx -> !Outpoint
- Lightning.Protocol.BOLT3.Tx: [htx_input_sequence] :: HTLCTx -> !Sequence
- Lightning.Protocol.BOLT3.Tx: [htx_locktime] :: HTLCTx -> !Locktime
- Lightning.Protocol.BOLT3.Tx: [htx_output_script] :: HTLCTx -> !Script
- Lightning.Protocol.BOLT3.Tx: [htx_output_value] :: HTLCTx -> !Satoshi
- Lightning.Protocol.BOLT3.Tx: [htx_version] :: HTLCTx -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Tx: [txout_script] :: TxOutput -> !Script
- Lightning.Protocol.BOLT3.Tx: [txout_type] :: TxOutput -> !OutputType
- Lightning.Protocol.BOLT3.Tx: [txout_value] :: TxOutput -> {-# UNPACK #-} !Satoshi
- Lightning.Protocol.BOLT3.Tx: build_closing_tx :: ClosingContext -> ClosingTx
- Lightning.Protocol.BOLT3.Tx: build_commitment_tx :: CommitmentContext -> CommitmentTx
- Lightning.Protocol.BOLT3.Tx: build_htlc_success_tx :: HTLCContext -> HTLCTx
- Lightning.Protocol.BOLT3.Tx: build_htlc_timeout_tx :: HTLCContext -> HTLCTx
- Lightning.Protocol.BOLT3.Tx: build_legacy_closing_tx :: ClosingContext -> ClosingTx
- Lightning.Protocol.BOLT3.Tx: commitment_fee :: FeeratePerKw -> ChannelFeatures -> Word64 -> Satoshi
- Lightning.Protocol.BOLT3.Tx: commitment_weight :: ChannelFeatures -> Word64 -> Word64
- Lightning.Protocol.BOLT3.Tx: data ClosingContext
- Lightning.Protocol.BOLT3.Tx: data ClosingTx
- Lightning.Protocol.BOLT3.Tx: data CommitmentContext
- Lightning.Protocol.BOLT3.Tx: data CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: data CommitmentTx
- Lightning.Protocol.BOLT3.Tx: data HTLCContext
- Lightning.Protocol.BOLT3.Tx: data HTLCTx
- Lightning.Protocol.BOLT3.Tx: data OutputType
- Lightning.Protocol.BOLT3.Tx: data TxOutput
- Lightning.Protocol.BOLT3.Tx: htlc_success_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi
- Lightning.Protocol.BOLT3.Tx: htlc_timeout_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi
- Lightning.Protocol.BOLT3.Tx: htlc_trim_threshold :: DustLimit -> FeeratePerKw -> ChannelFeatures -> HTLCDirection -> Satoshi
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.ClosingContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.ClosingTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.CommitmentContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.CommitmentTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.HTLCContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.HTLCTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.OutputType
- Lightning.Protocol.BOLT3.Tx: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Tx.TxOutput
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.ClosingContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.ClosingTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.CommitmentContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.CommitmentTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.HTLCContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.HTLCTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.OutputType
- Lightning.Protocol.BOLT3.Tx: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Tx.TxOutput
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.ClosingContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.ClosingTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.CommitmentContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.CommitmentKeys
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.CommitmentTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.HTLCContext
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.HTLCTx
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.OutputType
- Lightning.Protocol.BOLT3.Tx: instance GHC.Show.Show Lightning.Protocol.BOLT3.Tx.TxOutput
- Lightning.Protocol.BOLT3.Tx: is_trimmed :: DustLimit -> FeeratePerKw -> ChannelFeatures -> HTLC -> Bool
- Lightning.Protocol.BOLT3.Tx: sort_outputs :: [TxOutput] -> [TxOutput]
- Lightning.Protocol.BOLT3.Tx: trimmed_htlcs :: DustLimit -> FeeratePerKw -> ChannelFeatures -> [HTLC] -> [HTLC]
- Lightning.Protocol.BOLT3.Tx: untrimmed_htlcs :: DustLimit -> FeeratePerKw -> ChannelFeatures -> [HTLC] -> [HTLC]
- Lightning.Protocol.BOLT3.Types: Basepoints :: !RevocationBasepoint -> !PaymentBasepoint -> !DelayedPaymentBasepoint -> !HtlcBasepoint -> Basepoints
- Lightning.Protocol.BOLT3.Types: ChannelFeatures :: !Bool -> ChannelFeatures
- Lightning.Protocol.BOLT3.Types: CltvExpiry :: Word32 -> CltvExpiry
- Lightning.Protocol.BOLT3.Types: CommitmentNumber :: Word64 -> CommitmentNumber
- Lightning.Protocol.BOLT3.Types: DelayedPaymentBasepoint :: Point -> DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: DustLimit :: Satoshi -> DustLimit
- Lightning.Protocol.BOLT3.Types: FeeratePerKw :: Word32 -> FeeratePerKw
- Lightning.Protocol.BOLT3.Types: FundingPubkey :: Pubkey -> FundingPubkey
- Lightning.Protocol.BOLT3.Types: HTLC :: !HTLCDirection -> {-# UNPACK #-} !MilliSatoshi -> {-# UNPACK #-} !PaymentHash -> {-# UNPACK #-} !CltvExpiry -> HTLC
- Lightning.Protocol.BOLT3.Types: HTLCOffered :: HTLCDirection
- Lightning.Protocol.BOLT3.Types: HTLCReceived :: HTLCDirection
- Lightning.Protocol.BOLT3.Types: HtlcBasepoint :: Point -> HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: LocalDelayedPubkey :: Pubkey -> LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: LocalHtlcPubkey :: Pubkey -> LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: LocalPubkey :: Pubkey -> LocalPubkey
- Lightning.Protocol.BOLT3.Types: Locktime :: Word32 -> Locktime
- Lightning.Protocol.BOLT3.Types: MilliSatoshi :: Word64 -> MilliSatoshi
- Lightning.Protocol.BOLT3.Types: Outpoint :: {-# UNPACK #-} !TxId -> {-# UNPACK #-} !Word32 -> Outpoint
- Lightning.Protocol.BOLT3.Types: PaymentBasepoint :: Point -> PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: PaymentHash :: ByteString -> PaymentHash
- Lightning.Protocol.BOLT3.Types: PaymentPreimage :: ByteString -> PaymentPreimage
- Lightning.Protocol.BOLT3.Types: PerCommitmentPoint :: Point -> PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: PerCommitmentSecret :: ByteString -> PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: Point :: ByteString -> Point
- Lightning.Protocol.BOLT3.Types: Pubkey :: ByteString -> Pubkey
- Lightning.Protocol.BOLT3.Types: RemoteDelayedPubkey :: Pubkey -> RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: RemoteHtlcPubkey :: Pubkey -> RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: RemotePubkey :: Pubkey -> RemotePubkey
- Lightning.Protocol.BOLT3.Types: RevocationBasepoint :: Point -> RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: RevocationPubkey :: Pubkey -> RevocationPubkey
- Lightning.Protocol.BOLT3.Types: Satoshi :: Word64 -> Satoshi
- Lightning.Protocol.BOLT3.Types: Script :: ByteString -> Script
- Lightning.Protocol.BOLT3.Types: Seckey :: ByteString -> Seckey
- Lightning.Protocol.BOLT3.Types: Sequence :: Word32 -> Sequence
- Lightning.Protocol.BOLT3.Types: ToSelfDelay :: Word16 -> ToSelfDelay
- Lightning.Protocol.BOLT3.Types: TxId :: ByteString -> TxId
- Lightning.Protocol.BOLT3.Types: Witness :: [ByteString] -> Witness
- Lightning.Protocol.BOLT3.Types: [bp_delayed_payment] :: Basepoints -> !DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: [bp_htlc] :: Basepoints -> !HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: [bp_payment] :: Basepoints -> !PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: [bp_revocation] :: Basepoints -> !RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: [cf_option_anchors] :: ChannelFeatures -> !Bool
- Lightning.Protocol.BOLT3.Types: [htlc_amount_msat] :: HTLC -> {-# UNPACK #-} !MilliSatoshi
- Lightning.Protocol.BOLT3.Types: [htlc_cltv_expiry] :: HTLC -> {-# UNPACK #-} !CltvExpiry
- Lightning.Protocol.BOLT3.Types: [htlc_direction] :: HTLC -> !HTLCDirection
- Lightning.Protocol.BOLT3.Types: [htlc_payment_hash] :: HTLC -> {-# UNPACK #-} !PaymentHash
- Lightning.Protocol.BOLT3.Types: [outpoint_index] :: Outpoint -> {-# UNPACK #-} !Word32
- Lightning.Protocol.BOLT3.Types: [outpoint_txid] :: Outpoint -> {-# UNPACK #-} !TxId
- Lightning.Protocol.BOLT3.Types: [unCltvExpiry] :: CltvExpiry -> Word32
- Lightning.Protocol.BOLT3.Types: [unCommitmentNumber] :: CommitmentNumber -> Word64
- Lightning.Protocol.BOLT3.Types: [unDelayedPaymentBasepoint] :: DelayedPaymentBasepoint -> Point
- Lightning.Protocol.BOLT3.Types: [unDustLimit] :: DustLimit -> Satoshi
- Lightning.Protocol.BOLT3.Types: [unFeeratePerKw] :: FeeratePerKw -> Word32
- Lightning.Protocol.BOLT3.Types: [unFundingPubkey] :: FundingPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unHtlcBasepoint] :: HtlcBasepoint -> Point
- Lightning.Protocol.BOLT3.Types: [unLocalDelayedPubkey] :: LocalDelayedPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unLocalHtlcPubkey] :: LocalHtlcPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unLocalPubkey] :: LocalPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unLocktime] :: Locktime -> Word32
- Lightning.Protocol.BOLT3.Types: [unMilliSatoshi] :: MilliSatoshi -> Word64
- Lightning.Protocol.BOLT3.Types: [unPaymentBasepoint] :: PaymentBasepoint -> Point
- Lightning.Protocol.BOLT3.Types: [unPaymentHash] :: PaymentHash -> ByteString
- Lightning.Protocol.BOLT3.Types: [unPaymentPreimage] :: PaymentPreimage -> ByteString
- Lightning.Protocol.BOLT3.Types: [unPerCommitmentPoint] :: PerCommitmentPoint -> Point
- Lightning.Protocol.BOLT3.Types: [unPerCommitmentSecret] :: PerCommitmentSecret -> ByteString
- Lightning.Protocol.BOLT3.Types: [unPoint] :: Point -> ByteString
- Lightning.Protocol.BOLT3.Types: [unPubkey] :: Pubkey -> ByteString
- Lightning.Protocol.BOLT3.Types: [unRemoteDelayedPubkey] :: RemoteDelayedPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unRemoteHtlcPubkey] :: RemoteHtlcPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unRemotePubkey] :: RemotePubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unRevocationBasepoint] :: RevocationBasepoint -> Point
- Lightning.Protocol.BOLT3.Types: [unRevocationPubkey] :: RevocationPubkey -> Pubkey
- Lightning.Protocol.BOLT3.Types: [unSatoshi] :: Satoshi -> Word64
- Lightning.Protocol.BOLT3.Types: [unScript] :: Script -> ByteString
- Lightning.Protocol.BOLT3.Types: [unSeckey] :: Seckey -> ByteString
- Lightning.Protocol.BOLT3.Types: [unSequence] :: Sequence -> Word32
- Lightning.Protocol.BOLT3.Types: [unToSelfDelay] :: ToSelfDelay -> Word16
- Lightning.Protocol.BOLT3.Types: [unTxId] :: TxId -> ByteString
- Lightning.Protocol.BOLT3.Types: [unWitness] :: Witness -> [ByteString]
- Lightning.Protocol.BOLT3.Types: anchor_output_value :: Satoshi
- Lightning.Protocol.BOLT3.Types: commitment_number :: Word64 -> Maybe CommitmentNumber
- Lightning.Protocol.BOLT3.Types: commitment_weight_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: commitment_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: data Basepoints
- Lightning.Protocol.BOLT3.Types: data ChannelFeatures
- Lightning.Protocol.BOLT3.Types: data HTLC
- Lightning.Protocol.BOLT3.Types: data HTLCDirection
- Lightning.Protocol.BOLT3.Types: data Outpoint
- Lightning.Protocol.BOLT3.Types: dust_p2pkh :: Satoshi
- Lightning.Protocol.BOLT3.Types: dust_p2sh :: Satoshi
- Lightning.Protocol.BOLT3.Types: dust_p2wpkh :: Satoshi
- Lightning.Protocol.BOLT3.Types: dust_p2wsh :: Satoshi
- Lightning.Protocol.BOLT3.Types: has_anchors :: ChannelFeatures -> Bool
- Lightning.Protocol.BOLT3.Types: htlc_output_weight :: Word64
- Lightning.Protocol.BOLT3.Types: htlc_success_weight_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: htlc_success_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: htlc_timeout_weight_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: htlc_timeout_weight_no_anchors :: Word64
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Basepoints
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.ChannelFeatures
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.CltvExpiry
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.CommitmentNumber
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.DustLimit
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.FeeratePerKw
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.FundingPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.HTLC
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.HTLCDirection
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.LocalPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Locktime
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.MilliSatoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Outpoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.PaymentHash
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.PaymentPreimage
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Point
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Pubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.RemotePubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.RevocationPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Satoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Script
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Seckey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Sequence
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.ToSelfDelay
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.TxId
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Types.Witness
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.CltvExpiry
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.CommitmentNumber
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.DustLimit
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.FeeratePerKw
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.FundingPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.HTLCDirection
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.LocalPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Locktime
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.MilliSatoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Outpoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.PaymentHash
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Point
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Pubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.RemotePubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.RevocationPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Satoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Script
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Sequence
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.ToSelfDelay
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.TxId
- Lightning.Protocol.BOLT3.Types: instance GHC.Classes.Ord Lightning.Protocol.BOLT3.Types.Witness
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Basepoints
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.ChannelFeatures
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.CltvExpiry
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.CommitmentNumber
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.DustLimit
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.FeeratePerKw
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.FundingPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.HTLC
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.HTLCDirection
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.LocalPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Locktime
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.MilliSatoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Outpoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.PaymentHash
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.PaymentPreimage
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Point
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Pubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.RemotePubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.RevocationPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Satoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Script
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Seckey
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Sequence
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.ToSelfDelay
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.TxId
- Lightning.Protocol.BOLT3.Types: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Types.Witness
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.CltvExpiry
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.CommitmentNumber
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.FeeratePerKw
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.Locktime
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.MilliSatoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.Satoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.Sequence
- Lightning.Protocol.BOLT3.Types: instance GHC.Num.Num Lightning.Protocol.BOLT3.Types.ToSelfDelay
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Basepoints
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.ChannelFeatures
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.CltvExpiry
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.CommitmentNumber
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.DustLimit
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.FeeratePerKw
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.FundingPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.HTLC
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.HTLCDirection
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.LocalPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Locktime
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.MilliSatoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Outpoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.PaymentHash
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.PaymentPreimage
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Point
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Pubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.RemotePubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.RevocationPubkey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Satoshi
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Script
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Seckey
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Sequence
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.ToSelfDelay
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.TxId
- Lightning.Protocol.BOLT3.Types: instance GHC.Show.Show Lightning.Protocol.BOLT3.Types.Witness
- Lightning.Protocol.BOLT3.Types: msat_to_sat :: MilliSatoshi -> Satoshi
- Lightning.Protocol.BOLT3.Types: newtype CltvExpiry
- Lightning.Protocol.BOLT3.Types: newtype CommitmentNumber
- Lightning.Protocol.BOLT3.Types: newtype DelayedPaymentBasepoint
- Lightning.Protocol.BOLT3.Types: newtype DustLimit
- Lightning.Protocol.BOLT3.Types: newtype FeeratePerKw
- Lightning.Protocol.BOLT3.Types: newtype FundingPubkey
- Lightning.Protocol.BOLT3.Types: newtype HtlcBasepoint
- Lightning.Protocol.BOLT3.Types: newtype LocalDelayedPubkey
- Lightning.Protocol.BOLT3.Types: newtype LocalHtlcPubkey
- Lightning.Protocol.BOLT3.Types: newtype LocalPubkey
- Lightning.Protocol.BOLT3.Types: newtype Locktime
- Lightning.Protocol.BOLT3.Types: newtype MilliSatoshi
- Lightning.Protocol.BOLT3.Types: newtype PaymentBasepoint
- Lightning.Protocol.BOLT3.Types: newtype PaymentHash
- Lightning.Protocol.BOLT3.Types: newtype PaymentPreimage
- Lightning.Protocol.BOLT3.Types: newtype PerCommitmentPoint
- Lightning.Protocol.BOLT3.Types: newtype PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: newtype Point
- Lightning.Protocol.BOLT3.Types: newtype Pubkey
- Lightning.Protocol.BOLT3.Types: newtype RemoteDelayedPubkey
- Lightning.Protocol.BOLT3.Types: newtype RemoteHtlcPubkey
- Lightning.Protocol.BOLT3.Types: newtype RemotePubkey
- Lightning.Protocol.BOLT3.Types: newtype RevocationBasepoint
- Lightning.Protocol.BOLT3.Types: newtype RevocationPubkey
- Lightning.Protocol.BOLT3.Types: newtype Satoshi
- Lightning.Protocol.BOLT3.Types: newtype Script
- Lightning.Protocol.BOLT3.Types: newtype Seckey
- Lightning.Protocol.BOLT3.Types: newtype Sequence
- Lightning.Protocol.BOLT3.Types: newtype ToSelfDelay
- Lightning.Protocol.BOLT3.Types: newtype TxId
- Lightning.Protocol.BOLT3.Types: newtype Witness
- Lightning.Protocol.BOLT3.Types: payment_hash :: ByteString -> Maybe PaymentHash
- Lightning.Protocol.BOLT3.Types: payment_preimage :: ByteString -> Maybe PaymentPreimage
- Lightning.Protocol.BOLT3.Types: per_commitment_secret :: ByteString -> Maybe PerCommitmentSecret
- Lightning.Protocol.BOLT3.Types: point :: ByteString -> Maybe Point
- Lightning.Protocol.BOLT3.Types: pubkey :: ByteString -> Maybe Pubkey
- Lightning.Protocol.BOLT3.Types: sat_to_msat :: Satoshi -> MilliSatoshi
- Lightning.Protocol.BOLT3.Types: seckey :: ByteString -> Maybe Seckey
- Lightning.Protocol.BOLT3.Types: txid :: ByteString -> Maybe TxId
- Lightning.Protocol.BOLT3.Validate: DustLimitViolation :: {-# UNPACK #-} !Int -> !Satoshi -> !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidAnchorValue :: {-# UNPACK #-} !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidFee :: {-# UNPACK #-} !Satoshi -> {-# UNPACK #-} !Satoshi -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidHTLCLocktime :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidHTLCSequence :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidLocktime :: {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidOutputOrdering :: ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidSequence :: {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3.Validate: InvalidVersion :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Word32 -> ValidationError
- Lightning.Protocol.BOLT3.Validate: MissingAnchorOutput :: ValidationError
- Lightning.Protocol.BOLT3.Validate: NoOutputs :: ValidationError
- Lightning.Protocol.BOLT3.Validate: TooManyOutputs :: {-# UNPACK #-} !Int -> ValidationError
- Lightning.Protocol.BOLT3.Validate: data ValidationError
- Lightning.Protocol.BOLT3.Validate: instance GHC.Classes.Eq Lightning.Protocol.BOLT3.Validate.ValidationError
- Lightning.Protocol.BOLT3.Validate: instance GHC.Generics.Generic Lightning.Protocol.BOLT3.Validate.ValidationError
- Lightning.Protocol.BOLT3.Validate: instance GHC.Show.Show Lightning.Protocol.BOLT3.Validate.ValidationError
- Lightning.Protocol.BOLT3.Validate: validate_anchor_outputs :: [TxOutput] -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_closing_tx :: ClosingTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_commitment_fee :: FeeratePerKw -> ChannelFeatures -> Word64 -> Satoshi -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_commitment_locktime :: Locktime -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_commitment_sequence :: Sequence -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_commitment_tx :: DustLimit -> ChannelFeatures -> CommitmentTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_dust_limits :: DustLimit -> [TxOutput] -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_htlc_fee :: FeeratePerKw -> ChannelFeatures -> HTLCDirection -> Satoshi -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_htlc_success_tx :: ChannelFeatures -> HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_htlc_timeout_tx :: ChannelFeatures -> CltvExpiry -> HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_htlc_tx :: HTLCTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_legacy_closing_tx :: ClosingTx -> Either ValidationError ()
- Lightning.Protocol.BOLT3.Validate: validate_output_ordering :: [TxOutput] -> Either ValidationError ()
+ Lightning.Protocol.BOLT3: Anchors :: CommitmentFormat
+ Lightning.Protocol.BOLT3: CloseeOutputOnly :: ClosingOutputs
+ Lightning.Protocol.BOLT3: CloserAndCloseeOutputs :: ClosingOutputs
+ Lightning.Protocol.BOLT3: CloserOutputOnly :: ClosingOutputs
+ Lightning.Protocol.BOLT3: ClosingOutput :: {-# UNPACK #-} !Satoshi -> !Script -> ClosingOutput
+ Lightning.Protocol.BOLT3: CommitmentOutput :: {-# UNPACK #-} !Satoshi -> !Script -> !OutputType -> CommitmentOutput
+ Lightning.Protocol.BOLT3: LegacyClosingContext :: {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !MilliSatoshi -> {-# UNPACK #-} !MilliSatoshi -> !Script -> !Script -> {-# UNPACK #-} !DustLimit -> {-# UNPACK #-} !Satoshi -> !Bool -> !Bool -> LegacyClosingContext
+ Lightning.Protocol.BOLT3: OutputHTLC :: !HTLC -> OutputType
+ Lightning.Protocol.BOLT3: StaticRemotekey :: CommitmentFormat
+ Lightning.Protocol.BOLT3: [cc_format] :: CommitmentContext -> !CommitmentFormat
+ Lightning.Protocol.BOLT3: [clc_closee_msat] :: ClosingContext -> {-# UNPACK #-} !MilliSatoshi
+ Lightning.Protocol.BOLT3: [clc_closee_script] :: ClosingContext -> !Script
+ Lightning.Protocol.BOLT3: [clc_closer_msat] :: ClosingContext -> {-# UNPACK #-} !MilliSatoshi
+ Lightning.Protocol.BOLT3: [clc_closer_script] :: ClosingContext -> !Script
+ Lightning.Protocol.BOLT3: [clc_outputs] :: ClosingContext -> !ClosingOutputs
+ Lightning.Protocol.BOLT3: [clo_script] :: ClosingOutput -> !Script
+ Lightning.Protocol.BOLT3: [clo_value] :: ClosingOutput -> {-# UNPACK #-} !Satoshi
+ Lightning.Protocol.BOLT3: [co_script] :: CommitmentOutput -> !Script
+ Lightning.Protocol.BOLT3: [co_type] :: CommitmentOutput -> !OutputType
+ Lightning.Protocol.BOLT3: [co_value] :: CommitmentOutput -> {-# UNPACK #-} !Satoshi
+ Lightning.Protocol.BOLT3: [hc_format] :: HTLCContext -> !CommitmentFormat
+ Lightning.Protocol.BOLT3: [lcc_dust_limit] :: LegacyClosingContext -> {-# UNPACK #-} !DustLimit
+ Lightning.Protocol.BOLT3: [lcc_fee] :: LegacyClosingContext -> {-# UNPACK #-} !Satoshi
+ Lightning.Protocol.BOLT3: [lcc_funding_outpoint] :: LegacyClosingContext -> {-# UNPACK #-} !OutPoint
+ Lightning.Protocol.BOLT3: [lcc_is_funder] :: LegacyClosingContext -> !Bool
+ Lightning.Protocol.BOLT3: [lcc_local_msat] :: LegacyClosingContext -> {-# UNPACK #-} !MilliSatoshi
+ Lightning.Protocol.BOLT3: [lcc_local_script] :: LegacyClosingContext -> !Script
+ Lightning.Protocol.BOLT3: [lcc_omit_local] :: LegacyClosingContext -> !Bool
+ Lightning.Protocol.BOLT3: [lcc_remote_msat] :: LegacyClosingContext -> {-# UNPACK #-} !MilliSatoshi
+ Lightning.Protocol.BOLT3: [lcc_remote_script] :: LegacyClosingContext -> !Script
+ Lightning.Protocol.BOLT3: build_htlc_tx :: HTLCContext -> Maybe HTLCTx
+ Lightning.Protocol.BOLT3: closing_to_tx :: ClosingTx -> Tx
+ Lightning.Protocol.BOLT3: commitment_secret_index :: CommitmentNumber -> SecretIndex
+ Lightning.Protocol.BOLT3: commitment_to_tx :: CommitmentTx -> Tx
+ Lightning.Protocol.BOLT3: data ClosingOutput
+ Lightning.Protocol.BOLT3: data ClosingOutputs
+ Lightning.Protocol.BOLT3: data CommitmentFormat
+ Lightning.Protocol.BOLT3: data CommitmentNumber
+ Lightning.Protocol.BOLT3: data CommitmentOutput
+ Lightning.Protocol.BOLT3: data LegacyClosingContext
+ Lightning.Protocol.BOLT3: data Seckey
+ Lightning.Protocol.BOLT3: data SecretIndex
+ Lightning.Protocol.BOLT3: data Seed
+ Lightning.Protocol.BOLT3: derive_commitment_keys :: Basepoints -> FundingPubkey -> Basepoints -> FundingPubkey -> PerCommitmentPoint -> Maybe CommitmentKeys
+ Lightning.Protocol.BOLT3: derive_commitment_keys' :: Context -> Basepoints -> FundingPubkey -> Basepoints -> FundingPubkey -> PerCommitmentPoint -> Maybe CommitmentKeys
+ Lightning.Protocol.BOLT3: derive_per_commitment_point' :: Context -> PerCommitmentSecret -> Maybe PerCommitmentPoint
+ Lightning.Protocol.BOLT3: derive_privkey :: Seckey -> PerCommitmentPoint -> Maybe Seckey
+ Lightning.Protocol.BOLT3: derive_revocationprivkey :: Seckey -> PerCommitmentSecret -> Maybe Seckey
+ Lightning.Protocol.BOLT3: empty_secret_store :: SecretStore
+ Lightning.Protocol.BOLT3: encode_commitment_tx :: CommitmentTx -> ByteString
+ Lightning.Protocol.BOLT3: htlc_success_witness :: ByteString -> ByteString -> PaymentPreimage -> Script -> Witness
+ Lightning.Protocol.BOLT3: htlc_timeout_witness :: ByteString -> ByteString -> Script -> Witness
+ Lightning.Protocol.BOLT3: htlc_to_tx :: HTLCTx -> Tx
+ Lightning.Protocol.BOLT3: next_commitment_number :: CommitmentNumber -> Maybe CommitmentNumber
+ Lightning.Protocol.BOLT3: remote_htlc_sighash :: CommitmentFormat -> SighashType
+ Lightning.Protocol.BOLT3: secret_index :: Word64 -> Maybe SecretIndex
+ Lightning.Protocol.BOLT3: secret_store :: [(SecretIndex, PerCommitmentSecret)] -> Maybe SecretStore
+ Lightning.Protocol.BOLT3: seed :: ByteString -> Maybe Seed
+ Lightning.Protocol.BOLT3: to_remote_script_pubkey :: RemotePubkey -> CommitmentFormat -> Script
+ Lightning.Protocol.BOLT3: to_remote_witness_script :: RemotePubkey -> Script
+ Lightning.Protocol.BOLT3: un_commitment_number :: CommitmentNumber -> Word64
+ Lightning.Protocol.BOLT3: un_seckey :: Seckey -> ByteString
+ Lightning.Protocol.BOLT3: un_secret_index :: SecretIndex -> Word64
+ Lightning.Protocol.BOLT3: un_secret_store :: SecretStore -> [(SecretIndex, PerCommitmentSecret)]
+ Lightning.Protocol.BOLT3: un_seed :: Seed -> ByteString
- Lightning.Protocol.BOLT3: ClosingContext :: !Outpoint -> !Satoshi -> !Satoshi -> !Script -> !Script -> !DustLimit -> !DustLimit -> !Satoshi -> !Bool -> !Locktime -> !Script -> ClosingContext
+ Lightning.Protocol.BOLT3: ClosingContext :: {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !MilliSatoshi -> {-# UNPACK #-} !MilliSatoshi -> !Script -> !Script -> {-# UNPACK #-} !Satoshi -> {-# UNPACK #-} !Locktime -> !ClosingOutputs -> ClosingContext
- Lightning.Protocol.BOLT3: ClosingTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> ![TxOutput] -> !Script -> ClosingTx
+ Lightning.Protocol.BOLT3: ClosingTx :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Locktime -> {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !Sequence -> !NonEmpty ClosingOutput -> ClosingTx
- Lightning.Protocol.BOLT3: CommitmentContext :: !Outpoint -> !CommitmentNumber -> !PaymentBasepoint -> !PaymentBasepoint -> !ToSelfDelay -> !DustLimit -> !FeeratePerKw -> !ChannelFeatures -> !Bool -> !MilliSatoshi -> !MilliSatoshi -> ![HTLC] -> !CommitmentKeys -> CommitmentContext
+ Lightning.Protocol.BOLT3: CommitmentContext :: {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !CommitmentNumber -> !PaymentBasepoint -> !PaymentBasepoint -> {-# UNPACK #-} !ToSelfDelay -> {-# UNPACK #-} !DustLimit -> {-# UNPACK #-} !FeeratePerKw -> !CommitmentFormat -> !Bool -> {-# UNPACK #-} !MilliSatoshi -> {-# UNPACK #-} !MilliSatoshi -> ![HTLC] -> !CommitmentKeys -> CommitmentContext
- Lightning.Protocol.BOLT3: CommitmentKeys :: !RevocationPubkey -> !LocalDelayedPubkey -> !LocalHtlcPubkey -> !RemoteHtlcPubkey -> !LocalPubkey -> !RemotePubkey -> !FundingPubkey -> !FundingPubkey -> CommitmentKeys
+ Lightning.Protocol.BOLT3: CommitmentKeys :: !RevocationPubkey -> !LocalDelayedPubkey -> !LocalHtlcPubkey -> !RemoteHtlcPubkey -> !RemotePubkey -> !FundingPubkey -> !FundingPubkey -> CommitmentKeys
- Lightning.Protocol.BOLT3: CommitmentTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> ![TxOutput] -> !Script -> CommitmentTx
+ Lightning.Protocol.BOLT3: CommitmentTx :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Locktime -> {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !Sequence -> !NonEmpty CommitmentOutput -> !Script -> CommitmentTx
- Lightning.Protocol.BOLT3: FundingPubkey :: Pubkey -> FundingPubkey
+ Lightning.Protocol.BOLT3: FundingPubkey :: Point -> FundingPubkey
- Lightning.Protocol.BOLT3: HTLC :: !HTLCDirection -> {-# UNPACK #-} !MilliSatoshi -> {-# UNPACK #-} !PaymentHash -> {-# UNPACK #-} !CltvExpiry -> HTLC
+ Lightning.Protocol.BOLT3: HTLC :: !HTLCDirection -> {-# UNPACK #-} !MilliSatoshi -> !PaymentHash -> {-# UNPACK #-} !CltvExpiry -> HTLC
- Lightning.Protocol.BOLT3: HTLCContext :: !TxId -> {-# UNPACK #-} !Word32 -> !HTLC -> !ToSelfDelay -> !FeeratePerKw -> !ChannelFeatures -> !RevocationPubkey -> !LocalDelayedPubkey -> HTLCContext
+ Lightning.Protocol.BOLT3: HTLCContext :: !TxId -> {-# UNPACK #-} !Word32 -> !HTLC -> {-# UNPACK #-} !ToSelfDelay -> {-# UNPACK #-} !FeeratePerKw -> !CommitmentFormat -> !RevocationPubkey -> !LocalDelayedPubkey -> HTLCContext
- Lightning.Protocol.BOLT3: HTLCTx :: {-# UNPACK #-} !Word32 -> !Locktime -> !Outpoint -> !Sequence -> !Satoshi -> !Script -> HTLCTx
+ Lightning.Protocol.BOLT3: HTLCTx :: {-# UNPACK #-} !Word32 -> {-# UNPACK #-} !Locktime -> {-# UNPACK #-} !OutPoint -> {-# UNPACK #-} !Sequence -> {-# UNPACK #-} !Satoshi -> !Script -> HTLCTx
- Lightning.Protocol.BOLT3: LocalDelayedPubkey :: Pubkey -> LocalDelayedPubkey
+ Lightning.Protocol.BOLT3: LocalDelayedPubkey :: Point -> LocalDelayedPubkey
- Lightning.Protocol.BOLT3: LocalHtlcPubkey :: Pubkey -> LocalHtlcPubkey
+ Lightning.Protocol.BOLT3: LocalHtlcPubkey :: Point -> LocalHtlcPubkey
- Lightning.Protocol.BOLT3: RemoteHtlcPubkey :: Pubkey -> RemoteHtlcPubkey
+ Lightning.Protocol.BOLT3: RemoteHtlcPubkey :: Point -> RemoteHtlcPubkey
- Lightning.Protocol.BOLT3: RemotePubkey :: Pubkey -> RemotePubkey
+ Lightning.Protocol.BOLT3: RemotePubkey :: Point -> RemotePubkey
- Lightning.Protocol.BOLT3: RevocationPubkey :: Pubkey -> RevocationPubkey
+ Lightning.Protocol.BOLT3: RevocationPubkey :: Point -> RevocationPubkey
- Lightning.Protocol.BOLT3: [cc_commitment_number] :: CommitmentContext -> !CommitmentNumber
+ Lightning.Protocol.BOLT3: [cc_commitment_number] :: CommitmentContext -> {-# UNPACK #-} !CommitmentNumber
- Lightning.Protocol.BOLT3: [cc_dust_limit] :: CommitmentContext -> !DustLimit
+ Lightning.Protocol.BOLT3: [cc_dust_limit] :: CommitmentContext -> {-# UNPACK #-} !DustLimit
- Lightning.Protocol.BOLT3: [cc_feerate] :: CommitmentContext -> !FeeratePerKw
+ Lightning.Protocol.BOLT3: [cc_feerate] :: CommitmentContext -> {-# UNPACK #-} !FeeratePerKw
- Lightning.Protocol.BOLT3: [cc_funding_outpoint] :: CommitmentContext -> !Outpoint
+ Lightning.Protocol.BOLT3: [cc_funding_outpoint] :: CommitmentContext -> {-# UNPACK #-} !OutPoint
- Lightning.Protocol.BOLT3: [cc_to_local_msat] :: CommitmentContext -> !MilliSatoshi
+ Lightning.Protocol.BOLT3: [cc_to_local_msat] :: CommitmentContext -> {-# UNPACK #-} !MilliSatoshi
- Lightning.Protocol.BOLT3: [cc_to_remote_msat] :: CommitmentContext -> !MilliSatoshi
+ Lightning.Protocol.BOLT3: [cc_to_remote_msat] :: CommitmentContext -> {-# UNPACK #-} !MilliSatoshi
- Lightning.Protocol.BOLT3: [cc_to_self_delay] :: CommitmentContext -> !ToSelfDelay
+ Lightning.Protocol.BOLT3: [cc_to_self_delay] :: CommitmentContext -> {-# UNPACK #-} !ToSelfDelay
- Lightning.Protocol.BOLT3: [clc_fee] :: ClosingContext -> !Satoshi
+ Lightning.Protocol.BOLT3: [clc_fee] :: ClosingContext -> {-# UNPACK #-} !Satoshi
- Lightning.Protocol.BOLT3: [clc_funding_outpoint] :: ClosingContext -> !Outpoint
+ Lightning.Protocol.BOLT3: [clc_funding_outpoint] :: ClosingContext -> {-# UNPACK #-} !OutPoint
- Lightning.Protocol.BOLT3: [clc_locktime] :: ClosingContext -> !Locktime
+ Lightning.Protocol.BOLT3: [clc_locktime] :: ClosingContext -> {-# UNPACK #-} !Locktime
- Lightning.Protocol.BOLT3: [cltx_input_outpoint] :: ClosingTx -> !Outpoint
+ Lightning.Protocol.BOLT3: [cltx_input_outpoint] :: ClosingTx -> {-# UNPACK #-} !OutPoint
- Lightning.Protocol.BOLT3: [cltx_input_sequence] :: ClosingTx -> !Sequence
+ Lightning.Protocol.BOLT3: [cltx_input_sequence] :: ClosingTx -> {-# UNPACK #-} !Sequence
- Lightning.Protocol.BOLT3: [cltx_locktime] :: ClosingTx -> !Locktime
+ Lightning.Protocol.BOLT3: [cltx_locktime] :: ClosingTx -> {-# UNPACK #-} !Locktime
- Lightning.Protocol.BOLT3: [cltx_outputs] :: ClosingTx -> ![TxOutput]
+ Lightning.Protocol.BOLT3: [cltx_outputs] :: ClosingTx -> !NonEmpty ClosingOutput
- Lightning.Protocol.BOLT3: [ctx_input_outpoint] :: CommitmentTx -> !Outpoint
+ Lightning.Protocol.BOLT3: [ctx_input_outpoint] :: CommitmentTx -> {-# UNPACK #-} !OutPoint
- Lightning.Protocol.BOLT3: [ctx_input_sequence] :: CommitmentTx -> !Sequence
+ Lightning.Protocol.BOLT3: [ctx_input_sequence] :: CommitmentTx -> {-# UNPACK #-} !Sequence
- Lightning.Protocol.BOLT3: [ctx_locktime] :: CommitmentTx -> !Locktime
+ Lightning.Protocol.BOLT3: [ctx_locktime] :: CommitmentTx -> {-# UNPACK #-} !Locktime
- Lightning.Protocol.BOLT3: [ctx_outputs] :: CommitmentTx -> ![TxOutput]
+ Lightning.Protocol.BOLT3: [ctx_outputs] :: CommitmentTx -> !NonEmpty CommitmentOutput
- Lightning.Protocol.BOLT3: [hc_feerate] :: HTLCContext -> !FeeratePerKw
+ Lightning.Protocol.BOLT3: [hc_feerate] :: HTLCContext -> {-# UNPACK #-} !FeeratePerKw
- Lightning.Protocol.BOLT3: [hc_to_self_delay] :: HTLCContext -> !ToSelfDelay
+ Lightning.Protocol.BOLT3: [hc_to_self_delay] :: HTLCContext -> {-# UNPACK #-} !ToSelfDelay
- Lightning.Protocol.BOLT3: [htlc_payment_hash] :: HTLC -> {-# UNPACK #-} !PaymentHash
+ Lightning.Protocol.BOLT3: [htlc_payment_hash] :: HTLC -> !PaymentHash
- Lightning.Protocol.BOLT3: [htx_input_outpoint] :: HTLCTx -> !Outpoint
+ Lightning.Protocol.BOLT3: [htx_input_outpoint] :: HTLCTx -> {-# UNPACK #-} !OutPoint
- Lightning.Protocol.BOLT3: [htx_input_sequence] :: HTLCTx -> !Sequence
+ Lightning.Protocol.BOLT3: [htx_input_sequence] :: HTLCTx -> {-# UNPACK #-} !Sequence
- Lightning.Protocol.BOLT3: [htx_locktime] :: HTLCTx -> !Locktime
+ Lightning.Protocol.BOLT3: [htx_locktime] :: HTLCTx -> {-# UNPACK #-} !Locktime
- Lightning.Protocol.BOLT3: [htx_output_value] :: HTLCTx -> !Satoshi
+ Lightning.Protocol.BOLT3: [htx_output_value] :: HTLCTx -> {-# UNPACK #-} !Satoshi
- Lightning.Protocol.BOLT3: anchor_witness_anyone :: Witness
+ Lightning.Protocol.BOLT3: anchor_witness_anyone :: FundingPubkey -> Witness
- Lightning.Protocol.BOLT3: anchor_witness_owner :: ByteString -> Witness
+ Lightning.Protocol.BOLT3: anchor_witness_owner :: ByteString -> FundingPubkey -> Witness
- Lightning.Protocol.BOLT3: build_closing_tx :: ClosingContext -> ClosingTx
+ Lightning.Protocol.BOLT3: build_closing_tx :: ClosingContext -> Maybe ClosingTx
- Lightning.Protocol.BOLT3: build_commitment_tx :: CommitmentContext -> CommitmentTx
+ Lightning.Protocol.BOLT3: build_commitment_tx :: CommitmentContext -> Maybe CommitmentTx
- Lightning.Protocol.BOLT3: build_legacy_closing_tx :: ClosingContext -> ClosingTx
+ Lightning.Protocol.BOLT3: build_legacy_closing_tx :: LegacyClosingContext -> Maybe ClosingTx
- Lightning.Protocol.BOLT3: commitment_fee :: FeeratePerKw -> ChannelFeatures -> Word64 -> Satoshi
+ Lightning.Protocol.BOLT3: commitment_fee :: FeeratePerKw -> CommitmentFormat -> Word64 -> Satoshi
- Lightning.Protocol.BOLT3: derive_old_secret :: Word64 -> SecretStore -> Maybe ByteString
+ Lightning.Protocol.BOLT3: derive_old_secret :: SecretIndex -> SecretStore -> Maybe PerCommitmentSecret
- Lightning.Protocol.BOLT3: derive_pubkey :: Point -> PerCommitmentPoint -> Maybe Pubkey
+ Lightning.Protocol.BOLT3: derive_pubkey :: Point -> PerCommitmentPoint -> Maybe Point
- Lightning.Protocol.BOLT3: funding_witness :: ByteString -> ByteString -> Witness
+ Lightning.Protocol.BOLT3: funding_witness :: FundingPubkey -> ByteString -> FundingPubkey -> ByteString -> Witness
- Lightning.Protocol.BOLT3: generate_from_seed :: ByteString -> Word64 -> ByteString
+ Lightning.Protocol.BOLT3: generate_from_seed :: Seed -> SecretIndex -> PerCommitmentSecret
- Lightning.Protocol.BOLT3: htlc_success_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi
+ Lightning.Protocol.BOLT3: htlc_success_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi
- Lightning.Protocol.BOLT3: htlc_timeout_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi
+ Lightning.Protocol.BOLT3: htlc_timeout_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi
- Lightning.Protocol.BOLT3: htlc_trim_threshold :: DustLimit -> FeeratePerKw -> ChannelFeatures -> HTLCDirection -> Satoshi
+ Lightning.Protocol.BOLT3: htlc_trim_threshold :: DustLimit -> FeeratePerKw -> CommitmentFormat -> HTLCDirection -> Satoshi
- Lightning.Protocol.BOLT3: insert_secret :: ByteString -> Word64 -> SecretStore -> Maybe SecretStore
+ Lightning.Protocol.BOLT3: insert_secret :: PerCommitmentSecret -> SecretIndex -> SecretStore -> Maybe SecretStore
- Lightning.Protocol.BOLT3: is_trimmed :: DustLimit -> FeeratePerKw -> ChannelFeatures -> HTLC -> Bool
+ Lightning.Protocol.BOLT3: is_trimmed :: DustLimit -> FeeratePerKw -> CommitmentFormat -> HTLC -> Bool
- Lightning.Protocol.BOLT3: offered_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> ChannelFeatures -> Script
+ Lightning.Protocol.BOLT3: offered_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> CommitmentFormat -> Script
- Lightning.Protocol.BOLT3: offered_htlc_witness_preimage :: ByteString -> PaymentPreimage -> Witness
+ Lightning.Protocol.BOLT3: offered_htlc_witness_preimage :: ByteString -> PaymentPreimage -> Script -> Witness
- Lightning.Protocol.BOLT3: offered_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
+ Lightning.Protocol.BOLT3: offered_htlc_witness_revoke :: ByteString -> RevocationPubkey -> Script -> Witness
- Lightning.Protocol.BOLT3: received_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> CltvExpiry -> ChannelFeatures -> Script
+ Lightning.Protocol.BOLT3: received_htlc_script :: RevocationPubkey -> RemoteHtlcPubkey -> LocalHtlcPubkey -> PaymentHash -> CltvExpiry -> CommitmentFormat -> Script
- Lightning.Protocol.BOLT3: received_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
+ Lightning.Protocol.BOLT3: received_htlc_witness_revoke :: ByteString -> RevocationPubkey -> Script -> Witness
- Lightning.Protocol.BOLT3: received_htlc_witness_timeout :: ByteString -> Witness
+ Lightning.Protocol.BOLT3: received_htlc_witness_timeout :: ByteString -> Script -> Witness
- Lightning.Protocol.BOLT3: to_local_witness_revoke :: ByteString -> Witness
+ Lightning.Protocol.BOLT3: to_local_witness_revoke :: ByteString -> Script -> Witness
- Lightning.Protocol.BOLT3: to_local_witness_spend :: ByteString -> Witness
+ Lightning.Protocol.BOLT3: to_local_witness_spend :: ByteString -> Script -> Witness
- Lightning.Protocol.BOLT3: to_remote_witness :: ByteString -> RemotePubkey -> ChannelFeatures -> Witness
+ Lightning.Protocol.BOLT3: to_remote_witness :: ByteString -> RemotePubkey -> CommitmentFormat -> Witness

Files

CHANGELOG view
@@ -0,0 +1,69 @@+# Changelog++- 0.1.0 (2026-10-10)+  * A substantial rewrite, with breaking changes throughout:++    * Ports to ppad-bolt1 0.1.0 and ppad-tx 0.2.0. Amounts, points,+      hashes and per-commitment secrets are bolt1's types (amounts are+      bounded, with checked arithmetic); bolt3's Pubkey is merged into+      bolt1's Point, wrapped in role newtypes (RevocationPubkey, etc.).++    * The API is exported from Lightning.Protocol.BOLT3 alone, with+      snake_case names. Types with invariants (CommitmentNumber,+      SecretIndex, Seed, Seckey, SecretStore) are abstract, and every+      type has an NFData instance.++    * ChannelFeatures is replaced by CommitmentFormat (StaticRemotekey+      or Anchors).++    * CommitmentContext is owner-relative throughout: the payment+      basepoints are ordered for the obscured commitment number by+      cc_is_funder. build_commitment_tx fails rather than produce a+      transaction without outputs.++    * OutputType carries the HTLC an output pays. build_htlc_tx replaces+      build_htlc_timeout_tx and build_htlc_success_tx, choosing by HTLC+      direction, and fails if the fee exceeds the amount.++    * Closing transactions have separate builders with spec-shaped+      inputs: build_legacy_closing_tx applies the signer's dust limit to+      both outputs and can omit the signer's output; build_closing_tx+      (option_simple_close) charges the closer, takes an explicit choice+      of outputs, and gives OP_RETURN outputs amount zero.++    * to_remote_script is split into to_remote_witness_script and+      to_remote_script_pubkey. Witness functions append the witness+      script and take typed keys; funding_witness orders signatures by+      pubkey. Adds htlc_success_witness, htlc_timeout_witness and+      remote_htlc_sighash.++    * Per-commitment secrets use Seed, SecretIndex and bolt1's+      PerCommitmentSecret. SecretStore is abstract, with a redacted+      Show instance and secret_store/un_secret_store for persistence.+      Adds commitment_secret_index.++    * Adds derive_commitment_keys, derive_privkey and+      derive_revocationprivkey.++    * Renames encode_tx to encode_commitment_tx, and documents the+      encoders as producing unsigned, witness-free serializations.++    * Removes the Validate, Decode, Encode and Internal modules, the+      role-specific key derivation functions, encode_tx_for_signing,+      encode_witness, encode_funding_witness, has_anchors, sort_outputs,+      trimmed_htlcs, untrimmed_htlcs, derive_secret, the HTLC output+      script aliases, the weight constants and unused record fields.++  * Fixes insert_secret, which discarded the entries needed to derive+    older secrets and accepted invalid secret sequences.++  * Fixes closing transactions, which used each party's dust limit in+    legacy closes, and charged the funder and paid OP_RETURN outputs in+    option_simple_close.++  * Rejects key derivations yielding the point at infinity.++  * Tests cover every BOLT #3 Appendix B-F vector.++- 0.0.1 (2026-04-18)+  * Initial release.
+ bench/Fixtures.hs view
@@ -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"+  outpoint <- (\t -> BT.OutPoint t 0) <$> BT.mk_txid (BS.reverse txid)+  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))
bench/Main.hs view
@@ -1,489 +1,82 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-}- module Main where -import Control.DeepSeq (NFData(..)) import Criterion.Main-import Data.Word (Word64)-import qualified Data.ByteString as BS+import Fixtures import Lightning.Protocol.BOLT3 --- NFData instances for benchmarking---- Existing instances-instance NFData Satoshi where-  rnf (Satoshi x) = rnf x--instance NFData MilliSatoshi where-  rnf (MilliSatoshi x) = rnf x--instance NFData Pubkey where-  rnf (Pubkey x) = rnf x--instance NFData Point where-  rnf (Point 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 PaymentHash where-  rnf (PaymentHash 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 Witness where-  rnf (Witness items) = rnf items--instance NFData Outpoint where-  rnf (Outpoint t i) = rnf t `seq` rnf i--instance NFData Sequence where-  rnf (Sequence x) = rnf x--instance NFData Locktime where-  rnf (Locktime x) = rnf x--instance NFData TxId where-  rnf (TxId bs) = rnf bs--instance NFData ToSelfDelay where-  rnf (ToSelfDelay x) = rnf x--instance NFData CommitmentNumber where-  rnf (CommitmentNumber x) = rnf x---- Parsing types-instance NFData RawTx where-  rnf (RawTx v i o w l) =-    rnf v `seq` rnf i `seq` rnf o `seq` rnf w `seq` rnf l--instance NFData RawInput where-  rnf (RawInput o scr sq) = rnf o `seq` rnf scr `seq` rnf sq--instance NFData RawOutput where-  rnf (RawOutput v s) = rnf v `seq` rnf s---- 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--instance NFData PerCommitmentSecret where-  rnf (PerCommitmentSecret bs) = rnf bs---- 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---- Decode errors-instance NFData DecodeError where-  rnf (InsufficientBytes a b) = rnf a `seq` rnf b-  rnf (InvalidMarker a) = rnf a-  rnf (InvalidFlag a) = rnf a-  rnf InvalidVarint = ()-  rnf EmptyInput = ()- main :: IO ()-main = defaultMain [-    bgroup "key derivation" [-      bench "derive_pubkey" $-        whnf (derive_pubkey basepoint) perCommitmentPoint-    , 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-    ]-  , 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 $ encode_tx $ build_commitment_tx $-             mkCommitmentContext htlcs0 noAnchors)-        $ \bs -> bench "decode_tx (0 htlcs)" $ whnf decode_tx bs-    , env (pure $ encode_tx $ build_commitment_tx $-             mkCommitmentContext htlcs10 noAnchors)-        $ \bs -> bench "decode_tx (10 htlcs)" $ whnf decode_tx bs-    , env (pure $ encode_tx $ build_commitment_tx $-             mkCommitmentContext 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+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)+      ]     ]-  ]   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]--    -- 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--    -- 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
bench/Weight.hs view
@@ -1,479 +1,53 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-}- module Main where -import Control.DeepSeq (NFData(..))-import qualified Data.ByteString as BS-import Data.Word (Word32, Word64)+import Fixtures import Lightning.Protocol.BOLT3 import Weigh --- NFData instances for weigh---- Monetary types-instance NFData Satoshi where-  rnf (Satoshi x) = rnf x--instance NFData MilliSatoshi where-  rnf (MilliSatoshi x) = rnf x---- Key types-instance NFData Pubkey where-  rnf (Pubkey x) = rnf x--instance NFData Point where-  rnf (Point 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--instance NFData PerCommitmentSecret where-  rnf (PerCommitmentSecret bs) = rnf bs---- 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 PaymentHash where-  rnf (PaymentHash x) = rnf x--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 Witness where-  rnf (Witness items) = rnf items--instance NFData Outpoint where-  rnf (Outpoint t i) = rnf t `seq` rnf i--instance NFData Sequence where-  rnf (Sequence x) = rnf x--instance NFData Locktime where-  rnf (Locktime x) = rnf x--instance NFData TxId where-  rnf (TxId bs) = rnf bs--instance NFData ToSelfDelay where-  rnf (ToSelfDelay x) = rnf x--instance NFData CommitmentNumber where-  rnf (CommitmentNumber x) = rnf x---- Parsing types-instance NFData RawTx where-  rnf (RawTx v i o w l) =-    rnf v `seq` rnf i `seq` rnf o `seq` rnf w `seq` rnf l--instance NFData RawInput where-  rnf (RawInput o scr sq) = rnf o `seq` rnf scr `seq` rnf sq--instance NFData RawOutput where-  rnf (RawOutput v s) = rnf v `seq` rnf s---- 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)---- Error types-instance NFData DecodeError where-  rnf (InsufficientBytes e a) = rnf e `seq` rnf a-  rnf (InvalidMarker w) = rnf w-  rnf (InvalidFlag w) = rnf w-  rnf InvalidVarint = ()-  rnf EmptyInput = ()--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 = mainWith $ do-  setColumns [Case, Allocated, GCs, Max]--  -- Key derivation allocations-  func "derive_pubkey" (derive_pubkey basepoint) perCommitmentPoint-  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 (encode_tx $ build_commitment_tx $-                    mkCommitmentContext htlcs0 noAnchors)-  func "decode_tx (10 htlcs)"-       decode_tx (encode_tx $ build_commitment_tx $-                    mkCommitmentContext htlcs10 noAnchors)-  func "decode_tx (100 htlcs)"-       decode_tx (encode_tx $ build_commitment_tx $-                    mkCommitmentContext 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]--    -- 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--    -- 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)
lib/Lightning/Protocol/BOLT3.hs view
@@ -6,160 +6,135 @@ -- License: MIT -- Maintainer: Jared Tobin <jared@ppad.tech> ----- Bitcoin transaction formats for the Lightning Network, per--- [BOLT #3](https://github.com/lightning/bolts/blob/master/03-transactions.md).------ = Overview------ This library implements the transaction and script formats defined in--- BOLT #3, including:------ * Commitment transactions with to_local, to_remote, anchor, and HTLC---   outputs--- * HTLC-timeout and HTLC-success second-stage transactions--- * Closing transactions (legacy and option_simple_close)--- * Per-commitment key derivation and secret storage--- * Transaction serialization and parsing--- * Stateless validation------ = Quick Start------ @--- import Lightning.Protocol.BOLT3------ -- Build a commitment transaction--- let ctx = CommitmentContext { ... }---     tx = build_commitment_tx ctx------ -- Serialize for signing--- let bytes = encode_tx tx------ -- Validate the transaction--- case validate_commitment_tx dustLimit features tx of---   Right () -> putStrLn "Valid"---   Left err -> print err--- @------ = Modules+-- Bitcoin transaction and script formats for the Lightning Network, per+-- [BOLT #3](https://github.com/lightning/bolts/blob/master/03-transactions.md)+-- (commitment, HTLC and closing transactions, their output scripts and+-- witnesses, per-commitment key derivation, and per-commitment secret+-- generation and storage). ----- * "Lightning.Protocol.BOLT3.Types" - Core types (Satoshi, Pubkey, HTLC,---   etc.)--- * "Lightning.Protocol.BOLT3.Keys" - Per-commitment key derivation and---   secret storage--- * "Lightning.Protocol.BOLT3.Scripts" - Witness script templates (funding,---   to_local, HTLC, anchor)--- * "Lightning.Protocol.BOLT3.Tx" - Transaction assembly--- * "Lightning.Protocol.BOLT3.Encode" - Transaction serialization--- * "Lightning.Protocol.BOLT3.Decode" - Transaction parsing--- * "Lightning.Protocol.BOLT3.Validate" - Stateless validation+-- Amounts, points, hashes and per-commitment secrets are the types of+-- ppad-bolt1's "Lightning.Protocol.BOLT1"; transactions convert to+-- ppad-tx's "Bitcoin.Prim.Tx" for txids, sighashes and serialization.+-- The @option_static_remotekey@ and @option_anchors@ commitment formats+-- are supported ('CommitmentFormat').  module Lightning.Protocol.BOLT3 (-    -- * Types-    -- ** Monetary amounts-    Satoshi(..)-  , MilliSatoshi(..)-  , msat_to_sat-  , sat_to_msat--    -- ** Keys and points-  , Pubkey(..)-  , pubkey-  , Seckey(..)-  , seckey-  , Point(..)-  , point--    -- ** Hashes-  , PaymentHash(..)-  , payment_hash-  , PaymentPreimage(..)-  , payment_preimage+    -- * Commitment transactions+    CommitmentContext(..)+  , CommitmentFormat(..)+  , CommitmentTx(..)+  , CommitmentOutput(..)+  , OutputType(..)+  , build_commitment_tx -    -- ** Transaction primitives-  , TxId(..)-  , txid-  , Outpoint(..)-  , Sequence(..)-  , Locktime(..)+    -- ** HTLCs+  , HTLC(..)+  , HTLCDirection(..)      -- ** Channel parameters-  , CommitmentNumber(..)+  , CommitmentNumber   , commitment_number+  , un_commitment_number+  , next_commitment_number   , ToSelfDelay(..)   , CltvExpiry(..)   , DustLimit(..)   , FeeratePerKw(..)+  , Locktime(..)+  , Sequence(..) -    -- ** HTLC types-  , HTLC(..)-  , HTLCDirection(..)+    -- ** Fees and trimming+  , commitment_fee+  , htlc_timeout_fee+  , htlc_success_fee+  , htlc_trim_threshold+  , is_trimmed+  , anchor_output_value -    -- ** Basepoints+    -- * HTLC transactions+  , HTLCContext(..)+  , HTLCTx(..)+  , build_htlc_tx++    -- * Closing transactions+  , ClosingTx(..)+  , ClosingOutput(..)+  , ClosingContext(..)+  , ClosingOutputs(..)+  , build_closing_tx+  , LegacyClosingContext(..)+  , build_legacy_closing_tx++    -- ** Dust thresholds+  , dust_p2pkh+  , dust_p2sh+  , dust_p2wpkh+  , dust_p2wsh++    -- * Serialization+  , commitment_to_tx+  , htlc_to_tx+  , closing_to_tx+  , encode_commitment_tx+  , encode_htlc_tx+  , encode_closing_tx++    -- * Keys+    -- ** Basepoints and per-commitment points   , Basepoints(..)-  , PerCommitmentPoint(..)-  , PerCommitmentSecret(..)-  , per_commitment_secret   , RevocationBasepoint(..)   , PaymentBasepoint(..)   , DelayedPaymentBasepoint(..)   , HtlcBasepoint(..)+  , PerCommitmentPoint(..)+  , derive_per_commitment_point+  , derive_per_commitment_point' -    -- ** Derived keys-  , LocalPubkey(..)-  , RemotePubkey(..)+    -- ** Public keys+  , CommitmentKeys(..)+  , derive_commitment_keys+  , derive_commitment_keys'+  , RevocationPubkey(..)   , LocalDelayedPubkey(..)-  , RemoteDelayedPubkey(..)   , LocalHtlcPubkey(..)   , RemoteHtlcPubkey(..)-  , RevocationPubkey(..)+  , RemotePubkey(..)   , FundingPubkey(..)--    -- ** Script and witness-  , Script(..)-  , Witness(..)--    -- ** Channel features-  , ChannelFeatures(..)-  , has_anchors--    -- ** Constants-  , commitment_weight_no_anchors-  , commitment_weight_anchors-  , htlc_timeout_weight_no_anchors-  , htlc_timeout_weight_anchors-  , htlc_success_weight_no_anchors-  , htlc_success_weight_anchors-  , htlc_output_weight-  , dust_p2pkh-  , dust_p2sh-  , dust_p2wpkh-  , dust_p2wsh-  , anchor_output_value--    -- * Key derivation-  , derive_per_commitment_point   , derive_pubkey-  , derive_localpubkey-  , derive_local_htlcpubkey-  , derive_remote_htlcpubkey-  , derive_local_delayedpubkey-  , derive_remote_delayedpubkey   , derive_revocationpubkey -    -- ** Secret generation+    -- ** Private keys+  , Seckey+  , seckey+  , un_seckey+  , derive_privkey+  , derive_revocationprivkey++    -- * Per-commitment secrets+  , Seed+  , seed+  , un_seed+  , SecretIndex+  , secret_index+  , un_secret_index+  , commitment_secret_index   , generate_from_seed-  , derive_secret -    -- ** Secret storage+    -- ** Storage   , SecretStore-  , empty_store+  , empty_secret_store   , insert_secret   , derive_old_secret+  , secret_store+  , un_secret_store -    -- ** Commitment number+    -- ** Commitment number obscuring   , obscured_commitment_number -    -- * Scripts+    -- * Scripts and witnesses+  , Script(..)+  , to_p2wsh+     -- ** Funding output   , funding_script   , funding_witness@@ -170,7 +145,8 @@   , to_local_witness_revoke      -- ** to_remote output-  , to_remote_script+  , to_remote_witness_script+  , to_remote_script_pubkey   , to_remote_witness      -- ** Anchor outputs@@ -178,110 +154,23 @@   , anchor_witness_owner   , anchor_witness_anyone -    -- ** Offered HTLC+    -- ** Offered HTLC outputs   , offered_htlc_script   , offered_htlc_witness_preimage   , offered_htlc_witness_revoke -    -- ** Received HTLC+    -- ** Received HTLC outputs   , received_htlc_script   , received_htlc_witness_timeout   , received_htlc_witness_revoke -    -- ** HTLC output (same as to_local)-  , htlc_output_script-  , htlc_output_witness_spend-  , htlc_output_witness_revoke--    -- ** P2WSH helpers-  , to_p2wsh-  , witness_script_hash--    -- * Transaction assembly-    -- ** Commitment transactions-  , CommitmentTx(..)-  , CommitmentContext(..)-  , CommitmentKeys(..)-  , build_commitment_tx--    -- ** HTLC transactions-  , HTLCTx(..)-  , HTLCContext(..)-  , build_htlc_timeout_tx-  , build_htlc_success_tx--    -- ** Closing transactions-  , ClosingTx(..)-  , ClosingContext(..)-  , build_closing_tx-  , build_legacy_closing_tx--    -- ** Transaction outputs-  , TxOutput(..)-  , OutputType(..)--    -- ** Fee calculation-  , commitment_fee-  , commitment_weight-  , htlc_timeout_fee-  , htlc_success_fee--    -- ** Trimming-  , htlc_trim_threshold-  , is_trimmed-  , trimmed_htlcs-  , untrimmed_htlcs--    -- ** Output ordering-  , sort_outputs--    -- * Serialization-  , encode_tx-  , encode_htlc_tx-  , encode_closing_tx-  , encode_tx_for_signing-  , encode_varint-  , encode_le32-  , encode_le64-  , encode_outpoint-  , encode_output-  , encode_witness-  , encode_funding_witness--    -- * Parsing-  , DecodeError(..)-  , RawTx(..)-  , RawInput(..)-  , RawOutput(..)-  , decode_tx-  , decode_varint-  , decode_le32-  , decode_le64-  , decode_outpoint-  , decode_output-  , decode_witness--    -- * Validation-  , ValidationError(..)-  , validate_commitment_tx-  , validate_commitment_locktime-  , validate_commitment_sequence-  , validate_htlc_tx-  , validate_htlc_timeout_tx-  , validate_htlc_success_tx-  , validate_closing_tx-  , validate_legacy_closing_tx-  , validate_output_ordering-  , validate_dust_limits-  , validate_anchor_outputs-  , validate_commitment_fee-  , validate_htlc_fee+    -- ** HTLC transaction inputs+  , htlc_success_witness+  , htlc_timeout_witness+  , remote_htlc_sighash   ) where -import Lightning.Protocol.BOLT3.Types import Lightning.Protocol.BOLT3.Keys import Lightning.Protocol.BOLT3.Scripts import Lightning.Protocol.BOLT3.Tx-import Lightning.Protocol.BOLT3.Encode-import Lightning.Protocol.BOLT3.Decode-import Lightning.Protocol.BOLT3.Validate+import Lightning.Protocol.BOLT3.Types
− lib/Lightning/Protocol/BOLT3/Decode.hs
@@ -1,389 +0,0 @@-{-# OPTIONS_HADDOCK prune #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DeriveGeneric #-}---- |--- Module: Lightning.Protocol.BOLT3.Decode--- Copyright: (c) 2025 Jared Tobin--- License: MIT--- Maintainer: Jared Tobin <jared@ppad.tech>------ Parsing for BOLT #3 transactions and scripts.------ Decodes SegWit Bitcoin transactions from raw bytes.--module Lightning.Protocol.BOLT3.Decode (-    -- * Error types-    DecodeError(..)--    -- * Raw transaction type-  , RawTx(..)-  , RawInput(..)-  , RawOutput(..)--    -- * Transaction parsing-  , decode_tx--    -- * Witness parsing-  , decode_witness--    -- * Primitive decoding-  , decode_varint-  , decode_le32-  , decode_le64-  , decode_outpoint-  , decode_output-  ) where--import Data.Bits ((.|.), shiftL)-import Data.Word (Word8, Word32, Word64)-import qualified Data.ByteString as BS-import GHC.Generics (Generic)-import Lightning.Protocol.BOLT3.Types---- error types --------------------------------------------------------------------- | Errors that can occur during transaction decoding.-data DecodeError-  = InsufficientBytes !Int !Int-    -- ^ Expected bytes, actual bytes available-  | InvalidMarker !Word8-    -- ^ Invalid SegWit marker byte (expected 0x00)-  | InvalidFlag !Word8-    -- ^ Invalid SegWit flag byte (expected 0x01)-  | InvalidVarint-    -- ^ Malformed varint encoding-  | EmptyInput-    -- ^ No bytes to decode-  deriving (Eq, Show, Generic)---- raw transaction types ----------------------------------------------------------- | A raw transaction input as parsed from bytes.-data RawInput = RawInput-  { ri_outpoint   :: !Outpoint-  , ri_script_sig :: !BS.ByteString-  , ri_sequence   :: !Sequence-  } deriving (Eq, Show, Generic)---- | A raw transaction output as parsed from bytes.-data RawOutput = RawOutput-  { ro_value  :: !Satoshi-  , ro_script :: !Script-  } deriving (Eq, Show, Generic)---- | A raw transaction as parsed from bytes.------ Supports both legacy and SegWit transaction formats.-data RawTx = RawTx-  { rtx_version  :: {-# UNPACK #-} !Word32-  , rtx_inputs   :: ![RawInput]-  , rtx_outputs  :: ![RawOutput]-  , rtx_witness  :: ![[BS.ByteString]]-    -- ^ Witness stack for each input (empty list for legacy tx)-  , rtx_locktime :: !Locktime-  } deriving (Eq, Show, Generic)---- primitive decoding -------------------------------------------------------------- | Decode a little-endian 32-bit integer.------ >>> decode_le32 (BS.pack [0x01, 0x00, 0x00, 0x00])--- Right (1, "")-decode_le32 :: BS.ByteString -> Either DecodeError (Word32, BS.ByteString)-decode_le32 !bs-  | BS.length bs < 4 = Left (InsufficientBytes 4 (BS.length bs))-  | otherwise =-      let !b0 = fromIntegral (BS.index bs 0)-          !b1 = fromIntegral (BS.index bs 1)-          !b2 = fromIntegral (BS.index bs 2)-          !b3 = fromIntegral (BS.index bs 3)-          !val = b0 .|. (b1 `shiftL` 8) .|. (b2 `shiftL` 16)-                     .|. (b3 `shiftL` 24)-          !rest = BS.drop 4 bs-      in Right (val, rest)-{-# INLINE decode_le32 #-}---- | Decode a little-endian 64-bit integer.------ >>> decode_le64 (BS.pack [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])--- Right (1, "")-decode_le64 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)-decode_le64 !bs-  | BS.length bs < 8 = Left (InsufficientBytes 8 (BS.length bs))-  | otherwise =-      let !b0 = fromIntegral (BS.index bs 0)-          !b1 = fromIntegral (BS.index bs 1)-          !b2 = fromIntegral (BS.index bs 2)-          !b3 = fromIntegral (BS.index bs 3)-          !b4 = fromIntegral (BS.index bs 4)-          !b5 = fromIntegral (BS.index bs 5)-          !b6 = fromIntegral (BS.index bs 6)-          !b7 = fromIntegral (BS.index bs 7)-          !val = b0 .|. (b1 `shiftL` 8) .|. (b2 `shiftL` 16)-                    .|. (b3 `shiftL` 24) .|. (b4 `shiftL` 32)-                    .|. (b5 `shiftL` 40) .|. (b6 `shiftL` 48)-                    .|. (b7 `shiftL` 56)-          !rest = BS.drop 8 bs-      in Right (val, rest)-{-# INLINE decode_le64 #-}---- | Decode a Bitcoin varint (CompactSize).------ Encoding:--- * 0x00-0xFC: 1 byte--- * 0xFD: 2 bytes little-endian follow--- * 0xFE: 4 bytes little-endian follow--- * 0xFF: 8 bytes little-endian follow------ >>> decode_varint (BS.pack [0x01])--- Right (1, "")--- >>> decode_varint (BS.pack [0xfd, 0x00, 0x01])--- Right (256, "")-decode_varint :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)-decode_varint !bs-  | BS.null bs = Left EmptyInput-  | otherwise =-      let !first = BS.index bs 0-          !rest = BS.drop 1 bs-      in case first of-           0xFD -> decode_varint_16 rest-           0xFE -> decode_varint_32 rest-           0xFF -> decode_le64 rest-           _    -> Right (fromIntegral first, rest)-{-# INLINE decode_varint #-}---- | Decode a 16-bit varint payload.-decode_varint_16 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)-decode_varint_16 !bs-  | BS.length bs < 2 = Left (InsufficientBytes 2 (BS.length bs))-  | otherwise =-      let !b0 = fromIntegral (BS.index bs 0) :: Word64-          !b1 = fromIntegral (BS.index bs 1) :: Word64-          !val = b0 .|. (b1 `shiftL` 8)-          !rest = BS.drop 2 bs-      in Right (val, rest)-{-# INLINE decode_varint_16 #-}---- | Decode a 32-bit varint payload.-decode_varint_32 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)-decode_varint_32 !bs-  | BS.length bs < 4 = Left (InsufficientBytes 4 (BS.length bs))-  | otherwise =-      let !b0 = fromIntegral (BS.index bs 0) :: Word64-          !b1 = fromIntegral (BS.index bs 1) :: Word64-          !b2 = fromIntegral (BS.index bs 2) :: Word64-          !b3 = fromIntegral (BS.index bs 3) :: Word64-          !val = b0 .|. (b1 `shiftL` 8) .|. (b2 `shiftL` 16)-                    .|. (b3 `shiftL` 24)-          !rest = BS.drop 4 bs-      in Right (val, rest)-{-# INLINE decode_varint_32 #-}---- | Decode a transaction outpoint (txid + output index).------ Format: 32 bytes txid (little-endian) + 4 bytes index (little-endian)------ >>> let txid = BS.replicate 32 0--- >>> let idx = BS.pack [0x01, 0x00, 0x00, 0x00]--- >>> decode_outpoint (txid <> idx)--- Right (Outpoint {outpoint_txid = ..., outpoint_index = 1}, "")-decode_outpoint-  :: BS.ByteString-  -> Either DecodeError (Outpoint, BS.ByteString)-decode_outpoint !bs-  | BS.length bs < 36 = Left (InsufficientBytes 36 (BS.length bs))-  | otherwise =-      let !txid = TxId (BS.take 32 bs)-          !rest1 = BS.drop 32 bs-      in case decode_le32 rest1 of-           Left err -> Left err-           Right (!idx, !rest2) ->-             let !outpoint = Outpoint txid idx-             in Right (outpoint, rest2)-{-# INLINE decode_outpoint #-}---- | Decode a transaction output (value + scriptPubKey).------ Format: 8 bytes value (little-endian) + varint script length + script-decode_output :: BS.ByteString -> Either DecodeError (RawOutput, BS.ByteString)-decode_output !bs = do-  (!value, !rest1) <- decode_le64 bs-  (!scriptLen, !rest2) <- decode_varint rest1-  let !len = fromIntegral scriptLen-  if BS.length rest2 < len-    then Left (InsufficientBytes len (BS.length rest2))-    else-      let !script = Script (BS.take len rest2)-          !rest3 = BS.drop len rest2-          !output = RawOutput (Satoshi value) script-      in Right (output, rest3)-{-# INLINE decode_output #-}---- witness parsing ----------------------------------------------------------------- | Decode a witness stack for one input.------ Format: varint num_items + (varint length + data) for each item-decode_witness-  :: BS.ByteString-  -> Either DecodeError (Witness, BS.ByteString)-decode_witness !bs = do-  (!numItems, !rest1) <- decode_varint bs-  (!items, !rest2) <- decode_witness_items (fromIntegral numItems) rest1 []-  Right (Witness items, rest2)-{-# INLINE decode_witness #-}---- | Decode witness items recursively.-decode_witness_items-  :: Int-  -> BS.ByteString-  -> [BS.ByteString]-  -> Either DecodeError ([BS.ByteString], BS.ByteString)-decode_witness_items 0 !bs !acc = Right (reverse acc, bs)-decode_witness_items !n !bs !acc = do-  (!itemLen, !rest1) <- decode_varint bs-  let !len = fromIntegral itemLen-  if BS.length rest1 < len-    then Left (InsufficientBytes len (BS.length rest1))-    else-      let !item = BS.take len rest1-          !rest2 = BS.drop len rest1-      in decode_witness_items (n - 1) rest2 (item : acc)---- | Decode witness stacks for all inputs (internal, returns list).-decode_witness_stacks-  :: Int-  -> BS.ByteString-  -> [[BS.ByteString]]-  -> Either DecodeError ([[BS.ByteString]], BS.ByteString)-decode_witness_stacks 0 !bs !acc = Right (reverse acc, bs)-decode_witness_stacks !n !bs !acc = do-  (Witness !items, !rest) <- decode_witness bs-  decode_witness_stacks (n - 1) rest (items : acc)---- transaction parsing ------------------------------------------------------------- | Decode a raw Bitcoin transaction from bytes.------ Handles both legacy and SegWit transaction formats.------ SegWit format:--- * version (4 bytes LE)--- * marker (0x00) + flag (0x01)--- * input count (varint)--- * inputs: outpoint (32+4), scriptSig length (varint), scriptSig, sequence--- * output count (varint)--- * outputs: value (8 LE), scriptPubKey length (varint), scriptPubKey--- * witness data (for each input)--- * locktime (4 bytes LE)------ >>> decode_tx rawTxBytes--- Right (RawTx {...})-decode_tx :: BS.ByteString -> Either DecodeError RawTx-decode_tx !bs = do-  -- Version (4 bytes LE)-  (!version, !rest1) <- decode_le32 bs--  -- Check for SegWit marker/flag-  let !hasWitness = BS.length rest1 >= 2 &&-                    BS.index rest1 0 == 0x00 &&-                    BS.index rest1 1 == 0x01--  if hasWitness-    then decode_tx_segwit version (BS.drop 2 rest1)-    else decode_tx_legacy version rest1-{-# INLINE decode_tx #-}---- | Decode a SegWit transaction (after marker/flag consumed).-decode_tx_segwit-  :: Word32-  -> BS.ByteString-  -> Either DecodeError RawTx-decode_tx_segwit !version !bs = do-  -- Input count and inputs-  (!inputCount, !rest1) <- decode_varint bs-  (!inputs, !rest2) <- decode_inputs (fromIntegral inputCount) rest1 []--  -- Output count and outputs-  (!outputCount, !rest3) <- decode_varint rest2-  (!outputs, !rest4) <- decode_outputs (fromIntegral outputCount) rest3 []--  -- Witness data for each input-  (!witnesses, !rest5) <- decode_witness_stacks (length inputs) rest4 []--  -- Locktime (4 bytes LE)-  (!locktime, !_rest6) <- decode_le32 rest5--  Right RawTx-    { rtx_version  = version-    , rtx_inputs   = inputs-    , rtx_outputs  = outputs-    , rtx_witness  = witnesses-    , rtx_locktime = Locktime locktime-    }---- | Decode a legacy (non-SegWit) transaction.-decode_tx_legacy-  :: Word32-  -> BS.ByteString-  -> Either DecodeError RawTx-decode_tx_legacy !version !bs = do-  -- Input count and inputs-  (!inputCount, !rest1) <- decode_varint bs-  (!inputs, !rest2) <- decode_inputs (fromIntegral inputCount) rest1 []--  -- Output count and outputs-  (!outputCount, !rest3) <- decode_varint rest2-  (!outputs, !rest4) <- decode_outputs (fromIntegral outputCount) rest3 []--  -- Locktime (4 bytes LE)-  (!locktime, !_rest5) <- decode_le32 rest4--  Right RawTx-    { rtx_version  = version-    , rtx_inputs   = inputs-    , rtx_outputs  = outputs-    , rtx_witness  = []-    , rtx_locktime = Locktime locktime-    }---- | Decode transaction inputs recursively.-decode_inputs-  :: Int-  -> BS.ByteString-  -> [RawInput]-  -> Either DecodeError ([RawInput], BS.ByteString)-decode_inputs 0 !bs !acc = Right (reverse acc, bs)-decode_inputs !n !bs !acc = do-  (!input, !rest) <- decode_input bs-  decode_inputs (n - 1) rest (input : acc)---- | Decode a single transaction input.------ Format: outpoint (36 bytes) + scriptSig length (varint) + scriptSig +---         sequence (4 bytes LE)-decode_input :: BS.ByteString -> Either DecodeError (RawInput, BS.ByteString)-decode_input !bs = do-  (!outpoint, !rest1) <- decode_outpoint bs-  (!scriptLen, !rest2) <- decode_varint rest1-  let !len = fromIntegral scriptLen-  if BS.length rest2 < len-    then Left (InsufficientBytes len (BS.length rest2))-    else do-      let !scriptSig = BS.take len rest2-          !rest3 = BS.drop len rest2-      (!seqNum, !rest4) <- decode_le32 rest3-      let !input = RawInput outpoint scriptSig (Sequence seqNum)-      Right (input, rest4)---- | Decode transaction outputs recursively.-decode_outputs-  :: Int-  -> BS.ByteString-  -> [RawOutput]-  -> Either DecodeError ([RawOutput], BS.ByteString)-decode_outputs 0 !bs !acc = Right (reverse acc, bs)-decode_outputs !n !bs !acc = do-  (!output, !rest) <- decode_output bs-  decode_outputs (n - 1) rest (output : acc)
− lib/Lightning/Protocol/BOLT3/Encode.hs
@@ -1,298 +0,0 @@-{-# OPTIONS_HADDOCK prune #-}-{-# LANGUAGE BangPatterns #-}---- |--- Module: Lightning.Protocol.BOLT3.Encode--- Copyright: (c) 2025 Jared Tobin--- License: MIT--- Maintainer: Jared Tobin <jared@ppad.tech>------ Serialization for BOLT #3 transactions and scripts.------ Provides Bitcoin transaction serialization in both standard SegWit--- format (with witness data) and the stripped format used for signing.------ == Transaction Format (SegWit)------ * version (4 bytes LE)--- * marker (0x00) + flag (0x01)--- * input count (varint)--- * inputs: outpoint (32+4), scriptSig length (varint), scriptSig, sequence--- * output count (varint)--- * outputs: value (8 LE), scriptPubKey length (varint), scriptPubKey--- * witness data (for each input)--- * locktime (4 bytes LE)--module Lightning.Protocol.BOLT3.Encode (-    -- * Transaction serialization-    encode_tx-  , encode_htlc_tx-  , encode_closing_tx-  , encode_tx_for_signing--    -- * Witness serialization-  , encode_witness-  , encode_funding_witness--    -- * Primitive encoding-  , encode_varint-  , encode_le32-  , encode_le64-  , encode_outpoint-  , encode_output-  ) where--import Data.Word (Word32, Word64)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Builder as BSB-import qualified Data.ByteString.Lazy as BSL-import Lightning.Protocol.BOLT3.Types-import Lightning.Protocol.BOLT3.Tx---- primitive encoding -------------------------------------------------------------- | Encode a 32-bit value in little-endian format.------ >>> encode_le32 0x12345678--- "\x78\x56\x34\x12"-encode_le32 :: Word32 -> BS.ByteString-encode_le32 = BSL.toStrict . BSB.toLazyByteString . BSB.word32LE-{-# INLINE encode_le32 #-}---- | Encode a 64-bit value in little-endian format.------ >>> encode_le64 0x123456789ABCDEF0--- "\xF0\xDE\xBC\x9A\x78\x56\x34\x12"-encode_le64 :: Word64 -> BS.ByteString-encode_le64 = BSL.toStrict . BSB.toLazyByteString . BSB.word64LE-{-# INLINE encode_le64 #-}---- | Encode a value as a Bitcoin varint (CompactSize).------ Encoding scheme:------ * 0-252: 1 byte--- * 253-65535: 0xFD followed by 2 bytes LE--- * 65536-4294967295: 0xFE followed by 4 bytes LE--- * larger: 0xFF followed by 8 bytes LE------ >>> encode_varint 100--- "\x64"--- >>> encode_varint 1000--- "\xFD\xE8\x03"-encode_varint :: Word64 -> BS.ByteString-encode_varint !n-  | n < 0xFD = BS.singleton (fromIntegral n)-  | n <= 0xFFFF = BSL.toStrict $ BSB.toLazyByteString $-      BSB.word8 0xFD <> BSB.word16LE (fromIntegral n)-  | n <= 0xFFFFFFFF = BSL.toStrict $ BSB.toLazyByteString $-      BSB.word8 0xFE <> BSB.word32LE (fromIntegral n)-  | otherwise = BSL.toStrict $ BSB.toLazyByteString $-      BSB.word8 0xFF <> BSB.word64LE n-{-# INLINE encode_varint #-}---- | Encode an outpoint (txid + output index).------ Format: 32 bytes txid (already LE in TxId) + 4 bytes output index LE------ >>> encode_outpoint (Outpoint txid 0)--- <32-byte txid><4-byte index>-encode_outpoint :: Outpoint -> BS.ByteString-encode_outpoint !op = BSL.toStrict $ BSB.toLazyByteString $-  BSB.byteString (unTxId $ outpoint_txid op) <>-  BSB.word32LE (outpoint_index op)-{-# INLINE encode_outpoint #-}---- | Encode a transaction output.------ Format: 8 bytes value LE + varint scriptPubKey length + scriptPubKey------ >>> encode_output (TxOutput (Satoshi 100000) script OutputToLocal)--- <8-byte value><varint length><scriptPubKey>-encode_output :: TxOutput -> BS.ByteString-encode_output !out = BSL.toStrict $ BSB.toLazyByteString $-  let !script = unScript (txout_script out)-      !scriptLen = fromIntegral (BS.length script) :: Word64-  in BSB.word64LE (unSatoshi $ txout_value out) <>-     varint_builder scriptLen <>-     BSB.byteString script-{-# INLINE encode_output #-}---- witness encoding ---------------------------------------------------------------- | Encode a witness stack.------ Format: varint item count + (varint length + data) for each item------ >>> encode_witness (Witness [sig, pubkey])--- <varint 2><varint sigLen><sig><varint pkLen><pubkey>-encode_witness :: Witness -> BS.ByteString-encode_witness (Witness !items) = BSL.toStrict $ BSB.toLazyByteString $-  let !count = fromIntegral (length items) :: Word64-  in varint_builder count <> mconcat (map encode_witness_item items)-{-# INLINE encode_witness #-}---- | Encode a single witness stack item.-encode_witness_item :: BS.ByteString -> BSB.Builder-encode_witness_item !bs =-  let !len = fromIntegral (BS.length bs) :: Word64-  in varint_builder len <> BSB.byteString bs-{-# INLINE encode_witness_item #-}---- | Encode a funding witness (2-of-2 multisig).------ The witness stack is: @0 <sig1> <sig2> <witnessScript>@------ Signatures must be ordered to match pubkey order in the funding script.------ >>> encode_funding_witness sig1 sig2 fundingScript--- <witness with 4 items: empty, sig1, sig2, script>-encode_funding_witness-  :: BS.ByteString  -- ^ Signature for pubkey1 (lexicographically lesser)-  -> BS.ByteString  -- ^ Signature for pubkey2 (lexicographically greater)-  -> Script         -- ^ The funding witness script-  -> BS.ByteString-encode_funding_witness !sig1 !sig2 (Script !witnessScript) =-  BSL.toStrict $ BSB.toLazyByteString $-    varint_builder 4 <>-    encode_witness_item BS.empty <>-    encode_witness_item sig1 <>-    encode_witness_item sig2 <>-    encode_witness_item witnessScript-{-# INLINE encode_funding_witness #-}---- transaction encoding ------------------------------------------------------------ | Encode a commitment transaction (SegWit format with witness).------ SegWit format:------ * version (4 bytes LE)--- * marker (0x00)--- * flag (0x01)--- * input count (varint)--- * inputs--- * output count (varint)--- * outputs--- * witness data--- * locktime (4 bytes LE)------ Note: The witness is empty (just count=0) since the commitment tx--- spending the funding output requires external signatures.-encode_tx :: CommitmentTx -> BS.ByteString-encode_tx !tx = BSL.toStrict $ BSB.toLazyByteString $-  -- Version-  BSB.word32LE (ctx_version tx) <>-  -- SegWit marker and flag-  BSB.word8 0x00 <>-  BSB.word8 0x01 <>-  -- Input count (always 1 for commitment tx)-  varint_builder 1 <>-  -- Input: outpoint + empty scriptSig + sequence-  BSB.byteString (encode_outpoint (ctx_input_outpoint tx)) <>-  varint_builder 0 <>  -- scriptSig length (empty for SegWit)-  BSB.word32LE (unSequence $ ctx_input_sequence tx) <>-  -- Output count-  varint_builder (fromIntegral $ length $ ctx_outputs tx) <>-  -- Outputs-  mconcat (map (BSB.byteString . encode_output) (ctx_outputs tx)) <>-  -- Witness (empty stack for unsigned tx)-  varint_builder 0 <>-  -- Locktime-  BSB.word32LE (unLocktime $ ctx_locktime tx)---- | Encode an HTLC transaction (SegWit format with witness).------ HTLC transactions have a single input (the commitment tx HTLC output)--- and a single output (the to_local-style delayed output).-encode_htlc_tx :: HTLCTx -> BS.ByteString-encode_htlc_tx !tx = BSL.toStrict $ BSB.toLazyByteString $-  -- Version-  BSB.word32LE (htx_version tx) <>-  -- SegWit marker and flag-  BSB.word8 0x00 <>-  BSB.word8 0x01 <>-  -- Input count (always 1)-  varint_builder 1 <>-  -- Input: outpoint + empty scriptSig + sequence-  BSB.byteString (encode_outpoint (htx_input_outpoint tx)) <>-  varint_builder 0 <>  -- scriptSig length (empty for SegWit)-  BSB.word32LE (unSequence $ htx_input_sequence tx) <>-  -- Output count (always 1)-  varint_builder 1 <>-  -- Output: value + scriptPubKey-  BSB.word64LE (unSatoshi $ htx_output_value tx) <>-  let !script = unScript (htx_output_script tx)-      !scriptLen = fromIntegral (BS.length script) :: Word64-  in varint_builder scriptLen <> BSB.byteString script <>-  -- Witness (empty stack for unsigned tx)-  varint_builder 0 <>-  -- Locktime-  BSB.word32LE (unLocktime $ htx_locktime tx)---- | Encode a closing transaction (SegWit format with witness).------ Closing transactions have a single input (the funding output) and--- one or two outputs (to_local and/or to_remote).-encode_closing_tx :: ClosingTx -> BS.ByteString-encode_closing_tx !tx = BSL.toStrict $ BSB.toLazyByteString $-  -- Version-  BSB.word32LE (cltx_version tx) <>-  -- SegWit marker and flag-  BSB.word8 0x00 <>-  BSB.word8 0x01 <>-  -- Input count (always 1)-  varint_builder 1 <>-  -- Input: outpoint + empty scriptSig + sequence-  BSB.byteString (encode_outpoint (cltx_input_outpoint tx)) <>-  varint_builder 0 <>  -- scriptSig length (empty for SegWit)-  BSB.word32LE (unSequence $ cltx_input_sequence tx) <>-  -- Output count-  varint_builder (fromIntegral $ length $ cltx_outputs tx) <>-  -- Outputs-  mconcat (map (BSB.byteString . encode_output) (cltx_outputs tx)) <>-  -- Witness (empty stack for unsigned tx)-  varint_builder 0 <>-  -- Locktime-  BSB.word32LE (unLocktime $ cltx_locktime tx)---- | Encode a commitment transaction for signing (stripped format).------ The stripped format omits the SegWit marker, flag, and witness data.--- This is the format used to compute the sighash for signing.------ Format:------ * version (4 bytes LE)--- * input count (varint)--- * inputs--- * output count (varint)--- * outputs--- * locktime (4 bytes LE)-encode_tx_for_signing :: CommitmentTx -> BS.ByteString-encode_tx_for_signing !tx = BSL.toStrict $ BSB.toLazyByteString $-  -- Version-  BSB.word32LE (ctx_version tx) <>-  -- Input count (always 1 for commitment tx)-  varint_builder 1 <>-  -- Input: outpoint + empty scriptSig + sequence-  BSB.byteString (encode_outpoint (ctx_input_outpoint tx)) <>-  varint_builder 0 <>  -- scriptSig length (empty for SegWit)-  BSB.word32LE (unSequence $ ctx_input_sequence tx) <>-  -- Output count-  varint_builder (fromIntegral $ length $ ctx_outputs tx) <>-  -- Outputs-  mconcat (map (BSB.byteString . encode_output) (ctx_outputs tx)) <>-  -- Locktime-  BSB.word32LE (unLocktime $ ctx_locktime tx)---- internal helpers ---------------------------------------------------------------- | Build a varint directly to Builder.-varint_builder :: Word64 -> BSB.Builder-varint_builder !n-  | n < 0xFD = BSB.word8 (fromIntegral n)-  | n <= 0xFFFF = BSB.word8 0xFD <> BSB.word16LE (fromIntegral n)-  | n <= 0xFFFFFFFF = BSB.word8 0xFE <> BSB.word32LE (fromIntegral n)-  | otherwise = BSB.word8 0xFF <> BSB.word64LE n-{-# INLINE varint_builder #-}
lib/Lightning/Protocol/BOLT3/Keys.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-} @@ -8,419 +8,519 @@ -- License: MIT -- Maintainer: Jared Tobin <jared@ppad.tech> ----- Per-commitment key derivation per BOLT #3.------ Implements key derivation formulas:------ @--- pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G--- revocationpubkey = revocation_basepoint * SHA256(revocation_basepoint---                      || per_commitment_point)---                  + per_commitment_point * SHA256(per_commitment_point---                      || revocation_basepoint)--- @+-- Key derivation, per-commitment secrets and commitment number+-- obscuring, per BOLT #3.  module Lightning.Protocol.BOLT3.Keys (-    -- * Per-commitment point derivation+    -- * Per-commitment points     derive_per_commitment_point+  , derive_per_commitment_point' -    -- * Key derivation+    -- * Public keys   , derive_pubkey-  , derive_localpubkey-  , derive_local_htlcpubkey-  , derive_remote_htlcpubkey-  , derive_local_delayedpubkey-  , derive_remote_delayedpubkey--    -- * Revocation key derivation   , derive_revocationpubkey+  , CommitmentKeys(..)+  , derive_commitment_keys+  , derive_commitment_keys' -    -- * Per-commitment secret generation-  , generate_from_seed-  , derive_secret+    -- * Private keys+  , derive_privkey+  , derive_revocationprivkey -    -- * Per-commitment secret storage+    -- * Per-commitment secrets+  , generate_from_seed   , SecretStore-  , empty_store+  , empty_secret_store   , insert_secret   , derive_old_secret+  , secret_store+  , un_secret_store      -- * Commitment number obscuring   , obscured_commitment_number   ) where -import Data.Bits ((.&.), xor, shiftL, testBit, complementBit)+import Control.DeepSeq (NFData(..))+import qualified Crypto.Curve.Secp256k1 as S+import qualified Crypto.Hash.SHA256 as SHA256+import qualified Data.Choice as C+import Data.Bits ((.&.), (.|.), complement, complementBit, shiftL, shiftR,+                  testBit, xor) import qualified Data.ByteString as BS import Data.Word (Word64) import GHC.Generics (Generic)-import qualified Crypto.Curve.Secp256k1 as S-import qualified Crypto.Hash.SHA256 as SHA256+import Lightning.Protocol.BOLT1 (Point, PerCommitmentSecret)+import qualified Lightning.Protocol.BOLT1 as BOLT1 import Lightning.Protocol.BOLT3.Types+import qualified Numeric.Montgomery.Secp256k1.Scalar as SC --- Per-commitment point derivation ----------------------------------------+-- points --------------------------------------------------------------------- --- | Derive the per-commitment point from a per-commitment secret.+-- Serialize a secp256k1 point, failing on the point at infinity.+to_point :: S.Projective -> Maybe Point+to_point p+  | p == S._CURVE_ZERO = Nothing+  | otherwise          = case BOLT1.point (S.serialize_point p) of+      Just pt -> Just pt+      Nothing -> internal_error+{-# INLINE to_point #-}++-- per-commitment points ------------------------------------------------------++-- | Derive the per-commitment point from a per-commitment secret: ----- @per_commitment_point = per_commitment_secret * G@+--   @per_commitment_point = per_commitment_secret * G@ ----- >>> let secret = PerCommitmentSecret (BS.replicate 32 0x01)--- >>> derive_per_commitment_point secret--- Just (PerCommitmentPoint ...)+--   Fails if the secret is not a valid secp256k1 secret key.+--+--   >>> let Just s = BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0])+--   >>> let Just (PerCommitmentPoint p) = derive_per_commitment_point s+--   >>> B16.encode (BOLT1.un_point p)+--   "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486" derive_per_commitment_point   :: PerCommitmentSecret   -> Maybe PerCommitmentPoint-derive_per_commitment_point (PerCommitmentSecret sec) = do-  sk <- S.parse_int256 sec+derive_per_commitment_point s = do+  sk <- S.parse_int256 (BOLT1.un_per_commitment_secret s)   pk <- S.derive_pub sk-  let !bs = S.serialize_point pk-  pure $! PerCommitmentPoint (Point bs)-{-# INLINE derive_per_commitment_point #-}+  PerCommitmentPoint <$> to_point pk --- Key derivation ---------------------------------------------------------+-- | As 'derive_per_commitment_point', but uses a precomputed+--   'S.Context' to speed up the scalar multiplication.+derive_per_commitment_point'+  :: S.Context+  -> PerCommitmentSecret+  -> Maybe PerCommitmentPoint+derive_per_commitment_point' tex s = do+  sk <- S.parse_int256 (BOLT1.un_per_commitment_secret s)+  pk <- S.derive_pub' tex sk+  PerCommitmentPoint <$> to_point pk --- | Derive a pubkey from a basepoint and per-commitment point.+-- public keys ----------------------------------------------------------------++-- | Derive a per-commitment public key from a basepoint: ----- @pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G@+--   @pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G@ ----- This is the general derivation formula used for localpubkey,--- local_htlcpubkey, remote_htlcpubkey, local_delayedpubkey, and--- remote_delayedpubkey.+--   This gives @local_htlcpubkey@, @remote_htlcpubkey@,+--   @local_delayedpubkey@ and the like, depending on the basepoint;+--   'derive_commitment_keys' derives all of a commitment's keys at+--   once. ----- >>> derive_pubkey basepoint per_commitment_point--- Just (Pubkey ...)-derive_pubkey-  :: Point             -- ^ basepoint-  -> PerCommitmentPoint -- ^ per_commitment_point-  -> Maybe Pubkey-derive_pubkey (Point basepointBs) (PerCommitmentPoint (Point pcpBs)) = do-  basepoint <- S.parse_point basepointBs-  -- SHA256(per_commitment_point || basepoint)-  let !h = SHA256.hash (pcpBs <> basepointBs)-  -- Treat hash as scalar and multiply by G-  tweak <- S.parse_int256 h-  tweakPoint <- S.derive_pub tweak-  -- Add basepoint + tweak*G-  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.+--   Fails if the basepoint is not on the curve. ----- >>> derive_localpubkey payment_basepoint per_commitment_point--- Just (LocalPubkey ...)-derive_localpubkey-  :: PaymentBasepoint-  -> PerCommitmentPoint-  -> Maybe LocalPubkey-derive_localpubkey (PaymentBasepoint pt) pcp =-  LocalPubkey <$> derive_pubkey pt pcp-{-# INLINE derive_localpubkey #-}+--   >>> let Just s = BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0])+--   >>> let Just pcp = derive_per_commitment_point s+--   >>> let Just b = BOLT1.per_commitment_secret (BS.pack [0 .. 31])+--   >>> let Just (PerCommitmentPoint bp) = derive_per_commitment_point b+--   >>> fmap (B16.encode . BOLT1.un_point) (derive_pubkey bp pcp)+--   Just "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"+derive_pubkey :: Point -> PerCommitmentPoint -> Maybe Point+derive_pubkey = derive_pubkey_with mul_g+{-# INLINE derive_pubkey #-} --- | Derive local_htlcpubkey from htlc_basepoint and per_commitment_point.------ >>> derive_local_htlcpubkey htlc_basepoint per_commitment_point--- Just (LocalHtlcPubkey ...)-derive_local_htlcpubkey-  :: HtlcBasepoint-  -> PerCommitmentPoint-  -> Maybe LocalHtlcPubkey-derive_local_htlcpubkey (HtlcBasepoint pt) pcp =-  LocalHtlcPubkey <$> derive_pubkey pt pcp-{-# INLINE derive_local_htlcpubkey #-}+-- Multiply the generator by a 32-byte scalar.+mul_g :: BS.ByteString -> Maybe S.Projective+mul_g bs = S.derive_pub =<< S.parse_int256 bs+{-# INLINE mul_g #-} --- | Derive remote_htlcpubkey from htlc_basepoint and per_commitment_point.------ >>> derive_remote_htlcpubkey htlc_basepoint per_commitment_point--- Just (RemoteHtlcPubkey ...)-derive_remote_htlcpubkey-  :: HtlcBasepoint-  -> PerCommitmentPoint-  -> Maybe RemoteHtlcPubkey-derive_remote_htlcpubkey (HtlcBasepoint pt) pcp =-  RemoteHtlcPubkey <$> derive_pubkey pt pcp-{-# INLINE derive_remote_htlcpubkey #-}+-- As 'mul_g', using a precomputed context.+mul_g' :: S.Context -> BS.ByteString -> Maybe S.Projective+mul_g' tex bs = S.derive_pub' tex =<< S.parse_int256 bs+{-# INLINE mul_g' #-} --- | Derive local_delayedpubkey from delayed_payment_basepoint and--- per_commitment_point.------ >>> derive_local_delayedpubkey delayed_payment_basepoint per_commitment_point--- Just (LocalDelayedPubkey ...)-derive_local_delayedpubkey-  :: DelayedPaymentBasepoint+derive_pubkey_with+  :: (BS.ByteString -> Maybe S.Projective)+  -> Point   -> PerCommitmentPoint-  -> Maybe LocalDelayedPubkey-derive_local_delayedpubkey (DelayedPaymentBasepoint pt) pcp =-  LocalDelayedPubkey <$> derive_pubkey pt pcp-{-# INLINE derive_local_delayedpubkey #-}+  -> Maybe Point+derive_pubkey_with mul bp (PerCommitmentPoint pcp) = do+  let !bp_bs = BOLT1.un_point bp+  base <- S.parse_point bp_bs+  t    <- mul (SHA256.hash (BOLT1.un_point pcp <> bp_bs))+  to_point (S.add base t)+{-# INLINE derive_pubkey_with #-} --- | Derive remote_delayedpubkey from delayed_payment_basepoint and--- per_commitment_point.+-- | Derive a commitment's @revocationpubkey@ from the revocation+--   basepoint of the party that will hold the revocation key, and the+--   commitment owner's per-commitment point: ----- >>> derive_remote_delayedpubkey delayed_payment_basepoint pcp--- Just (RemoteDelayedPubkey ...)-derive_remote_delayedpubkey-  :: DelayedPaymentBasepoint-  -> PerCommitmentPoint-  -> Maybe RemoteDelayedPubkey-derive_remote_delayedpubkey (DelayedPaymentBasepoint pt) pcp =-  RemoteDelayedPubkey <$> derive_pubkey pt pcp-{-# INLINE derive_remote_delayedpubkey #-}---- Revocation key derivation -------------------------------------------------- | Derive revocationpubkey from revocation_basepoint and--- per_commitment_point.+--   @+--   revocationpubkey =+--       revocation_basepoint+--         * SHA256(revocation_basepoint || per_commitment_point)+--     + per_commitment_point+--         * SHA256(per_commitment_point || revocation_basepoint)+--   @ ----- @--- revocationpubkey = revocation_basepoint---                      * SHA256(revocation_basepoint || per_commitment_point)---                  + per_commitment_point---                      * SHA256(per_commitment_point || revocation_basepoint)--- @+--   Fails if either point is not on the curve. ----- >>> derive_revocationpubkey revocation_basepoint per_commitment_point--- Just (RevocationPubkey ...)+--   >>> let Just s = BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0])+--   >>> let Just pcp = derive_per_commitment_point s+--   >>> let Just b = BOLT1.per_commitment_secret (BS.pack [0 .. 31])+--   >>> let Just (PerCommitmentPoint p) = derive_per_commitment_point b+--   >>> let Just r = derive_revocationpubkey (RevocationBasepoint p) pcp+--   >>> (\(RevocationPubkey k) -> B16.encode (BOLT1.un_point k)) r+--   "02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0" derive_revocationpubkey   :: RevocationBasepoint   -> PerCommitmentPoint   -> Maybe RevocationPubkey derive_revocationpubkey-  (RevocationBasepoint (Point rbpBs))-  (PerCommitmentPoint (Point pcpBs)) = do-    rbp <- S.parse_point rbpBs-    pcp <- S.parse_point pcpBs-    -- SHA256(revocation_basepoint || per_commitment_point)-    let !h1 = SHA256.hash (rbpBs <> pcpBs)-    -- SHA256(per_commitment_point || revocation_basepoint)-    let !h2 = SHA256.hash (pcpBs <> rbpBs)-    -- Multiply points by their respective scalars-    s1 <- S.parse_int256 h1-    s2 <- S.parse_int256 h2-    p1 <- S.mul rbp s1  -- revocation_basepoint * h1-    p2 <- S.mul pcp s2  -- per_commitment_point * h2-    -- Add the two points-    let !result = S.add p1 p2-        !bs = S.serialize_point result-    pure $! RevocationPubkey (Pubkey bs)-{-# INLINE derive_revocationpubkey #-}+  (RevocationBasepoint rbp)+  (PerCommitmentPoint pcp) = do+    let !rbp_bs = BOLT1.un_point rbp+        !pcp_bs = BOLT1.un_point pcp+    r  <- S.parse_point rbp_bs+    p  <- S.parse_point pcp_bs+    s1 <- S.parse_int256 (SHA256.hash (rbp_bs <> pcp_bs))+    s2 <- S.parse_int256 (SHA256.hash (pcp_bs <> rbp_bs))+    p1 <- S.mul r s1+    p2 <- S.mul p s2+    RevocationPubkey <$> to_point (S.add p1 p2) --- Per-commitment secret generation ---------------------------------------+-- | The keys a commitment transaction's scripts use.+data CommitmentKeys = CommitmentKeys+  { ck_revocation_pubkey :: !RevocationPubkey+  , ck_local_delayed     :: !LocalDelayedPubkey+  , ck_local_htlc        :: !LocalHtlcPubkey+  , ck_remote_htlc       :: !RemoteHtlcPubkey+  , ck_remote_payment    :: !RemotePubkey+  , ck_local_funding     :: !FundingPubkey+  , ck_remote_funding    :: !FundingPubkey+  } deriving (Eq, Show, Generic) --- | Generate the I'th per-commitment secret from a seed.------ Implements the generate_from_seed algorithm from BOLT #3:+instance NFData CommitmentKeys++-- | Derive the keys for a commitment transaction from both parties'+--   basepoints and funding pubkeys, and the commitment owner's+--   per-commitment point. ----- @--- generate_from_seed(seed, I):---     P = seed---     for B in 47 down to 0:---         if B set in I:---             flip(B) in P---             P = SHA256(P)---     return P--- @+--   The owner is the "local" party: its delayed and HTLC basepoints+--   give @local_delayedpubkey@ and @local_htlcpubkey@, while the other+--   party's revocation and HTLC basepoints give @revocationpubkey@ and+--   @remote_htlcpubkey@. As every supported 'CommitmentFormat' uses+--   @option_static_remotekey@, @remotepubkey@ is the other party's+--   @payment_basepoint@. ----- >>> generate_from_seed seed 281474976710655--- <32-byte secret>-generate_from_seed-  :: BS.ByteString  -- ^ seed (32 bytes)-  -> Word64         -- ^ index I (max 2^48 - 1)-  -> BS.ByteString  -- ^ per-commitment secret (32 bytes)-generate_from_seed seed idx = go 47 seed where-  go :: Int -> BS.ByteString -> BS.ByteString-  go !b !p-    | b < 0 = p-    | testBit idx b =-        let !p' = flip_bit b p-            !p'' = SHA256.hash p'-        in  go (b - 1) p''-    | otherwise = go (b - 1) p-{-# INLINE generate_from_seed #-}+--   Fails if a basepoint it derives a key from, or the per-commitment+--   point, is not on the curve.+derive_commitment_keys+  :: Basepoints          -- ^ the owner's basepoints+  -> FundingPubkey       -- ^ the owner's funding pubkey+  -> Basepoints          -- ^ the other party's basepoints+  -> FundingPubkey       -- ^ the other party's funding pubkey+  -> PerCommitmentPoint  -- ^ the owner's per-commitment point+  -> Maybe CommitmentKeys+derive_commitment_keys = derive_commitment_keys_with mul_g --- | Derive a secret from a base secret.+-- | As 'derive_commitment_keys', but uses a precomputed 'S.Context'+--   to speed up the scalar multiplications.+derive_commitment_keys'+  :: S.Context+  -> Basepoints+  -> FundingPubkey+  -> Basepoints+  -> FundingPubkey+  -> PerCommitmentPoint+  -> Maybe CommitmentKeys+derive_commitment_keys' tex = derive_commitment_keys_with (mul_g' tex)++derive_commitment_keys_with+  :: (BS.ByteString -> Maybe S.Projective)+  -> Basepoints+  -> FundingPubkey+  -> Basepoints+  -> FundingPubkey+  -> PerCommitmentPoint+  -> Maybe CommitmentKeys+derive_commitment_keys_with mul local local_fund remote remote_fund pcp = do+  let DelayedPaymentBasepoint local_delayed_bp = bp_delayed_payment local+      HtlcBasepoint local_htlc_bp = bp_htlc local+      HtlcBasepoint remote_htlc_bp = bp_htlc remote+      PaymentBasepoint remote_payment_bp = bp_payment remote+  revocation   <- derive_revocationpubkey (bp_revocation remote) pcp+  delayed      <- derive_pubkey_with mul local_delayed_bp pcp+  local_htlc   <- derive_pubkey_with mul local_htlc_bp pcp+  remote_htlc  <- derive_pubkey_with mul remote_htlc_bp pcp+  pure CommitmentKeys+    { ck_revocation_pubkey = revocation+    , ck_local_delayed     = LocalDelayedPubkey delayed+    , ck_local_htlc        = LocalHtlcPubkey local_htlc+    , ck_remote_htlc       = RemoteHtlcPubkey remote_htlc+    , ck_remote_payment    = RemotePubkey remote_payment_bp+    , ck_local_funding     = local_fund+    , ck_remote_funding    = remote_fund+    }++-- private keys ---------------------------------------------------------------++-- | Derive the private key for a per-commitment public key from its+--   basepoint secret: ----- This is a generalization of generate_from_seed used for efficient--- secret storage. Given a base secret whose index has bits..47 the same--- as target index I, derive the I'th secret.+--   @privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)@ ----- @--- derive_secret(base, bits, I):---     P = base---     for B in bits - 1 down to 0:---         if B set in I:---             flip(B) in P---             P = SHA256(P)---     return P--- @-derive_secret-  :: BS.ByteString  -- ^ base secret-  -> Int            -- ^ bits (number of trailing bits to process)-  -> Word64         -- ^ target index I-  -> BS.ByteString  -- ^ derived secret-derive_secret base bits idx = go (bits - 1) base where-  go :: Int -> BS.ByteString -> BS.ByteString-  go !b !p-    | b < 0 = p-    | testBit idx b =-        let !p' = flip_bit b p-            !p'' = SHA256.hash p'-        in  go (b - 1) p''-    | otherwise = go (b - 1) p-{-# INLINE derive_secret #-}+--   where @basepoint = basepoint_secret * G@. The result is the secret+--   key for @'derive_pubkey' basepoint per_commitment_point@.+--+--   The arithmetic on the secret is constant-time. Fails (with+--   negligible probability) if the result is zero.+--+--   >>> let Just s = BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0])+--   >>> let Just pcp = derive_per_commitment_point s+--   >>> let Just k = seckey (BS.pack [0 .. 31])+--   >>> fmap (B16.encode . un_seckey) (derive_privkey k pcp)+--   Just "cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f"+derive_privkey :: Seckey -> PerCommitmentPoint -> Maybe Seckey+derive_privkey (Seckey sk_bs) (PerCommitmentPoint pcp) = do+  sk <- S.parse_int256 sk_bs+  bp <- to_point =<< S.derive_pub sk+  let !h = SHA256.hash (BOLT1.un_point pcp <> BOLT1.un_point bp)+  to_seckey (SC.to sk + SC.to (S.unsafe_roll32 h)) --- | Flip bit B in a 32-byte bytestring.+-- | Derive a commitment's @revocationprivkey@ from the revocation+--   basepoint secret and the commitment's (revealed) per-commitment+--   secret: ----- "flip(B)" alternates the (B mod 8) bit of the (B div 8) byte.+--   @+--   revocationprivkey =+--       revocation_basepoint_secret+--         * SHA256(revocation_basepoint || per_commitment_point)+--     + per_commitment_secret+--         * SHA256(per_commitment_point || revocation_basepoint)+--   @+--+--   The result is the secret key for the 'derive_revocationpubkey'+--   of the corresponding points.+--+--   The arithmetic on the secrets is constant-time. Fails if the+--   per-commitment secret is not a valid secret key, or (with+--   negligible probability) if the result is zero.+--+--   >>> let Just s = BOLT1.per_commitment_secret (BS.pack [31, 30 .. 0])+--   >>> let Just k = seckey (BS.pack [0 .. 31])+--   >>> fmap (B16.encode . un_seckey) (derive_revocationprivkey k s)+--   Just "d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110"+derive_revocationprivkey+  :: Seckey               -- ^ revocation basepoint secret+  -> PerCommitmentSecret  -- ^ per-commitment secret+  -> Maybe Seckey+derive_revocationprivkey (Seckey rbs_bs) pcs = do+  let !pcs_bs = BOLT1.un_per_commitment_secret pcs+  rbs <- S.parse_int256 rbs_bs+  ps  <- S.parse_int256 pcs_bs+  rbp <- to_point =<< S.derive_pub rbs+  pcp <- to_point =<< S.derive_pub ps+  let !rbp_bs = BOLT1.un_point rbp+      !pcp_bs = BOLT1.un_point pcp+      !h1 = SC.to (S.unsafe_roll32 (SHA256.hash (rbp_bs <> pcp_bs)))+      !h2 = SC.to (S.unsafe_roll32 (SHA256.hash (pcp_bs <> rbp_bs)))+  to_seckey (SC.to rbs * h1 + SC.to ps * h2)++-- The secret key for a nonzero scalar. Reduction, arithmetic and the+-- zero check are constant-time (ppad-fixed).+to_seckey :: SC.Montgomery -> Maybe Seckey+to_seckey r+  | C.decide (SC.eq r 0) = Nothing+  | otherwise            = Just (Seckey (S.unroll32 (SC.retr r)))++-- per-commitment secrets -----------------------------------------------------++-- Wrap a 32-byte value as a per-commitment secret.+to_secret :: BS.ByteString -> PerCommitmentSecret+to_secret bs = case BOLT1.per_commitment_secret bs of+  Just s  -> s+  Nothing -> internal_error+{-# INLINE to_secret #-}++-- Flip bit b (< 256) of a 32-byte value: the (b mod 8) bit of the+-- (b div 8) byte. flip_bit :: Int -> BS.ByteString -> BS.ByteString flip_bit b bs =-  let !byteIdx = b `div` 8-      !bitIdx = b `mod` 8-      !len = BS.length bs-  in  if byteIdx >= len-      then bs-      else-        let !prefix = BS.take byteIdx bs-            !byte = BS.index bs byteIdx-            !byte' = complementBit byte bitIdx-            !suffix = BS.drop (byteIdx + 1) bs-        in  prefix <> BS.singleton byte' <> suffix+  let !(pre, post) = BS.splitAt (b `shiftR` 3) bs+  in  case BS.uncons post of+        Just (h, t) -> pre <> BS.cons (complementBit h (b .&. 7)) t+        Nothing     -> bs {-# INLINE flip_bit #-} --- Per-commitment secret storage ------------------------------------------+-- The spec's derive_secret: the secret for index i, from a base+-- secret whose index agrees with i in bits 47 down to 'bits'.+derive_secret :: BS.ByteString -> Int -> Word64 -> BS.ByteString+derive_secret base bits i = go (bits - 1) base where+  go !b !p+    | b < 0       = p+    | testBit i b = go (b - 1) (SHA256.hash (flip_bit b p))+    | otherwise   = go (b - 1) p --- | Entry in the secret store: (bucket, index, secret).-data SecretEntry = SecretEntry-  { se_bucket :: {-# UNPACK #-} !Int-  , se_index  :: {-# UNPACK #-} !Word64-  , se_secret :: !BS.ByteString-  } deriving (Eq, Show, Generic)+-- | Generate the per-commitment secret with a given index from a seed,+--   per BOLT #3's @generate_from_seed@.+--+--   >>> let Just s = seed (BS.replicate 32 0xff)+--   >>> let Just i = secret_index 281474976710655+--   >>> B16.encode (BOLT1.un_per_commitment_secret (generate_from_seed s i))+--   "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+generate_from_seed :: Seed -> SecretIndex -> PerCommitmentSecret+generate_from_seed (Seed s) (SecretIndex i) = to_secret (derive_secret s 48 i) --- | Compact storage for per-commitment secrets.+-- | Compact storage for the per-commitment secrets received from a+--   peer, per BOLT #3's "Efficient Per-commitment Secret Storage". ----- Stores up to 49 (value, index) pairs, allowing efficient derivation--- of any previously-received secret. This is possible because for a--- given secret on a 2^X boundary, all secrets up to the next 2^X--- boundary can be derived from it.-newtype SecretStore = SecretStore { unSecretStore :: [SecretEntry] }-  deriving (Eq, Show, Generic)+--   Holds at most 49 secrets, from which every secret received so far+--   can be derived. This is secret material: its 'Show' instance is+--   redacted, and it has no 'Eq' instance.+newtype SecretStore = SecretStore [Entry] --- | Empty secret store.-empty_store :: SecretStore-empty_store = SecretStore []-{-# INLINE empty_store #-}+-- A stored secret: its bucket (the number of trailing zeros of its+-- index), index, and value. Kept in ascending bucket order.+data Entry = Entry+  {-# UNPACK #-} !Int+  {-# UNPACK #-} !Word64+  !BS.ByteString --- | Determine which bucket to store a secret in based on its index.------ Counts trailing zeros in the index. Returns 0-47 for normal indices,--- or 48 if index is 0 (the seed).-where_to_put_secret :: Word64 -> Int-where_to_put_secret idx = go 0 where+instance Show SecretStore where+  showsPrec d _ = showParen (d > 10) $+    showString "SecretStore <redacted>"++instance NFData SecretStore where+  rnf (SecretStore es) = rnf_entries es where+    rnf_entries [] = ()+    rnf_entries (Entry _ _ s : rest) = rnf s `seq` rnf_entries rest++-- | The empty 'SecretStore'.+empty_secret_store :: SecretStore+empty_secret_store = SecretStore []++-- The bucket for an index: its number of trailing zeros (48 for 0).+bucket_of :: Word64 -> Int+bucket_of i = go 0 where   go !b-    | b > 47 = 48  -- index 0, this is the seed-    | testBit idx b = b-    | otherwise = go (b + 1)-{-# INLINE where_to_put_secret #-}+    | b > 47      = 48+    | testBit i b = b+    | otherwise   = go (b + 1) --- | Insert a secret into the store, validating against existing secrets.+-- Whether a secret at index j in bucket b can derive index i.+can_derive :: Int -> Word64 -> Word64 -> Bool+can_derive b j i = i .&. complement ((1 `shiftL` b) - 1) == j+{-# INLINE can_derive #-}++-- | Add a newly received per-commitment secret to the store, per+--   BOLT #3's @insert_secret@. ----- Returns Nothing if the secret doesn't derive correctly from known--- secrets (indicating the secrets weren't generated from the same seed).+--   Fails if a stored secret cannot be derived from the new one, i.e.+--   if the secrets were not generated from the same seed (or were not+--   received in order). ----- >>> insert_secret secret 281474976710655 empty_store--- Just (SecretStore ...)+--   >>> let Just s = seed (BS.replicate 32 0xff)+--   >>> let Just i = secret_index 281474976710655+--   >>> let secret = generate_from_seed s i+--   >>> let Just st = insert_secret secret i empty_secret_store+--   >>> fmap (== secret) (derive_old_secret i st)+--   Just True insert_secret-  :: BS.ByteString  -- ^ secret (32 bytes)-  -> Word64         -- ^ index-  -> SecretStore    -- ^ current store+  :: PerCommitmentSecret+  -> SecretIndex+  -> SecretStore   -> Maybe SecretStore-insert_secret secret idx (SecretStore known) = do-  let !bucket = where_to_put_secret idx-  -- Validate: for each bucket < this bucket, check we can derive-  validated <- validateBuckets bucket known-  if validated-    then-      -- Remove entries at bucket >= this bucket, then insert-      let !known' = filter (\e -> se_bucket e < bucket) known-          !entry = SecretEntry bucket idx secret-      in  pure $! SecretStore (known' ++ [entry])-    else Nothing+insert_secret pcs (SecretIndex i) (SecretStore known)+  | all derives known = Just $! SecretStore (put known)+  | otherwise         = Nothing   where-    validateBuckets :: Int -> [SecretEntry] -> Maybe Bool-    validateBuckets b entries = go entries where-      go [] = Just True-      go (SecretEntry entryBucket knownIdx knownSecret : rest)-        | entryBucket >= b = go rest  -- skip entries at higher buckets-        | otherwise =-            -- Check if we can derive the known secret from the new one-            let !derived = derive_secret secret b knownIdx-            in  if derived == knownSecret-                then go rest-                else Nothing-{-# INLINE insert_secret #-}+    !s = BOLT1.un_per_commitment_secret pcs+    !b = bucket_of i+    !e = Entry b i s --- | Derive a previously-received secret from the store.------ Iterates over known secrets to find one whose index is a prefix of--- the target index, then derives the target secret from it.------ >>> derive_old_secret 281474976710654 store--- Just <32-byte secret>+    -- every secret in a lower bucket must derive from the new one+    derives (Entry kb ki ks)+      | kb < b    = to_secret (derive_secret s b ki) == to_secret ks+      | otherwise = True++    -- replace this bucket's entry, keeping ascending bucket order+    put [] = [e]+    put (k@(Entry kb _ _) : ks) = case compare kb b of+      LT -> k : put ks+      EQ -> e : ks+      GT -> e : k : ks++-- | Derive a previously received per-commitment secret from the+--   store. Fails if the index has not been received. derive_old_secret-  :: Word64       -- ^ target index-  -> SecretStore  -- ^ store-  -> Maybe BS.ByteString-derive_old_secret targetIdx (SecretStore known) = go known where-  go :: [SecretEntry] -> Maybe BS.ByteString+  :: SecretIndex+  -> SecretStore+  -> Maybe PerCommitmentSecret+derive_old_secret (SecretIndex i) (SecretStore known) = go known where   go [] = Nothing-  go (SecretEntry bucket knownIdx knownSecret : rest) =-    -- Mask off the non-zero prefix of the index using the entry's bucket-    let !mask = complement ((1 `shiftL` bucket) - 1)-    in  if (targetIdx .&. mask) == knownIdx-        then Just $! derive_secret knownSecret bucket targetIdx-        else go rest+  go (Entry b j s : rest)+    | can_derive b j i = Just $! to_secret (derive_secret s b i)+    | otherwise        = go rest -  complement :: Word64 -> Word64-  complement x = x `xor` 0xFFFFFFFFFFFFFFFF-{-# INLINE derive_old_secret #-}+-- | Rebuild a 'SecretStore' from its entries, e.g. as produced by+--   'un_secret_store'.+--+--   Fails if two entries share a bucket (i.e. their indices have the+--   same number of trailing zeros), or if an entry contradicts another+--   from which it can be derived.+secret_store+  :: [(SecretIndex, PerCommitmentSecret)]+  -> Maybe SecretStore+secret_store = go [] where+  go acc [] = Just (SecretStore acc)+  go acc ((SecretIndex i, pcs) : rest) = do+    let !e = Entry (bucket_of i) i (BOLT1.un_per_commitment_secret pcs)+    acc' <- place e acc+    go acc' rest --- Commitment number obscuring --------------------------------------------+  -- insert in ascending bucket order, checking consistency with the+  -- entries already placed+  place e@(Entry b i s) es+    | all (consistent e) es = put es+    | otherwise             = Nothing+    where+      put [] = Just [e]+      put (k@(Entry kb _ _) : ks) = case compare kb b of+        LT -> (k :) <$> put ks+        EQ -> Nothing+        GT -> Just (e : k : ks)+      consistent _ (Entry kb ki ks)+        | kb > b && can_derive kb ki i =+            to_secret (derive_secret ks kb i) == to_secret s+        | kb < b && can_derive b i ki =+            to_secret (derive_secret s b ki) == to_secret ks+        | otherwise = True --- | Calculate the obscured commitment number.+-- | The entries of a 'SecretStore', for persistence; see+--   'secret_store'.+un_secret_store :: SecretStore -> [(SecretIndex, PerCommitmentSecret)]+un_secret_store (SecretStore es) =+  [ (SecretIndex i, to_secret s) | Entry _ i s <- es ]++-- commitment number obscuring ------------------------------------------------++-- | Obscure a commitment number, by XOR with the lower 48 bits of ----- The 48-bit commitment number is obscured by XOR with the lower 48 bits--- of SHA256(payment_basepoint from open_channel---         || payment_basepoint from accept_channel).+--   @SHA256(payment_basepoint from open_channel+--           || payment_basepoint from accept_channel)@ ----- >>> obscured_commitment_number local_payment_bp remote_payment_bp cn--- <obscured value>+--   As XOR is an involution, applying this to an obscured number+--   recovers the commitment number. ('build_commitment_tx' obscures+--   the commitment number itself.)+--+--   >>> let pay h = fmap PaymentBasepoint (BOLT1.point =<< B16.decode h)+--   >>> let o1 = "034f355bdcb7cc0af728ef3cceb9615d9068"+--   >>> let Just o = pay (o1 <> "4bb5b2ca5f859ab0f0b704075871aa")+--   >>> let a1 = "032c0b7cf95324a07d05398b240174dc0c2b"+--   >>> let Just a = pay (a1 <> "e444d96b159aa6c7f7b1e668680991")+--   >>> fmap (obscured_commitment_number o a) (commitment_number 42)+--   Just 48035859142974 obscured_commitment_number   :: PaymentBasepoint   -- ^ opener's payment_basepoint   -> PaymentBasepoint   -- ^ accepter's payment_basepoint-  -> CommitmentNumber   -- ^ commitment number (48-bit)-  -> Word64             -- ^ obscured commitment number+  -> CommitmentNumber+  -> Word64 obscured_commitment_number-  (PaymentBasepoint (Point openerBs))-  (PaymentBasepoint (Point accepterBs))-  (CommitmentNumber cn) =-    let !h = SHA256.hash (openerBs <> accepterBs)-        -- Extract lower 48 bits (6 bytes) from the hash-        !lower48 = extractLower48 h-        -- Mask commitment number to 48 bits-        !cn48 = cn .&. 0xFFFFFFFFFFFF-    in  cn48 `xor` lower48-{-# INLINE obscured_commitment_number #-}---- | Extract lower 48 bits from a 32-byte hash.------ Takes bytes 26-31 (last 6 bytes) and interprets as big-endian Word64.-extractLower48 :: BS.ByteString -> Word64-extractLower48 h =-  let !b0 = fromIntegral (BS.index h 26) `shiftL` 40-      !b1 = fromIntegral (BS.index h 27) `shiftL` 32-      !b2 = fromIntegral (BS.index h 28) `shiftL` 24-      !b3 = fromIntegral (BS.index h 29) `shiftL` 16-      !b4 = fromIntegral (BS.index h 30) `shiftL` 8-      !b5 = fromIntegral (BS.index h 31)-  in  b0 + b1 + b2 + b3 + b4 + b5-{-# INLINE extractLower48 #-}+  (PaymentBasepoint opener)+  (PaymentBasepoint accepter)+  cn =+    let !h = SHA256.hash (BOLT1.un_point opener <> BOLT1.un_point accepter)+        !mask = BS.foldl' (\acc x -> (acc `shiftL` 8) .|. fromIntegral x) 0+                  (BS.drop 26 h)+    in  un_commitment_number cn `xor` mask
lib/Lightning/Protocol/BOLT3/Scripts.hs view
@@ -1,6 +1,5 @@-{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-}  -- | -- Module: Lightning.Protocol.BOLT3.Scripts@@ -8,17 +7,7 @@ -- License: MIT -- Maintainer: Jared Tobin <jared@ppad.tech> ----- Script templates for BOLT #3 transaction outputs.------ Includes witness scripts for:------ * Funding output (2-of-2 multisig)--- * to_local output (revocable with CSV delay)--- * to_remote output (P2WPKH or anchored)--- * Anchor outputs--- * Offered HTLC outputs--- * Received HTLC outputs--- * HTLC-timeout/success output (revocable with delay)+-- Scripts and witnesses for BOLT #3 transaction outputs.  module Lightning.Protocol.BOLT3.Scripts (     -- * Funding output@@ -31,7 +20,8 @@   , to_local_witness_revoke      -- * to_remote output-  , to_remote_script+  , to_remote_witness_script+  , to_remote_script_pubkey   , to_remote_witness      -- * Anchor outputs@@ -39,639 +29,475 @@   , anchor_witness_owner   , anchor_witness_anyone -    -- * Offered HTLC output+    -- * Offered HTLC outputs   , offered_htlc_script   , offered_htlc_witness_preimage   , offered_htlc_witness_revoke -    -- * Received HTLC output+    -- * Received HTLC outputs   , received_htlc_script   , received_htlc_witness_timeout   , received_htlc_witness_revoke -    -- * HTLC-timeout/success output (same as to_local)-  , htlc_output_script-  , htlc_output_witness_spend-  , htlc_output_witness_revoke+    -- * HTLC transaction inputs+  , htlc_success_witness+  , htlc_timeout_witness+  , remote_htlc_sighash -    -- * P2WSH helpers+    -- * P2WSH   , to_p2wsh-  , witness_script_hash   ) where +import Bitcoin.Prim.Tx (Witness(..))+import Bitcoin.Prim.Tx.Sighash (SighashType(..))+import qualified Crypto.Hash.RIPEMD160 as RIPEMD160+import qualified Crypto.Hash.SHA256 as SHA256 import Data.Bits ((.&.), shiftR)-import Data.Word (Word8, Word16, Word32) import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as BSB import qualified Data.ByteString.Lazy as BSL-import qualified Crypto.Hash.SHA256 as SHA256-import qualified Crypto.Hash.RIPEMD160 as RIPEMD160+import Data.Word (Word8, Word16, Word32)+import Lightning.Protocol.BOLT1 (Point, PaymentPreimage)+import qualified Lightning.Protocol.BOLT1 as BOLT1 import Lightning.Protocol.BOLT3.Types --- opcodes ------------------------------------------------------------------------- | OP_0 / OP_FALSE (0x00)-op_0 :: Word8-op_0 = 0x00---- | OP_PUSHDATA for 1-75 bytes just uses the length as opcode--- For data <=75 bytes, opcode is just the length+-- opcodes -------------------------------------------------------------------- --- | OP_IF (0x63)-op_if :: Word8-op_if = 0x63+op_0, op_1, op_2, op_16 :: Word8+op_0  = 0x00+op_1  = 0x51+op_2  = 0x52+op_16 = 0x60 --- | OP_NOTIF (0x64)-op_notif :: Word8+op_if, op_notif, op_else, op_endif, op_drop, op_dup, op_ifdup :: Word8+op_if    = 0x63 op_notif = 0x64---- | OP_ELSE (0x67)-op_else :: Word8-op_else = 0x67---- | OP_ENDIF (0x68)-op_endif :: Word8+op_else  = 0x67 op_endif = 0x68---- | OP_DROP (0x75)-op_drop :: Word8-op_drop = 0x75---- | OP_DUP (0x76)-op_dup :: Word8-op_dup = 0x76---- | OP_SWAP (0x7c)-op_swap :: Word8-op_swap = 0x7c---- | OP_SIZE (0x82)-op_size :: Word8-op_size = 0x82---- | OP_EQUAL (0x87)-op_equal :: Word8-op_equal = 0x87---- | OP_EQUALVERIFY (0x88)-op_equalverify :: Word8-op_equalverify = 0x88---- | OP_IFDUP (0x73)-op_ifdup :: Word8 op_ifdup = 0x73---- | OP_HASH160 (0xa9)-op_hash160 :: Word8-op_hash160 = 0xa9+op_drop  = 0x75+op_dup   = 0x76 --- | OP_CHECKSIG (0xac)-op_checksig :: Word8-op_checksig = 0xac+op_swap, op_size, op_equal, op_equalverify, op_hash160 :: Word8+op_swap        = 0x7c+op_size        = 0x82+op_equal       = 0x87+op_equalverify = 0x88+op_hash160     = 0xa9 --- | OP_CHECKSIGVERIFY (0xad)-op_checksigverify :: Word8+op_checksig, op_checksigverify, op_checkmultisig :: Word8+op_checksig       = 0xac op_checksigverify = 0xad---- | OP_CHECKMULTISIG (0xae)-op_checkmultisig :: Word8-op_checkmultisig = 0xae+op_checkmultisig  = 0xae --- | OP_CHECKLOCKTIMEVERIFY (0xb1)-op_checklocktimeverify :: Word8+op_checklocktimeverify, op_checksequenceverify :: Word8 op_checklocktimeverify = 0xb1---- | OP_CHECKSEQUENCEVERIFY (0xb2)-op_checksequenceverify :: Word8 op_checksequenceverify = 0xb2 --- | OP_1 (0x51)-op_1 :: Word8-op_1 = 0x51+-- helpers -------------------------------------------------------------------- --- | OP_2 (0x52)-op_2 :: Word8-op_2 = 0x52+op :: Word8 -> BSB.Builder+op = BSB.word8+{-# INLINE op #-} --- | OP_16 (0x60)-op_16 :: Word8-op_16 = 0x60+-- Push data of at most 75 bytes (all pushes here are keys, hashes or+-- short numbers).+push :: BS.ByteString -> BSB.Builder+push bs = BSB.word8 (fromIntegral (BS.length bs)) <> BSB.byteString bs+{-# INLINE push #-} --- helpers ---------------------------------------------------------------------+push_point :: Point -> BSB.Builder+push_point = push . BOLT1.un_point+{-# INLINE push_point #-} --- | Push a bytestring onto the stack (handles length encoding).------ For data <= 75 bytes, the length itself is the opcode.-push_data :: BS.ByteString -> BSB.Builder-push_data !bs-  | len <= 75 = BSB.word8 (fromIntegral len) <> BSB.byteString bs-  | len <= 255 = BSB.word8 0x4c <> BSB.word8 (fromIntegral len)-                 <> BSB.byteString bs-  | len <= 65535 = BSB.word8 0x4d <> BSB.word16LE (fromIntegral len)-                   <> BSB.byteString bs-  | otherwise = BSB.word8 0x4e <> BSB.word32LE (fromIntegral len)-                <> BSB.byteString bs+-- Push a non-negative number, minimally encoded.+push_num :: Word32 -> BSB.Builder+push_num n+  | n == 0    = op op_0+  | n <= 16   = op (0x50 + fromIntegral n)+  | otherwise = push (BS.pack (signed (le n)))   where-    !len = BS.length bs-{-# INLINE push_data #-}+    -- little-endian magnitude+    le :: Word32 -> [Word8]+    le !x+      | x == 0    = []+      | otherwise = fromIntegral (x .&. 0xff) : le (x `shiftR` 8) --- | Encode a Word16 as minimal script number (for CSV delays).-push_csv_delay :: Word16 -> BSB.Builder-push_csv_delay !n-  | n == 0 = BSB.word8 op_0-  | n <= 16 = BSB.word8 (0x50 + fromIntegral n)-  | n <= 0x7f = push_data (BS.singleton (fromIntegral n))-  | n <= 0x7fff = push_data (BS.pack [lo, hi])-  | otherwise = push_data (BS.pack [lo, hi, 0x00])  -- need sign byte-  where-    !lo = fromIntegral (n .&. 0xff)-    !hi = fromIntegral ((n `shiftR` 8) .&. 0xff)-{-# INLINE push_csv_delay #-}+    -- append a sign byte if the top bit is set+    signed bs = case reverse bs of+      (h : _) | h .&. 0x80 /= 0 -> bs <> [0x00]+      _ -> bs --- | Encode a Word32 as minimal script number (for CLTV).-push_cltv :: Word32 -> BSB.Builder-push_cltv !n-  | n == 0 = BSB.word8 op_0-  | n <= 16 = BSB.word8 (0x50 + fromIntegral n)-  | otherwise = push_data (encode_scriptnum n)-  where-    encode_scriptnum :: Word32 -> BS.ByteString-    encode_scriptnum 0 = BS.empty-    encode_scriptnum !v =-      let -- Build bytes little-endian (LSB first)-          go :: Word32 -> [Word8]-          go 0 = []-          go !x = fromIntegral (x .&. 0xff) : go (x `shiftR` 8)-          !bytes = go v-          -- If MSB has high bit set, need 0x00 suffix for positive numbers-          !result = case reverse bytes of-            [] -> bytes-            (msb:_) | msb .&. 0x80 /= 0 -> bytes ++ [0x00]-            _ -> bytes-      in BS.pack result-{-# INLINE push_cltv #-}+push_delay :: Word16 -> BSB.Builder+push_delay = push_num . fromIntegral+{-# INLINE push_delay #-} --- | Build script from builder.-build_script :: BSB.Builder -> Script-build_script = Script . BSL.toStrict . BSB.toLazyByteString-{-# INLINE build_script #-}+script :: BSB.Builder -> Script+script = Script . BSL.toStrict . BSB.toLazyByteString+{-# INLINE script #-} --- | HASH160 = RIPEMD160(SHA256(x)) hash160 :: BS.ByteString -> BS.ByteString hash160 = RIPEMD160.hash . SHA256.hash {-# INLINE hash160 #-} --- P2WSH helpers ---------------------------------------------------------------+-- Append a witness script to a witness stack.+with_script :: [BS.ByteString] -> Script -> Witness+with_script items (Script s) = Witness (items <> [s])+{-# INLINE with_script #-} --- | Compute SHA256 hash of a witness script.------ >>> witness_script_hash (Script "some_script")--- <32-byte SHA256 hash>-witness_script_hash :: Script -> BS.ByteString-witness_script_hash (Script !s) = SHA256.hash s-{-# INLINE witness_script_hash #-}+-- P2WSH ---------------------------------------------------------------------- --- | Convert a witness script to P2WSH scriptPubKey.------ P2WSH format: OP_0 <32-byte-hash>------ >>> to_p2wsh some_witness_script--- Script "\x00\x20<32-byte-hash>"+-- | The P2WSH scriptPubKey for a witness script:+--   @0 \<SHA256(script)\>@. to_p2wsh :: Script -> Script-to_p2wsh !script =-  let !h = witness_script_hash script-  in build_script (BSB.word8 op_0 <> push_data h)-{-# INLINE to_p2wsh #-}+to_p2wsh (Script s) = script (op op_0 <> push (SHA256.hash s)) --- funding output --------------------------------------------------------------+-- funding output ------------------------------------------------------------- --- | Funding output witness script (2-of-2 multisig).------ Script: @2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG@+-- | The funding output's witness script: ----- Where pubkey1 is lexicographically lesser.+--   @2 \<pubkey1\> \<pubkey2\> 2 OP_CHECKMULTISIG@ ----- >>> funding_script pk1 pk2--- Script "R!<pk_lesser>!<pk_greater>R\xae"+--   where @pubkey1@ is the lexicographically lesser of the two funding+--   pubkeys. The argument order doesn't matter. funding_script :: FundingPubkey -> FundingPubkey -> Script-funding_script (FundingPubkey (Pubkey !pk1)) (FundingPubkey (Pubkey !pk2)) =-  let (!lesser, !greater) = if pk1 <= pk2 then (pk1, pk2) else (pk2, pk1)-  in build_script $-       BSB.word8 op_2-       <> push_data lesser-       <> push_data greater-       <> BSB.word8 op_2-       <> BSB.word8 op_checkmultisig+funding_script (FundingPubkey a) (FundingPubkey b) =+  let (lo, hi) = if a <= b then (a, b) else (b, a)+  in  script $+           op op_2 <> push_point lo <> push_point hi+        <> op op_2 <> op op_checkmultisig --- | Witness for spending funding output.------ Witness: @0 <sig1> <sig2>@+-- | The witness spending the funding output, given each party's+--   funding pubkey and signature (DER-encoded, with sighash byte): ----- Signatures ordered to match pubkey order in script.+--   @0 \<signature_for_pubkey1\> \<signature_for_pubkey2\> \<script\>@ ----- >>> funding_witness sig1 sig2--- Witness ["", sig1, sig2]-funding_witness :: BS.ByteString -> BS.ByteString -> Witness-funding_witness !sig1 !sig2 = Witness [BS.empty, sig1, sig2]+--   The signatures are ordered to match the pubkeys in+--   'funding_script', so the argument order doesn't matter.+funding_witness+  :: FundingPubkey -> BS.ByteString+  -> FundingPubkey -> BS.ByteString+  -> Witness+funding_witness pa@(FundingPubkey a) sa pb@(FundingPubkey b) sb =+  let (s1, s2) = if a <= b then (sa, sb) else (sb, sa)+  in  with_script [BS.empty, s1, s2] (funding_script pa pb) --- to_local output -------------------------------------------------------------+-- to_local output ------------------------------------------------------------ --- | to_local witness script (revocable with CSV delay).------ Script:+-- | The @to_local@ output's witness script: ----- @--- OP_IF---     <revocationpubkey>--- OP_ELSE---     <to_self_delay>---     OP_CHECKSEQUENCEVERIFY---     OP_DROP---     <local_delayedpubkey>--- OP_ENDIF--- OP_CHECKSIG--- @+--   @+--   OP_IF+--       \<revocationpubkey\>+--   OP_ELSE+--       \<to_self_delay\> OP_CHECKSEQUENCEVERIFY OP_DROP+--       \<local_delayedpubkey\>+--   OP_ENDIF+--   OP_CHECKSIG+--   @ ----- >>> to_local_script revpk delay localpk--- Script "c!<revpk>g<delay>\xb2u!<localpk>h\xac"+--   HTLC-success and HTLC-timeout transaction outputs use the same+--   script. to_local_script   :: RevocationPubkey   -> ToSelfDelay   -> LocalDelayedPubkey   -> Script to_local_script-  (RevocationPubkey (Pubkey !revpk))-  (ToSelfDelay !delay)-  (LocalDelayedPubkey (Pubkey !localpk)) =-    build_script $-      BSB.word8 op_if-      <> push_data revpk-      <> BSB.word8 op_else-      <> push_csv_delay delay-      <> BSB.word8 op_checksequenceverify-      <> BSB.word8 op_drop-      <> push_data localpk-      <> BSB.word8 op_endif-      <> BSB.word8 op_checksig+  (RevocationPubkey rev)+  (ToSelfDelay delay)+  (LocalDelayedPubkey delayed) = script $+       op op_if+    <> push_point rev+    <> op op_else+    <> push_delay delay+    <> op op_checksequenceverify+    <> op op_drop+    <> push_point delayed+    <> op op_endif+    <> op op_checksig --- | Witness for delayed spend of to_local output.------ Input nSequence must be set to to_self_delay.+-- | The witness for the owner's delayed spend of a @to_local@ (or+--   HTLC transaction) output, given its witness script: ----- Witness: @<local_delayedsig> <>@+--   @\<local_delayedsig\> \<\> \<script\>@ ----- >>> to_local_witness_spend sig--- Witness [sig, ""]-to_local_witness_spend :: BS.ByteString -> Witness-to_local_witness_spend !sig = Witness [sig, BS.empty]+--   The spending input's @nSequence@ must be @to_self_delay@.+to_local_witness_spend :: BS.ByteString -> Script -> Witness+to_local_witness_spend sig = with_script [sig, BS.empty] --- | Witness for revocation spend of to_local output.------ Witness: @<revocation_sig> 1@+-- | The witness for the revocation spend of a @to_local@ (or HTLC+--   transaction) output, given its witness script: ----- >>> to_local_witness_revoke sig--- Witness [sig, "\x01"]-to_local_witness_revoke :: BS.ByteString -> Witness-to_local_witness_revoke !sig = Witness [sig, BS.singleton 0x01]+--   @\<revocation_sig\> 1 \<script\>@+to_local_witness_revoke :: BS.ByteString -> Script -> Witness+to_local_witness_revoke sig = with_script [sig, BS.singleton 0x01] --- to_remote output ------------------------------------------------------------+-- to_remote output ----------------------------------------------------------- --- | to_remote witness script.------ With option_anchors:------ @--- <remotepubkey> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY--- @+-- | The @to_remote@ output's witness script under 'Anchors': ----- Without option_anchors: P2WPKH (just the pubkey hash).+--   @\<remotepubkey\> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY@ ----- >>> to_remote_script pk (ChannelFeatures True)--- Script "!<pk>\xadQ\xb2"-to_remote_script :: RemotePubkey -> ChannelFeatures -> Script-to_remote_script (RemotePubkey (Pubkey !pk)) !features-  | has_anchors features =-      -- Anchors: script with 1-block CSV-      build_script $-        push_data pk-        <> BSB.word8 op_checksigverify-        <> BSB.word8 op_1-        <> BSB.word8 op_checksequenceverify-  | otherwise =-      -- No anchors: P2WPKH (OP_0 <20-byte-hash>)-      let !h = hash160 pk-      in build_script (BSB.word8 op_0 <> push_data h)+--   (Under 'StaticRemotekey' the output is P2WPKH, with no witness+--   script; see 'to_remote_script_pubkey'.)+to_remote_witness_script :: RemotePubkey -> Script+to_remote_witness_script (RemotePubkey pk) = script $+     push_point pk+  <> op op_checksigverify+  <> op op_1+  <> op op_checksequenceverify --- | Witness for spending to_remote output.------ With option_anchors (P2WSH), input nSequence must be 1.--- Witness: @<remote_sig>@ (witness script appended by caller)------ Without option_anchors (P2WPKH):--- Witness: @<remote_sig> <remotepubkey>@------ >>> to_remote_witness sig pk (ChannelFeatures False)--- Witness [sig, pk]-to_remote_witness :: BS.ByteString -> RemotePubkey -> ChannelFeatures -> Witness-to_remote_witness !sig (RemotePubkey (Pubkey !pk)) !features-  | has_anchors features = Witness [sig]-  | otherwise = Witness [sig, pk]+-- | The @to_remote@ output's scriptPubKey: P2WPKH to @remotepubkey@+--   under 'StaticRemotekey', and P2WSH of 'to_remote_witness_script'+--   under 'Anchors'.+to_remote_script_pubkey :: RemotePubkey -> CommitmentFormat -> Script+to_remote_script_pubkey pk@(RemotePubkey p) fmt = case fmt of+  StaticRemotekey -> script (op op_0 <> push (hash160 (BOLT1.un_point p)))+  Anchors         -> to_p2wsh (to_remote_witness_script pk) --- anchor outputs --------------------------------------------------------------+-- | The witness spending a @to_remote@ output: @\<remote_sig\>+--   \<remotepubkey\>@ (P2WPKH) under 'StaticRemotekey', and+--   @\<remote_sig\> \<script\>@ under 'Anchors', where the spending+--   input's @nSequence@ must be 1.+to_remote_witness+  :: BS.ByteString -> RemotePubkey -> CommitmentFormat -> Witness+to_remote_witness sig pk@(RemotePubkey p) fmt = case fmt of+  StaticRemotekey -> Witness [sig, BOLT1.un_point p]+  Anchors         -> with_script [sig] (to_remote_witness_script pk) --- | Anchor output witness script.------ Script:------ @--- <funding_pubkey> OP_CHECKSIG OP_IFDUP--- OP_NOTIF---     OP_16 OP_CHECKSEQUENCEVERIFY--- OP_ENDIF--- @+-- anchor outputs -------------------------------------------------------------++-- | An anchor output's witness script, for the funding pubkey of the+--   party it belongs to: ----- >>> anchor_script fundpk--- Script "!<fundpk>\xac\x73d`\xb2h"+--   @+--   \<funding_pubkey\> OP_CHECKSIG OP_IFDUP+--   OP_NOTIF+--       OP_16 OP_CHECKSEQUENCEVERIFY+--   OP_ENDIF+--   @ anchor_script :: FundingPubkey -> Script-anchor_script (FundingPubkey (Pubkey !pk)) =-  build_script $-    push_data pk-    <> BSB.word8 op_checksig-    <> BSB.word8 op_ifdup-    <> BSB.word8 op_notif-    <> BSB.word8 op_16-    <> BSB.word8 op_checksequenceverify-    <> BSB.word8 op_endif+anchor_script (FundingPubkey pk) = script $+     push_point pk+  <> op op_checksig+  <> op op_ifdup+  <> op op_notif+  <> op op_16+  <> op op_checksequenceverify+  <> op op_endif --- | Witness for owner to spend anchor output.------ Witness: @<sig>@------ >>> anchor_witness_owner sig--- Witness [sig]-anchor_witness_owner :: BS.ByteString -> Witness-anchor_witness_owner !sig = Witness [sig]+-- | The witness for an anchor's owner to spend it:+--   @\<sig\> \<script\>@.+anchor_witness_owner :: BS.ByteString -> FundingPubkey -> Witness+anchor_witness_owner sig pk = with_script [sig] (anchor_script pk) --- | Witness for anyone to sweep anchor output after 16 blocks.------ Witness: @<>@------ >>> anchor_witness_anyone--- Witness [""]-anchor_witness_anyone :: Witness-anchor_witness_anyone = Witness [BS.empty]+-- | The witness for anyone to sweep an anchor, 16 blocks after the+--   commitment transaction confirms: @\<\> \<script\>@.+anchor_witness_anyone :: FundingPubkey -> Witness+anchor_witness_anyone pk = with_script [BS.empty] (anchor_script pk) --- offered HTLC output ---------------------------------------------------------+-- offered HTLC outputs ------------------------------------------------------- --- | Offered HTLC witness script.------ Without option_anchors:+-- csv suffix for anchors HTLC scripts+htlc_csv :: CommitmentFormat -> BSB.Builder+htlc_csv fmt = case fmt of+  StaticRemotekey -> mempty+  Anchors         -> op op_1 <> op op_checksequenceverify <> op op_drop+{-# INLINE htlc_csv #-}++-- | An offered HTLC output's witness script: ----- @--- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL--- OP_IF---     OP_CHECKSIG--- OP_ELSE---     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL---     OP_NOTIF---         OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG---     OP_ELSE---         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY---         OP_CHECKSIG---     OP_ENDIF--- OP_ENDIF--- @+--   @+--   OP_DUP OP_HASH160 \<RIPEMD160(SHA256(revocationpubkey))\> OP_EQUAL+--   OP_IF+--       OP_CHECKSIG+--   OP_ELSE+--       \<remote_htlcpubkey\> OP_SWAP OP_SIZE 32 OP_EQUAL+--       OP_NOTIF+--           OP_DROP 2 OP_SWAP \<local_htlcpubkey\> 2 OP_CHECKMULTISIG+--       OP_ELSE+--           OP_HASH160 \<RIPEMD160(payment_hash)\> OP_EQUALVERIFY+--           OP_CHECKSIG+--       OP_ENDIF+--   OP_ENDIF+--   @ ----- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before--- final OP_ENDIF.+--   Under 'Anchors', @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ precedes the+--   final @OP_ENDIF@. offered_htlc_script   :: RevocationPubkey   -> RemoteHtlcPubkey   -> LocalHtlcPubkey-  -> PaymentHash-  -> ChannelFeatures+  -> BOLT1.PaymentHash+  -> CommitmentFormat   -> Script offered_htlc_script-  (RevocationPubkey (Pubkey !revpk))-  (RemoteHtlcPubkey (Pubkey !remotepk))-  (LocalHtlcPubkey (Pubkey !localpk))-  (PaymentHash !ph)-  !features =-    let !revpk_hash = hash160 revpk-        !payment_hash160 = RIPEMD160.hash ph-        !csv_suffix = if has_anchors features-          then BSB.word8 op_1-               <> BSB.word8 op_checksequenceverify-               <> BSB.word8 op_drop-          else mempty-    in build_script $-         -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL-         BSB.word8 op_dup-         <> BSB.word8 op_hash160-         <> push_data revpk_hash-         <> BSB.word8 op_equal-         -- OP_IF OP_CHECKSIG-         <> BSB.word8 op_if-         <> BSB.word8 op_checksig-         -- OP_ELSE-         <> BSB.word8 op_else-         -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL-         <> push_data remotepk-         <> BSB.word8 op_swap-         <> BSB.word8 op_size-         <> push_data (BS.singleton 32)-         <> BSB.word8 op_equal-         -- OP_NOTIF-         <> BSB.word8 op_notif-         -- OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG-         <> BSB.word8 op_drop-         <> BSB.word8 op_2-         <> BSB.word8 op_swap-         <> push_data localpk-         <> BSB.word8 op_2-         <> BSB.word8 op_checkmultisig-         -- OP_ELSE-         <> BSB.word8 op_else-         -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY OP_CHECKSIG-         <> BSB.word8 op_hash160-         <> push_data payment_hash160-         <> BSB.word8 op_equalverify-         <> BSB.word8 op_checksig-         -- OP_ENDIF-         <> BSB.word8 op_endif-         -- CSV suffix for anchors-         <> csv_suffix-         -- OP_ENDIF-         <> BSB.word8 op_endif+  (RevocationPubkey rev)+  (RemoteHtlcPubkey remote)+  (LocalHtlcPubkey local)+  ph+  fmt = script $+       op op_dup <> op op_hash160+    <> push (hash160 (BOLT1.un_point rev))+    <> op op_equal+    <> op op_if+    <>   op op_checksig+    <> op op_else+    <>   push_point remote <> op op_swap <> op op_size+    <>   push (BS.singleton 32) <> op op_equal+    <>   op op_notif+    <>     op op_drop <> op op_2 <> op op_swap+    <>     push_point local <> op op_2 <> op op_checkmultisig+    <>   op op_else+    <>     op op_hash160+    <>     push (RIPEMD160.hash (BOLT1.un_payment_hash ph))+    <>     op op_equalverify <> op op_checksig+    <>   op op_endif+    <>   htlc_csv fmt+    <> op op_endif --- | Witness for remote node to claim offered HTLC with preimage.------ With option_anchors, input nSequence must be 1.+-- | The witness for the remote party to claim an offered HTLC output+--   with the payment preimage, given its witness script: ----- Witness: @<remotehtlcsig> <payment_preimage>@+--   @\<remotehtlcsig\> \<payment_preimage\> \<script\>@ ----- >>> offered_htlc_witness_preimage sig preimage--- Witness [sig, preimage]+--   Under 'Anchors' the spending input's @nSequence@ must be 1. offered_htlc_witness_preimage-  :: BS.ByteString -> PaymentPreimage -> Witness-offered_htlc_witness_preimage !sig (PaymentPreimage !preimage) =-  Witness [sig, preimage]+  :: BS.ByteString -> PaymentPreimage -> Script -> Witness+offered_htlc_witness_preimage sig pre =+  with_script [sig, BOLT1.un_payment_preimage pre] --- | Witness for revocation spend of offered HTLC.------ Witness: @<revocation_sig> <revocationpubkey>@+-- | The witness for the revocation spend of an offered HTLC output,+--   given its witness script: ----- >>> offered_htlc_witness_revoke sig revpk--- Witness [sig, revpk]-offered_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness-offered_htlc_witness_revoke !sig (Pubkey !revpk) = Witness [sig, revpk]+--   @\<revocation_sig\> \<revocationpubkey\> \<script\>@+offered_htlc_witness_revoke+  :: BS.ByteString -> RevocationPubkey -> Script -> Witness+offered_htlc_witness_revoke sig (RevocationPubkey rev) =+  with_script [sig, BOLT1.un_point rev] --- received HTLC output --------------------------------------------------------+-- received HTLC outputs ------------------------------------------------------ --- | Received HTLC witness script.------ Without option_anchors:+-- | A received HTLC output's witness script: ----- @--- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL--- OP_IF---     OP_CHECKSIG--- OP_ELSE---     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL---     OP_IF---         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY---         2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG---     OP_ELSE---         OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP---         OP_CHECKSIG---     OP_ENDIF--- OP_ENDIF--- @+--   @+--   OP_DUP OP_HASH160 \<RIPEMD160(SHA256(revocationpubkey))\> OP_EQUAL+--   OP_IF+--       OP_CHECKSIG+--   OP_ELSE+--       \<remote_htlcpubkey\> OP_SWAP OP_SIZE 32 OP_EQUAL+--       OP_IF+--           OP_HASH160 \<RIPEMD160(payment_hash)\> OP_EQUALVERIFY+--           2 OP_SWAP \<local_htlcpubkey\> 2 OP_CHECKMULTISIG+--       OP_ELSE+--           OP_DROP \<cltv_expiry\> OP_CHECKLOCKTIMEVERIFY OP_DROP+--           OP_CHECKSIG+--       OP_ENDIF+--   OP_ENDIF+--   @ ----- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before--- final OP_ENDIF.+--   Under 'Anchors', @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ precedes the+--   final @OP_ENDIF@. received_htlc_script   :: RevocationPubkey   -> RemoteHtlcPubkey   -> LocalHtlcPubkey-  -> PaymentHash+  -> BOLT1.PaymentHash   -> CltvExpiry-  -> ChannelFeatures+  -> CommitmentFormat   -> Script received_htlc_script-  (RevocationPubkey (Pubkey !revpk))-  (RemoteHtlcPubkey (Pubkey !remotepk))-  (LocalHtlcPubkey (Pubkey !localpk))-  (PaymentHash !ph)-  (CltvExpiry !expiry)-  !features =-    let !revpk_hash = hash160 revpk-        !payment_hash160 = RIPEMD160.hash ph-        !csv_suffix = if has_anchors features-          then BSB.word8 op_1-               <> BSB.word8 op_checksequenceverify-               <> BSB.word8 op_drop-          else mempty-    in build_script $-         -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL-         BSB.word8 op_dup-         <> BSB.word8 op_hash160-         <> push_data revpk_hash-         <> BSB.word8 op_equal-         -- OP_IF OP_CHECKSIG-         <> BSB.word8 op_if-         <> BSB.word8 op_checksig-         -- OP_ELSE-         <> BSB.word8 op_else-         -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL-         <> push_data remotepk-         <> BSB.word8 op_swap-         <> BSB.word8 op_size-         <> push_data (BS.singleton 32)-         <> BSB.word8 op_equal-         -- OP_IF-         <> BSB.word8 op_if-         -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY-         <> BSB.word8 op_hash160-         <> push_data payment_hash160-         <> BSB.word8 op_equalverify-         -- 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG-         <> BSB.word8 op_2-         <> BSB.word8 op_swap-         <> push_data localpk-         <> BSB.word8 op_2-         <> BSB.word8 op_checkmultisig-         -- OP_ELSE-         <> BSB.word8 op_else-         -- OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG-         <> BSB.word8 op_drop-         <> push_cltv expiry-         <> BSB.word8 op_checklocktimeverify-         <> BSB.word8 op_drop-         <> BSB.word8 op_checksig-         -- OP_ENDIF-         <> BSB.word8 op_endif-         -- CSV suffix for anchors-         <> csv_suffix-         -- OP_ENDIF-         <> BSB.word8 op_endif+  (RevocationPubkey rev)+  (RemoteHtlcPubkey remote)+  (LocalHtlcPubkey local)+  ph+  (CltvExpiry expiry)+  fmt = script $+       op op_dup <> op op_hash160+    <> push (hash160 (BOLT1.un_point rev))+    <> op op_equal+    <> op op_if+    <>   op op_checksig+    <> op op_else+    <>   push_point remote <> op op_swap <> op op_size+    <>   push (BS.singleton 32) <> op op_equal+    <>   op op_if+    <>     op op_hash160+    <>     push (RIPEMD160.hash (BOLT1.un_payment_hash ph))+    <>     op op_equalverify+    <>     op op_2 <> op op_swap <> push_point local+    <>     op op_2 <> op op_checkmultisig+    <>   op op_else+    <>     op op_drop <> push_num expiry+    <>     op op_checklocktimeverify <> op op_drop+    <>     op op_checksig+    <>   op op_endif+    <>   htlc_csv fmt+    <> op op_endif --- | Witness for remote node to timeout received HTLC.------ With option_anchors, input nSequence must be 1.+-- | The witness for the remote party to time out a received HTLC+--   output, given its witness script: ----- Witness: @<remotehtlcsig> <>@+--   @\<remotehtlcsig\> \<\> \<script\>@ ----- >>> received_htlc_witness_timeout sig--- Witness [sig, ""]-received_htlc_witness_timeout :: BS.ByteString -> Witness-received_htlc_witness_timeout !sig = Witness [sig, BS.empty]+--   Under 'Anchors' the spending input's @nSequence@ must be 1.+received_htlc_witness_timeout :: BS.ByteString -> Script -> Witness+received_htlc_witness_timeout sig = with_script [sig, BS.empty] --- | Witness for revocation spend of received HTLC.------ Witness: @<revocation_sig> <revocationpubkey>@+-- | The witness for the revocation spend of a received HTLC output,+--   given its witness script: ----- >>> received_htlc_witness_revoke sig revpk--- Witness [sig, revpk]-received_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness-received_htlc_witness_revoke !sig (Pubkey !revpk) = Witness [sig, revpk]+--   @\<revocation_sig\> \<revocationpubkey\> \<script\>@+received_htlc_witness_revoke+  :: BS.ByteString -> RevocationPubkey -> Script -> Witness+received_htlc_witness_revoke sig (RevocationPubkey rev) =+  with_script [sig, BOLT1.un_point rev] --- HTLC-timeout/success output -------------------------------------------------+-- HTLC transaction inputs ---------------------------------------------------- --- | HTLC output witness script (same structure as to_local).------ Used for HTLC-timeout and HTLC-success transaction outputs.+-- | The witness for an HTLC-success transaction's input, given both+--   HTLC signatures, the payment preimage and the received HTLC+--   output's witness script: ----- Script:+--   @0 \<remotehtlcsig\> \<localhtlcsig\> \<payment_preimage\> \<script\>@ ----- @--- OP_IF---     <revocationpubkey>--- OP_ELSE---     <to_self_delay>---     OP_CHECKSEQUENCEVERIFY---     OP_DROP---     <local_delayedpubkey>--- OP_ENDIF--- OP_CHECKSIG--- @-htlc_output_script-  :: RevocationPubkey-  -> ToSelfDelay-  -> LocalDelayedPubkey-  -> Script-htlc_output_script = to_local_script+--   See 'remote_htlc_sighash' for the signatures' sighash types.+htlc_success_witness+  :: BS.ByteString    -- ^ remotehtlcsig+  -> BS.ByteString    -- ^ localhtlcsig+  -> PaymentPreimage+  -> Script           -- ^ the received HTLC output's witness script+  -> Witness+htlc_success_witness remote local pre =+  with_script [BS.empty, remote, local, BOLT1.un_payment_preimage pre] --- | Witness for delayed spend of HTLC output.+-- | The witness for an HTLC-timeout transaction's input, given both+--   HTLC signatures and the offered HTLC output's witness script: ----- Input nSequence must be set to to_self_delay.+--   @0 \<remotehtlcsig\> \<localhtlcsig\> \<\> \<script\>@ ----- Witness: @<local_delayedsig> 0@-htlc_output_witness_spend :: BS.ByteString -> Witness-htlc_output_witness_spend = to_local_witness_spend+--   See 'remote_htlc_sighash' for the signatures' sighash types.+htlc_timeout_witness+  :: BS.ByteString    -- ^ remotehtlcsig+  -> BS.ByteString    -- ^ localhtlcsig+  -> Script           -- ^ the offered HTLC output's witness script+  -> Witness+htlc_timeout_witness remote local =+  with_script [BS.empty, remote, local, BS.empty] --- | Witness for revocation spend of HTLC output.+-- | The sighash type of @remotehtlcsig@: the signature on an+--   HTLC-success or HTLC-timeout transaction that the commitment's+--   non-owner sends in @commitment_signed@. ----- Witness: @<revocationsig> 1@-htlc_output_witness_revoke :: BS.ByteString -> Witness-htlc_output_witness_revoke = to_local_witness_revoke+--   Under 'Anchors' it is @SIGHASH_SINGLE|SIGHASH_ANYONECANPAY@, so+--   that the owner can add inputs and outputs to pay the (otherwise+--   zero) fee; under 'StaticRemotekey' it is @SIGHASH_ALL@. The+--   owner's own signature, @localhtlcsig@, always uses @SIGHASH_ALL@.+--+--   >>> remote_htlc_sighash Anchors+--   SIGHASH_SINGLE_ANYONECANPAY+remote_htlc_sighash :: CommitmentFormat -> SighashType+remote_htlc_sighash fmt = case fmt of+  StaticRemotekey -> SIGHASH_ALL+  Anchors         -> SIGHASH_SINGLE_ANYONECANPAY
lib/Lightning/Protocol/BOLT3/Tx.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-} @@ -8,584 +8,561 @@ -- License: MIT -- Maintainer: Jared Tobin <jared@ppad.tech> ----- Transaction assembly for BOLT #3.------ Constructs:------ * Commitment transactions--- * HTLC-timeout transactions--- * HTLC-success transactions--- * Closing transactions+-- Commitment, HTLC and closing transactions, per BOLT #3.  module Lightning.Protocol.BOLT3.Tx (-    -- * Commitment transaction-    CommitmentTx(..)-  , CommitmentContext(..)-  , CommitmentKeys(..)+    -- * Commitment transactions+    CommitmentContext(..)+  , CommitmentTx(..)+  , CommitmentOutput(..)+  , OutputType(..)   , build_commitment_tx      -- * HTLC transactions-  , HTLCTx(..)   , HTLCContext(..)-  , build_htlc_timeout_tx-  , build_htlc_success_tx+  , HTLCTx(..)+  , build_htlc_tx -    -- * Closing transaction+    -- * Closing transactions   , ClosingTx(..)+  , ClosingOutput(..)   , ClosingContext(..)+  , ClosingOutputs(..)   , build_closing_tx+  , LegacyClosingContext(..)   , build_legacy_closing_tx -    -- * Transaction outputs-  , TxOutput(..)-  , OutputType(..)--    -- * Fee calculation+    -- * Fees   , commitment_fee   , htlc_timeout_fee   , htlc_success_fee-  , commitment_weight      -- * Trimming-  , is_trimmed-  , trimmed_htlcs-  , untrimmed_htlcs   , htlc_trim_threshold+  , is_trimmed -    -- * Output ordering-  , sort_outputs+    -- * Serialization+  , commitment_to_tx+  , htlc_to_tx+  , closing_to_tx+  , encode_commitment_tx+  , encode_htlc_tx+  , encode_closing_tx   ) where -import Data.Bits ((.&.), (.|.), shiftL, shiftR)+import Bitcoin.Prim.Tx (OutPoint(..), TxId)+import qualified Bitcoin.Prim.Tx as BT+import Control.DeepSeq (NFData)+import Data.Bits ((.&.), (.|.), shiftR)+import qualified Data.ByteString as BS import Data.List (sortBy)+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NE+import Data.Maybe (fromMaybe) import Data.Word (Word32, Word64) import GHC.Generics (Generic)+import Lightning.Protocol.BOLT1 (Satoshi, MilliSatoshi)+import qualified Lightning.Protocol.BOLT1 as BOLT1 import Lightning.Protocol.BOLT3.Keys import Lightning.Protocol.BOLT3.Scripts import Lightning.Protocol.BOLT3.Types --- transaction outputs ---------------------------------------------------------+-- commitment transactions ---------------------------------------------------- --- | Type of output in a commitment transaction.+-- | What a commitment transaction output pays. data OutputType   = OutputToLocal   | OutputToRemote   | OutputLocalAnchor   | OutputRemoteAnchor-  | OutputOfferedHTLC  {-# UNPACK #-} !CltvExpiry-  | OutputReceivedHTLC {-# UNPACK #-} !CltvExpiry+  | OutputHTLC !HTLC+    -- ^ an offered or received HTLC (per its 'htlc_direction')   deriving (Eq, Show, Generic) --- | A transaction output with value, script, and type information.-data TxOutput = TxOutput-  { txout_value     :: {-# UNPACK #-} !Satoshi-  , txout_script    :: !Script-  , txout_type      :: !OutputType-  } deriving (Eq, Show, Generic)---- commitment transaction ------------------------------------------------------+instance NFData OutputType --- | Derived keys needed for commitment transaction outputs.-data CommitmentKeys = CommitmentKeys-  { ck_revocation_pubkey   :: !RevocationPubkey-  , ck_local_delayed       :: !LocalDelayedPubkey-  , ck_local_htlc          :: !LocalHtlcPubkey-  , ck_remote_htlc         :: !RemoteHtlcPubkey-  , ck_local_payment       :: !LocalPubkey-  , ck_remote_payment      :: !RemotePubkey-  , ck_local_funding       :: !FundingPubkey-  , ck_remote_funding      :: !FundingPubkey+-- | A commitment transaction output.+data CommitmentOutput = CommitmentOutput+  { co_value  :: {-# UNPACK #-} !Satoshi+  , co_script :: !Script     -- ^ scriptPubKey+  , co_type   :: !OutputType   } deriving (Eq, Show, Generic) --- | Context for building a commitment transaction.+instance NFData CommitmentOutput++-- | The parameters of a commitment transaction, from the point of view+--   of its owner (the "local" party, whose outputs are delayed). data CommitmentContext = CommitmentContext-  { cc_funding_outpoint    :: !Outpoint-  , cc_commitment_number   :: !CommitmentNumber-  , cc_local_payment_bp    :: !PaymentBasepoint-  , cc_remote_payment_bp   :: !PaymentBasepoint-  , cc_to_self_delay       :: !ToSelfDelay-  , cc_dust_limit          :: !DustLimit-  , cc_feerate             :: !FeeratePerKw-  , cc_features            :: !ChannelFeatures-  , cc_is_funder           :: !Bool-  , cc_to_local_msat       :: !MilliSatoshi-  , cc_to_remote_msat      :: !MilliSatoshi-  , cc_htlcs               :: ![HTLC]-  , cc_keys                :: !CommitmentKeys+  { cc_funding_outpoint  :: {-# UNPACK #-} !OutPoint+  , cc_commitment_number :: {-# UNPACK #-} !CommitmentNumber+  , cc_local_payment_bp  :: !PaymentBasepoint+    -- ^ the owner's @payment_basepoint@+  , cc_remote_payment_bp :: !PaymentBasepoint+    -- ^ the other party's @payment_basepoint@+  , cc_to_self_delay     :: {-# UNPACK #-} !ToSelfDelay+    -- ^ the delay on the owner's outputs (set by the other party)+  , cc_dust_limit        :: {-# UNPACK #-} !DustLimit+    -- ^ the owner's @dust_limit_satoshis@+  , cc_feerate           :: {-# UNPACK #-} !FeeratePerKw+  , cc_format            :: !CommitmentFormat+  , cc_is_funder         :: !Bool+    -- ^ whether the owner opened the channel, and so pays the fee+  , cc_to_local_msat     :: {-# UNPACK #-} !MilliSatoshi+  , cc_to_remote_msat    :: {-# UNPACK #-} !MilliSatoshi+  , cc_htlcs             :: ![HTLC]+  , cc_keys              :: !CommitmentKeys   } deriving (Eq, Show, Generic) --- | A commitment transaction.+instance NFData CommitmentContext++-- | An unsigned commitment transaction. data CommitmentTx = CommitmentTx-  { ctx_version            :: {-# UNPACK #-} !Word32-  , ctx_locktime           :: !Locktime-  , ctx_input_outpoint     :: !Outpoint-  , ctx_input_sequence     :: !Sequence-  , ctx_outputs            :: ![TxOutput]-  , ctx_funding_script     :: !Script+  { ctx_version        :: {-# UNPACK #-} !Word32+  , ctx_locktime       :: {-# UNPACK #-} !Locktime+  , ctx_input_outpoint :: {-# UNPACK #-} !OutPoint+  , ctx_input_sequence :: {-# UNPACK #-} !Sequence+  , ctx_outputs        :: !(NonEmpty CommitmentOutput)+    -- ^ in BIP69+CLTV order+  , ctx_funding_script :: !Script+    -- ^ the funding output's witness script, which signatures commit+    --   to   } deriving (Eq, Show, Generic) --- | Build a commitment transaction.+instance NFData CommitmentTx++-- | Build a commitment transaction, per BOLT #3's "Commitment+--   Transaction Construction": ----- Follows the algorithm from BOLT #3:+--   * the locktime and input sequence encode the obscured commitment+--     number (the payment basepoints are ordered by 'cc_is_funder');+--   * HTLCs whose amount, less the HTLC transaction fee, is below the+--     dust limit are trimmed;+--   * the base fee (and under 'Anchors', both anchors) are taken from+--     the funder's output, which is left at zero if it can't cover+--     them;+--   * @to_local@ and @to_remote@ outputs below the dust limit are+--     omitted, and under 'Anchors' each party's anchor is added if its+--     output, or any HTLC output, exists;+--   * outputs are sorted by value, scriptPubKey and CLTV expiry. ----- 1. Initialize input and locktime with obscured commitment number--- 2. Calculate which HTLCs are trimmed--- 3. Calculate base fee and subtract from funder--- 4. Add untrimmed HTLC outputs--- 5. Add to_local output if above dust--- 6. Add to_remote output if above dust--- 7. Add anchor outputs if option_anchors--- 8. Sort outputs per BIP69+CLTV-build_commitment_tx :: CommitmentContext -> CommitmentTx-build_commitment_tx ctx =-  let !obscured = obscured_commitment_number-        (cc_local_payment_bp ctx)-        (cc_remote_payment_bp ctx)-        (cc_commitment_number ctx)+--   Fails if the transaction would have no outputs.+build_commitment_tx :: CommitmentContext -> Maybe CommitmentTx+build_commitment_tx ctx = do+    outputs <- NE.nonEmpty . sort_outputs $+      to_local <> to_remote <> local_anchor <> remote_anchor <> htlcs+    pure CommitmentTx+      { ctx_version        = 2+      , ctx_locktime       =+          Locktime (0x20000000 .|. (fromIntegral obscured .&. 0xFFFFFF))+      , ctx_input_outpoint = cc_funding_outpoint ctx+      , ctx_input_sequence =+          Sequence+            (0x80000000 .|. (fromIntegral (obscured `shiftR` 24) .&. 0xFFFFFF))+      , ctx_outputs        = outputs+      , ctx_funding_script =+          funding_script (ck_local_funding keys) (ck_remote_funding keys)+      }+  where+    !keys = cc_keys ctx+    !fmt  = cc_format ctx+    !rate = cc_feerate ctx+    !dust = cc_dust_limit ctx+    DustLimit dust_sat = dust -      -- Locktime: upper 8 bits are 0x20, lower 24 bits are lower 24 of obscured-      !locktime = Locktime $-        (0x20 `shiftL` 24) .|. (fromIntegral obscured .&. 0x00FFFFFF)+    (!opener, !accepter)+      | cc_is_funder ctx = (cc_local_payment_bp ctx, cc_remote_payment_bp ctx)+      | otherwise        = (cc_remote_payment_bp ctx, cc_local_payment_bp ctx)+    !obscured =+      obscured_commitment_number opener accepter (cc_commitment_number ctx) -      -- Sequence: upper 8 bits are 0x80, lower 24 bits are upper 24 of obscured-      !inputSeq = Sequence $-        (0x80 `shiftL` 24) .|.-        (fromIntegral (obscured `shiftR` 24) .&. 0x00FFFFFF)+    untrimmed = filter (not . is_trimmed dust rate fmt) (cc_htlcs ctx) -      -- Funding script for witness-      !fundingScript = funding_script-        (ck_local_funding $ cc_keys ctx)-        (ck_remote_funding $ cc_keys ctx)+    !fee = commitment_fee rate fmt (fromIntegral (length untrimmed))+    !deduction = case fmt of+      StaticRemotekey -> fee+      Anchors         ->+        fromMaybe BOLT1.max_satoshi (BOLT1.add_sat fee (sat 660))+    deduct s = fromMaybe (sat 0) (BOLT1.sub_sat s deduction) -      -- Calculate untrimmed HTLCs-      !untrimmedHtlcs = untrimmed_htlcs-        (cc_dust_limit ctx)-        (cc_feerate ctx)-        (cc_features ctx)-        (cc_htlcs ctx)+    !local_sat  = BOLT1.msat_to_sat (cc_to_local_msat ctx)+    !remote_sat = BOLT1.msat_to_sat (cc_to_remote_msat ctx)+    (!to_local_sat, !to_remote_sat)+      | cc_is_funder ctx = (deduct local_sat, remote_sat)+      | otherwise        = (local_sat, deduct remote_sat) -      -- Calculate base fee-      !baseFee = commitment_fee-        (cc_feerate ctx)-        (cc_features ctx)-        (fromIntegral $ length untrimmedHtlcs)+    to_local =+      [ CommitmentOutput to_local_sat+          (to_p2wsh (to_local_script (ck_revocation_pubkey keys)+                       (cc_to_self_delay ctx) (ck_local_delayed keys)))+          OutputToLocal+      | to_local_sat >= dust_sat ] -      -- Anchor cost if applicable-      !anchorCost = if has_anchors (cc_features ctx)-        then 2 * anchor_output_value-        else Satoshi 0+    to_remote =+      [ CommitmentOutput to_remote_sat+          (to_remote_script_pubkey (ck_remote_payment keys) fmt)+          OutputToRemote+      | to_remote_sat >= dust_sat ] -      -- Subtract fees and anchors from funder-      !totalDeduction = baseFee + anchorCost-      !(toLocalSat, toRemoteSat) = if cc_is_funder ctx-        then-          let !local = msat_to_sat (cc_to_local_msat ctx)-              !deducted = if unSatoshi local >= unSatoshi totalDeduction-                          then Satoshi (unSatoshi local - unSatoshi totalDeduction)-                          else Satoshi 0-          in (deducted, msat_to_sat (cc_to_remote_msat ctx))-        else-          let !remote = msat_to_sat (cc_to_remote_msat ctx)-              !deducted = if unSatoshi remote >= unSatoshi totalDeduction-                          then Satoshi (unSatoshi remote - unSatoshi totalDeduction)-                          else Satoshi 0-          in (msat_to_sat (cc_to_local_msat ctx), deducted)+    has_htlcs = not (null untrimmed)+    anchor pk ty = CommitmentOutput anchor_output_value+      (to_p2wsh (anchor_script pk)) ty -      !dustLimit = unDustLimit (cc_dust_limit ctx)+    local_anchor = case fmt of+      Anchors | not (null to_local) || has_htlcs ->+        [anchor (ck_local_funding keys) OutputLocalAnchor]+      _ -> [] -      -- Build HTLC outputs-      !htlcOutputs = map (htlcOutput ctx) untrimmedHtlcs+    remote_anchor = case fmt of+      Anchors | not (null to_remote) || has_htlcs ->+        [anchor (ck_remote_funding keys) OutputRemoteAnchor]+      _ -> [] -      -- Build to_local output if above dust-      !toLocalOutput =-        if unSatoshi toLocalSat >= unSatoshi dustLimit-        then-          let !script = to_p2wsh $ to_local_script-                (ck_revocation_pubkey $ cc_keys ctx)-                (cc_to_self_delay ctx)-                (ck_local_delayed $ cc_keys ctx)-          in [TxOutput toLocalSat script OutputToLocal]-        else []+    htlcs = fmap htlc_output untrimmed -      -- Build to_remote output if above dust-      !toRemoteOutput =-        if unSatoshi toRemoteSat >= unSatoshi dustLimit-        then-          let !script = if has_anchors (cc_features ctx)-                then to_p2wsh $ to_remote_script-                       (ck_remote_payment $ cc_keys ctx)-                       (cc_features ctx)-                else to_remote_script-                       (ck_remote_payment $ cc_keys ctx)-                       (cc_features ctx)-          in [TxOutput toRemoteSat script OutputToRemote]-        else []+    htlc_output h =+      let !script = case htlc_direction h of+            HTLCOffered  -> offered_htlc_script+              (ck_revocation_pubkey keys) (ck_remote_htlc keys)+              (ck_local_htlc keys) (htlc_payment_hash h) fmt+            HTLCReceived -> received_htlc_script+              (ck_revocation_pubkey keys) (ck_remote_htlc keys)+              (ck_local_htlc keys) (htlc_payment_hash h)+              (htlc_cltv_expiry h) fmt+      in  CommitmentOutput (BOLT1.msat_to_sat (htlc_amount_msat h))+            (to_p2wsh script) (OutputHTLC h) -      -- Build anchor outputs if option_anchors-      !hasUntrimmedHtlcs = not (null untrimmedHtlcs)-      !toLocalExists = not (null toLocalOutput)-      !toRemoteExists = not (null toRemoteOutput)+-- BIP69+CLTV order: value, then scriptPubKey, then (for HTLCs) CLTV+-- expiry.+sort_outputs :: [CommitmentOutput] -> [CommitmentOutput]+sort_outputs = sortBy cmp where+  cmp a b = compare (co_value a) (co_value b)+         <> compare (co_script a) (co_script b)+         <> compare (cltv (co_type a)) (cltv (co_type b))+  cltv t = case t of+    OutputHTLC h -> Just (htlc_cltv_expiry h)+    _            -> Nothing -      !localAnchorOutput =-        if has_anchors (cc_features ctx) &&-           (toLocalExists || hasUntrimmedHtlcs)-        then-          let !script = to_p2wsh $ anchor_script-                (ck_local_funding $ cc_keys ctx)-          in [TxOutput anchor_output_value script OutputLocalAnchor]-        else []+-- HTLC transactions ---------------------------------------------------------- -      !remoteAnchorOutput =-        if has_anchors (cc_features ctx) &&-           (toRemoteExists || hasUntrimmedHtlcs)-        then-          let !script = to_p2wsh $ anchor_script-                (ck_remote_funding $ cc_keys ctx)-          in [TxOutput anchor_output_value script OutputRemoteAnchor]-        else []+-- | The parameters of an HTLC-success or HTLC-timeout transaction,+--   spending an HTLC output of the owner's commitment transaction.+data HTLCContext = HTLCContext+  { hc_commitment_txid   :: !TxId+  , hc_output_index      :: {-# UNPACK #-} !Word32+  , hc_htlc              :: !HTLC+  , hc_to_self_delay     :: {-# UNPACK #-} !ToSelfDelay+  , hc_feerate           :: {-# UNPACK #-} !FeeratePerKw+  , hc_format            :: !CommitmentFormat+  , hc_revocation_pubkey :: !RevocationPubkey+  , hc_local_delayed     :: !LocalDelayedPubkey+  } deriving (Eq, Show, Generic) -      -- Combine and sort all outputs-      !allOutputs = toLocalOutput ++ toRemoteOutput ++-                    localAnchorOutput ++ remoteAnchorOutput ++-                    htlcOutputs-      !sortedOutputs = sort_outputs allOutputs+instance NFData HTLCContext -  in CommitmentTx-       { ctx_version = 2-       , ctx_locktime = locktime-       , ctx_input_outpoint = cc_funding_outpoint ctx-       , ctx_input_sequence = inputSeq-       , ctx_outputs = sortedOutputs-       , ctx_funding_script = fundingScript-       }-{-# INLINE build_commitment_tx #-}+-- | An unsigned HTLC-success or HTLC-timeout transaction.+data HTLCTx = HTLCTx+  { htx_version        :: {-# UNPACK #-} !Word32+  , htx_locktime       :: {-# UNPACK #-} !Locktime+  , htx_input_outpoint :: {-# UNPACK #-} !OutPoint+  , htx_input_sequence :: {-# UNPACK #-} !Sequence+  , htx_output_value   :: {-# UNPACK #-} !Satoshi+  , htx_output_script  :: !Script+  } deriving (Eq, Show, Generic) --- | Build an HTLC output for commitment transaction.-htlcOutput :: CommitmentContext -> HTLC -> TxOutput-htlcOutput ctx htlc =-  let !amountSat = msat_to_sat (htlc_amount_msat htlc)-      !keys = cc_keys ctx-      !features = cc_features ctx-      !expiry = htlc_cltv_expiry htlc-  in case htlc_direction htlc of-       HTLCOffered ->-         let !script = to_p2wsh $ offered_htlc_script-               (ck_revocation_pubkey keys)-               (ck_remote_htlc keys)-               (ck_local_htlc keys)-               (htlc_payment_hash htlc)-               features-         in TxOutput amountSat script (OutputOfferedHTLC expiry)-       HTLCReceived ->-         let !script = to_p2wsh $ received_htlc_script-               (ck_revocation_pubkey keys)-               (ck_remote_htlc keys)-               (ck_local_htlc keys)-               (htlc_payment_hash htlc)-               expiry-               features-         in TxOutput amountSat script (OutputReceivedHTLC expiry)-{-# INLINE htlcOutput #-}+instance NFData HTLCTx --- HTLC transactions -----------------------------------------------------------+-- | Build the second-stage transaction for an HTLC output of the+--   owner's commitment transaction: HTLC-timeout (locktime+--   @cltv_expiry@) for an offered HTLC, and HTLC-success (locktime 0)+--   for a received one.+--+--   The output pays the HTLC amount, less the HTLC transaction fee+--   (zero under 'Anchors'), to the 'to_local_script' of the owner's+--   keys. Fails if the fee exceeds the amount (such an HTLC is always+--   trimmed).+build_htlc_tx :: HTLCContext -> Maybe HTLCTx+build_htlc_tx ctx = do+    value <- BOLT1.sub_sat (BOLT1.msat_to_sat (htlc_amount_msat h)) fee+    pure HTLCTx+      { htx_version        = 2+      , htx_locktime       = locktime+      , htx_input_outpoint =+          OutPoint (hc_commitment_txid ctx) (hc_output_index ctx)+      , htx_input_sequence = case fmt of+          StaticRemotekey -> Sequence 0+          Anchors         -> Sequence 1+      , htx_output_value   = value+      , htx_output_script  = to_p2wsh $ to_local_script+          (hc_revocation_pubkey ctx) (hc_to_self_delay ctx)+          (hc_local_delayed ctx)+      }+  where+    !h = hc_htlc ctx+    !fmt = hc_format ctx+    (!fee, !locktime) = case htlc_direction h of+      HTLCOffered  ->+        let CltvExpiry e = htlc_cltv_expiry h+        in  (htlc_timeout_fee (hc_feerate ctx) fmt, Locktime e)+      HTLCReceived ->+        (htlc_success_fee (hc_feerate ctx) fmt, Locktime 0) --- | Context for building HTLC transactions.-data HTLCContext = HTLCContext-  { hc_commitment_txid     :: !TxId-  , hc_output_index        :: {-# UNPACK #-} !Word32-  , hc_htlc                :: !HTLC-  , hc_to_self_delay       :: !ToSelfDelay-  , hc_feerate             :: !FeeratePerKw-  , hc_features            :: !ChannelFeatures-  , hc_revocation_pubkey   :: !RevocationPubkey-  , hc_local_delayed       :: !LocalDelayedPubkey-  } deriving (Eq, Show, Generic)+-- closing transactions ------------------------------------------------------- --- | An HTLC transaction (timeout or success).-data HTLCTx = HTLCTx-  { htx_version            :: {-# UNPACK #-} !Word32-  , htx_locktime           :: !Locktime-  , htx_input_outpoint     :: !Outpoint-  , htx_input_sequence     :: !Sequence-  , htx_output_value       :: !Satoshi-  , htx_output_script      :: !Script+-- | A closing transaction output.+data ClosingOutput = ClosingOutput+  { clo_value  :: {-# UNPACK #-} !Satoshi+  , clo_script :: !Script     -- ^ scriptPubKey   } deriving (Eq, Show, Generic) --- | Internal helper for HTLC transaction construction.------ Both HTLC-timeout and HTLC-success transactions share the same--- structure, differing only in locktime and fee calculation.-build_htlc_tx_common-  :: HTLCContext-  -> Locktime           -- ^ Transaction locktime-  -> Satoshi            -- ^ Fee to subtract from output-  -> HTLCTx-build_htlc_tx_common ctx locktime fee =-  let !amountSat = msat_to_sat (htlc_amount_msat $ hc_htlc ctx)-      !outputValue = if unSatoshi amountSat >= unSatoshi fee-                     then Satoshi (unSatoshi amountSat - unSatoshi fee)-                     else Satoshi 0-      !inputSeq = if has_anchors (hc_features ctx)-                   then Sequence 1-                   else Sequence 0-      !outpoint = Outpoint (hc_commitment_txid ctx) (hc_output_index ctx)-      !outputScript = to_p2wsh $ htlc_output_script-        (hc_revocation_pubkey ctx)-        (hc_to_self_delay ctx)-        (hc_local_delayed ctx)-  in HTLCTx-       { htx_version = 2-       , htx_locktime = locktime-       , htx_input_outpoint = outpoint-       , htx_input_sequence = inputSeq-       , htx_output_value = outputValue-       , htx_output_script = outputScript-       }-{-# INLINE build_htlc_tx_common #-}+instance NFData ClosingOutput --- | Build an HTLC-timeout transaction.------ * locktime: cltv_expiry--- * sequence: 0 (or 1 with option_anchors)--- * output: to_local style script with revocation and delayed paths-build_htlc_timeout_tx :: HTLCContext -> HTLCTx-build_htlc_timeout_tx ctx =-  let !fee = htlc_timeout_fee (hc_feerate ctx) (hc_features ctx)-      !locktime = Locktime (unCltvExpiry $ htlc_cltv_expiry $ hc_htlc ctx)-  in build_htlc_tx_common ctx locktime fee-{-# INLINE build_htlc_timeout_tx #-}+-- | An unsigned closing transaction.+data ClosingTx = ClosingTx+  { cltx_version        :: {-# UNPACK #-} !Word32+  , cltx_locktime       :: {-# UNPACK #-} !Locktime+  , cltx_input_outpoint :: {-# UNPACK #-} !OutPoint+  , cltx_input_sequence :: {-# UNPACK #-} !Sequence+  , cltx_outputs        :: !(NonEmpty ClosingOutput)+    -- ^ in BIP69 order+  } deriving (Eq, Show, Generic) --- | Build an HTLC-success transaction.------ * locktime: 0--- * sequence: 0 (or 1 with option_anchors)--- * output: to_local style script with revocation and delayed paths-build_htlc_success_tx :: HTLCContext -> HTLCTx-build_htlc_success_tx ctx =-  let !fee = htlc_success_fee (hc_feerate ctx) (hc_features ctx)-  in build_htlc_tx_common ctx (Locktime 0) fee-{-# INLINE build_htlc_success_tx #-}+instance NFData ClosingTx --- closing transaction ---------------------------------------------------------+-- | Which outputs an @option_simple_close@ closing transaction has,+--   matching the signature fields of @closing_complete@.+data ClosingOutputs+  = CloserOutputOnly+  | CloseeOutputOnly+  | CloserAndCloseeOutputs+  deriving (Eq, Show, Generic) --- | Context for building closing transactions.+instance NFData ClosingOutputs++-- | The parameters of an @option_simple_close@ closing transaction,+--   as given by a @closing_complete@ message. data ClosingContext = ClosingContext-  { clc_funding_outpoint   :: !Outpoint-  , clc_local_amount       :: !Satoshi-  , clc_remote_amount      :: !Satoshi-  , clc_local_script       :: !Script-  , clc_remote_script      :: !Script-  , clc_local_dust_limit   :: !DustLimit-  , clc_remote_dust_limit  :: !DustLimit-  , clc_fee                :: !Satoshi-  , clc_is_funder          :: !Bool-  , clc_locktime           :: !Locktime-  , clc_funding_script     :: !Script+  { clc_funding_outpoint :: {-# UNPACK #-} !OutPoint+  , clc_closer_msat      :: {-# UNPACK #-} !MilliSatoshi+    -- ^ the closer's final balance+  , clc_closee_msat      :: {-# UNPACK #-} !MilliSatoshi+    -- ^ the closee's final balance+  , clc_closer_script    :: !Script+  , clc_closee_script    :: !Script+  , clc_fee              :: {-# UNPACK #-} !Satoshi+  , clc_locktime         :: {-# UNPACK #-} !Locktime+  , clc_outputs          :: !ClosingOutputs   } deriving (Eq, Show, Generic) --- | A closing transaction.-data ClosingTx = ClosingTx-  { cltx_version           :: {-# UNPACK #-} !Word32-  , cltx_locktime          :: !Locktime-  , cltx_input_outpoint    :: !Outpoint-  , cltx_input_sequence    :: !Sequence-  , cltx_outputs           :: ![TxOutput]-  , cltx_funding_script    :: !Script-  } deriving (Eq, Show, Generic)+instance NFData ClosingContext --- | Build a closing transaction (option_simple_close).+-- | Build an @option_simple_close@ closing transaction (BOLT #3's+--   "Closing Transaction"), for @closing_complete@ and @closing_sig@. ----- * locktime: from closing_complete message--- * sequence: 0xFFFFFFFD--- * outputs: sorted per BIP69-build_closing_tx :: ClosingContext -> ClosingTx-build_closing_tx ctx =-  let -- Subtract fee from closer-      !(localAmt, remoteAmt) = if clc_is_funder ctx-        then-          let !deducted = if unSatoshi (clc_local_amount ctx) >=-                             unSatoshi (clc_fee ctx)-                          then Satoshi (unSatoshi (clc_local_amount ctx) --                                        unSatoshi (clc_fee ctx))-                          else Satoshi 0-          in (deducted, clc_remote_amount ctx)-        else-          let !deducted = if unSatoshi (clc_remote_amount ctx) >=-                             unSatoshi (clc_fee ctx)-                          then Satoshi (unSatoshi (clc_remote_amount ctx) --                                        unSatoshi (clc_fee ctx))-                          else Satoshi 0-          in (clc_local_amount ctx, deducted)+--   The closer pays the fee: its output is its balance, rounded down+--   to whole satoshis, less the fee. An output whose scriptPubKey+--   starts with @OP_RETURN@ has amount 0. Only the chosen outputs are+--   included, and none is trimmed. Fails if the fee exceeds the+--   closer's balance.+build_closing_tx :: ClosingContext -> Maybe ClosingTx+build_closing_tx ctx = do+    closer_sat <-+      BOLT1.sub_sat (BOLT1.msat_to_sat (clc_closer_msat ctx)) (clc_fee ctx)+    let !closer = output closer_sat (clc_closer_script ctx)+        !closee = output (BOLT1.msat_to_sat (clc_closee_msat ctx))+                    (clc_closee_script ctx)+    pure ClosingTx+      { cltx_version        = 2+      , cltx_locktime       = clc_locktime ctx+      , cltx_input_outpoint = clc_funding_outpoint ctx+      , cltx_input_sequence = Sequence 0xFFFFFFFD+      , cltx_outputs        = case clc_outputs ctx of+          CloserOutputOnly       -> closer :| []+          CloseeOutputOnly       -> closee :| []+          CloserAndCloseeOutputs -> sort_closing (closer :| [closee])+      }+  where+    output amt spk@(Script s) = case BS.uncons s of+      Just (0x6a, _) -> ClosingOutput (sat 0) spk+      _              -> ClosingOutput amt spk -      -- Build outputs, omitting dust-      !localOutput =-        if unSatoshi localAmt >= unSatoshi (unDustLimit $ clc_local_dust_limit ctx)-        then [TxOutput localAmt (clc_local_script ctx) OutputToLocal]-        else []+-- | The parameters of a legacy closing transaction (for+--   @closing_signed@), from the point of view of the node producing+--   the signature (the "local" party).+data LegacyClosingContext = LegacyClosingContext+  { lcc_funding_outpoint :: {-# UNPACK #-} !OutPoint+  , lcc_local_msat       :: {-# UNPACK #-} !MilliSatoshi+  , lcc_remote_msat      :: {-# UNPACK #-} !MilliSatoshi+  , lcc_local_script     :: !Script+  , lcc_remote_script    :: !Script+  , lcc_dust_limit       :: {-# UNPACK #-} !DustLimit+    -- ^ the signer's @dust_limit_satoshis@+  , lcc_fee              :: {-# UNPACK #-} !Satoshi+  , lcc_is_funder        :: !Bool+    -- ^ whether the signer funded the channel, and so pays the fee+  , lcc_omit_local       :: !Bool+    -- ^ whether the signer eliminates its own output+  } deriving (Eq, Show, Generic) -      !remoteOutput =-        if unSatoshi remoteAmt >= unSatoshi (unDustLimit $ clc_remote_dust_limit ctx)-        then [TxOutput remoteAmt (clc_remote_script ctx) OutputToRemote]-        else []+instance NFData LegacyClosingContext -      !allOutputs = localOutput ++ remoteOutput-      !sortedOutputs = sort_outputs allOutputs+-- | Build a legacy closing transaction (BOLT #3's "Legacy Closing+--   Transaction"), for @closing_signed@.+--+--   Balances are rounded down to whole satoshis and the fee is taken+--   from the funder's output. Outputs below the signer's dust limit+--   are removed, as is the signer's own output if 'lcc_omit_local' is+--   set. Fails if the fee exceeds the funder's balance, or if no+--   output remains.+build_legacy_closing_tx :: LegacyClosingContext -> Maybe ClosingTx+build_legacy_closing_tx ctx = do+    let !local_sat  = BOLT1.msat_to_sat (lcc_local_msat ctx)+        !remote_sat = BOLT1.msat_to_sat (lcc_remote_msat ctx)+        DustLimit dust = lcc_dust_limit ctx+    (l, r) <-+      if   lcc_is_funder ctx+      then fmap (\x -> (x, remote_sat)) (BOLT1.sub_sat local_sat fee)+      else fmap (\x -> (local_sat, x)) (BOLT1.sub_sat remote_sat fee)+    outputs <- NE.nonEmpty $+         [ ClosingOutput l (lcc_local_script ctx)+         | not (lcc_omit_local ctx), l >= dust ]+      <> [ ClosingOutput r (lcc_remote_script ctx) | r >= dust ]+    pure ClosingTx+      { cltx_version        = 2+      , cltx_locktime       = Locktime 0+      , cltx_input_outpoint = lcc_funding_outpoint ctx+      , cltx_input_sequence = Sequence 0xFFFFFFFF+      , cltx_outputs        = sort_closing outputs+      }+  where+    !fee = lcc_fee ctx -  in ClosingTx-       { cltx_version = 2-       , cltx_locktime = clc_locktime ctx-       , cltx_input_outpoint = clc_funding_outpoint ctx-       , cltx_input_sequence = Sequence 0xFFFFFFFD-       , cltx_outputs = sortedOutputs-       , cltx_funding_script = clc_funding_script ctx-       }-{-# INLINE build_closing_tx #-}+-- BIP69 order: value, then scriptPubKey.+sort_closing :: NonEmpty ClosingOutput -> NonEmpty ClosingOutput+sort_closing = NE.sortBy $ \a b ->+  compare (clo_value a) (clo_value b) <> compare (clo_script a) (clo_script b) --- | Build a legacy closing transaction (closing_signed).------ * locktime: 0--- * sequence: 0xFFFFFFFF--- * outputs: sorted per BIP69-build_legacy_closing_tx :: ClosingContext -> ClosingTx-build_legacy_closing_tx ctx =-  let !result = build_closing_tx ctx-        { clc_locktime = Locktime 0 }-  in result { cltx_input_sequence = Sequence 0xFFFFFFFF }-{-# INLINE build_legacy_closing_tx #-}+-- fees ----------------------------------------------------------------------- --- fee calculation -------------------------------------------------------------+-- feerate * weight / 1000, saturating at 21 million BTC.+weight_fee :: FeeratePerKw -> Integer -> Satoshi+weight_fee (FeeratePerKw rate) weight =+  let !f = toInteger rate * weight `quot` 1000+  in  if   f > toInteger (BOLT1.un_satoshi BOLT1.max_satoshi)+      then BOLT1.max_satoshi+      else sat (fromInteger f) --- | Calculate the base commitment transaction fee.+-- | The base fee of a commitment transaction with a given number of+--   untrimmed HTLC outputs: ----- @fee = feerate_per_kw * weight / 1000@+--   @feerate_per_kw * (724 + 172 * num_htlcs) / 1000@ ----- where @weight = base_weight + 172 * num_htlcs@-commitment_fee :: FeeratePerKw -> ChannelFeatures -> Word64 -> Satoshi-commitment_fee feerate features numHtlcs =-  let !weight = commitment_weight features numHtlcs-      !fee = (fromIntegral (unFeeratePerKw feerate) * weight) `div` 1000-  in Satoshi fee-{-# INLINE commitment_fee #-}---- | Calculate commitment transaction weight.+--   with a base weight of 1124 rather than 724 under 'Anchors'. ----- @weight = base + 172 * num_htlcs@-commitment_weight :: ChannelFeatures -> Word64 -> Word64-commitment_weight features numHtlcs =-  let !base = if has_anchors features-              then commitment_weight_anchors-              else commitment_weight_no_anchors-  in base + htlc_output_weight * numHtlcs-{-# INLINE commitment_weight #-}+--   >>> commitment_fee (FeeratePerKw 5000) StaticRemotekey 2+--   Satoshi 5340+commitment_fee :: FeeratePerKw -> CommitmentFormat -> Word64 -> Satoshi+commitment_fee rate fmt n = weight_fee rate (base + 172 * toInteger n) where+  base = case fmt of+    StaticRemotekey -> 724+    Anchors         -> 1124 --- | Calculate HTLC-timeout transaction fee.+-- | The fee of an HTLC-timeout transaction: @feerate_per_kw * 663 /+--   1000@, or zero under 'Anchors'. ----- With option_anchors, fee is 0 (CPFP).--- Otherwise, @fee = feerate_per_kw * 663 / 1000@-htlc_timeout_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi-htlc_timeout_fee feerate features-  | has_anchors features = Satoshi 0-  | otherwise =-      let !weight = htlc_timeout_weight_no_anchors-          !fee = (fromIntegral (unFeeratePerKw feerate) * weight) `div` 1000-      in Satoshi fee-{-# INLINE htlc_timeout_fee #-}+--   >>> htlc_timeout_fee (FeeratePerKw 5000) StaticRemotekey+--   Satoshi 3315+htlc_timeout_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi+htlc_timeout_fee rate fmt = case fmt of+  StaticRemotekey -> weight_fee rate 663+  Anchors         -> sat 0 --- | Calculate HTLC-success transaction fee.+-- | The fee of an HTLC-success transaction: @feerate_per_kw * 703 /+--   1000@, or zero under 'Anchors'. ----- With option_anchors, fee is 0 (CPFP).--- Otherwise, @fee = feerate_per_kw * 703 / 1000@-htlc_success_fee :: FeeratePerKw -> ChannelFeatures -> Satoshi-htlc_success_fee feerate features-  | has_anchors features = Satoshi 0-  | otherwise =-      let !weight = htlc_success_weight_no_anchors-          !fee = (fromIntegral (unFeeratePerKw feerate) * weight) `div` 1000-      in Satoshi fee-{-# INLINE htlc_success_fee #-}+--   >>> htlc_success_fee (FeeratePerKw 5000) StaticRemotekey+--   Satoshi 3515+htlc_success_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi+htlc_success_fee rate fmt = case fmt of+  StaticRemotekey -> weight_fee rate 703+  Anchors         -> sat 0 --- trimming --------------------------------------------------------------------+-- trimming ------------------------------------------------------------------- --- | Calculate the trim threshold for an HTLC.+-- | The amount below which an HTLC in a given direction is trimmed:+--   the dust limit plus the fee of its HTLC transaction. ----- An HTLC is trimmed if:--- @amount < dust_limit + htlc_tx_fee@+--   >>> let Just d = fmap DustLimit (BOLT1.satoshi 546)+--   >>> htlc_trim_threshold d (FeeratePerKw 5000) StaticRemotekey HTLCOffered+--   Satoshi 3861 htlc_trim_threshold   :: DustLimit   -> FeeratePerKw-  -> ChannelFeatures+  -> CommitmentFormat   -> HTLCDirection   -> Satoshi-htlc_trim_threshold dust feerate features direction =-  let !dustVal = unDustLimit dust-      !htlcFee = case direction of-        HTLCOffered  -> htlc_timeout_fee feerate features-        HTLCReceived -> htlc_success_fee feerate features-  in Satoshi (unSatoshi dustVal + unSatoshi htlcFee)-{-# INLINE htlc_trim_threshold #-}+htlc_trim_threshold (DustLimit dust) rate fmt dir =+  let !fee = case dir of+        HTLCOffered  -> htlc_timeout_fee rate fmt+        HTLCReceived -> htlc_success_fee rate fmt+  in  fromMaybe BOLT1.max_satoshi (BOLT1.add_sat dust fee) --- | Check if an HTLC should be trimmed.------ An HTLC is trimmed if its amount minus the HTLC tx fee is below--- the dust limit.-is_trimmed :: DustLimit -> FeeratePerKw -> ChannelFeatures -> HTLC -> Bool-is_trimmed dust feerate features htlc =-  let !threshold = htlc_trim_threshold dust feerate features-                     (htlc_direction htlc)-      !amountSat = msat_to_sat (htlc_amount_msat htlc)-  in unSatoshi amountSat < unSatoshi threshold-{-# INLINE is_trimmed #-}+-- | Whether an HTLC is trimmed from a commitment transaction: whether+--   its amount, rounded down to whole satoshis, is below+--   'htlc_trim_threshold'.+is_trimmed :: DustLimit -> FeeratePerKw -> CommitmentFormat -> HTLC -> Bool+is_trimmed dust rate fmt h =+  BOLT1.msat_to_sat (htlc_amount_msat h)+    < htlc_trim_threshold dust rate fmt (htlc_direction h) --- | Filter HTLCs that are trimmed.-trimmed_htlcs-  :: DustLimit-  -> FeeratePerKw-  -> ChannelFeatures-  -> [HTLC]-  -> [HTLC]-trimmed_htlcs dust feerate features =-  filter (is_trimmed dust feerate features)-{-# INLINE trimmed_htlcs #-}+-- serialization -------------------------------------------------------------- --- | Filter HTLCs that are not trimmed.-untrimmed_htlcs-  :: DustLimit-  -> FeeratePerKw-  -> ChannelFeatures-  -> [HTLC]-  -> [HTLC]-untrimmed_htlcs dust feerate features =-  filter (not . is_trimmed dust feerate features)-{-# INLINE untrimmed_htlcs #-}+unsigned_tx+  :: Word32 -> Locktime -> OutPoint -> Sequence -> NonEmpty BT.TxOut+  -> BT.Tx+unsigned_tx version (Locktime lt) prevout (Sequence sq) outs = BT.Tx+  { BT.tx_version   = version+  , BT.tx_inputs    = BT.TxIn prevout BS.empty sq (BT.Witness []) :| []+  , BT.tx_outputs   = outs+  , BT.tx_locktime  = lt+  } --- output ordering -------------------------------------------------------------+txout :: Satoshi -> Script -> BT.TxOut+txout v (Script s) = BT.TxOut (BOLT1.un_satoshi v) s+{-# INLINE txout #-} --- | Sort outputs per BOLT #3 ordering.------ Outputs are sorted by:--- 1. Value (smallest first)--- 2. ScriptPubKey (lexicographic)--- 3. CLTV expiry (for HTLCs)-sort_outputs :: [TxOutput] -> [TxOutput]-sort_outputs = sortBy compareOutputs-{-# INLINE sort_outputs #-}+-- | The unsigned commitment transaction as a ppad-tx 'BT.Tx' (with no+--   witnesses), e.g. for computing its txid or sighashes.+commitment_to_tx :: CommitmentTx -> BT.Tx+commitment_to_tx c = unsigned_tx (ctx_version c) (ctx_locktime c)+  (ctx_input_outpoint c) (ctx_input_sequence c)+  (fmap (\o -> txout (co_value o) (co_script o)) (ctx_outputs c)) --- | Compare two outputs for ordering.-compareOutputs :: TxOutput -> TxOutput -> Ordering-compareOutputs o1 o2 =-  case compare (txout_value o1) (txout_value o2) of-    EQ -> case compare (unScript $ txout_script o1)-                       (unScript $ txout_script o2) of-            EQ -> compareCltvExpiry (txout_type o1) (txout_type o2)-            other -> other-    other -> other-{-# INLINE compareOutputs #-}+-- | The unsigned HTLC transaction as a ppad-tx 'BT.Tx'.+htlc_to_tx :: HTLCTx -> BT.Tx+htlc_to_tx h = unsigned_tx (htx_version h) (htx_locktime h)+  (htx_input_outpoint h) (htx_input_sequence h)+  (txout (htx_output_value h) (htx_output_script h) :| []) --- | Compare CLTV expiry for HTLC outputs.-compareCltvExpiry :: OutputType -> OutputType -> Ordering-compareCltvExpiry (OutputOfferedHTLC e1)  (OutputOfferedHTLC e2)  = compare e1 e2-compareCltvExpiry (OutputReceivedHTLC e1) (OutputReceivedHTLC e2) = compare e1 e2-compareCltvExpiry (OutputOfferedHTLC e1)  (OutputReceivedHTLC e2) = compare e1 e2-compareCltvExpiry (OutputReceivedHTLC e1) (OutputOfferedHTLC e2)  = compare e1 e2-compareCltvExpiry _ _ = EQ-{-# INLINE compareCltvExpiry #-}+-- | The unsigned closing transaction as a ppad-tx 'BT.Tx'.+closing_to_tx :: ClosingTx -> BT.Tx+closing_to_tx c = unsigned_tx (cltx_version c) (cltx_locktime c)+  (cltx_input_outpoint c) (cltx_input_sequence c)+  (fmap (\o -> txout (clo_value o) (clo_script o)) (cltx_outputs c))++-- | Serialize an unsigned commitment transaction, without witness+--   data. This is the form whose double-SHA256 is the txid; it is not+--   what segwit signatures commit to (see ppad-tx's+--   @Bitcoin.Prim.Tx.Sighash@). To broadcast, attach the witness to+--   'commitment_to_tx' and serialize with ppad-tx.+encode_commitment_tx :: CommitmentTx -> BS.ByteString+encode_commitment_tx = BT.to_bytes_legacy . commitment_to_tx++-- | Serialize an unsigned HTLC transaction, without witness data (cf.+--   'encode_commitment_tx').+encode_htlc_tx :: HTLCTx -> BS.ByteString+encode_htlc_tx = BT.to_bytes_legacy . htlc_to_tx++-- | Serialize an unsigned closing transaction, without witness data+--   (cf. 'encode_commitment_tx').+encode_closing_tx :: ClosingTx -> BS.ByteString+encode_closing_tx = BT.to_bytes_legacy . closing_to_tx
lib/Lightning/Protocol/BOLT3/Types.hs view
@@ -1,7 +1,6 @@-{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}  -- | -- Module: Lightning.Protocol.BOLT3.Types@@ -9,435 +8,404 @@ -- License: MIT -- Maintainer: Jared Tobin <jared@ppad.tech> ----- Core types for BOLT #3 transaction and script formats.+-- Types for BOLT #3 transaction and script formats.  module Lightning.Protocol.BOLT3.Types (-    -- * Monetary amounts-    Satoshi(..)-  , MilliSatoshi(..)-  , msat_to_sat-  , sat_to_msat--    -- * Keys and points-  , Pubkey(..)-  , pubkey+    -- * Points and keys+    PerCommitmentPoint(..)+  , RevocationBasepoint(..)+  , PaymentBasepoint(..)+  , DelayedPaymentBasepoint(..)+  , HtlcBasepoint(..)+  , Basepoints(..)+  , RevocationPubkey(..)+  , LocalDelayedPubkey(..)+  , LocalHtlcPubkey(..)+  , RemoteHtlcPubkey(..)+  , RemotePubkey(..)+  , FundingPubkey(..)   , Seckey(..)   , seckey-  , Point(..)-  , point--    -- * Hashes-  , PaymentHash(..)-  , payment_hash-  , PaymentPreimage(..)-  , payment_preimage+  , un_seckey -    -- * Transaction primitives-  , TxId(..)-  , txid-  , Outpoint(..)-  , Sequence(..)-  , Locktime(..)+    -- * Per-commitment secrets+  , Seed(..)+  , seed+  , un_seed+  , SecretIndex(..)+  , secret_index+  , un_secret_index+  , commitment_secret_index      -- * Channel parameters   , CommitmentNumber(..)   , commitment_number+  , un_commitment_number+  , next_commitment_number   , ToSelfDelay(..)   , CltvExpiry(..)   , DustLimit(..)   , FeeratePerKw(..)+  , Locktime(..)+  , Sequence(..)+  , CommitmentFormat(..) -    -- * HTLC types+    -- * HTLCs   , HTLC(..)   , HTLCDirection(..) -    -- * Basepoints-  , Basepoints(..)-  , PerCommitmentPoint(..)-  , PerCommitmentSecret(..)-  , per_commitment_secret-  , RevocationBasepoint(..)-  , PaymentBasepoint(..)-  , DelayedPaymentBasepoint(..)-  , HtlcBasepoint(..)--    -- * Derived keys-  , LocalPubkey(..)-  , RemotePubkey(..)-  , LocalDelayedPubkey(..)-  , RemoteDelayedPubkey(..)-  , LocalHtlcPubkey(..)-  , RemoteHtlcPubkey(..)-  , RevocationPubkey(..)-  , FundingPubkey(..)--    -- * Script and witness+    -- * Scripts   , Script(..)-  , Witness(..) -    -- * Channel options-  , ChannelFeatures(..)-  , has_anchors--    -- * Transaction weights (constants)-  , commitment_weight_no_anchors-  , commitment_weight_anchors-  , htlc_timeout_weight_no_anchors-  , htlc_timeout_weight_anchors-  , htlc_success_weight_no_anchors-  , htlc_success_weight_anchors-  , htlc_output_weight--    -- * Dust thresholds (constants)+    -- * Dust thresholds   , dust_p2pkh   , dust_p2sh   , dust_p2wpkh   , dust_p2wsh   , anchor_output_value++    -- * Internal+  , sat+  , internal_error   ) where -import Data.Word (Word16, Word32, Word64)+import Control.DeepSeq (NFData(..))+import qualified Crypto.Curve.Secp256k1 as S import qualified Data.ByteString as BS+import Data.Word (Word16, Word32, Word64) import GHC.Generics (Generic)+import Lightning.Protocol.BOLT1 (Point, Satoshi, MilliSatoshi, PaymentHash)+import qualified Lightning.Protocol.BOLT1 as BOLT1 --- monetary amounts ------------------------------------------------------------+-- internal ------------------------------------------------------------------ --- | Amount in satoshis.-newtype Satoshi = Satoshi { unSatoshi :: Word64 }-  deriving (Eq, Ord, Show, Generic, Num)+-- Abort on a state that the surrounding code rules out.+internal_error :: a+internal_error = error "ppad-bolt3: internal error, please report a bug!"+{-# NOINLINE internal_error #-} --- | Amount in millisatoshis.-newtype MilliSatoshi = MilliSatoshi { unMilliSatoshi :: Word64 }-  deriving (Eq, Ord, Show, Generic, Num)+-- A 'Satoshi' amount known to be at most 21 million BTC (a constant,+-- or a value bounded by construction).+sat :: Word64 -> Satoshi+sat w = case BOLT1.satoshi w of+  Just s  -> s+  Nothing -> internal_error+{-# INLINE sat #-} --- | Convert millisatoshis to satoshis (rounds down).-msat_to_sat :: MilliSatoshi -> Satoshi-msat_to_sat (MilliSatoshi m) = Satoshi (m `div` 1000)-{-# INLINE msat_to_sat #-}+-- points and keys ------------------------------------------------------------ --- | Convert satoshis to millisatoshis.-sat_to_msat :: Satoshi -> MilliSatoshi-sat_to_msat (Satoshi s) = MilliSatoshi (s * 1000)-{-# INLINE sat_to_msat #-}+-- | A per-commitment point, from which a commitment's keys are+--   derived.+newtype PerCommitmentPoint = PerCommitmentPoint Point+  deriving (Eq, Ord, Show, Generic) --- keys and points -------------------------------------------------------------+instance NFData PerCommitmentPoint --- | Compressed public key (33 bytes).-newtype Pubkey = Pubkey { unPubkey :: BS.ByteString }+-- | A @revocation_basepoint@.+newtype RevocationBasepoint = RevocationBasepoint Point   deriving (Eq, Ord, Show, Generic) --- | Parse a 33-byte compressed public key.------ Returns Nothing if the input is not exactly 33 bytes.------ >>> pubkey (BS.replicate 33 0x02)--- Just (Pubkey ...)--- >>> pubkey (BS.replicate 32 0x02)--- Nothing-pubkey :: BS.ByteString -> Maybe Pubkey-pubkey bs-  | BS.length bs == 33 = Just (Pubkey bs)-  | otherwise = Nothing-{-# INLINE pubkey #-}+instance NFData RevocationBasepoint --- | Secret key (32 bytes).-newtype Seckey = Seckey { unSeckey :: BS.ByteString }-  deriving (Eq, Generic)+-- | A @payment_basepoint@.+newtype PaymentBasepoint = PaymentBasepoint Point+  deriving (Eq, Ord, Show, Generic) --- Don't show secret keys-instance Show Seckey where-  show _ = "Seckey <redacted>"+instance NFData PaymentBasepoint --- | Parse a 32-byte secret key.------ Returns Nothing if the input is not exactly 32 bytes.-seckey :: BS.ByteString -> Maybe Seckey-seckey bs-  | BS.length bs == 32 = Just (Seckey bs)-  | otherwise = Nothing-{-# INLINE seckey #-}+-- | A @delayed_payment_basepoint@.+newtype DelayedPaymentBasepoint = DelayedPaymentBasepoint Point+  deriving (Eq, Ord, Show, Generic) --- | Elliptic curve point (33-byte compressed form).-newtype Point = Point { unPoint :: BS.ByteString }+instance NFData DelayedPaymentBasepoint++-- | An @htlc_basepoint@.+newtype HtlcBasepoint = HtlcBasepoint Point   deriving (Eq, Ord, Show, Generic) --- | Parse a 33-byte elliptic curve point.------ Returns Nothing if the input is not exactly 33 bytes.-point :: BS.ByteString -> Maybe Point-point bs-  | BS.length bs == 33 = Just (Point bs)-  | otherwise = Nothing-{-# INLINE point #-}+instance NFData HtlcBasepoint --- hashes ----------------------------------------------------------------------+-- | The four basepoints a party announces in @open_channel@ or+--   @accept_channel@.+data Basepoints = Basepoints+  { bp_revocation      :: !RevocationBasepoint+  , bp_payment         :: !PaymentBasepoint+  , bp_delayed_payment :: !DelayedPaymentBasepoint+  , bp_htlc            :: !HtlcBasepoint+  } deriving (Eq, Show, Generic) --- | Payment hash (32 bytes, SHA256 of preimage).-newtype PaymentHash = PaymentHash { unPaymentHash :: BS.ByteString }+instance NFData Basepoints++-- | A commitment's @revocationpubkey@.+newtype RevocationPubkey = RevocationPubkey Point   deriving (Eq, Ord, Show, Generic) --- | Parse a 32-byte payment hash.------ Returns Nothing if the input is not exactly 32 bytes.-payment_hash :: BS.ByteString -> Maybe PaymentHash-payment_hash bs-  | BS.length bs == 32 = Just (PaymentHash bs)-  | otherwise = Nothing-{-# INLINE payment_hash #-}+instance NFData RevocationPubkey --- | Payment preimage (32 bytes).-newtype PaymentPreimage = PaymentPreimage { unPaymentPreimage :: BS.ByteString }-  deriving (Eq, Generic)+-- | A commitment's @local_delayedpubkey@: the owner's key for its+--   delayed outputs.+newtype LocalDelayedPubkey = LocalDelayedPubkey Point+  deriving (Eq, Ord, Show, Generic) -instance Show PaymentPreimage where-  show _ = "PaymentPreimage <redacted>"+instance NFData LocalDelayedPubkey --- | Parse a 32-byte payment preimage.------ Returns Nothing if the input is not exactly 32 bytes.-payment_preimage :: BS.ByteString -> Maybe PaymentPreimage-payment_preimage bs-  | BS.length bs == 32 = Just (PaymentPreimage bs)-  | otherwise = Nothing-{-# INLINE payment_preimage #-}+-- | A commitment's @local_htlcpubkey@: the owner's HTLC key.+newtype LocalHtlcPubkey = LocalHtlcPubkey Point+  deriving (Eq, Ord, Show, Generic) --- transaction primitives ------------------------------------------------------+instance NFData LocalHtlcPubkey --- | Transaction ID (32 bytes, little-endian hash).-newtype TxId = TxId { unTxId :: BS.ByteString }+-- | A commitment's @remote_htlcpubkey@: the other party's HTLC key.+newtype RemoteHtlcPubkey = RemoteHtlcPubkey Point   deriving (Eq, Ord, Show, Generic) --- | Parse a 32-byte transaction ID.------ Returns Nothing if the input is not exactly 32 bytes.-txid :: BS.ByteString -> Maybe TxId-txid bs-  | BS.length bs == 32 = Just (TxId bs)-  | otherwise = Nothing-{-# INLINE txid #-}---- | Transaction outpoint (txid + output index).-data Outpoint = Outpoint-  { outpoint_txid  :: {-# UNPACK #-} !TxId-  , outpoint_index :: {-# UNPACK #-} !Word32-  } deriving (Eq, Ord, Show, Generic)+instance NFData RemoteHtlcPubkey --- | Transaction input sequence number.-newtype Sequence = Sequence { unSequence :: Word32 }-  deriving (Eq, Ord, Show, Generic, Num)+-- | A commitment's @remotepubkey@: the other party's+--   @payment_basepoint@, which its @to_remote@ output pays.+newtype RemotePubkey = RemotePubkey Point+  deriving (Eq, Ord, Show, Generic) --- | Transaction locktime.-newtype Locktime = Locktime { unLocktime :: Word32 }-  deriving (Eq, Ord, Show, Generic, Num)+instance NFData RemotePubkey --- channel parameters ----------------------------------------------------------+-- | A @funding_pubkey@.+newtype FundingPubkey = FundingPubkey Point+  deriving (Eq, Ord, Show, Generic) --- | 48-bit commitment number.-newtype CommitmentNumber = CommitmentNumber { unCommitmentNumber :: Word64 }-  deriving (Eq, Ord, Show, Generic, Num)+instance NFData FundingPubkey --- | Parse a 48-bit commitment number.+-- | A secp256k1 secret key: 32 bytes encoding an integer in+--   [1, n), for n the group order. ----- Returns Nothing if the value exceeds 2^48 - 1.-commitment_number :: Word64 -> Maybe CommitmentNumber-commitment_number n-  | n <= 281474976710655 = Just (CommitmentNumber n)-  | otherwise = Nothing-{-# INLINE commitment_number #-}+--   This is secret material: its 'Show' instance is redacted, and it+--   has no 'Eq' instance.+newtype Seckey = Seckey BS.ByteString --- | CSV delay for to_local outputs.-newtype ToSelfDelay = ToSelfDelay { unToSelfDelay :: Word16 }-  deriving (Eq, Ord, Show, Generic, Num)+instance Show Seckey where+  showsPrec d _ = showParen (d > 10) $ showString "Seckey <redacted>" --- | CLTV expiry for HTLCs.-newtype CltvExpiry = CltvExpiry { unCltvExpiry :: Word32 }-  deriving (Eq, Ord, Show, Generic, Num)+instance NFData Seckey where+  rnf (Seckey bs) = rnf bs --- | Dust limit threshold.-newtype DustLimit = DustLimit { unDustLimit :: Satoshi }-  deriving (Eq, Ord, Show, Generic)+-- | Construct a 'Seckey' from 32 big-endian bytes. Fails unless the+--   bytes encode an integer in [1, n).+--+--   >>> fmap (BS.length . un_seckey) (seckey (BS.replicate 32 0x01))+--   Just 32+--   >>> seckey (BS.replicate 32 0x00)+--   Nothing+--   >>> seckey (BS.replicate 32 0xff)+--   Nothing+seckey :: BS.ByteString -> Maybe Seckey+seckey bs = do+  w <- S.parse_int256 bs+  if   S.ge w+  then Just (Seckey bs)+  else Nothing --- | Fee rate in satoshis per 1000 weight units.-newtype FeeratePerKw = FeeratePerKw { unFeeratePerKw :: Word32 }-  deriving (Eq, Ord, Show, Generic, Num)+-- | The bytes of a 'Seckey'.+un_seckey :: Seckey -> BS.ByteString+un_seckey (Seckey bs) = bs+{-# INLINE un_seckey #-} --- HTLC types ------------------------------------------------------------------+-- per-commitment secrets ----------------------------------------------------- --- | Direction of an HTLC from the commitment tx owner's perspective.-data HTLCDirection-  = HTLCOffered   -- ^ We offered this HTLC (outgoing)-  | HTLCReceived  -- ^ We received this HTLC (incoming)-  deriving (Eq, Ord, Show, Generic)+-- | The 32-byte seed from which a party generates its per-commitment+--   secrets.+--+--   This is secret material: its 'Show' instance is redacted, and it+--   has no 'Eq' instance.+newtype Seed = Seed BS.ByteString --- | HTLC output details.+instance Show Seed where+  showsPrec d _ = showParen (d > 10) $ showString "Seed <redacted>"++instance NFData Seed where+  rnf (Seed bs) = rnf bs++-- | Construct a 'Seed' from exactly 32 bytes. ----- NOTE: No Ord instance is provided. BOLT #3 requires output ordering by--- amount then scriptPubKey, but scriptPubKey depends on derived keys which--- are not available here. Use 'sort_outputs' in Tx module for proper BIP69--- output ordering.-data HTLC = HTLC-  { htlc_direction    :: !HTLCDirection-  , htlc_amount_msat  :: {-# UNPACK #-} !MilliSatoshi-  , htlc_payment_hash :: {-# UNPACK #-} !PaymentHash-  , htlc_cltv_expiry  :: {-# UNPACK #-} !CltvExpiry-  } deriving (Eq, Show, Generic)+--   >>> fmap (BS.length . un_seed) (seed (BS.replicate 32 0xff))+--   Just 32+--   >>> seed (BS.replicate 31 0xff)+--   Nothing+seed :: BS.ByteString -> Maybe Seed+seed bs+  | BS.length bs == 32 = Just (Seed bs)+  | otherwise          = Nothing+{-# INLINE seed #-} --- basepoints ------------------------------------------------------------------+-- | The bytes of a 'Seed'.+un_seed :: Seed -> BS.ByteString+un_seed (Seed bs) = bs+{-# INLINE un_seed #-} --- | Per-commitment point (used to derive keys).-newtype PerCommitmentPoint = PerCommitmentPoint { unPerCommitmentPoint :: Point }+-- | The 48-bit index /I/ of a per-commitment secret. Secrets are+--   used from index 2^48 - 1 downwards.+newtype SecretIndex = SecretIndex Word64   deriving (Eq, Ord, Show, Generic) --- | Per-commitment secret (32 bytes).-newtype PerCommitmentSecret = PerCommitmentSecret-  { unPerCommitmentSecret :: BS.ByteString }-  deriving (Eq, Generic)--instance Show PerCommitmentSecret where-  show _ = "PerCommitmentSecret <redacted>"+instance NFData SecretIndex --- | Parse a 32-byte per-commitment secret.+-- | Construct a 'SecretIndex'. Fails at or above 2^48. ----- Returns Nothing if the input is not exactly 32 bytes.-per_commitment_secret :: BS.ByteString -> Maybe PerCommitmentSecret-per_commitment_secret bs-  | BS.length bs == 32 = Just (PerCommitmentSecret bs)-  | otherwise = Nothing-{-# INLINE per_commitment_secret #-}+--   >>> secret_index 281474976710655+--   Just (SecretIndex 281474976710655)+--   >>> secret_index 281474976710656+--   Nothing+secret_index :: Word64 -> Maybe SecretIndex+secret_index w+  | w <= 0xFFFFFFFFFFFF = Just (SecretIndex w)+  | otherwise           = Nothing+{-# INLINE secret_index #-} --- | Revocation basepoint.-newtype RevocationBasepoint = RevocationBasepoint-  { unRevocationBasepoint :: Point }-  deriving (Eq, Ord, Show, Generic)+-- | The value of a 'SecretIndex'.+un_secret_index :: SecretIndex -> Word64+un_secret_index (SecretIndex w) = w+{-# INLINE un_secret_index #-} --- | Payment basepoint.-newtype PaymentBasepoint = PaymentBasepoint-  { unPaymentBasepoint :: Point }-  deriving (Eq, Ord, Show, Generic)+-- | The index of the per-commitment secret for a commitment number:+--   2^48 - 1 - n.+--+--   >>> fmap commitment_secret_index (commitment_number 0)+--   Just (SecretIndex 281474976710655)+commitment_secret_index :: CommitmentNumber -> SecretIndex+commitment_secret_index (CommitmentNumber n) = SecretIndex (0xFFFFFFFFFFFF - n)+{-# INLINE commitment_secret_index #-} --- | Delayed payment basepoint.-newtype DelayedPaymentBasepoint = DelayedPaymentBasepoint-  { unDelayedPaymentBasepoint :: Point }-  deriving (Eq, Ord, Show, Generic)+-- channel parameters --------------------------------------------------------- --- | HTLC basepoint.-newtype HtlcBasepoint = HtlcBasepoint { unHtlcBasepoint :: Point }+-- | A 48-bit commitment number.+newtype CommitmentNumber = CommitmentNumber Word64   deriving (Eq, Ord, Show, Generic) --- | Collection of all basepoints for one party.-data Basepoints = Basepoints-  { bp_revocation      :: !RevocationBasepoint-  , bp_payment         :: !PaymentBasepoint-  , bp_delayed_payment :: !DelayedPaymentBasepoint-  , bp_htlc            :: !HtlcBasepoint-  } deriving (Eq, Show, Generic)+instance NFData CommitmentNumber --- derived keys ----------------------------------------------------------------+-- | Construct a 'CommitmentNumber'. Fails at or above 2^48.+--+--   >>> commitment_number 42+--   Just (CommitmentNumber 42)+--   >>> commitment_number 281474976710656+--   Nothing+commitment_number :: Word64 -> Maybe CommitmentNumber+commitment_number n+  | n <= 0xFFFFFFFFFFFF = Just (CommitmentNumber n)+  | otherwise           = Nothing+{-# INLINE commitment_number #-} --- | Local pubkey (derived from payment_basepoint + per_commitment_point).-newtype LocalPubkey = LocalPubkey { unLocalPubkey :: Pubkey }-  deriving (Eq, Ord, Show, Generic)+-- | The value of a 'CommitmentNumber'.+un_commitment_number :: CommitmentNumber -> Word64+un_commitment_number (CommitmentNumber n) = n+{-# INLINE un_commitment_number #-} --- | Remote pubkey (simply the remote's payment_basepoint).-newtype RemotePubkey = RemotePubkey { unRemotePubkey :: Pubkey }-  deriving (Eq, Ord, Show, Generic)+-- | The next commitment number. Fails past 2^48 - 1.+--+--   >>> commitment_number 0 >>= next_commitment_number+--   Just (CommitmentNumber 1)+--   >>> commitment_number 281474976710655 >>= next_commitment_number+--   Nothing+next_commitment_number :: CommitmentNumber -> Maybe CommitmentNumber+next_commitment_number (CommitmentNumber n)+  | n < 0xFFFFFFFFFFFF = Just (CommitmentNumber (n + 1))+  | otherwise          = Nothing+{-# INLINE next_commitment_number #-} --- | Local delayed pubkey.-newtype LocalDelayedPubkey = LocalDelayedPubkey-  { unLocalDelayedPubkey :: Pubkey }+-- | The CSV delay (@to_self_delay@) on a commitment owner's outputs.+newtype ToSelfDelay = ToSelfDelay Word16   deriving (Eq, Ord, Show, Generic) --- | Remote delayed pubkey.-newtype RemoteDelayedPubkey = RemoteDelayedPubkey-  { unRemoteDelayedPubkey :: Pubkey }-  deriving (Eq, Ord, Show, Generic)+instance NFData ToSelfDelay --- | Local HTLC pubkey.-newtype LocalHtlcPubkey = LocalHtlcPubkey { unLocalHtlcPubkey :: Pubkey }+-- | An HTLC's absolute CLTV expiry.+newtype CltvExpiry = CltvExpiry Word32   deriving (Eq, Ord, Show, Generic) --- | Remote HTLC pubkey.-newtype RemoteHtlcPubkey = RemoteHtlcPubkey { unRemoteHtlcPubkey :: Pubkey }-  deriving (Eq, Ord, Show, Generic)+instance NFData CltvExpiry --- | Revocation pubkey (derived from revocation_basepoint + per_commitment).-newtype RevocationPubkey = RevocationPubkey { unRevocationPubkey :: Pubkey }+-- | A @dust_limit_satoshis@ threshold.+newtype DustLimit = DustLimit Satoshi   deriving (Eq, Ord, Show, Generic) --- | Funding pubkey (used in 2-of-2 multisig).-newtype FundingPubkey = FundingPubkey { unFundingPubkey :: Pubkey }+instance NFData DustLimit++-- | A fee rate, in satoshis per 1000 weight units.+newtype FeeratePerKw = FeeratePerKw Word32   deriving (Eq, Ord, Show, Generic) --- script and witness ----------------------------------------------------------+instance NFData FeeratePerKw --- | Bitcoin script (serialized).-newtype Script = Script { unScript :: BS.ByteString }+-- | A transaction locktime.+newtype Locktime = Locktime Word32   deriving (Eq, Ord, Show, Generic) --- | Transaction witness stack.-newtype Witness = Witness { unWitness :: [BS.ByteString] }+instance NFData Locktime++-- | A transaction input's sequence number.+newtype Sequence = Sequence Word32   deriving (Eq, Ord, Show, Generic) --- channel options -------------------------------------------------------------+instance NFData Sequence --- | Channel feature flags relevant to BOLT #3.-data ChannelFeatures = ChannelFeatures-  { cf_option_anchors :: !Bool-  } deriving (Eq, Show, Generic)+-- | The commitment format a channel uses, as fixed by its+--   @channel_type@.+data CommitmentFormat+  = StaticRemotekey+    -- ^ @option_static_remotekey@ without anchors: @to_remote@ is+    --   P2WPKH and HTLC transactions pay their own fees.+  | Anchors+    -- ^ @option_anchors@: two anchor outputs, a CSV-locked+    --   @to_remote@, and zero-fee HTLC transactions.+  deriving (Eq, Ord, Show, Generic) --- | Check if option_anchors is enabled.-has_anchors :: ChannelFeatures -> Bool-has_anchors = cf_option_anchors-{-# INLINE has_anchors #-}+instance NFData CommitmentFormat --- transaction weights (constants from spec) -----------------------------------+-- HTLCs ---------------------------------------------------------------------- --- | Base commitment tx weight without option_anchors.-commitment_weight_no_anchors :: Word64-commitment_weight_no_anchors = 724+-- | The direction of an HTLC, from the commitment owner's point of+--   view.+data HTLCDirection+  = HTLCOffered   -- ^ offered by the owner+  | HTLCReceived  -- ^ received by the owner+  deriving (Eq, Ord, Show, Generic) --- | Base commitment tx weight with option_anchors.-commitment_weight_anchors :: Word64-commitment_weight_anchors = 1124+instance NFData HTLCDirection --- | HTLC-timeout tx weight without option_anchors.-htlc_timeout_weight_no_anchors :: Word64-htlc_timeout_weight_no_anchors = 663+-- | An HTLC committed to a commitment transaction.+data HTLC = HTLC+  { htlc_direction    :: !HTLCDirection+  , htlc_amount_msat  :: {-# UNPACK #-} !MilliSatoshi+  , htlc_payment_hash :: !PaymentHash+  , htlc_cltv_expiry  :: {-# UNPACK #-} !CltvExpiry+  } deriving (Eq, Show, Generic) --- | HTLC-timeout tx weight with option_anchors.-htlc_timeout_weight_anchors :: Word64-htlc_timeout_weight_anchors = 666+instance NFData HTLC --- | HTLC-success tx weight without option_anchors.-htlc_success_weight_no_anchors :: Word64-htlc_success_weight_no_anchors = 703+-- scripts -------------------------------------------------------------------- --- | HTLC-success tx weight with option_anchors.-htlc_success_weight_anchors :: Word64-htlc_success_weight_anchors = 706+-- | A serialized Bitcoin script.+newtype Script = Script BS.ByteString+  deriving (Eq, Ord, Show, Generic) --- | Weight added per HTLC output in commitment tx.-htlc_output_weight :: Word64-htlc_output_weight = 172+instance NFData Script --- dust thresholds (constants from Bitcoin Core) -------------------------------+-- dust thresholds ------------------------------------------------------------ --- | P2PKH dust threshold (546 satoshis).+-- | Bitcoin Core's P2PKH dust threshold (546 satoshis). dust_p2pkh :: Satoshi-dust_p2pkh = Satoshi 546+dust_p2pkh = sat 546 --- | P2SH dust threshold (540 satoshis).+-- | Bitcoin Core's P2SH dust threshold (540 satoshis). dust_p2sh :: Satoshi-dust_p2sh = Satoshi 540+dust_p2sh = sat 540 --- | P2WPKH dust threshold (294 satoshis).+-- | Bitcoin Core's P2WPKH dust threshold (294 satoshis). dust_p2wpkh :: Satoshi-dust_p2wpkh = Satoshi 294+dust_p2wpkh = sat 294 --- | P2WSH dust threshold (330 satoshis).+-- | Bitcoin Core's P2WSH dust threshold (330 satoshis). dust_p2wsh :: Satoshi-dust_p2wsh = Satoshi 330+dust_p2wsh = sat 330 --- | Fixed anchor output value (330 satoshis).+-- | The value of each anchor output (330 satoshis). anchor_output_value :: Satoshi-anchor_output_value = Satoshi 330+anchor_output_value = sat 330
− lib/Lightning/Protocol/BOLT3/Validate.hs
@@ -1,359 +0,0 @@-{-# OPTIONS_HADDOCK prune #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DeriveGeneric #-}---- |--- Module: Lightning.Protocol.BOLT3.Validate--- Copyright: (c) 2025 Jared Tobin--- License: MIT--- Maintainer: Jared Tobin <jared@ppad.tech>------ Stateless validation for BOLT #3 transactions.------ Provides validation for:------ * Commitment transaction structure and outputs--- * HTLC transaction structure--- * Closing transaction structure--- * Output ordering per BIP69+CLTV--- * Dust limit compliance--module Lightning.Protocol.BOLT3.Validate (-    -- * Validation errors-    ValidationError(..)--    -- * Commitment transaction validation-  , validate_commitment_tx-  , validate_commitment_locktime-  , validate_commitment_sequence--    -- * HTLC transaction validation-  , validate_htlc_tx-  , validate_htlc_timeout_tx-  , validate_htlc_success_tx--    -- * Closing transaction validation-  , validate_closing_tx-  , validate_legacy_closing_tx--    -- * Output validation-  , validate_output_ordering-  , validate_dust_limits-  , validate_anchor_outputs--    -- * Fee validation-  , validate_commitment_fee-  , validate_htlc_fee-  ) where--import Data.Bits ((.&.), shiftR)-import Data.Word (Word32, Word64)-import GHC.Generics (Generic)-import Lightning.Protocol.BOLT3.Types-import Lightning.Protocol.BOLT3.Tx---- validation errors --------------------------------------------------------------- | Errors that can occur during validation.-data ValidationError-  = InvalidVersion {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32-    -- ^ Expected version, actual version-  | InvalidLocktime {-# UNPACK #-} !Word32-    -- ^ Invalid locktime format-  | InvalidSequence {-# UNPACK #-} !Word32-    -- ^ Invalid sequence format-  | InvalidOutputOrdering-    -- ^ Outputs not in BIP69+CLTV order-  | DustLimitViolation {-# UNPACK #-} !Int !Satoshi !Satoshi-    -- ^ Output index, actual value, dust limit-  | MissingAnchorOutput-    -- ^ Expected anchor output not present-  | InvalidAnchorValue {-# UNPACK #-} !Satoshi-    -- ^ Anchor value not 330 satoshis-  | InvalidFee {-# UNPACK #-} !Satoshi {-# UNPACK #-} !Satoshi-    -- ^ Expected fee, actual fee-  | InvalidHTLCLocktime {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32-    -- ^ Expected locktime, actual locktime-  | InvalidHTLCSequence {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32-    -- ^ Expected sequence, actual sequence-  | NoOutputs-    -- ^ Transaction has no outputs-  | TooManyOutputs {-# UNPACK #-} !Int-    -- ^ More outputs than expected-  deriving (Eq, Show, Generic)---- commitment transaction validation ----------------------------------------------- | Validate a commitment transaction.------ Checks:------ * Version is 2--- * Locktime format (upper 8 bits = 0x20)--- * Sequence format (upper 8 bits = 0x80)--- * Output ordering per BIP69+CLTV--- * Dust limit compliance--- * Anchor outputs if option_anchors-validate_commitment_tx-  :: DustLimit-  -> ChannelFeatures-  -> CommitmentTx-  -> Either ValidationError ()-validate_commitment_tx dust features tx = do-  -- Version must be 2-  validateVersion 2 (ctx_version tx)-  -- Locktime format-  validate_commitment_locktime (ctx_locktime tx)-  -- Sequence format-  validate_commitment_sequence (ctx_input_sequence tx)-  -- Output ordering-  validate_output_ordering (ctx_outputs tx)-  -- Dust limits-  validate_dust_limits dust (ctx_outputs tx)-  -- Anchors if applicable-  if has_anchors features-    then validate_anchor_outputs (ctx_outputs tx)-    else pure ()-{-# INLINE validate_commitment_tx #-}---- | Validate commitment transaction locktime format.------ Upper 8 bits must be 0x20.-validate_commitment_locktime :: Locktime -> Either ValidationError ()-validate_commitment_locktime (Locktime lt) =-  let !upper = (lt `shiftR` 24) .&. 0xFF-  in if upper == 0x20-     then Right ()-     else Left (InvalidLocktime lt)-{-# INLINE validate_commitment_locktime #-}---- | Validate commitment transaction sequence format.------ Upper 8 bits must be 0x80.-validate_commitment_sequence :: Sequence -> Either ValidationError ()-validate_commitment_sequence (Sequence sq) =-  let !upper = (sq `shiftR` 24) .&. 0xFF-  in if upper == 0x80-     then Right ()-     else Left (InvalidSequence sq)-{-# INLINE validate_commitment_sequence #-}---- HTLC transaction validation ----------------------------------------------------- | Validate an HTLC transaction (timeout or success).------ Checks:------ * Version is 2--- * Single output-validate_htlc_tx :: HTLCTx -> Either ValidationError ()-validate_htlc_tx tx = do-  validateVersion 2 (htx_version tx)-  pure ()-{-# INLINE validate_htlc_tx #-}---- | Validate an HTLC-timeout transaction.------ Checks:------ * Base HTLC validation--- * Locktime equals HTLC cltv_expiry--- * Sequence is 0 (or 1 with option_anchors)-validate_htlc_timeout_tx-  :: ChannelFeatures-  -> CltvExpiry-  -> HTLCTx-  -> Either ValidationError ()-validate_htlc_timeout_tx features expiry tx = do-  validate_htlc_tx tx-  -- Locktime must be cltv_expiry-  let !expectedLt = unCltvExpiry expiry-      !actualLt = unLocktime (htx_locktime tx)-  if expectedLt == actualLt-    then pure ()-    else Left (InvalidHTLCLocktime expectedLt actualLt)-  -- Sequence-  let !expectedSeq = if has_anchors features then 1 else 0-      !actualSeq = unSequence (htx_input_sequence tx)-  if expectedSeq == actualSeq-    then pure ()-    else Left (InvalidHTLCSequence expectedSeq actualSeq)-{-# INLINE validate_htlc_timeout_tx #-}---- | Validate an HTLC-success transaction.------ Checks:------ * Base HTLC validation--- * Locktime is 0--- * Sequence is 0 (or 1 with option_anchors)-validate_htlc_success_tx-  :: ChannelFeatures-  -> HTLCTx-  -> Either ValidationError ()-validate_htlc_success_tx features tx = do-  validate_htlc_tx tx-  -- Locktime must be 0-  let !actualLt = unLocktime (htx_locktime tx)-  if actualLt == 0-    then pure ()-    else Left (InvalidHTLCLocktime 0 actualLt)-  -- Sequence-  let !expectedSeq = if has_anchors features then 1 else 0-      !actualSeq = unSequence (htx_input_sequence tx)-  if expectedSeq == actualSeq-    then pure ()-    else Left (InvalidHTLCSequence expectedSeq actualSeq)-{-# INLINE validate_htlc_success_tx #-}---- closing transaction validation -------------------------------------------------- | Validate a closing transaction (option_simple_close).------ Checks:------ * Version is 2--- * Sequence is 0xFFFFFFFD--- * At least one output--- * Output ordering per BIP69-validate_closing_tx :: ClosingTx -> Either ValidationError ()-validate_closing_tx tx = do-  validateVersion 2 (cltx_version tx)-  let !actualSeq = unSequence (cltx_input_sequence tx)-  if actualSeq == 0xFFFFFFFD-    then pure ()-    else Left (InvalidSequence actualSeq)-  validateOutputCount (cltx_outputs tx)-  validate_output_ordering (cltx_outputs tx)-{-# INLINE validate_closing_tx #-}---- | Validate a legacy closing transaction (closing_signed).------ Checks:------ * Version is 2--- * Locktime is 0--- * Sequence is 0xFFFFFFFF--- * At least one output--- * Output ordering per BIP69-validate_legacy_closing_tx :: ClosingTx -> Either ValidationError ()-validate_legacy_closing_tx tx = do-  validateVersion 2 (cltx_version tx)-  let !actualLt = unLocktime (cltx_locktime tx)-  if actualLt == 0-    then pure ()-    else Left (InvalidLocktime actualLt)-  let !actualSeq = unSequence (cltx_input_sequence tx)-  if actualSeq == 0xFFFFFFFF-    then pure ()-    else Left (InvalidSequence actualSeq)-  validateOutputCount (cltx_outputs tx)-  validate_output_ordering (cltx_outputs tx)-{-# INLINE validate_legacy_closing_tx #-}---- output validation --------------------------------------------------------------- | Validate output ordering per BIP69+CLTV.------ Outputs must be sorted by:--- 1. Value (smallest first)--- 2. ScriptPubKey (lexicographic)--- 3. CLTV expiry (for HTLC outputs)-validate_output_ordering :: [TxOutput] -> Either ValidationError ()-validate_output_ordering outputs =-  let !sorted = sort_outputs outputs-  in if outputs == sorted-     then Right ()-     else Left InvalidOutputOrdering-{-# INLINE validate_output_ordering #-}---- | Validate that no output violates dust limits.-validate_dust_limits-  :: DustLimit-  -> [TxOutput]-  -> Either ValidationError ()-validate_dust_limits dust = go 0 where-  !limit = unDustLimit dust-  go !_ [] = Right ()-  go !idx (out:rest) =-    let !val = txout_value out-    in case txout_type out of-         -- Anchors have fixed value, don't check against dust limit-         OutputLocalAnchor -> go (idx + 1) rest-         OutputRemoteAnchor -> go (idx + 1) rest-         -- All other outputs must be above dust-         _ -> if unSatoshi val >= unSatoshi limit-              then go (idx + 1) rest-              else Left (DustLimitViolation idx val limit)-{-# INLINE validate_dust_limits #-}---- | Validate anchor outputs are present and correctly valued.-validate_anchor_outputs :: [TxOutput] -> Either ValidationError ()-validate_anchor_outputs outputs =-  let !anchors = filter isAnchor outputs-  in if null anchors-     then Left MissingAnchorOutput-     else validateAnchorValues anchors-  where-    isAnchor out = case txout_type out of-      OutputLocalAnchor  -> True-      OutputRemoteAnchor -> True-      _ -> False--    validateAnchorValues [] = Right ()-    validateAnchorValues (a:as) =-      let !val = txout_value a-      in if val == anchor_output_value-         then validateAnchorValues as-         else Left (InvalidAnchorValue val)-{-# INLINE validate_anchor_outputs #-}---- fee validation ------------------------------------------------------------------ | Validate commitment transaction fee.------ Checks that the fee matches the expected calculation.-validate_commitment_fee-  :: FeeratePerKw-  -> ChannelFeatures-  -> Word64           -- ^ Number of untrimmed HTLCs-  -> Satoshi          -- ^ Actual fee-  -> Either ValidationError ()-validate_commitment_fee feerate features numHtlcs actualFee =-  let !expectedFee = commitment_fee feerate features numHtlcs-  in if actualFee == expectedFee-     then Right ()-     else Left (InvalidFee expectedFee actualFee)-{-# INLINE validate_commitment_fee #-}---- | Validate HTLC transaction fee.-validate_htlc_fee-  :: FeeratePerKw-  -> ChannelFeatures-  -> HTLCDirection-  -> Satoshi          -- ^ Actual fee-  -> Either ValidationError ()-validate_htlc_fee feerate features direction actualFee =-  let !expectedFee = case direction of-        HTLCOffered  -> htlc_timeout_fee feerate features-        HTLCReceived -> htlc_success_fee feerate features-  in if actualFee == expectedFee-     then Right ()-     else Left (InvalidFee expectedFee actualFee)-{-# INLINE validate_htlc_fee #-}---- helpers ------------------------------------------------------------------------- | Validate transaction version.-validateVersion :: Word32 -> Word32 -> Either ValidationError ()-validateVersion expected actual =-  if expected == actual-  then Right ()-  else Left (InvalidVersion expected actual)-{-# INLINE validateVersion #-}---- | Validate that transaction has at least one output.-validateOutputCount :: [TxOutput] -> Either ValidationError ()-validateOutputCount [] = Left NoOutputs-validateOutputCount _  = Right ()-{-# INLINE validateOutputCount #-}
ppad-bolt3.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               ppad-bolt3-version:            0.0.1+version:            0.1.0 synopsis:           Bitcoin transaction formats per BOLT #3 license:            MIT license-file:       LICENSE@@ -8,10 +8,10 @@ maintainer:         jared@ppad.tech category:           Cryptography build-type:         Simple-tested-with:        GHC == 9.10.3+tested-with:        GHC == { 9.10.3 } extra-doc-files:    CHANGELOG description:-  Bitcoin transaction formats for the Lightning Network, per+  Bitcoin transaction and script formats for the Lightning Network, per   [BOLT #3](https://github.com/lightning/bolts/blob/master/03-transactions.md).  source-repository head@@ -25,66 +25,87 @@       -Wall   exposed-modules:       Lightning.Protocol.BOLT3-      Lightning.Protocol.BOLT3.Decode-      Lightning.Protocol.BOLT3.Encode+  other-modules:       Lightning.Protocol.BOLT3.Keys       Lightning.Protocol.BOLT3.Scripts       Lightning.Protocol.BOLT3.Tx       Lightning.Protocol.BOLT3.Types-      Lightning.Protocol.BOLT3.Validate   build-depends:       base >= 4.9 && < 5     , bytestring >= 0.9 && < 0.13+    , deepseq >= 1.4 && < 1.6+    , ppad-bolt1 >= 0.1 && < 0.2+    , ppad-fixed >= 0.2 && < 0.3     , ppad-ripemd160 >= 0.1.4 && < 0.2-    , ppad-secp256k1 >= 0.5.4 && < 0.6+    , ppad-secp256k1 >= 0.5.8 && < 0.6     , ppad-sha256 >= 0.3.2 && < 0.4+    , ppad-tx >= 0.2 && < 0.3  test-suite bolt3-tests   type:                exitcode-stdio-1.0   default-language:    Haskell2010   hs-source-dirs:      test   main-is:             Main.hs+  other-modules:+      Vectors    ghc-options:-    -rtsopts -Wall -O2+    -rtsopts -Wall    build-depends:       base-    , base16-bytestring     , bytestring+    , ppad-base16+    , ppad-bolt1     , ppad-bolt3+    , ppad-secp256k1+    , ppad-sha256+    , ppad-tx+    , QuickCheck     , tasty     , tasty-hunit+    , tasty-quickcheck  benchmark bolt3-bench   type:                exitcode-stdio-1.0   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+    , 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+    , ppad-sha256+    , ppad-tx     , weigh-
test/Main.hs view
@@ -2,336 +2,1022 @@  module Main where -import qualified Data.ByteString as BS-import qualified Data.ByteString.Base16 as B16-import Data.Maybe (isJust, isNothing)-import Test.Tasty-import Test.Tasty.HUnit-import Lightning.Protocol.BOLT3--main :: IO ()-main = defaultMain $ testGroup "ppad-bolt3" [-    testGroup "Key derivation" [-      keyDerivationTests-    ]-  , testGroup "Secret generation" [-      secretGenerationTests-    ]-  , testGroup "Secret storage" [-      secretStorageTests-    ]-  , testGroup "Fee calculation" [-      feeCalculationTests-    ]-  , testGroup "Trimming" [-      trimmingTests-    ]-  , testGroup "Smart constructors" [-      smartConstructorTests-    ]-  ]---- hex decoding helper-hex :: BS.ByteString -> BS.ByteString-hex h = case B16.decode h of-  Right bs -> bs-  Left _ -> error "invalid hex"---- Key derivation test vectors from Appendix E -----------------------------------keyDerivationTests :: TestTree-keyDerivationTests = testGroup "BOLT #3 Appendix E" [-    testCase "derive_pubkey" $ do-      let basepoint = Point $ hex-            "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"-          perCommitmentPoint = PerCommitmentPoint $ Point $ hex-            "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"-          expected = hex-            "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"-      case derive_pubkey basepoint perCommitmentPoint of-        Nothing -> assertFailure "derive_pubkey returned Nothing"-        Just (Pubkey pk) -> pk @?= expected--  , testCase "derive_revocationpubkey" $ do-      let revocationBasepoint = RevocationBasepoint $ Point $ hex-            "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"-          perCommitmentPoint = PerCommitmentPoint $ Point $ hex-            "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"-          expected = hex-            "02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0"-      case derive_revocationpubkey revocationBasepoint perCommitmentPoint of-        Nothing -> assertFailure "derive_revocationpubkey returned Nothing"-        Just (RevocationPubkey (Pubkey pk)) -> pk @?= expected-  ]---- Secret generation test vectors from Appendix D --------------------------------secretGenerationTests :: TestTree-secretGenerationTests = testGroup "BOLT #3 Appendix D - Generation" [-    testCase "generate_from_seed 0 final node" $ do-      let seed = hex-            "0000000000000000000000000000000000000000000000000000000000000000"-          i = 281474976710655-          expected = hex-            "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148"-      generate_from_seed seed i @?= expected--  , testCase "generate_from_seed FF final node" $ do-      let seed = hex-            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"-          i = 281474976710655-          expected = hex-            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"-      generate_from_seed seed i @?= expected--  , testCase "generate_from_seed FF alternate bits 1" $ do-      let seed = hex-            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"-          i = 0xaaaaaaaaaaa-          expected = hex-            "56f4008fb007ca9acf0e15b054d5c9fd12ee06cea347914ddbaed70d1c13a528"-      generate_from_seed seed i @?= expected--  , testCase "generate_from_seed FF alternate bits 2" $ do-      let seed = hex-            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"-          i = 0x555555555555-          expected = hex-            "9015daaeb06dba4ccc05b91b2f73bd54405f2be9f217fbacd3c5ac2e62327d31"-      generate_from_seed seed i @?= expected--  , testCase "generate_from_seed 01 last nontrivial node" $ do-      let seed = hex-            "0101010101010101010101010101010101010101010101010101010101010101"-          i = 1-          expected = hex-            "915c75942a26bb3a433a8ce2cb0427c29ec6c1775cfc78328b57f6ba7bfeaa9c"-      generate_from_seed seed i @?= expected-  ]---- Secret storage test vectors from Appendix D -----------------------------------secretStorageTests :: TestTree-secretStorageTests = testGroup "BOLT #3 Appendix D - Storage" [-    testCase "insert_secret correct sequence" $ do-      let secrets = [-              (281474976710655, hex-                "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc")-            , (281474976710654, hex-                "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964")-            , (281474976710653, hex-                "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8")-            , (281474976710652, hex-                "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116")-            , (281474976710651, hex-                "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd")-            , (281474976710650, hex-                "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2")-            , (281474976710649, hex-                "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32")-            , (281474976710648, hex-                "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17")-            ]-      let insertAll store [] = Just store-          insertAll store ((idx, secret):rest) =-            case insert_secret secret idx store of-              Nothing -> Nothing-              Just store' -> insertAll store' rest-      case insertAll empty_store secrets of-        Nothing -> assertFailure "insert_secret failed on correct sequence"-        Just _ -> return ()--  , testCase "insert_secret #1 incorrect" $ do-      -- First secret is from wrong seed, second should fail-      let store0 = empty_store-      case insert_secret (hex-             "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148")-             281474976710655 store0 of-        Nothing -> assertFailure "First insert should succeed"-        Just store1 ->-          case insert_secret (hex-                 "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964")-                 281474976710654 store1 of-            Nothing -> return ()  -- Expected to fail-            Just _ -> assertFailure "Second insert should fail"-  ]---- Fee calculation tests ---------------------------------------------------------feeCalculationTests :: TestTree-feeCalculationTests = testGroup "Fee calculation" [-    testCase "commitment_fee no anchors, 0 htlcs" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          fee = commitment_fee feerate features 0-      fee @?= Satoshi 3620  -- 5000 * 724 / 1000 = 3620--  , testCase "commitment_fee no anchors, 2 htlcs" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          fee = commitment_fee feerate features 2-      -- weight = 724 + 172*2 = 1068-      -- fee = 5000 * 1068 / 1000 = 5340-      fee @?= Satoshi 5340--  , testCase "commitment_fee with anchors, 0 htlcs" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = True }-          fee = commitment_fee feerate features 0-      -- 5000 * 1124 / 1000 = 5620-      fee @?= Satoshi 5620--  , testCase "htlc_timeout_fee no anchors" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          fee = htlc_timeout_fee feerate features-      -- 5000 * 663 / 1000 = 3315-      fee @?= Satoshi 3315--  , testCase "htlc_success_fee no anchors" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          fee = htlc_success_fee feerate features-      -- 5000 * 703 / 1000 = 3515-      fee @?= Satoshi 3515--  , testCase "htlc_timeout_fee with anchors is 0" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = True }-          fee = htlc_timeout_fee feerate features-      fee @?= Satoshi 0--  , testCase "htlc_success_fee with anchors is 0" $ do-      let feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = True }-          fee = htlc_success_fee feerate features-      fee @?= Satoshi 0-  ]---- Trimming tests ----------------------------------------------------------------trimmingTests :: TestTree-trimmingTests = testGroup "HTLC trimming" [-    testCase "offered HTLC above threshold not trimmed" $ do-      let dust = DustLimit (Satoshi 546)-          feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          htlc = HTLC-            { htlc_direction = HTLCOffered-            , htlc_amount_msat = MilliSatoshi 5000000  -- 5000 sats-            , htlc_payment_hash = PaymentHash (BS.replicate 32 0)-            , htlc_cltv_expiry = CltvExpiry 500000-            }-      -- threshold = 546 + 3315 = 3861-      -- 5000 > 3861, so not trimmed-      is_trimmed dust feerate features htlc @?= False--  , testCase "offered HTLC below threshold is trimmed" $ do-      let dust = DustLimit (Satoshi 546)-          feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          htlc = HTLC-            { htlc_direction = HTLCOffered-            , htlc_amount_msat = MilliSatoshi 1000000  -- 1000 sats-            , htlc_payment_hash = PaymentHash (BS.replicate 32 0)-            , htlc_cltv_expiry = CltvExpiry 500000-            }-      -- threshold = 546 + 3315 = 3861-      -- 1000 < 3861, so trimmed-      is_trimmed dust feerate features htlc @?= True--  , testCase "received HTLC above threshold not trimmed" $ do-      let dust = DustLimit (Satoshi 546)-          feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          htlc = HTLC-            { htlc_direction = HTLCReceived-            , htlc_amount_msat = MilliSatoshi 7000000  -- 7000 sats-            , htlc_payment_hash = PaymentHash (BS.replicate 32 0)-            , htlc_cltv_expiry = CltvExpiry 500000-            }-      -- threshold = 546 + 3515 = 4061-      -- 7000 > 4061, so not trimmed-      is_trimmed dust feerate features htlc @?= False--  , testCase "received HTLC below threshold is trimmed" $ do-      let dust = DustLimit (Satoshi 546)-          feerate = FeeratePerKw 5000-          features = ChannelFeatures { cf_option_anchors = False }-          htlc = HTLC-            { htlc_direction = HTLCReceived-            , htlc_amount_msat = MilliSatoshi 800000  -- 800 sats-            , htlc_payment_hash = PaymentHash (BS.replicate 32 0)-            , htlc_cltv_expiry = CltvExpiry 500000-            }-      -- threshold = 546 + 3515 = 4061-      -- 800 < 4061, so trimmed-      is_trimmed dust feerate features htlc @?= True-  ]---- Smart constructor tests -------------------------------------------------------smartConstructorTests :: TestTree-smartConstructorTests = testGroup "validation" [-    -- 33-byte types-    testCase "pubkey accepts 33 bytes" $ do-      let bs = BS.replicate 33 0x02-      isJust (pubkey bs) @?= True-  , testCase "pubkey rejects 32 bytes" $ do-      let bs = BS.replicate 32 0x02-      isNothing (pubkey bs) @?= True-  , testCase "pubkey rejects 34 bytes" $ do-      let bs = BS.replicate 34 0x02-      isNothing (pubkey bs) @?= True-  , testCase "point accepts 33 bytes" $ do-      let bs = BS.replicate 33 0x03-      isJust (point bs) @?= True-  , testCase "point rejects 32 bytes" $ do-      let bs = BS.replicate 32 0x03-      isNothing (point bs) @?= True--    -- 32-byte types-  , testCase "seckey accepts 32 bytes" $ do-      let bs = BS.replicate 32 0x01-      isJust (seckey bs) @?= True-  , testCase "seckey rejects 31 bytes" $ do-      let bs = BS.replicate 31 0x01-      isNothing (seckey bs) @?= True-  , testCase "seckey rejects 33 bytes" $ do-      let bs = BS.replicate 33 0x01-      isNothing (seckey bs) @?= True-  , testCase "txid accepts 32 bytes" $ do-      let bs = BS.replicate 32 0x00-      isJust (txid bs) @?= True-  , testCase "txid rejects 31 bytes" $ do-      let bs = BS.replicate 31 0x00-      isNothing (txid bs) @?= True-  , testCase "payment_hash accepts 32 bytes" $ do-      let bs = BS.replicate 32 0xab-      isJust (payment_hash bs) @?= True-  , testCase "payment_hash rejects 33 bytes" $ do-      let bs = BS.replicate 33 0xab-      isNothing (payment_hash bs) @?= True-  , testCase "payment_preimage accepts 32 bytes" $ do-      let bs = BS.replicate 32 0xcd-      isJust (payment_preimage bs) @?= True-  , testCase "payment_preimage rejects 31 bytes" $ do-      let bs = BS.replicate 31 0xcd-      isNothing (payment_preimage bs) @?= True-  , testCase "per_commitment_secret accepts 32 bytes" $ do-      let bs = BS.replicate 32 0xef-      isJust (per_commitment_secret bs) @?= True-  , testCase "per_commitment_secret rejects 33 bytes" $ do-      let bs = BS.replicate 33 0xef-      isNothing (per_commitment_secret bs) @?= True--    -- 48-bit commitment number-  , testCase "commitment_number accepts 0" $ do-      isJust (commitment_number 0) @?= True-  , testCase "commitment_number accepts 2^48-1" $ do-      isJust (commitment_number 281474976710655) @?= True-  , testCase "commitment_number rejects 2^48" $ do-      isNothing (commitment_number 281474976710656) @?= True-  , testCase "commitment_number rejects maxBound Word64" $ do-      isNothing (commitment_number maxBound) @?= True-  ]+import qualified Bitcoin.Prim.Tx as BT+import qualified Bitcoin.Prim.Tx.Sighash as Sighash+import Control.Monad (foldM, forM_)+import qualified Crypto.Curve.Secp256k1 as S+import qualified Crypto.Hash.SHA256 as SHA256+import Data.Bits (xor)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base16 as B16+import qualified Data.List.NonEmpty as NE+import Data.Maybe (isJust, isNothing)+import Data.Word (Word8, Word64)+import qualified Lightning.Protocol.BOLT1 as BOLT1+import Lightning.Protocol.BOLT3+import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck+import Vectors++-- Shared wNAF context.+tex :: S.Context+tex = S.precompute+{-# NOINLINE tex #-}++main :: IO ()+main = defaultMain $ testGroup "ppad-bolt3" [+    keyTests+  , secretGenerationTests+  , secretStorageTests+  , fundingTests+  , testGroup "Commitment and HTLC transactions (Appendix C)" $+      fmap (commitVectorTests StaticRemotekey) appendix_c+  , testGroup "Commitment and HTLC transactions (Appendix F)" $+      fmap (commitVectorTests Anchors) appendix_f+  , commitmentTests+  , htlcTxTests+  , closingTests+  , scriptTests+  , feeTests+  , constructorTests+  , propertyTests+  ]++-- helpers --------------------------------------------------------------------++-- | Decode a hex literal, failing the enclosing test on bad input.+hex :: BS.ByteString -> IO BS.ByteString+hex h = case B16.decode h of+  Just bs -> pure bs+  Nothing -> assertFailure ("invalid hex literal: " ++ show h)++-- | Unwrap a Just, failing the enclosing test on Nothing.+need :: String -> Maybe a -> IO a+need msg = maybe (assertFailure msg) pure++pt :: BS.ByteString -> IO BOLT1.Point+pt h = hex h >>= need ("invalid point " ++ show h) . BOLT1.point++sat :: Word64 -> IO BOLT1.Satoshi+sat = need "invalid satoshi amount" . BOLT1.satoshi++msat :: Word64 -> IO BOLT1.MilliSatoshi+msat = need "invalid millisatoshi amount" . BOLT1.milli_satoshi++sk :: BS.ByteString -> IO Seckey+sk h = hex h >>= need ("invalid secret key " ++ show h) . seckey++pcsecret :: BS.ByteString -> IO BOLT1.PerCommitmentSecret+pcsecret h = hex h >>= need "invalid secret" . BOLT1.per_commitment_secret++idx :: Word64 -> IO SecretIndex+idx = need "invalid secret index" . secret_index++unpoint :: BOLT1.Point -> BS.ByteString+unpoint = BOLT1.un_point++unsecret :: BOLT1.PerCommitmentSecret -> BS.ByteString+unsecret = BOLT1.un_per_commitment_secret++-- | The compressed public key of a secret key.+pubkey_of :: Seckey -> Maybe BS.ByteString+pubkey_of k = do+  w <- S.parse_int256 (un_seckey k)+  S.serialize_point <$> S.derive_pub w++-- Appendix E, and the internal values of Appendix C --------------------------++e_base_secret, e_per_commitment_secret, e_base_point,+  e_per_commitment_point :: BS.ByteString+e_base_secret =+  "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"+e_per_commitment_secret =+  "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"+e_base_point =+  "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"+e_per_commitment_point =+  "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"++-- | Appendix C's (commented) derivation inputs.+c_local_delayed_payment_basepoint_secret, c_local_delayed_payment_basepoint,+  c_remote_revocation_basepoint, c_local_per_commitment_point,+  c_local_delayed_privkey :: BS.ByteString+c_local_delayed_payment_basepoint_secret =+  "3333333333333333333333333333333333333333333333333333333333333333"+c_local_delayed_payment_basepoint =+  "023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1"+c_remote_revocation_basepoint =+  "02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27"+c_local_per_commitment_point =+  "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"+c_local_delayed_privkey =+  "adf3464ce9c2f230fd2582fda4c6965e4993ca5524e8c9580e3df0cf226981ad"++keyTests :: TestTree+keyTests = testGroup "Key derivation (Appendix E)" [+    testCase "per_commitment_point from per_commitment_secret" $ do+      s <- pcsecret e_per_commitment_secret+      expected <- hex e_per_commitment_point+      PerCommitmentPoint p <- need "derive_per_commitment_point"+        (derive_per_commitment_point s)+      unpoint p @?= expected+      PerCommitmentPoint p' <- need "derive_per_commitment_point'"+        (derive_per_commitment_point' tex s)+      unpoint p' @?= expected++  , testCase "localpubkey from basepoint and per_commitment_point" $ do+      bp <- pt e_base_point+      pcp <- PerCommitmentPoint <$> pt e_per_commitment_point+      expected <- hex+        "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"+      fmap unpoint (derive_pubkey bp pcp) @?= Just expected++  , testCase "localprivkey from basepoint secret" $ do+      bs <- sk e_base_secret+      pcp <- PerCommitmentPoint <$> pt e_per_commitment_point+      expected <- hex+        "cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f"+      fmap un_seckey (derive_privkey bs pcp) @?= Just expected++  , testCase "revocationpubkey from basepoint and per_commitment_point" $ do+      rbp <- RevocationBasepoint <$> pt e_base_point+      pcp <- PerCommitmentPoint <$> pt e_per_commitment_point+      expected <- hex+        "02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0"+      fmap (\(RevocationPubkey p) -> unpoint p)+        (derive_revocationpubkey rbp pcp) @?= Just expected++  , testCase "revocationprivkey from secrets" $ do+      rbs <- sk e_base_secret+      pcs <- pcsecret e_per_commitment_secret+      expected <- hex+        "d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110"+      fmap un_seckey (derive_revocationprivkey rbs pcs) @?= Just expected++  , testCase "Appendix C local_delayed_privkey" $ do+      bs <- sk c_local_delayed_payment_basepoint_secret+      pcp <- PerCommitmentPoint <$> pt c_local_per_commitment_point+      expected <- hex c_local_delayed_privkey+      fmap un_seckey (derive_privkey bs pcp) @?= Just expected+      -- whose public key is Appendix C's local_delayedpubkey+      expected_pub <- hex c_local_delayedpubkey+      (pubkey_of =<< derive_privkey bs pcp) @?= Just expected_pub++  , testCase "Appendix C commitment keys from basepoints" $ do+      keys <- vectorKeys+      pcp <- PerCommitmentPoint <$> pt c_local_per_commitment_point+      local_bp <- pt c_local_payment_basepoint+      remote_bp <- pt c_remote_payment_basepoint+      local_delayed_bp <- pt c_local_delayed_payment_basepoint+      remote_rev_bp <- pt c_remote_revocation_basepoint+      let local = Basepoints+            { bp_revocation      = RevocationBasepoint local_bp+            , bp_payment         = PaymentBasepoint local_bp+            , bp_delayed_payment =+                DelayedPaymentBasepoint local_delayed_bp+            , bp_htlc            = HtlcBasepoint local_bp+            }+          remote = Basepoints+            { bp_revocation      = RevocationBasepoint remote_rev_bp+            , bp_payment         = PaymentBasepoint remote_bp+            , bp_delayed_payment = DelayedPaymentBasepoint remote_bp+            , bp_htlc            = HtlcBasepoint remote_bp+            }+          derived = derive_commitment_keys local (ck_local_funding keys)+            remote (ck_remote_funding keys) pcp+      derived @?= Just keys+      derive_commitment_keys' tex local (ck_local_funding keys)+        remote (ck_remote_funding keys) pcp @?= Just keys++  , testCase "rejects a basepoint off the curve" $ do+      bad <- pt+        "020000000000000000000000000000000000000000000000000000000000000000"+      pcp <- PerCommitmentPoint <$> pt e_per_commitment_point+      isNothing (derive_pubkey bad pcp) @?= True+      isNothing (derive_revocationpubkey (RevocationBasepoint bad) pcp)+        @?= True++  , testCase "rejects a per_commitment_point off the curve" $ do+      rbp <- RevocationBasepoint <$> pt e_base_point+      bad <- PerCommitmentPoint <$> pt+        "020000000000000000000000000000000000000000000000000000000000000000"+      isNothing (derive_revocationpubkey rbp bad) @?= True++  , testCase "rejects an invalid per_commitment_secret" $ do+      zero <- pcsecret+        "0000000000000000000000000000000000000000000000000000000000000000"+      big <- pcsecret+        "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      rbs <- sk e_base_secret+      isNothing (derive_per_commitment_point zero) @?= True+      isNothing (derive_per_commitment_point big) @?= True+      isNothing (derive_per_commitment_point' tex big) @?= True+      isNothing (derive_revocationprivkey rbs zero) @?= True+      isNothing (derive_revocationprivkey rbs big) @?= True+  ]++-- Appendix D: generation -----------------------------------------------------++secretGenerationTests :: TestTree+secretGenerationTests = testGroup "Secret generation (Appendix D)" [+    gen "generate_from_seed 0 final node"+      "0000000000000000000000000000000000000000000000000000000000000000"+      281474976710655+      "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148"+  , gen "generate_from_seed FF final node"+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      281474976710655+      "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+  , gen "generate_from_seed FF alternate bits 1"+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      0xaaaaaaaaaaa+      "56f4008fb007ca9acf0e15b054d5c9fd12ee06cea347914ddbaed70d1c13a528"+  , gen "generate_from_seed FF alternate bits 2"+      "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+      0x555555555555+      "9015daaeb06dba4ccc05b91b2f73bd54405f2be9f217fbacd3c5ac2e62327d31"+  , gen "generate_from_seed 01 last nontrivial node"+      "0101010101010101010101010101010101010101010101010101010101010101"+      1+      "915c75942a26bb3a433a8ce2cb0427c29ec6c1775cfc78328b57f6ba7bfeaa9c"+  , testCase "commitment_secret_index" $ do+      cn <- need "commitment_number" (commitment_number 42)+      un_secret_index (commitment_secret_index cn) @?= 0xFFFFFFFFFFFF - 42+  ]+  where+    gen name s i expected = testCase name $ do+      sd <- hex s >>= need "seed" . seed+      ix <- idx i+      e <- hex expected+      unsecret (generate_from_seed sd ix) @?= e++-- Appendix D: storage --------------------------------------------------------++secretStorageTests :: TestTree+secretStorageTests = testGroup "Secret storage (Appendix D)" $+     fmap storageVectorTest appendix_d_storage+  ++ [+    testCase "derive_old_secret after each correct insert" $ do+      known <- correctSequence+      let step (store, seen) (i, s) = do+            store' <- need ("rejected correct secret at I=" ++ show i)+                        (insert_secret s i store)+            let seen' = (i, s) : seen+            forM_ seen' $ \(j, t) ->+              assertEqual+                ("after I=" ++ show i ++ ", derive_old_secret " ++ show j)+                (Just (unsecret t))+                (fmap unsecret (derive_old_secret j store'))+            pure (store', seen')+      (store, _) <- foldM step (empty_secret_store, []) known+      -- the next index has not been received+      next <- idx 281474976710647+      isNothing (derive_old_secret next store) @?= True++  , testCase "un_secret_store/secret_store round trip" $ do+      known <- correctSequence+      store <- need "insert" (foldM (\st (i, s) -> insert_secret s i st)+                                    empty_secret_store known)+      store' <- need "secret_store" (secret_store (un_secret_store store))+      forM_ known $ \(i, s) ->+        fmap unsecret (derive_old_secret i store') @?= Just (unsecret s)+      -- entry order doesn't matter+      store'' <- need "secret_store (reversed)"+        (secret_store (reverse (un_secret_store store)))+      forM_ known $ \(i, s) ->+        fmap unsecret (derive_old_secret i store'') @?= Just (unsecret s)++  , testCase "secret_store rejects a repeated bucket" $ do+      known <- correctSequence+      case known of+        ((i0, s0) : _ : (i2, s2) : _) ->+          -- indices 2^48-1 and 2^48-3 both have no trailing zeros+          isNothing (secret_store [(i0, s0), (i2, s2)]) @?= True+        _ -> assertFailure "short sequence"++  , testCase "secret_store rejects contradictory entries" $ do+      known <- correctSequence+      bad <- pcsecret+        "0000000000000000000000000000000000000000000000000000000000000000"+      case known of+        ((i0, _) : (i1, s1) : _) ->+          -- index 2^48-1 is derivable from index 2^48-2+          isNothing (secret_store [(i0, bad), (i1, s1)]) @?= True+        _ -> assertFailure "short sequence"++  , testCase "Show is redacted" $ do+      known <- correctSequence+      store <- need "insert" (foldM (\st (i, s) -> insert_secret s i st)+                                    empty_secret_store known)+      show store @?= "SecretStore <redacted>"+      sd <- need "seed" (seed (BS.replicate 32 0xff))+      show sd @?= "Seed <redacted>"+      k <- sk e_base_secret+      show k @?= "Seckey <redacted>"+  ]++-- | The spec's correct storage sequence, as (index, secret) pairs.+correctSequence :: IO [(SecretIndex, BOLT1.PerCommitmentSecret)]+correctSequence = do+  steps <- case filter isCorrect appendix_d_storage of+    (v : _) -> pure (sv_steps v)+    [] -> assertFailure "missing correct sequence vector"+  traverse (\st -> (,) <$> idx (ss_index st) <*> pcsecret (ss_secret st))+    steps+  where+    isCorrect v = sv_name v == "insert_secret correct sequence"++-- | Run a storage vector, asserting that each insert_secret call is+--   accepted or rejected exactly as the spec says.+storageVectorTest :: StorageVector -> TestTree+storageVectorTest (StorageVector name steps) =+    testCase name (go empty_secret_store steps)+  where+    go _ [] = pure ()+    go store (StorageStep i ok s : rest) = do+      sec <- pcsecret s+      ix <- idx i+      case (insert_secret sec ix store, ok) of+        (Just store', True) -> go store' rest+        (Nothing, False) -> go store rest+        (Just _, False) ->+          assertFailure ("accepted incorrect secret at I=" ++ show i)+        (Nothing, True) ->+          assertFailure ("rejected correct secret at I=" ++ show i)++-- Appendix B -----------------------------------------------------------------++fundingTests :: TestTree+fundingTests = testGroup "Funding transaction (Appendix B)" [+    testCase "funding witness script" $ do+      (lpk, rpk) <- fundingPubkeys+      wscript <- hex b_funding_wscript+      funding_script lpk rpk @?= Script wscript+      funding_script rpk lpk @?= Script wscript++  , testCase "funding output" $ do+      (lpk, rpk) <- fundingPubkeys+      tx <- hex b_funding_tx >>= decodeTx+      txid <- hex b_funding_txid+      BT.un_txid (BT.txid tx) @?= BS.reverse txid+      case drop (fromIntegral b_funding_output) (NE.toList (BT.tx_outputs tx))+        of+          (BT.TxOut value spk : _) -> do+            value @?= b_funding_satoshis+            Script spk @?= to_p2wsh (funding_script lpk rpk)+          [] -> assertFailure "missing funding output"+  ]+  where+    fundingPubkeys = do+      lpk <- FundingPubkey <$> pt b_local_funding_pubkey+      rpk <- FundingPubkey <$> pt b_remote_funding_pubkey+      pure (lpk, rpk)++-- Appendices C and F ---------------------------------------------------------++-- | Decode a serialized transaction, failing the test on bad input.+decodeTx :: BS.ByteString -> IO BT.Tx+decodeTx = need "undecodable transaction" . BT.from_bytes++-- | The single input's witness stack of a spec transaction.+witnessOf :: BT.Tx -> IO [BS.ByteString]+witnessOf tx = case NE.toList (BT.tx_inputs tx) of+  [i] | BT.Witness items <- BT.txin_witness i -> pure items+  _ -> assertFailure "expected one input"++-- | A test HTLC from the Appendix C parameters.+testHtlc :: Int -> IO HTLC+testHtlc i = case drop i c_htlcs of+  (TestHtlc offered amt expiry pre : _) -> do+    preimage <- hex pre+    amount <- msat amt+    ph <- need "payment hash" (BOLT1.payment_hash (SHA256.hash preimage))+    pure HTLC+      { htlc_direction    = if offered then HTLCOffered else HTLCReceived+      , htlc_amount_msat  = amount+      , htlc_payment_hash = ph+      , htlc_cltv_expiry  = CltvExpiry expiry+      }+  [] -> assertFailure ("no test HTLC #" ++ show i)++testPreimage :: Int -> IO BOLT1.PaymentPreimage+testPreimage i = case drop i c_htlcs of+  (TestHtlc _ _ _ pre : _) ->+    hex pre >>= need "preimage" . BOLT1.payment_preimage+  [] -> assertFailure ("no test HTLC #" ++ show i)++-- | Commitment keys from the Appendix C parameters, which Appendix F+--   shares. The vectors use the payment basepoints as HTLC+--   basepoints; to_remote pays the remote payment basepoint.+vectorKeys :: IO CommitmentKeys+vectorKeys = do+  revocation <- pt c_local_revocation_pubkey+  delayed <- pt c_local_delayedpubkey+  local_htlc <- pt c_local_htlcpubkey+  remote_htlc <- pt c_remote_htlcpubkey+  remote_payment <- pt c_remote_payment_basepoint+  local_funding <- pt c_local_funding_pubkey+  remote_funding <- pt c_remote_funding_pubkey+  pure CommitmentKeys+    { ck_revocation_pubkey = RevocationPubkey revocation+    , ck_local_delayed     = LocalDelayedPubkey delayed+    , ck_local_htlc        = LocalHtlcPubkey local_htlc+    , ck_remote_htlc       = RemoteHtlcPubkey remote_htlc+    , ck_remote_payment    = RemotePubkey remote_payment+    , ck_local_funding     = FundingPubkey local_funding+    , ck_remote_funding    = FundingPubkey remote_funding+    }++vectorOutpoint :: IO BT.OutPoint+vectorOutpoint = do+  txid <- hex c_funding_txid+  t <- need "invalid txid" (BT.mk_txid (BS.reverse txid))+  pure (BT.OutPoint t c_funding_output_index)++-- | The commitment context for a vector: local's commitment, with local+--   as the opener.+vectorContext :: CommitmentFormat -> CommitVector -> IO CommitmentContext+vectorContext fmt v = do+  keys <- vectorKeys+  outpoint <- vectorOutpoint+  local_bp <- PaymentBasepoint <$> pt c_local_payment_basepoint+  remote_bp <- PaymentBasepoint <$> pt c_remote_payment_basepoint+  cn <- need "commitment number" (commitment_number c_commitment_number)+  htlcs <- traverse testHtlc (cv_htlcs v)+  dust <- DustLimit <$> sat (cv_dust_limit_sat v)+  to_local <- msat (cv_to_local_msat v)+  to_remote <- msat (cv_to_remote_msat v)+  pure CommitmentContext+    { cc_funding_outpoint  = outpoint+    , cc_commitment_number = cn+    , cc_local_payment_bp  = local_bp+    , cc_remote_payment_bp = remote_bp+    , cc_to_self_delay     = ToSelfDelay c_local_delay+    , cc_dust_limit        = dust+    , cc_feerate           = FeeratePerKw (cv_feerate_per_kw v)+    , cc_format            = fmt+    , cc_is_funder         = True+    , cc_to_local_msat     = to_local+    , cc_to_remote_msat    = to_remote+    , cc_htlcs             = htlcs+    , cc_keys              = keys+    }++-- | Assert that the commitment tx and each HTLC tx of a vector match+--   the spec's, with witnesses stripped, and that the spec's witnesses+--   are those the witness functions build.+commitVectorTests :: CommitmentFormat -> CommitVector -> TestTree+commitVectorTests fmt v = testGroup (cv_name v) $+    testCase "commitment tx" (do+      ctx <- vectorContext fmt v+      spec <- hex (cv_commit_tx v) >>= decodeTx+      commit <- need "no commitment tx" (build_commitment_tx ctx)+      encode_commitment_tx commit @?= BT.to_bytes_legacy spec+      -- the funding witness, with its signatures in either order+      w <- witnessOf spec+      case w of+        [_, s1, s2, _] -> do+          let keys = cc_keys ctx+              lpk = ck_local_funding keys+              rpk = ck_remote_funding keys+          funding_witness lpk s1 rpk s2 @?= BT.Witness w+          funding_witness rpk s2 lpk s1 @?= BT.Witness w+        _ -> assertFailure "unexpected funding witness")+  : fmap htlcTxTest (cv_htlc_txs v)+  where+    htlcTxTest (HtlcTxVector kind i out raw) =+      testCase (kindName kind ++ " for htlc #" ++ show i+                 ++ " (output " ++ show out ++ ")") $ do+        ctx <- vectorContext fmt v+        commit <- need "no commitment tx" (build_commitment_tx ctx)+        expected_htlc <- testHtlc i+        -- the output names its HTLC+        htlc <- case drop (fromIntegral out) (NE.toList (ctx_outputs commit))+          of+            (CommitmentOutput _ _ (OutputHTLC h) : _) -> pure h+            _ -> assertFailure "not an HTLC output"+        htlc @?= expected_htlc+        let keys = cc_keys ctx+            hctx = HTLCContext+              { hc_commitment_txid   = BT.txid (commitment_to_tx commit)+              , hc_output_index      = out+              , hc_htlc              = htlc+              , hc_to_self_delay     = cc_to_self_delay ctx+              , hc_feerate           = cc_feerate ctx+              , hc_format            = fmt+              , hc_revocation_pubkey = ck_revocation_pubkey keys+              , hc_local_delayed     = ck_local_delayed keys+              }+        spec <- hex raw >>= decodeTx+        htx <- need "no htlc tx" (build_htlc_tx hctx)+        encode_htlc_tx htx @?= BT.to_bytes_legacy spec+        -- the input witness+        w <- witnessOf spec+        let rev = ck_revocation_pubkey keys+            rh = ck_remote_htlc keys+            lh = ck_local_htlc keys+            ph = htlc_payment_hash htlc+        case (kind, w) of+          (HtlcSuccess, [_, rsig, lsig, _, _]) -> do+            pre <- testPreimage i+            let ws = received_htlc_script rev rh lh ph+                       (htlc_cltv_expiry htlc) fmt+            htlc_success_witness rsig lsig pre ws @?= BT.Witness w+            sighashes rsig lsig+          (HtlcTimeout, [_, rsig, lsig, _, _]) -> do+            let ws = offered_htlc_script rev rh lh ph fmt+            htlc_timeout_witness rsig lsig ws @?= BT.Witness w+            sighashes rsig lsig+          _ -> assertFailure "unexpected htlc witness"++    -- the remote signature uses remote_htlc_sighash, the local one+    -- SIGHASH_ALL+    sighashes rsig lsig = do+      let flag = fromIntegral . Sighash.encode_sighash+      fmap snd (BS.unsnoc rsig) @?= Just (flag (remote_htlc_sighash fmt))+      fmap snd (BS.unsnoc lsig) @?= Just (flag Sighash.SIGHASH_ALL)++    kindName HtlcSuccess = "htlc-success tx"+    kindName HtlcTimeout = "htlc-timeout tx"++-- commitment transactions ----------------------------------------------------++simpleVector :: IO CommitVector+simpleVector = case appendix_c of+  (v : _) -> pure v+  [] -> assertFailure "missing Appendix C vectors"++commitmentTests :: TestTree+commitmentTests = testGroup "Commitment transactions" [+    testCase "obscured_commitment_number (Appendix C)" $ do+      o <- PaymentBasepoint <$> pt c_local_payment_basepoint+      a <- PaymentBasepoint <$> pt c_remote_payment_basepoint+      cn <- need "commitment_number" (commitment_number 42)+      obscured_commitment_number o a cn @?= 0x2bb038521914 `xor` 42++  , testCase "obscured commitment number from the accepter's side" $ do+      v <- simpleVector+      opener_ctx <- vectorContext StaticRemotekey v+      spec <- hex (cv_commit_tx v) >>= decodeTx+      -- the accepter's (remote's) commitment, with the owner-relative+      -- payment basepoints swapped and the owner not the funder+      let accepter_ctx = opener_ctx+            { cc_local_payment_bp  = cc_remote_payment_bp opener_ctx+            , cc_remote_payment_bp = cc_local_payment_bp opener_ctx+            , cc_is_funder         = False+            }+      commit <- need "no commitment tx" (build_commitment_tx accepter_ctx)+      let Locktime lt = ctx_locktime commit+          Sequence sq = ctx_input_sequence commit+      lt @?= BT.tx_locktime spec+      [sq] @?= fmap BT.txin_sequence (NE.toList (BT.tx_inputs spec))+      -- the obscured number is 0x2bb038521914 ^ 42+      let obscured = 0x2bb038521914 `xor` 42 :: Word64+      lt @?= 0x20000000 + fromIntegral (obscured `mod` 0x1000000)+      sq @?= 0x80000000 + fromIntegral (obscured `div` 0x1000000)++  , testCase "fee taken from the remote output when it funds" $ do+      v <- simpleVector+      ctx <- vectorContext StaticRemotekey v+      commit <- need "no commitment tx" (build_commitment_tx ctx+                  { cc_is_funder = False })+      -- the 10860 sat base fee comes from to_remote's 3000000 sat+      let values = [ (co_type o, BOLT1.un_satoshi (co_value o))+                   | o <- NE.toList (ctx_outputs commit) ]+      values @?= [(OutputToRemote, 2989140), (OutputToLocal, 7000000)]++  , testCase "no outputs" $ do+      v <- simpleVector+      ctx <- vectorContext Anchors v+      zero <- msat 0+      isNothing (build_commitment_tx ctx+        { cc_to_local_msat = zero, cc_to_remote_msat = zero })+        @?= True++  , testCase "anchors: only the anchor of a materialized output" $ do+      v <- simpleVector+      ctx <- vectorContext Anchors v+      zero <- msat 0+      commit <- need "no commitment tx" (build_commitment_tx ctx+                  { cc_to_remote_msat = zero })+      fmap co_type (NE.toList (ctx_outputs commit))+        @?= [OutputLocalAnchor, OutputToLocal]+  ]++-- HTLC transactions ----------------------------------------------------------++htlcTxTests :: TestTree+htlcTxTests = testGroup "HTLC transactions" [+    testCase "fee exceeding the amount" $ do+      keys <- vectorKeys+      outpoint <- vectorOutpoint+      h <- testHtlc 2   -- offered, 2000 sat+      let ctx = HTLCContext+            { hc_commitment_txid   = BT.op_txid outpoint+            , hc_output_index      = 0+            , hc_htlc              = h+            , hc_to_self_delay     = ToSelfDelay 144+            , hc_feerate           = FeeratePerKw 5000  -- fee 3315+            , hc_format            = StaticRemotekey+            , hc_revocation_pubkey = ck_revocation_pubkey keys+            , hc_local_delayed     = ck_local_delayed keys+            }+      isNothing (build_htlc_tx ctx) @?= True+      -- zero-fee under anchors+      htx <- need "anchors htlc tx" (build_htlc_tx ctx { hc_format = Anchors })+      BOLT1.un_satoshi (htx_output_value htx) @?= 2000+      htx_input_sequence htx @?= Sequence 1+      htx_locktime htx @?= Locktime 502+  ]++-- closing transactions -------------------------------------------------------++p2wpkh :: Word8 -> Script+p2wpkh b = Script (BS.pack [0x00, 0x14] <> BS.replicate 20 b)++closingValues :: ClosingTx -> [(Word64, Script)]+closingValues c =+  [ (BOLT1.un_satoshi (clo_value o), clo_script o)+  | o <- NE.toList (cltx_outputs c) ]++closingTests :: TestTree+closingTests = testGroup "Closing transactions" [+    testGroup "legacy" [+      testCase "fee from the funder, outputs in BIP69 order" $ do+        ctx <- legacy 6000500 4000999 546 1000 True False+        c <- need "no closing tx" (build_legacy_closing_tx ctx)+        closingValues c @?= [(4000, remote_spk), (5000, local_spk)]+        cltx_locktime c @?= Locktime 0+        cltx_input_sequence c @?= Sequence 0xFFFFFFFF+        cltx_version c @?= 2++    , testCase "fee from the remote output when it funds" $ do+        ctx <- legacy 6000000 4000000 546 1000 False False+        c <- need "no closing tx" (build_legacy_closing_tx ctx)+        closingValues c @?= [(3000, remote_spk), (6000, local_spk)]++    , testCase "the signer's dust limit applies to both outputs" $ do+        ctx <- legacy 100000000 600000 1000 500 True False+        c <- need "no closing tx" (build_legacy_closing_tx ctx)+        closingValues c @?= [(99500, local_spk)]++    , testCase "the signer may omit its own output" $ do+        ctx <- legacy 6000000 4000000 546 1000 True True+        c <- need "no closing tx" (build_legacy_closing_tx ctx)+        closingValues c @?= [(4000, remote_spk)]++    , testCase "fee exceeding the funder's balance" $ do+        ctx <- legacy 999999 4000000 546 1000 True False+        isNothing (build_legacy_closing_tx ctx) @?= True++    , testCase "no outputs" $ do+        ctx <- legacy 500000 500000 546 0 True False+        isNothing (build_legacy_closing_tx ctx) @?= True+    ]++  , testGroup "option_simple_close" [+      testCase "closer pays the fee, outputs in BIP69 order" $ do+        ctx <- simple 10000999 5000000 500 CloserAndCloseeOutputs+        c <- need "no closing tx" (build_closing_tx ctx)+        closingValues c @?= [(5000, remote_spk), (9500, local_spk)]+        cltx_locktime c @?= Locktime 800000+        cltx_input_sequence c @?= Sequence 0xFFFFFFFD+        cltx_version c @?= 2++    , testCase "closer output only" $ do+        ctx <- simple 10000000 5000000 500 CloserOutputOnly+        c <- need "no closing tx" (build_closing_tx ctx)+        closingValues c @?= [(9500, local_spk)]++    , testCase "closee output only" $ do+        ctx <- simple 10000000 5000000 500 CloseeOutputOnly+        c <- need "no closing tx" (build_closing_tx ctx)+        closingValues c @?= [(5000, remote_spk)]++    , testCase "OP_RETURN outputs have amount zero and are kept" $ do+        ctx <- simple 10000000 5000000 500 CloserAndCloseeOutputs+        let op_return = Script (BS.pack [0x6a, 0x01, 0x00])+        c <- need "no closing tx" (build_closing_tx ctx+               { clc_closer_script = op_return })+        closingValues c @?= [(0, op_return), (5000, remote_spk)]+        c' <- need "no closing tx" (build_closing_tx ctx+                { clc_closee_script = op_return })+        closingValues c' @?= [(0, op_return), (9500, local_spk)]++    , testCase "dust outputs are not trimmed" $ do+        ctx <- simple 10000000 100000 500 CloserAndCloseeOutputs+        c <- need "no closing tx" (build_closing_tx ctx)+        closingValues c @?= [(100, remote_spk), (9500, local_spk)]++    , testCase "fee exceeding the closer's balance" $ do+        ctx <- simple 499999 5000000 500 CloseeOutputOnly+        isNothing (build_closing_tx ctx) @?= True+    ]+  ]+  where+    local_spk = p2wpkh 0x11+    remote_spk = p2wpkh 0x22++    legacy l r dust fee funder omit = do+      outpoint <- vectorOutpoint+      lm <- msat l+      rm <- msat r+      d <- DustLimit <$> sat dust+      f <- sat fee+      pure LegacyClosingContext+        { lcc_funding_outpoint = outpoint+        , lcc_local_msat       = lm+        , lcc_remote_msat      = rm+        , lcc_local_script     = local_spk+        , lcc_remote_script    = remote_spk+        , lcc_dust_limit       = d+        , lcc_fee              = f+        , lcc_is_funder        = funder+        , lcc_omit_local       = omit+        }++    simple closer closee fee outs = do+      outpoint <- vectorOutpoint+      cm <- msat closer+      em <- msat closee+      f <- sat fee+      pure ClosingContext+        { clc_funding_outpoint = outpoint+        , clc_closer_msat      = cm+        , clc_closee_msat      = em+        , clc_closer_script    = local_spk+        , clc_closee_script    = remote_spk+        , clc_fee              = f+        , clc_locktime         = Locktime 800000+        , clc_outputs          = outs+        }++-- scripts --------------------------------------------------------------------++scriptTests :: TestTree+scriptTests = testGroup "Scripts and witnesses" [+    testCase "to_remote scriptPubKeys" $ do+      pk <- RemotePubkey <$> pt c_remote_payment_basepoint+      let Script ws = to_remote_witness_script pk+      -- <remotepubkey> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY+      BS.length ws @?= 37+      BS.drop 34 ws @?= BS.pack [0xad, 0x51, 0xb2]+      to_remote_script_pubkey pk Anchors @?= to_p2wsh (Script ws)+      -- P2WPKH, as in Appendix C's to_remote output+      v <- simpleVector+      spec <- hex (cv_commit_tx v) >>= decodeTx+      let p2wpkhs = [ spk | BT.TxOut _ spk <- NE.toList (BT.tx_outputs spec)+                          , BS.length spk == 22 ]+      [to_remote_script_pubkey pk StaticRemotekey] @?= fmap Script p2wpkhs++  , testCase "to_remote witnesses" $ do+      pk@(RemotePubkey p) <- RemotePubkey <$> pt c_remote_payment_basepoint+      to_remote_witness "sig" pk StaticRemotekey+        @?= BT.Witness ["sig", unpoint p]+      let Script ws = to_remote_witness_script pk+      to_remote_witness "sig" pk Anchors @?= BT.Witness ["sig", ws]++  , testCase "to_local witnesses" $ do+      keys <- vectorKeys+      let s@(Script ws) = to_local_script (ck_revocation_pubkey keys)+            (ToSelfDelay 144) (ck_local_delayed keys)+      to_local_witness_spend "sig" s @?= BT.Witness ["sig", "", ws]+      to_local_witness_revoke "sig" s @?= BT.Witness ["sig", "\x01", ws]++  , testCase "anchor witnesses" $ do+      keys <- vectorKeys+      let fpk = ck_local_funding keys+          Script ws = anchor_script fpk+      anchor_witness_owner "sig" fpk @?= BT.Witness ["sig", ws]+      anchor_witness_anyone fpk @?= BT.Witness ["", ws]++  , testCase "HTLC output witnesses" $ do+      keys <- vectorKeys+      h <- testHtlc 2+      pre <- testPreimage 2+      let rev@(RevocationPubkey rp) = ck_revocation_pubkey keys+          off@(Script ows) = offered_htlc_script rev (ck_remote_htlc keys)+            (ck_local_htlc keys) (htlc_payment_hash h) Anchors+          rcv@(Script rws) = received_htlc_script rev (ck_remote_htlc keys)+            (ck_local_htlc keys) (htlc_payment_hash h) (htlc_cltv_expiry h)+            Anchors+      offered_htlc_witness_preimage "sig" pre off+        @?= BT.Witness ["sig", BOLT1.un_payment_preimage pre, ows]+      offered_htlc_witness_revoke "sig" rev off+        @?= BT.Witness ["sig", unpoint rp, ows]+      received_htlc_witness_timeout "sig" rcv+        @?= BT.Witness ["sig", "", rws]+      received_htlc_witness_revoke "sig" rev rcv+        @?= BT.Witness ["sig", unpoint rp, rws]++  , testCase "remote_htlc_sighash" $ do+      remote_htlc_sighash StaticRemotekey @?= Sighash.SIGHASH_ALL+      remote_htlc_sighash Anchors @?= Sighash.SIGHASH_SINGLE_ANYONECANPAY++  , testCase "CSV delays and CLTV expiries are minimally encoded" $ do+      keys <- vectorKeys+      h <- testHtlc 0+      let rev = ck_revocation_pubkey keys+          delayed = ck_local_delayed keys+          delay d = let Script s = to_local_script rev (ToSelfDelay d) delayed+                    in  BS.take 4 (BS.drop 36 s)+          expiry e =+            let Script s = received_htlc_script rev (ck_remote_htlc keys)+                  (ck_local_htlc keys) (htlc_payment_hash h) (CltvExpiry e)+                  StaticRemotekey+            in  BS.take 5 (BS.drop 130 s)+      delay 16 @?= BS.pack [0x60, 0xb2, 0x75, 0x21]+      delay 17 @?= BS.pack [0x01, 0x11, 0xb2, 0x75]+      delay 0x80 @?= BS.pack [0x02, 0x80, 0x00, 0xb2]+      delay 0x8000 @?= BS.pack [0x03, 0x00, 0x80, 0x00]+      expiry 500 @?= BS.pack [0x02, 0xf4, 0x01, 0xb1, 0x75]+      expiry 0x800000 @?= BS.pack [0x04, 0x00, 0x00, 0x80, 0x00]+  ]++-- fees and trimming ----------------------------------------------------------++feeTests :: TestTree+feeTests = testGroup "Fees and trimming (BOLT #3 fee example)" [+    testCase "HTLC transaction fees" $ do+      fmap BOLT1.un_satoshi+        [ htlc_timeout_fee rate StaticRemotekey+        , htlc_success_fee rate StaticRemotekey+        , htlc_timeout_fee rate Anchors+        , htlc_success_fee rate Anchors ]+        @?= [3315, 3515, 0, 0]++  , testCase "trim thresholds" $ do+      d <- DustLimit <$> sat 546+      fmap (BOLT1.un_satoshi . htlc_trim_threshold d rate StaticRemotekey)+        [HTLCOffered, HTLCReceived] @?= [3861, 4061]++  , testCase "trimmed HTLCs and base fee" $ do+      d <- DustLimit <$> sat 546+      hs <- sequence+        [ example HTLCOffered 5000000, example HTLCOffered 1000000+        , example HTLCReceived 7000000, example HTLCReceived 800000 ]+      fmap (is_trimmed d rate StaticRemotekey) hs+        @?= [False, True, False, True]+      BOLT1.un_satoshi (commitment_fee rate StaticRemotekey 2) @?= 5340+      BOLT1.un_satoshi (commitment_fee rate Anchors 0) @?= 5620++  , testCase "commitment fee saturates" $+      commitment_fee (FeeratePerKw maxBound) StaticRemotekey maxBound+        @?= BOLT1.max_satoshi+  ]+  where+    rate = FeeratePerKw 5000+    example dir amt = do+      a <- msat amt+      ph <- need "payment hash" (BOLT1.payment_hash (BS.replicate 32 0))+      pure (HTLC dir a ph (CltvExpiry 500))++-- smart constructors ---------------------------------------------------------++constructorTests :: TestTree+constructorTests = testGroup "Smart constructors" [+    testCase "commitment_number" $ do+      isJust (commitment_number 0) @?= True+      isJust (commitment_number 0xFFFFFFFFFFFF) @?= True+      isNothing (commitment_number 0x1000000000000) @?= True+  , testCase "next_commitment_number" $ do+      cn <- need "commitment_number" (commitment_number 0xFFFFFFFFFFFE)+      fmap un_commitment_number (next_commitment_number cn)+        @?= Just 0xFFFFFFFFFFFF+      top <- need "commitment_number" (commitment_number 0xFFFFFFFFFFFF)+      isNothing (next_commitment_number top) @?= True+  , testCase "secret_index" $ do+      isJust (secret_index 0xFFFFFFFFFFFF) @?= True+      isNothing (secret_index 0x1000000000000) @?= True+  , testCase "seed" $ do+      isJust (seed (BS.replicate 32 0)) @?= True+      isNothing (seed (BS.replicate 31 0)) @?= True+      isNothing (seed (BS.replicate 33 0)) @?= True+  , testCase "seckey" $ do+      isJust (seckey (BS.replicate 32 0x01)) @?= True+      isNothing (seckey (BS.replicate 31 0x01)) @?= True+      isNothing (seckey (BS.replicate 32 0x00)) @?= True+      -- the group order n is out of range, n - 1 in range+      n <- hex+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"+      n1 <- hex+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+      isNothing (seckey n) @?= True+      isJust (seckey n1) @?= True+  ]++-- properties -----------------------------------------------------------------++propertyTests :: TestTree+propertyTests = testGroup "Properties" [+    testProperty "derive_privkey matches derive_pubkey" propPrivkey+  , testProperty "derive_revocationprivkey matches derive_revocationpubkey"+      propRevocationPrivkey+  , testProperty "derive_per_commitment_point' = derive_per_commitment_point"+      propPcpWnaf+  , testProperty "derive_commitment_keys' = derive_commitment_keys"+      propCommitmentKeysWnaf+  , testProperty "every inserted secret is derivable, before and after \+                 \a round trip through un_secret_store" propSecretStore+  , testProperty "commitment_number accepts exactly 48-bit values"+      propCommitmentNumber+  , testProperty "commitment_secret_index counts down from 2^48 - 1"+      propSecretIndex+  ]++genBytes :: Int -> Gen BS.ByteString+genBytes n = BS.pack <$> vectorOf n arbitrary++-- | A random valid secret key.+genSeckey :: Gen Seckey+genSeckey = do+  bs <- genBytes 32+  maybe genSeckey pure (seckey bs)++-- | A random point (as the public key of a random secret key).+genPoint :: Gen BOLT1.Point+genPoint = do+  k <- genSeckey+  case pubkey_of k >>= BOLT1.point of+    Just p  -> pure p+    Nothing -> genPoint++genSecret :: Gen BOLT1.PerCommitmentSecret+genSecret = do+  k <- genSeckey+  maybe genSecret pure (BOLT1.per_commitment_secret (un_seckey k))++genBasepoints :: Gen Basepoints+genBasepoints = Basepoints+  <$> fmap RevocationBasepoint genPoint+  <*> fmap PaymentBasepoint genPoint+  <*> fmap DelayedPaymentBasepoint genPoint+  <*> fmap HtlcBasepoint genPoint++propPrivkey :: Property+propPrivkey = forAll genSeckey $ \k -> forAll genPoint $ \p ->+  let pcp = PerCommitmentPoint p+      bp = pubkey_of k >>= BOLT1.point+  in  (pubkey_of =<< derive_privkey k pcp)+        === fmap unpoint (bp >>= \b -> derive_pubkey b pcp)++propRevocationPrivkey :: Property+propRevocationPrivkey = forAll genSeckey $ \k -> forAll genSecret $ \s ->+  let rbp = fmap RevocationBasepoint (pubkey_of k >>= BOLT1.point)+      pcp = derive_per_commitment_point s+      expected = do+        r <- rbp+        c <- pcp+        RevocationPubkey p <- derive_revocationpubkey r c+        pure (unpoint p)+  in  (pubkey_of =<< derive_revocationprivkey k s) === expected++propPcpWnaf :: Property+propPcpWnaf = forAll genSecret $ \s ->+  derive_per_commitment_point' tex s === derive_per_commitment_point s++propCommitmentKeysWnaf :: Property+propCommitmentKeysWnaf =+  forAll genBasepoints $ \l -> forAll genBasepoints $ \r ->+  forAll genPoint $ \lf -> forAll genPoint $ \rf -> forAll genPoint $ \p ->+    let args f = f l (FundingPubkey lf) r (FundingPubkey rf)+                   (PerCommitmentPoint p)+    in  args (derive_commitment_keys' tex) === args derive_commitment_keys++-- | After inserting a prefix of a correct sequence (descending from+--   2^48-1, all from one seed), every inserted index is derivable, and+--   stays so after a round trip through the store's entries.+propSecretStore :: Property+propSecretStore =+  forAll (genBytes 32) $ \sd ->+  forAll (choose (1, 600)) $ \n ->+    case seed sd of+      Nothing -> counterexample "seed" False+      Just s ->+        let idxs = [ i | Just i <- fmap secret_index+                       (take n [0xFFFFFFFFFFFF, 0xFFFFFFFFFFFE ..]) ]+            secret = generate_from_seed s+            insert st i = st >>= insert_secret (secret i) i+            derivable st = conjoin+              [ fmap unsecret (derive_old_secret i st)+                  === Just (unsecret (secret i))+              | i <- idxs ]+        in  case foldl insert (Just empty_secret_store) idxs of+              Nothing -> counterexample "insert_secret rejected" False+              Just store -> case secret_store (un_secret_store store) of+                Nothing -> counterexample "secret_store rejected" False+                Just store' -> derivable store .&&. derivable store'++propCommitmentNumber :: Property+propCommitmentNumber = forAll (choose (0, maxBound)) $ \n ->+  case commitment_number n of+    Just cn -> n <= 0xFFFFFFFFFFFF && un_commitment_number cn == n+    Nothing -> n > 0xFFFFFFFFFFFF++propSecretIndex :: Property+propSecretIndex = forAll (choose (0, 0xFFFFFFFFFFFF)) $ \n ->+  case commitment_number n of+    Nothing -> counterexample "commitment_number" False+    Just cn ->+      un_secret_index (commitment_secret_index cn) === 0xFFFFFFFFFFFF - n
+ test/Vectors.hs view
@@ -0,0 +1,1699 @@+{-# LANGUAGE OverloadedStrings #-}++-- Test vectors from BOLT #3 (etc/03-transactions.md):+--+-- * Appendix B: funding transaction+-- * Appendix C: commitment and HTLC transactions+-- * Appendix D: per-commitment secret storage+-- * Appendix F: commitment and HTLC transactions (anchors)+--+-- Hex strings are copied verbatim from the spec. Transactions include+-- their witnesses; txids are in the spec's (big-endian) display order.++module Vectors where++import qualified Data.ByteString as BS+import Data.Word (Word16, Word32, Word64)++-- types ----------------------------------------------------------------------++-- | A test HTLC from the Appendix C parameters.+data TestHtlc = TestHtlc+  { th_offered     :: !Bool           -- ^ local->remote+  , th_amount_msat :: !Word64+  , th_expiry      :: !Word32+  , th_preimage    :: !BS.ByteString  -- ^ hex+  }++-- | Second-stage HTLC transaction kind.+data HtlcTxKind = HtlcSuccess | HtlcTimeout+  deriving (Eq, Show)++-- | An expected HTLC-success or HTLC-timeout transaction.+data HtlcTxVector = HtlcTxVector+  { htv_kind   :: !HtlcTxKind+  , htv_htlc   :: !Int            -- ^ index into 'c_htlcs'+  , htv_output :: !Word32         -- ^ commitment output index spent+  , htv_tx     :: !BS.ByteString  -- ^ hex+  }++-- | A commitment transaction vector, with its HTLC transactions.+data CommitVector = CommitVector+  { cv_name              :: String+  , cv_to_local_msat     :: !Word64+  , cv_to_remote_msat    :: !Word64+  , cv_feerate_per_kw    :: !Word32+  , cv_dust_limit_sat    :: !Word64+  , cv_htlcs             :: ![Int]  -- ^ indices into 'c_htlcs'+  , cv_commit_tx         :: !BS.ByteString  -- ^ hex+  , cv_htlc_txs          :: ![HtlcTxVector]+  }++-- | One insert_secret call and whether the spec accepts it.+data StorageStep = StorageStep+  { ss_index  :: !Word64+  , ss_ok     :: !Bool+  , ss_secret :: !BS.ByteString  -- ^ hex+  }++-- | A secret storage vector.+data StorageVector = StorageVector+  { sv_name  :: String+  , sv_steps :: ![StorageStep]+  }++-- Appendix B -----------------------------------------------------------------++b_local_funding_pubkey :: BS.ByteString+b_local_funding_pubkey =+  "023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb"++b_remote_funding_pubkey :: BS.ByteString+b_remote_funding_pubkey =+  "030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1"++b_funding_wscript :: BS.ByteString+b_funding_wscript =+  "5221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb2103\+  \0e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae"++b_funding_satoshis :: Word64+b_funding_satoshis = 10000000++b_funding_output :: Word32+b_funding_output = 0++b_funding_tx :: BS.ByteString+b_funding_tx =+  "0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd\+  \000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a36\+  \2184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454\+  \d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d8750691\+  \84ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d\+  \490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8a\+  \d1afdcf652f900000000"++b_funding_txid :: BS.ByteString+b_funding_txid =+  "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be"++-- Appendix C -----------------------------------------------------------------++c_funding_txid :: BS.ByteString+c_funding_txid =+  "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be"++c_funding_output_index :: Word32+c_funding_output_index = 0++c_funding_amount_sat :: Word64+c_funding_amount_sat = 10000000++c_commitment_number :: Word64+c_commitment_number = 42++c_local_delay :: Word16+c_local_delay = 144++c_local_payment_basepoint :: BS.ByteString+c_local_payment_basepoint =+  "034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"++c_remote_payment_basepoint :: BS.ByteString+c_remote_payment_basepoint =+  "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991"++c_local_funding_pubkey :: BS.ByteString+c_local_funding_pubkey =+  "023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb"++c_remote_funding_pubkey :: BS.ByteString+c_remote_funding_pubkey =+  "030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1"++c_local_htlcpubkey :: BS.ByteString+c_local_htlcpubkey =+  "030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e7"++c_remote_htlcpubkey :: BS.ByteString+c_remote_htlcpubkey =+  "0394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b"++c_local_delayedpubkey :: BS.ByteString+c_local_delayedpubkey =+  "03fd5960528dc152014952efdb702a88f71e3c1653b2314431701ec77e57fde83c"++c_local_revocation_pubkey :: BS.ByteString+c_local_revocation_pubkey =+  "0212a140cd0c6539d07cd08dfe09984dec3251ea808b892efeac3ede9402bf2b19"++-- | HTLCs 0 through 6.+c_htlcs :: [TestHtlc]+c_htlcs = [+    TestHtlc False 1000000 500+      "0000000000000000000000000000000000000000000000000000000000000000"+  , TestHtlc False 2000000 501+      "0101010101010101010101010101010101010101010101010101010101010101"+  , TestHtlc True 2000000 502+      "0202020202020202020202020202020202020202020202020202020202020202"+  , TestHtlc True 3000000 503+      "0303030303030303030303030303030303030303030303030303030303030303"+  , TestHtlc False 4000000 504+      "0404040404040404040404040404040404040404040404040404040404040404"+  , TestHtlc True 5000000 506+      "0505050505050505050505050505050505050505050505050505050505050505"+  , TestHtlc True 5000001 505+      "0505050505050505050505050505050505050505050505050505050505050505"+  ]++-- | Appendix C commitment vectors.+appendix_c :: [CommitVector]+appendix_c = [+    CommitVector+      { cv_name =+          "simple commitment tx with no HTLCs"+      , cv_to_local_msat = 7000000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 15000+      , cv_dust_limit_sat = 546+      , cv_htlcs = []+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8002c0c62d0000000000160014cc1b07838e38\+          \7deacd0e5232e1e8b49f4c29e48454a56a00000000002200204adb4e2f00643db3\+          \96dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402206162\+          \10b2cc4d3afb601013c373bbd8aac54febd9f15400379a8cb65ce7deca60022034\+          \236c010991beb7ff770510561ae8dc885b8d38d1947248c38f2ae0556471420148\+          \3045022100c3127b33dcc741dd6b05b1e63cbd1a9a7d816f37af9b6756fa2376b0\+          \56f032370220408b96279808fe57eb7e463710804cdf4f108388bc5cf722d8c848\+          \d2c7f9f3b001475221023da092f6980e58d2c037173180e9a465476026ee50f966\+          \95963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385\+          \a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with all five HTLCs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 0+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7\+          \b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad0070000000000\+          \00220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda8898965\+          \1e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6\+          \078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd9044\+          \3e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200\+          \208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4\+          \c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e484e0a0\+          \6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857\+          \accc862d6b7dd80e040047304402206fc2d1f10ea59951eefac0b4b7c396a3c3d8\+          \7b71ff0b019796ef4535beaf36f902201765b0181e514d04f4c8ad75659d7037be\+          \26cdb3f8bb6f78fe61decef484c3ea01473044022009b048187705a8cbc9ad73ad\+          \be5af148c3d012e1f067961486c822c7af08158c022006d66f3704cfab3eb2dc49\+          \dae24e4aa22a6910fc9b424007583204e3621af2e501475221023da092f6980e58\+          \d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2c\+          \cc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 0 0+              "02000000000101ab84ff284f162cfbfef241f853b47d4368d171f9e2a14451\+              \60cd591c4c7d882b00000000000000000001e8030000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100d9e29616b8f3959f1d3d7f7ce893ffedcdc407717d0de8e3\+              \7d808c91d3a7c50d022078c3033f6d00095c8720a4bc943c1b45727818c082\+              \e4e3ddbc6d3116435b624b014730440220636de5682ef0c5b61f124ec74e8a\+              \a2461a69777521d6998295dcea36bc3338110220165285594b23c50b28b82d\+              \f200234566628a27bcd17f7f14404bd865354eb3ce01200000000000000000\+              \0000000000000000000000000000000000000000000000008a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \14b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc688527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f401b175ac686800000000"+          , HtlcTxVector HtlcTimeout 2 1+              "02000000000101ab84ff284f162cfbfef241f853b47d4368d171f9e2a14451\+              \60cd591c4c7d882b01000000000000000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220649fe8b20e67e46cbb0d09b4acea87dbec001b39b08dee7bdd\+              \0b1f03922a8640022037c462dff79df501cecfdb12ea7f4de91f99230bb544\+              \726f6e04527b1f89600401483045022100803159dee7935dba4a1d36a61055\+              \ce8fd62caa528573cc221ae288515405a252022029c59e7cffce374fe86010\+              \0a4a63787e105c3cf5156d40b12dd53ff55ac8cf3f01008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac\+              \6868f6010000"+          , HtlcTxVector HtlcSuccess 1 2+              "02000000000101ab84ff284f162cfbfef241f853b47d4368d171f9e2a14451\+              \60cd591c4c7d882b02000000000000000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220770fc321e97a19f38985f2e7732dd9fe08d16a2efa4bcbc042\+              \9400a447faf49102204d40b417f3113e1b0944ae0986f517564ab4acd3d190\+              \503faf97a6e420d4335201483045022100a437cc2ce77400ecde441b3398fe\+              \a3c3ad8bdad8132be818227fe3c5b8345989022069d45e7fa0ae551ec37240\+              \845e2c561ceb2567eacf3076a6a43a502d05865faa01200101010101010101\+              \0101010101010101010101010101010101010101010101018a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f501b175ac686800000000"+          , HtlcTxVector HtlcTimeout 3 3+              "02000000000101ab84ff284f162cfbfef241f853b47d4368d171f9e2a14451\+              \60cd591c4c7d882b03000000000000000001b80b0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402207bcbf4f60a9829b05d2dbab84ed593e0291836be715dc7db6b\+              \72a64caf646af802201e489a5a84f7c5cc130398b841d138d031a5137ac8f4\+              \c49c770a4959dc3c13630147304402203121d9b9c055f354304b016a36662e\+              \e99e1110d9501cb271b087ddb6f382c2c80220549882f3f3b78d9c492de475\+              \43cb9a697cecc493174726146536c5954dac748701008576a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac68\+              \68f7010000"+          , HtlcTxVector HtlcSuccess 4 4+              "02000000000101ab84ff284f162cfbfef241f853b47d4368d171f9e2a14451\+              \60cd591c4c7d882b04000000000000000001a00f0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022076dca5cb81ba7e466e349b7128cdba216d4d01659e29b96025\+              \b9524aaf0d1899022060de85697b88b21c749702b7d2cfa7dfeaa1f472c8f1\+              \d7d9c23f2bf968464b8701483045022100d9080f103cc92bac15ec42464a95\+              \f070c7fb6925014e673ee2ea1374d36a7f7502200c65294d22eb20d4856495\+              \4d5afe04a385551919d8b2ddb4ae2459daaeee1d9501200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with seven outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 647+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7\+          \b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad0070000000000\+          \00220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda8898965\+          \1e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6\+          \078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd9044\+          \3e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200\+          \208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4\+          \c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e484e09c\+          \6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857\+          \accc862d6b7dd80e04004830450221009ec15c687898bb4da8b3a833e5ab8bfc51\+          \ec6e9202aaa8e66611edfd4a85ed1102203d7183e45078b9735c93450bc3415d3e\+          \5a8c576141a711ec6ddcb4a893926bb701483045022100a135f9e8a5ed25f72774\+          \46c67956b00ce6f610ead2bdec2c2f686155b7814772022059f1f6e1a8b336a68e\+          \fcc1af3fe4d422d4827332b5b067501b099c47b7b5b5ee01475221023da092f698\+          \0e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b62\+          \3d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e1952\+          \20"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 0 0+              "020000000001012cfb3e4788c206881d38f2996b6cb2109b5935acb527d14b\+              \daa7b908afa9b2fe0000000000000000000122020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004830450221008437627f9ad84ac67052e2a414a4367b8556fd1f94d8b025\+              \90f89f50525cd33502205b9c21ff6e7fc864f2352746ad8ba59182510819ac\+              \b644e25b8a12fc37bbf24f014730440220344b0deb055230d01703e6c7acd4\+              \5853c4af2328b49b5d8af4f88a060733406602202ea64f2a43d5751edfe755\+              \03cbc35a62e3141b5ed032fa03360faf4ca66f670b01200000000000000000\+              \0000000000000000000000000000000000000000000000008a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \14b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc688527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f401b175ac686800000000"+          , HtlcTxVector HtlcTimeout 2 1+              "020000000001012cfb3e4788c206881d38f2996b6cb2109b5935acb527d14b\+              \daa7b908afa9b2fe0100000000000000000124060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402205a67f92bf6845cf2892b48d874ac1daf88a36495cf8a06f93d\+              \83180d930a6f75022031da1621d95c3f335cc06a3056cf960199dae600b7cf\+              \89088f65fc53cdbef28c014830450221009e5e3822b0185c6799a95288c597\+              \b671d6cc69ab80f43740f00c6c3d0752bdda02206da947a74bd98f3175324d\+              \c56fdba86cc783703a120a6f0297537e60632f4c7f01008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac\+              \6868f6010000"+          , HtlcTxVector HtlcSuccess 1 2+              "020000000001012cfb3e4788c206881d38f2996b6cb2109b5935acb527d14b\+              \daa7b908afa9b2fe020000000000000000010a060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220437e21766054a3eef7f65690c5bcfa9920babbc5af92b819f7\+              \72f6ea96df6c7402207173622024bd97328cfb26c6665e25c2f5d67c319443\+              \ccdc60c903217005d8c801483045022100fcfc47e36b712624677626cef3dc\+              \1d67f6583bd46926a6398fe6b00b0c9a37760220525788257b187fc775c637\+              \0d04eadf34d06f3650a63f8df851cee0ecb47a167301200101010101010101\+              \0101010101010101010101010101010101010101010101018a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f501b175ac686800000000"+          , HtlcTxVector HtlcTimeout 3 3+              "020000000001012cfb3e4788c206881d38f2996b6cb2109b5935acb527d14b\+              \daa7b908afa9b2fe030000000000000000010c0a0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402207436e10737e4df499fc051686d3e11a5bb2310e4d1f1e691d2\+              \87cef66514791202207cb58e71a6b7a42dd001b7e3ae672ea4f71ea3e1cd41\+              \2b742e9124abb0739c6401483045022100e78211b8409afb7255ffe37337da\+              \87f38646f1faebbdd61bc1920d69e3ead67a02201a626305adfcd16bfb7e93\+              \40928d9b6305464eab4aa4c4a3af6646e9b9f69dee01008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac\+              \6868f7010000"+          , HtlcTxVector HtlcSuccess 4 4+              "020000000001012cfb3e4788c206881d38f2996b6cb2109b5935acb527d14b\+              \daa7b908afa9b2fe04000000000000000001da0d0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004830450221009acd6a827a76bfee50806178dfe0495cd4e1d9c58279c194\+              \c7b01520fe68cb8d022024d439047c368883e570997a7d40f0b430cb5a742f\+              \507965e7d3063ae3feccca01473044022048762cf546bbfe474f1536365ea7\+              \c416e3c0389d60558bc9412cb148fb6ab68202207215d7083b75c96ff9d2b0\+              \8c59c34e287b66820f530b486a9aa4cdd9c347d5b901200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with six outputs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 648+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8006d007000000000000220020403d394747ca\+          \e42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d0070000000000\+          \00220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8\+          \ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9\+          \333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731\+          \df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d00000000001600\+          \14cc1b07838e387deacd0e5232e1e8b49f4c29e4844e9d6a00000000002200204a\+          \db4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400\+          \483045022100b15f72908ba3382a34ca5b32519240a22300cc6015b6f9418635fb\+          \41f3d01d8802207adb331b9ed1575383dca0f2355e86c173802feecf8298fbea53\+          \b9d4610583e90147304402203948f900a5506b8de36a4d8502f94f21dd84fd9c23\+          \14ab427d52feaa7a0a19f2022059b6a37a4adaa2c5419dc8aea63c6e2a2ec4c4bd\+          \e46207f6dc1fcd22152fc6e501475221023da092f6980e58d2c037173180e9a465\+          \476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21\+          \ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 2 0+              "020000000001010f44041fdfba175987cf4e6135ba2a154e3b7fb96483dc0e\+              \d5efc0678e5b6bf10000000000000000000123060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100a031202f3be94678f0e998622ee95ebb6ada8da1e9a51102\+              \28b5e04a747351e4022010ca6a21e18314ed53cfaae3b1f51998552a61a468\+              \e596368829a50ce40110e00148304502210097e1873b57267730154595187a\+              \34949d3744f52933070c74757005e61ce2112e02204ecfba2aa42d4f14bdf8\+              \bad4206bb97217b702e6c433e0e1b0ce6587e6d46ec601008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188\+              \ac6868f6010000"+          , HtlcTxVector HtlcSuccess 1 1+              "020000000001010f44041fdfba175987cf4e6135ba2a154e3b7fb96483dc0e\+              \d5efc0678e5b6bf10100000000000000000109060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402202361012a634aee7835c5ecdd6413dcffa8f404b7e77364c792\+              \cff984e4ee71e90220715c5e90baa08daa45a7439b1ee4fa4843ed77b19c05\+              \8240b69406606d38412401473044022019de73b00f1d818fb388e83b2c8c31\+              \f6bce35ac624e215bc12f88f9dc33edf48022006ff814bb9f700ee6abc3294\+              \e146fac3efd4f13f0005236b41c0a946ee00c9ae0120010101010101010101\+              \01010101010101010101010101010101010101010101018a76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \4b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f501b175ac686800000000"+          , HtlcTxVector HtlcTimeout 3 2+              "020000000001010f44041fdfba175987cf4e6135ba2a154e3b7fb96483dc0e\+              \d5efc0678e5b6bf1020000000000000000010b0a0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402207e8e82cd71ed4febeb593732c260456836e97d81896153ecd2\+              \b3cf320ca6861702202dd4a30f68f98ced7cc56a36369ac1fdd978248c5ff4\+              \ed204fc00cc62553298901483045022100bd0be6100c4fd8f102ec220e1b05\+              \3e4c4e2ecca25615490150007b40d314dc3902201a1e0ea266965b43164d9e\+              \6576f58fa6726d42883dd1c3996d2925c2e226079601008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac\+              \6868f7010000"+          , HtlcTxVector HtlcSuccess 4 3+              "020000000001010f44041fdfba175987cf4e6135ba2a154e3b7fb96483dc0e\+              \d5efc0678e5b6bf103000000000000000001d90d0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022024cd52e4198c8ae0e414a86d86b5a65ea7450f2eb4e7830967\+              \36d93395eca5ce022078f0094745b45be4d4b2b04dd5978c9e66ba49109e57\+              \04403e84aaf5f387d6be01483045022100bbfb9d0a946d420807c86e985d63\+              \6cceb16e71c3694ed186316251a00cbd807202207773223f9a337e145f6467\+              \3825be9b30d07ef1542c82188b264bedcf7cda78c601200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with six outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 2069+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8006d007000000000000220020403d394747ca\+          \e42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d0070000000000\+          \00220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8\+          \ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9\+          \333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731\+          \df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d00000000001600\+          \14cc1b07838e387deacd0e5232e1e8b49f4c29e48477956a00000000002200204a\+          \db4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400\+          \483045022100ad9a9bbbb75d506ca3b716b336ee3cf975dd7834fcf129d7dd1881\+          \46eb58a8b4022061a759ee417339f7fe2ea1e8deb83abb6a74db31a09b7648a932\+          \a639cda23e330148304502210090b96a2498ce0c0f2fadbec2aab278fed54c1a78\+          \38df793ec4d2c78d96ec096202204fdd439c50f90d483baa7b68feeef4bd33bc27\+          \7695405447bcd0bfb2ca34d7bc01475221023da092f6980e58d2c037173180e9a4\+          \65476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce\+          \21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 2 0+              "02000000000101adbe717a63fb658add30ada1e6e12ed257637581898abe47\+              \5c11d7bbcd65bd4d0000000000000000000175020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100f33513ee38abf1c582876f921f8fddc06acff48e04515532\+              \a32d3938de938ffd02203aa308a2c1863b7d6fdf53159a1465bf2e115c1315\+              \2546cc5d74483ceaa7f69901483045022100a637902a5d4c9ba9e7c472a225\+              \337d5aac9e2e3f6744f76e237132e7619ba0400220035c60d784a031c0d9f6\+              \df66b7eab8726a5c25397399ee4aa960842059eb3f9d01008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188\+              \ac6868f6010000"+          , HtlcTxVector HtlcSuccess 1 1+              "02000000000101adbe717a63fb658add30ada1e6e12ed257637581898abe47\+              \5c11d7bbcd65bd4d0100000000000000000122020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100ce07682cf4b90093c22dc2d9ab2a77ad6803526b655ef857\+              \221cc96af5c9e0bf02200f501cee22e7a268af40b555d15a8237c9f36ad67e\+              \f1841daf9f6a0267b1e6df01483045022100e57e46234f8782d3ff7aa593b4\+              \f7446fb5316c842e693dc63ee324fd49f6a1c302204a2f7b44c48bd26e1554\+              \422afae13153eb94b29d3687b733d18930615fb2db61012001010101010101\+              \010101010101010101010101010101010101010101010101018a76a9141401\+              \1f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2\+              \a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763\+              \a9144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a46\+              \946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67\+              \7502f501b175ac686800000000"+          , HtlcTxVector HtlcTimeout 3 2+              "02000000000101adbe717a63fb658add30ada1e6e12ed257637581898abe47\+              \5c11d7bbcd65bd4d020000000000000000015d060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100e3e35492e55f82ec0bc2f317ffd7a486d1f7024330fe9743\+              \c3559fc39f32ef0c02203d1d4db651fc388a91d5ad8ecdd8e83673063bc8ee\+              \fe27cfd8c189090e3a23e001473044022068613fb1b98eb3aec7f44c5b115b\+              \12343c2f066c4277c82b5f873dfe68f37f50022028109b4650f3f528ca4bfe\+              \9a467aff2e3e43893b61b5159157119d5d95cf1c1801008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac\+              \6868f7010000"+          , HtlcTxVector HtlcSuccess 4 3+              "02000000000101adbe717a63fb658add30ada1e6e12ed257637581898abe47\+              \5c11d7bbcd65bd4d03000000000000000001f2090000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402207475aeb0212ef9bf5130b60937817ad88c9a87976988ef1f32\+              \3f026148cc4a850220739fea17ad3257dcad72e509c73eebe86bee30b17846\+              \7b9fdab213d631b109df01483045022100d315522e09e7d53d2a659a79cb67\+              \fef56d6c4bddf3f46df6772d0d20a7beb7c8022070bcc17e288607b6a72be0\+              \bd83368bb6d53488db266c1cdb4d72214e4f02ac3301200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with five outputs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 2070+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8005d007000000000000220020403d394747ca\+          \e42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5b80b0000000000\+          \00220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635\+          \c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665\+          \fbfe92b4a6878e88a499f741c4c0c62d0000000000160014cc1b07838e387deacd\+          \0e5232e1e8b49f4c29e484da966a00000000002200204adb4e2f00643db396dd12\+          \0d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022001014419b5\+          \ba00e083ac4e0a85f19afc848aacac2d483b4b525d15e2ae5adbfe022015ebddad\+          \6ee1e72b47cb09f3e78459da5be01ccccd95dceca0e056a00cc773c10147304402\+          \204ca1ba260dee913d318271d86e10ca0f5883026fb5653155cff600fb40895223\+          \022037b145204b7054a40e08bb1fefbd826f827b40838d3e501423bcc57924bcb5\+          \0c01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8e\+          \fe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6\+          \d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 2 0+              "02000000000101403ad7602b43293497a3a2235a12ecefda4f3a1f1d06e49b\+              \1786d945685de1ff0000000000000000000174020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402205f6b6d12d8d2529fb24f4445630566cf4abbd0f9330ab6c2bd\+              \b94222d6a2a0c502202f556258ae6f05b193749e4c541dfcc13b525a5422f6\+              \291f073f15617ba8579b014730440220150b11069454da70caf2492ded9e00\+              \65c9a57f25ac2a4c52657b1d15b6c6ed85022068a38833b603c88927172063\+              \83611bad210f1cbb4b1f87ea29c6c65b9e1cb3e501008576a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac68\+              \68f6010000"+          , HtlcTxVector HtlcTimeout 3 1+              "02000000000101403ad7602b43293497a3a2235a12ecefda4f3a1f1d06e49b\+              \1786d945685de1ff010000000000000000015c060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100f960dfb1c9aee7ce1437efa65b523e399383e8149790e05d\+              \8fed27ff6e42fe0002202fe8613e062ffe0b0c518cc4101fba1c6de70f64a5\+              \bcc7ae663f2efae43b8546014830450221009a6ed18e6873bc3644332a6ee2\+              \1c152a5b102821865350df7a8c74451a51f9f2022050d801fb4895d7d7fbf4\+              \52824c0168347f5c0cbe821cf6a97a63af5b8b2563c601008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88\+              \ac6868f7010000"+          , HtlcTxVector HtlcSuccess 4 2+              "02000000000101403ad7602b43293497a3a2235a12ecefda4f3a1f1d06e49b\+              \1786d945685de1ff02000000000000000001f1090000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100ae5fc7717ae684bc1fcf9020854e5dbe9842c9e7472879ac\+              \06ff95ac2bb10e4e022057728ada4c00083a3e65493fb5d50a232165948a1a\+              \0f530ef63185c2c8c56504014730440220408ad3009827a8fccf774cb28558\+              \7686bfb2ed041f89a89453c311ce9c8ee0f902203c7392d9f8306d3a46522a\+              \66bd2723a7eb2628cb2d9b34d4c104f1766bf3750201200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with five outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 2194+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8005d007000000000000220020403d394747ca\+          \e42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5b80b0000000000\+          \00220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635\+          \c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665\+          \fbfe92b4a6878e88a499f741c4c0c62d0000000000160014cc1b07838e387deacd\+          \0e5232e1e8b49f4c29e48440966a00000000002200204adb4e2f00643db396dd12\+          \0d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022072c2e2b1c8\+          \99b2242656a537dde2892fa3801be0d6df0a87836c550137acde8302201654aa19\+          \74d37a829083c3ba15088689f30b56d6a4f6cb14c7bad0ee3116d3980147304402\+          \204bb3d6e279d71d9da414c82de42f1f954267c762b2e2eb8b76bc3be4ea07d4b0\+          \022014febc009c5edc8c3fc5d94015de163200f780046f1c293bfed8568f08b70f\+          \b301475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8e\+          \fe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6\+          \d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 2 0+              "02000000000101153cd825fdb3aa624bfe513e8031d5d08c5e582fb3d1d1fe\+              \8faf27d3eed410cd0000000000000000000122020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100939726680351a7856c1bc386d4a1f422c7d29bd7b56afc13\+              \9570f508474e6c40022023175a799ccf44c017fbaadb924c40b2a12115a5b7\+              \d0dfd3228df803a2de84500148304502210099c98c2edeeee6ec0fb5f3bea8\+              \b79bb016a2717afa9b5072370f34382de281d302206f5e2980a995e045cf90\+              \a547f0752a7ee99d48547bc135258fe7bc07e015430101008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188\+              \ac6868f6010000"+          , HtlcTxVector HtlcTimeout 3 1+              "02000000000101153cd825fdb3aa624bfe513e8031d5d08c5e582fb3d1d1fe\+              \8faf27d3eed410cd010000000000000000010a060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022021bb883bf324553d085ba2e821cad80c28ef8b303dbead8f98\+              \e548783c02d1600220638f9ef2a9bba25869afc923f4b5dc38be3bb459f9ef\+              \a5d869392d5f7779a4a001483045022100fd85bd7697b89c08ec12acc8ba89\+              \b23090637d83abd26ca37e01ae93e67c367302202b551fe69386116c47f984\+              \aab9c8dfd25d864dcde5d3389cfbef2447a85c4b7701008576a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac\+              \6868f7010000"+          , HtlcTxVector HtlcSuccess 4 2+              "02000000000101153cd825fdb3aa624bfe513e8031d5d08c5e582fb3d1d1fe\+              \8faf27d3eed410cd020000000000000000019a090000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100c9e6f0454aa598b905a35e641a70cc9f67b5f38cc4b00843\+              \a041238c4a9f1c4a0220260a2822a62da97e44583e837245995ca2e3678176\+              \9c52f19e498efbdcca262b014830450221008a9f2ea24cd455c2b64c1472a5\+              \fa83865b0a5f49a62b661801e884cf2849af8302204d44180e50bf6adfcf1c\+              \1e581d75af91aba4e28681ce4a5ee5f3cbf65eca10f3012004040404040404\+              \040404040404040404040404040404040404040404040404048a76a9141401\+              \1f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2\+              \a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763\+              \a91418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a46\+              \946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67\+              \7502f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with four outputs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 2195+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8004b80b000000000000220020c20b5d1f8584\+          \fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000\+          \002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499\+          \f741c4c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e4\+          \84b8976a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a\+          \40d857accc862d6b7dd80e0400473044022044d592025b610c0d678f65032e8703\+          \5cdfe89d1598c522cc32524ae8172417c30220749fef9d5b2ae8cdd91ece442ba8\+          \809bc891efedae2291e578475f97715d17670147304402201a8c1b1f9671cd9e46\+          \c7323a104d7047cc48d3ee80d40d4512e0c72b8dc65666022066d7f9a2ce18c9eb\+          \22d2739ffcce05721c767f9b607622a31b6ea5793ddce40301475221023da092f6\+          \980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b\+          \623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e19\+          \5220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 3 0+              "020000000001018130a10f09b13677ba2885a8bca32860f3a952e5912b829a\+              \473639b5a2c07b900000000000000000000109060000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100e57b845066a06ee7c2cbfc29eabffe52daa9bf6f6de76006\+              \6d04df9f9b250e0002202ffb197f0e6e0a77a75a9aff27014bd3de83b7f748\+              \d7efef986abe655e1dd50e01483045022100ecc8c6529d0b2316d046f0f075\+              \7c1e1c25a636db168ec4f3aa1b9278df685dc0022067ae6b65e936f1337091\+              \f7b18a15935b608c5f2cdddb2f892ed0babfdd376d7601008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88\+              \ac6868f7010000"+          , HtlcTxVector HtlcSuccess 4 1+              "020000000001018130a10f09b13677ba2885a8bca32860f3a952e5912b829a\+              \473639b5a2c07b900100000000000000000199090000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100d193b7ecccad8057571620a0b1ffa6c48e9483311723b59c\+              \f536043b20bc51550220546d4bd37b3b101ecda14f6c907af46ec391abce1c\+              \d9c7ce22b1a62b534f2f2a01473044022014d66f11f9cacf923807eba49542\+              \076c5fe5cccf252fb08fe98c78ef3ca6ab5402201b290dbe043cc512d9d78d\+              \e074a5a129b8759bc6a6c546b190d120b690bd6e8201200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with four outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 3702+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8004b80b000000000000220020c20b5d1f8584\+          \fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000\+          \002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499\+          \f741c4c0c62d0000000000160014cc1b07838e387deacd0e5232e1e8b49f4c29e4\+          \846f916a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a\+          \40d857accc862d6b7dd80e0400483045022100e5efb73c32d32da2d79702299b63\+          \17de6fb24a60476e3855926d78484dd1b3c802203557cb66a42c944ef06e00bcc4\+          \da35a5bcb2f185aab0f8e403e519e1d66aaf750148304502210092a587aeb777f8\+          \69e7ff0d7898ea619ee26a3dacd1f3672b945eea600be431100220077ee9eae352\+          \8d15251f2a52b607b189820e57a6ccfac8d1af502b132ee4016901475221023da0\+          \92f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e\+          \9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae\+          \3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 3 0+              "020000000001018db483bff65c70ee71d8282aeec5a880e2e2b39e45772bda\+              \5460403095c62e3f0000000000000000000122020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402206fa54c11f98c3bae1e93df43fc7affeb05b476bf8060c03e29\+              \c377c69bc08e8b0220672701cce50d5c379ff45a5d2cfe48ac44973adb066a\+              \c32608e21221d869bb890147304402206e36c683ebf2cb16bcef3d5439cf8b\+              \53cd97280a365ed8acd7abb85a8ba5f21c02206e8621edfc2a5766cbc96eb6\+              \7fd501127ff163eb6b85518a39f7d4974aef126f01008576a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac68\+              \68f7010000"+          , HtlcTxVector HtlcSuccess 4 1+              "020000000001018db483bff65c70ee71d8282aeec5a880e2e2b39e45772bda\+              \5460403095c62e3f0100000000000000000176050000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022057649739b0eb74d541ead0dfdb3d4b2c15aa192720031044c3\+              \434c67812e5ca902201e5ede42d960ae551707f4a6b34b09393cf4dee24185\+              \07daa022e3550dbb58170147304402207faad26678c8850e01b4a0696d6084\+              \1f7305e1832b786110ee9075cb92ed14a30220516ef8ee5dfa80824ea28cbc\+              \ec0dd95f8b847146257c16960db98507db15ffdc0120040404040404040404\+              \04040404040404040404040404040404040404040404048a76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \18bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with three outputs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 3703+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8003a00f0000000000002200208c48d1516039\+          \7c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d00000000\+          \00160014cc1b07838e387deacd0e5232e1e8b49f4c29e484eb936a000000000022\+          \00204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd8\+          \0e040047304402201b736d1773a124c745586217a75bed5f66c05716fbe8c7db4f\+          \db3c3069741cdd02205083f39c321c1bcadfc8d97e3c791a66273d936abac0c6a2\+          \fde2ed46019508e101483045022100b495d239772a237ff2cf354b1b11be152fd8\+          \52704cb184e7356d13f2fb1e5e430220723db5cdb9cbd6ead7bfd3deb419cf4105\+          \3a932418cbb22a67b581f40bc1f13e01475221023da092f6980e58d2c037173180\+          \e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66\+          \d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 4 0+              "0200000000010120060e4a29579d429f0f27c17ee5f1ee282f20d706d6f90b\+              \63d35946d8f3029a0000000000000000000175050000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100c34c61735f93f2e324cc873c3b248111ccf8f6db15d59695\+              \83757010d4ad2b4602207867bb919b2ddd6387873e425345c9b7fd18d1d66a\+              \ba41f3607bc2896ef3c30a01483045022100988c143e2110067117d2321bdd\+              \4bd16ca1734c98b29290d129384af0962b634e02206c1b02478878c5f54701\+              \8b833986578f90c3e9be669fe5788ad0072a55acbb05012004040404040404\+              \040404040404040404040404040404040404040404040404048a76a9141401\+              \1f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2\+              \a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763\+              \a91418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a46\+              \946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67\+              \7502f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with three outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 4914+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8003a00f0000000000002200208c48d1516039\+          \7c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d00000000\+          \00160014cc1b07838e387deacd0e5232e1e8b49f4c29e484ae8f6a000000000022\+          \00204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd8\+          \0e0400483045022100d72638bc6308b88bb6d45861aae83e5b9ff6e10986546e13\+          \bce769c70036e2620220320be7c6d66d22f30b9fcd52af66531505b1310ca3b848\+          \c19285b38d8a1a8c1901483045022100b4b16d5f8cc9fc4c1aff48831e832a0d89\+          \90e133978a66e302c133550954a44d022073573ce127e2200d316f6b612803a5c0\+          \c97b8d20e1e44dbe2ac0dd2fb8c9524401475221023da092f6980e58d2c0371731\+          \80e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d\+          \66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 4 0+              "02000000000101a9172908eace869cc35128c31fc2ab502f72e4dff31aab23\+              \e0244c4b04b11ab00000000000000000000122020000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100f43591c156038ba217756006bb3c55f7d113a325cdd7d930\+              \3c82115372858d68022016355b5aadf222bc8d12e426c75f4a03423917b244\+              \3a103eb2a498a3a2234374014730440220585dee80fafa264beac535c3c0bb\+              \5838ac348b156fdc982f86adc08dfc9bfd250220130abb82f9f295cc9ef423\+              \dcfef772fde2acd85d9df48cc538981d26a10a9c1001200404040404040404\+              \0404040404040404040404040404040404040404040404048a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac686800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with two outputs untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 4915+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8002c0c62d0000000000160014cc1b07838e38\+          \7deacd0e5232e1e8b49f4c29e484fa926a00000000002200204adb4e2f00643db3\+          \96dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004830450221008a\+          \953551f4d67cb4df3037207fc082ddaf6be84d417b0bd14c80aab66f1b01a40220\+          \7508796dc75034b2dee876fe01dc05a08b019f3e5d689ac8842ade2f1befccf501\+          \47304402203a286936e74870ca1459c700c71202af0381910a6bfab687ef494ef1\+          \bc3e02c902202506c362d0e3bee15e802aa729bf378e051644648253513f1c085b\+          \264cc2a72001475221023da092f6980e58d2c037173180e9a465476026ee50f966\+          \95963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385\+          \a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with two outputs untrimmed (maximum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 9651180+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b800222020000000000002200204adb4e2f0064\+          \3db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80ec0c62d00000000\+          \00160014cc1b07838e387deacd0e5232e1e8b49f4c29e4840400483045022100e1\+          \1b638c05c650c2f63a421d36ef8756c5ce82f2184278643520311cdf50aa200220\+          \259565fb9c8e4a87ccaf17f27a3b9ca4f20625754a0920d9c6c239d8156a11de01\+          \47304402200a8544eba1d216f5c5e530597665fa9bec56943c0f66d98fc3d028df\+          \52d84f7002201e45fa5c6bc3a506cc2553e7d1c0043a9811313fc39c954692c0d4\+          \7cfce2bbd301475221023da092f6980e58d2c037173180e9a465476026ee50f966\+          \95963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385\+          \a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with one output untrimmed (minimum feerate)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 9651181+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8001c0c62d0000000000160014cc1b07838e38\+          \7deacd0e5232e1e8b49f4c29e484040047304402207e8d51e0c570a5868a78414f\+          \4e0cbfaed1106b171b9581542c30718ee4eb95ba02203af84194c97adf98898c9a\+          \fe2f2ed4a7f8dba05a2dfab28ac9d9c604aa49a3790147304402202ade01420083\+          \09eb376736575ad58d03e5b115499709c6db0b46e36ff394b492022037b63d78d6\+          \6404d6504d4c4ac13be346f3d1802928a6d3ad95a6a944227161a201475221023d\+          \a092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb2103\+          \0e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152\+          \ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with fee greater than funder amount"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 9651936+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8001c0c62d0000000000160014cc1b07838e38\+          \7deacd0e5232e1e8b49f4c29e484040047304402207e8d51e0c570a5868a78414f\+          \4e0cbfaed1106b171b9581542c30718ee4eb95ba02203af84194c97adf98898c9a\+          \fe2f2ed4a7f8dba05a2dfab28ac9d9c604aa49a3790147304402202ade01420083\+          \09eb376736575ad58d03e5b115499709c6db0b46e36ff394b492022037b63d78d6\+          \6404d6504d4c4ac13be346f3d1802928a6d3ad95a6a944227161a201475221023d\+          \a092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb2103\+          \0e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152\+          \ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with 3 htlc outputs, 2 offered having the same \+          \amount and preimage"+      , cv_to_local_msat = 6987999999+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 253+      , cv_dust_limit_sat = 546+      , cv_htlcs = [1, 5, 6]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b8005d007000000000000220020748eba944fed\+          \c8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8ce0c2d88130000000000\+          \00220020305c12e1a0bc21e283c131cea1c66d68857d28b7b2fce0a6fbc40c1648\+          \52121b8813000000000000220020305c12e1a0bc21e283c131cea1c66d68857d28\+          \b7b2fce0a6fbc40c164852121bc0c62d0000000000160014cc1b07838e387deacd\+          \0e5232e1e8b49f4c29e484a69f6a00000000002200204adb4e2f00643db396dd12\+          \0d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402200d10bf5bc5\+          \397fc59d7188ae438d80c77575595a2d488e41bd6363a810cc8d72022012b57e71\+          \4fbbfdf7a28c47d5b370cb8ac37c8545f596216e5b21e9b236ef457c0147304402\+          \207d0870964530f97b62497b11153c551dca0a1e226815ef0a336651158da0f824\+          \02200f5378beee0e77759147b8a0a284decd11bfd2bc55c8fafa41c134fe996d43\+          \c801475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8e\+          \fe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6\+          \d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 1 0+              "020000000001014bdccf28653066a2c554cafeffdfe1e678e64a69b056684d\+              \eb0c4fba909423ec000000000000000000011f070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100b470fe12e5b7fea9eccb8cbff1972cea4f96758041898982\+              \a02bcc7f9d56d50b0220338a75b2afaab4ec00cdd2d9273c68c7581ff5a28b\+              \cbb40c4d138b81f1d45ce501473044022017b90c65207522a907fb6a137f9d\+              \d528b3389465a8ae72308d9e1d564f512cf402204fc917b4f0e88604a3e994\+              \f85bfae7c7c1f9d9e9f78e8cd112e0889720d9405b01200101010101010101\+              \0101010101010101010101010101010101010101010101018a76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f501b175ac686800000000"+          , HtlcTxVector HtlcTimeout 6 1+              "020000000001014bdccf28653066a2c554cafeffdfe1e678e64a69b056684d\+              \eb0c4fba909423ec01000000000000000001e1120000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100b575379f6d8743cb0087648f81cfd82d17a97fbf8f67e058\+              \c65ce8b9d25df9500220554a210d65b02d9f36c6adf0f639430ca8293196ba\+              \5089bf67cc3a9813b7b00a01483045022100ee2e16b90930a479b13f8823a7\+              \f14b600198c838161160b9436ed086d3fc57e002202a66fa2324f342a17129\+              \949c640bfe934cbc73a869ba7c06aa25c5a3d0bfb53d01008576a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a9142002cc93ebefbb1b73f0af055dcc27a0b504ad7688\+              \ac6868f9010000"+          , HtlcTxVector HtlcTimeout 5 2+              "020000000001014bdccf28653066a2c554cafeffdfe1e678e64a69b056684d\+              \eb0c4fba909423ec02000000000000000001e1120000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220471c9f3ad92e49b13b7b8059f43ecf8f7887b0dccbb9fdb54b\+              \fe23d62a8ae332022024bd22fae0740e86a44228c35330da9526fd7306dffb\+              \2b9dc362d5e78abef7cc0147304402207157f452f2506d73c3151923118938\+              \00cfb3cc235cc1185b1cfcc136b55230db022014be242dbc6c5da141fec403\+              \4e7f387f74d6ff1899453d72ba957467540e1ecb01008576a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9142002cc93ebefbb1b73f0af055dcc27a0b504ad7688ac68\+              \68fa010000"+          ]+      }+  ]++-- Appendix D -----------------------------------------------------------------++-- | Appendix D storage vectors (seed 0xFF..FF; incorrect entries are+--   seeded with 0x00..00).+appendix_d_storage :: [StorageVector]+appendix_d_storage = [+    StorageVector+      { sv_name =+          "insert_secret correct sequence"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 True+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          , StorageStep 281474976710651 True+            "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd"+          , StorageStep 281474976710650 True+            "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2"+          , StorageStep 281474976710649 True+            "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32"+          , StorageStep 281474976710648 True+            "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #1 incorrect"+      , sv_steps = [+            StorageStep 281474976710655 True+            "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148"+          , StorageStep 281474976710654 False+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #2 incorrect (#1 derived from incorrect)"+      , sv_steps = [+            StorageStep 281474976710655 True+            "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148"+          , StorageStep 281474976710654 True+            "dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 False+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #3 incorrect"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a"+          , StorageStep 281474976710652 False+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #4 incorrect (1,2,3 derived from incorrect)"+      , sv_steps = [+            StorageStep 281474976710655 True+            "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148"+          , StorageStep 281474976710654 True+            "dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3"+          , StorageStep 281474976710653 True+            "c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a"+          , StorageStep 281474976710652 True+            "ba65d7b0ef55a3ba300d4e87af29868f394f8f138d78a7011669c79b37b936f4"+          , StorageStep 281474976710651 True+            "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd"+          , StorageStep 281474976710650 True+            "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2"+          , StorageStep 281474976710649 True+            "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32"+          , StorageStep 281474976710648 False+            "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #5 incorrect"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 True+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          , StorageStep 281474976710651 True+            "631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6"+          , StorageStep 281474976710650 False+            "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #6 incorrect (5 derived from incorrect)"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 True+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          , StorageStep 281474976710651 True+            "631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6"+          , StorageStep 281474976710650 True+            "b7e76a83668bde38b373970155c868a653304308f9896692f904a23731224bb1"+          , StorageStep 281474976710649 True+            "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32"+          , StorageStep 281474976710648 False+            "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #7 incorrect"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 True+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          , StorageStep 281474976710651 True+            "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd"+          , StorageStep 281474976710650 True+            "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2"+          , StorageStep 281474976710649 True+            "e7971de736e01da8ed58b94c2fc216cb1dca9e326f3a96e7194fe8ea8af6c0a3"+          , StorageStep 281474976710648 False+            "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17"+          ]+      }+  , StorageVector+      { sv_name =+          "insert_secret #8 incorrect"+      , sv_steps = [+            StorageStep 281474976710655 True+            "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"+          , StorageStep 281474976710654 True+            "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964"+          , StorageStep 281474976710653 True+            "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8"+          , StorageStep 281474976710652 True+            "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116"+          , StorageStep 281474976710651 True+            "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd"+          , StorageStep 281474976710650 True+            "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2"+          , StorageStep 281474976710649 True+            "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32"+          , StorageStep 281474976710648 False+            "a7efbc61aac46d34f77778bac22c8a20c6a46ca460addc49009bda875ec88fa4"+          ]+      }+  ]++-- Appendix F -----------------------------------------------------------------++-- | Appendix F (anchors) commitment vectors.+appendix_f :: [CommitVector]+appendix_f = [+    CommitVector+      { cv_name =+          "simple commitment tx with no HTLCs"+      , cv_to_local_msat = 7000000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 15000+      , cv_dust_limit_sat = 546+      , cv_htlcs = []+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80044a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994c0c62d0000000000220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b\+          \84ebe014ad1d43a564d012994a508b6a00000000002200204adb4e2f00643db396\+          \dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004830450221008266\+          \ac6db5ea71aac3c95d97b0e172ff596844851a3216eb88382a8dddfd33d2022050\+          \e240974cfd5d708708b4365574517c18e7ae535ef732a3484d43d0d82be9f70148\+          \3045022100f89034eba16b2be0e5581f750a0a6309192b75cce0f202f0ee2b4ec0\+          \cc394850022076c65dc507fe42276152b7a3d90e961e678adbe966e916ecfe85e6\+          \4d430e75f301475221023da092f6980e58d2c037173180e9a465476026ee50f966\+          \95963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385\+          \a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "simple commitment tx with no HTLCs and single anchor"+      , cv_to_local_msat = 10000000000+      , cv_to_remote_msat = 0+      , cv_feerate_per_kw = 15000+      , cv_dust_limit_sat = 546+      , cv_htlcs = []+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80024a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f10529800000000\+          \002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b\+          \7dd80e0400473044022007cf6b405e9c9b4f527b0ecad9d8bb661fabb8b12abf7d\+          \1c0b3ad1855db3ed490220616d5c1eeadccc63bd775a131149455d62d95a42c2a1\+          \b01cc7821fc42dce7778014730440220655bf909fb6fa81d086f1336ac72c97906\+          \dce29d1b166e305c99152d810e26e1022051f577faa46412c46707aaac46b65d50\+          \053550a66334e00a44af2706f27a865801475221023da092f6980e58d2c0371731\+          \80e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d\+          \66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with seven outputs untrimmed"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 644+      , cv_dust_limit_sat = 546+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80094a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994e80300000000000022002010f88bf09e56f14fb4543fd26e47b0db50ea5d\+          \e9cf3fc46434792471082621aed0070000000000002200203e68115ae0b15b8de7\+          \5b6c6bc9af5ac9f01391544e0870dae443a1e8fe7837ead0070000000000002200\+          \20fe0598d74fee2205cc3672e6e6647706b4f3099713b4661b62482c3addd04a5e\+          \b80b000000000000220020f96d0334feb64a4f40eb272031d07afcb038db56aa57\+          \446d60308c9f8ccadef9a00f000000000000220020ce6e751274836ff59622a0d1\+          \e07f8831d80bd6730bd48581398bfadd2bb8da9ac0c62d0000000000220020f339\+          \4e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d012994a4f996a\+          \00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857ac\+          \cc862d6b7dd80e0400483045022100ef82a405364bfc4007e63a7cc82925a513d7\+          \9065bdbc216d60b6a4223a323f8a02200716730b8561f3c6d362eaf47f202e99fb\+          \30d0557b61b92b5f9134f8e2de368101483045022100e0106830467a558c07544a\+          \3de7715610c1147062e7d091deeebe8b5c661cda9402202ad049c1a6d04834317a\+          \78483f723c205c9f638d17222aafc620800cc1b6ae3501475221023da092f6980e\+          \58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d\+          \2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 0 2+              "02000000000101b8cefef62ea66f5178b9361b2371be0759cbc8c689bcfa7a\+              \8e6746d497ec221a02000000000100000001e8030000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220746dc89a593e1b50915db63359b50c3c404f8324f78075c877\+              \08c866ccefda2502202a11062012dc8607b17e8c46ea4eefdfcc6b89a35440\+              \de99432ba12788d7bb1783473044022036f77f88b49bd03dc16d3a015efcc7\+              \acffc2a93b213324035d77a716f79013ff02203c218eb882a40402a8bb05db\+              \b751a442345a4e56307be76b214b6d4db9ed3c920120000000000000000000\+              \00000000000000000000000000000000000000000000008d76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc688527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f401b175ac6851b2756800000000"+          , HtlcTxVector HtlcTimeout 2 3+              "02000000000101b8cefef62ea66f5178b9361b2371be0759cbc8c689bcfa7a\+              \8e6746d497ec221a03000000000100000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100a847bc3b8cf2441013725ae32dc589c419835d069afd6d7f\+              \3d9834d8be7cea6e02204ce9d35ec7f0788da80e4d62e810c4ccd13617e58f\+              \a10832e38fb7ae87bdf33883473044022026739b1adbfa34c485bf0e5a19e0\+              \cf7532f64545bcc5c95b95643ccc2d351e1902201743bb1b34f00e031cc15f\+              \6eff0f8ab764508d2681ff965ba62e76e540bca1e801008876a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac\+              \6851b27568f6010000"+          , HtlcTxVector HtlcSuccess 1 4+              "02000000000101b8cefef62ea66f5178b9361b2371be0759cbc8c689bcfa7a\+              \8e6746d497ec221a04000000000100000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022035977f2aa6d6ae12f1dae3366440faa17558e9c88c3188bfc8\+              \df7e276c6c65410220659f1f9070c725d9d46e43b99a36fc6d711d36069704\+              \add2d57fc1fa2818cf12834730440220191ba44e57b1601a59d99f85971d48\+              \01b286d428de275487c87ceeb8df1a4811022002215875d92833df0c9615c9\+              \096cf97152f87139ed9f82718bbb5b8b3d3125240120010101010101010101\+              \01010101010101010101010101010101010101010101018d76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \4b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f501b175ac6851b2756800000000"+          , HtlcTxVector HtlcTimeout 3 5+              "02000000000101b8cefef62ea66f5178b9361b2371be0759cbc8c689bcfa7a\+              \8e6746d497ec221a05000000000100000001b80b0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022002f3ff9a31270092c214d3d5b8b4f826599404bba64b87f753\+              \6ef6324d41551b022079dc4cb25f7ecd84b49f5cce03eae7e2655b59f39d54\+              \3765daef0d4f11c93fa283483045022100f03047e38bc0aae2d80d53424b8c\+              \1d1b8139120e2bf09ad31a2803978745e6e102205b74c0eef0b472710b98c7\+              \7e619ee9d0cc47a9dd786f4f214a564f44d79f9b9a01008876a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c82012087647552\+              \7c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b\+              \140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac\+              \6851b27568f7010000"+          , HtlcTxVector HtlcSuccess 4 6+              "02000000000101b8cefef62ea66f5178b9361b2371be0759cbc8c689bcfa7a\+              \8e6746d497ec221a06000000000100000001a00f0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220547937564288f64fb3e3c945a1348b912471f0c1c4cc7dc8ce\+              \ca15a4cbd299b6022053c4f8e30832b13dfbe31e4091e313428625e0b5ac61\+              \eecba93f8f11c1e2622583473044022022604660234aef9bd21284598ec50f\+              \070ac82a3a0152e0af5e98a02cd6e8976f022042b0b9112ee00806b856dff6\+              \de52a82c98b036a4fe14bb5fd2926725e2fc81910120040404040404040404\+              \04040404040404040404040404040404040404040404048d76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \18bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f801b175ac6851b2756800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with six outputs untrimmed (minimum dust limit)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 645+      , cv_dust_limit_sat = 1001+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80084a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994d0070000000000002200203e68115ae0b15b8de75b6c6bc9af5ac9f01391\+          \544e0870dae443a1e8fe7837ead007000000000000220020fe0598d74fee2205cc\+          \3672e6e6647706b4f3099713b4661b62482c3addd04a5eb80b0000000000002200\+          \20f96d0334feb64a4f40eb272031d07afcb038db56aa57446d60308c9f8ccadef9\+          \a00f000000000000220020ce6e751274836ff59622a0d1e07f8831d80bd6730bd4\+          \8581398bfadd2bb8da9ac0c62d0000000000220020f3394e1e619b0eca1f91be2f\+          \b5ab4dfc59ba5b84ebe014ad1d43a564d012994abc996a00000000002200204adb\+          \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040048\+          \3045022100d57697c707b6f6d053febf24b98e8989f186eea42e37e9e91663ec2c\+          \70bb8f70022079b0715a472118f262f43016a674f59c015d9cafccec885968e76d\+          \9d9c5d005101473044022025d97466c8049e955a5afce28e322f4b34d2561118e5\+          \2332fb400f9b908cc0a402205dc6fba3a0d67ee142c428c535580cd1f2ff42e2f8\+          \9b47e0c8a01847caffc31201475221023da092f6980e58d2c037173180e9a46547\+          \6026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce\+          \504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 2 2+              "02000000000101104f394af4c4fad78337f95e3e9f802f4c0d86ab231853af\+              \09b285348561320002000000000100000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100e04d160a326432659fe9fb127304c1d348dfeaba840081bd\+              \c57d8efd902a48d8022008a824e7cf5492b97e4d9e03c06a09f822775a44f6\+              \b5b2533a2088904abfc28283483045022100b7c49846466b13b190ff739bbe\+              \3005c105482fc55539e55b1c561f76b6982b6c02200e5c35808619cf543c84\+              \05cff9fedd25f333a4a2f6f6d5e8af8150090c40ef0901008876a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188\+              \ac6851b27568f6010000"+          , HtlcTxVector HtlcSuccess 1 3+              "02000000000101104f394af4c4fad78337f95e3e9f802f4c0d86ab231853af\+              \09b285348561320003000000000100000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100fbdc3c367ce3bf30796025cc590ee1f2ce0e72ae1ac19f59\+              \86d6d0a4fc76211f02207e45ae9267e8e820d188569604f71d1abd11bd385d\+              \58853dd7dc034cdb3e9a6e83483045022100d29330f24db213b26206870609\+              \9b39c15fa7e070c3fcdf8836c09723fc4d365602203ce57d01e9f28601e461\+              \a0b5c4a50119b270bde8b70148d133a6849c70b115ac012001010101010101\+              \010101010101010101010101010101010101010101010101018d76a9141401\+              \1f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2\+              \a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763\+              \a9144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a46\+              \946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67\+              \7502f501b175ac6851b2756800000000"+          , HtlcTxVector HtlcTimeout 3 4+              "02000000000101104f394af4c4fad78337f95e3e9f802f4c0d86ab231853af\+              \09b285348561320004000000000100000001b80b0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022066c5ef625cee3ddd2bc7b6bfb354b5834cf1cc6d52dd972fb4\+              \1b7b225437ae4a022066cb85647df65c6b87a54e416dcdcca778a776c36a96\+              \43d2b5dc793c9b29f4c18347304402202d4ce515cd9000ec37575972d70b8d\+              \24f73909fb7012e8ebd8c2066ef6fe187902202830b53e64ea565fecd0f398\+              \100691da6bb2a5cf9bb0d1926f1d71d05828a11e01008876a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac68\+              \51b27568f7010000"+          , HtlcTxVector HtlcSuccess 4 5+              "02000000000101104f394af4c4fad78337f95e3e9f802f4c0d86ab231853af\+              \09b285348561320005000000000100000001a00f0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022022c7e11595c53ee89a57ca76baf0aed730da035952d6ab3fe6\+              \459f5eff3b337a022075e10cc5f5fd724a35ce4087a5d03cd616698626c698\+              \14032132b50bb97dc61583483045022100b20cd63e0587d1711beaebda4730\+              \775c4ac8b8b2ec78fe18a0c44c3f168c25230220079abb7fc4924e2fca5950\+              \842e5b9e416735585026914570078c4ef62f28622601200404040404040404\+              \0404040404040404040404040404040404040404040404048d76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac6851b2756800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with four outputs untrimmed (minimum dust limit)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 2185+      , cv_dust_limit_sat = 2001+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80064a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994b80b000000000000220020f96d0334feb64a4f40eb272031d07afcb038db\+          \56aa57446d60308c9f8ccadef9a00f000000000000220020ce6e751274836ff596\+          \22a0d1e07f8831d80bd6730bd48581398bfadd2bb8da9ac0c62d00000000002200\+          \20f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d012994a\+          \c5916a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40\+          \d857accc862d6b7dd80e0400483045022100cd8479cfe1edb1e5a1d487391e0451\+          \a469c7171e51e680183f19eb4321f20e9b02204eab7d5a6384b1b08e03baa6e4d9\+          \748dfd2b5ab2bae7e39604a0d0055bbffdd501473044022040f63a16148cf35c8d\+          \3d41827f5ae7f7c3746885bb64d4d1b895892a83812b3e02202fcf95c2bf02c466\+          \163b3fa3ced6a24926fbb4035095a96842ef516e86ba54c001475221023da092f6\+          \980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b\+          \623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e19\+          \5220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcTimeout 3 2+              "02000000000101ac13a7715f80b8e52dda43c6929cade5521bdced3a405da0\+              \2b443f1ffb1e33cc02000000000100000001b80b0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402206870514a72ad6e723ff7f1e0370d7a33c1cd2a0b9272674143\+              \ebaf6a1d02dee102205bd953c34faf5e7322e9a1c0103581cb090280fda4f1\+              \039ee8552668afa90ebb834730440220669de9ca7910eff65a7773ebd14a9f\+              \c371fe88cde5b8e2a81609d85c87ac939b02201ac29472fa4067322e92d75b\+              \624942d60be5050139b20bb363db75be79eb946f01008876a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac68\+              \51b27568f7010000"+          , HtlcTxVector HtlcSuccess 4 3+              "02000000000101ac13a7715f80b8e52dda43c6929cade5521bdced3a405da0\+              \2b443f1ffb1e33cc03000000000100000001a00f0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100949e8dd938da56445b1cdfdebe1b7efea086edd05d89910d\+              \205a1e2e033ce47102202cbd68b5262ab144d9ec12653f87dfb0bb6bd05d1f\+              \58ae1e523f028eaefd727183483045022100e3104ed8b239f8019e5f0a1a73\+              \d7782a94a8c36e7984f476c3a0b3cb0e62e27902207e3d52884600985f8a20\+              \98e53a5c30dd6a5e857733acfaa07ab2162421ed2688012004040404040404\+              \040404040404040404040404040404040404040404040404048d76a9141401\+              \1f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2\+              \a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763\+              \a91418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a46\+              \946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67\+              \7502f801b175ac6851b2756800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with three outputs untrimmed (minimum dust limit)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 3687+      , cv_dust_limit_sat = 3001+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80054a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994a00f000000000000220020ce6e751274836ff59622a0d1e07f8831d80bd6\+          \730bd48581398bfadd2bb8da9ac0c62d0000000000220020f3394e1e619b0eca1f\+          \91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d012994aa28b6a00000000002200\+          \204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e\+          \0400483045022100c970799bcb33f43179eb43b3378a0a61991cf2923f69b36ef1\+          \2548c3df0e6d500220413dc27d2e39ee583093adfcb7799be680141738babb31cc\+          \7b0669a777a31f5d01483045022100ad6c71569856b2d7ff42e838b4abe74a7134\+          \26b37f22fa667a195a4c88908c6902202b37272b02a42dc6d9f4f82cab3eaf84ac\+          \882d9ed762859e1e75455c2c22837701475221023da092f6980e58d2c037173180\+          \e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66\+          \d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 4 2+              "02000000000101542562b326c08e3a076d9cfca2be175041366591da334d8d\+              \513ff1686fd95a6002000000000100000001a00f0000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00473044022017b558a3cf5f0cb94269e2e927b29ed22bd2416abb8a7ce6de\+              \4d1256f359b93602202e9ca2b1a23ea3e69f433c704e327739e219804b8c18\+              \8b1d52f74fd5a9de954c83483045022100af7a8b7c7ff2080c68995254cb66\+              \d64d9954edcc5baac3bb4f27ed2d29aaa6120220421c27da7a60574a9263f2\+              \71e0f3bd34594ec6011095190022b3b54596ea03de01200404040404040404\+              \0404040404040404040404040404040404040404040404048d76a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9\+              \1418bc1a114ccf9c052d3d23e28d3b0a9d1227434288527c21030d417a4694\+              \6384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae6775\+              \02f801b175ac6851b2756800000000"+          ]+      }+  , CommitVector+      { cv_name =+          "commitment tx with two outputs untrimmed (minimum dust limit)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 4894+      , cv_dust_limit_sat = 4001+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80044a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994c0c62d0000000000220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b\+          \84ebe014ad1d43a564d012994ad0886a00000000002200204adb4e2f00643db396\+          \dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004830450221009f16\+          \ac85d232e4eddb3fcd750a68ebf0b58e3356eaada45d3513ede7e817bf4c02207c\+          \2b043b4e5f971261975406cb955219fa56bffe5d834a833694b5abc1ce4cfd0148\+          \3045022100e784a66b1588575801e237d35e510fd92a81ae3a4a2a1b90c031ad80\+          \3d07b3f3022021bc5f16501f167607d63b681442da193eb0a76b4b7fd25c2ed4f8\+          \b28fd35b9501475221023da092f6980e58d2c037173180e9a465476026ee50f966\+          \95963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385\+          \a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with one output untrimmed (minimum dust limit)"+      , cv_to_local_msat = 6988000000+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 6216010+      , cv_dust_limit_sat = 4001+      , cv_htlcs = [0, 1, 2, 3, 4]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80024a01000000000000220020e9e86e4823fa\+          \a62e222ebc858a226636856158f07e69898da3b0d1af0ddb3994c0c62d00000000\+          \00220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe014ad1d43a564d0\+          \12994a04004830450221009ad80792e3038fe6968d12ff23e6888a565c3ddd0650\+          \37f357445f01675d63f3022018384915e5f1f4ae157e15debf4f49b61c8d9d2b07\+          \3c7d6f97c4a68caa3ed4c1014830450221008fd5dbff02e4b59020d4cd23a3c30d\+          \3e287065fda75a0a09b402980adf68ccda022001e0b8b620cd915ddff11f1de32a\+          \ddf23d81d51b90e6841b2cb8dcaf3faa5ecf01475221023da092f6980e58d2c037\+          \173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9b\+          \d44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"+      , cv_htlc_txs = []+      }+  , CommitVector+      { cv_name =+          "commitment tx with 3 htlc outputs, 2 offered having the same \+          \amount and preimage"+      , cv_to_local_msat = 6987999999+      , cv_to_remote_msat = 3000000000+      , cv_feerate_per_kw = 253+      , cv_dust_limit_sat = 546+      , cv_htlcs = [1, 5, 6]+      , cv_commit_tx =+          "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b82\+          \0b584a488489000000000038b02b80074a010000000000002200202b1b5854183c\+          \12d3316565972c4668929d314d81c5dcdbb21cb45fe8a9a8114f4a010000000000\+          \00220020e9e86e4823faa62e222ebc858a226636856158f07e69898da3b0d1af0d\+          \db3994d007000000000000220020fe0598d74fee2205cc3672e6e6647706b4f309\+          \9713b4661b62482c3addd04a5e881300000000000022002018e40f9072c44350f1\+          \34bdc887bab4d9bdfc8aa468a25616c80e21757ba5dac788130000000000002200\+          \2018e40f9072c44350f134bdc887bab4d9bdfc8aa468a25616c80e21757ba5dac7\+          \c0c62d0000000000220020f3394e1e619b0eca1f91be2fb5ab4dfc59ba5b84ebe0\+          \14ad1d43a564d012994aad9c6a00000000002200204adb4e2f00643db396dd120d\+          \4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400483045022100b4014970d9\+          \d7962853f3f85196144671d7d5d87426250f0a5fdaf9a55292e92502205360910c\+          \9abb397467e19dbd63d081deb4a3240903114c98cec0a23591b79b760147304402\+          \2027b38dfb654c34032ffb70bb43022981652fce923cbbe3cbe7394e2ade8b3423\+          \0220584195b78da6e25c2e8da6b4308d9db25b65b64975db9266163ef592abb7c7\+          \2501475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8e\+          \fe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6\+          \d3c39fa711c152ae3e195220"+      , cv_htlc_txs = [+            HtlcTxVector HtlcSuccess 1 2+              "020000000001013d060d0305c9616eaabc21d41fae85bcb5477b5d7f1c92aa\+              \429cf15339bbe1c402000000000100000001d0070000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \004730440220078fe5343dab88c348a3a8a9c1a9293259dbf35507ae971702\+              \cc39dd623ea9af022011ed0c0f35243cd0bb4d9ca3c772379b2b5f4af93140\+              \e9fdc5600dfec1cdb0c28347304402205df665e2908c7690d2d33eb70e6e11\+              \9958c28febe141a94ed0dd9a55ce7c8cfc0220364d02663a5d019af35c5cd5\+              \fda9465d985d85bbd12db207738d61163449a4240120010101010101010101\+              \01010101010101010101010101010101010101010101018d76a91414011f72\+              \54d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a812\+              \2cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914\+              \4b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a469463\+              \84f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502\+              \f501b175ac6851b2756800000000"+          , HtlcTxVector HtlcTimeout 6 3+              "020000000001013d060d0305c9616eaabc21d41fae85bcb5477b5d7f1c92aa\+              \429cf15339bbe1c40300000000010000000188130000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \0047304402202df6bf0f98a42cfd0172a16bded7d1b16c14f5f42ba23f5c54\+              \648c14b647531302200fe1508626817f23925bb56951d5e4b2654c751743ab\+              \6db48a6cce7dda17c01c8347304402203f99ec05cdd89558a23683b471c1dc\+              \ce8f6a92295f1fff3b0b5d21be4d4f97ea022019d29070690fc2c126fe27cc\+              \4ab2f503f289d362721b2efa7418e7fddb939a5b01008876a91414011f7254\+              \d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122c\+              \c726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c\+              \21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b14\+              \0639e752ae67a9142002cc93ebefbb1b73f0af055dcc27a0b504ad7688ac68\+              \51b27568f9010000"+          , HtlcTxVector HtlcTimeout 5 4+              "020000000001013d060d0305c9616eaabc21d41fae85bcb5477b5d7f1c92aa\+              \429cf15339bbe1c40400000000010000000188130000000000002200204adb\+              \4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e05\+              \00483045022100bd206b420c495f3aa714d3ea4766cbe95441deacb5d2f737\+              \f1913349aee7c2ae02200249d2c950dd3b15326bf378ae5d2b871d33d6737f\+              \5d70735f3de8383140f2a183483045022100f2cd35e385b9b7e15b92a5d78d\+              \120b6b2c5af4e974bc01e884c5facb3bb5966c0220706e0506477ce809a400\+              \22d6de8e041e9ef13136c45abee9c36f58a01fdb188b01008876a91414011f\+              \7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8\+              \122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475\+              \527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e2173\+              \4b140639e752ae67a9142002cc93ebefbb1b73f0af055dcc27a0b504ad7688\+              \ac6851b27568fa010000"+          ]+      }+  ]