script

Primitive (Bitcoin) Script support for Haskell.
git clone git://git.ppad.tech/script.git
Log | Files | Refs | LICENSE

commit a387559048a6820bdcbdda48a6ea3121bb62ad9f
parent 7df83c62372cecc47be0cd67ef9022362bc73e39
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 19 Jan 2025 17:53:09 +0400

lib: remove script hashes

These feel a little out of place at present; we're mostly following
rust-bitcoin, which includes them here, so they may get reinserted at
some point if I better-understand why they should be stuffed in this
module rather than another. For now I'm biasing towards simplicity and
keeping them out.

Diffstat:
Mbench/Main.hs | 1-
Mflake.lock | 585+------------------------------------------------------------------------------
Mflake.nix | 51+++++----------------------------------------------
Mlib/Bitcoin/Prim/Script.hs | 82+------------------------------------------------------------------------------
Mppad-script.cabal | 16+++++-----------
Mtest/Main.hs | 53++++++++++-------------------------------------------
6 files changed, 24 insertions(+), 764 deletions(-)

diff --git a/bench/Main.hs b/bench/Main.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-unused-imports #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-} diff --git a/flake.lock b/flake.lock @@ -18,132 +18,6 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_4": { - "inputs": { - "systems": "systems_4" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_5": { - "inputs": { - "systems": "systems_5" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_6": { - "inputs": { - "systems": "systems_6" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_7": { - "inputs": { - "systems": "systems_7" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_8": { - "inputs": { - "systems": "systems_8" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1725910328, @@ -160,118 +34,6 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_5": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_6": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_7": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_8": { - "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "ppad-base16": { "inputs": { "flake-utils": "flake-utils", @@ -292,253 +54,17 @@ "url": "git://git.ppad.tech/base16.git" } }, - "ppad-base58": { - "inputs": { - "flake-utils": [ - "ppad-base58", - "ppad-sha256", - "flake-utils" - ], - "nixpkgs": [ - "ppad-base58", - "ppad-sha256", - "nixpkgs" - ], - "ppad-sha256": "ppad-sha256" - }, - "locked": { - "lastModified": 1737143175, - "narHash": "sha256-Soj/qor6EFXgWjqDgO1dJGh63+4zqxyIV4+LyDmGnjY=", - "ref": "master", - "rev": "08a271a7faf8fe4d5640e90ecabe107dbdda9dbb", - "revCount": 15, - "type": "git", - "url": "git://git.ppad.tech/base58.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/base58.git" - } - }, - "ppad-bech32": { - "inputs": { - "flake-utils": "flake-utils_3", - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1736713898, - "narHash": "sha256-NpTs+/zBwn6ibJekGOD1XsQSxjC9gx0vr4VBRJtVcm8=", - "ref": "master", - "rev": "a6e777c02e55e48442dbe5fa46a58d321570b6a6", - "revCount": 59, - "type": "git", - "url": "git://git.ppad.tech/bech32.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/bech32.git" - } - }, - "ppad-hmac-drbg": { - "inputs": { - "flake-utils": [ - "ppad-secp256k1", - "ppad-hmac-drbg", - "ppad-sha256", - "flake-utils" - ], - "nixpkgs": [ - "ppad-secp256k1", - "ppad-hmac-drbg", - "ppad-sha256", - "nixpkgs" - ], - "ppad-sha256": "ppad-sha256_2", - "ppad-sha512": "ppad-sha512" - }, - "locked": { - "lastModified": 1731565487, - "narHash": "sha256-ARrXvAIAaA3H1KuvZxWywh72s5KTIZ+BUHMZ3Rgp9dA=", - "ref": "master", - "rev": "e7ec90dfcbb3749148da69e3919c3c14880f92be", - "revCount": 42, - "type": "git", - "url": "git://git.ppad.tech/hmac-drbg.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/hmac-drbg.git" - } - }, - "ppad-ripemd160": { - "inputs": { - "flake-utils": "flake-utils_4", - "nixpkgs": "nixpkgs_4" - }, - "locked": { - "lastModified": 1737140550, - "narHash": "sha256-tmbu+Z33FC2VnAMOLYcyjBQK43BNXcTSKLto402Wd1I=", - "ref": "master", - "rev": "7ee1639639d7e7c85ebaa35a1da8fb0a0628ab8b", - "revCount": 17, - "type": "git", - "url": "git://git.ppad.tech/ripemd160.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/ripemd160.git" - } - }, - "ppad-secp256k1": { - "inputs": { - "flake-utils": [ - "ppad-secp256k1", - "ppad-sha256", - "flake-utils" - ], - "nixpkgs": [ - "ppad-secp256k1", - "ppad-sha256", - "nixpkgs" - ], - "ppad-hmac-drbg": "ppad-hmac-drbg", - "ppad-sha256": "ppad-sha256_3" - }, - "locked": { - "lastModified": 1737143218, - "narHash": "sha256-g7SDUf/gS5cDMQLA0y/3cecAoRJctqovyyILiuWzmXY=", - "ref": "master", - "rev": "a456a84a32574eec829bbb152bc07093e2039a88", - "revCount": 133, - "type": "git", - "url": "git://git.ppad.tech/secp256k1.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/secp256k1.git" - } - }, - "ppad-sha256": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1737140591, - "narHash": "sha256-jsIDUQuxf7iv/r0Yxn6yCMRVhgq7Sk0SNjwdQ9GhASU=", - "ref": "master", - "rev": "c1d5aa2a01d183f2ef71573cdba4591ce0f7b59c", - "revCount": 86, - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - } - }, - "ppad-sha256_2": { - "inputs": { - "flake-utils": "flake-utils_5", - "nixpkgs": "nixpkgs_5" - }, - "locked": { - "lastModified": 1726493969, - "narHash": "sha256-toJ5A5+0/xijqVELjXfE3AdWV24B672R16JWq+gKLFk=", - "ref": "master", - "rev": "e92f4e13d6afa962109e76d35c6fcb38045e28ed", - "revCount": 69, - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - } - }, - "ppad-sha256_3": { - "inputs": { - "flake-utils": "flake-utils_7", - "nixpkgs": "nixpkgs_7" - }, - "locked": { - "lastModified": 1737140591, - "narHash": "sha256-jsIDUQuxf7iv/r0Yxn6yCMRVhgq7Sk0SNjwdQ9GhASU=", - "ref": "master", - "rev": "c1d5aa2a01d183f2ef71573cdba4591ce0f7b59c", - "revCount": 86, - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - } - }, - "ppad-sha256_4": { - "inputs": { - "flake-utils": "flake-utils_8", - "nixpkgs": "nixpkgs_8" - }, - "locked": { - "lastModified": 1737140591, - "narHash": "sha256-jsIDUQuxf7iv/r0Yxn6yCMRVhgq7Sk0SNjwdQ9GhASU=", - "ref": "master", - "rev": "c1d5aa2a01d183f2ef71573cdba4591ce0f7b59c", - "revCount": 86, - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/sha256.git" - } - }, - "ppad-sha512": { - "inputs": { - "flake-utils": "flake-utils_6", - "nixpkgs": "nixpkgs_6" - }, - "locked": { - "lastModified": 1728147405, - "narHash": "sha256-KQ56eDSkXNvy7VXPC2+L64b94nqYjxU0JkZxKFtIePA=", - "ref": "master", - "rev": "c454a32a4a39b0e18601b8139a12ae895c9b631e", - "revCount": 8, - "type": "git", - "url": "git://git.ppad.tech/sha512.git" - }, - "original": { - "ref": "master", - "type": "git", - "url": "git://git.ppad.tech/sha512.git" - } - }, "root": { "inputs": { "flake-utils": [ - "ppad-sha256", + "ppad-base16", "flake-utils" ], "nixpkgs": [ - "ppad-sha256", + "ppad-base16", "nixpkgs" ], - "ppad-base16": "ppad-base16", - "ppad-base58": "ppad-base58", - "ppad-bech32": "ppad-bech32", - "ppad-ripemd160": "ppad-ripemd160", - "ppad-secp256k1": "ppad-secp256k1", - "ppad-sha256": "ppad-sha256_4" + "ppad-base16": "ppad-base16" } }, "systems": { @@ -555,111 +81,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_4": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_5": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_6": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_7": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_8": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix @@ -1,45 +1,19 @@ { - description = "Haskell Bitcoin primitives."; + description = "Primitive Script support for Haskell."; inputs = { - ppad-sha256 = { - type = "git"; - url = "git://git.ppad.tech/sha256.git"; - ref = "master"; - }; ppad-base16 = { type = "git"; url = "git://git.ppad.tech/base16.git"; ref = "master"; }; - ppad-base58 = { - type = "git"; - url = "git://git.ppad.tech/base58.git"; - ref = "master"; - }; - ppad-bech32 = { - type = "git"; - url = "git://git.ppad.tech/bech32.git"; - ref = "master"; - }; - ppad-ripemd160 = { - type = "git"; - url = "git://git.ppad.tech/ripemd160.git"; - ref = "master"; - }; - ppad-secp256k1 = { - type = "git"; - url = "git://git.ppad.tech/secp256k1.git"; - ref = "master"; - }; - flake-utils.follows = "ppad-sha256/flake-utils"; - nixpkgs.follows = "ppad-sha256/nixpkgs"; + flake-utils.follows = "ppad-base16/flake-utils"; + nixpkgs.follows = "ppad-base16/nixpkgs"; }; outputs = { self, nixpkgs, flake-utils - , ppad-sha256, ppad-ripemd160 - , ppad-bech32, ppad-base58, ppad-base16 - , ppad-secp256k1 }: + , ppad-base16 + }: flake-utils.lib.eachDefaultSystem (system: let lib = "ppad-script"; @@ -47,27 +21,12 @@ pkgs = import nixpkgs { inherit system; }; hlib = pkgs.haskell.lib; - sha256 = ppad-sha256.packages.${system}.default; - bech32 = ppad-bech32.packages.${system}.default; base16 = ppad-base16.packages.${system}.default; - base58 = ppad-base58.packages.${system}.default; - ripemd160 = ppad-ripemd160.packages.${system}.default; - secp256k1 = ppad-secp256k1.packages.${system}.default; hpkgs = pkgs.haskell.packages.ghc981.extend (new: old: { - ppad-sha256 = sha256; - ppad-bech32 = bech32; ppad-base16 = base16; - ppad-base58 = base58; - ppad-ripemd160 = ripemd160; - ppad-secp256k1 = secp256k1; ${lib} = old.callCabal2nixWithOptions lib ./. "--enable-profiling" { - ppad-sha256 = new.ppad-sha256; - ppad-bech32 = new.ppad-bech32; ppad-base16 = new.ppad-base16; - ppad-base58 = new.ppad-base58; - ppad-ripemd160 = new.ppad-ripemd160; - ppad-secp256k1 = new.ppad-secp256k1; }; }); diff --git a/lib/Bitcoin/Prim/Script.hs b/lib/Bitcoin/Prim/Script.hs @@ -16,13 +16,6 @@ -- including abstract syntax, 'ByteArray', and base16-encoded -- 'ByteString' versions, as well as fast conversion utilities for -- working with them. --- --- Also included are the 'ScriptHash' and --- 'WitnessScriptHash' types representing --- [BIP16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki) --- redeem script and --- [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) --- witness script hashes. module Bitcoin.Prim.Script ( -- * Script and Script Terms @@ -36,22 +29,12 @@ module Bitcoin.Prim.Script ( , to_script , from_script - -- * Script Hashes - , ScriptHash - , WitnessScriptHash - , to_scripthash - , to_witness_scripthash - -- for testing etc. - , _MAX_REDEEM_SCRIPT_SIZE - , _MAX_WITNESS_SCRIPT_SIZE , ba_to_bs , bs_to_ba ) where import Control.Monad (guard) -import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 -import qualified Crypto.Hash.SHA256 as SHA256 import qualified Data.Bits as B import Data.Bits ((.&.), (.|.)) import qualified Data.ByteString as BS @@ -64,16 +47,6 @@ import Data.Word (Word8, Word16, Word32) import GHC.ForeignPtr import System.IO.Unsafe --- constants ------------------------------------------------------------------ - --- max redeem script size for a p2sh output -_MAX_REDEEM_SCRIPT_SIZE :: Int -_MAX_REDEEM_SCRIPT_SIZE = 520 - --- max witness script size -_MAX_WITNESS_SCRIPT_SIZE :: Int -_MAX_WITNESS_SCRIPT_SIZE = 10_000 - -- utilities ------------------------------------------------------------------ fi :: (Num a, Integral b) => b -> a @@ -109,7 +82,7 @@ hilo b = lo = BU.unsafeIndex hex_charset (fi b .&. 0b00001111) in (hi, lo) --- script, hash, and term representation -------------------------------------- +-- script and term representation --------------------------------------------- -- | A Script program, represented as a 'ByteArray'. newtype Script = Script BA.ByteArray @@ -127,41 +100,6 @@ instance Show Term where let (hi, lo) = hilo w in "0x" <> (C.chr (fi hi) : C.chr (fi lo) : []) --- XX we're following rust-bitcoin, but should these really be included --- in this particular library? - --- | A p2sh scripthash, i.e. HASH160 of a 'Script'. --- --- The underlying 'Script' is guaranteed to be at most 520 bytes, to --- guarantee in-principle p2sh spendability per --- [BIP16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki). -newtype ScriptHash = ScriptHash BS.ByteString - deriving Eq - -instance Show ScriptHash where - show (ScriptHash bs) = "ScriptHash 0x" <> go bs where - go b = case BS.uncons b of - Nothing -> mempty - Just (h, t) -> - let (hi, lo) = hilo h - in C.chr (fi hi) : C.chr (fi lo) : go t - --- | A p2wsh witness scripthash, i.e. SHA256 of a 'Script'. --- --- The underlying 'Script' is guaranteed to be at most 10,000 bytes, to --- guarantee in-principle p2wsh spendability per --- [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki). -newtype WitnessScriptHash = WitnessScriptHash BS.ByteString - deriving Eq - -instance Show WitnessScriptHash where - show (WitnessScriptHash bs) = "WitnessScriptHash 0x" <> go bs where - go b = case BS.uncons b of - Nothing -> mempty - Just (h, t) -> - let (hi, lo) = hilo h - in C.chr (fi hi) : C.chr (fi lo) : go t - -- script conversions --------------------------------------------------------- -- | Convert a 'Script' to a base16-encoded ByteString. @@ -234,24 +172,6 @@ from_script (Script bs) = go 0 where _ -> go (succ j) --- script hashes -------------------------------------------------------------- - --- | Convert a 'Script' to a 'ScriptHash', ensuring that it doesn't exceed --- the maximum redeemscript size. -to_scripthash :: Script -> Maybe ScriptHash -to_scripthash (Script bs) - | BA.sizeofByteArray bs > _MAX_REDEEM_SCRIPT_SIZE = Nothing - | otherwise = Just $! - ScriptHash (RIPEMD160.hash (SHA256.hash (ba_to_bs bs))) - --- | Convert a 'Script' to a 'WitnessScriptHash', ensuring that it doesn't --- the maximum witness script size. -to_witness_scripthash :: Script -> Maybe WitnessScriptHash -to_witness_scripthash (Script bs) - | BA.sizeofByteArray bs > _MAX_WITNESS_SCRIPT_SIZE = Nothing - | otherwise = Just $! - WitnessScriptHash (SHA256.hash (ba_to_bs bs)) - -- opcodes and utilities ------------------------------------------------------ -- | Primitive opcodes. diff --git a/ppad-script.cabal b/ppad-script.cabal @@ -1,7 +1,7 @@ cabal-version: 3.0 name: ppad-script version: 0.1.0 -synopsis: Script and utilities. +synopsis: Primitive (Bitcoin) Script support. license: MIT license-file: LICENSE author: Jared Tobin @@ -11,7 +11,10 @@ build-type: Simple tested-with: GHC == { 9.8.1 } extra-doc-files: CHANGELOG description: - A representation of Script, as well as utilities for working with it. + Representations for [Script](https://en.bitcoin.it/wiki/Script), + including abstract syntax, 'ByteArray', and base16-encoded + 'ByteString' versions, as well as fast conversion utilities for + working with them. source-repository head type: git @@ -29,11 +32,6 @@ library , bytestring >= 0.9 && < 0.13 , primitive >= 0.8 && < 0.10 , ppad-base16 >= 0.1 && < 0.2 - , ppad-base58 >= 0.1 && < 0.2 - , ppad-bech32 >= 0.2 && < 0.3 - , ppad-ripemd160 >= 0.1 && < 0.2 - , ppad-secp256k1 >= 0.2.1 && < 0.3 - , ppad-sha256 >= 0.2 && < 0.3 test-suite script-tests type: exitcode-stdio-1.0 @@ -48,11 +46,7 @@ test-suite script-tests , base , bytestring , ppad-base16 - , ppad-base58 , ppad-script - , ppad-ripemd160 - , ppad-secp256k1 - , ppad-sha256 , primitive , tasty , tasty-hunit diff --git a/test/Main.hs b/test/Main.hs @@ -1,5 +1,4 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} -{-# OPTIONS_GHC -fno-warn-unused-imports #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} @@ -7,12 +6,9 @@ module Main where import Bitcoin.Prim.Script -import qualified Crypto.Hash.SHA256 as SHA256 -import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16 import qualified Data.Primitive.ByteArray as BA -import Data.Word (Word8) import Test.Tasty import qualified Test.Tasty.HUnit as H import qualified Test.Tasty.QuickCheck as Q @@ -53,29 +49,18 @@ newtype RawScript = RawScript Script instance Q.Arbitrary RawScript where arbitrary = fmap (RawScript . Script) Q.arbitrary --- XX better generators for valid and invalid redeemscripts would be nice. +-- XX better generators would be nice. -- pushdata generation needs to be handled carefully. -newtype ValidRedeemScript = ValidRedeemScript Script +newtype NonPathologicalScript = NonPathologicalScript Script deriving (Eq, Show) -instance Q.Arbitrary ValidRedeemScript where +instance Q.Arbitrary NonPathologicalScript where arbitrary = do - l <- Q.chooseInt (0, _MAX_REDEEM_SCRIPT_SIZE) + l <- Q.chooseInt (0, 1_024) -- pushdata must be added with care; easy to blow up quickcheck - bs <- fmap BS.pack (Q.vectorOf l (Q.chooseEnum (100, 255))) - pure (ValidRedeemScript (Script (bs_to_ba bs))) - --- too large -newtype InvalidRedeemScript = InvalidRedeemScript Script - deriving (Eq, Show) - -instance Q.Arbitrary InvalidRedeemScript where - arbitrary = do - l <- Q.chooseInt (_MAX_REDEEM_SCRIPT_SIZE + 1, 20_000) - -- pushdata must be added with care; easy to blow up quickcheck - bs <- fmap BS.pack (Q.vectorOf l (Q.chooseEnum (100, 255))) - pure (InvalidRedeemScript (Script (bs_to_ba bs))) + bs <- fmap BS.pack (Q.vectorOf l (Q.chooseEnum (80, 255))) + pure (NonPathologicalScript (Script (bs_to_ba bs))) -- properties ----------------------------------------------------------------- @@ -101,25 +86,13 @@ to_base16_inverts_from_base16 (HexBS bs) = -- we can only use 'from_script' on non-pathological scripts -- --- note the converse is not true -to_script_inverts_from_script :: ValidRedeemScript -> Bool -to_script_inverts_from_script (ValidRedeemScript s) = +-- note the converse (from_script . to_script ~ id) is not true +to_script_inverts_from_script :: NonPathologicalScript -> Bool +to_script_inverts_from_script (NonPathologicalScript s) = let !terms = from_script s !script = to_script terms in script == s -valid_redeem_script_produces_hash :: ValidRedeemScript -> Bool -valid_redeem_script_produces_hash (ValidRedeemScript s) = - case to_scripthash s of - Just {} -> True - _ -> False - -invalid_redeem_script_doesnt_produce_hash :: InvalidRedeemScript -> Bool -invalid_redeem_script_doesnt_produce_hash (InvalidRedeemScript s) = - case to_scripthash s of - Nothing -> True - _ -> False - -- assertions ----------------------------------------------------------------- decodes_to :: BS.ByteString -> [Term] -> H.Assertion @@ -221,13 +194,7 @@ main = defaultMain $ , Q.testProperty "to_script . from_script ~ id" $ Q.withMaxSuccess 1000 to_script_inverts_from_script ] - , testGroup "hashes" [ - Q.testProperty "valid redeem script produces scripthash" $ - Q.withMaxSuccess 100 valid_redeem_script_produces_hash - , Q.testProperty "invalid redeem script doesn't produce scripthash" $ - Q.withMaxSuccess 100 invalid_redeem_script_doesnt_produce_hash - ] - ] + ] , testGroup "unit tests" [ H.testCase "p2pkh script decodes to expected terms" p2pkh_script_decodes_as_expected