haskoin-core
Bitcoin & Bitcoin Cash library for Haskell
http://github.com/jprupp/haskoin-core#readme
| Version on this page: | 1.6.0 |
| LTS Haskell 24.66: | 1.2.2 |
| Stackage Nightly 2026-11-06: | 1.6.0 |
| Latest on Hackage: | 1.6.1 |
haskoin-core-1.6.0@sha256:f7bc0fe84aefa0dd92502f5b6fd541c6bde8ba280b036bc363a32aad9c081a4b,6180Haskoin Core
Haskoin Core is a Bitcoin and Bitcoin Cash library for Haskell. It covers keys, addresses, scripts, transactions, PSBTs, block headers and the peer-to-peer message format.
It does no I/O of its own. It never opens a socket or writes a file. You can use it in a wallet, a block explorer or a node, and bring your own networking and storage.
Contents
- Features
- Installation
- Quick start
- Concepts
- Usage guide
- Module overview
- Security notes
- Development
- Related projects
- License
Features
| Area | What you get |
|---|---|
| Networks | Bitcoin and Bitcoin Cash: mainnet, testnet3, testnet4 and regtest |
| Keys | secp256k1 keys, WIF, BIP-32 extended keys and paths, BIP-39 mnemonics, SLIP-39 Shamir shares |
| Signatures | Deterministic ECDSA (RFC 6979) and BIP-340 Schnorr |
| Addresses | Base58, Bech32, Bech32m and CashAddr, including token-aware CashAddr types |
| Scripts | Script parsing and serialization, standard input and output templates, BIP-67 sorted multisig |
| Transactions | Building, signing and verifying P2PK, P2PKH, P2SH, multisig, P2WPKH, P2WSH, nested SegWit and Taproot key path spends |
| Wallet tools | Coin selection, and weight, size and fee estimates |
| PSBT | Versions 0 and 2, with the Taproot fields (BIP-174, BIP-370, BIP-371) |
| Taproot | Script trees, output keys, witnesses, control blocks and signature hashes (BIP-341, BIP-342) |
| Blocks | Header validation, proof of work, the difficulty rules of both chains, Merkle trees and an in-memory header chain |
| Peer-to-peer | Message encoding, including addrv2, compact blocks and compact filter messages, and BIP-37 Bloom filters |
| Testing | QuickCheck generators for the library’s types |
What is not included
- Peer connections and chain sync. Use haskoin-node.
- Address and transaction indexing. Use haskoin-store.
- A script interpreter.
verifyStdTxrecognises the standard templates. It is not a consensus check. - Signet, output descriptors, Miniscript and MuSig2.
- Schnorr transaction signatures,
SIGHASH_UTXOSand token outputs of Bitcoin Cash. An input or an output that uses them does not decode or verify. - BIP-324 encrypted transport.
- BIP-39 word lists other than English.
Installation
System library
Haskoin Core needs libsecp256k1
0.2.0 or later, and pkg-config to find it.
# Debian and Ubuntu
sudo apt-get install libsecp256k1-dev pkg-config
# macOS with Homebrew
brew install secp256k1 pkg-config
# Nix
nix-shell -p secp256k1 pkg-config
Check what you have with pkg-config --modversion libsecp256k1. Debian 11 and
other older systems ship a version below 0.2.0. On those, build the library from
source and enable its recovery module with ./configure --enable-module-recovery.
Compiler
The library is tested with GHC 9.10 and 9.12. It cannot build below GHC 9.2,
because it uses OverloadedRecordDot.
With Cabal
Add the packages to your .cabal file:
build-depends:
base
, bytes
, haskoin-core
, text
You need bytes to serialize values with runPutS and serialize.
Run cabal update first. Haskoin Core needs a recent secp256k1-haskell, and a
stale package index makes the build fail with a dependency error.
With Stack
Haskoin Core is not in Stackage, so add it to stack.yaml as an extra
dependency. Use the latest version from Hackage.
resolver: nightly-2026-10-28
extra-deps:
- haskoin-core-1.6.0
- base16-2.0
- secp256k1-haskell-2.0.0
Quick start
This program turns a mnemonic into the first receiving address of a BIP-84 wallet.
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Data.Text (Text)
import Haskoin
-- This is the public BIP-39 test mnemonic. Never send coins to it.
mnemonic :: Text
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
main :: IO ()
main = withContext $ \ctx -> do
seed <- either fail pure (mnemonicToSeed "" mnemonic)
let master = makeXPrvKey seed
-- Hardened steps need the private key.
account = derivePath ctx ("m/84'/0'/0'" :: HardPath) master
accountPub = deriveXPubKey ctx account
-- The remaining steps only need the public key.
receiving = derivePubPath ctx ("m/0" :: SoftPath) accountPub
(address, _publicKey) = deriveWitnessAddr ctx receiving 0
print (addrToText btc address)
-- => Just "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
The address is the one that BIP-84 publishes for this mnemonic.
Concepts
A few conventions apply across the whole library. Reading this section first will save you time.
The context
Most cryptographic functions take a Ctx, a secp256k1 context. Create one when
your program starts, with withContext or createContext. Then pass it around.
You can share it between threads.
Networks
A Network is a record of the parameters of one chain. Functions that depend on
the chain take one as an argument. Functions that need both take the Network
first and the Ctx second.
| Value | Chain | Name for netByName |
|---|---|---|
btc |
Bitcoin mainnet | btc |
btcTest |
Bitcoin testnet3 | btctest |
btcTest4 |
Bitcoin testnet4 | btctest4 |
btcRegTest |
Bitcoin regtest | btcreg |
bch |
Bitcoin Cash mainnet | bch |
bchTest |
Bitcoin Cash testnet3 | bchtest |
bchTest4 |
Bitcoin Cash testnet4 | bchtest4 |
bchRegTest |
Bitcoin Cash regtest | bchreg |
Record fields
The library is built with NoFieldSelectors, so record fields are not functions.
Turn on OverloadedRecordDot in your modules and read fields with a dot.
print btc.defaultPort
-- => 8333
print (map (.value) btc.genesisTx.outputs)
-- => [5000000000]
Many records share field names. Tx and Psbt both have inputs, for example.
To update such a field, import the module qualified and name the field through
it, as the PSBT example does.
Errors
Functions that can fail return Maybe or Either String. The examples below
turn those into IO failures with either fail pure.
Hashes, paths and public keys have IsString instances, so you can write them as
literals. Those instances call error on bad input. For input you do not control,
use hexToTxHash, hexToBlockHash, parsePath and textToAddr.
Amounts
Amounts are satoshi, as Word64.
Serialization
Most types have Serial instances, from the
bytes package, and also Binary and
Serialize instances. Some types need a context or a network to encode, so they
have Marshal or MarshalJSON instances instead.
| Task | Encode | Decode |
|---|---|---|
| Binary | runPutS (serialize x) |
runGetS deserialize bytes |
| Binary with context | marshal ctx x |
unmarshal ctx bytes |
| Hexadecimal | encodeHex bytes |
decodeHex text |
| JSON | toJSON x |
parseJSON value |
| JSON with context | marshalJSON ctx x |
unmarshalJSON ctx json |
The first argument depends on the type. For example, it is a Ctx for
PublicKey, a Network for XPrvKey and a (Network, Ctx) pair for XPubKey.
unmarshal fails when bytes are left over after the value, and decodeTx does
the same for a transaction. runGetS deserialize reads a value from the start of
the bytes and ignores the rest.
Hash byte order
TxHash and BlockHash show as hexadecimal in reversed byte order. This is the
form that Bitcoin Core and block explorers display, so you can paste those values
straight in. The serialized bytes keep the internal order.
Usage guide
The examples below are fragments of main in this program. Paste one in place of
the comment to run it. If an example starts with import lines, add them to the
top of the file. A -- => comment shows what the line above it prints.
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Data.Bytes.Put (runPutS)
import Data.Bytes.Serial (serialize)
import Haskoin
main :: IO ()
main = withContext $ \ctx -> do
-- This is the public BIP-39 test mnemonic. Never send coins to it.
seed <-
either fail pure $
mnemonicToSeed "" "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
let master = makeXPrvKey seed
-- Paste an example here.
pure ()
Mnemonics
Generate entropy with a cryptographic source. This example uses the entropy package.
import qualified Data.ByteString as B
import System.Entropy (getEntropy)
-- 16 bytes of entropy give 12 words. 32 bytes give 24 words.
entropy <- getEntropy 16
words12 <- either fail pure (toMnemonic entropy)
-- The passphrase is optional. Use "" for none.
newSeed <- either fail pure (mnemonicToSeed "my passphrase" words12)
print (B.length newSeed)
-- => 64
mnemonicToSeed checks the words and the checksum. fromMnemonic gives the
entropy back.
Keys and signatures
-- A secret key is 32 bytes. decodeSecKey refuses zero and values at or above
-- the curve order.
bytes <- maybe (fail "bad hex") pure (decodeHex "0000000000000000000000000000000000000000000000000000000000000001")
secret <- maybe (fail "bad key") pure (decodeSecKey bytes)
-- True asks for the compressed form of the public key.
let private = wrapSecKey True secret
public = derivePublicKey ctx private
print private
-- => PrivateKey {key = <redacted>, compress = True}
print (toWif btc private)
-- => "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
print (encodeHex (marshal ctx public))
-- => "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
print (addrToText btc (pubKeyAddr ctx public))
-- => Just "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"
-- Sign and verify a hash.
let digest = doubleSHA256 bytes
signature = signHash ctx secret digest
print (verifyHashSig ctx digest signature public.point)
-- => True
fromWif btc text reads a WIF key back. Haskoin also re-exports the
secp256k1-haskell API,
which has the Schnorr functions such as signSchnorr and verifySchnorr.
Extended keys
Paths have three types. A HardPath has only hardened steps. A SoftPath has
only normal steps, which a public key can derive. A DerivPath can mix both.
Hardened steps can be written ', h or H.
let account = derivePath ctx ("m/84'/0'/0'" :: HardPath) master
accountPub = deriveXPubKey ctx account
print (xPubExport btc ctx accountPub)
-- => "xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V"
-- The account's public key derives all its receiving addresses.
let receiving = derivePubPath ctx ("m/0" :: SoftPath) accountPub
addresses = [address | (address, _, _) <- deriveWitnessAddrs ctx receiving 0]
print (map (addrToText btc) (take 2 addresses))
-- => [Just "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",Just "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"]
Pick the address function that matches the wallet standard:
| Standard | Address type | Path | One address | Endless list |
|---|---|---|---|---|
| BIP-44 | P2PKH | m/44'/0'/0' |
deriveAddr |
deriveAddrs |
| BIP-49 | P2WPKH nested in P2SH | m/49'/0'/0' |
deriveCompatWitnessAddr |
deriveCompatWitnessAddrs |
| BIP-84 | P2WPKH | m/84'/0'/0' |
deriveWitnessAddr |
deriveWitnessAddrs |
| BIP-86 | Taproot | m/86'/0'/0' |
See Taproot |
xPrvExport and xPubExport write the standard xprv and xpub forms.
xPrvImport and xPubImport read them back. The ypub and zpub prefixes are
not supported.
parsePath reads a path at run time. It returns a ParsedPath, which
applyPath applies to a private or a public key.
Addresses
let text = "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
print (textToAddr btc text)
-- => Just (WitnessPubKeyAddress {hash160 = "c0cebcd6c3d3ca8c75dc5ec62ebe55330ef910e2"})
-- An address of another network is refused.
print (textToAddr bch text)
-- => Nothing
-- The output script an address pays to.
print (encodeHex <$> (addressToScriptBS ctx =<< textToAddr btc text))
-- => Just "0014c0cebcd6c3d3ca8c75dc5ec62ebe55330ef910e2"
textToAddr and addrToText handle every address format of a network.
addrToBase58, addrToBech32 and addrToCash each write one chosen format.
Scripts
ScriptOutput is a standard output script. Its constructors are PayPK,
PayPKHash, PayMulSig, PayScriptHash, PayWitnessPKHash,
PayWitnessScriptHash, PayWitness, PayScript32Hash and DataCarrier.
let keyA = wrapPubKey True "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
keyB = wrapPubKey True "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f"
-- BIP-67 sorts the keys, so that all signers build the same script.
multisig = sortMulSig ctx (PayMulSig [keyA, keyB] 2)
-- The address can fail, because a P2SH redeem script holds at most 520 bytes.
print (addrToText btc <$> payToScriptAddress ctx multisig)
-- => Right (Just "39bgKC7RFbpoCRbtD5KEdkYKtNyhpsNa3Z")
-- OP_RETURN is 6a, then the push of 5 bytes, then "hello".
print (encodeHex (marshal ctx (DataCarrier ["hello"])))
-- => "6a0568656c6c6f"
-- Read the same bytes back.
script <- maybe (fail "bad hex") pure (decodeHex "6a0568656c6c6f")
print (unmarshal ctx script :: Either String ScriptOutput)
-- => Right (DataCarrier {pushes = ["hello"]})
Decoding a ScriptOutput fails for a script that matches no template. A Script
holds any script, as a list of ScriptOp values, so use that type for the others.
encodeOutput and decodeOutput convert between ScriptOutput and Script.
payToWitnessScriptAddress and payToNestedScriptAddress give the P2WSH and
P2SH-P2WSH addresses of a script.
The three functions return Either. A P2SH redeem script holds at most 520
bytes, which is a multisig of 15 compressed keys. Coins sent to a longer script
could never be spent, so payToScriptAddress refuses it. A P2WSH script holds up
to 10,000 bytes, so a multisig of 16 to 20 keys needs P2WSH.
Building and signing a transaction
-- The key at m/84'/0'/0'/0/0 and its address.
let key = derivePath ctx ("m/84'/0'/0'/0/0" :: DerivPath) master
address = xPubWitnessAddr ctx (deriveXPubKey ctx key)
-- The coin to spend, and the transaction that paid it. Both are made up.
-- The coin holds 100,000 satoshi.
coinScript <- maybe (fail "no output script") pure (addressToOutput address)
let amount = 100000
fundingInput = TxIn (OutPoint "1111111111111111111111111111111111111111111111111111111111111111" 0) "" 0xffffffff
funding = Tx 2 [fundingInput] [TxOut amount (marshal ctx coinScript)] [] 0
coin = OutPoint (txHash funding) 0
-- Pay 99,000 satoshi. The 1,000 satoshi left over are the fee.
unsigned <-
either fail pure $
buildAddrTx btc ctx [coin] [("bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", 99000)]
-- SigInput describes the coin. The last argument is the redeem script.
signed <-
either fail pure $
signTx btc ctx unsigned [SigInput coinScript amount coin sigHashAll Nothing] [key.key]
print (verifyStdTx btc ctx signed [(coinScript, amount, coin)])
-- => True
print (txVirtualSize signed)
-- => 110
print (txHash signed)
-- => "9a079e6b7939909415b7b027a19e093bc539dac5a2ed50df8beb0942ec821848"
-- This hexadecimal form is what you broadcast.
print (encodeHex (runPutS (serialize signed)))
-- => "01000000000101..."
buildAddrTx adds no change output. Whatever the outputs do not spend goes to the
miners as a fee, so list your change output along with the payments.
Fill in SigInput and pick the signing function by the type of the coin:
| Coin | script |
redeem |
Function |
|---|---|---|---|
| P2PKH | PayPKHash |
Nothing |
signTx |
| P2SH multisig | PayScriptHash |
Just (PayMulSig ks m) |
signTx |
| P2WPKH | PayWitnessPKHash |
Nothing |
signTx |
| P2WSH | PayWitnessScriptHash |
Just script |
signTx |
| P2SH-P2WPKH | PayWitnessPKHash |
Nothing |
signNestedWitnessTx |
| P2SH-P2WSH | PayWitnessScriptHash |
Just script |
signNestedWitnessTx |
| Taproot key path | PayWitness 1 key |
Nothing |
signTx |
For multisig, each signer can call signTx with their own keys. mergeTxs
combines the results for legacy inputs. For SegWit multisig, use a PSBT.
Coin selection and fees
Make your coin type an instance of Coin. The library then picks coins and
estimates the fee from the kinds of the inputs and outputs. This example is a
complete program.
{-# LANGUAGE OverloadedRecordDot #-}
module Main (main) where
import Data.Word (Word64)
import Haskoin
newtype Utxo = Utxo {amount :: Word64}
instance Coin Utxo where
coinValue utxo = utxo.amount
coinInputKind _ = InputWitnessPKHash
main :: IO ()
main = do
let outputs = [OutputWitnessPKHash, OutputWitnessPKHash]
coins = [Utxo 50000, Utxo 120000, Utxo 300000]
target = 150000
feeRate = 5
-- The size is in virtual bytes, and the fee rate in satoshi per virtual byte.
print (guessTxSize [InputWitnessPKHash] outputs)
-- => 141
print (guessTxFee feeRate [InputWitnessPKHash] outputs)
-- => Right 705
-- The result is the chosen coins and the change to send back to yourself.
-- True keeps searching the rest of the list for a smaller solution.
case chooseCoins target feeRate outputs True coins of
Left err -> putStrLn err
Right (chosen, change) -> print (map (.amount) chosen, change)
-- => ([120000,50000],18955)
The change here is 170,000 - 150,000 - 1,045, where 1,045 is the fee for two inputs and two outputs.
chooseCoins takes the coins in the order of the list, so sort them the way you
prefer first. The output list must include the change output.
The change can be smaller than the dustThreshold of its output kind, down to
one satoshi. Nodes do not relay such an output, so leave that change to the fee.
chooseCoins refuses a selection above maxStandardTxWeight, which nodes do not
relay either.
The estimates are upper bounds, because they count 72 bytes per signature. The
kinds include InputPKHash, InputWitnessPKHash and InputTaproot. See
InputKind and OutputKind in Haskoin.Transaction.Builder for all of them.
PSBT
Use a PSBT when several parties or devices sign a transaction. This example continues the one in Building and signing.
-- Continues the example above.
import qualified Haskoin.Transaction.Psbt as Psbt
-- Creator: start from the unsigned transaction.
let created = psbtOf unsigned
-- Updater: attach the transaction that paid each coin. The signer checks the
-- amount of the coin against it.
updated = created {Psbt.inputs = map (insertNonWitnessUtxo funding) created.inputs}
-- This base64 text is what you pass to the signers.
let shared = encodePsbtBase64 updated
-- Signer: read the text, and sign every input the keys can sign.
received <- either fail pure (decodePsbtBase64 shared)
signedPsbt <- either fail pure (signPsbt btc ctx (keySigner ctx key.key) received)
-- Finalizer and extractor: check the signatures and build the transaction.
finalized <- either fail pure (finalizePsbt btc ctx signedPsbt)
extracted <- either fail pure (psbtTransaction finalized)
-- RFC 6979 signatures are deterministic, so this is the same transaction.
print (extracted == signed)
-- => True
Some other useful functions:
xPrvSignersigns from an extended private key, following the derivation records of the inputs.combinePsbtmerges the copies that different signers return.insertWitnessUtxoattaches a coin without the transaction that paid it. That is enough for a Taproot coin. For a SegWit version 0 coin, the amount is then a claim that the signature does not cover for the other inputs, and a signer can be led to burn coins as fee (CVE-2020-14199).keySignerandxPrvSignertherefore refuse such a coin. SetrequirePreviousTx = Falseon theSignerto accept it.psbtFeeandinputCoinsgive the fee and the coins as the signer checks them. Show those to the user, not amounts read from the fields yourself.psbtTransactionrefuses a PSBT whose inputs are not all final.psbtPartialTransactiongives the transaction of a PSBT that is still being signed.insertInputRedeemScriptandinsertInputWitnessScriptattach the scripts of P2SH and P2WSH coins.withVersion Version2converts a PSBT to version 2.addInputandaddOutputthen extend it.
Taproot
let key = derivePath ctx ("m/86'/0'/0'/0/0" :: DerivPath) master
internalKey = (deriveXPubKey ctx key).key
-- Nothing means that the output has no script tree.
output = taprootScriptOutput ctx (TaprootOutput internalKey Nothing)
print (addrToText btc =<< outputAddress btc ctx output)
-- => Just "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"
-- Spend a made-up coin of 100,000 satoshi by its key path.
let coin = OutPoint "1111111111111111111111111111111111111111111111111111111111111111" 0
unsigned = buildTx ctx [coin] [(output, 99000)]
signed <-
either fail pure $
signTx btc ctx unsigned [SigInput output 100000 coin sigHashDefault Nothing] [key.key]
print (verifyStdTx btc ctx signed [(output, 100000, coin)])
-- => True
The signature commits to every coin the transaction spends. signTx therefore
needs a SigInput for every input of a Taproot transaction.
To commit to scripts, pass a MAST in place of Nothing. signTx only signs key
path spends. signPsbt also signs a script path when the leaf is a single key
check, <key> OP_CHECKSIG. For other scripts, build the witness yourself with
taprootSigHash and encodeTaprootWitness.
checkTapTree tells whether every leaf of a tree can be spent. A leaf can be at
most 128 levels deep, and its version must be one that BIP 341 allows.
SLIP-39 secret sharing
SLIP-39 splits a
secret into mnemonic shares, and a threshold of shares rebuilds it. It is
available from version 1.6.0. The Haskoin module does not re-export it, so
import it yourself.
import qualified Haskoin.Crypto.Keys.Slip39 as Slip39
-- One group of 3 members, of which 2 rebuild the secret.
let scheme = Slip39.Scheme 1 [(2, 3)]
-- A secret has at least 16 bytes, and an even number of them.
-- Use random bytes in practice.
secret = "0123456789abcdef"
-- True makes the shares extendable. 1 sets the cost of the encryption.
groups <- either fail pure =<< Slip39.generateMnemonics scheme True 1 "passphrase" secret
let shares = concat groups
print (length shares)
-- => 3
print (Slip39.combineMnemonics "passphrase" (take 2 shares))
-- => Right "0123456789abcdef"
print (Slip39.combineMnemonics "passphrase" (take 1 shares))
-- => Left "Group 0 has 1 share, but its member threshold is 2"
A wrong passphrase is not an error. It returns a different secret, as the specification requires. The passphrase must be printable ASCII, which the specification requires too. A SLIP-39 share is not a BIP-39 mnemonic, and one cannot be converted into the other.
Scheme can also describe two levels. For example, Scheme 2 [(1, 1), (2, 3), (3, 5)] needs two of the three groups, each with its own member threshold.
Bitcoin Cash
Pass bch wherever the examples above pass btc. A few things differ.
-- The BIP-44 coin type of Bitcoin Cash is 145.
let key = derivePath ctx ("m/44'/145'/0'/0/0" :: DerivPath) master
address = xPubAddr ctx (deriveXPubKey ctx key)
-- addrToText writes CashAddr on Bitcoin Cash.
print (addrToText bch address)
-- => Just "bitcoincash:qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6"
print (addrToBase58 bch address)
-- => Just "1mW6fDEMjKrDHvLvoEsaeLxSCzZBf3Bfg"
-- textToAddr reads both forms.
print ((== Just address) (textToAddr bch "1mW6fDEMjKrDHvLvoEsaeLxSCzZBf3Bfg"))
-- => True
-- SegWit addresses do not exist on Bitcoin Cash.
print (addrToText bch (xPubWitnessAddr ctx (deriveXPubKey ctx key)))
-- => Nothing
signTx sets the fork id flag of the signature hash when the network needs it.
Pass sigHashAll as usual.
Blocks and headers
print (headerHash btc.genesisHeader)
-- => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
print (isValidPOW btc btc.genesisHeader)
-- => True
-- 3.125 BTC since the halving at this height.
print (computeSubsidy btc 840000)
-- => 312500000
connectBlocks validates headers and adds them to a chain. This covers proof of
work, difficulty, timestamps, versions and checkpoints. It runs in any monad with
a BlockHeaders instance. StateT HeaderMemory is the in-memory one. Write your
own instance to keep headers in a database.
Once the best block has the minimumChainWork of the network, a fork with less
work is refused. Below that work nothing bounds the side chains a peer can send,
so a node that is still syncing should not keep headers from a peer it does not
trust.
This example needs the mtl package.
import Control.Monad.State.Strict (runStateT)
-- Mine three regtest headers on top of the genesis block.
let net = btcRegTest
headers = appendBlocks net 7 net.genesisHeader 3
now = net.genesisHeader.timestamp + 3600
(result, chain) <- runStateT (connectBlocks net now headers) (initialChain net)
nodes <- either fail pure result
-- The nodes come back with the highest one first.
print (map (.height) nodes)
-- => [3,2,1]
print chain.best.height
-- => 3
buildMerkleRoot and buildPartialMerkle compute Merkle trees.
extractMatches checks a partial tree from a merkleblock message against its
Merkle root. The header and the number of transactions are claims of the sender,
which the caller must check.
Peer-to-peer messages
The library encodes and decodes messages. Moving the bytes is up to you.
import Data.Bytes.Get (runGetS)
let bytes = runPutS (putMessage btc MVerAck)
print (encodeHex bytes)
-- => "f9beb4d976657261636b000000000000000000005df6e0e2"
print (runGetS (getMessage btc) bytes)
-- => Right MVerAck
getMessage checks the network magic and the checksum, and bounds the payload by
the maxPayloadSize of its command. Read the 24-byte MessageHeader first to
learn how many more bytes to take from the socket, and refuse a length above
maxPayloadSize net command before you read them.
Message has a constructor for each message type. bloomCreate, bloomInsert
and bloomContains build BIP-37 filters for filterload.
Module overview
import Haskoin brings in everything below except the two modules marked as
separate. It exports many short names, so you may prefer to import only the
modules you need, or to import it qualified.
| Module | Contents |
|---|---|
Haskoin.Address |
Address types and Base58, Bech32 and CashAddr encoding |
Haskoin.Block |
Block types, header chain, validation, difficulty and Merkle trees |
Haskoin.Crypto |
Hashes, keys, signatures and the Crypto.Secp256k1 API |
Haskoin.Crypto.Keys.Slip39 |
SLIP-39 secret sharing (separate import) |
Haskoin.Network |
Network constants, P2P messages, Bloom filters |
Haskoin.Script |
Scripts, standard templates and signature hashes |
Haskoin.Transaction |
Transaction types, builder, signing, SegWit, Taproot and PSBT |
Haskoin.Util |
Hexadecimal helpers, Marshal and small utilities |
Haskoin.Util.Arbitrary |
QuickCheck generators such as arbitraryTx (separate import) |
Security notes
Bugs in code that handles keys can lose money. Try your program on regtest or a test network before you use it on mainnet.
- Randomness. Create keys and mnemonics with a cryptographic source, such as
getEntropyfrom the entropy package. Never useSystem.Random. - Secrets in logs.
PrivateKeyandXPrvKeyshow as<redacted>and have noReadinstance.toWifandxPrvExportwrite a key out on purpose. A bareSecKey, such as thekeyfield of aPrivateKey, still shows its bytes. The errors offromMnemonicandmnemonicToSeedname the position of a bad word, never the word. - Secrets in memory. Keys live in the GHC heap. The library cannot wipe them, and the garbage collector can copy them.
- Review before you sign.
signPsbtchecks that a PSBT is sound, but not that it pays who you expect. Show the outputs andpsbtFeeto the user first. By default the signer wants the previous transaction of every SegWit version 0 coin, because an amount in a witness output alone is a claim. - Signature hash types. By default
signPsbtsigns onlySIGHASH_ALLandSIGHASH_DEFAULT, because the other types leave part of the transaction unsigned. To accept others, setacceptedSigHasheson theSigner. - Verification.
verifyStdTxchecks the signatures of the standard templates by the rules that nodes relay by, and that outputs do not exceed inputs. It is not a script interpreter and does not apply every consensus rule. - Untrusted text. The cost of
decodeBase58anddecodeBase58Checkgrows with the square of the input length. Limit the length of input you pass to them directly. The address and key decoders of the library already do.
Development
git clone https://github.com/jprupp/haskoin-core.git
cd haskoin-core
stack build # build the library
stack test # run all the tests
stack test --test-arguments='--match "Slip39"' # run the tests of one module
stack haddock --no-haddock-deps # build the API documentation
cabal build and cabal test work too. On NixOS, or with stack --nix, the
nix section of stack.yaml provides libsecp256k1.
package.yaml is the source of the package description. Change it rather than
haskoin-core.cabal, which hpack generates. Stack
regenerates the file whenever it builds.
The tests are in test/ and use hspec. The test
vectors from the BIPs, Bitcoin Core and other implementations are in data/.
Contributing
Pull requests are welcome.
- Format the code with Ormolu. The script scripts/format does this for all the sources.
- Add tests for the behaviour you change.
- Describe the change under
[Unreleased]inCHANGELOG.md. The file follows Keep a Changelog, and versions follow Semantic Versioning.
Related projects
- haskoin-node connects to peers and syncs headers and blocks.
- haskoin-store indexes the block chain and serves it over a REST API.
- secp256k1-haskell is the binding to libsecp256k1 that this library uses.
License
Haskoin Core is released under the MIT License.
Changes
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[1.6.0] - 2026-11-06
Added
Haskoin.Crypto.Keys.Slip39: Shamir’s secret-sharing for mnemonic codes, SLIP-0039, as a standalone module.generateMnemonicssplits a master secret, encrypted with a passphrase, into share mnemonics under a two-level scheme of groups and members, andcombineMnemonicsrebuilds it from a threshold of them.generateSharesandgenerateMnemonicsdraw their random bytes from the operating system, throughgetEntropyof the entropy package;generateSharesFromtakes the source of random bytes as an argument, andgenerateSharesWiththe identifier and the random values themselves, so that a split can be reproduced.encodeShareanddecodeShareread and write one share, with its checksum, padding and ranges checked as the specification requires; a share has at most 1023 words, the length the checksum covers, which is a value of 1270 bytes.encryptMasterSecretanddecryptMasterSecretare the passphrase step, and both refuse a passphrase outside printable ASCII, as the specification requires;decryptMasterSecretBytestakes the passphrase as unchecked bytes, to recover shares of software that allowed other characters.splitSecret,recoverSecretandinterpolateare the sharing over GF(256), andrs1024Checksumthe checksum. No error text and noShowinstance holds a word or a value of a share: an unknown word is named by its position. The 45 vectors of the reference implementation, a share set of Sparrow’s library, and split cases with fixed random values made by an independent Python implementation of the specification,test/Haskoin/Crypto/Keys/slip39_verify.py, check it. The word list isdata/slip39_wordlist.txt, the file of the specification, and its SHA-256 is checked.tryDerivePathandtryDerivePubPathinHaskoin.Crypto.Keys.Extendedderive along a path and return an error value when the path cannot be followed. Use them for a path that comes from outside, such as the path of a PSBT record.derivePathandderivePubPaththrowDerivationExceptionin that case, as before.maxRedeemScriptSize(520) andmaxWitnessScriptSize(10,000) inHaskoin.Script.Standardare the largest scripts that a pay-to-script-hash and a pay-to-witness-script-hash output can commit to and still be spent:MAX_SCRIPT_ELEMENT_SIZEandMAX_SCRIPT_SIZEof Bitcoin Core.p2shScriptHashandp2wshScriptHashgive the hash of a script for those outputs, and fail above the limit.p2shP2wpkhAddrinHaskoin.Addressis the P2SH-P2WPKH address of a public key hash.SigHashFlaghas aBoundedinstance.decodeTxinHaskoin.Transaction.Commonreads a transaction from bytes that hold the transaction and nothing else, asDecodeHexTxrequires in Bitcoin Core.deserializereads a transaction from the front of a longer input, as a block needs it to, and does not notice bytes that follow. UsedecodeTxfor a transaction that arrives on its own.dustThresholdinHaskoin.Transaction.Buildergives the amount below which nodes do not relay an output of a kind, asGetDustThresholdcomputes it in Bitcoin Core at the default dust relay fee: 546 satoshi for pay-to-public-key-hash, 294 for pay-to-witness-public-key-hash, 330 for pay-to-witness-script-hash and Taproot.chooseCoinscan return a change below it, and its documentation now says to leave such a change to the fee.maxStandardTxWeight(400,000) inHaskoin.Transaction.Builderis the weight above which nodes do not relay a transaction:MAX_STANDARD_TX_WEIGHTof Bitcoin Core, and the 100,000 bytes ofMAX_STANDARD_TX_SIZEof Bitcoin Cash Node.data/core_signed_txs.jsonholds 97 transactions that Bitcoin Core 32.1 signed on regtest, one for each of 16 kinds of coin and each hash type. The tests verify them withverifyStdTx, so that the verifier is checked against signatures the library did not make.arbitraryKeyPairCgenerates a key pair in compressed form.maxPayloadSizeinHaskoin.Network.Messagegives the largest payload a message of a command may have on a network, andgetMessageapplies it. On Bitcoin Cash every message butblock,cmpctblockandblocktxnis limited to 2 MiB, the newmaxProtocolMessageSizeofNetwork, asCMessageHeader::IsOversizeddoes in Bitcoin Cash Node. The limit was 64 MB for every message, and one 33.5 MBgetblocktxntook 19 seconds and 5.7 GiB to parse. A node should usemaxPayloadSizeon the message header before it buffers a payload.getCompactBlock,getGetBlockTxnandgetBlockTxninHaskoin.Network.CompactBlockread the compact block messages of a network. On Bitcoin a transaction position above 65535, the newmaxCompactBlockIndexofNetwork, fails the parse, and so does a compact block of more than 65535 transactions, as in BIP 152 and Bitcoin Core. Bitcoin Cash keeps 32 bits, as Bitcoin Cash Node.getMessageuses them.getNetworkTxinHaskoin.Network.Datareads a transaction as a peer of the network sends it: without the SegWit format on a network that has no SegWit.getMessageuses it fortx,block,blocktxnandcmpctblock.minimumChainWorkinNetworkis thenMinimumChainWorkof Bitcoin Core 32.1 and of Bitcoin Cash Node. Once the best block has that work,connectBlockandconnectBlocksrefuse a new header whose chain has less, which bounds cheap forks from far below the tip. A node that is still syncing below that work is not protected by this.getVarIntListMax, and the limitsmaxInvSize(50000),maxAddrSize(1000),maxHeadersSize(2000),maxLocatorSize(101) andmaxFilterHashes(2000) inHaskoin.Network.Common. The parsers ofinv,getdata,addr,addrv2,headers,getheaders,getblocksandcfheadersfail above them, as soon as the count is read.extractIndexedMatchesandmerkleBlockIndexedTxsinHaskoin.Block.Merklegive the position of each matching transaction of a partial Merkle tree.maxMerkleFlagBitsis the number of flag bits a valid proof can use.leafHashBytesinHaskoin.Transaction.Taprootis the tapleaf hash of a script given as bytes, for a script that is not a list of opcodes.checkTapTreetells whether every leaf of a script tree can be spent: it fails for a node deeper than 128, which no control block can prove, and for a leaf version that is odd or is0x50. Run it on a tree before you give out its address.scriptPathScriptBytesgives the script bytes of a script path spend as they are in the witness.taprootSignaturebuilds aTaprootSignatureand fails for a hash type that Taproot does not define.data/taproot_mainnet_spends.jsonholds 24 Taproot script path spends from Bitcoin mainnet blocks 840000 and 900000, with scripts of 34 to 635 bytes. The tests check that each one is read and that its script and control block commit to the output key of the coin it spends.requirePreviousTxon a PSBTSigner. With it, the signer refuses a SegWit version 0 coin that is known from its witness output only, since the amount of such a coin is a claim of whoever wrote the PSBT (CVE-2020-14199).keySignerandxPrvSignerset it.inputCoinsandpsbtFee: the coin of each PSBT input with whether it comes from the previous transaction, and the fee when every coin does.psbtPartialTransaction: the transaction of a PSBT that is still being signed.data/psbt_complex_core32.json: nine coins of every kind, signed by Bitcoin Core 32.1 without finalizing.
Changed
unmarshalandunmarshalLazyfail when bytes are left over after the value. They used to read a value from the start of the input and ignore the rest, so two different byte strings could read as the same public key, signature or extended key. Code that reads a value from the start of a longer stream must runmarshalGetitself.- The binary form of
XPubKeyholds a compressed public key in exactly 33 bytes, as BIP 32 defines it and asxPubImportalready required. A key in the 65-byte form used to be read. decodeStrictSig, and with it the binary and JSON forms ofSig, apply the encoding rule of BIP 66 and nothing more. A signature with a high S value is now read. Low S is a relay rule of Bitcoin Core (SCRIPT_VERIFY_LOW_S), not a consensus rule, and blocks hold signatures with a high S.isCanonicalHalfOrdertests the relay rule. The form of a transaction signature,TxSignature, still applies it. The signers make low-S signatures, as before.makeXPrvKeythrowsDerivationExceptionfor a seed that is not 16 to 64 bytes long, the range that BIP 32 gives and that Bitcoin Core enforces. A seed of any length used to give a master key, the empty string included, and anyone can compute the keys of such a wallet. A wallet that was made from a seed of another length can still be opened without this function: its master key isXPrvKey 0 "00000000" 0 chain key, wherekeyandchainare the two halves ofhmac512 "Bitcoin seed" seed.applyPathreturnsLeftfor a path that leads below depth 255, which no key can have. It used to throwDerivationException, although its type promises an error value.- The path constructors
:|and:/throwDerivationExceptionfor an index of 2^31 or more. Such a path could be built before, but its text and binary forms did not read back to the same path, and deriving along it threw. The two constructors are now pattern synonyms. Code that builds and matches paths with them compiles as before. fromMiniKeyrequires a mini key to start with the letterSand to hold only characters of the Base58 alphabet, as the format defines. One arbitrary string of 22 or 30 characters in 256 used to be read as a private key.getIntegerreads only the encoding thatputIntegerwrites: a tag byte of 0 or 1, a sign byte of 0x01 or 0xff, and the shortest bytes that hold the number. Several other byte strings used to read as the same number. Data written byputIntegerreads as before. The reader also takes close to linear time now. A number of 400 KB used to take 54 seconds.getIntMaprefuses keys that are not in ascending order, which is howputIntMapwrites them. A key given twice used to be read, and its second element silently replaced the first.convertBitsreturnsNothingfor a width that is zero or negative, where it used to loop forever, and for a digit that does not fit the width it is converted from, whose high bits it used to drop.bech32EncodereturnsNothingfor a five-bit word above 31. It used to raise an array index error.- The JSON parser of
Signo longer copies the text it refuses into its error message. data/key_io_valid.jsonanddata/key_io_invalid.jsonare the files of Bitcoin Core 32.1, with its Bech32m and testnet4 vectors. The tests run every vector throughtextToAddrandfromWif, askey_io_tests.cppdoes.payToScriptAddress,payToWitnessScriptAddressandpayToNestedScriptAddressreturnEither String Address, andtoP2SHandtoP2WSHreturnEither String ScriptOutput. They fail for a redeem script above 520 bytes and for a witness script above 10,000 bytes. A pay-to-script-hash redeem script is a push of the spending input, and consensus limits a push to 520 bytes, so coins sent to the hash of a longer script can never be spent.payToScriptAddressused to give an address for a multisig of 16 to 20 compressed keys (547 to 684 bytes), or of 8 or more uncompressed keys, and Bitcoin Core rejects every spend of such a coin with “Push value size limit exceeded”. Fifteen compressed keys fit, and twenty still fit a pay-to-witness-script-hash output. These functions also return the failure ofencodeOutputfor a multisig that no script can express, wherepayToScriptAddressused to raise anerrorcall.deriveMSAddrandderivePathMSAddrreturnEither String (Address, RedeemScript), andderiveMSAddrsandderivePathMSAddrsreturnEither Stringof their list. They fail for more than 15 keys, whose address would be unspendable as described above, and for a number of required signatures that is not between 1 and the number of keys, which used to raise anerrorcall when the address was forced. Coins that were sent to an address these functions gave for 16 to 20 keys are lost: no spend of them is valid.scriptToAddress,scriptToAddressBSandoutputAddresstake theNetworkas their first argument and give an address only for an output kind that the network has. A witness program has no address on Bitcoin Cash, and a 256-bit script hash (OP_HASH256 <32 bytes> OP_EQUAL) has none on Bitcoin, where Bitcoin Core reports that script as non-standard. Without the network these functions returned an address that has no text form on the chain it came from, and the JSON encoder ofAddressthrows for such an address. One such output is in Bitcoin block 211997 (transactiona4bfa8ab6435ae5f25dae9d89e4eb67dfa94283ca751f393c1ddc5a837bbc31b, output 0), so an indexer that encoded the addresses of outputs failed on that transaction. Every address these functions return now has a text form on the network.decodeInputonly reads a pay-to-script-hash input whose redeem script is in the formencodeOutputwrites. A multisig redeem script whose keys are pushed withOP_PUSHDATA1used to decode, andinputAddressthen gave the hash of the re-encoded script, an address that never held the coin. Such an input now fails to decode, soinputAddressis always the hash of the redeem script bytes of the input, and encoding a decoded input never rewrites its redeem script. Spending such a script breaks theMINIMALDATArule of the relay policy, so standard transactions are not affected.decodeInputreads aNestedWitnessInputonly when the witness program is a direct push, as BIP 141 requires (“the scriptSig must be exactly a push of the BIP16 redeemScript”). A program pushed withOP_PUSHDATA1used to decode, although such an input is invalid by consensus.inputAddressgives no address for a redeem script above 520 bytes.txSigHashForkIdwrites the hash type as four bytes on a network without a fork id, asSignatureHashof Bitcoin Core does for a SegWit input. It used to keep the low byte only, whiletxSigHashwrote all four. Nothing changes for a hash type that fits in one byte, which is every hash type a signature can carry.cashAddrDecode, and with ittextToAddrand the JSON parser ofAddresson Bitcoin Cash networks, refuse a string longer than the prefix of the network, the colon and 112 characters before any other work. No valid address is longer, since the largest hash of the format is 64 bytes. A string of 1 MB used to take 1.5 seconds and several hundred megabytes of memory, and now takes less than a millisecond.cash32decodeandcash32decodeTyperefuse a prefix that is not made of letters only, as the decoder of Bitcoin Cash Node does. A digit or a second colon in the prefix used to be read.cash32encodereturnsMaybe Cash32. It fails for a prefix that is empty or not made of ASCII letters, and it writes an uppercase prefix in lowercase. It used to write strings thatcash32decoderefuses: with an empty prefix, a digit, a colon, or a prefix of another case than the data.- The
Readinstance ofAddressgoes throughwitnessAddressandcashAddressfor the two generic kinds. The derived instance read a witness version above 16, a program of any length and a CashAddr type above 15, which no other function builds. enumFrom,enumFromTo,enumFromThen,enumFromThenTo,succandpredofSigHashFlagwalk the five flags in the order of their constructors.[SIGHASH_ALL ..]andsucc SIGHASH_SINGLEused to raise “Not a valid sighash flag”, because the values of the flags are not consecutive.- The generators
arbitraryMulSigSHInput,arbitraryMulSigSHInputC,arbitraryMulSigSHInputFullandarbitraryMulSigSHInputFullCdraw a redeem script of at most 520 bytes. More than half of the inputs they drew could not exist.arbitraryP2SHMSParam,arbitraryP2SHMSParamC,arbitraryP2SHMSOutputandarbitraryP2SHMSOutputCare the new generators behind them.arbitrarySHSigInputandarbitraryPartialTxsuse at most 7 keys for the same reason. arbitraryScriptOutputdraws a 256-bit script hash output on networks that have CashAddr, andarbitraryScriptInputdraws a nested witness input only on networks that have SegWit.arbitraryBitcoinAddressdraws a Taproot address one time in six, through the newarbitraryTaprootAddress, andarbitraryBitcoinCashAddressa payable token-aware address one time in five, through the newarbitraryTokenAddress.makeSignature, and with itsignTx,signInput,signNestedWitnessTxandsignPsbt, refuse a hash type that no node accepts, with the reason: one that is notSIGHASH_ALL,SIGHASH_NONEorSIGHASH_SINGLEwith or withoutSIGHASH_ANYONECANPAY(the test ofIsDefinedHashtypeSignaturein Bitcoin Core), the fork id flag on a network without a fork id, and a value that does not fit in the one byte a signature carries. On Bitcoin Cash this refusesSIGHASH_UTXOS(0x20), whose digest the library does not compute. The types 0x00, 0x04, 0x21, 0x41 and 0x80 used to be signed, and the result was a transaction that nodes do not relay. A type with upper bytes set, such as 0x101, gave a signature that does not verify.signTxstill signsSIGHASH_NONEandSIGHASH_SINGLEwhen theSigInputasks for them: check the hash type of aSigInputthat comes from someone else.makeSignaturerefuses a pay-to-script-hash coin without its redeem script, with a redeem script above 520 bytes, or with one that does not hash to the coin, and the same for a pay-to-witness-script-hash coin and its witness script, above 10,000 bytes. It used to sign whatever script it was given, and the output script itself when it was given none. Such a signature spends nothing.makeSignaturerefuses a SegWit coin, andsignTxa Taproot coin, on a network without SegWit.signTx bchused to return a transaction with a witness, which no Bitcoin Cash node reads.signTx,signNestedWitnessTxandsigKeysrefuse to sign a SegWit version 0 coin with an uncompressed key, whether the coin pays to the hash of the key or to a witness script that holds it. Nodes do not relay such a spend (SCRIPT_VERIFY_WITNESS_PUBKEYTYPEin Bitcoin Core), so the transaction they returned could not be broadcast. A block may still hold the spend. To make it on purpose, callsignInputwith thePrivateKeyin its uncompressed form.signInputandsignNestedInputrefuse a key that is not a key of the coin: the key of a pay-to-public-key coin, the key whose hash the coin pays to, or a key of its multisig. They used to return an input that no node accepts.signTaprootInputrefuses a secret key whose x-only public key is not the output key of the coin, and a spent output that is not a Taproot output. Passing the internal key in place of its tweak used to give a signature that does not verify.verifyStdInputandverifyStdTxrequire a multisig input to hold exactly as many signatures as its script requires, and no other item.OP_CHECKMULTISIGtakes that many items as signatures, and the item below them must be empty (BIP 147), so no block can contain an input with an item after its signatures or with an empty item among them. They used to answerTruefor both, in bare, pay-to-script-hash and pay-to-witness-script-hash form. An empty item before the signatures is refused too: nodes do not relay it, and in a witness it is invalid.verifyStdTxrefuses a transaction in which two inputs spend the same outpoint, asCheckTransactiondoes in Bitcoin Core (bad-txns-inputs-duplicate).verifyStdInputandverifyStdTxrefuse a SegWit or Taproot input on a network without SegWit, and compare the input script of a nested SegWit input with the push of its program byte for byte.verifyStdTxlooks up the coins once for the whole transaction. It used to walk the list of coins for every input, and to rebuild the list of spent outputs for every Taproot input, so its time grew with the cube of the number of Taproot inputs: 400 of them took 1.6 s and now take 0.14 s.signTxderives the public keys of its secret keys once for the whole transaction, not once per input. 400 Taproot inputs with 400 keys took 11.7 s and now take 0.2 s.guessTxFeereturnsEither String Word64. It fails when the fee does not fit in 64 bits, where it used to wrap around: 192 bytes at 2^58 satoshi per byte gave a fee of 0. It also fails for a kind of input or output that cannot exist, such asInputMulSig 16 16, whose redeem script would be 547 bytes, or a multisig of 3 of 2.chooseCoinsandchooseCoinsSinkadd amounts as unbounded integers. A target near 2^64 used to wrap around with its fee, so that a coin of 1,000 satoshi “covered” a target of 2^64 - 101. They fail for a kind of input or output that cannot exist, and they stop with an error when the transaction would weigh more thanmaxStandardTxWeight. They keep the sums of the selection as coins are added: selecting 1,400 coins took 0.11 s and now takes under a millisecond.greedyAddSinkadds amounts as unbounded integers as well.chooseCoinsSinkno longer uses it.mergeTxsandmergeTxInputkeep the witness of the transaction. They used to remove it, so a SegWit input that was signed before the multisig signers took their copies lost its signature.mergeTxsnow answers “Transactions do not match” for copies whose witnesses differ, since it does not merge witnesses.mergeTxInputreturnsLeftfor an input index that one of the transactions does not have. It used to raise an error.hasWitnesslooks only at the stacks of the inputs. A witness item in a stack beyond the inputs used to turn the witness format on, which gave bytes with the marker and no witness item, a wrong weight and a wrong witness transaction id. Neither Bitcoin Core nor this library reads those bytes.- The JSON form of
Txrequires one witness stack per input, or none. buildAddrTxrefuses an amount above themaxSatoshiof the network, ascreaterawtransactionrefuses an amount out of range in Bitcoin Core.- The generators of
Haskoin.Util.Arbitrary.Transactiondraw compressed keys for SegWit signing data, a pay-to-script-hash multisigInputKindof at most 15 keys, SegWit and Taproot signing data only on networks that have SegWit, and amounts of which a third are below 10,000 satoshi. fromMnemonicandmnemonicToSeedno longer put words of the sentence or checksum values into their error text. An unknown word is named by its position, as infromMnemonic: words 3 and 7 are not in the list, and a failed checksum isfromMnemonic: checksum failed. A sentence typed in capitals used to be echoed whole, and the text of aLeftends up in logs.fromMnemonicandmnemonicToSeeddo a bounded amount of work on text from an untrusted source. The sentence is split into words before anything is normalised, and a word longer than the eight letters of the longest list word is refused as it is.mnemonicToSeedrefuses a passphrase whose NFKD form would hold more than 30 combining characters in a row, the limit of the Stream-Safe Text Format of Unicode (UAX #15), which no real text passes. 32 KB of combining characters used to take half a minute, and the time grew with the square of the length. Every sentence and passphrase that was accepted before, other than such a passphrase, gives the same seed. This adds a direct dependency onunicode-data, whichunicode-transformsalready used.- The parser of
MerkleBlockrefuses more hashes than transactions and more flag bytes than the tree can use, before it reads them. Amerkleblockmessage used to cost about 95 bytes of memory per flag byte, 1.5 GB for a 16 MB message. Proofs that verify are not affected. - An empty witness item, and any empty byte string of the wire format, costs one list cell. A
txmessage with four million empty witness items kept 356 MB and now keeps 100 MB, as much as Bitcoin Core. bloomRelevantUpdatematches a transaction as BIP 37 andIsRelevantAndUpdateof Bitcoin Core do: by its hash, by the data of its output scripts, by the outpoints it spends and by the data of its input scripts. It only looked at outputs, so a transaction that spends a watched coin was not matched. Pay-to-pubkey and multisig outputs are told by the shape of the script, asSolverdoes, so a hybrid key or a key that is not on the curve counts. TheCtxargument is no longer used.- The
version,timestampandstartHeightfields ofVersionareInt32,Int64andInt32, signed as in Bitcoin Core. A peer that sent version ffffffff appeared to have version 4294967295. validVersionapplies its three conditions independently, asContextualCheckBlockHeaderdoes. It accepted old block versions on networks whose activation heights are not in the order BIP 34, BIP 66, BIP 65, which are Bitcoin Cash regtest and testnet4.chooseBestkeeps the first block when two have the same work, whatever their heights, as Bitcoin Core does. It preferred the higher one.connectBlockandconnectBlocksreturn the stored node of a header that is already in the store, asAcceptBlockHeaderdoes in Bitcoin Core. A known header at or below a checkpoint used to be refused as “Rewriting pre-checkpoint chain”.- The memory header store returns a node only for the hash that was asked for. It returned any node whose hash shared the first eight bytes.
calcHashreturnsMaybe Hash256and no longer callserroroutside the tree.splitInwith a length below one returns the list as one chunk and no longer loops.Hashable BlockNodehashes the header only, likeEq.- An
addrv2entry of the Tor v2 network is read whatever its length, since BIP 155 says to ignore such entries, and no longer fails the message.fromNetworkAddressV2givesNothingfor an IPv6 entry that holds an IPv4-mapped or an OnionCat address.toNetworkAddressV2gives a Tor v2 entry for an OnionCat address. getMessagechecks the magic bytes before the command name.bloomCreatepicks the number of hash functions with the integer division of Bitcoin Core.Read MessageCommandreads whatShowwrites for any bytes.btcRegTesthas a target timespan of one day, as Bitcoin Core regtest, andbtcTest4pins no BIP 34 block hash, as Bitcoin Core testnet4.- The reader of
BlockNoderefuses a chain work that is negative or does not fit 256 bits. - The generators
arbitraryMerkleBlock,arbitraryBlock,arbitraryBlockNode,arbitraryVarInt,arbitraryReject,arbitrarySendCompact,arbitraryCompactBlockandarbitraryBlockTxncover more cases, andarbitraryNetworkTx,arbitraryNetworkGetBlockTxnandarbitraryPositionsUpToare new. - A
TaprootSignatureonly holds a hash type thatisTaprootSigHashaccepts. Its constructor is now a pattern that only matches. Build a value withtaprootSignatureordecodeTaprootSig; the fieldssigandhashare read as before and cannot be updated.encodeTaprootSigused to cut any hash type to one byte, so0x100was written as a 65-byte signature ending in00, which BIP 341 forbids, and0x101asSIGHASH_ALL. ScriptPathDatahas a new last field,opaqueScript: the bytes of the script when they are not a list of opcodes, in which casescriptis empty.viewTaprootWitnessnow reads a script path spend whatever its script bytes are. It used to returnNothingfor a Tapscript with anOP_SUCCESSxopcode followed by bytes that do not decode, and for a leaf of another version, although such spends can be valid.verifyScriptPathDatatakes the leaf hash on the script bytes of the witness, and isFalsefor a proof of more than 128 hashes.encodeTaprootWitnessreturnsEither String WitnessStack. It fails for an annex that is empty or does not start with0x50, for a proof of more than 128 hashes, and for a leaf version that is odd or is0x50. It used to write a witness that reads back as another spend, or that every node refuses.taprootSigHashfails for an annex that is empty or does not start with0x50. No node takes such an item for an annex, so no node computes that digest.data/bip341.jsonis the file of the BIPs repository, whose key isfullySignedTx.Signerhas a fourth field,requirePreviousTx.keySignerandxPrvSignerrefuse a SegWit version 0 coin without its previous transaction. Attach the previous transaction, or setrequirePreviousTx = Falseto trust the PSBT writer.psbtTransactionrefuses a PSBT with an input that is not final. UsepsbtPartialTransactionfor the old behaviour.decodePsbtrefuses what Bitcoin Core refuses: a key type that is not a compact size in its shortest form, a public key that is not on the curve, a partial signature that is not in the encoding of BIP 66 with a defined sighash byte, a previous transaction that the input does not spend, and a script tree that is not whole.signPsbtandfinalizePsbtleave alone an input whose redeem or witness script is not a standard template, instead of failing the whole PSBT.signPsbtsigns only for keys that the signed script names. It adds no signature to a script-hash coin whose script is missing, and signs a Taproot leaf only when its control block commits it to the coin.signPsbtandfinalizePsbtrefuse an uncompressed key on a SegWit version 0 coin.finalizePsbtverifies only the signatures of keys the script names, against one digest per input.finalizePsbtrefuses a signature that is notSIGHASH_ALL(SIGHASH_DEFAULTfor Taproot) when the input names no sighash type, and leaves alone a legacy coin known from its witness output only.addInputandaddOutputcheck the map they are given.- A PSBT error names at most 40 bytes of a key.
Fixed
- The documentation of the hardened markers, of the depth limit and of
decodeSecKeyno longer describes old behaviour of Bitcoin Core and of the secp256k1 binding. The documentation ofPrivateKeyandXPrvKeysays that theShowinstance of theSecKeyinside them prints the secret. - The documentation of
TxSignature,decodeTxSiganddecodeInputlists the rules they apply: strict DER, a low S and a defined hash type, the three signature rules of the relay policy of Bitcoin Core. A signature that only consensus accepts does not decode, so an input of an old block can fail to decode. The documentation also says that a Schnorr signature of Bitcoin Cash does not decode. - The documentation of
decodeOutputsays that a multisig whose keys are not direct pushes, and a data carrier whose pushes are not the shortest, come back fromencodeOutputas other bytes, and lists the scripts that theSolverof Bitcoin Core classifies and this function does not: a key in hybrid form or off the curve, andOP_RETURN OP_RESERVED. - The documentation of
txSigHashnamesSerializeScriptCodeof Bitcoin Core for the removal ofOP_CODESEPARATOR, and says that the pushes of the signature are not removed from the script, whichFindAndDeletedoes in Bitcoin Core. The documentation oftxSigHashForkIdlists what is not supported on Bitcoin Cash:SIGHASH_UTXOS, coins that hold tokens, and Schnorr signatures. - The documentation of
Addresssays that a token-aware CashAddr and its plain form are different values that pay the same script, and that a value built with a constructor or read from the binary form is not checked against a network. The documentation oftextToAddrsays that white space around an address is refused and that some CashAddr kinds decode without being payable. The documentation ofaddressToOutputsays that it does not know the network. - The documentation of
maxMultisigKeyssays that 20 keys fit a bare and a pay-to-witness-script-hash multisig only. - The documentation of
verifyStdTxandverifyStdInputsays what they check and what they do not: they are not a script interpreter, they never answerTruefor an input of the kinds they know that no block can contain, and they answerFalsefor some inputs that a block may hold but nodes do not relay. - The documentation of
SigInputsays that the value of a legacy coin is not committed to by its signature, so that a signer cannot check it without the previous transaction. - The documentation of
guessTxSizeandtxVirtualSizesays that they do not count signature operations, which Bitcoin Core charges for in a transaction that is dense in them. - The documentation of
InputKindgives the ranges of a multisig kind, and that ofbuildTxandbuildAddrTxsays what they do not check. leafHash, and with itmastCommitment,taprootOutputKey,taprootScriptOutput,getMerkleProofsandverifyScriptPathData, write the script length of a Taproot leaf as a CompactSize, as BIP 341 defines the tapleaf hash and asComputeTapleafHashdoes in Bitcoin Core. They wrote a base-128 varint, which is the same byte below 128 only. A leaf script of 128 bytes or more, such as amulti_aof four keys, therefore got a leaf hash, an output key and an address that no other software derives, and that leaf could not be spent. The address of every script tree with such a leaf changes. Trees whose leaf scripts are all shorter than 128 bytes, and outputs without a tree, are not affected. Coins at an address that version 1.5.1 or earlier made from such a tree can still move by the key path, or by the script path of a leaf shorter than 128 bytes. To rebuild the old output, put the old hash of each long leaf in the tree as aMASTCommitment. The old hash is the tagged hashTapLeafof the leaf version, the script length asData.Bytes.VarInt.VarIntof the bytes package, and the script bytes.taprootOutputKeyandtaprootOutputSecKeythen give the old output key and the secret key of its key path, andgetMerkleProofsgives the proofs of the short leaves. The long leaves of such an output can never be spent. The test “An output made by versions up to 1.5.1 with a leaf of 128 bytes or more” inTaprootSpec.hshas the code.getMerkleProofslists the hashes of a proof from the leaf up to the root for a leaf at any depth, as BIP 341 and Bitcoin Core do. From depth 3 the order was wrong, and every node refused the control block built from it. It also hashes each subtree once instead of once per level.- The JSON form of
XOnlyPubKeyreads the hex of 32 bytes only. It used to read the first 32 bytes of a longer value, so a 33-byte compressed key was taken for another key. tapTreeLeavesreturnsNothingfor a tree with a leaf deeper than 128, and no longer wraps the depth at 256.xPrvSignerno longer throws for a derivation path of more than 255 steps. The input stays unsigned.signPsbtsigns a P2SH-P2WPKH coin by the hash of its key, without a derivation record.signPsbtandfinalizePsbtuseSIGHASH_ALLwith the fork id flag on Bitcoin Cash when an input names no sighash type.signPsbtsigns a Taproot key path when the record of the internal key lists a leaf.- The PSBT scripts under
scripts/run again, against Bitcoin Core 32.1.
[1.5.1] - 2026-11-04
Fixed
nextAsertWorkRequiredevaluates the aserti3-2d formula at the parent of the new block, with the parent’s timestamp and height, asGetNextASERTWorkRequireddoes in Bitcoin Cash Node. It used the new block’s own timestamp and height, so the bits it required were wrong for every block that did not come exactly ten minutes after its parent. The bits check that 1.5.0 added toconnectBlocksturned this into a rejected block on Bitcoin Cash mainnet at height 975899.
[1.5.0] - 2026-11-03
Added
-
Taproot signing and verification, BIP 341 and BIP 342:
taprootSigHashcomputes the digest a Taproot signature signs, for the key path and for a leaf of a script path;taprootOutputSecKeytweaks a secret key astaprootOutputKeytweaks its public key;signTxsigns aSigInputof a Taproot output by its key path, with a key whose x-only public key is the output key or whose tweak without a script tree is, as a BIP 86 wallet spends, andsignTaprootInputsigns one input;verifyStdTxverifies key path spends, and script path spends whose leaf is a single key check.TaprootSignature,encodeTaprootSiganddecodeTaprootSigare a Schnorr signature with its hash type,isTaprootSigHashtells the hash types a Taproot signature may use, andsigHashDefaultis the default one. The key path vectors of BIP 341 and the BIP 86 vectors check them. -
The Taproot fields of PSBTs, BIP 371, in
Haskoin.Transaction.Psbt:tapKeySig,tapScriptSigs,tapLeafScripts,inputTapDerivations,inputTapInternalKey,inputTapMerkleRoot,outputTapInternalKey,outputTapTreeandoutputTapDerivations, with their inserts. They are checked against the spec when a PSBT is read.signPsbtsigns a Taproot coin by its key path when the input has its internal key, and by each leaf script that is a single key check;finalizePsbtfinalizes both. The test vectors of BIP 371 check them. -
tapTreeLeavesandtapTreeFromLeavesinHaskoin.Transaction.Taprootconvert a script tree to and from its leaves with their depth, the form ofPSBT_OUT_TAP_TREE.encodeControlBlockanddecodeControlBlockbuild and read a control block, which must be 33 bytes plus 32 per node with at mostmaxControlBlockNodes(128) nodes.leafHash,keyParityandtapscriptLeafVersionare exported. -
txWitnessHashinHaskoin.Transaction.Commonis the witness transaction id of BIP 141.txWeightandtxVirtualSizegive the weight and the virtual size of a transaction, andhasWitnesstells whether it has a witness. -
InputKindandOutputKindinHaskoin.Transaction.Buildername the kinds of inputs and outputs: pay-to-public-key, pay-to-public-key-hash with a compressed or an uncompressed key, pay-to-script-hash multisig, the native and nested SegWit kinds, Taproot, pay-to-script-hash of a 256-bit hash, and any other output script by its size.guessTxWeight,guessTxSizeandguessTxFeeestimate the weight, the virtual size and the fee of a transaction of those kinds, withinputWeightandoutputSizeper kind. The guess is an upper bound: it counts 72 bytes per signature, the one-byte empty witness stack of a legacy input in a transaction with a witness, and two bytes for each count of a multisig script above 16. -
SignerinHaskoin.Transaction.Psbtis a record:keyOfRecordgives the key a derivation record names, as before, andkeyOfHashthe key of a public key hash, so thatsignPsbtsigns the coins whose keys it holds when the input has no derivation record.keySignerprovides both,xPrvSignerthe first. -
Tests for every function of the
Haskoin.TransactionandHaskoin.Utilmodules, with the key path vectors of BIP 341, the vectors of BIP 86 and BIP 371, and literal sizes, ids and fee estimates. -
connectBlockandconnectBlocksnow check that a header carries the difficulty bits the chain requires, asContextualCheckBlockHeaderdoes in Bitcoin Core.validBlocktakes the required bits as a new argument. Without this check a chain of headers mined at the minimum difficulty was accepted. -
asertAnchorfinds the anchor block of the aserti3-2d algorithm for the chain that ends at a given block.mtpis now the median time past of the block itself, as in Bitcoin Core, instead of its parent’s. -
getVarIntListandputVarIntListinHaskoin.Network.Commonread and write lists with theirVarIntcount. A count abovemaxListSize(32 MiB, theMAX_SIZEof Bitcoin Core) fails the parse instead of wrapping around and reading an empty list. Blocks, Merkle blocks,getblocks,getheaders,headers,inv,getdata,notfoundandaddrmessages use them. -
genesisTxinNetworkholds the coinbase transaction of the genesis block.Haskoin.Transaction.GenesisexportsmainnetGenesisTx,testnet4GenesisTxandgenesisCoinbase. -
skipHeightandmedianTimeare exported fromHaskoin.Block.Headers. -
Tests for every function of the
Haskoin.Blockmodules, with vectors from Bitcoin Core and from mainnet block 100000. -
PSBT version 2 (BIP 370) in
Haskoin.Transaction.Psbt: a PSBT of version 2 is read and written as its maps, with the unsigned transaction its fields describe and the lock time they determine. The roles take both versions, and version 2 has its constructor (addInput,addOutput), its updater (setInputSequence), the flags ofPSBT_GLOBAL_TX_MODIFIABLE, which the signer and the combiner keep as the spec and Bitcoin Core do, and conversion between the versions (withVersion). The test vectors of BIP 370 and files of Bitcoin Core 32.1 check it. -
addrToBase58,addrToBech32andaddrToCashencode an address in one chosen format.addrToBase58gives the legacy form of a Bitcoin Cash address. -
Token-aware CashAddr addresses (types 2 and 3) can be paid.
addressToOutput,addressHash160andaddressHash256treat them like their plain P2PKH, P2SH and P2SH32 counterparts. -
decodeSecKeyinHaskoin.Crypto.Keys.Commonbuilds a secret key from 32 bytes and refuses zero and values at or above the curve order.fromWif,fromMiniKey,xPrvImport,makeXPrvKeyand the binary form ofPrivateKeyuse it. ThesecKeyof secp256k1-haskell 1.7.0 only checks the length, so such keys used to import and then crashderivePubKeyandsignMsgwith an error call. -
Derivation paths accept
handHas hardened markers, som/84h/0h/0hparses. Bitcoin Core and BIP 380 write'orh, and BIP 32 writes a subscript H. -
Tests for BIP32 test vectors 4 and 5, for a BIP39 passphrase with accented letters, and for every function of the
Haskoin.Cryptomodules. -
The P2P messages added since 2016:
addrv2andsendaddrv2(BIP 155) withAddrV2,NetworkAddressV2and conversions from and toaddrentries;feefilter(BIP 133);sendcmpct,cmpctblock,getblocktxnandblocktxn(BIP 152) inHaskoin.Network.CompactBlock;getcfilters,cfilter,getcfheaders,cfheaders,getcfcheckptandcfcheckpt(BIP 157) inHaskoin.Network.BlockFilter;wtxidrelay(BIP 339) andsendtxrcncl(BIP 330). The inventory typesInvCompactBlockandInvWTx, and the service bitsnodeCompactFilters,nodeNetworkLimitedandnodeP2PV2. -
maxMessageSizeinNetworkbounds the payloadgetMessagereads: 4,000,000 bytes on Bitcoin, as in Bitcoin Core, and twice the block size on Bitcoin Cash. -
scriptPushDatalists the data elements a script pushes, asGetOpdoes in Bitcoin Core. -
getVarIntBytesandputVarIntBytesread and write bytes with their count, under the same limit as lists. -
Tests with fixed wire bytes for every message type, the sample
version,verackandaddrmessages of the protocol documentation, and every function of theHaskoin.Networkmodules. -
pushOpDatagives the data that a push opcode puts on the stack, andisMinimalPushtells whether a push is in its shortest encoding, the rule ofCheckMinimalPushin Bitcoin Core. -
OP_CHECKSIGADDis a pattern synonym for opcode 0xba, whichOP_CHECKDATASIGalso names, so a Tapscript can be written with the name of BIP 342. -
NestedWitnessInputis the input script of a P2SH-P2WPKH or P2SH-P2WSH output: a push of the witness output script.decodeInput,encodeInput,inputAddressand the signer use it, andisNestedWitnessInputtests for it. Such an input used to fail to decode. -
maxMultisigKeysis 20, theMAX_PUBKEYS_PER_MULTISIGof Bitcoin Core.decodeOutputreads a multisig output with 17 to 20 keys, whose counts are one-byte pushes, andencodeOutputwrites one. Only 16 keys used to be supported. -
decodePubKeyinHaskoin.Crypto.Keys.Commonreads a public key from bytes that must hold the key and nothing else. -
Tests for every function of the
Haskoin.Scriptmodules, with literal vectors from BIP 67, BIP 143, BIP 173 and BIP 341.
Changed
makeSignature, and with itsignTx,signInputandsignPsbt, refuse to sign a legacy input withSIGHASH_SINGLEwhen the input has no output at its index. The digest of that case is the constant 1, which commits to nothing, so the signature would spend any other coin of the key wherever the same case comes up.txSigHashstill computes that digest, as a node must to verify such signatures, andverifyStdInputstill accepts them. A SegWit input is signed in that case, since the BIP 143 digest commits to the coin.Signerhas a new field,acceptedSigHashes: the sighash types it signs with.keySignerandxPrvSignerset it todefaultSigHashes, which areSIGHASH_DEFAULTandSIGHASH_ALL.signPsbtrefuses an input that asks for another type, as BIP 174 asks of a signer: the other types leave part of the transaction unsigned, and the PSBT comes from someone else. A PSBT asking forSIGHASH_NONEorSIGHASH_SINGLEused to be signed as asked. The fork id flag is not compared, since the network decides it.PrivateKeyandXPrvKeyshow their secret as<redacted>, and no longer have aReadinstance; nor doesXKey. The derived instances printed the key, and forXPrvKeythe chain code, so a value in a log or an error message gave the coins away.toWifandxPrvExportwrite a key out.prvSubKey,pubSubKey,hardSubKeyandcycleIndexthrowDerivationExceptionfor a child index with the hard bit set, like every other failure of a derivation. They used to callerror.- A transaction with no inputs whose next byte, the flags of BIP 144, is neither 0x00 nor 0x01 fails to parse with “Unknown transaction optional data”, as in Bitcoin Core. Such bytes used to parse as a legacy transaction with no inputs that did not serialize back to the same bytes. A
Txstring literal must hold the transaction and nothing else, asDecodeHexTxrequires in Bitcoin Core. - The documentation of
decodeBase58anddecodeBase58Checksays that their cost grows with the square of the length and that callers must bound untrusted input. The decoders of the library bound it already. taprootOutputKeyandtaprootScriptOutputtweak the x-only internal key, as BIP 341 defines the output key. An internal key with an odd y coordinate used to be tweaked as a full point, which gave an output key and an address no other wallet derives from that key, and that a BIP 341 signer could not spend.viewTaprootWitnessreads a witness in the order of BIP 341: the annex last, then the control block, then the script, then the stack. It used to read the control block and the script in the other order, and so decoded no real script path witness.encodeTaprootWitnesswrites the annex only when there is one; it used to append an empty item, which made the witness invalid. The control block of aScriptPathDatais a list of digests, and a control block of a wrong length is refused.KeyPathSpendcarries the annex of a key path spend.signPsbtapplies the checks of the BIP 174 signer. A coin known fromPSBT_IN_WITNESS_UTXOalone is signed only with a witness signature, since the legacy digest does not commit to the amount; the previous transaction is used first, and a witness output that is not its output is refused. A sighash type no node accepts, or whose fork id flag does not fit the network, is refused instead of signed. A redeem script that does not hash to the coin, or a witness script that does not hash to the witness program, is refused. An input whose coin script is not standard is left alone instead of failing the PSBT.finalizePsbttakes theNetworkand verifies every signature it uses, against the digest of the coin and the sighash type of the input when it has one, asPSBTInputSignedAndVerifieddoes in Bitcoin Core. A signature that does not verify, is not a signature, or uses another sighash type is refused. It used to finalize any bytes stored as a signature.mergeTxsleaves an input that no transaction signed as it is. It used to crash withPrelude.head: empty list.chooseCoinstakes the kinds of the outputs and a fee rate per virtual byte, and estimates the fee from thecoinInputKindof each coin, a new method ofCoin.chooseMSCoins,chooseMSCoinsSink,guessMSTxFeeandguessMSSizeare gone, since the kind of input lives on the coin. The old estimate counted every input as pay-to-public-key-hash and charged bytes rather than virtual bytes, so a SegWit wallet overpaid fees by about 60 %.hexToTxHash, and theRead, string and JSON forms ofTxHashandOutPoint, refuse hex that is not exactly 32 bytes. Longer hex used to be accepted with its leading bytes dropped.- Transaction parsing bounds the counts of inputs, outputs, witness stacks and witness items by
maxListSize, asReadCompactSizedoes in Bitcoin Core; a count of 2^64 - 1 used to wrap around and read as zero. A transaction with the witness marker but no witness item is refused (“Superfluous witness record”), as Bitcoin Core refuses it. A transaction whose witness stacks are all empty is serialized without the marker, andhasWitnessis false for it. verifyStdTxandverifyStdInputtake the coins of every input, matched by outpoint, so that Taproot inputs can be verified. A witness on an input whose coin is not a witness program is refused, asVerifyScriptrefuses it in Bitcoin Core, andverifyStdTxrefuses outputs that spend more than the coins hold.nosigTxHashremoves the witness as well as the input scripts, so that it is the same before and after signing a SegWit input.getBitsraises an error for input shorter than the bits asked,getListandgetLengthBytesrefuse a count that does not fit anInt, andunmarshalLazyreturnsEither String ainstead of throwing.setInputSequencerefuses to change the sequence of an input that is signed, since every signature commits to it.WitnessProgram,WitnessProgramPKH,MAST,TaprootWitnessandScriptPathDatahaveShowandEqinstances.- The package requires secp256k1-haskell 2.0.0, whose
normalizeSigis total.verifyHashSignormalises the signature with it, andisCanonicalHalfOrderis itsisLowS. buildMerkleRootreturnsEither String MerkleRoot. It fails for an empty list, which used to crash, and for a mutated tree in which two sibling nodes are identical (CVE-2012-2459).extractMatchesrejects such a tree too, asCPartialMerkleTree::TraverseAndExtractdoes in Bitcoin Core, and reports why a partial tree is malformed.buildMerkleRoot,buildPartialMerkleandcalcHashbuild the tree in linear time instead of quadratic.decodeMerkleFlagsis linear too; it took a minute for 100 KB of flags, which a peer could send in amerkleblockmessage.genesisBlockno longer takes aCtxand returns the right coinbase for every network. Bitcoin testnet4 has its own genesis coinbase, sogenesisBlock btcTest4used to return a block whose transactions did not match its header.- The
merklerootfield of the JSON form ofBlockHeaderis in display order, likeprevblockand thegetblockheaderRPC call of Bitcoin Core. It was in internal byte order. hexToBlockHashand the JSON parser ofBlockHeaderreject a hash that is not exactly 32 bytes. They used to accept longer input and drop the leading bytes.computeAsertBitstakes the proof-of-work limit of the network as its first argument instead of using the mainnet limit.connectBlocksadds each header to the store as it passes, so a batch with a bad header keeps the headers before it and makes the last of them the best block when it has the most work.Ord BlockNodecompares the height and then the header, so that it agrees withEq, which compares the header.appendBlocksdates every block one target spacing after its parent, so the chains it mines pass the median time past rule.blockLocatorNodessteps back likeLocatorEntriesin Bitcoin Core and lists the genesis block once.encodeCompactraises an error for a number wider than 255 bytes instead of wrapping the exponent.traverseAndBuildandtraverseAndExtractare no longer exported.getMessageconsumes exactly one message. It used to leave the payload in the stream and let the payload parser read past the declared length, so bytes outside the checksum could end up in a message. It now checks the magic before reading the payload, refuses a payload abovemaxMessageSize, and accepts a payload on message types that carry none, as Bitcoin Core does.VarIntrefuses an encoding longer than the value needs, asReadCompactSizedoes in Bitcoin Core. A transaction with such an encoding used to parse and re-serialize to different bytes.VarStringrefuses a count abovemaxListSize.MessageCommandrefuses a name with a byte outside printable ASCII or with a byte other than NUL after it, as Bitcoin Core does, and serializing a name longer than twelve bytes is an error instead of a silent truncation.- The
extrafield ofRejectis aMaybe Hash256. It was aByteStringthat only round-tripped when empty or 32 bytes long. Versionrefuses a user agent longer than 256 bytes, as Bitcoin Core does.BloomFilterrefuses a filter above 36000 bytes or 50 hash functions when parsing, as Bitcoin Core refuses such afilterload, and reads its bytes in one go instead of one at a time.FilterAddrefuses an element above 520 bytes.BloomFlagsreads only the two low bits of its byte, as Bitcoin Core does.bloomRelevantUpdatematches the data pushed by every output script, standard or not, and inserts outpoints according to the flags, asIsRelevantAndUpdatedoes in Bitcoin Core. It used to raise an error for any transaction with a non-standard output, and for a matching output that was not pay-to-pubkey or pay-to-multisig underBloomUpdateP2PubKeyOnly. It returnsJustthe filter whenever an output matches, unchanged underBloomUpdateNone.bloomInsertandbloomContainstreat a filter with zero hash functions as matching everything, as Bitcoin Core does.bloomInsertused to loop over 2^32 hash functions andbloomContainsused to answerFalse.bloomCreategives zero hash functions for zero elements.bip34BlockinNetworkis split intobip34Heightandbip34Hash, the hash being optional.btcRegTestactivates BIP34, BIP65 and BIP66 at height 1, as Bitcoin Core regtest does; it had the heights of Bitcoin Cash Node regtest.bchRegTesthas the 32 MB block size of Bitcoin Cash Node.arbitraryBloomFiltergenerates aBloomFilterwith random bits instead of a tuple.- Replace the PSBT module with
Haskoin.Transaction.Psbt, which keeps every field of a PSBT, known or not, writes maps with their keys in order as Bitcoin Core does, refuses what breaks BIP 174 (duplicate keys, keys out of shape, bytes after the last map, other versions), and reads and writes the global xpub and proprietary fields. It still creates, combines, signs, finalizes and extracts. TheHaskoin.Transaction.Partialmodule and its types are gone. pubKeyWitnessAddrandpubKeyCompatWitnessAddrnow returnMaybe Addressand refuse uncompressed keys. BIP 143 makes witness spends with uncompressed keys non-standard, so coins sent to such an address could not be spent through the normal network.- The
WitnessAddressandCashAddressconstructors are now patterns that can only match. Build these addresses withwitnessAddressandcashAddress, which validate the version and payload and return the dedicated constructor when one exists. Every address now has a single representation, so equal addresses compare equal. outputAddressandscriptToAddressno longer report the P2PKH address of the key for a pay-to-public-key output. Such coins cannot be spent with the P2PKH input script, so the address was misleading.convertBitsinHaskoin.Util.HelpersreturnsMaybe [Word]and fails on padding bits that are not zero, or that are wide enough to hold a whole input digit, when padding is not allowed.- JSON encoding of an address that has no text form on the given network now fails with an error instead of writing
null. mnemonicToSeedNFKD-normalises the sentence and the passphrase, as BIP39 requires and as Trezor, Electrum, python-mnemonic and bitcoinjs do. A passphrase with accented letters used to give a seed that no other wallet derives, so the funds could not be restored elsewhere.fromMnemonicnormalises the sentence before looking up its words. This adds a dependency onunicode-transforms.toMnemonicaccepts 16 to 32 bytes of entropy andfromMnemonicaccepts sentences of 12, 15, 18, 21 or 24 words, the sizes BIP39 allows. Entropy of 4 to 12 bytes and of 36 to 64 bytes, and sentences of 3 to 9 and 27 to 48 words, were accepted before, and no other wallet reads them. A word that is not in the list is reported with thefromMnemonic:prefix.xPrvImportandxPubImportrequire a payload of exactly 78 bytes, as Bitcoin Core does. Trailing bytes and an uncompressed public key used to be accepted. They also refuse a key at depth 0 whose parent fingerprint or index is not zero, as BIP32 test vector 5 requires.fromWifrefuses strings longer than 52 characters, andxPrvImportandxPubImportstrings longer than 112, before decoding them. No valid key is longer, and the time to decode Base58 grows with the square of the length.prvSubKey,pubSubKeyandhardSubKeythrowDerivationExceptionwhen the parent is at depth 255. The depth of the child used to wrap around to 0.- Parsing a derivation path reads each index as an unbounded integer and accepts decimal digits only.
m/4294967296used to wrap around tom/0, and hex, parenthesised and signed numbers were accepted. - The JSON form of
Sigis decoded withdecodeStrictSig, like the binary form, so a high-S signature is refused by both. - The
Readinstances ofHash160,Hash256andHash512, andtextToFingerprint, refuse hex of the wrong length, as the string literal instances already did. A two-byte hash could be read and then crashsignHash. encodeOutputreturnsEither String Script. It fails for aPayMulSigwhose required count is not between 1 and its number of keys or that has more than 20 keys, and for aPayWitnesswhose version is 0 or above 16 or whose program is not 2 to 40 bytes long. Those values used to crash with anerrorcall.marshalPutstill fails with anerrorcall for them, since it cannot report a failure.txSigHashandtxSigHashForkIdreturnEither String Hash256. They fail for an input index the transaction does not have. WithSIGHASH_ALL,txSigHashused to return a digest over the transaction without any script code for such an index, and crash for the other hash types.makeSignatureandmakeSigHashreturnEithertoo.- On a network with a fork id,
txSigHashrequires theSIGHASH_FORKIDflag anddecodeTxSigrejects a signature without it.makeSignaturesets the flag, sosignTx bchwith a plainsigHashAllproduces a signature the network accepts. It used to sign the legacy digest, which Bitcoin Cash has rejected since 2017. isSigHashUnknownis the test ofIsDefinedHashtypeSignaturein Bitcoin Core: after masking theSIGHASH_ANYONECANPAYandSIGHASH_FORKIDbits, the hash type byte must be 1, 2 or 3. A byte such as 0x21 used to pass and be signed, although every node rejects it underSTRICTENC.opPushDatagives the minimal encoding of BIP 62:OP_0for the empty string,OP_1toOP_16for one byte from 1 to 16 andOP_1NEGATEfor the byte 0x81. The empty push used to be anOP_PUSHDATAthat serialized to the byte ofOP_0but was not equal to it.DataCarrierholds the data of every push afterOP_RETURN, as a list, sinceTxoutType::NULL_DATAof Bitcoin Core isOP_RETURNfollowed by any pushes. A bareOP_RETURN, several pushes and an empty push used to be “Non-standard output”.intToScriptOpreturnsEither String ScriptOpinstead of crashing outside 1 to 16.scriptOpToIntno longer serializes its argument, so it cannot crash on anOP_PUSHDATAwhose payload does not fit its type.sortMulSigreturns any output that is not a multisig unchanged instead of crashing.decodeInputreads the input of a pay-to-script-hash output as its redeem script requires. A multisig template with the redeem script and no signature yet,OP_0 <redeem>, is aSpendMulSig [], whichmergeTxsused to reject. An input that does not fit its redeem script fails.- The generators of
Haskoin.Util.Arbitrary.Scriptproduce pushes of everyPushDataType, pushes that are not minimal, multisig outputs of up to 20 keys, data carriers with any number of pushes, nested witness inputs, and, on networks with a fork id, only hash types with the flag.
Fixed
- A block whose timestamp equals the median time past of its parent is rejected, as in Bitcoin Core. It used to be accepted.
connectBlockskeeps the block that was seen first as the best block when a competing block has the same work and height, asconnectBlockand Bitcoin Core do. It used to switch the best block to every competitor.calcNextWorkandcomputeTargetmeasure the time span of a retarget period as a signed number. A period whose last block is dated before its first block used to wrap around to four times the target timespan instead of being limited to a quarter.headerWorkreturns zero for bits that are negative, overflow or encode a zero target, likeGetBlockProofin Bitcoin Core. It used to divide by zero for bits0x03800001.- The block version is compared as a signed number, as in Bitcoin Core, so versions with the top bit set are rejected once BIP34 is active.
- The Bitcoin Cash difficulty algorithms follow Bitcoin Cash Node: the minimum-difficulty rule of test networks applies to the ASERT algorithm and no longer applies to the EDA and DAA algorithms on mainnet, the EDA returns the bits of the last block without minimum difficulty on test networks,
getSuitableBlockpicks the same block as Bitcoin Cash Node when timestamps are equal,computeTargetno longer overshoots by one, the ASERT anchor is the first block whose own median time past reaches the activation time, and regtest never retargets. - A PSBT of another version is refused by its version. The unsigned transaction was looked for first, and a PSBT of version 2 holds none.
addressBytesreturned a length-prefixed value for a generic CashAddr instead of the raw hash.- Base58, Bech32 and CashAddr decoders accepted non-ASCII look-alike characters. Base58 truncated code points to one byte and Bech32 used Unicode case folding. All three now accept ASCII only, as the specifications require.
cashAddrDecodeaccepted mixed-case strings, which the CashAddr specification rejects.- CashAddr decoding accepted non-canonical strings whose padding bits were not zero, or that carried an extra group of padding, so several strings could stand for the same address.
textToAddrdecoded Base58 input of any length, with a cost growing with the square of the length. Strings longer than any valid address are now rejected first.- Binary deserialization of an address now validates the witness version, the CashAddr type and the payload length, and checks the length prefix before converting it to an
Int. Previously an invalid value could be read and later crashaddressToScript. - The Bech32 and bit conversion accumulators are masked so that they cannot overflow on long inputs.
encodeOutputhad no case forPayScript32Hashand crashed on it, so a P2SH32 address could not be turned into a script.- The P2WSH test generator produced P2WPKH addresses.
fromWifthrew an exception on a Base58Check string with an empty payload, such as3QJmnh, also through the JSON parser ofPrivateKey. It returnsNothingnow.- The JSON parser of
PublicKeyaccepted bytes after the key. decodeOutputmatches the output templates on their exact bytes, as Bitcoin Core does. A script that wrote the hash of a P2PKH, P2SH, P2SH32 or witness output withOP_PUSHDATA1or a longer push used to decode as that output and give its address. By consensus such a script is not a P2SH or witness output at all, and anyone can spend it, so a wallet could credit a payment to its address that was not one.- A public key pushed in a script or a witness must fill its push. A 65-byte item that starts with 0x02 or 0x03 used to parse as the 33-byte key it starts with, so
decodeOutput,decodeInputand the witness decoders reported a key the script did not hold, andverifyStdTxaccepted a P2PKH or P2WPKH spend whose key item carried 32 extra bytes, which no node accepts. verifyStdInputreturnsFalsefor an input index the transaction does not have, andparseExistingSigsreads no witness signatures when the witness list is shorter than the input index. Both used to crash.
[1.4.0] - 2026-10-31
Added
- Bitcoin testnet4 network (
btctest4) with the BIP94 block storm and time warp rules.
Changed
- Update DNS seeds to match Bitcoin Core and Bitcoin Cash Node. Drop the Bitcoin ABC seeds, which now serve eCash.
- This package now requires base16 2.0 and secp256k1-haskell 0.7.
Fixed
- Compute the difficulty retarget in
nextPowWorkRequiredandnextEdaWorkRequired, which never reached their retarget branches. - Fix the Bitcoin Cash DAA activation height, which had a typo (404031 instead of 504032).
- Start the DAA one block later on Bitcoin Cash testnet and testnet4, to match Bitcoin Cash Node.
- Fix the Bitcoin Cash ASERT half-life, which must be two days.
[1.3.0] - 2026-08-12
Changed
- Make script and address parsing more strict.
- Use smart constructors for addresses to avoid inconsistent or incorrect represtentations.
Fixed
- Support Bitcoin Cash 256-bit pay-to-script-hash scripts and addresses.
- Make addresses of incorrect length invalid.
- Avoid decoding scripts as standard when hash or public key length incorrect.
- Fix secp256 library function names.
[1.2.2] - 2025-05-08
Changed
- Standardize author name and correct README link.
[1.2.1] - 2025-05-08
Changed
- New home in github.com/jprupp rather than github.com/haskoin.
[1.2.0] - 2023-07-23
Changed
- Move from
cryptonitetocryptonfor now.
[1.1.0] - 2023-03-14
Changed
- Update upstream dependencies & LTS Haskell.
[1.0.4] - 2023-10-09
Changed
- Export the BIP39 word list
- Add new BIP39 test vectors
[1.0.3] - 2023-09-02
Changed
- New dependency on data-default
- Renamed some arbitrary (test) functions for clarity
- Reworked the identity testing tool to include Marshal and MarshalJSON classes
[1.0.2] - 2023-08-04
Fixed
- Text files needed for testing now in tarball.
[1.0.1] - 2023-08-03
Changed
- Do not use alternative monad for Either String values.
[1.0.0] - 2023-07-28
Changed
- Shorten all field names allow duplicates, using DuplicateRecordFields extension.
- Use OverloadedRecordDot language extension and syntax.
- Use explicit context object from secp256k1-haskell library.
- Unify serialization into custom Marhsal and MarshalJSON classes.
- Use ormolu for formatting.
- Simplify module organisation.
- Strongly break backwards compatibility.
[0.22.0] - 2023-06-28
Changed
- Upgrade to lastest secp256k1 and base16 packages.
[0.21.2] - 2022-04-13
Changed
- Serialisation test now works for both strict and lazy bytestrings.
[0.21.1] - 2021-12-13
Changed
- Make Base58 faster.
[0.21.0] - 2022-11-23
Added
- BCH Testnet4 support.
Changed
- Fix inconsistent serialization/deserialization issues.
Fixed
- Makes
finalScriptWitnessfield encoding conform to bitcoin core. - Fixes bug in
finalizeTransaction
Added
- Signing support for PSBTs
- Helper function for merging PSBTs
- More PSBT tests
- Partial support for taproot
[0.20.5] - 2021-09-13
Added
- Support Bech32m address format for Taproot.
[0.20.4] - 2021-06-08
Fixed
- Add missing case for witness version.
[0.20.3] - 2021-05-17
Fixed
- Allow unknown inv types.
[0.20.2] - 2021-05-17
Fixed
- Allow unknown messages of zero length.
[0.20.1] - 2021-05-14
Fixed
- Correct case where binary search returned the wrong element.
[0.20.0] - 2021-02-22
Chaged
- Use bytes instead of binary or cereal.
[0.19.0] - 2021-01-25
Added
- Hashable instances for extended keys.
Changed
- Mnemonic passphrases now
Textinstead ofByteString.
Fixed
- Tests now pass for witness addresses.
[0.18.0] - 2020-12-10
Added
- Support SegWit addresses with version other than 0.
[0.17.6] - 2020-12-07
Added
- Serialize instances for
XPubKeyandXPrvKey.
[0.17.5] - 2020-12-03
Fixed
- Handle special case in block header binary search function.
[0.17.4] - 2020-12-03
Fixed
- Bounds check too restrictive in block header binary search function.
[0.17.3] - 2020-11-17
Changed
- Reduce minimum version of text package dependency.
[0.17.2] - 2020-11-17
Changed
- Update lists of seeds for all networks.
[0.17.1] - 2020-11-02
Changed
- Use the C-preprocessor to handle versions of
base16-bytestring
[0.17.0] - 2020-10-21
Added
- Support for Bitcoin Cash November 2020 hard fork.
- Functions to find block headers matching arbitrary sorted attributes.
Removed
- GenesisNode constructor for BlockNode type.
[0.15.0] - 2020-07-23
Added
- Add more test vectors
Changed
- stringToAddr renamed to textToAddr
- Move ScriptOutput to Standard.hs
- Move WIF encoding/decoding to Keys.hs
- (breaking) rename
OP_NOP2andOP_NOP3toOP_CHECKLOCKTIMEVERIFYandOP_CHECKSEQUENCEVERIFYresp. - Update to latest secp256k1 bindings.
[0.14.1] - 2020-06-14
Fixed
- Correct some Bitcoin Cash Testnet3 seeds.
- Add helpers for writing Data.Serialize and Data.Aeson identity tests
[0.14.0] - 2020-06-14
Changed
- Expose all modules for tests.
- Tests depend on library instead of having access to its source code.
- Use MIT license.
- Update seeds.
- Bump secp256k1-haskell dependency.
[0.13.6] - 2020-06-05
Changed
- Expose the Arbitrary test instances under Haskoin.Util.Arbitrary
[0.13.5] - 2020-05-16
Changed
- Provide meaningful JSON instances for most types.
[0.13.4] - 2020-05-14
Added
- Support for Bitcoin Cash May 2020 hard fork.
[0.13.3] - 2020-05-08
Changed
- Improve code and documentation organisation.
[0.13.2] - 2020-05-08
Changed
- Move all packages from Network.Haskoin namespace to Haskoin namespace.
- Expose all top-level modules directly.
[0.13.1] - 2020-05-06
Changed
- Faster JSON serialization.
[0.13.0] - 2020-05-06
Changed
- Consolidate all modules in Haskoin module.
Removed
- Deprecate Network.Haskoin namespace.
- Hide QuickCheck generators in test suite.
[0.12.0] - 2020-04-10
Added
- Support for signing segwit transactions.
- High-level representation of segwit v0 data and auxilliary functions.
Changed
- Adds handling of segwit signing parameters to transaction signing code.
[0.10.1] - 2020-02-08
Added
- Lower bound versions for some dependencies.
[0.10.0] - 2020-01-15
Added
- DeepSeq instances for all data types.
Changed
- There is no
SockAddrinsideNetworkAddressanymore.
[0.9.8] - 2020-01-01
Added
- Ord instance for
DerivPathI
[0.9.7] - 2019-12-04
Added
- JSON encoding/decoding for blocks.
Fixed
- Fix lowercase HRP test for Bech32.
[0.9.6] - 2019-10-29
Added
bloomRelevantUpdateimplementation for Bloom filters (thanks to @IlyasRidhuan).
Fixed
- Fix for Bech32 encoding (thanks to @pavel-main).
[0.9.5] - 2019-10-23
Added
- Expose functions added in 0.9.4.
[0.9.4] - 2019-10-23
Added
- Support for (P2SH-)P2WPKH addresses derived from extended keys.
Changed
- Change names of backwards-compatible P2SH-P2WPKH functions from 0.9.3.
[0.9.3] - 2019-10-22
Added
- Some support for P2WPKH-over-P2SH addresses.
[0.9.2] - 2019-10-09
Removed
- Disable unnecessary
-O2optimisation added in previous version.
Added
- Allow decoding unknown P2P messages.
[0.9.1] - 2019-10-02
Added
- Add a function to produce a structured signature over a transaction.
- Enable
-O2optimisations.
[0.9.0] - 2019-04-12
Changed
- Address conversion to string now defined for all inputs.
[0.8.4] - 2018-12-05
Added
- Add reward computation to block functions.
- Add PSBT BIP-174 types and functions.
[0.8.3] - 2018-12-04
Added
- Add reward halving interval parameter to network constants.
[0.8.2] - 2018-11-06
Added
- Recognize
OP_CHECKDATASIGandOP_CHECKDATASIGVERIFYopcodes.
[0.8.1] - 2018-10-13
Added
- Add instances of
HashableandGenericwhere possible.
[0.8.0] - 2018-10-13
Removed
- Remove
deepseqdependency. - Remove network constant reference from address and extended keys.
[0.7.0] - 2018-10-13
Added
- Add
Serializeinstance for network constants. - Add
Serializeinstance for addresses that includes network constants.
Changed
- Move functions related to addresses from
ScripttoAddressmodule.
[0.6.1] - 2018-10-09
Added
- Compatibility with latest GHC and base.
Changed
- Update minimum base to 4.9.
[0.6.0] - 2018-10-08
Changed
- Force initialization of addresses through smart constructor.
- Assume addresses are always valid when instantiated in code.
- Allow to provide unwrapped private keys to transaction signing functions.
[0.5.2] - 2018-09-10
Changed
- Make dependencies more specific.
[0.5.1] - 2018-09-10
Changed
- Remove some unneeded dependencies from
stack.yaml. - Change
secp256k1dependency tosecp256k1-haskell.
[0.5.0] - 2018-09-09
Added
- Support for Bitcoin Cash network block sychronization.
- Support for Bitcoin Cash signatures.
- Initial work on SegWit support.
- New version of
secp256k1bindings. - Block header validation.
- Support for RegTest networks on Bitcoin and Bitcoin Cash.
- Support for Bitcoin Cash Testnet3 Network.
- Support for new Haskoin Wallet.
- Minikey decoding for Casascius coins.
- New tests for various networks and new features.
- Added
CHANGELOG.mdfile. - Support for SegWit addresses.
- Support for CashAddr addresses.
Changed
- Use of hpack
package.yamlfile to auto-generate Cabal file. - Removal of dependency version limits, relying on
stack.yamlinstead. - Tests moved to
hspec. - New documentation.
- Updated
.gitignore. - Renamed network constants to use same style for BTC and BCH.
- Network constants must be passed explicitly.
- Target LTS Haskell 12.9.
Removed
- Removed
.stylish-haskell.yamlfiles. - Removed old
haskoin-nodeandhaskoin-walletpackages from main repository. - Removed support for non-strict signatures and related tests.
- Removed script evaluator and related tests.