diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml
new file mode 100644
index 0000000000..96f3735504
--- /dev/null
+++ b/.cargo/mutants.toml
@@ -0,0 +1,34 @@
+additional_cargo_args = ["--all-features"]
+examine_globs = ["units/src/**/*.rs", "primitives/src/**/*.rs"]
+exclude_globs = [
+ "units/src/amount/verification.rs" # kani tests
+]
+exclude_re = [
+ "impl Debug",
+ "impl Arbitrary",
+ "impl Display",
+ ".*Error",
+ "deserialize", # Skip serde mutation tests
+ "Iterator", # Mutating operations in an iterator can result in an infinite loop
+
+ # ----------------------------------Crate-specific exclusions----------------------------------
+ # Units
+ # src/amount/mod.rs
+ "parse_signed_to_satoshi", # Can't kill all mutants since there is no denomination smaller than Satoshi
+ "fmt_satoshi_in", # Related to formatting/display
+ "dec_width", # Replacing num /= 10 with num %=10 in a loop causes a timeout due to infinite loop
+ # src/locktime/relative.rs
+ "Time::to_consensus_u32", # Mutant from replacing | with ^, this returns the same value since the XOR is taken against the u16 with an all-zero bitmask
+ "FeeRate::fee_vb", # Deprecated
+ "FeeRate::fee_wu", # Deprecated
+
+ # primitives
+ "Sequence::from_512_second_intervals", # Mutant from replacing | with ^, this returns the same value since the XOR is taken against the u16 with an all-zero bitmask
+ "Opcode::classify", # Not possible to kill all mutants without individually checking every opcode classification
+ "Block::cached_witness_root", # Skip getters
+ "Block::transactions", # Skip getters
+ "Script::to_bytes", # Deprecated
+ "decode_cursor", # Mutating operations in decode_cursor can result in an infinite loop
+ "fmt_debug", # Mutants from formatting/display changes
+ "fmt_debug_pretty", # Mutants from formatting/display changes
+]
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index 54da70c567..f93b40cd1c 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -22,11 +22,11 @@ Run from rust.yml unless stated otherwise. Unfortunately we are now exceeding th
8. `Docs`
9. `Docsrs`
10. `Bench`
-11. `ASAN`
-12. `WASM`
-13. `Arch32bit`
-14. `Cross`
-15. `Embedded`
+11. `Arch32bit`
+12. `Cross`
+13. `Embedded`
+14. `ASAN`
+15. `WASM`
16. `Kani`
17. `Coveralls` - run by `coveralls.yml`
18. `release` - run by `release.yml`
diff --git a/.github/workflows/cargo-semver-checks-version b/.github/workflows/cargo-semver-checks-version
index 93d4c1ef06..9b0025a785 100644
--- a/.github/workflows/cargo-semver-checks-version
+++ b/.github/workflows/cargo-semver-checks-version
@@ -1 +1 @@
-0.36.0
+0.40.0
diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml
index a3d37cd3c5..688481ef81 100644
--- a/.github/workflows/coveralls.yml
+++ b/.github/workflows/coveralls.yml
@@ -7,7 +7,7 @@ name: Code coverage with llvm-cov
jobs:
Coveralls:
name: Code coverage - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
diff --git a/.github/workflows/cron-daily-fuzz.yml b/.github/workflows/cron-daily-fuzz.yml
index 2686f70ffd..253be2af24 100644
--- a/.github/workflows/cron-daily-fuzz.yml
+++ b/.github/workflows/cron-daily-fuzz.yml
@@ -11,7 +11,7 @@ on:
jobs:
fuzz:
if: ${{ !github.event.act }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -59,7 +59,7 @@ jobs:
echo "Using RUSTFLAGS $RUSTFLAGS"
cd fuzz && ./fuzz.sh "${{ matrix.fuzz_target }}"
- run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }}
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
with:
name: executed_${{ matrix.fuzz_target }}
path: executed_${{ matrix.fuzz_target }}
@@ -67,10 +67,10 @@ jobs:
verify-execution:
if: ${{ !github.event.act }}
needs: fuzz
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
- name: Display structure of downloaded files
run: ls -R
- run: find executed_* -type f -exec cat {} + | sort > executed
diff --git a/.github/workflows/cron-daily-kani.yml b/.github/workflows/cron-daily-kani.yml
index dff1e54be8..61ef6e3957 100644
--- a/.github/workflows/cron-daily-kani.yml
+++ b/.github/workflows/cron-daily-kani.yml
@@ -5,7 +5,7 @@ on:
- cron: '59 23 * * *' # midnight every day.
jobs:
run-kani:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
steps:
- name: 'Checkout your code.'
uses: actions/checkout@v4
diff --git a/.github/workflows/cron-weekly-cargo-mutants.yml b/.github/workflows/cron-weekly-cargo-mutants.yml
new file mode 100644
index 0000000000..74ec346299
--- /dev/null
+++ b/.github/workflows/cron-weekly-cargo-mutants.yml
@@ -0,0 +1,41 @@
+name: Weekly cargo-mutants
+on:
+ schedule:
+ - cron: "0 0 * * 0" # runs weekly on Sunday at 00:00
+ workflow_dispatch: # allows manual triggering
+jobs:
+ cargo-mutants:
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: taiki-e/install-action@v2
+ with:
+ tool: cargo-mutants
+ - run: cargo mutants --in-place --no-shuffle
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: mutants.out
+ path: mutants.out
+ - name: Check for new mutants
+ if: always()
+ run: |
+ if [ -s mutants.out/missed.txt ]; then
+ echo "New missed mutants found"
+ gh issue create \
+ --title "New Mutants Found" \
+ --body "$(cat <> $GITHUB_ENV
+ else
+ echo "No new mutants found"
+ echo "create_issue=false" >> $GITHUB_ENV
+ fi
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/cron-weekly-rustfmt.yml b/.github/workflows/cron-weekly-rustfmt.yml
index 81a5323fb1..d35073c4a6 100644
--- a/.github/workflows/cron-weekly-rustfmt.yml
+++ b/.github/workflows/cron-weekly-rustfmt.yml
@@ -6,7 +6,7 @@ on:
jobs:
format:
name: Nightly rustfmt
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
diff --git a/.github/workflows/cron-weekly-update-cargo-semver-checks.yml b/.github/workflows/cron-weekly-update-cargo-semver-checks.yml
index c3c86f9035..75f706a1cc 100644
--- a/.github/workflows/cron-weekly-update-cargo-semver-checks.yml
+++ b/.github/workflows/cron-weekly-update-cargo-semver-checks.yml
@@ -6,7 +6,7 @@ on:
jobs:
format:
name: Update cargo-semver-checks
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Update semver-checks to use latest crates.io published version
diff --git a/.github/workflows/cron-semi-weekly-update-nightly.yml b/.github/workflows/cron-weekly-update-nightly.yml
similarity index 95%
rename from .github/workflows/cron-semi-weekly-update-nightly.yml
rename to .github/workflows/cron-weekly-update-nightly.yml
index 8cfb698854..b31ad20cc0 100644
--- a/.github/workflows/cron-semi-weekly-update-nightly.yml
+++ b/.github/workflows/cron-weekly-update-nightly.yml
@@ -1,12 +1,12 @@
name: Update Nightly rustc
on:
schedule:
- - cron: "5 0 * * 1,4" # runs every Monday and Thursday at 00:05
+ - cron: "5 0 * * 6" # Saturday at 00:05
workflow_dispatch: # allows manual triggering
jobs:
format:
name: Update nightly rustc
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
diff --git a/.github/workflows/cron-weekly-update-stable.yml b/.github/workflows/cron-weekly-update-stable.yml
index 42f49cc772..456ccd9ae4 100644
--- a/.github/workflows/cron-weekly-update-stable.yml
+++ b/.github/workflows/cron-weekly-update-stable.yml
@@ -6,7 +6,7 @@ on:
jobs:
format:
name: Update stable rustc
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
diff --git a/.github/workflows/gh-release.yml b/.github/workflows/gh-release.yml
index 69ea930c5e..86014a98cf 100644
--- a/.github/workflows/gh-release.yml
+++ b/.github/workflows/gh-release.yml
@@ -7,7 +7,7 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: ncipollo/release-action@v1
diff --git a/.github/workflows/manage-pr.yml b/.github/workflows/manage-pr.yml
index 49407eead0..3e92b33f94 100644
--- a/.github/workflows/manage-pr.yml
+++ b/.github/workflows/manage-pr.yml
@@ -7,7 +7,7 @@ jobs:
permissions:
contents: read
pull-requests: write
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- name: Checkout master
uses: actions/checkout@v4
diff --git a/.github/workflows/miri.yml b/.github/workflows/miri.yml
index 38eaa4afd0..ed9f05a702 100644
--- a/.github/workflows/miri.yml
+++ b/.github/workflows/miri.yml
@@ -11,7 +11,7 @@ name: Miri
jobs:
Miri:
name: Miri
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cd2a872550..1241ecf9ba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,7 +12,7 @@ name: Release
jobs:
release:
name: Release - dry-run
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- name: Checkout Crate
uses: actions/checkout@v4
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index cf06c57ae8..ea83c99cbc 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -10,7 +10,7 @@ name: Continuous integration
jobs:
Prepare:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
outputs:
nightly_version: ${{ steps.read_toolchain.outputs.nightly_version }}
steps:
@@ -22,7 +22,7 @@ jobs:
Stable: # 2 jobs, one per manifest.
name: Test - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@stable
@@ -46,7 +46,7 @@ jobs:
Nightly: # 2 jobs, one per manifest.
name: Test - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -58,7 +58,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@v1
@@ -71,7 +71,7 @@ jobs:
MSRV: # 2 jobs, one per manifest.
name: Test - MSRV toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -83,7 +83,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@stable
@@ -97,7 +97,7 @@ jobs:
Lint:
name: Lint - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -109,7 +109,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@v1
@@ -124,7 +124,7 @@ jobs:
Docs:
name: Docs - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -136,7 +136,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@stable
@@ -148,7 +148,7 @@ jobs:
Docsrs:
name: Docs - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -160,7 +160,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@v1
@@ -174,7 +174,7 @@ jobs:
Bench:
name: Bench - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -186,7 +186,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: rust-bitcoin/rust-bitcoin-maintainer-tools
- ref: 4f17a059a2f57c1b99b7c240a1467a5c0acebdc3
+ ref: c3324024ced9bb1eb854397686919c3ff7d97e1e
path: maintainer-tools
- name: "Select toolchain"
uses: dtolnay/rust-toolchain@v1
@@ -199,7 +199,7 @@ jobs:
Arch32bit:
name: Test 32-bit version
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
@@ -217,7 +217,7 @@ jobs:
Cross:
name: Cross test - stable toolchain
if: ${{ !github.event.act }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
@@ -233,7 +233,7 @@ jobs:
Embedded:
name: Embedded - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
env:
RUSTFLAGS: "-C link-arg=-Tlink.x"
CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER: "qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -nographic -semihosting-config enable=on,target=native -kernel"
@@ -261,7 +261,7 @@ jobs:
ASAN: # hashes crate only.
name: ASAN - nightly toolchain
needs: Prepare
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -282,7 +282,7 @@ jobs:
WASM: # hashes crate only.
name: WASM - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
# Note we do not use the recent lock file for wasm testing.
@@ -296,7 +296,7 @@ jobs:
Kani:
name: Kani codegen - stable toolchain
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
@@ -304,4 +304,3 @@ jobs:
uses: model-checking/kani-github-action@v1.1
with:
args: "--only-codegen"
-
diff --git a/.github/workflows/semver-checks-pr-label.yml b/.github/workflows/semver-checks-pr-label.yml
index 56ce356de1..c7f9b1c66c 100644
--- a/.github/workflows/semver-checks-pr-label.yml
+++ b/.github/workflows/semver-checks-pr-label.yml
@@ -8,7 +8,7 @@ name: Check semver breaks - Label and Comment PR
jobs:
Download:
name: Download, Unzip and Add Labels/Comments
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: write
diff --git a/.github/workflows/semver-checks.yml b/.github/workflows/semver-checks.yml
index 93f37d2692..32d01b6b28 100644
--- a/.github/workflows/semver-checks.yml
+++ b/.github/workflows/semver-checks.yml
@@ -6,7 +6,7 @@ name: Check semver breaks
jobs:
PR:
name: PR Semver - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
@@ -45,7 +45,7 @@ jobs:
Feature:
name: Non additive cargo features - stable toolchain
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index 8577c05033..dea02c3a04 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -6,7 +6,7 @@ on:
jobs:
shellcheck:
name: Shellcheck
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
diff --git a/.github/workflows/stable-version b/.github/workflows/stable-version
index 71fae54fb2..8510ffad03 100644
--- a/.github/workflows/stable-version
+++ b/.github/workflows/stable-version
@@ -1 +1 @@
-1.82.0
+1.85.1
diff --git a/.gitignore b/.gitignore
index 6055dd8637..48c9cedca4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ hashes/target
# Test artifacts
bitcoin/dep_test
+mutants.out*
# Fuzz artifacts
hfuzz_target
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d9139bacc9..98107dc615 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -130,7 +130,7 @@ adhere to the ideas presented in the following two blog posts:
Whenever any part of your code wants to mention the version number the code will
be released in, primarily in deprecation notices, you should use the string
`TBD` (verbatim), so that the release preparation script can detect the
-change and the correct version number can be filled in in preparation of the
+change and the correct version number can be filled in preparation of the
release.
```rust
@@ -145,6 +145,21 @@ test out the patch set and opine on the technical merits of the patch. Please,
first review PR on the conceptual level before focusing on code style or
grammar fixes.
+### API changes
+
+The API of the following crates is almost stable. Changing it is supposed to be non-trivial. To
+assist in this effort ll PRs that change the public API of any these crates must include a patch to
+the `api/` text files. This should be a separate final patch to the PR that is the diff created by
+running `just check-api`.
+
+- `hashes`
+- `io`
+- `primitives`
+- `units`
+
+Check the [API text files](api/README.md) for more information
+on how to install the dependencies and create the text files.
+
### Repository maintainers
Pull request merge requirements:
@@ -194,12 +209,9 @@ in Bitcoin Core, with the following exceptions:
If your change requires a dependency to be upgraded you must do the following:
1. Modify `Cargo.toml`
-2. Copy `Cargo-minimal.lock` to `Cargo.lock`
-3. Trigger cargo to update the required entries in the lock file - use `--precise` using the minimum version number that works
-4. Test your change
-5. Copy `Cargo.lock` to `Cargo-minimal.lock`
-6. Update `Cargo-recent.lock` if it is also behind
-7. Commit both lock files together with `Cargo.toml` and your code changes
+2. Run `just update-lock-files`, if necessary install `just` first with `cargo install just`.
+3. Test your change
+4. Commit both `Cargo-minimal.lock` and `Cargo-recent.lock` together with `Cargo.toml` and your code changes
### Unsafe code
@@ -348,7 +360,7 @@ All errors that live in an `error` module (eg, `foo/error.rs`) and appear in a p
With respect to `expect` messages, they should follow the
[Rust standard library guidelines](https://doc.rust-lang.org/std/option/enum.Option.html#recommended-message-style).
-More specifically, `expect` messages should be used to to describe the reason
+More specifically, `expect` messages should be used to describe the reason
you expect the operation to succeed.
For example, this `expect` message clearly states why the operation should succeed:
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index 5ec37a0166..f1b5097fa9 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -2,17 +2,11 @@
# It is not intended for manual editing.
version = 3
-[[package]]
-name = "anyhow"
-version = "1.0.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
-
[[package]]
name = "arbitrary"
-version = "1.0.1"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arrayvec"
@@ -22,10 +16,10 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "base58ck"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"bitcoin-internals",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
]
@@ -53,7 +47,7 @@ dependencies = [
[[package]]
name = "bitcoin"
-version = "0.33.0-alpha"
+version = "0.33.0-alpha.0"
dependencies = [
"arbitrary",
"base58ck",
@@ -61,15 +55,13 @@ dependencies = [
"bech32",
"bincode",
"bitcoin-internals",
- "bitcoin-io",
+ "bitcoin-io 0.2.0",
"bitcoin-primitives",
"bitcoin-units",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"bitcoinconsensus",
"hex-conservative 0.3.0",
"hex_lit",
- "mutagen",
- "ordered",
"secp256k1",
"serde",
"serde_json",
@@ -100,11 +92,18 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "bitcoin-io"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
+
[[package]]
name = "bitcoin-io"
version = "0.2.0"
dependencies = [
"bitcoin-internals",
+ "bitcoin_hashes 0.16.0",
]
[[package]]
@@ -114,12 +113,9 @@ dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
- "bitcoin-io",
"bitcoin-units",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
- "mutagen",
- "ordered",
"serde",
"serde_json",
]
@@ -129,6 +125,7 @@ name = "bitcoin-units"
version = "0.2.0"
dependencies = [
"arbitrary",
+ "bincode",
"bitcoin-internals",
"serde",
"serde_json",
@@ -141,14 +138,15 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
+ "bitcoin-io 0.1.3",
"hex-conservative 0.2.0",
]
[[package]]
name = "bitcoin_hashes"
-version = "0.15.0"
+version = "0.16.0"
dependencies = [
- "bitcoin-io",
+ "bitcoin-internals",
"hex-conservative 0.3.0",
"serde",
"serde_json",
@@ -241,12 +239,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
-[[package]]
-name = "json"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
-
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -268,45 +260,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "mutagen"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "mutagen-core",
- "mutagen-transform",
-]
-
-[[package]]
-name = "mutagen-core"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "anyhow",
- "json",
- "lazy_static",
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "syn",
-]
-
-[[package]]
-name = "mutagen-transform"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "mutagen-core",
- "proc-macro2",
-]
-
-[[package]]
-name = "ordered"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0642533dea0bb58bd5cae31bafc1872429f0f12ac8c61fe2b4ba44f80b959b"
-
[[package]]
name = "ppv-lite86"
version = "0.2.8"
@@ -388,9 +341,9 @@ checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
[[package]]
name = "secp256k1"
-version = "0.29.0"
+version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3"
+checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252"
dependencies = [
"bitcoin_hashes 0.14.0",
"rand",
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index ed3d594ec8..558ade0a89 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -2,17 +2,11 @@
# It is not intended for manual editing.
version = 3
-[[package]]
-name = "anyhow"
-version = "1.0.89"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
-
[[package]]
name = "arbitrary"
-version = "1.3.2"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arrayvec"
@@ -22,10 +16,10 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "base58ck"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"bitcoin-internals",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
]
@@ -52,7 +46,7 @@ dependencies = [
[[package]]
name = "bitcoin"
-version = "0.33.0-alpha"
+version = "0.33.0-alpha.0"
dependencies = [
"arbitrary",
"base58ck",
@@ -60,15 +54,13 @@ dependencies = [
"bech32",
"bincode",
"bitcoin-internals",
- "bitcoin-io",
+ "bitcoin-io 0.2.0",
"bitcoin-primitives",
"bitcoin-units",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"bitcoinconsensus",
"hex-conservative 0.3.0",
"hex_lit",
- "mutagen",
- "ordered",
"secp256k1",
"serde",
"serde_json",
@@ -99,11 +91,18 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "bitcoin-io"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
+
[[package]]
name = "bitcoin-io"
version = "0.2.0"
dependencies = [
"bitcoin-internals",
+ "bitcoin_hashes 0.16.0",
]
[[package]]
@@ -113,12 +112,9 @@ dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
- "bitcoin-io",
"bitcoin-units",
- "bitcoin_hashes 0.15.0",
+ "bitcoin_hashes 0.16.0",
"hex-conservative 0.3.0",
- "mutagen",
- "ordered",
"serde",
"serde_json",
]
@@ -128,6 +124,7 @@ name = "bitcoin-units"
version = "0.2.0"
dependencies = [
"arbitrary",
+ "bincode",
"bitcoin-internals",
"serde",
"serde_json",
@@ -140,14 +137,15 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
+ "bitcoin-io 0.1.3",
"hex-conservative 0.2.1",
]
[[package]]
name = "bitcoin_hashes"
-version = "0.15.0"
+version = "0.16.0"
dependencies = [
- "bitcoin-io",
+ "bitcoin-internals",
"hex-conservative 0.3.0",
"serde",
"serde_json",
@@ -243,12 +241,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
-[[package]]
-name = "json"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
-
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -276,45 +268,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "mutagen"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "mutagen-core",
- "mutagen-transform",
-]
-
-[[package]]
-name = "mutagen-core"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "anyhow",
- "json",
- "lazy_static",
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "mutagen-transform"
-version = "0.2.0"
-source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53"
-dependencies = [
- "mutagen-core",
- "proc-macro2",
-]
-
-[[package]]
-name = "ordered"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0642533dea0bb58bd5cae31bafc1872429f0f12ac8c61fe2b4ba44f80b959b"
-
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@@ -389,9 +342,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "secp256k1"
-version = "0.29.1"
+version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
+checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252"
dependencies = [
"bitcoin_hashes 0.14.0",
"rand",
@@ -431,7 +384,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.79",
+ "syn",
]
[[package]]
@@ -461,17 +414,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
[[package]]
name = "syn"
version = "2.0.79"
@@ -513,5 +455,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.79",
+ "syn",
]
diff --git a/README.md b/README.md
index a841d5122e..5cfe2c98a4 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,9 @@
-
+
-
+
@@ -49,7 +49,7 @@ are no plans to do so. Of course, patches to fix specific consensus incompatibil
### Support for 16-bit pointer sizes
-16-bit pointer sizes are not supported and we can't promise they will be. If you care about them
+16-bit pointer sizes are not supported, and we can't promise they will be. If you care about them
please let us know, so we can know how large the interest is and possibly decide to support them.
### Semver compliance
@@ -103,13 +103,6 @@ current stable one (see MSRV section).
## Building
-The cargo feature `std` is enabled by default. At least one of the features `std` or `no-std` or
-both must be enabled.
-
-Enabling the `no-std` feature does not disable `std`. To disable the `std` feature you must disable
-default features. The `no-std` feature only enables additional features required for this crate to
-be usable without `std`. Both can be enabled without conflict.
-
The library can be built and tested using [`cargo`](https://github.com/rust-lang/cargo/):
```
@@ -127,10 +120,19 @@ cargo test
Please refer to the [`cargo` documentation](https://doc.rust-lang.org/stable/cargo/) for more
detailed instructions.
+### No-std support
+
+The `std` cargo feature is enabled by default. To build this project without the Rust standard
+library, use the `--no-default-features` flag or set `default-features = false` in your dependency
+declaration when adding it to your project.
+
+For embedded device examples, see [`bitcoin/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/bitcoin/embedded)
+or [`hashes/embedded`](https://github.com/rust-bitcoin/rust-bitcoin/tree/master/hashes/embedded).
+
### Just
We support [`just`](https://just.systems/man/en/) for running dev workflow commands. Run `just` from
-your shell to see list available sub-commands.
+your shell to see a list of available sub-commands.
### Building the docs
@@ -146,8 +148,8 @@ alias build-docs='RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features=
Unit and integration tests are available for those interested, along with benchmarks. For project
developers, especially new contributors looking for something to work on, we do:
-- Fuzz testing with [`Hongfuzz`](https://github.com/rust-fuzz/honggfuzz-rs)
-- Mutation testing with [`Mutagen`](https://github.com/llogiq/mutagen)
+- Fuzz testing with [`Honggfuzz`](https://github.com/rust-fuzz/honggfuzz-rs)
+- Mutation testing with [`cargo-mutants`](https://github.com/sourcefrog/cargo-mutants)
- Code verification with [`Kani`](https://github.com/model-checking/kani)
There are always more tests to write and more bugs to find, contributions to our testing efforts
@@ -165,9 +167,9 @@ bench marks use: `RUSTFLAGS='--cfg=bench' cargo +nightly bench`.
### Mutation tests
-We have started doing mutation testing with [mutagen](https://github.com/llogiq/mutagen). To run
-these tests first install the latest dev version with `cargo +nightly install --git https://github.com/llogiq/mutagen`
-then run with `RUSTFLAGS='--cfg=mutate' cargo +nightly mutagen`.
+We are doing mutation testing with [cargo-mutants](https://github.com/sourcefrog/cargo-mutants). To run
+these tests first install with `cargo install --locked cargo-mutants` then run with `cargo mutants --in-place --no-shuffle`.
+Note that running these mutation tests will take on the order of 10's of minutes.
### Code verification
diff --git a/base58/CHANGELOG.md b/base58/CHANGELOG.md
index b219fb4776..9bd3876aa3 100644
--- a/base58/CHANGELOG.md
+++ b/base58/CHANGELOG.md
@@ -1,3 +1,12 @@
+# 0.2.0 - 2024-12-10
+
+- Bump MSRV to `1.63` [#3100](https://github.com/rust-bitcoin/rust-bitcoin/pull/3100)
+- Optimize `base58` on small inputs [#3002](https://github.com/rust-bitcoin/rust-bitcoin/pull/3002)
+- Add `alloc` feature [#2996](https://github.com/rust-bitcoin/rust-bitcoin/pull/2996)
+- Remove zeroed vector by pushing front [#3227](https://github.com/rust-bitcoin/rust-bitcoin/pull/3227)
+- Close all errors [#3533](https://github.com/rust-bitcoin/rust-bitcoin/pull/3533)
+- Bump `hex-conservative` to `0.3.0` [#3543](https://github.com/rust-bitcoin/rust-bitcoin/pull/3543)
+
# 0.1.0 - 2024-03-14
Initial release of the `base58ck` crate. This crate was cut out of
diff --git a/base58/Cargo.toml b/base58/Cargo.toml
index 198b91bf4d..06000bcbf9 100644
--- a/base58/Cargo.toml
+++ b/base58/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "base58ck"
-version = "0.1.0"
+version = "0.2.0"
authors = ["Andrew Poelstra "]
license = "CC0-1.0"
repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
@@ -18,7 +18,7 @@ std = ["alloc", "hashes/std", "internals/std"]
alloc = ["hashes/alloc", "internals/alloc"]
[dependencies]
-hashes = { package = "bitcoin_hashes", version = "0.15.0", default-features = false }
+hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false }
internals = { package = "bitcoin-internals", version = "0.4.0" }
[dev-dependencies]
diff --git a/base58/README.md b/base58/README.md
index f28534d5a6..4b530e94ed 100644
--- a/base58/README.md
+++ b/base58/README.md
@@ -26,5 +26,5 @@ This library should always compile with any combination of features on **Rust 1.
## Licensing
-The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](LICENSE).
+The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](../LICENSE).
We use the [SPDX license list](https://spdx.org/licenses/) and [SPDX IDs](https://spdx.dev/ids/).
diff --git a/base58/src/error.rs b/base58/src/error.rs
index 6257056ecc..678c22d930 100644
--- a/base58/src/error.rs
+++ b/base58/src/error.rs
@@ -2,6 +2,7 @@
//! Error code for the `base58` crate.
+use core::convert::Infallible;
use core::fmt;
use internals::write_err;
@@ -20,11 +21,16 @@ pub(super) enum ErrorInner {
TooShort(TooShortError),
}
-internals::impl_from_infallible!(Error);
-internals::impl_from_infallible!(ErrorInner);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl From for ErrorInner {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl Error {
- /// Returns the invalid base58 ssscharacter, if encountered.
+ /// Returns the invalid base58 character, if encountered.
pub fn invalid_character(&self) -> Option {
match self.0 {
ErrorInner::Decode(ref e) => Some(e.invalid_character()),
@@ -95,7 +101,9 @@ pub(super) struct IncorrectChecksumError {
pub(super) expected: u32,
}
-internals::impl_from_infallible!(IncorrectChecksumError);
+impl From for IncorrectChecksumError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for IncorrectChecksumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -116,8 +124,9 @@ pub(super) struct TooShortError {
/// The length of the decoded data.
pub(super) length: usize,
}
-
-internals::impl_from_infallible!(TooShortError);
+impl From for TooShortError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for TooShortError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -141,8 +150,13 @@ pub(super) struct InvalidCharacterErrorInner {
pub(super) invalid: u8,
}
-internals::impl_from_infallible!(InvalidCharacterError);
-internals::impl_from_infallible!(InvalidCharacterErrorInner);
+impl From for InvalidCharacterError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl From for InvalidCharacterErrorInner {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl InvalidCharacterError {
pub(super) fn new(invalid: u8) -> Self { Self(InvalidCharacterErrorInner { invalid }) }
diff --git a/base58/src/lib.rs b/base58/src/lib.rs
index b5b977ebc5..13db9fd1f1 100644
--- a/base58/src/lib.rs
+++ b/base58/src/lib.rs
@@ -20,6 +20,7 @@
// Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
+#![allow(clippy::incompatible_msrv)] // Has FPs and we're testing it which is more reliable anyway.
extern crate alloc;
@@ -40,7 +41,10 @@ use core::fmt;
pub use std::{string::String, vec::Vec};
use hashes::sha256d;
+use internals::array::ArrayExt;
use internals::array_vec::ArrayVec;
+#[allow(unused)] // MSRV polyfill
+use internals::slice::SliceExt;
use crate::error::{IncorrectChecksumError, TooShortError};
@@ -109,15 +113,9 @@ pub fn decode(data: &str) -> Result, InvalidCharacterError> {
/// Decodes a base58check-encoded string into a byte vector verifying the checksum.
pub fn decode_check(data: &str) -> Result, Error> {
let mut ret: Vec = decode(data)?;
- if ret.len() < 4 {
- return Err(TooShortError { length: ret.len() }.into());
- }
- let check_start = ret.len() - 4;
+ let (remaining, &data_check) = ret.split_last_chunk::<4>().ok_or(TooShortError { length: ret.len() })?;
- let hash_check = sha256d::Hash::hash(&ret[..check_start]).as_byte_array()[..4]
- .try_into()
- .expect("4 byte slice");
- let data_check = ret[check_start..].try_into().expect("4 byte slice");
+ let hash_check = *sha256d::Hash::hash(remaining).as_byte_array().sub_array::<0, 4>();
let expected = u32::from_le_bytes(hash_check);
let actual = u32::from_le_bytes(data_check);
@@ -126,7 +124,7 @@ pub fn decode_check(data: &str) -> Result, Error> {
return Err(IncorrectChecksumError { incorrect: actual, expected }.into());
}
- ret.truncate(check_start);
+ ret.truncate(remaining.len());
Ok(ret)
}
@@ -258,7 +256,7 @@ mod tests {
use super::*;
#[test]
- fn test_base58_encode() {
+ fn base58_encode() {
// Basics
assert_eq!(&encode(&[0][..]), "1");
assert_eq!(&encode(&[1][..]), "2");
@@ -287,7 +285,7 @@ mod tests {
}
#[test]
- fn test_base58_decode() {
+ fn base58_decode() {
// Basics
assert_eq!(decode("1").ok(), Some(vec![0u8]));
assert_eq!(decode("2").ok(), Some(vec![1u8]));
@@ -308,7 +306,7 @@ mod tests {
}
#[test]
- fn test_base58_roundtrip() {
+ fn base58_roundtrip() {
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
let v: Vec = decode_check(s).unwrap();
assert_eq!(encode_check(&v[..]), s);
@@ -337,7 +335,7 @@ mod benches {
#[bench]
pub fn bench_encode_check_xpub(bh: &mut Bencher) {
- let data: alloc::vec::Vec<_> = (0u8..78).collect(); // lenght of xpub
+ let data: alloc::vec::Vec<_> = (0u8..78).collect(); // length of xpub
bh.iter(|| {
let r = super::encode_check(&data);
diff --git a/bitcoin/CHANGELOG.md b/bitcoin/CHANGELOG.md
index c6c779bd37..0972ca23b7 100644
--- a/bitcoin/CHANGELOG.md
+++ b/bitcoin/CHANGELOG.md
@@ -1,6 +1,113 @@
-# 0.33.0-alpha - TODO: Set date
-
-- Enforce script size limit when hashing scripts [#2794](https://github.com/rust-bitcoin/rust-bitcoin/pull/2794)
+# Unreleased
+
+- TODO: Make a comment about `Amount::MAX_MONEY` (perhaps here in `bitcoin` release notes as well as in `amount`)
+
+- Use MAX_MONEY in serde regression test [#3950](https://github.com/rust-bitcoin/rust-bitcoin/pull/3950)
+
+# 0.33.0-alpha.0 - 2024-11-18
+
+This series of alpha releases is meant for two things:
+
+1. To facilitate testing of `primitives 0.101`.
+2. To allow testing of upcoming `1.0` releases of:
+
+ - `bitcoin_hashes`
+ - `hex`
+ - `bitcoin-io`
+ - `primitives`
+ - `units`
+ - `ordered`
+
+You likely want to explicitly set the version if doing testing. `cargo` can be surprising when there
+is a `-` in the version number.
+
+We do not currently intend on releasing `bitcoin 0.33.0` until the `1.0` releases above are done.
+
+For changes to our dependencies included in this release see:
+
+- `bitcoin_hashes 0.15`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/hashes/CHANGELOG.md)
+- `hex-conservative 0.3`: [changelog](https://github.com/rust-bitcoin/hex-conservative/blob/master/CHANGELOG.md)
+- `bitcoin-io 0.2`: [changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/io/CHANGELOG.md)
+- `bitcoin-primitives: 0.101`: [changelog]((https://github.com/rust-bitcoin/rust-bitcoin/blob/master/primitives/CHANGELOG.md))
+- `bitcoin-units 0.2`: [changelog]((https://github.com/rust-bitcoin/rust-bitcoin/blob/master/units/CHANGELOG.md))
+- `bitcoinconsensus: 0.106.0+26`: [changelog](https://github.com/rust-bitcoin/rust-bitcoinconsensus/blob/master/CHANGELOG.md)
+
+## Changes
+
+- Fix psbt fuzz crash [#3667](https://github.com/rust-bitcoin/rust-bitcoin/pull/3667)
+- Update `from_next_work_required` to take an `i64` for timespan [#3660](https://github.com/rust-bitcoin/rust-bitcoin/pull/3660)
+- Account for data pushing opcodes in `is_standard_op_return` [#3643](https://github.com/rust-bitcoin/rust-bitcoin/pull/3643)
+- Add p2wpkh address creation example [#3642](https://github.com/rust-bitcoin/rust-bitcoin/pull/3642)
+- Add `Address::into_unchecked` [#3640](https://github.com/rust-bitcoin/rust-bitcoin/pull/3640)
+- Mark `checked_` functions as const [#3636](https://github.com/rust-bitcoin/rust-bitcoin/pull/3636)
+- Mark functions const in `fee_rate` [#3627](https://github.com/rust-bitcoin/rust-bitcoin/pull/3627)
+- Mark funtions const [#3608](https://github.com/rust-bitcoin/rust-bitcoin/pull/3608)
+- Add constructor to `FeeRate` [#3604](https://github.com/rust-bitcoin/rust-bitcoin/pull/3604)
+- Fix bug in witness stack getters [#3601](https://github.com/rust-bitcoin/rust-bitcoin/pull/3601)
+- Split `checked_div_by_weight` into floor and ceiling version [#3587](https://github.com/rust-bitcoin/rust-bitcoin/pull/3587)
+- script: remove `unsafe` marker from slice-to-script conversions [#3569](https://github.com/rust-bitcoin/rust-bitcoin/pull/3569)
+- io: Bump version to `0.1.3` [#3566](https://github.com/rust-bitcoin/rust-bitcoin/pull/3566)
+- Re-export `block::Header` as `BlockHeader` [#3562](https://github.com/rust-bitcoin/rust-bitcoin/pull/3562)
+- Bump `hex-conservative` to `0.3.0` [#3543](https://github.com/rust-bitcoin/rust-bitcoin/pull/3543)
+- Re-organise the `amount` module [#3541](https://github.com/rust-bitcoin/rust-bitcoin/pull/3541)
+- Improve the `amount` module [#3539](https://github.com/rust-bitcoin/rust-bitcoin/pull/3539)
+- base58: Close all errors [#3533](https://github.com/rust-bitcoin/rust-bitcoin/pull/3533)
+- psbt: Fix bug in `Subtype` consensus_encode [#3519](https://github.com/rust-bitcoin/rust-bitcoin/pull/3519)
+- Explicitly re-export stuff from crates down the stack [#3497](https://github.com/rust-bitcoin/rust-bitcoin/pull/3497)
+- Expose `units::amount::ParseError` [#3496](https://github.com/rust-bitcoin/rust-bitcoin/pull/3496)
+- Make `Amount::to_sat and SignedAmount::to_sat` const [#3493](https://github.com/rust-bitcoin/rust-bitcoin/pull/3493)
+- Decode an address string based on prefix [#3481](https://github.com/rust-bitcoin/rust-bitcoin/pull/3481)
+- Replace `ENABLE_RBF_NO_LOCKTIME` with `ENABLE_LOCKTIME_AND_RBF` [#3459](https://github.com/rust-bitcoin/rust-bitcoin/pull/3459)
+- Add version three variant to transaction version [#3450](https://github.com/rust-bitcoin/rust-bitcoin/pull/3450)
+- Input weight prediction helpers for nested P2WPKH [#3443](https://github.com/rust-bitcoin/rust-bitcoin/pull/3443)
+- Clarify sequence constant name and add `FINAL` [#3439](https://github.com/rust-bitcoin/rust-bitcoin/pull/3439)
+- Add checked div by weight to amount [#3430](https://github.com/rust-bitcoin/rust-bitcoin/pull/3430)
+- Rename `Midstate::into_parts` to `Midstate::to_parts` since it derives `Copy` [#3429](https://github.com/rust-bitcoin/rust-bitcoin/pull/3429)
+- Const locktime constructors [#3421](https://github.com/rust-bitcoin/rust-bitcoin/pull/3421)
+- Fix script number overflow check for `push_int` [#3392](https://github.com/rust-bitcoin/rust-bitcoin/pull/3392)
+- transaction: Remove `Default` implementations [#3386](https://github.com/rust-bitcoin/rust-bitcoin/pull/3386)
+- Add `FeeRate` addition and subtraction traits [#3381](https://github.com/rust-bitcoin/rust-bitcoin/pull/3381)
+- Add `Xpriv::to_xpub` and improve related method names [#3358](https://github.com/rust-bitcoin/rust-bitcoin/pull/3358)
+- Support `impl AsRef<[#u8]>` in `signed_msg_hash` [3357](https://github.com/rust-bitcoin/rust-bitcoin/pull/u8)
+- Fix `GetKey` for sets (plus some related changes) [#3356](https://github.com/rust-bitcoin/rust-bitcoin/pull/3356)
+- Add a condition for parsing zero from string when not denominated [#3346](https://github.com/rust-bitcoin/rust-bitcoin/pull/3346)
+- Add basic `miri` checks [#3328](https://github.com/rust-bitcoin/rust-bitcoin/pull/3328)
+- Add coinbase associated consts [#3308](https://github.com/rust-bitcoin/rust-bitcoin/pull/3308)
+- Fix bug in `ArrayVec::extend_from_slice` [#3272](https://github.com/rust-bitcoin/rust-bitcoin/pull/3272)
+- Change `T::from_str(s)` to `s.parse::()` in examples, docs and tests [#3262](https://github.com/rust-bitcoin/rust-bitcoin/pull/3262)
+- Add `Arbitrary` to `Weight` [#3257](https://github.com/rust-bitcoin/rust-bitcoin/pull/3257)
+- Bump `units` version [#3248](https://github.com/rust-bitcoin/rust-bitcoin/pull/3248)
+- Rename key field in Key to key_data [#3048](https://github.com/rust-bitcoin/rust-bitcoin/pull/3048)
+- Optimize `base58` on small inputs [#3002](https://github.com/rust-bitcoin/rust-bitcoin/pull/3002)
+- Add `TxIdentifier` trait [#2987](https://github.com/rust-bitcoin/rust-bitcoin/pull/2987)
+- Fix `Amount` decimals handling [#2951](https://github.com/rust-bitcoin/rust-bitcoin/pull/2951)
+- `OP_RETURN` standardness check [#2949](https://github.com/rust-bitcoin/rust-bitcoin/pull/2949)
+- Support Testnet4 Network [#2945](https://github.com/rust-bitcoin/rust-bitcoin/pull/2945)
+- Remove `VarInt` and use `ReadExt` and `WriteExt` trait methods instead [#2931](https://github.com/rust-bitcoin/rust-bitcoin/pull/2931)
+- bip32: Add `From<&'a [#u32]>` for `DerivationPath` [2909](https://github.com/rust-bitcoin/rust-bitcoin/pull/u32)
+- psbt: Encode keytype as a compact size unsigned integer [#2906](https://github.com/rust-bitcoin/rust-bitcoin/pull/2906)
+- Pass sigs and associated types by value [#2899](https://github.com/rust-bitcoin/rust-bitcoin/pull/2899)
+- Re-export `UnprefixedHexError` in the bitcoin crate root [#2895](https://github.com/rust-bitcoin/rust-bitcoin/pull/2895)
+- taproot: Split errors up [#2886](https://github.com/rust-bitcoin/rust-bitcoin/pull/2886)
+- Remove usage of `blockdata` from paths [#2885](https://github.com/rust-bitcoin/rust-bitcoin/pull/2885)
+- Update `PushBytes::read_scriptint(x)` to `x.read_scriptint()` [#2872](https://github.com/rust-bitcoin/rust-bitcoin/pull/2872)
+- Remove `Denomination::MilliSatoshi` [#2870](https://github.com/rust-bitcoin/rust-bitcoin/pull/2870)
+- Pass keys by value [#2868](https://github.com/rust-bitcoin/rust-bitcoin/pull/2868)
+- Clarify the meaning of `Height` & `Time` based locktime [#2858](https://github.com/rust-bitcoin/rust-bitcoin/pull/2858)
+- Add API for extracting the inner payload of `RawNetworkMessage` [#2839](https://github.com/rust-bitcoin/rust-bitcoin/pull/2839)
+- Update `bitcoinconsensus` version to `0.106.0+26` [#2833] (https://github.com/rust-bitcoin/rust-bitcoin/pull/2833)
+- Make `difficulty_float` general to all networks [#2816](https://github.com/rust-bitcoin/rust-bitcoin/pull/2816)
+- Add const modifier to `Magic::from_bytes` [#2815](https://github.com/rust-bitcoin/rust-bitcoin/pull/2815)
+- Add an `AddressData` type [#2808](https://github.com/rust-bitcoin/rust-bitcoin/pull/2808)
+- Make `Address:p2sh_from_hash` public [#2795](https://github.com/rust-bitcoin/rust-bitcoin/pull/2795)
+- Enable getting the witness program from an address [#2796](https://github.com/rust-bitcoin/rust-bitcoin/pull/2796)
+- Enforce script size limit when hashing scripts [##2794](https://github.com/rust-bitcoin/rust-bitcoin/pull/2794https://github.com/rust-bitcoin/rust-bitcoin/pull/#2794)
+- Deprecate `to_vec` in favour of `to_bytes` [#2768](https://github.com/rust-bitcoin/rust-bitcoin/pull/2768)
+- Flesh out hex unit parsing API [#2765](https://github.com/rust-bitcoin/rust-bitcoin/pull/2765)
+- Bench `base58` encoding and remove `SmallVec` to improve perf [#2759](https://github.com/rust-bitcoin/rust-bitcoin/pull/2759)
+- Add difficulty adjustment calculation [#2740](https://github.com/rust-bitcoin/rust-bitcoin/pull/2740)
+- Upgrade `base64` dependency [#2721](https://github.com/rust-bitcoin/rust-bitcoin/pull/2721)
+- Some additional inspectors on `Script` and `Witness` [#2646](https://github.com/rust-bitcoin/rust-bitcoin/pull/2646)
## Upgrade notes
@@ -14,6 +121,20 @@
- Change `TxIn::default()` to `TxIn::EMPTY_COINBASE` if appropriate.
- Change `to_raw_hash()` to `to_byte_array()`.
- `bitcoin::error::UnprefixedHexError` moved to `bitcoin::parse::UnprefixedHexError`.
+# 0.32.5 - 2024-11-27
+
+- Backport - Re-export `bech32` crate [#3662](https://github.com/rust-bitcoin/rust-bitcoin/pull/3662)
+- Backport - Add API for extracting the inner payload of `RawNetworkMessage` [#3523](https://github.com/rust-bitcoin/rust-bitcoin/pull/3523)
+- Backport - Fix bug in witness stack getters [#3626](https://github.com/rust-bitcoin/rust-bitcoin/pull/3626)
+- Backport - address: Add `Address::into_unchecked` [#3655](https://github.com/rust-bitcoin/rust-bitcoin/pull/3655)
+
+# 0.32.4 - 2024-10-24
+
+- Bound decode methods on `Read`, rather than `BufRead` [#3173](https://github.com/rust-bitcoin/rust-bitcoin/pull/3173)
+- Backport - Some additional inspectors on `Script` and `Witness` [#2646](https://github.com/rust-bitcoin/rust-bitcoin/pull/2646)
+- Backport - Add difficulty adjustment calculation [#3494](https://github.com/rust-bitcoin/rust-bitcoin/pull/3494)
+- Backport - Add testnet 4 support [#3453](https://github.com/rust-bitcoin/rust-bitcoin/pull/3453)
+- Backport - CI: Copy main workflow from master [#3418](https://github.com/rust-bitcoin/rust-bitcoin/pull/3418)
# 0.32.3 - 2024-09-27
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index c4ba294c44..7784efff71 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bitcoin"
-version = "0.33.0-alpha"
+version = "0.33.0-alpha.0"
authors = ["Andrew Poelstra "]
license = "CC0-1.0"
repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
@@ -17,7 +17,7 @@ exclude = ["tests", "contrib"]
[features]
default = [ "std", "secp-recovery" ]
std = ["base58/std", "bech32/std", "hashes/std", "hex/std", "internals/std", "io/std", "primitives/std", "secp256k1/std", "units/std", "bitcoinconsensus?/std"]
-rand-std = ["secp256k1/rand-std", "std"]
+rand-std = ["secp256k1/rand", "std"]
rand = ["secp256k1/rand"]
serde = ["dep:serde", "hashes/serde", "internals/serde", "primitives/serde", "secp256k1/serde", "units/serde"]
secp-lowmemory = ["secp256k1/lowmemory"]
@@ -25,21 +25,20 @@ secp-recovery = ["secp256k1/recovery"]
arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary"]
[dependencies]
-base58 = { package = "base58ck", version = "0.1.0", default-features = false, features = ["alloc"] }
+base58 = { package = "base58ck", version = "0.2.0", default-features = false, features = ["alloc"] }
bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] }
-hashes = { package = "bitcoin_hashes", version = "0.15.0", default-features = false, features = ["alloc", "bitcoin-io", "hex"] }
+hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, features = ["alloc", "hex"] }
hex = { package = "hex-conservative", version = "0.3.0", default-features = false, features = ["alloc"] }
-internals = { package = "bitcoin-internals", version = "0.4.0", features = ["alloc"] }
-io = { package = "bitcoin-io", version = "0.2.0", default-features = false, features = ["alloc"] }
+internals = { package = "bitcoin-internals", version = "0.4.0", features = ["alloc", "hex"] }
+io = { package = "bitcoin-io", version = "0.2.0", default-features = false, features = ["alloc", "hashes"] }
primitives = { package = "bitcoin-primitives", version = "0.101.0", default-features = false, features = ["alloc"] }
-secp256k1 = { version = "0.29.0", default-features = false, features = ["hashes", "alloc"] }
+secp256k1 = { version = "0.30.0", default-features = false, features = ["hashes", "alloc", "rand"] }
units = { package = "bitcoin-units", version = "0.2.0", default-features = false, features = ["alloc"] }
-arbitrary = { version = "1.0.1", optional = true }
+arbitrary = { version = "1.4", optional = true }
base64 = { version = "0.22.0", optional = true }
# `bitcoinconsensus` version includes metadata which indicates the version of Core. Use `cargo tree` to see it.
bitcoinconsensus = { version = "0.106.0", default-features = false, optional = true }
-ordered = { version = "0.2.0", optional = true }
serde = { version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
[dev-dependencies]
@@ -49,9 +48,6 @@ serde_test = "1.0.19"
bincode = "1.3.1"
hex_lit = "0.1.1"
-[target.'cfg(mutate)'.dev-dependencies]
-mutagen = { git = "https://github.com/llogiq/mutagen" }
-
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
@@ -71,6 +67,10 @@ required-features = ["std", "bitcoinconsensus"]
name = "ecdsa-psbt-simple"
required-features = ["rand-std"]
+[[example]]
+name = "create-p2wpkh-address"
+required-features = ["rand-std"]
+
[[example]]
name = "sign-tx-segwit-v0"
required-features = ["rand-std"]
@@ -90,5 +90,9 @@ required-features = ["rand-std"]
[[example]]
name = "sighash"
+[[example]]
+name = "io"
+required-features = ["std"]
+
[lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', 'cfg(kani)', 'cfg(mutate)'] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', 'cfg(kani)'] }
diff --git a/bitcoin/contrib/test_vars.sh b/bitcoin/contrib/test_vars.sh
index c65af099da..f28ecdb33a 100644
--- a/bitcoin/contrib/test_vars.sh
+++ b/bitcoin/contrib/test_vars.sh
@@ -5,10 +5,10 @@
# shellcheck disable=SC2034
# Test all these features with "std" enabled.
-FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus base64 ordered arbitrary"
+FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus base64 arbitrary"
# Test all these features without "std" or "alloc" enabled.
-FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered arbitrary"
+FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 arbitrary"
# Run these examples.
EXAMPLES="ecdsa-psbt:std,bitcoinconsensus sign-tx-segwit-v0:rand-std sign-tx-taproot:rand-std taproot-psbt:bitcoinconsensus,rand-std sighash:std"
diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs
index f2f36d4099..9df5ef6732 100644
--- a/bitcoin/examples/bip32.rs
+++ b/bitcoin/examples/bip32.rs
@@ -1,5 +1,3 @@
-extern crate bitcoin;
-
use std::{env, process};
use bitcoin::address::{Address, KnownHrp};
@@ -13,7 +11,7 @@ fn main() {
// This example derives root xprv from a 32-byte seed,
// derives the child xprv with path m/84h/0h/0h,
// prints out corresponding xpub,
- // calculates and prints out the first receiving segwit address.
+ // calculates and prints out the first receiving SegWit address.
// Run this example with cargo and seed(hex-encoded) argument:
// cargo run --example bip32 7934c09359b234e076b9fa5a1abfd38e3dc2a9939745b7cc3c22a48d831d14bd
diff --git a/bitcoin/examples/create-p2wpkh-address.rs b/bitcoin/examples/create-p2wpkh-address.rs
new file mode 100644
index 0000000000..b8ec758f82
--- /dev/null
+++ b/bitcoin/examples/create-p2wpkh-address.rs
@@ -0,0 +1,24 @@
+use bitcoin::secp256k1::{rand, Secp256k1};
+use bitcoin::{Address, CompressedPublicKey, Network, PrivateKey};
+
+/// Generate a P2WPKH (pay-to-witness-public-key-hash) address and print it
+/// along with the associated private key needed to transact.
+fn main() {
+ // Create new secp256k1 instance.
+ let secp = Secp256k1::new();
+
+ // Generate secp256k1 public and private key pair.
+ let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
+
+ // Create a Bitcoin private key to be used on the Bitcoin mainnet.
+ let private_key = PrivateKey::new(secret_key, Network::Bitcoin);
+
+ // Create a compressed Bitcoin public key from the secp256k1 public key.
+ let public_key = CompressedPublicKey(public_key);
+
+ // Create a Bitcoin P2WPKH address.
+ let address = Address::p2wpkh(public_key, Network::Bitcoin);
+
+ println!("Private Key: {}", private_key);
+ println!("Address: {}", address);
+}
diff --git a/bitcoin/examples/ecdsa-psbt-simple.rs b/bitcoin/examples/ecdsa-psbt-simple.rs
index 4e2ca10855..ac5d84b43e 100644
--- a/bitcoin/examples/ecdsa-psbt-simple.rs
+++ b/bitcoin/examples/ecdsa-psbt-simple.rs
@@ -26,13 +26,14 @@ use std::collections::BTreeMap;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
+use bitcoin::key::WPubkeyHash;
use bitcoin::locktime::absolute;
use bitcoin::psbt::Input;
use bitcoin::secp256k1::{Secp256k1, Signing};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
consensus, transaction, Address, Amount, EcdsaSighashType, Network, OutPoint, Psbt, ScriptBuf,
- Sequence, Transaction, TxIn, TxOut, Txid, WPubkeyHash, Witness,
+ Sequence, Transaction, TxIn, TxOut, Txid, Witness,
};
// The master xpriv, from which we derive the keys we control.
@@ -46,12 +47,12 @@ const BIP84_DERIVATION_PATH: &str = "m/84'/0'/0'";
const MASTER_FINGERPRINT: &str = "9680603f";
// The dummy UTXO amounts we are spending.
-const DUMMY_UTXO_AMOUNT_INPUT_1: Amount = Amount::from_sat(20_000_000);
-const DUMMY_UTXO_AMOUNT_INPUT_2: Amount = Amount::from_sat(10_000_000);
+const DUMMY_UTXO_AMOUNT_INPUT_1: Amount = Amount::from_sat_u32(20_000_000);
+const DUMMY_UTXO_AMOUNT_INPUT_2: Amount = Amount::from_sat_u32(10_000_000);
// The amounts we are sending to someone, and receiving back as change.
-const SPEND_AMOUNT: Amount = Amount::from_sat(25_000_000);
-const CHANGE_AMOUNT: Amount = Amount::from_sat(4_990_000); // 10_000 sat fee.
+const SPEND_AMOUNT: Amount = Amount::from_sat_u32(25_000_000);
+const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(4_990_000); // 10_000 sat fee.
// Derive the external address xpriv.
fn get_external_address_xpriv(
diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
index 54ae34767c..e6bbaf1e66 100644
--- a/bitcoin/examples/ecdsa-psbt.rs
+++ b/bitcoin/examples/ecdsa-psbt.rs
@@ -54,7 +54,7 @@ const INPUT_UTXO_TXID: &str = "295f06639cde6039bf0c3dbf4827f0e3f2b2c2b476408e2f9
const INPUT_UTXO_VOUT: u32 = 0;
const INPUT_UTXO_SCRIPT_PUBKEY: &str = "00149891eeb8891b3e80a2a1ade180f143add23bf5de";
const INPUT_UTXO_VALUE: &str = "50 BTC";
-// Get this from the desciptor,
+// Get this from the descriptor,
// "wpkh([97f17dca/0'/0'/0']02749483607dafb30c66bd93ece4474be65745ce538c2d70e8e246f17e7a4e0c0c)#m9n56cx0".
const INPUT_UTXO_DERIVATION_PATH: &str = "0h/0h/0h";
diff --git a/bitcoin/examples/handshake.rs b/bitcoin/examples/handshake.rs
index 8cfa6a9e22..db5a4fb171 100644
--- a/bitcoin/examples/handshake.rs
+++ b/bitcoin/examples/handshake.rs
@@ -1,5 +1,3 @@
-extern crate bitcoin;
-
use std::io::{BufReader, Write};
use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream};
use std::time::{SystemTime, UNIX_EPOCH};
diff --git a/bitcoin/examples/io.rs b/bitcoin/examples/io.rs
new file mode 100644
index 0000000000..812c6b1664
--- /dev/null
+++ b/bitcoin/examples/io.rs
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Demonstrate reading and writing `rust-bitcoin` objects.
+//!
+//! The `std::io` module is not exposed in `no-std` Rust so building `no-std` applications which
+//! require reading and writing objects via standard traits is not generally possible. To support
+//! this we provide the `bitcoin_io` crate which provides `io::Read`, `io::BufRead`, and
+//! `io::Write`. This module demonstrates its usage.
+
+use bitcoin::consensus::{Decodable, Encodable as _};
+use bitcoin::{OutPoint, Txid};
+
+fn main() {
+ // Encode/Decode a `rust-bitcoin` type to/from a stdlib type.
+ encode_decode_from_stdlib_type();
+
+ // Encode to a custom type by implementing `bitcoin_io` traits.
+ encode_to_custom_type();
+
+ // Encode to a foreign custom type by using the `bitcoin_io::bridge::FromStd` wrapper.
+ encode_using_wrapper();
+}
+
+/// Encodes/Decodes a `rust-bitcoin` type to/from a stdlib type.
+///
+/// The consensus encoding and decoding traits are generic over `bitcoin_io::Write` and
+/// `bitcoin_io::Read`. However for various stdlib types we implement our traits so _most_ things
+/// should just work.
+fn encode_decode_from_stdlib_type() {
+ let data = dummy_utxo();
+
+ // A type that implements `std::io::Write`.
+ let mut v = Vec::new();
+
+ // Under the hood we implement our `io` traits for a bunch of stdlib types so this just works.
+ let _bytes_written = data.consensus_encode(&mut v).expect("failed to encode to writer");
+
+ // Slices implement `std::io::Read`.
+ let mut reader = v.as_ref();
+
+ let _: OutPoint =
+ Decodable::consensus_decode(&mut reader).expect("failed to decode from reader");
+}
+
+/// Encodes to a custom type by implementing the `bitcoin_io::Write` trait.
+///
+/// To use the `Encodable` (and `Decodable`) traits you can implement the `bitcoin_io` traits.
+fn encode_to_custom_type() {
+ /// A byte counter - counts how many bytes where written to it.
+ struct WriteCounter {
+ count: usize,
+ }
+
+ /// This `io` is `bitcoin_io` - see `Cargo.toml` usage of `io = { package = "bitcoin-io" }`.
+ impl io::Write for WriteCounter {
+ fn write(&mut self, buf: &[u8]) -> Result {
+ let written = buf.len();
+ self.count += written;
+ Ok(written)
+ }
+ fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> {
+ self.count += buf.len();
+ Ok(())
+ }
+ fn flush(&mut self) -> Result<(), io::Error> { Ok(()) }
+ }
+
+ let data = dummy_utxo();
+
+ let mut counter = WriteCounter { count: 0 };
+ let bytes_written = data.consensus_encode(&mut counter).expect("failed to encode to writer");
+ assert_eq!(bytes_written, 36); // 32 bytes for txid + 4 bytes for vout.
+}
+
+/// Encodes to a custom type by using the `bitcoin_io::bridge` module.
+///
+/// If you have a type that you don't control that implements `std::io::Write` you can still encode
+/// to it by way of the `io::bridge::FromStd` wrapper.
+fn encode_using_wrapper() {
+ use pretend_this_is_some_other_crate::WriteCounter;
+
+ let data = dummy_utxo();
+
+ // This will not build because `WriteCounter` does not implement `bitcoin_io::Write`.
+ //
+ // let mut counter = WriteCounter::new();
+ // let bytes_written = data.consensus_encode(&mut counter)?;
+
+ let mut counter = io::FromStd::new(WriteCounter::new());
+ let bytes_written = data.consensus_encode(&mut counter).expect("failed to encode to writer");
+ assert_eq!(bytes_written, 36); // 32 bytes for txid + 4 bytes for vout.
+ assert_eq!(bytes_written, counter.get_ref().written());
+
+ // Take back ownership of the `WriteCounter`.
+ let _ = counter.into_inner();
+}
+
+mod pretend_this_is_some_other_crate {
+ /// A byte counter - counts how many bytes where written to it.
+ pub struct WriteCounter {
+ count: usize,
+ }
+
+ impl WriteCounter {
+ /// Constructs a new `WriteCounter`.
+ pub fn new() -> Self { Self { count: 0 } }
+
+ /// Returns the number of bytes written to this counter.
+ pub fn written(&self) -> usize { self.count }
+ }
+
+ impl std::io::Write for WriteCounter {
+ fn write(&mut self, buf: &[u8]) -> Result {
+ let written = buf.len();
+ self.count += written;
+ Ok(written)
+ }
+ fn write_all(&mut self, buf: &[u8]) -> Result<(), std::io::Error> {
+ self.count += buf.len();
+ Ok(())
+ }
+ fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) }
+ }
+}
+
+/// Constructs a dummy UTXO that is just to represent some `rust-bitcoin` type that implements the
+/// [`consensus::Encodable`] and [`consensus::Decodable`] traits.
+fn dummy_utxo() -> OutPoint {
+ let txid = Txid::from_byte_array([0xFF; 32]); // Arbitrary invalid dummy value.
+ OutPoint { txid, vout: 1 }
+}
diff --git a/bitcoin/examples/sighash.rs b/bitcoin/examples/sighash.rs
index 18e63ea6a2..1055a52f7e 100644
--- a/bitcoin/examples/sighash.rs
+++ b/bitcoin/examples/sighash.rs
@@ -13,14 +13,14 @@ use hex_lit::hex;
//run with: cargo run --example sighash
-/// Computes segwit sighash for a transaction input that spends a p2wpkh output with "witness_v0_keyhash" scriptPubKey.type
+/// Computes SegWit sighash for a transaction input that spends a p2wpkh output with "witness_v0_keyhash" scriptPubKey.type
///
/// # Parameters
///
/// * `raw_tx` - spending tx hex
/// * `inp_idx` - spending tx input index
-/// * `value` - ref tx output value in sats
-fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, value: u64) {
+/// * `amount` - ref tx output value in sats
+fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, amount: Amount) {
let tx: Transaction = consensus::deserialize(raw_tx).unwrap();
let inp = &tx.input[inp_idx];
let witness = &inp.witness;
@@ -29,8 +29,8 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, value: u64) {
// BIP-141: The witness must consist of exactly 2 items (≤ 520 bytes each). The first one a
// signature, and the second one a public key.
assert_eq!(witness.len(), 2);
- let sig_bytes = witness.nth(0).unwrap();
- let pk_bytes = witness.nth(1).unwrap();
+ let sig_bytes = witness.get(0).unwrap();
+ let pk_bytes = witness.get(1).unwrap();
let sig = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig");
@@ -43,9 +43,9 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, value: u64) {
let mut cache = sighash::SighashCache::new(&tx);
let sighash = cache
- .p2wpkh_signature_hash(inp_idx, &spk, Amount::from_sat(value), sig.sighash_type)
+ .p2wpkh_signature_hash(inp_idx, &spk, amount, sig.sighash_type)
.expect("failed to compute sighash");
- println!("Segwit p2wpkh sighash: {:x}", sighash);
+ println!("SegWit p2wpkh sighash: {:x}", sighash);
let msg = secp256k1::Message::from(sighash);
println!("Message is {:x}", msg);
let secp = secp256k1::Secp256k1::verification_only();
@@ -57,7 +57,7 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, value: u64) {
/// # Parameters
///
/// * `raw_tx` - spending tx hex
-/// * `inp_idx` - spending tx input inde
+/// * `inp_idx` - spending tx input index
/// * `script_pubkey_bytes_opt` - Option with scriptPubKey bytes. If None, it's p2sh case, i.e., reftx output's scriptPubKey.type is "scripthash". In this case scriptPubkey is extracted from the spending transaction's scriptSig. If Some(), it's p2ms case, i.e., reftx output's scriptPubKey.type is "multisig", and the scriptPubkey is supplied from the referenced output.
fn compute_sighash_legacy(raw_tx: &[u8], inp_idx: usize, script_pubkey_bytes_opt: Option<&[u8]>) {
let tx: Transaction = consensus::deserialize(raw_tx).unwrap();
@@ -98,14 +98,14 @@ fn compute_sighash_legacy(raw_tx: &[u8], inp_idx: usize, script_pubkey_bytes_opt
}
}
-/// Computes sighash for a segwit multisig transaction input that spends a p2wsh output with "witness_v0_scripthash" scriptPubKey.type
+/// Computes sighash for a SegWit multisig transaction input that spends a p2wsh output with "witness_v0_scripthash" scriptPubKey.type
///
/// # Parameters
///
/// * `raw_tx` - spending tx hex
/// * `inp_idx` - spending tx input index
-/// * `value` - ref tx output value in sats
-fn compute_sighash_p2wsh(raw_tx: &[u8], inp_idx: usize, value: u64) {
+/// * `amount` - ref tx output value in sats
+fn compute_sighash_p2wsh(raw_tx: &[u8], inp_idx: usize, amount: Amount) {
let tx: Transaction = consensus::deserialize(raw_tx).unwrap();
let inp = &tx.input[inp_idx];
let witness = &inp.witness;
@@ -118,21 +118,16 @@ fn compute_sighash_p2wsh(raw_tx: &[u8], inp_idx: usize, value: u64) {
//in an M of N multisig, the witness elements from 1 (0-based) to M-2 are signatures (with sighash flags as the last byte)
for n in 1..=witness.len() - 2 {
- let sig_bytes = witness.nth(n).expect("out of bounds");
+ let sig_bytes = witness.get(n).expect("out of bounds");
let sig = ecdsa::Signature::from_slice(sig_bytes).expect("failed to parse sig");
let sig_len = sig_bytes.len() - 1; //last byte is EcdsaSighashType sighash flag
//ECDSA signature in DER format lengths are between 70 and 72 bytes
assert!((70..=72).contains(&sig_len), "signature length {} out of bounds", sig_len);
//here we assume that all sighash_flags are the same. Can they be different?
let sighash = cache
- .p2wsh_signature_hash(
- inp_idx,
- witness_script,
- Amount::from_sat(value),
- sig.sighash_type,
- )
+ .p2wsh_signature_hash(inp_idx, witness_script, amount, sig.sighash_type)
.expect("failed to compute sighash");
- println!("Segwit p2wsh sighash: {:x} ({})", sighash, sig.sighash_type);
+ println!("SegWit p2wsh sighash: {:x} ({})", sighash, sig.sighash_type);
}
}
@@ -153,14 +148,14 @@ fn sighash_p2wpkh() {
let inp_idx = 0;
//output value from the referenced vout:0 from the referenced tx:
//bitcoin-cli getrawtransaction 752d675b9cc0bd14e0bd23969effee0005ad6d7e550dcc832f0216c7ffd4e15c 3
- let ref_out_value = 200000000;
+ let ref_out_value = Amount::from_sat_u32(200000000);
println!("\nsighash_p2wpkh:");
compute_sighash_p2wpkh(&raw_tx, inp_idx, ref_out_value);
}
fn sighash_p2sh_multisig_2x2() {
- //Spending transactoin:
+ //Spending transaction:
//bitcoin-cli getrawtransaction 214646c4b563cd8c788754ec94468ab71602f5ed07d5e976a2b0e41a413bcc0e 3
//after decoding ScriptSig from the input:0, its last ASM element is the scriptpubkey:
//bitcoin-cli decodescript 5221032d7306898e980c66aefdfb6b377eaf71597c449bf9ce741a3380c5646354f6de2103e8c742e1f283ef810c1cd0c8875e5c2998a05fc5b23c30160d3d33add7af565752ae
@@ -183,7 +178,7 @@ fn sighash_p2wsh_multisig_2x2() {
//For the witness transaction sighash computation, we need its referenced output's value from the original transaction:
//bitcoin-cli getrawtransaction 2845399a8cd7a52733f9f9d0e0b8b6c5d1c88aea4cee09f8d8fa762912b49e1b 3
//we need vout 0 value in sats:
- let ref_out_value = 968240;
+ let ref_out_value = Amount::from_sat_u32(968240);
println!("\nsighash_p2wsh_multisig_2x2:");
compute_sighash_p2wsh(&raw_tx, 0, ref_out_value);
diff --git a/bitcoin/examples/sign-tx-segwit-v0.rs b/bitcoin/examples/sign-tx-segwit-v0.rs
index d3ab610301..b163a24b08 100644
--- a/bitcoin/examples/sign-tx-segwit-v0.rs
+++ b/bitcoin/examples/sign-tx-segwit-v0.rs
@@ -3,18 +3,19 @@
//! Demonstrate creating a transaction that spends to and from p2wpkh outputs.
use bitcoin::address::script_pubkey::ScriptBufExt as _;
+use bitcoin::key::WPubkeyHash;
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing};
use bitcoin::sighash::{EcdsaSighashType, SighashCache};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
transaction, Address, Amount, Network, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut,
- Txid, WPubkeyHash, Witness,
+ Txid, Witness,
};
-const DUMMY_UTXO_AMOUNT: Amount = Amount::from_sat(20_000_000);
-const SPEND_AMOUNT: Amount = Amount::from_sat(5_000_000);
-const CHANGE_AMOUNT: Amount = Amount::from_sat(14_999_000); // 1000 sat fee.
+const DUMMY_UTXO_AMOUNT: Amount = Amount::from_sat_u32(20_000_000);
+const SPEND_AMOUNT: Amount = Amount::from_sat_u32(5_000_000);
+const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(14_999_000); // 1000 sat fee.
fn main() {
let secp = Secp256k1::new();
diff --git a/bitcoin/examples/sign-tx-taproot.rs b/bitcoin/examples/sign-tx-taproot.rs
index b7e22aa4d8..06a3fab162 100644
--- a/bitcoin/examples/sign-tx-taproot.rs
+++ b/bitcoin/examples/sign-tx-taproot.rs
@@ -13,9 +13,9 @@ use bitcoin::{
Txid, Witness,
};
-const DUMMY_UTXO_AMOUNT: Amount = Amount::from_sat(20_000_000);
-const SPEND_AMOUNT: Amount = Amount::from_sat(5_000_000);
-const CHANGE_AMOUNT: Amount = Amount::from_sat(14_999_000); // 1000 sat fee.
+const DUMMY_UTXO_AMOUNT: Amount = Amount::from_sat_u32(20_000_000);
+const SPEND_AMOUNT: Amount = Amount::from_sat_u32(5_000_000);
+const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(14_999_000); // 1000 sat fee.
fn main() {
let secp = Secp256k1::new();
@@ -71,7 +71,7 @@ fn main() {
// Sign the sighash using the secp256k1 library (exported by rust-bitcoin).
let tweaked: TweakedKeypair = keypair.tap_tweak(&secp, None);
let msg = Message::from(sighash);
- let signature = secp.sign_schnorr(&msg, &tweaked.to_inner());
+ let signature = secp.sign_schnorr(msg.as_ref(), &tweaked.to_inner());
// Update the witness stack.
let signature = bitcoin::taproot::Signature { signature, sighash_type };
diff --git a/bitcoin/examples/taproot-psbt-simple.rs b/bitcoin/examples/taproot-psbt-simple.rs
index 372c1a9693..8181573a27 100644
--- a/bitcoin/examples/taproot-psbt-simple.rs
+++ b/bitcoin/examples/taproot-psbt-simple.rs
@@ -45,12 +45,12 @@ const BIP86_DERIVATION_PATH: &str = "m/86'/0'/0'";
const MASTER_FINGERPRINT: &str = "9680603f";
// The dummy UTXO amounts we are spending.
-const DUMMY_UTXO_AMOUNT_INPUT_1: Amount = Amount::from_sat(20_000_000);
-const DUMMY_UTXO_AMOUNT_INPUT_2: Amount = Amount::from_sat(10_000_000);
+const DUMMY_UTXO_AMOUNT_INPUT_1: Amount = Amount::from_sat_u32(20_000_000);
+const DUMMY_UTXO_AMOUNT_INPUT_2: Amount = Amount::from_sat_u32(10_000_000);
// The amounts we are sending to someone, and receiving back as change.
-const SPEND_AMOUNT: Amount = Amount::from_sat(25_000_000);
-const CHANGE_AMOUNT: Amount = Amount::from_sat(4_990_000); // 10_000 sat fee.
+const SPEND_AMOUNT: Amount = Amount::from_sat_u32(25_000_000);
+const CHANGE_AMOUNT: Amount = Amount::from_sat_u32(4_990_000); // 10_000 sat fee.
// Derive the external address xpriv.
fn get_external_address_xpriv(
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 4a69d718fd..92af851561 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -40,7 +40,7 @@ const UTXO_SCRIPT_PUBKEY: &str =
"5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16";
const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19";
const UTXO_MASTER_FINGERPRINT: &str = "e61b318f";
-const ABSOLUTE_FEES_IN_SATS: Amount = Amount::from_sat(1_000);
+const ABSOLUTE_FEES: Amount = Amount::from_sat_u32(1_000);
// UTXO_1 will be used for spending example 1
const UTXO_1: P2trUtxo = P2trUtxo {
@@ -49,7 +49,7 @@ const UTXO_1: P2trUtxo = P2trUtxo {
script_pubkey: UTXO_SCRIPT_PUBKEY,
pubkey: UTXO_PUBKEY,
master_fingerprint: UTXO_MASTER_FINGERPRINT,
- amount_in_sats: Amount::from_int_btc_const(50),
+ amount: Amount::FIFTY_BTC,
derivation_path: BIP86_DERIVATION_PATH,
};
@@ -60,7 +60,7 @@ const UTXO_2: P2trUtxo = P2trUtxo {
script_pubkey: UTXO_SCRIPT_PUBKEY,
pubkey: UTXO_PUBKEY,
master_fingerprint: UTXO_MASTER_FINGERPRINT,
- amount_in_sats: Amount::from_int_btc_const(50),
+ amount: Amount::FIFTY_BTC,
derivation_path: BIP86_DERIVATION_PATH,
};
@@ -71,7 +71,7 @@ const UTXO_3: P2trUtxo = P2trUtxo {
script_pubkey: UTXO_SCRIPT_PUBKEY,
pubkey: UTXO_PUBKEY,
master_fingerprint: UTXO_MASTER_FINGERPRINT,
- amount_in_sats: Amount::from_int_btc_const(50),
+ amount: Amount::FIFTY_BTC,
derivation_path: BIP86_DERIVATION_PATH,
};
@@ -106,11 +106,11 @@ fn main() -> Result<(), Box> {
let change_address = "bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76"
.parse::>()?
.require_network(Network::Regtest)?;
- let amount_to_send_in_sats = Amount::ONE_BTC;
+ let amount_to_send = Amount::ONE_BTC;
let change_amount = UTXO_1
- .amount_in_sats
- .checked_sub(amount_to_send_in_sats)
- .and_then(|x| x.checked_sub(ABSOLUTE_FEES_IN_SATS))
+ .amount
+ .checked_sub(amount_to_send)
+ .and_then(|x| x.checked_sub(ABSOLUTE_FEES))
.ok_or("fees more than input amount!")?;
let tx_hex_string = encode::serialize_hex(&generate_bip86_key_spend_tx(
@@ -120,7 +120,7 @@ fn main() -> Result<(), Box> {
// Set these fields with valid data for the UTXO from step 5 above
UTXO_1,
vec![
- TxOut { value: amount_to_send_in_sats, script_pubkey: to_address.script_pubkey() },
+ TxOut { value: amount_to_send, script_pubkey: to_address.script_pubkey() },
TxOut { value: change_amount, script_pubkey: change_address.script_pubkey() },
],
)?);
@@ -215,7 +215,7 @@ struct P2trUtxo<'a> {
script_pubkey: &'a str,
pubkey: &'a str,
master_fingerprint: &'a str,
- amount_in_sats: Amount,
+ amount: Amount,
derivation_path: &'a str,
}
@@ -226,7 +226,7 @@ fn generate_bip86_key_spend_tx(
input_utxo: P2trUtxo,
outputs: Vec,
) -> Result> {
- let from_amount = input_utxo.amount_in_sats;
+ let from_amount = input_utxo.amount;
let input_pubkey = input_utxo.pubkey.parse::()?;
// CREATOR + UPDATER
@@ -274,7 +274,7 @@ fn generate_bip86_key_spend_tx(
let mut input_txouts = Vec::::new();
for input in [&input_utxo].iter() {
input_txouts.push(TxOut {
- value: input.amount_in_sats,
+ value: input.amount,
script_pubkey: ScriptBuf::from_hex(input.script_pubkey)?,
});
}
@@ -412,7 +412,8 @@ impl BenefactorWallet {
taproot_spend_info.internal_key(),
taproot_spend_info.merkle_root(),
);
- let value = input_utxo.amount_in_sats - ABSOLUTE_FEES_IN_SATS;
+ let value = (input_utxo.amount - ABSOLUTE_FEES)
+ .expect("ABSOLUTE_FEES must be set below input amount");
// Spend a normal BIP86-like output as an input in our inheritance funding transaction
let tx = generate_bip86_key_spend_tx(
@@ -476,7 +477,7 @@ impl BenefactorWallet {
let mut psbt = self.next_psbt.clone().expect("should have next_psbt");
let input = &mut psbt.inputs[0];
let input_value = input.witness_utxo.as_ref().unwrap().value;
- let output_value = input_value - ABSOLUTE_FEES_IN_SATS;
+ let output_value = (input_value - ABSOLUTE_FEES).into_result()?;
// We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure
// that we use an unhardened path so we can make use of xpubs.
@@ -649,7 +650,8 @@ impl BeneficiaryWallet {
psbt.unsigned_tx.lock_time = lock_time;
psbt.unsigned_tx.output = vec![TxOut {
script_pubkey: to_address.script_pubkey(),
- value: input_value - ABSOLUTE_FEES_IN_SATS,
+ value: (input_value - ABSOLUTE_FEES)
+ .expect("ABSOLUTE_FEES must be set below input amount"),
}];
psbt.outputs = vec![Output::default()];
let unsigned_tx = psbt.unsigned_tx.clone();
@@ -747,7 +749,7 @@ fn sign_psbt_taproot(
};
let msg = secp256k1::Message::from(hash);
- let signature = secp.sign_schnorr(&msg, &keypair);
+ let signature = secp.sign_schnorr(msg.as_ref(), &keypair);
let final_signature = taproot::Signature { signature, sighash_type };
diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs
index b972f7b124..c174710994 100644
--- a/bitcoin/src/address/error.rs
+++ b/bitcoin/src/address/error.rs
@@ -1,5 +1,6 @@
//! Error code for the address module.
+use core::convert::Infallible;
use core::fmt;
use internals::write_err;
@@ -21,7 +22,9 @@ pub enum FromScriptError {
WitnessVersion(witness_version::TryFromError),
}
-internals::impl_from_infallible!(FromScriptError);
+impl From for FromScriptError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for FromScriptError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -78,13 +81,15 @@ impl std::error::Error for UnknownAddressTypeError {
pub enum ParseError {
/// Base58 legacy decoding error.
Base58(Base58Error),
- /// Bech32 segwit decoding error.
+ /// Bech32 SegWit decoding error.
Bech32(Bech32Error),
/// Address's network differs from required one.
NetworkValidation(NetworkValidationError),
}
-internals::impl_from_infallible!(ParseError);
+impl From for ParseError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -165,7 +170,7 @@ impl std::error::Error for NetworkValidationError {}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Bech32Error {
- /// Parse segwit Bech32 error.
+ /// Parse SegWit Bech32 error.
ParseBech32(ParseBech32Error),
/// A witness version conversion/parsing error.
WitnessVersion(witness_version::TryFromError),
@@ -175,14 +180,16 @@ pub enum Bech32Error {
UnknownHrp(UnknownHrpError),
}
-internals::impl_from_infallible!(Bech32Error);
+impl From for Bech32Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Bech32Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Bech32Error::*;
match *self {
- ParseBech32(ref e) => write_err!(f, "segwit parsing error"; e),
+ ParseBech32(ref e) => write_err!(f, "SegWit parsing error"; e),
WitnessVersion(ref e) => write_err!(f, "witness version conversion/parsing error"; e),
WitnessProgram(ref e) => write_err!(f, "witness program error"; e),
UnknownHrp(ref e) => write_err!(f, "unknown hrp error"; e),
@@ -221,7 +228,9 @@ impl From for Bech32Error {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseBech32Error(pub(crate) bech32::segwit::DecodeError);
-internals::impl_from_infallible!(ParseBech32Error);
+impl From for ParseBech32Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for ParseBech32Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -248,7 +257,9 @@ pub enum Base58Error {
InvalidLegacyPrefix(InvalidLegacyPrefixError),
}
-internals::impl_from_infallible!(Base58Error);
+impl From for Base58Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Base58Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -323,7 +334,11 @@ pub struct LegacyAddressTooLongError {
impl LegacyAddressTooLongError {
/// Returns the invalid legacy address length.
- pub fn invalid_legcay_address_length(&self) -> usize { self.length }
+ pub fn invalid_legacy_address_length(&self) -> usize { self.length }
+
+ #[doc(hidden)]
+ #[deprecated = "Use invalid_legacy_address_length() instead"]
+ pub fn invalid_legcay_address_length(&self) -> usize { self.invalid_legacy_address_length() }
}
impl fmt::Display for LegacyAddressTooLongError {
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index c7053f6674..93bc290d87 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -2,28 +2,41 @@
//! Bitcoin addresses.
//!
-//! Support for ordinary base58 Bitcoin addresses and private keys.
+//! Support for segwit and legacy addresses (bech32 and base58 respectively).
//!
-//! # Example: creating a new address from a randomly-generated key pair
+//! # Examples
+//!
+//! ### Creating a new address from a randomly-generated key pair.
//!
//! ```rust
-//! # #[cfg(feature = "rand-std")] {
-//! use bitcoin::{Address, PublicKey, Network};
+//! #[cfg(feature = "rand-std")] {
//! use bitcoin::secp256k1::{rand, Secp256k1};
+//! use bitcoin::{Address, Network, PublicKey};
//!
//! // Generate random key pair.
-//! let s = Secp256k1::new();
-//! let public_key = PublicKey::new(s.generate_keypair(&mut rand::thread_rng()).1);
+//! let secp = Secp256k1::new();
+//! let (_sk, pk) = secp.generate_keypair(&mut rand::thread_rng());
+//! let public_key = PublicKey::new(pk); // Or `PublicKey::from(pk)`.
//!
-//! // Generate pay-to-pubkey-hash address.
+//! // Generate a mainnet pay-to-pubkey-hash address.
//! let address = Address::p2pkh(&public_key, Network::Bitcoin);
-//! # }
+//! }
//! ```
//!
-//! # Note: creating a new address requires the rand-std feature flag
+//! ### Using an `Address` as a struct field.
//!
-//! ```toml
-//! bitcoin = { version = "...", features = ["rand-std"] }
+//! ```rust
+//! # #[cfg(feature = "serde")] {
+//! # use serde::{self, Deserialize, Serialize};
+//! use bitcoin::address::{Address, NetworkValidation, NetworkValidationUnchecked};
+//! #[derive(Serialize, Deserialize)]
+//! struct Foo
+//! where V: NetworkValidation,
+//! {
+//! #[serde(bound(deserialize = "V: NetworkValidationUnchecked"))]
+//! address: Address,
+//! }
+//! # }
//! ```
pub mod error;
@@ -36,6 +49,7 @@ use core::str::FromStr;
use bech32::primitives::gf32::Fe32;
use bech32::primitives::hrp::Hrp;
use hashes::{hash160, HashEngine};
+use internals::array::ArrayExt;
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use crate::address::script_pubkey::ScriptBufExt as _;
@@ -78,6 +92,8 @@ pub enum AddressType {
P2wsh,
/// Pay to taproot.
P2tr,
+ /// Pay to anchor.
+ P2a,
}
impl fmt::Display for AddressType {
@@ -88,6 +104,7 @@ impl fmt::Display for AddressType {
AddressType::P2wpkh => "p2wpkh",
AddressType::P2wsh => "p2wsh",
AddressType::P2tr => "p2tr",
+ AddressType::P2a => "p2a",
})
}
}
@@ -101,6 +118,7 @@ impl FromStr for AddressType {
"p2wpkh" => Ok(AddressType::P2wpkh),
"p2wsh" => Ok(AddressType::P2wsh),
"p2tr" => Ok(AddressType::P2tr),
+ "p2a" => Ok(AddressType::P2a),
_ => Err(UnknownAddressTypeError(s.to_owned())),
}
}
@@ -110,6 +128,9 @@ mod sealed {
pub trait NetworkValidation {}
impl NetworkValidation for super::NetworkChecked {}
impl NetworkValidation for super::NetworkUnchecked {}
+
+ pub trait NetworkValidationUnchecked {}
+ impl NetworkValidationUnchecked for super::NetworkUnchecked {}
}
/// Marker of status of address's network validation. See section [*Parsing addresses*](Address#parsing-addresses)
@@ -119,14 +140,25 @@ pub trait NetworkValidation: sealed::NetworkValidation + Sync + Send + Sized + U
const IS_CHECKED: bool;
}
+/// Marker trait for `FromStr` and `serde::Deserialize`.
+///
+/// This allows users to use `V: NetworkValidation` in conjunction with derives. Is only ever
+/// implemented for `NetworkUnchecked`.
+pub trait NetworkValidationUnchecked:
+ NetworkValidation + sealed::NetworkValidationUnchecked + Sync + Send + Sized + Unpin
+{
+}
+
/// Marker that address's network has been successfully validated. See section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`] for details.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NetworkChecked {}
/// Marker that address's network has not yet been validated. See section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`] for details.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NetworkUnchecked {}
impl NetworkValidation for NetworkChecked {
@@ -136,6 +168,8 @@ impl NetworkValidation for NetworkUnchecked {
const IS_CHECKED: bool = false;
}
+impl NetworkValidationUnchecked for NetworkUnchecked {}
+
/// The inner representation of an address, without the network validation tag.
///
/// This struct represents the inner representation of an address without the network validation
@@ -240,6 +274,16 @@ impl From for KnownHrp {
fn from(n: Network) -> Self { Self::from_network(n) }
}
+impl From for NetworkKind {
+ fn from(hrp: KnownHrp) -> Self {
+ match hrp {
+ KnownHrp::Mainnet => NetworkKind::Main,
+ KnownHrp::Testnets => NetworkKind::Test,
+ KnownHrp::Regtest => NetworkKind::Test,
+ }
+ }
+}
+
/// The data encoded by an `Address`.
///
/// This is the data used to encumber an output that pays to this address i.e., it is the address
@@ -257,7 +301,7 @@ pub enum AddressData {
/// The script hash used to encumber outputs to this address.
script_hash: ScriptHash,
},
- /// Data encoded by a Segwit address.
+ /// Data encoded by a SegWit address.
Segwit {
/// The witness program used to encumber outputs to this address.
witness_program: WitnessProgram,
@@ -364,10 +408,41 @@ impl fmt::Display for DisplayUnchecked<'_, N> {
}
#[cfg(feature = "serde")]
-internals::serde_string_deserialize_impl!(Address, "a Bitcoin address");
+impl<'de, U: NetworkValidationUnchecked> serde::Deserialize<'de> for Address {
+ fn deserialize(deserializer: D) -> Result, D::Error>
+ where
+ D: serde::de::Deserializer<'de>,
+ {
+ use core::fmt::Formatter;
+
+ struct Visitor(PhantomData);
+ impl serde::de::Visitor<'_> for Visitor
+ where
+ U: NetworkValidationUnchecked + NetworkValidation,
+ Address: FromStr,
+ {
+ type Value = Address;
+
+ fn expecting(&self, f: &mut Formatter) -> core::fmt::Result {
+ f.write_str("A Bitcoin address")
+ }
+
+ fn visit_str(self, v: &str) -> core::result::Result
+ where
+ E: serde::de::Error,
+ {
+ // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
+ let address = v.parse::>().map_err(E::custom)?;
+ Ok(Address(address.0, PhantomData::))
+ }
+ }
+
+ deserializer.deserialize_str(Visitor(PhantomData::))
+ }
+}
#[cfg(feature = "serde")]
-impl serde::Serialize for Address {
+impl serde::Serialize for Address {
fn serialize(&self, serializer: S) -> Result
where
S: serde::Serializer,
@@ -383,6 +458,19 @@ impl Address {
pub fn as_unchecked(&self) -> &Address {
unsafe { &*(self as *const Address as *const Address) }
}
+
+ /// Marks the network of this address as unchecked.
+ pub fn into_unchecked(self) -> Address { Address(self.0, PhantomData) }
+
+ /// Returns the [`NetworkKind`] of this address.
+ pub fn network_kind(&self) -> NetworkKind {
+ use AddressInner::*;
+ match self.0 {
+ P2pkh { hash: _, ref network } => *network,
+ P2sh { hash: _, ref network } => *network,
+ Segwit { program: _, ref hrp } => NetworkKind::from(*hrp),
+ }
+ }
}
/// Methods and functions that can be called only on `Address`.
@@ -421,7 +509,7 @@ impl Address {
/// Constructs a new pay-to-witness-public-key-hash (P2WPKH) [`Address`] from a public key.
///
- /// This is the native segwit address type for an output redeemable with a single signature.
+ /// This is the native SegWit address type for an output redeemable with a single signature.
pub fn p2wpkh(pk: CompressedPublicKey, hrp: impl Into) -> Self {
let program = WitnessProgram::p2wpkh(pk);
Address::from_witness_program(program, hrp)
@@ -430,7 +518,7 @@ impl Address {
/// Constructs a new pay-to-script-hash (P2SH) [`Address`] that embeds a
/// pay-to-witness-public-key-hash (P2WPKH).
///
- /// This is a segwit address type that looks familiar (as p2sh) to legacy clients.
+ /// This is a SegWit address type that looks familiar (as p2sh) to legacy clients.
pub fn p2shwpkh(pk: CompressedPublicKey, network: impl Into) -> Address {
let builder = script::Builder::new().push_int_unchecked(0).push_slice(pk.wpubkey_hash());
let script_hash = builder.as_script().script_hash().expect("script is less than 520 bytes");
@@ -455,7 +543,7 @@ impl Address {
/// Constructs a new pay-to-script-hash (P2SH) [`Address`] that embeds a
/// pay-to-witness-script-hash (P2WSH).
///
- /// This is a segwit address type that looks familiar (as p2sh) to legacy clients.
+ /// This is a SegWit address type that looks familiar (as p2sh) to legacy clients.
pub fn p2shwsh(
witness_script: &Script,
network: impl Into,
@@ -509,6 +597,8 @@ impl Address {
Some(AddressType::P2wsh)
} else if program.is_p2tr() {
Some(AddressType::P2tr)
+ } else if program.is_p2a() {
+ Some(AddressType::P2a)
} else {
None
},
@@ -546,7 +636,7 @@ impl Address {
}
}
- /// Gets the witness program for this address if this is a segwit address.
+ /// Gets the witness program for this address if this is a SegWit address.
pub fn witness_program(&self) -> Option {
use AddressInner::*;
@@ -642,7 +732,7 @@ impl Address {
/// Returns true if the given pubkey is directly related to the address payload.
///
/// This is determined by directly comparing the address payload with either the
- /// hash of the given public key or the segwit redeem hash generated from the
+ /// hash of the given public key or the SegWit redeem hash generated from the
/// given key. For Taproot addresses, the supplied key is assumed to be tweaked
pub fn is_related_to_pubkey(&self, pubkey: PublicKey) -> bool {
let pubkey_hash = pubkey.pubkey_hash();
@@ -684,7 +774,7 @@ impl Address {
///
/// - For p2sh, the payload is the script hash.
/// - For p2pkh, the payload is the pubkey hash.
- /// - For segwit addresses, the payload is the witness program.
+ /// - For SegWit addresses, the payload is the witness program.
fn payload_as_bytes(&self) -> &[u8] {
use AddressInner::*;
match self.0 {
@@ -705,7 +795,7 @@ impl Address {
}
/// Parsed addresses do not always have *one* network. The problem is that legacy testnet,
- /// regtest and signet addresse use the same prefix instead of multiple different ones. When
+ /// regtest and signet addresses use the same prefix instead of multiple different ones. When
/// parsing, such addresses are always assumed to be testnet addresses (the same is true for
/// bech32 signet addresses). So if one wants to check if an address belongs to a certain
/// network a simple comparison is not enough anymore. Instead this function can be used.
@@ -792,16 +882,7 @@ impl Address {
/// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`].
#[inline]
- pub fn assume_checked(self) -> Address {
- use AddressInner::*;
-
- let inner = match self.0 {
- P2pkh { hash, network } => P2pkh { hash, network },
- P2sh { hash, network } => P2sh { hash, network },
- Segwit { program, hrp } => Segwit { program, hrp },
- };
- Address(inner, PhantomData)
- }
+ pub fn assume_checked(self) -> Address { Address(self.0, PhantomData) }
/// Parse a bech32 Address string
pub fn from_bech32_str(s: &str) -> Result, Bech32Error> {
@@ -822,12 +903,9 @@ impl Address {
return Err(LegacyAddressTooLongError { length: s.len() }.into());
}
let data = base58::decode_check(s)?;
- if data.len() != 21 {
- return Err(InvalidBase58PayloadLengthError { length: s.len() }.into());
- }
+ let data: &[u8; 21] = (&*data).try_into().map_err(|_| InvalidBase58PayloadLengthError { length: s.len() })?;
- let (prefix, data) = data.split_first().expect("length checked above");
- let data: [u8; 20] = data.try_into().expect("length checked above");
+ let (prefix, &data) = data.split_first();
let inner = match *prefix {
PUBKEY_ADDRESS_PREFIX_MAIN => {
@@ -877,27 +955,30 @@ impl fmt::Debug for Address {
/// Address can be parsed only with `NetworkUnchecked`.
///
-/// Only segwit bech32 addresses prefixed with `bc`, `bcrt` or `tb` and legacy base58 addresses
-/// prefixed with `1`, `2, `3`, `m` or `n` are supported.
+/// Only SegWit bech32 addresses prefixed with `bc`, `bcrt` or `tb` and legacy base58 addresses
+/// prefixed with `1`, `2`, `3`, `m` or `n` are supported.
///
/// # Errors
///
-/// - [`ParseError::Bech32`] if the segwit address begins with a `bc`, `bcrt` or `tb` and is not a
+/// - [`ParseError::Bech32`] if the SegWit address begins with a `bc`, `bcrt` or `tb` and is not a
/// valid bech32 address.
///
/// - [`ParseError::Base58`] if the legacy address begins with a `1`, `2`, `3`, `m` or `n` and is
/// not a valid base58 address.
///
-/// - [`UnknownHrpError`] if the address does not begin with one of the above segwit or
-/// legacy prifixes.
-impl FromStr for Address {
+/// - [`UnknownHrpError`] if the address does not begin with one of the above SegWit or
+/// legacy prefixes.
+impl FromStr for Address {
type Err = ParseError;
- fn from_str(s: &str) -> Result, ParseError> {
+ fn from_str(s: &str) -> Result {
if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
- Ok(Address::from_bech32_str(s)?)
+ let address = Address::from_bech32_str(s)?;
+ // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
+ Ok(Address(address.0, PhantomData::))
} else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
- Ok(Address::from_base58_str(s)?)
+ let address = Address::from_base58_str(s)?;
+ Ok(Address(address.0, PhantomData::))
} else {
let hrp = match s.rfind('1') {
Some(pos) => &s[..pos],
@@ -908,7 +989,7 @@ impl FromStr for Address {
}
}
-/// Convert a byte array of a pubkey hash into a segwit redeem hash
+/// Convert a byte array of a pubkey hash into a SegWit redeem hash
fn segwit_redeem_hash(pubkey_hash: PubkeyHash) -> hash160::Hash {
let mut sha_engine = hash160::Hash::engine();
sha_engine.input(&[0, 20]);
@@ -950,7 +1031,7 @@ mod tests {
}
#[test]
- fn test_p2pkh_address_58() {
+ fn p2pkh_address_58() {
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::().unwrap();
let addr = Address::p2pkh(hash, NetworkKind::Main);
@@ -964,7 +1045,7 @@ mod tests {
}
#[test]
- fn test_p2pkh_from_key() {
+ fn p2pkh_from_key() {
let key = "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183".parse::().unwrap();
let addr = Address::p2pkh(key, NetworkKind::Main);
assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY");
@@ -979,7 +1060,7 @@ mod tests {
}
#[test]
- fn test_p2sh_address_58() {
+ fn p2sh_address_58() {
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::().unwrap();
let addr = Address::p2sh_from_hash(hash, NetworkKind::Main);
@@ -993,7 +1074,7 @@ mod tests {
}
#[test]
- fn test_p2sh_parse() {
+ fn p2sh_parse() {
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae").unwrap();
let addr = Address::p2sh(&script, NetworkKind::Test).unwrap();
assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
@@ -1002,16 +1083,14 @@ mod tests {
}
#[test]
- fn test_p2sh_parse_for_large_script() {
+ fn p2sh_parse_for_large_script() {
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap();
- assert_eq!(
- Address::p2sh(&script, NetworkKind::Test),
- Err(RedeemScriptSizeError { size: script.len() })
- );
+ let res = Address::p2sh(&script, NetworkKind::Test);
+ assert_eq!(res.unwrap_err().invalid_size(), script.len())
}
#[test]
- fn test_p2wpkh() {
+ fn p2wpkh() {
// stolen from Bitcoin transaction: b3c8c2b6cfc335abbcb2c7823a8453f55d64b2b5125a9a61e8737230cdb8ce20
let key = "033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc"
.parse::()
@@ -1023,7 +1102,7 @@ mod tests {
}
#[test]
- fn test_p2wsh() {
+ fn p2wsh() {
// stolen from Bitcoin transaction 5df912fda4becb1c29e928bec8d64d93e9ba8efa9b5b405bd683c86fd2c65667
let script = ScriptBuf::from_hex("52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae").unwrap();
let addr = Address::p2wsh(&script, KnownHrp::Mainnet).expect("script is valid");
@@ -1036,7 +1115,7 @@ mod tests {
}
#[test]
- fn test_p2shwpkh() {
+ fn p2shwpkh() {
// stolen from Bitcoin transaction: ad3fd9c6b52e752ba21425435ff3dd361d6ac271531fc1d2144843a9f550ad01
let key = "026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"
.parse::()
@@ -1048,7 +1127,7 @@ mod tests {
}
#[test]
- fn test_p2shwsh() {
+ fn p2shwsh() {
// stolen from Bitcoin transaction f9ee2be4df05041d0e0a35d7caa3157495ca4f93b233234c9967b6901dacf7a9
let script = ScriptBuf::from_hex("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap();
let addr = Address::p2shwsh(&script, NetworkKind::Main).expect("script is valid");
@@ -1058,7 +1137,7 @@ mod tests {
}
#[test]
- fn test_non_existent_segwit_version() {
+ fn non_existent_segwit_version() {
// 40-byte program
let program = hex!(
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
@@ -1070,7 +1149,7 @@ mod tests {
}
#[test]
- fn test_address_debug() {
+ fn address_debug() {
// This is not really testing output of Debug but the ability and proper functioning
// of Debug derivation on structs generic in NetworkValidation.
#[derive(Debug)]
@@ -1094,7 +1173,7 @@ mod tests {
}
#[test]
- fn test_address_type() {
+ fn address_type() {
let addresses = [
("1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY", Some(AddressType::P2pkh)),
("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k", Some(AddressType::P2sh)),
@@ -1108,9 +1187,9 @@ mod tests {
Some(AddressType::P2tr),
),
// Related to future extensions, addresses are valid but have no type
- // segwit v1 and len != 32
+ // SegWit v1 and len != 32
("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", None),
- // segwit v2
+ // SegWit v2
("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", None),
];
for (address, expected_type) in &addresses {
@@ -1125,7 +1204,7 @@ mod tests {
#[test]
#[cfg(feature = "serde")]
- fn test_json_serialize() {
+ fn json_serialize() {
use serde_json;
let addr =
@@ -1207,7 +1286,7 @@ mod tests {
}
#[test]
- fn test_qr_string() {
+ fn qr_string() {
for el in
["132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM", "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"].iter()
{
@@ -1247,7 +1326,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_pubkey_p2wpkh() {
+ fn is_related_to_pubkey_p2wpkh() {
let address_string = "bc1qhvd6suvqzjcu9pxjhrwhtrlj85ny3n2mqql5w4";
let address = address_string
.parse::>()
@@ -1268,7 +1347,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_pubkey_p2shwpkh() {
+ fn is_related_to_pubkey_p2shwpkh() {
let address_string = "3EZQk4F8GURH5sqVMLTFisD17yNeKa7Dfs";
let address = address_string
.parse::>()
@@ -1289,7 +1368,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_pubkey_p2pkh() {
+ fn is_related_to_pubkey_p2pkh() {
let address_string = "1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx";
let address = address_string
.parse::>()
@@ -1310,7 +1389,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_pubkey_p2pkh_uncompressed_key() {
+ fn is_related_to_pubkey_p2pkh_uncompressed_key() {
let address_string = "msvS7KzhReCDpQEJaV2hmGNvuQqVUDuC6p";
let address = address_string
.parse::>()
@@ -1331,7 +1410,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_pubkey_p2tr() {
+ fn is_related_to_pubkey_p2tr() {
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
let pubkey = pubkey_string.parse::().expect("pubkey");
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
@@ -1357,7 +1436,7 @@ mod tests {
}
#[test]
- fn test_is_related_to_xonly_pubkey() {
+ fn is_related_to_xonly_pubkey() {
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
let pubkey = pubkey_string.parse::().expect("pubkey");
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
@@ -1378,7 +1457,7 @@ mod tests {
}
#[test]
- fn test_fail_address_from_script() {
+ fn fail_address_from_script() {
use crate::witness_program;
let bad_p2wpkh = ScriptBuf::from_hex("0014dbc5b0a8f9d4353b4b54c3db48846bb15abfec").unwrap();
@@ -1412,7 +1491,7 @@ mod tests {
}
#[test]
- fn test_matches_script_pubkey() {
+ fn matches_script_pubkey() {
let addresses = [
"1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY",
"1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx",
@@ -1436,4 +1515,55 @@ mod tests {
}
}
}
+
+ #[test]
+ #[cfg(feature = "serde")]
+ fn serde_address_usage_in_struct() {
+ use serde::{self, Deserialize, Serialize};
+
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+ struct Foo
+ where
+ V: NetworkValidation,
+ {
+ #[serde(bound(deserialize = "V: NetworkValidationUnchecked"))]
+ address: Address,
+ }
+
+ let addr_str = "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k";
+ let unchecked = addr_str.parse::>().unwrap();
+
+ // Serialize with an unchecked address.
+ let foo_unchecked = Foo { address: unchecked.clone() };
+ let ser = serde_json::to_string(&foo_unchecked).expect("failed to serialize");
+ let rinsed: Foo =
+ serde_json::from_str(&ser).expect("failed to deserialize");
+ assert_eq!(rinsed, foo_unchecked);
+
+ // Serialize with a checked address.
+ let foo_checked = Foo { address: unchecked.assume_checked() };
+ let ser = serde_json::to_string(&foo_checked).expect("failed to serialize");
+ let rinsed: Foo =
+ serde_json::from_str(&ser).expect("failed to deserialize");
+ assert_eq!(&rinsed.address, foo_checked.address.as_unchecked());
+ assert_eq!(rinsed, foo_unchecked);
+ }
+
+ #[test]
+ fn pay_to_anchor_address_regtest() {
+ // Verify that p2a uses the expected address for regtest.
+ // This test-vector is borrowed from the bitcoin source code.
+ let address_str = "bcrt1pfeesnyr2tx";
+
+ let script = ScriptBuf::new_p2a();
+ let address_unchecked = address_str.parse().unwrap();
+ let address = Address::from_script(&script, Network::Regtest).unwrap();
+ assert_eq!(address.as_unchecked(), &address_unchecked);
+ assert_eq!(address.to_string(), address_str);
+
+ // Verify that the address is considered standard
+ // and that the output type is P2a
+ assert!(address.is_spend_standard());
+ assert_eq!(address.address_type(), Some(AddressType::P2a));
+ }
}
diff --git a/bitcoin/src/address/script_pubkey.rs b/bitcoin/src/address/script_pubkey.rs
index 2d801b0c87..b89008ca33 100644
--- a/bitcoin/src/address/script_pubkey.rs
+++ b/bitcoin/src/address/script_pubkey.rs
@@ -2,6 +2,7 @@
//! Bitcoin scriptPubkey script extensions.
+use internals::array::ArrayExt;
use secp256k1::{Secp256k1, Verification};
use crate::internal_macros::define_extension_trait;
@@ -10,7 +11,7 @@ use crate::key::{
XOnlyPublicKey,
};
use crate::opcodes::all::*;
-use crate::script::witness_program::WitnessProgram;
+use crate::script::witness_program::{WitnessProgram, P2A_PROGRAM};
use crate::script::witness_version::WitnessVersion;
use crate::script::{
self, Builder, PushBytes, RedeemScriptSizeError, Script, ScriptBuf, ScriptExt as _, ScriptHash,
@@ -99,14 +100,16 @@ define_extension_trait! {
pub(crate) trait ScriptExtPrivate impl for Script {
/// Returns the bytes of the (possibly invalid) public key if this script is P2PK.
fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
- match self.len() {
- 67 if self.as_bytes()[0] == OP_PUSHBYTES_65.to_u8()
- && self.as_bytes()[66] == OP_CHECKSIG.to_u8() =>
- Some(&self.as_bytes()[1..66]),
- 35 if self.as_bytes()[0] == OP_PUSHBYTES_33.to_u8()
- && self.as_bytes()[34] == OP_CHECKSIG.to_u8() =>
- Some(&self.as_bytes()[1..34]),
- _ => None,
+ if let Ok(bytes) = <&[u8; 67]>::try_from(self.as_bytes()) {
+ let (&first, bytes) = bytes.split_first::<66>();
+ let (&last, pubkey) = bytes.split_last::<65>();
+ (first == OP_PUSHBYTES_65.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
+ } else if let Ok(bytes) = <&[u8; 35]>::try_from(self.as_bytes()) {
+ let (&first, bytes) = bytes.split_first::<34>();
+ let (&last, pubkey) = bytes.split_last::<33>();
+ (first == OP_PUSHBYTES_33.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
+ } else {
+ None
}
}
}
@@ -170,6 +173,11 @@ define_extension_trait! {
new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
+ /// Generates pay to anchor output.
+ fn new_p2a() -> Self {
+ new_witness_program_unchecked(WitnessVersion::V1, P2A_PROGRAM)
+ }
+
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
fn new_witness_program(witness_program: &WitnessProgram) -> Self {
Builder::new()
@@ -183,14 +191,14 @@ define_extension_trait! {
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes.
/// Does not do any checks on version or program length.
///
-/// Convenience method used by `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
+/// Convenience method used by `new_p2a`, `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
pub(super) fn new_witness_program_unchecked>(
version: WitnessVersion,
program: T,
) -> ScriptBuf {
let program = program.as_ref();
debug_assert!(program.len() >= 2 && program.len() <= 40);
- // In segwit v0, the program must be 20 or 32 bytes long.
+ // In SegWit v0, the program must be either 20 (P2WPKH) bytes or 32 (P2WSH) bytes long
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
Builder::new().push_opcode(version.into()).push_slice(program).into_script()
}
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index 7fcfe38fca..bb25066d18 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -4,12 +4,13 @@
//!
//! Implementation of compact blocks data structure and algorithms.
+use core::convert::Infallible;
use core::{convert, fmt, mem};
#[cfg(feature = "std")]
use std::error;
use hashes::{sha256, siphash24};
-use internals::ToU64 as _;
+use internals::{ToU64 as _, array::ArrayExt as _};
use io::{BufRead, Write};
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
@@ -30,7 +31,9 @@ pub enum Error {
InvalidPrefill,
}
-internals::impl_from_infallible!(Error);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -114,8 +117,8 @@ impl ShortId {
// 2. Running SipHash-2-4 with the input being the transaction ID and the keys (k0/k1)
// set to the first two little-endian 64-bit integers from the above hash, respectively.
(
- u64::from_le_bytes(h.as_byte_array()[0..8].try_into().expect("8 byte slice")),
- u64::from_le_bytes(h.as_byte_array()[8..16].try_into().expect("8 byte slice")),
+ u64::from_le_bytes(*h.as_byte_array().sub_array::<0, 8>()),
+ u64::from_le_bytes(*h.as_byte_array().sub_array::<8, 8>()),
)
}
@@ -410,8 +413,8 @@ mod test {
use crate::merkle_tree::TxMerkleNode;
use crate::transaction::OutPointExt;
use crate::{
- transaction, Amount, BlockChecked, CompactTarget, OutPoint, ScriptBuf, Sequence, TxIn,
- TxOut, Txid, Witness,
+ transaction, Amount, BlockChecked, BlockTime, CompactTarget, OutPoint, ScriptBuf, Sequence,
+ TxIn, TxOut, Txid, Witness,
};
fn dummy_tx(nonce: &[u8]) -> Transaction {
@@ -434,7 +437,7 @@ mod test {
version: block::Version::ONE,
prev_blockhash: BlockHash::from_byte_array([0x99; 32]),
merkle_root: TxMerkleNode::from_byte_array([0x77; 32]),
- time: 2,
+ time: BlockTime::from_u32(2),
bits: CompactTarget::from_consensus(3),
nonce: 4,
};
@@ -443,7 +446,7 @@ mod test {
}
#[test]
- fn test_header_and_short_ids_from_block() {
+ fn header_and_short_ids_from_block() {
let block = dummy_block();
let compact = HeaderAndShortIds::from_block(&block, 42, 2, &[]).unwrap();
@@ -463,7 +466,7 @@ mod test {
}
#[test]
- fn test_compact_block_vector() {
+ fn compact_block_vector() {
// Tested with Elements implementation of compact blocks.
let raw_block = Vec::::from_hex("000000206c750a364035aefd5f81508a08769975116d9195312ee4520dceac39e1fdc62c4dc67473b8e354358c1e610afeaff7410858bd45df43e2940f8a62bd3d5e3ac943c2975cffff7f200000000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04016b0101ffffffff020006062a0100000001510000000000000000266a24aa21a9ed4a3d9f3343dafcc0d6f6d4310f2ee5ce273ed34edca6c75db3a73e7f368734200120000000000000000000000000000000000000000000000000000000000000000000000000020000000001021fc20ba2bd745507b8e00679e3b362558f9457db374ca28ffa5243f4c23a4d5f00000000171600147c9dea14ffbcaec4b575e03f05ceb7a81cd3fcbffdffffff915d689be87b43337f42e26033df59807b768223368f189a023d0242d837768900000000171600147c9dea14ffbcaec4b575e03f05ceb7a81cd3fcbffdffffff0200cdf5050000000017a9146803c72d9154a6a20f404bed6d3dcee07986235a8700e1f5050000000017a9144e6a4c7cb5b5562904843bdf816342f4db9f5797870247304402205e9bf6e70eb0e4b495bf483fd8e6e02da64900f290ef8aaa64bb32600d973c450220670896f5d0e5f33473e5f399ab680cc1d25c2d2afd15abd722f04978f28be887012103e4e4d9312b2261af508b367d8ba9be4f01b61d6d6e78bec499845b4f410bcf2702473044022045ac80596a6ac9c8c572f94708709adaf106677221122e08daf8b9741a04f66a022003ccd52a3b78f8fd08058fc04fc0cffa5f4c196c84eae9e37e2a85babe731b57012103e4e4d9312b2261af508b367d8ba9be4f01b61d6d6e78bec499845b4f410bcf276a000000").unwrap();
let raw_compact = Vec::::from_hex("000000206c750a364035aefd5f81508a08769975116d9195312ee4520dceac39e1fdc62c4dc67473b8e354358c1e610afeaff7410858bd45df43e2940f8a62bd3d5e3ac943c2975cffff7f2000000000a4df3c3744da89fa010a6979e971450100020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04016b0101ffffffff020006062a0100000001510000000000000000266a24aa21a9ed4a3d9f3343dafcc0d6f6d4310f2ee5ce273ed34edca6c75db3a73e7f368734200120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
@@ -478,7 +481,7 @@ mod test {
}
#[test]
- fn test_getblocktx_differential_encoding_de_and_serialization() {
+ fn getblocktx_differential_encoding_de_and_serialization() {
let testcases = vec![
// differentially encoded VarInts, indicies
(vec![4, 0, 5, 1, 10], vec![0, 6, 8, 19]),
@@ -523,7 +526,7 @@ mod test {
#[test]
#[cfg(debug_assertions)]
#[should_panic] // 'attempt to add with overflow' in consensus_encode()
- fn test_getblocktx_panic_when_encoding_u64_max() {
+ fn getblocktx_panic_when_encoding_u64_max() {
serialize(&BlockTransactionsRequest {
block_hash: BlockHash::from_byte_array([0; 32]),
indexes: vec![u64::MAX],
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 8a9188a2ce..abdde2a9c2 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -38,10 +38,11 @@
//! ```
use core::cmp::{self, Ordering};
+use core::convert::Infallible;
use core::fmt;
use hashes::{sha256d, siphash24, HashEngine as _};
-use internals::{write_err, ToU64 as _};
+use internals::{write_err, ToU64 as _, array::ArrayExt as _};
use io::{BufRead, Write};
use crate::block::{Block, BlockHash, Checked};
@@ -75,11 +76,13 @@ impl_hashencode!(FilterHeader);
pub enum Error {
/// Missing UTXO, cannot calculate script filter.
UtxoMissing(OutPoint),
- /// IO error reading or writing binary serialization of the filter.
+ /// I/O error reading or writing binary serialization of the filter.
Io(io::Error),
}
-internals::impl_from_infallible!(Error);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
@@ -87,7 +90,7 @@ impl fmt::Display for Error {
match *self {
UtxoMissing(ref coin) => write!(f, "unresolved UTXO {}", coin),
- Io(ref e) => write_err!(f, "IO error"; e),
+ Io(ref e) => write_err!(f, "I/O error"; e),
}
}
}
@@ -192,8 +195,8 @@ impl<'a, W: Write> BlockFilterWriter<'a, W> {
/// Constructs a new [`BlockFilterWriter`] from `block`.
pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> {
let block_hash_as_int = block.block_hash().to_byte_array();
- let k0 = u64::from_le_bytes(block_hash_as_int[0..8].try_into().expect("8 byte slice"));
- let k1 = u64::from_le_bytes(block_hash_as_int[8..16].try_into().expect("8 byte slice"));
+ let k0 = u64::from_le_bytes(*block_hash_as_int.sub_array::<0, 8>());
+ let k1 = u64::from_le_bytes(*block_hash_as_int.sub_array::<8, 8>());
let writer = GcsFilterWriter::new(writer, k0, k1, M, P);
BlockFilterWriter { block, writer }
}
@@ -247,8 +250,8 @@ impl BlockFilterReader {
/// Constructs a new [`BlockFilterReader`] from `block_hash`.
pub fn new(block_hash: BlockHash) -> BlockFilterReader {
let block_hash_as_int = block_hash.to_byte_array();
- let k0 = u64::from_le_bytes(block_hash_as_int[0..8].try_into().expect("8 byte slice"));
- let k1 = u64::from_le_bytes(block_hash_as_int[8..16].try_into().expect("8 byte slice"));
+ let k0 = u64::from_le_bytes(*block_hash_as_int.sub_array::<0, 8>());
+ let k1 = u64::from_le_bytes(*block_hash_as_int.sub_array::<8, 8>());
BlockFilterReader { reader: GcsFilterReader::new(k0, k1, M, P) }
}
@@ -582,7 +585,7 @@ mod test {
use crate::ScriptBuf;
#[test]
- fn test_blockfilters() {
+ fn blockfilters() {
// test vectors from: https://github.com/jimpo/bitcoin/blob/c7efb652f3543b001b4dd22186a354605b14f47e/src/test/data/blockfilters.json
let data = include_str!("../tests/data/blockfilters.json");
@@ -649,7 +652,7 @@ mod test {
}
#[test]
- fn test_filter() {
+ fn filter() {
let mut patterns = BTreeSet::new();
patterns.insert(hex!("000000"));
@@ -718,7 +721,7 @@ mod test {
}
#[test]
- fn test_bit_stream() {
+ fn bit_stream() {
let mut out = Vec::new();
{
let mut writer = BitStreamWriter::new(&mut out);
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index 8f11e67b83..73d74ffc82 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -5,11 +5,13 @@
//! Implementation of BIP32 hierarchical deterministic wallets, as defined
//! at .
+use core::convert::Infallible;
use core::ops::Index;
use core::str::FromStr;
use core::{fmt, slice};
-use hashes::{hash160, hash_newtype, sha512, GeneralHash, HashEngine, Hmac, HmacEngine};
+use hashes::{hash160, hash_newtype, sha512, Hash, HashEngine, Hmac, HmacEngine};
+use internals::array::ArrayExt;
use internals::write_err;
use secp256k1::{Secp256k1, XOnlyPublicKey};
@@ -43,7 +45,7 @@ impl_array_newtype_stringify!(ChainCode, 32);
impl ChainCode {
fn from_hmac(hmac: Hmac) -> Self {
- hmac.as_ref()[32..].try_into().expect("half of hmac is guaranteed to be 32 bytes")
+ ChainCode(*hmac.as_byte_array().split_array::<32, 32>().1)
}
}
@@ -517,9 +519,17 @@ pub enum Error {
InvalidPublicKeyHexLength(usize),
/// Base58 decoded data was an invalid length.
InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
+ /// Invalid private key prefix (byte 45 must be 0)
+ InvalidPrivateKeyPrefix,
+ /// Non-zero parent fingerprint for a master key (depth 0)
+ NonZeroParentFingerprintForMasterKey,
+ /// Non-zero child number for a master key (depth 0)
+ NonZeroChildNumberForMasterKey,
}
-internals::impl_from_infallible!(Error);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -541,6 +551,11 @@ impl fmt::Display for Error {
InvalidPublicKeyHexLength(got) =>
write!(f, "PublicKey hex should be 66 or 130 digits long, got: {}", got),
InvalidBase58PayloadLength(ref e) => write_err!(f, "base58 payload"; e),
+ InvalidPrivateKeyPrefix =>
+ f.write_str("invalid private key prefix, byte 45 must be 0 as required by BIP-32"),
+ NonZeroParentFingerprintForMasterKey =>
+ f.write_str("non-zero parent fingerprint in master key"),
+ NonZeroChildNumberForMasterKey => f.write_str("non-zero child number in master key"),
}
}
}
@@ -562,6 +577,9 @@ impl std::error::Error for Error {
| UnknownVersion(_)
| WrongExtendedKeyLength(_)
| InvalidPublicKeyHexLength(_) => None,
+ InvalidPrivateKeyPrefix => None,
+ NonZeroParentFingerprintForMasterKey => None,
+ NonZeroChildNumberForMasterKey => None,
}
}
}
@@ -581,17 +599,19 @@ impl From for Error {
impl Xpriv {
/// Constructs a new master key from a seed value
pub fn new_master(network: impl Into, seed: &[u8]) -> Result {
- let mut hmac_engine: HmacEngine = HmacEngine::new(b"Bitcoin seed");
- hmac_engine.input(seed);
- let hmac_result: Hmac = Hmac::from_engine(hmac_engine);
+ let mut engine = HmacEngine::::new(b"Bitcoin seed");
+ engine.input(seed);
+ let hmac = engine.finalize();
Ok(Xpriv {
network: network.into(),
depth: 0,
parent_fingerprint: Default::default(),
child_number: ChildNumber::ZERO_NORMAL,
- private_key: secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])?,
- chain_code: ChainCode::from_hmac(hmac_result),
+ private_key: secp256k1::SecretKey::from_byte_array(
+ hmac.as_byte_array().split_array::<32, 32>().0,
+ )?,
+ chain_code: ChainCode::from_hmac(hmac),
})
}
@@ -605,8 +625,8 @@ impl Xpriv {
}
/// Constructs a new extended public key from this extended private key.
- pub fn to_xpub(&self, secp: &Secp256k1) -> Xpub {
- Xpub::from_xpriv(secp, self)
+ pub fn to_xpub(self, secp: &Secp256k1) -> Xpub {
+ Xpub::from_xpriv(secp, &self)
}
/// Constructs a new BIP340 keypair for Schnorr signatures and Taproot use matching the internal
@@ -645,25 +665,27 @@ impl Xpriv {
/// Private->Private child key derivation
fn ckd_priv(&self, secp: &Secp256k1, i: ChildNumber) -> Xpriv {
- let mut hmac_engine: HmacEngine = HmacEngine::new(&self.chain_code[..]);
+ let mut engine = HmacEngine::::new(&self.chain_code[..]);
match i {
ChildNumber::Normal { .. } => {
// Non-hardened key: compute public data and use that
- hmac_engine.input(
+ engine.input(
&secp256k1::PublicKey::from_secret_key(secp, &self.private_key).serialize()[..],
);
}
ChildNumber::Hardened { .. } => {
// Hardened key: use only secret data to prevent public derivation
- hmac_engine.input(&[0u8]);
- hmac_engine.input(&self.private_key[..]);
+ engine.input(&[0u8]);
+ engine.input(&self.private_key[..]);
}
}
- hmac_engine.input(&u32::from(i).to_be_bytes());
- let hmac_result: Hmac = Hmac::from_engine(hmac_engine);
- let sk = secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])
- .expect("statistically impossible to hit");
+ engine.input(&u32::from(i).to_be_bytes());
+ let hmac: Hmac = engine.finalize();
+ let sk = secp256k1::SecretKey::from_byte_array(
+ hmac.as_byte_array().split_array::<32, 32>().0,
+ )
+ .expect("statistically impossible to hit");
let tweaked =
sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
@@ -673,36 +695,39 @@ impl Xpriv {
parent_fingerprint: self.fingerprint(secp),
child_number: i,
private_key: tweaked,
- chain_code: ChainCode::from_hmac(hmac_result),
+ chain_code: ChainCode::from_hmac(hmac),
}
}
/// Decoding extended private key from binary data according to BIP 32
pub fn decode(data: &[u8]) -> Result {
- if data.len() != 78 {
- return Err(Error::WrongExtendedKeyLength(data.len()));
- }
+ let Common {
+ network,
+ depth,
+ parent_fingerprint,
+ child_number,
+ chain_code,
+ key,
+ } = Common::decode(data)?;
- let network = if data.starts_with(&VERSION_BYTES_MAINNET_PRIVATE) {
- NetworkKind::Main
- } else if data.starts_with(&VERSION_BYTES_TESTNETS_PRIVATE) {
- NetworkKind::Test
- } else {
- let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
- return Err(Error::UnknownVersion([b0, b1, b2, b3]));
+ let network = match network {
+ VERSION_BYTES_MAINNET_PRIVATE => NetworkKind::Main,
+ VERSION_BYTES_TESTNETS_PRIVATE => NetworkKind::Test,
+ unknown => return Err(Error::UnknownVersion(unknown)),
};
+ let (&zero, private_key) = key.split_first();
+ if zero != 0 {
+ return Err(Error::InvalidPrivateKeyPrefix);
+ }
+
Ok(Xpriv {
network,
- depth: data[4],
- parent_fingerprint: data[5..9]
- .try_into()
- .expect("9 - 5 == 4, which is the Fingerprint length"),
- child_number: u32::from_be_bytes(data[9..13].try_into().expect("4 byte slice")).into(),
- chain_code: data[13..45]
- .try_into()
- .expect("45 - 13 == 32, which is the ChainCode length"),
- private_key: secp256k1::SecretKey::from_slice(&data[46..78])?,
+ depth,
+ parent_fingerprint,
+ child_number,
+ chain_code,
+ private_key: secp256k1::SecretKey::from_byte_array(private_key)?,
})
}
@@ -729,7 +754,7 @@ impl Xpriv {
/// Returns the first four bytes of the identifier
pub fn fingerprint(&self, secp: &Secp256k1) -> Fingerprint {
- self.identifier(secp).as_byte_array()[0..4].try_into().expect("4 is the fingerprint length")
+ self.identifier(secp).as_byte_array().sub_array::<0, 4>().into()
}
}
@@ -803,15 +828,15 @@ impl Xpub {
match i {
ChildNumber::Hardened { .. } => Err(Error::CannotDeriveFromHardenedKey),
ChildNumber::Normal { index: n } => {
- let mut hmac_engine: HmacEngine =
- HmacEngine::new(&self.chain_code[..]);
- hmac_engine.input(&self.public_key.serialize()[..]);
- hmac_engine.input(&n.to_be_bytes());
-
- let hmac_result: Hmac = Hmac::from_engine(hmac_engine);
-
- let private_key = secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])?;
- let chain_code = ChainCode::from_hmac(hmac_result);
+ let mut engine = HmacEngine::::new(&self.chain_code[..]);
+ engine.input(&self.public_key.serialize()[..]);
+ engine.input(&n.to_be_bytes());
+
+ let hmac = engine.finalize();
+ let private_key = secp256k1::SecretKey::from_byte_array(
+ hmac.as_byte_array().split_array::<32, 32>().0
+ )?;
+ let chain_code = ChainCode::from_hmac(hmac);
Ok((private_key, chain_code))
}
}
@@ -838,30 +863,28 @@ impl Xpub {
/// Decoding extended public key from binary data according to BIP 32
pub fn decode(data: &[u8]) -> Result {
- if data.len() != 78 {
- return Err(Error::WrongExtendedKeyLength(data.len()));
- }
+ let Common {
+ network,
+ depth,
+ parent_fingerprint,
+ child_number,
+ chain_code,
+ key,
+ } = Common::decode(data)?;
- let network = if data.starts_with(&VERSION_BYTES_MAINNET_PUBLIC) {
- NetworkKind::Main
- } else if data.starts_with(&VERSION_BYTES_TESTNETS_PUBLIC) {
- NetworkKind::Test
- } else {
- let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
- return Err(Error::UnknownVersion([b0, b1, b2, b3]));
+ let network = match network {
+ VERSION_BYTES_MAINNET_PUBLIC => NetworkKind::Main,
+ VERSION_BYTES_TESTNETS_PUBLIC => NetworkKind::Test,
+ unknown => return Err(Error::UnknownVersion(unknown)),
};
Ok(Xpub {
network,
- depth: data[4],
- parent_fingerprint: data[5..9]
- .try_into()
- .expect("9 - 5 == 4, which is the Fingerprint length"),
- child_number: u32::from_be_bytes(data[9..13].try_into().expect("4 byte slice")).into(),
- chain_code: data[13..45]
- .try_into()
- .expect("45 - 13 == 32, which is the ChainCode length"),
- public_key: secp256k1::PublicKey::from_slice(&data[45..78])?,
+ depth,
+ parent_fingerprint,
+ child_number,
+ chain_code,
+ public_key: secp256k1::PublicKey::from_slice(&key)?,
})
}
@@ -880,14 +903,14 @@ impl Xpub {
ret
}
- /// Returns the HASH160 of the chaincode
+ /// Returns the HASH160 of the public key component of the xpub
pub fn identifier(&self) -> XKeyIdentifier {
XKeyIdentifier(hash160::Hash::hash(&self.public_key.serialize()))
}
/// Returns the first four bytes of the identifier
pub fn fingerprint(&self) -> Fingerprint {
- self.identifier().as_byte_array()[0..4].try_into().expect("4 is the fingerprint length")
+ self.identifier().as_byte_array().sub_array::<0, 4>().into()
}
}
@@ -964,6 +987,48 @@ impl fmt::Display for InvalidBase58PayloadLengthError {
#[cfg(feature = "std")]
impl std::error::Error for InvalidBase58PayloadLengthError {}
+// Helps unify decoding
+struct Common {
+ network: [u8; 4],
+ depth: u8,
+ parent_fingerprint: Fingerprint,
+ child_number: ChildNumber,
+ chain_code: ChainCode,
+ // public key (compressed) or 0 byte followed by a private key
+ key: [u8; 33],
+}
+
+impl Common {
+ fn decode(data: &[u8]) -> Result {
+ let data: &[u8; 78] = data.try_into().map_err(|_| Error::WrongExtendedKeyLength(data.len()))?;
+
+ let (&network, data) = data.split_array::<4, 74>();
+ let (&depth, data) = data.split_first::<73>();
+ let (&parent_fingerprint, data) = data.split_array::<4, 69>();
+ let (&child_number, data) = data.split_array::<4, 65>();
+ let (&chain_code, &key) = data.split_array::<32, 33>();
+
+ if depth == 0 {
+ if parent_fingerprint != [0u8; 4] {
+ return Err(Error::NonZeroParentFingerprintForMasterKey);
+ }
+
+ if child_number != [0u8; 4] {
+ return Err(Error::NonZeroChildNumberForMasterKey);
+ }
+ }
+
+ Ok(Common {
+ network,
+ depth,
+ parent_fingerprint: parent_fingerprint.into(),
+ child_number: u32::from_be_bytes(child_number).into(),
+ chain_code: chain_code.into(),
+ key,
+ })
+ }
+}
+
#[cfg(test)]
mod tests {
use hex::test_hex_unwrap as hex;
@@ -974,7 +1039,7 @@ mod tests {
use super::*;
#[test]
- fn test_parse_derivation_path() {
+ fn parse_derivation_path() {
assert_eq!("n/0'/0".parse::(), Err(Error::InvalidChildNumberFormat));
assert_eq!("4/m/5".parse::(), Err(Error::InvalidChildNumberFormat));
assert_eq!("//3/0'".parse::(), Err(Error::InvalidChildNumberFormat));
@@ -1036,7 +1101,7 @@ mod tests {
}
#[test]
- fn test_derivation_path_conversion_index() {
+ fn derivation_path_conversion_index() {
let path = "0h/1/2'".parse::().unwrap();
let numbers: Vec = path.clone().into();
let path2: DerivationPath = numbers.into();
@@ -1096,7 +1161,7 @@ mod tests {
}
#[test]
- fn test_increment() {
+ fn increment() {
let idx = 9345497; // randomly generated, I promise
let cn = ChildNumber::from_normal_idx(idx).unwrap();
assert_eq!(cn.increment().ok(), Some(ChildNumber::from_normal_idx(idx + 1).unwrap()));
@@ -1139,7 +1204,7 @@ mod tests {
}
#[test]
- fn test_vector_1() {
+ fn vector_1() {
let secp = Secp256k1::new();
let seed = hex!("000102030405060708090a0b0c0d0e0f");
@@ -1175,7 +1240,7 @@ mod tests {
}
#[test]
- fn test_vector_2() {
+ fn vector_2() {
let secp = Secp256k1::new();
let seed = hex!("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542");
@@ -1211,7 +1276,7 @@ mod tests {
}
#[test]
- fn test_vector_3() {
+ fn vector_3() {
let secp = Secp256k1::new();
let seed = hex!("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be");
@@ -1226,6 +1291,44 @@ mod tests {
"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y");
}
+ #[test]
+ fn test_reject_xpriv_with_non_zero_byte_at_index_45() {
+ let mut xpriv = base58::decode_check("xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9").unwrap();
+
+ // Modify byte at index 45 to be non-zero (e.g., 1)
+ xpriv[45] = 1;
+
+ let result = Xpriv::decode(&xpriv);
+ assert!(result.is_err());
+
+ match result {
+ Err(Error::InvalidPrivateKeyPrefix) => {}
+ _ => panic!("Expected InvalidPrivateKeyPrefix error, got {:?}", result),
+ }
+ }
+
+ #[test]
+ fn test_reject_xpriv_with_zero_depth_and_non_zero_index() {
+ let result = "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN".parse::();
+ assert!(result.is_err());
+
+ match result {
+ Err(Error::NonZeroChildNumberForMasterKey) => {}
+ _ => panic!("Expected NonZeroChildNumberForMasterKey error, got {:?}", result),
+ }
+ }
+
+ #[test]
+ fn test_reject_xpriv_with_zero_depth_and_non_zero_parent_fingerprint() {
+ let result = "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv".parse::();
+ assert!(result.is_err());
+
+ match result {
+ Err(Error::NonZeroParentFingerprintForMasterKey) => {}
+ _ => panic!("Expected NonZeroParentFingerprintForMasterKey error, got {:?}", result),
+ }
+ }
+
#[test]
#[cfg(feature = "serde")]
pub fn encode_decode_childnumber() {
@@ -1307,4 +1410,33 @@ mod tests {
let xpriv_str = "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fENZ3QzxW";
xpriv_str.parse::().unwrap();
}
+
+ #[test]
+ fn official_vectors_5() {
+ let invalid_keys = [
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J",
+ "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv",
+ "xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ",
+ "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN",
+ "xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8",
+ "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4",
+ "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx",
+ "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G",
+ "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY",
+ "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL",
+ ];
+ for key in invalid_keys {
+ if key.starts_with("xpub") {
+ key.parse::().unwrap_err();
+ } else {
+ key.parse::().unwrap_err();
+ }
+ }
+ }
}
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index 88971c6089..a8dae5e4ba 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -7,11 +7,13 @@
//! module describes structures and functions needed to describe
//! these blocks and the blockchain.
+use core::convert::Infallible;
use core::fmt;
use hashes::{sha256d, HashEngine};
-use internals::compact_size;
+use internals::{compact_size, ToU64};
use io::{BufRead, Write};
+use units::BlockTime;
use super::Weight;
use crate::consensus::encode::WriteExt as _;
@@ -84,6 +86,18 @@ impl Decodable for Version {
}
}
+impl Encodable for BlockTime {
+ fn consensus_encode(&self, w: &mut W) -> Result {
+ self.to_u32().consensus_encode(w)
+ }
+}
+
+impl Decodable for BlockTime {
+ fn consensus_decode(r: &mut R) -> Result {
+ Decodable::consensus_decode(r).map(BlockTime::from_u32)
+ }
+}
+
/// Extension functionality for the [`Block`] type.
pub trait BlockUncheckedExt: sealed::Sealed {
/// Validates (or checks) a block.
@@ -263,7 +277,7 @@ impl BlockCheckedExt for Block {
fn weight(&self) -> Weight {
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
let wu = block_base_size(self.transactions()) * 3 + self.total_size();
- Weight::from_wu_usize(wu)
+ Weight::from_wu(wu.to_u64())
}
fn total_size(&self) -> usize {
@@ -383,7 +397,9 @@ pub enum InvalidBlockError {
InvalidWitnessCommitment,
}
-internals::impl_from_infallible!(InvalidBlockError);
+impl From for InvalidBlockError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for InvalidBlockError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -413,7 +429,9 @@ pub enum Bip34Error {
NegativeHeight,
}
-internals::impl_from_infallible!(Bip34Error);
+impl From for Bip34Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Bip34Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -457,7 +475,9 @@ pub enum ValidationError {
BadTarget,
}
-internals::impl_from_infallible!(ValidationError);
+impl From for ValidationError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -508,7 +528,7 @@ mod tests {
}
#[test]
- fn test_coinbase_and_bip34() {
+ fn coinbase_and_bip34() {
// testnet block 100,000
const BLOCK_HEX: &str = "0200000035ab154183570282ce9afc0b494c9fc6a3cfea05aa8c1add2ecc56490000000038ba3d78e4500a5a7570dbe61960398add4410d278b21cd9708e6d9743f374d544fc055227f1001c29c1ea3b0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3703a08601000427f1001c046a510100522cfabe6d6d0000000000000000000068692066726f6d20706f6f6c7365727665726aac1eeeed88ffffffff0100f2052a010000001976a914912e2b234f941f30b18afbb4fa46171214bf66c888ac00000000";
let block: Block = deserialize(&hex!(BLOCK_HEX)).unwrap();
@@ -589,7 +609,7 @@ mod tests {
block::compute_merkle_root(&transactions).unwrap()
);
assert_eq!(serialize(&real_decode.header().merkle_root), merkle);
- assert_eq!(real_decode.header().time, 1231965655);
+ assert_eq!(real_decode.header().time, BlockTime::from_u32(1231965655));
assert_eq!(real_decode.header().bits, CompactTarget::from_consensus(486604799));
assert_eq!(real_decode.header().nonce, 2067413810);
assert_eq!(real_decode.header().work(), work);
@@ -640,7 +660,7 @@ mod tests {
real_decode.header().merkle_root,
block::compute_merkle_root(&transactions).unwrap()
);
- assert_eq!(real_decode.header().time, 1472004949);
+ assert_eq!(real_decode.header().time, BlockTime::from_u32(1472004949));
assert_eq!(real_decode.header().bits, CompactTarget::from_consensus(0x1a06d450));
assert_eq!(real_decode.header().nonce, 1879759182);
assert_eq!(real_decode.header().work(), work);
@@ -723,7 +743,7 @@ mod tests {
#[test]
fn soft_fork_signalling() {
for i in 0..31 {
- let version_int = (0x20000000u32 ^ 1 << i) as i32;
+ let version_int = (0x20000000u32 ^ (1 << i)) as i32;
let version = Version::from_consensus(version_int);
if i < 29 {
assert!(version.is_signalling_soft_fork(i));
@@ -732,7 +752,7 @@ mod tests {
}
}
- let segwit_signal = Version::from_consensus(0x20000000 ^ 1 << 1);
+ let segwit_signal = Version::from_consensus(0x20000000 ^ (1 << 1));
assert!(!segwit_signal.is_signalling_soft_fork(0));
assert!(segwit_signal.is_signalling_soft_fork(1));
assert!(!segwit_signal.is_signalling_soft_fork(2));
diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
index 6073570e9a..60d528862c 100644
--- a/bitcoin/src/blockdata/constants.rs
+++ b/bitcoin/src/blockdata/constants.rs
@@ -6,8 +6,6 @@
//! consensus code. In particular, it defines the genesis block and its
//! single transaction.
-use hashes::sha256d;
-
use crate::block::{self, Block, Checked};
use crate::internal_macros::{impl_array_newtype, impl_array_newtype_stringify};
use crate::locktime::absolute;
@@ -16,7 +14,7 @@ use crate::opcodes::all::*;
use crate::pow::CompactTarget;
use crate::transaction::{self, OutPoint, Transaction, TxIn, TxOut};
use crate::witness::Witness;
-use crate::{script, Amount, BlockHash, Sequence, TestnetVersion};
+use crate::{script, Amount, BlockHash, BlockTime, Sequence, TestnetVersion};
/// How many seconds between blocks we expect on average.
pub const TARGET_BLOCK_SPACING: u32 = 600;
@@ -38,9 +36,9 @@ pub const PUBKEY_ADDRESS_PREFIX_TEST: u8 = 111; // 0x6f
/// Test (tesnet, signet, regtest) script address prefix.
pub const SCRIPT_ADDRESS_PREFIX_TEST: u8 = 196; // 0xc4
/// The maximum allowed redeem script size for a P2SH output.
-pub const MAX_REDEEM_SCRIPT_SIZE: usize = 520;
+pub const MAX_REDEEM_SCRIPT_SIZE: usize = primitives::script::MAX_REDEEM_SCRIPT_SIZE; // 520
/// The maximum allowed redeem script size of the witness script.
-pub const MAX_WITNESS_SCRIPT_SIZE: usize = 10_000;
+pub const MAX_WITNESS_SCRIPT_SIZE: usize = primitives::script::MAX_WITNESS_SCRIPT_SIZE; // 10_000
/// The maximum allowed size of any single witness stack element.
pub const MAX_STACK_ELEMENT_SIZE: usize = 520;
/// How may blocks between halvings.
@@ -52,6 +50,8 @@ pub const SUBSIDY_HALVING_INTERVAL: u32 = 210_000;
pub const MAX_SCRIPTNUM_VALUE: u32 = 0x80000000; // 2^31
/// Number of blocks needed for an output from a coinbase transaction to be spendable.
pub const COINBASE_MATURITY: u32 = 100;
+/// The maximum allowed size for a serialized block, in bytes (only for buffer size limits)
+pub const MAX_BLOCK_SERIALIZED_SIZE: usize = 4_000_000;
// This is the 65 byte (uncompressed) pubkey used as the one-and-only output of the genesis transaction.
//
@@ -112,7 +112,7 @@ fn bitcoin_genesis_tx(params: &Params) -> Transaction {
witness: Witness::default(),
});
- ret.output.push(TxOut { value: Amount::from_sat(50 * 100_000_000), script_pubkey: out_script });
+ ret.output.push(TxOut { value: Amount::FIFTY_BTC, script_pubkey: out_script });
// end
ret
@@ -122,8 +122,7 @@ fn bitcoin_genesis_tx(params: &Params) -> Transaction {
pub fn genesis_block(params: impl AsRef) -> Block {
let params = params.as_ref();
let transactions = vec![bitcoin_genesis_tx(params)];
- let hash: sha256d::Hash = transactions[0].compute_txid().into();
- let merkle_root: crate::TxMerkleNode = hash.into();
+ let merkle_root = block::compute_merkle_root(&transactions).expect("transactions is not empty");
let witness_root = block::compute_witness_root(&transactions);
match params.network {
@@ -132,7 +131,7 @@ pub fn genesis_block(params: impl AsRef) -> Block {
version: block::Version::ONE,
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
merkle_root,
- time: 1231006505,
+ time: BlockTime::from_u32(1231006505),
bits: CompactTarget::from_consensus(0x1d00ffff),
nonce: 2083236893,
},
@@ -144,7 +143,7 @@ pub fn genesis_block(params: impl AsRef) -> Block {
version: block::Version::ONE,
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
merkle_root,
- time: 1296688602,
+ time: BlockTime::from_u32(1296688602),
bits: CompactTarget::from_consensus(0x1d00ffff),
nonce: 414098458,
},
@@ -156,7 +155,7 @@ pub fn genesis_block(params: impl AsRef) -> Block {
version: block::Version::ONE,
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
merkle_root,
- time: 1714777860,
+ time: BlockTime::from_u32(1714777860),
bits: CompactTarget::from_consensus(0x1d00ffff),
nonce: 393743547,
},
@@ -168,7 +167,7 @@ pub fn genesis_block(params: impl AsRef) -> Block {
version: block::Version::ONE,
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
merkle_root,
- time: 1598918400,
+ time: BlockTime::from_u32(1598918400),
bits: CompactTarget::from_consensus(0x1e0377ae),
nonce: 52613770,
},
@@ -180,7 +179,7 @@ pub fn genesis_block(params: impl AsRef) -> Block {
version: block::Version::ONE,
prev_blockhash: BlockHash::GENESIS_PREVIOUS_BLOCK_HASH,
merkle_root,
- time: 1296688602,
+ time: BlockTime::from_u32(1296688602),
bits: CompactTarget::from_consensus(0x207fffff),
nonce: 2,
},
@@ -320,7 +319,7 @@ mod test {
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
);
- assert_eq!(gen.header().time, 1231006505);
+ assert_eq!(gen.header().time, BlockTime::from_u32(1231006505));
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1d00ffff));
assert_eq!(gen.header().nonce, 2083236893);
assert_eq!(
@@ -338,7 +337,7 @@ mod test {
gen.header().merkle_root.to_string(),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
);
- assert_eq!(gen.header().time, 1296688602);
+ assert_eq!(gen.header().time, BlockTime::from_u32(1296688602));
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1d00ffff));
assert_eq!(gen.header().nonce, 414098458);
assert_eq!(
@@ -356,7 +355,7 @@ mod test {
gen.header().merkle_root.to_string(),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
);
- assert_eq!(gen.header().time, 1598918400);
+ assert_eq!(gen.header().time, BlockTime::from_u32(1598918400));
assert_eq!(gen.header().bits, CompactTarget::from_consensus(0x1e0377ae));
assert_eq!(gen.header().nonce, 52613770);
assert_eq!(
@@ -373,7 +372,7 @@ mod test {
// The genesis block hash is a double-sha256 and it is displayed backwards.
let genesis_hash = genesis_block(network).block_hash();
// We abuse the sha256 hash here so we get a LowerHex impl that does not print the hex backwards.
- let hash = sha256::Hash::from_slice(genesis_hash.as_byte_array()).unwrap();
+ let hash = sha256::Hash::from_byte_array(genesis_hash.to_byte_array());
let want = format!("{:02x}", hash);
let chain_hash = ChainHash::using_genesis_block_const(network);
diff --git a/bitcoin/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs
index a8d0af44bf..a6f39ba9a8 100644
--- a/bitcoin/src/blockdata/mod.rs
+++ b/bitcoin/src/blockdata/mod.rs
@@ -7,6 +7,7 @@
pub mod block;
pub mod constants;
+pub mod opcodes;
pub mod script;
pub mod transaction;
pub mod witness;
@@ -20,32 +21,10 @@ pub use self::{
/// Implements `FeeRate` and assoctiated features.
pub mod fee_rate {
+ #[cfg(feature = "serde")]
+ pub use units::fee_rate::serde;
/// Re-export everything from the [`units::fee_rate`] module.
pub use units::fee_rate::FeeRate;
-
- #[cfg(test)]
- mod tests {
- use internals::ToU64 as _;
-
- use super::*;
-
- #[test]
- fn fee_convenience_functions_agree() {
- use hex::test_hex_unwrap as hex;
-
- use crate::consensus::Decodable;
- use crate::transaction::{Transaction, TransactionExt as _};
-
- const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
-
- let raw_tx = hex!(SOME_TX);
- let tx: Transaction = Decodable::consensus_decode(&mut raw_tx.as_slice()).unwrap();
-
- let rate = FeeRate::from_sat_per_vb(1).expect("1 sat/byte is valid");
-
- assert_eq!(rate.fee_vb(tx.vsize().to_u64()), rate.fee_wu(tx.weight()));
- }
- }
}
/// Provides absolute and relative locktimes.
@@ -94,12 +73,6 @@ pub mod locktime {
}
}
-/// Bitcoin script opcodes.
-pub mod opcodes {
- /// Re-export everything from the [`primitives::opcodes`] module.
- pub use primitives::opcodes::*;
-}
-
/// Implements `Weight` and associated features.
pub mod weight {
/// Re-export everything from the [`units::weight`] module.
diff --git a/bitcoin/src/blockdata/opcodes.rs b/bitcoin/src/blockdata/opcodes.rs
new file mode 100644
index 0000000000..5da0295d39
--- /dev/null
+++ b/bitcoin/src/blockdata/opcodes.rs
@@ -0,0 +1,901 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Bitcoin script opcodes.
+//!
+//! Bitcoin's script uses a stack-based assembly language. This module defines
+//! all of the opcodes for that language.
+
+#![allow(non_camel_case_types)]
+
+use core::fmt;
+
+#[cfg(feature = "serde")]
+use crate::prelude::ToString;
+
+/// A script Opcode.
+///
+/// We do not implement Ord on this type because there is no natural ordering on opcodes, but there
+/// may appear to be one (e.g. because all the push opcodes appear in a consecutive block) and we
+/// don't want to encourage subtly buggy code. Please use [`Opcode::classify`] to distinguish different
+/// types of opcodes.
+///
+///
+/// Example of Core bug caused by assuming ordering
+///
+/// Bitcoin Core's `IsPushOnly` considers `OP_RESERVED` to be a "push code", allowing this opcode
+/// in contexts where only pushes are supposed to be allowed.
+///
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode {
+ code: u8,
+}
+
+use self::all::*;
+
+macro_rules! all_opcodes {
+ ($($op:ident => $val:expr, $doc:expr);*) => {
+ /// Enables wildcard imports to bring into scope all opcodes and nothing else.
+ ///
+ /// The `all` module is provided so one can use a wildcard import `use bitcoin::opcodes::all::*` to
+ /// get all the `OP_FOO` opcodes without getting other types defined in `opcodes` (e.g. `Opcode`, `Class`).
+ ///
+ /// This module is guaranteed to never contain anything except opcode constants and all opcode
+ /// constants are guaranteed to begin with OP_.
+ pub mod all {
+ use super::Opcode;
+ $(
+ #[doc = $doc]
+ pub const $op: Opcode = Opcode { code: $val};
+ )*
+ }
+
+ /// Push an empty array onto the stack.
+ pub static OP_0: Opcode = OP_PUSHBYTES_0;
+ /// Empty stack is also FALSE.
+ pub static OP_FALSE: Opcode = OP_PUSHBYTES_0;
+ /// Number 1 is also TRUE.
+ pub static OP_TRUE: Opcode = OP_PUSHNUM_1;
+ /// Previously called OP_NOP2.
+ pub static OP_NOP2: Opcode = OP_CLTV;
+ /// Previously called OP_NOP3.
+ pub static OP_NOP3: Opcode = OP_CSV;
+
+ impl fmt::Display for Opcode {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ match *self {
+ $(
+ $op => core::fmt::Display::fmt(stringify!($op), f),
+ )+
+ }
+ }
+ }
+ }
+}
+
+all_opcodes! {
+ OP_PUSHBYTES_0 => 0x00, "Push an empty array onto the stack.";
+ OP_PUSHBYTES_1 => 0x01, "Push the next byte as an array onto the stack.";
+ OP_PUSHBYTES_2 => 0x02, "Push the next 2 bytes as an array onto the stack.";
+ OP_PUSHBYTES_3 => 0x03, "Push the next 3 bytes as an array onto the stack.";
+ OP_PUSHBYTES_4 => 0x04, "Push the next 4 bytes as an array onto the stack.";
+ OP_PUSHBYTES_5 => 0x05, "Push the next 5 bytes as an array onto the stack.";
+ OP_PUSHBYTES_6 => 0x06, "Push the next 6 bytes as an array onto the stack.";
+ OP_PUSHBYTES_7 => 0x07, "Push the next 7 bytes as an array onto the stack.";
+ OP_PUSHBYTES_8 => 0x08, "Push the next 8 bytes as an array onto the stack.";
+ OP_PUSHBYTES_9 => 0x09, "Push the next 9 bytes as an array onto the stack.";
+ OP_PUSHBYTES_10 => 0x0a, "Push the next 10 bytes as an array onto the stack.";
+ OP_PUSHBYTES_11 => 0x0b, "Push the next 11 bytes as an array onto the stack.";
+ OP_PUSHBYTES_12 => 0x0c, "Push the next 12 bytes as an array onto the stack.";
+ OP_PUSHBYTES_13 => 0x0d, "Push the next 13 bytes as an array onto the stack.";
+ OP_PUSHBYTES_14 => 0x0e, "Push the next 14 bytes as an array onto the stack.";
+ OP_PUSHBYTES_15 => 0x0f, "Push the next 15 bytes as an array onto the stack.";
+ OP_PUSHBYTES_16 => 0x10, "Push the next 16 bytes as an array onto the stack.";
+ OP_PUSHBYTES_17 => 0x11, "Push the next 17 bytes as an array onto the stack.";
+ OP_PUSHBYTES_18 => 0x12, "Push the next 18 bytes as an array onto the stack.";
+ OP_PUSHBYTES_19 => 0x13, "Push the next 19 bytes as an array onto the stack.";
+ OP_PUSHBYTES_20 => 0x14, "Push the next 20 bytes as an array onto the stack.";
+ OP_PUSHBYTES_21 => 0x15, "Push the next 21 bytes as an array onto the stack.";
+ OP_PUSHBYTES_22 => 0x16, "Push the next 22 bytes as an array onto the stack.";
+ OP_PUSHBYTES_23 => 0x17, "Push the next 23 bytes as an array onto the stack.";
+ OP_PUSHBYTES_24 => 0x18, "Push the next 24 bytes as an array onto the stack.";
+ OP_PUSHBYTES_25 => 0x19, "Push the next 25 bytes as an array onto the stack.";
+ OP_PUSHBYTES_26 => 0x1a, "Push the next 26 bytes as an array onto the stack.";
+ OP_PUSHBYTES_27 => 0x1b, "Push the next 27 bytes as an array onto the stack.";
+ OP_PUSHBYTES_28 => 0x1c, "Push the next 28 bytes as an array onto the stack.";
+ OP_PUSHBYTES_29 => 0x1d, "Push the next 29 bytes as an array onto the stack.";
+ OP_PUSHBYTES_30 => 0x1e, "Push the next 30 bytes as an array onto the stack.";
+ OP_PUSHBYTES_31 => 0x1f, "Push the next 31 bytes as an array onto the stack.";
+ OP_PUSHBYTES_32 => 0x20, "Push the next 32 bytes as an array onto the stack.";
+ OP_PUSHBYTES_33 => 0x21, "Push the next 33 bytes as an array onto the stack.";
+ OP_PUSHBYTES_34 => 0x22, "Push the next 34 bytes as an array onto the stack.";
+ OP_PUSHBYTES_35 => 0x23, "Push the next 35 bytes as an array onto the stack.";
+ OP_PUSHBYTES_36 => 0x24, "Push the next 36 bytes as an array onto the stack.";
+ OP_PUSHBYTES_37 => 0x25, "Push the next 37 bytes as an array onto the stack.";
+ OP_PUSHBYTES_38 => 0x26, "Push the next 38 bytes as an array onto the stack.";
+ OP_PUSHBYTES_39 => 0x27, "Push the next 39 bytes as an array onto the stack.";
+ OP_PUSHBYTES_40 => 0x28, "Push the next 40 bytes as an array onto the stack.";
+ OP_PUSHBYTES_41 => 0x29, "Push the next 41 bytes as an array onto the stack.";
+ OP_PUSHBYTES_42 => 0x2a, "Push the next 42 bytes as an array onto the stack.";
+ OP_PUSHBYTES_43 => 0x2b, "Push the next 43 bytes as an array onto the stack.";
+ OP_PUSHBYTES_44 => 0x2c, "Push the next 44 bytes as an array onto the stack.";
+ OP_PUSHBYTES_45 => 0x2d, "Push the next 45 bytes as an array onto the stack.";
+ OP_PUSHBYTES_46 => 0x2e, "Push the next 46 bytes as an array onto the stack.";
+ OP_PUSHBYTES_47 => 0x2f, "Push the next 47 bytes as an array onto the stack.";
+ OP_PUSHBYTES_48 => 0x30, "Push the next 48 bytes as an array onto the stack.";
+ OP_PUSHBYTES_49 => 0x31, "Push the next 49 bytes as an array onto the stack.";
+ OP_PUSHBYTES_50 => 0x32, "Push the next 50 bytes as an array onto the stack.";
+ OP_PUSHBYTES_51 => 0x33, "Push the next 51 bytes as an array onto the stack.";
+ OP_PUSHBYTES_52 => 0x34, "Push the next 52 bytes as an array onto the stack.";
+ OP_PUSHBYTES_53 => 0x35, "Push the next 53 bytes as an array onto the stack.";
+ OP_PUSHBYTES_54 => 0x36, "Push the next 54 bytes as an array onto the stack.";
+ OP_PUSHBYTES_55 => 0x37, "Push the next 55 bytes as an array onto the stack.";
+ OP_PUSHBYTES_56 => 0x38, "Push the next 56 bytes as an array onto the stack.";
+ OP_PUSHBYTES_57 => 0x39, "Push the next 57 bytes as an array onto the stack.";
+ OP_PUSHBYTES_58 => 0x3a, "Push the next 58 bytes as an array onto the stack.";
+ OP_PUSHBYTES_59 => 0x3b, "Push the next 59 bytes as an array onto the stack.";
+ OP_PUSHBYTES_60 => 0x3c, "Push the next 60 bytes as an array onto the stack.";
+ OP_PUSHBYTES_61 => 0x3d, "Push the next 61 bytes as an array onto the stack.";
+ OP_PUSHBYTES_62 => 0x3e, "Push the next 62 bytes as an array onto the stack.";
+ OP_PUSHBYTES_63 => 0x3f, "Push the next 63 bytes as an array onto the stack.";
+ OP_PUSHBYTES_64 => 0x40, "Push the next 64 bytes as an array onto the stack.";
+ OP_PUSHBYTES_65 => 0x41, "Push the next 65 bytes as an array onto the stack.";
+ OP_PUSHBYTES_66 => 0x42, "Push the next 66 bytes as an array onto the stack.";
+ OP_PUSHBYTES_67 => 0x43, "Push the next 67 bytes as an array onto the stack.";
+ OP_PUSHBYTES_68 => 0x44, "Push the next 68 bytes as an array onto the stack.";
+ OP_PUSHBYTES_69 => 0x45, "Push the next 69 bytes as an array onto the stack.";
+ OP_PUSHBYTES_70 => 0x46, "Push the next 70 bytes as an array onto the stack.";
+ OP_PUSHBYTES_71 => 0x47, "Push the next 71 bytes as an array onto the stack.";
+ OP_PUSHBYTES_72 => 0x48, "Push the next 72 bytes as an array onto the stack.";
+ OP_PUSHBYTES_73 => 0x49, "Push the next 73 bytes as an array onto the stack.";
+ OP_PUSHBYTES_74 => 0x4a, "Push the next 74 bytes as an array onto the stack.";
+ OP_PUSHBYTES_75 => 0x4b, "Push the next 75 bytes as an array onto the stack.";
+ OP_PUSHDATA1 => 0x4c, "Read the next byte as N; push the next N bytes as an array onto the stack.";
+ OP_PUSHDATA2 => 0x4d, "Read the next 2 bytes as N; push the next N bytes as an array onto the stack.";
+ OP_PUSHDATA4 => 0x4e, "Read the next 4 bytes as N; push the next N bytes as an array onto the stack.";
+ OP_PUSHNUM_NEG1 => 0x4f, "Push the array `0x81` onto the stack.";
+ OP_RESERVED => 0x50, "Synonym for OP_RETURN.";
+ OP_PUSHNUM_1 => 0x51, "Push the array `0x01` onto the stack.";
+ OP_PUSHNUM_2 => 0x52, "Push the array `0x02` onto the stack.";
+ OP_PUSHNUM_3 => 0x53, "Push the array `0x03` onto the stack.";
+ OP_PUSHNUM_4 => 0x54, "Push the array `0x04` onto the stack.";
+ OP_PUSHNUM_5 => 0x55, "Push the array `0x05` onto the stack.";
+ OP_PUSHNUM_6 => 0x56, "Push the array `0x06` onto the stack.";
+ OP_PUSHNUM_7 => 0x57, "Push the array `0x07` onto the stack.";
+ OP_PUSHNUM_8 => 0x58, "Push the array `0x08` onto the stack.";
+ OP_PUSHNUM_9 => 0x59, "Push the array `0x09` onto the stack.";
+ OP_PUSHNUM_10 => 0x5a, "Push the array `0x0a` onto the stack.";
+ OP_PUSHNUM_11 => 0x5b, "Push the array `0x0b` onto the stack.";
+ OP_PUSHNUM_12 => 0x5c, "Push the array `0x0c` onto the stack.";
+ OP_PUSHNUM_13 => 0x5d, "Push the array `0x0d` onto the stack.";
+ OP_PUSHNUM_14 => 0x5e, "Push the array `0x0e` onto the stack.";
+ OP_PUSHNUM_15 => 0x5f, "Push the array `0x0f` onto the stack.";
+ OP_PUSHNUM_16 => 0x60, "Push the array `0x10` onto the stack.";
+ OP_NOP => 0x61, "Does nothing.";
+ OP_VER => 0x62, "Synonym for OP_RETURN.";
+ OP_IF => 0x63, "Pop and execute the next statements if a nonzero element was popped.";
+ OP_NOTIF => 0x64, "Pop and execute the next statements if a zero element was popped.";
+ OP_VERIF => 0x65, "Fail the script unconditionally, does not even need to be executed.";
+ OP_VERNOTIF => 0x66, "Fail the script unconditionally, does not even need to be executed.";
+ OP_ELSE => 0x67, "Execute statements if those after the previous OP_IF were not, and vice-versa. \
+ If there is no previous OP_IF, this acts as a RETURN.";
+ OP_ENDIF => 0x68, "Pop and execute the next statements if a zero element was popped.";
+ OP_VERIFY => 0x69, "If the top value is zero or the stack is empty, fail; otherwise, pop the stack.";
+ OP_RETURN => 0x6a, "Fail the script immediately. (Must be executed.).";
+ OP_TOALTSTACK => 0x6b, "Pop one element from the main stack onto the alt stack.";
+ OP_FROMALTSTACK => 0x6c, "Pop one element from the alt stack onto the main stack.";
+ OP_2DROP => 0x6d, "Drops the top two stack items.";
+ OP_2DUP => 0x6e, "Duplicates the top two stack items as AB -> ABAB.";
+ OP_3DUP => 0x6f, "Duplicates the two three stack items as ABC -> ABCABC.";
+ OP_2OVER => 0x70, "Copies the two stack items of items two spaces back to the front, as xxAB -> ABxxAB.";
+ OP_2ROT => 0x71, "Moves the two stack items four spaces back to the front, as xxxxAB -> ABxxxx.";
+ OP_2SWAP => 0x72, "Swaps the top two pairs, as ABCD -> CDAB.";
+ OP_IFDUP => 0x73, "Duplicate the top stack element unless it is zero.";
+ OP_DEPTH => 0x74, "Push the current number of stack items onto the stack.";
+ OP_DROP => 0x75, "Drops the top stack item.";
+ OP_DUP => 0x76, "Duplicates the top stack item.";
+ OP_NIP => 0x77, "Drops the second-to-top stack item.";
+ OP_OVER => 0x78, "Copies the second-to-top stack item, as xA -> AxA.";
+ OP_PICK => 0x79, "Pop the top stack element as N. Copy the Nth stack element to the top.";
+ OP_ROLL => 0x7a, "Pop the top stack element as N. Move the Nth stack element to the top.";
+ OP_ROT => 0x7b, "Rotate the top three stack items, as [top next1 next2] -> [next2 top next1].";
+ OP_SWAP => 0x7c, "Swap the top two stack items.";
+ OP_TUCK => 0x7d, "Copy the top stack item to before the second item, as [top next] -> [top next top].";
+ OP_CAT => 0x7e, "Fail the script unconditionally, does not even need to be executed.";
+ OP_SUBSTR => 0x7f, "Fail the script unconditionally, does not even need to be executed.";
+ OP_LEFT => 0x80, "Fail the script unconditionally, does not even need to be executed.";
+ OP_RIGHT => 0x81, "Fail the script unconditionally, does not even need to be executed.";
+ OP_SIZE => 0x82, "Pushes the length of the top stack item onto the stack.";
+ OP_INVERT => 0x83, "Fail the script unconditionally, does not even need to be executed.";
+ OP_AND => 0x84, "Fail the script unconditionally, does not even need to be executed.";
+ OP_OR => 0x85, "Fail the script unconditionally, does not even need to be executed.";
+ OP_XOR => 0x86, "Fail the script unconditionally, does not even need to be executed.";
+ OP_EQUAL => 0x87, "Pushes 1 if the inputs are exactly equal, 0 otherwise.";
+ OP_EQUALVERIFY => 0x88, "Returns success if the inputs are exactly equal, failure otherwise.";
+ OP_RESERVED1 => 0x89, "Synonym for OP_RETURN.";
+ OP_RESERVED2 => 0x8a, "Synonym for OP_RETURN.";
+ OP_1ADD => 0x8b, "Increment the top stack element in place.";
+ OP_1SUB => 0x8c, "Decrement the top stack element in place.";
+ OP_2MUL => 0x8d, "Fail the script unconditionally, does not even need to be executed.";
+ OP_2DIV => 0x8e, "Fail the script unconditionally, does not even need to be executed.";
+ OP_NEGATE => 0x8f, "Multiply the top stack item by -1 in place.";
+ OP_ABS => 0x90, "Absolute value the top stack item in place.";
+ OP_NOT => 0x91, "Map 0 to 1 and everything else to 0, in place.";
+ OP_0NOTEQUAL => 0x92, "Map 0 to 0 and everything else to 1, in place.";
+ OP_ADD => 0x93, "Pop two stack items and push their sum.";
+ OP_SUB => 0x94, "Pop two stack items and push the second minus the top.";
+ OP_MUL => 0x95, "Fail the script unconditionally, does not even need to be executed.";
+ OP_DIV => 0x96, "Fail the script unconditionally, does not even need to be executed.";
+ OP_MOD => 0x97, "Fail the script unconditionally, does not even need to be executed.";
+ OP_LSHIFT => 0x98, "Fail the script unconditionally, does not even need to be executed.";
+ OP_RSHIFT => 0x99, "Fail the script unconditionally, does not even need to be executed.";
+ OP_BOOLAND => 0x9a, "Pop the top two stack items and push 1 if both are nonzero, else push 0.";
+ OP_BOOLOR => 0x9b, "Pop the top two stack items and push 1 if either is nonzero, else push 0.";
+ OP_NUMEQUAL => 0x9c, "Pop the top two stack items and push 1 if both are numerically equal, else push 0.";
+ OP_NUMEQUALVERIFY => 0x9d, "Pop the top two stack items and return success if both are numerically equal, else return failure.";
+ OP_NUMNOTEQUAL => 0x9e, "Pop the top two stack items and push 0 if both are numerically equal, else push 1.";
+ OP_LESSTHAN => 0x9f, "Pop the top two items; push 1 if the second is less than the top, 0 otherwise.";
+ OP_GREATERTHAN => 0xa0, "Pop the top two items; push 1 if the second is greater than the top, 0 otherwise.";
+ OP_LESSTHANOREQUAL => 0xa1, "Pop the top two items; push 1 if the second is <= the top, 0 otherwise.";
+ OP_GREATERTHANOREQUAL => 0xa2, "Pop the top two items; push 1 if the second is >= the top, 0 otherwise.";
+ OP_MIN => 0xa3, "Pop the top two items; push the smaller.";
+ OP_MAX => 0xa4, "Pop the top two items; push the larger.";
+ OP_WITHIN => 0xa5, "Pop the top three items; if the top is >= the second and < the third, push 1, otherwise push 0.";
+ OP_RIPEMD160 => 0xa6, "Pop the top stack item and push its RIPEMD160 hash.";
+ OP_SHA1 => 0xa7, "Pop the top stack item and push its SHA1 hash.";
+ OP_SHA256 => 0xa8, "Pop the top stack item and push its SHA256 hash.";
+ OP_HASH160 => 0xa9, "Pop the top stack item and push its RIPEMD(SHA256) hash.";
+ OP_HASH256 => 0xaa, "Pop the top stack item and push its SHA256(SHA256) hash.";
+ OP_CODESEPARATOR => 0xab, "Ignore this and everything preceding when deciding what to sign when signature-checking.";
+ OP_CHECKSIG => 0xac, " pushing 1/0 for success/failure.";
+ OP_CHECKSIGVERIFY => 0xad, " returning success/failure.";
+ OP_CHECKMULTISIG => 0xae, "Pop N, N pubkeys, M, M signatures, a dummy (due to bug in reference code), \
+ and verify that all M signatures are valid. Push 1 for 'all valid', 0 otherwise.";
+ OP_CHECKMULTISIGVERIFY => 0xaf, "Like the above but return success/failure.";
+ OP_NOP1 => 0xb0, "Does nothing.";
+ OP_CLTV => 0xb1, "";
+ OP_CSV => 0xb2, "";
+ OP_NOP4 => 0xb3, "Does nothing.";
+ OP_NOP5 => 0xb4, "Does nothing.";
+ OP_NOP6 => 0xb5, "Does nothing.";
+ OP_NOP7 => 0xb6, "Does nothing.";
+ OP_NOP8 => 0xb7, "Does nothing.";
+ OP_NOP9 => 0xb8, "Does nothing.";
+ OP_NOP10 => 0xb9, "Does nothing.";
+ // Every other opcode acts as OP_RETURN
+ OP_CHECKSIGADD => 0xba, "OP_CHECKSIGADD post tapscript.";
+ OP_RETURN_187 => 0xbb, "Synonym for OP_RETURN.";
+ OP_RETURN_188 => 0xbc, "Synonym for OP_RETURN.";
+ OP_RETURN_189 => 0xbd, "Synonym for OP_RETURN.";
+ OP_RETURN_190 => 0xbe, "Synonym for OP_RETURN.";
+ OP_RETURN_191 => 0xbf, "Synonym for OP_RETURN.";
+ OP_RETURN_192 => 0xc0, "Synonym for OP_RETURN.";
+ OP_RETURN_193 => 0xc1, "Synonym for OP_RETURN.";
+ OP_RETURN_194 => 0xc2, "Synonym for OP_RETURN.";
+ OP_RETURN_195 => 0xc3, "Synonym for OP_RETURN.";
+ OP_RETURN_196 => 0xc4, "Synonym for OP_RETURN.";
+ OP_RETURN_197 => 0xc5, "Synonym for OP_RETURN.";
+ OP_RETURN_198 => 0xc6, "Synonym for OP_RETURN.";
+ OP_RETURN_199 => 0xc7, "Synonym for OP_RETURN.";
+ OP_RETURN_200 => 0xc8, "Synonym for OP_RETURN.";
+ OP_RETURN_201 => 0xc9, "Synonym for OP_RETURN.";
+ OP_RETURN_202 => 0xca, "Synonym for OP_RETURN.";
+ OP_RETURN_203 => 0xcb, "Synonym for OP_RETURN.";
+ OP_RETURN_204 => 0xcc, "Synonym for OP_RETURN.";
+ OP_RETURN_205 => 0xcd, "Synonym for OP_RETURN.";
+ OP_RETURN_206 => 0xce, "Synonym for OP_RETURN.";
+ OP_RETURN_207 => 0xcf, "Synonym for OP_RETURN.";
+ OP_RETURN_208 => 0xd0, "Synonym for OP_RETURN.";
+ OP_RETURN_209 => 0xd1, "Synonym for OP_RETURN.";
+ OP_RETURN_210 => 0xd2, "Synonym for OP_RETURN.";
+ OP_RETURN_211 => 0xd3, "Synonym for OP_RETURN.";
+ OP_RETURN_212 => 0xd4, "Synonym for OP_RETURN.";
+ OP_RETURN_213 => 0xd5, "Synonym for OP_RETURN.";
+ OP_RETURN_214 => 0xd6, "Synonym for OP_RETURN.";
+ OP_RETURN_215 => 0xd7, "Synonym for OP_RETURN.";
+ OP_RETURN_216 => 0xd8, "Synonym for OP_RETURN.";
+ OP_RETURN_217 => 0xd9, "Synonym for OP_RETURN.";
+ OP_RETURN_218 => 0xda, "Synonym for OP_RETURN.";
+ OP_RETURN_219 => 0xdb, "Synonym for OP_RETURN.";
+ OP_RETURN_220 => 0xdc, "Synonym for OP_RETURN.";
+ OP_RETURN_221 => 0xdd, "Synonym for OP_RETURN.";
+ OP_RETURN_222 => 0xde, "Synonym for OP_RETURN.";
+ OP_RETURN_223 => 0xdf, "Synonym for OP_RETURN.";
+ OP_RETURN_224 => 0xe0, "Synonym for OP_RETURN.";
+ OP_RETURN_225 => 0xe1, "Synonym for OP_RETURN.";
+ OP_RETURN_226 => 0xe2, "Synonym for OP_RETURN.";
+ OP_RETURN_227 => 0xe3, "Synonym for OP_RETURN.";
+ OP_RETURN_228 => 0xe4, "Synonym for OP_RETURN.";
+ OP_RETURN_229 => 0xe5, "Synonym for OP_RETURN.";
+ OP_RETURN_230 => 0xe6, "Synonym for OP_RETURN.";
+ OP_RETURN_231 => 0xe7, "Synonym for OP_RETURN.";
+ OP_RETURN_232 => 0xe8, "Synonym for OP_RETURN.";
+ OP_RETURN_233 => 0xe9, "Synonym for OP_RETURN.";
+ OP_RETURN_234 => 0xea, "Synonym for OP_RETURN.";
+ OP_RETURN_235 => 0xeb, "Synonym for OP_RETURN.";
+ OP_RETURN_236 => 0xec, "Synonym for OP_RETURN.";
+ OP_RETURN_237 => 0xed, "Synonym for OP_RETURN.";
+ OP_RETURN_238 => 0xee, "Synonym for OP_RETURN.";
+ OP_RETURN_239 => 0xef, "Synonym for OP_RETURN.";
+ OP_RETURN_240 => 0xf0, "Synonym for OP_RETURN.";
+ OP_RETURN_241 => 0xf1, "Synonym for OP_RETURN.";
+ OP_RETURN_242 => 0xf2, "Synonym for OP_RETURN.";
+ OP_RETURN_243 => 0xf3, "Synonym for OP_RETURN.";
+ OP_RETURN_244 => 0xf4, "Synonym for OP_RETURN.";
+ OP_RETURN_245 => 0xf5, "Synonym for OP_RETURN.";
+ OP_RETURN_246 => 0xf6, "Synonym for OP_RETURN.";
+ OP_RETURN_247 => 0xf7, "Synonym for OP_RETURN.";
+ OP_RETURN_248 => 0xf8, "Synonym for OP_RETURN.";
+ OP_RETURN_249 => 0xf9, "Synonym for OP_RETURN.";
+ OP_RETURN_250 => 0xfa, "Synonym for OP_RETURN.";
+ OP_RETURN_251 => 0xfb, "Synonym for OP_RETURN.";
+ OP_RETURN_252 => 0xfc, "Synonym for OP_RETURN.";
+ OP_RETURN_253 => 0xfd, "Synonym for OP_RETURN.";
+ OP_RETURN_254 => 0xfe, "Synonym for OP_RETURN.";
+ OP_INVALIDOPCODE => 0xff, "Synonym for OP_RETURN."
+}
+
+/// Classification context for the opcode.
+///
+/// Some opcodes like [`OP_RESERVED`] abort the script in `ClassifyContext::Legacy` context,
+/// but will act as `OP_SUCCESSx` in `ClassifyContext::TapScript` (see BIP342 for full list).
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum ClassifyContext {
+ /// Opcode used in tapscript context.
+ TapScript,
+ /// Opcode used in legacy context.
+ Legacy,
+}
+
+impl Opcode {
+ /// Classifies an Opcode into a broad class.
+ #[inline]
+ #[must_use]
+ pub fn classify(self, ctx: ClassifyContext) -> Class {
+ match (self, ctx) {
+ // 3 opcodes illegal in all contexts
+ (OP_VERIF, _) | (OP_VERNOTIF, _) | (OP_INVALIDOPCODE, _) => Class::IllegalOp,
+
+ // 15 opcodes illegal in Legacy context
+ #[rustfmt::skip]
+ (OP_CAT, ctx) | (OP_SUBSTR, ctx)
+ | (OP_LEFT, ctx) | (OP_RIGHT, ctx)
+ | (OP_INVERT, ctx)
+ | (OP_AND, ctx) | (OP_OR, ctx) | (OP_XOR, ctx)
+ | (OP_2MUL, ctx) | (OP_2DIV, ctx)
+ | (OP_MUL, ctx) | (OP_DIV, ctx) | (OP_MOD, ctx)
+ | (OP_LSHIFT, ctx) | (OP_RSHIFT, ctx) if ctx == ClassifyContext::Legacy => Class::IllegalOp,
+
+ // 87 opcodes of SuccessOp class only in TapScript context
+ (op, ClassifyContext::TapScript)
+ if op.code == 80
+ || op.code == 98
+ || (op.code >= 126 && op.code <= 129)
+ || (op.code >= 131 && op.code <= 134)
+ || (op.code >= 137 && op.code <= 138)
+ || (op.code >= 141 && op.code <= 142)
+ || (op.code >= 149 && op.code <= 153)
+ || (op.code >= 187 && op.code <= 254) =>
+ Class::SuccessOp,
+
+ // 11 opcodes of NoOp class
+ (OP_NOP, _) => Class::NoOp,
+ (op, _) if op.code >= OP_NOP1.code && op.code <= OP_NOP10.code => Class::NoOp,
+
+ // 1 opcode for `OP_RETURN`
+ (OP_RETURN, _) => Class::ReturnOp,
+
+ // 4 opcodes operating equally to `OP_RETURN` only in Legacy context
+ (OP_RESERVED, ctx) | (OP_RESERVED1, ctx) | (OP_RESERVED2, ctx) | (OP_VER, ctx)
+ if ctx == ClassifyContext::Legacy =>
+ Class::ReturnOp,
+
+ // 71 opcodes operating equally to `OP_RETURN` only in Legacy context
+ (op, ClassifyContext::Legacy) if op.code >= OP_CHECKSIGADD.code => Class::ReturnOp,
+
+ // 2 opcodes operating equally to `OP_RETURN` only in TapScript context
+ (OP_CHECKMULTISIG, ClassifyContext::TapScript)
+ | (OP_CHECKMULTISIGVERIFY, ClassifyContext::TapScript) => Class::ReturnOp,
+
+ // 1 opcode of PushNum class
+ (OP_PUSHNUM_NEG1, _) => Class::PushNum(-1),
+
+ // 16 opcodes of PushNum class
+ (op, _) if op.code >= OP_PUSHNUM_1.code && op.code <= OP_PUSHNUM_16.code =>
+ Class::PushNum(1 + self.code as i32 - OP_PUSHNUM_1.code as i32),
+
+ // 76 opcodes of PushBytes class
+ (op, _) if op.code <= OP_PUSHBYTES_75.code => Class::PushBytes(self.code as u32),
+
+ // opcodes of Ordinary class: 61 for Legacy and 60 for TapScript context
+ (_, _) => Class::Ordinary(Ordinary::with(self)),
+ }
+ }
+
+ /// Encodes [`Opcode`] as a byte.
+ #[inline]
+ pub const fn to_u8(self) -> u8 { self.code }
+
+ /// Encodes PUSHNUM [`Opcode`] as a `u8` representing its number (1-16).
+ ///
+ /// Does not convert `OP_FALSE` to 0. Only `1` to `OP_PUSHNUM_16` are covered.
+ ///
+ /// # Returns
+ ///
+ /// Returns `None` if `self` is not a PUSHNUM.
+ #[inline]
+ #[must_use]
+ pub const fn decode_pushnum(self) -> Option {
+ const START: u8 = OP_PUSHNUM_1.code;
+ const END: u8 = OP_PUSHNUM_16.code;
+ match self.code {
+ START..=END => Some(self.code - START + 1),
+ _ => None,
+ }
+ }
+}
+
+impl From for Opcode {
+ #[inline]
+ fn from(b: u8) -> Opcode { Opcode { code: b } }
+}
+
+impl fmt::Debug for Opcode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
+}
+
+#[cfg(feature = "serde")]
+impl serde::Serialize for Opcode {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(&self.to_string())
+ }
+}
+
+/// Broad categories of opcodes with similar behavior.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Class {
+ /// Pushes the given number onto the stack.
+ PushNum(i32),
+ /// Pushes the given number of bytes onto the stack.
+ PushBytes(u32),
+ /// Fails the script if executed.
+ ReturnOp,
+ /// Succeeds the script even if not executed.
+ SuccessOp,
+ /// Fails the script even if not executed.
+ IllegalOp,
+ /// Does nothing.
+ NoOp,
+ /// Any opcode not covered above.
+ Ordinary(Ordinary),
+}
+
+macro_rules! ordinary_opcode {
+ ($($op:ident),*) => (
+ #[repr(u8)]
+ #[doc(hidden)]
+ #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+ pub enum Ordinary {
+ $( $op = $op.code ),*
+ }
+
+ impl fmt::Display for Ordinary {
+ fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
+ match *self {
+ $(Ordinary::$op => { f.pad(stringify!($op)) }),*
+ }
+ }
+ }
+
+ impl Ordinary {
+ fn with(b: Opcode) -> Self {
+ match b {
+ $( $op => { Ordinary::$op } ),*
+ _ => unreachable!("construction of `Ordinary` type from non-ordinary opcode {}", b),
+ }
+ }
+
+ /// Constructs a new [`Ordinary`] from an [`Opcode`].
+ pub fn from_opcode(b: Opcode) -> Option {
+ match b {
+ $( $op => { Some(Ordinary::$op) } ),*
+ _ => None,
+ }
+ }
+ }
+ );
+}
+
+// "Ordinary" opcodes -- should be 61 of these
+ordinary_opcode! {
+ // pushdata
+ OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4,
+ // control flow
+ OP_IF, OP_NOTIF, OP_ELSE, OP_ENDIF, OP_VERIFY,
+ // stack
+ OP_TOALTSTACK, OP_FROMALTSTACK,
+ OP_2DROP, OP_2DUP, OP_3DUP, OP_2OVER, OP_2ROT, OP_2SWAP,
+ OP_DROP, OP_DUP, OP_NIP, OP_OVER, OP_PICK, OP_ROLL, OP_ROT, OP_SWAP, OP_TUCK,
+ OP_IFDUP, OP_DEPTH, OP_SIZE,
+ // equality
+ OP_EQUAL, OP_EQUALVERIFY,
+ // arithmetic
+ OP_1ADD, OP_1SUB, OP_NEGATE, OP_ABS, OP_NOT, OP_0NOTEQUAL,
+ OP_ADD, OP_SUB, OP_BOOLAND, OP_BOOLOR,
+ OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_LESSTHAN,
+ OP_GREATERTHAN, OP_LESSTHANOREQUAL, OP_GREATERTHANOREQUAL,
+ OP_MIN, OP_MAX, OP_WITHIN,
+ // crypto
+ OP_RIPEMD160, OP_SHA1, OP_SHA256, OP_HASH160, OP_HASH256,
+ OP_CODESEPARATOR, OP_CHECKSIG, OP_CHECKSIGVERIFY,
+ OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY,
+ OP_CHECKSIGADD
+}
+
+impl Ordinary {
+ /// Encodes [`Opcode`] as a byte.
+ #[inline]
+ pub fn to_u8(self) -> u8 { self as u8 }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::HashSet;
+
+ use super::*;
+
+ macro_rules! roundtrip {
+ ($unique:expr, $op:ident) => {
+ assert_eq!($op, Opcode::from($op.to_u8()));
+
+ let s1 = format!("{}", $op);
+ let s2 = format!("{:?}", $op);
+ assert_eq!(s1, s2);
+ assert_eq!(s1, stringify!($op));
+ assert!($unique.insert(s1));
+ };
+ }
+
+ #[test]
+ fn formatting_works() {
+ let op = all::OP_NOP;
+ let s = format!("{:>10}", op);
+ assert_eq!(s, " OP_NOP");
+ }
+
+ #[test]
+ fn ordinary_op_code() {
+ let ordinary_op = Ordinary::from_opcode(OP_PUSHDATA1).expect("0x4C");
+ assert_eq!(ordinary_op.to_u8(), 0x4C_u8);
+ }
+
+ #[test]
+ fn decode_pushnum() {
+ // Test all possible opcodes
+ // - Sanity check
+ assert_eq!(OP_PUSHNUM_1.code, 0x51_u8);
+ assert_eq!(OP_PUSHNUM_16.code, 0x60_u8);
+ for i in 0x00..=0xff_u8 {
+ let expected = match i {
+ // OP_PUSHNUM_1 ..= OP_PUSHNUM_16
+ 0x51..=0x60 => Some(i - 0x50),
+ _ => None,
+ };
+ assert_eq!(Opcode::from(i).decode_pushnum(), expected);
+ }
+
+ // Test the named opcode constants
+ // - This is the OP right before PUSHNUMs start
+ assert!(OP_RESERVED.decode_pushnum().is_none());
+ assert_eq!(OP_PUSHNUM_1.decode_pushnum().expect("pushnum"), 1);
+ assert_eq!(OP_PUSHNUM_2.decode_pushnum().expect("pushnum"), 2);
+ assert_eq!(OP_PUSHNUM_3.decode_pushnum().expect("pushnum"), 3);
+ assert_eq!(OP_PUSHNUM_4.decode_pushnum().expect("pushnum"), 4);
+ assert_eq!(OP_PUSHNUM_5.decode_pushnum().expect("pushnum"), 5);
+ assert_eq!(OP_PUSHNUM_6.decode_pushnum().expect("pushnum"), 6);
+ assert_eq!(OP_PUSHNUM_7.decode_pushnum().expect("pushnum"), 7);
+ assert_eq!(OP_PUSHNUM_8.decode_pushnum().expect("pushnum"), 8);
+ assert_eq!(OP_PUSHNUM_9.decode_pushnum().expect("pushnum"), 9);
+ assert_eq!(OP_PUSHNUM_10.decode_pushnum().expect("pushnum"), 10);
+ assert_eq!(OP_PUSHNUM_11.decode_pushnum().expect("pushnum"), 11);
+ assert_eq!(OP_PUSHNUM_12.decode_pushnum().expect("pushnum"), 12);
+ assert_eq!(OP_PUSHNUM_13.decode_pushnum().expect("pushnum"), 13);
+ assert_eq!(OP_PUSHNUM_14.decode_pushnum().expect("pushnum"), 14);
+ assert_eq!(OP_PUSHNUM_15.decode_pushnum().expect("pushnum"), 15);
+ assert_eq!(OP_PUSHNUM_16.decode_pushnum().expect("pushnum"), 16);
+ // - This is the OP right after PUSHNUMs end
+ assert!(OP_NOP.decode_pushnum().is_none());
+ }
+
+ #[test]
+ fn classify_test() {
+ let op174 = OP_CHECKMULTISIG;
+ assert_eq!(
+ op174.classify(ClassifyContext::Legacy),
+ Class::Ordinary(Ordinary::OP_CHECKMULTISIG)
+ );
+ assert_eq!(op174.classify(ClassifyContext::TapScript), Class::ReturnOp);
+
+ let op175 = OP_CHECKMULTISIGVERIFY;
+ assert_eq!(
+ op175.classify(ClassifyContext::Legacy),
+ Class::Ordinary(Ordinary::OP_CHECKMULTISIGVERIFY)
+ );
+ assert_eq!(op175.classify(ClassifyContext::TapScript), Class::ReturnOp);
+
+ let op186 = OP_CHECKSIGADD;
+ assert_eq!(op186.classify(ClassifyContext::Legacy), Class::ReturnOp);
+ assert_eq!(
+ op186.classify(ClassifyContext::TapScript),
+ Class::Ordinary(Ordinary::OP_CHECKSIGADD)
+ );
+
+ let op187 = OP_RETURN_187;
+ assert_eq!(op187.classify(ClassifyContext::Legacy), Class::ReturnOp);
+ assert_eq!(op187.classify(ClassifyContext::TapScript), Class::SuccessOp);
+ }
+
+ #[test]
+ fn str_roundtrip() {
+ let mut unique = HashSet::new();
+ roundtrip!(unique, OP_PUSHBYTES_0);
+ roundtrip!(unique, OP_PUSHBYTES_1);
+ roundtrip!(unique, OP_PUSHBYTES_2);
+ roundtrip!(unique, OP_PUSHBYTES_3);
+ roundtrip!(unique, OP_PUSHBYTES_4);
+ roundtrip!(unique, OP_PUSHBYTES_5);
+ roundtrip!(unique, OP_PUSHBYTES_6);
+ roundtrip!(unique, OP_PUSHBYTES_7);
+ roundtrip!(unique, OP_PUSHBYTES_8);
+ roundtrip!(unique, OP_PUSHBYTES_9);
+ roundtrip!(unique, OP_PUSHBYTES_10);
+ roundtrip!(unique, OP_PUSHBYTES_11);
+ roundtrip!(unique, OP_PUSHBYTES_12);
+ roundtrip!(unique, OP_PUSHBYTES_13);
+ roundtrip!(unique, OP_PUSHBYTES_14);
+ roundtrip!(unique, OP_PUSHBYTES_15);
+ roundtrip!(unique, OP_PUSHBYTES_16);
+ roundtrip!(unique, OP_PUSHBYTES_17);
+ roundtrip!(unique, OP_PUSHBYTES_18);
+ roundtrip!(unique, OP_PUSHBYTES_19);
+ roundtrip!(unique, OP_PUSHBYTES_20);
+ roundtrip!(unique, OP_PUSHBYTES_21);
+ roundtrip!(unique, OP_PUSHBYTES_22);
+ roundtrip!(unique, OP_PUSHBYTES_23);
+ roundtrip!(unique, OP_PUSHBYTES_24);
+ roundtrip!(unique, OP_PUSHBYTES_25);
+ roundtrip!(unique, OP_PUSHBYTES_26);
+ roundtrip!(unique, OP_PUSHBYTES_27);
+ roundtrip!(unique, OP_PUSHBYTES_28);
+ roundtrip!(unique, OP_PUSHBYTES_29);
+ roundtrip!(unique, OP_PUSHBYTES_30);
+ roundtrip!(unique, OP_PUSHBYTES_31);
+ roundtrip!(unique, OP_PUSHBYTES_32);
+ roundtrip!(unique, OP_PUSHBYTES_33);
+ roundtrip!(unique, OP_PUSHBYTES_34);
+ roundtrip!(unique, OP_PUSHBYTES_35);
+ roundtrip!(unique, OP_PUSHBYTES_36);
+ roundtrip!(unique, OP_PUSHBYTES_37);
+ roundtrip!(unique, OP_PUSHBYTES_38);
+ roundtrip!(unique, OP_PUSHBYTES_39);
+ roundtrip!(unique, OP_PUSHBYTES_40);
+ roundtrip!(unique, OP_PUSHBYTES_41);
+ roundtrip!(unique, OP_PUSHBYTES_42);
+ roundtrip!(unique, OP_PUSHBYTES_43);
+ roundtrip!(unique, OP_PUSHBYTES_44);
+ roundtrip!(unique, OP_PUSHBYTES_45);
+ roundtrip!(unique, OP_PUSHBYTES_46);
+ roundtrip!(unique, OP_PUSHBYTES_47);
+ roundtrip!(unique, OP_PUSHBYTES_48);
+ roundtrip!(unique, OP_PUSHBYTES_49);
+ roundtrip!(unique, OP_PUSHBYTES_50);
+ roundtrip!(unique, OP_PUSHBYTES_51);
+ roundtrip!(unique, OP_PUSHBYTES_52);
+ roundtrip!(unique, OP_PUSHBYTES_53);
+ roundtrip!(unique, OP_PUSHBYTES_54);
+ roundtrip!(unique, OP_PUSHBYTES_55);
+ roundtrip!(unique, OP_PUSHBYTES_56);
+ roundtrip!(unique, OP_PUSHBYTES_57);
+ roundtrip!(unique, OP_PUSHBYTES_58);
+ roundtrip!(unique, OP_PUSHBYTES_59);
+ roundtrip!(unique, OP_PUSHBYTES_60);
+ roundtrip!(unique, OP_PUSHBYTES_61);
+ roundtrip!(unique, OP_PUSHBYTES_62);
+ roundtrip!(unique, OP_PUSHBYTES_63);
+ roundtrip!(unique, OP_PUSHBYTES_64);
+ roundtrip!(unique, OP_PUSHBYTES_65);
+ roundtrip!(unique, OP_PUSHBYTES_66);
+ roundtrip!(unique, OP_PUSHBYTES_67);
+ roundtrip!(unique, OP_PUSHBYTES_68);
+ roundtrip!(unique, OP_PUSHBYTES_69);
+ roundtrip!(unique, OP_PUSHBYTES_70);
+ roundtrip!(unique, OP_PUSHBYTES_71);
+ roundtrip!(unique, OP_PUSHBYTES_72);
+ roundtrip!(unique, OP_PUSHBYTES_73);
+ roundtrip!(unique, OP_PUSHBYTES_74);
+ roundtrip!(unique, OP_PUSHBYTES_75);
+ roundtrip!(unique, OP_PUSHDATA1);
+ roundtrip!(unique, OP_PUSHDATA2);
+ roundtrip!(unique, OP_PUSHDATA4);
+ roundtrip!(unique, OP_PUSHNUM_NEG1);
+ roundtrip!(unique, OP_RESERVED);
+ roundtrip!(unique, OP_PUSHNUM_1);
+ roundtrip!(unique, OP_PUSHNUM_2);
+ roundtrip!(unique, OP_PUSHNUM_3);
+ roundtrip!(unique, OP_PUSHNUM_4);
+ roundtrip!(unique, OP_PUSHNUM_5);
+ roundtrip!(unique, OP_PUSHNUM_6);
+ roundtrip!(unique, OP_PUSHNUM_7);
+ roundtrip!(unique, OP_PUSHNUM_8);
+ roundtrip!(unique, OP_PUSHNUM_9);
+ roundtrip!(unique, OP_PUSHNUM_10);
+ roundtrip!(unique, OP_PUSHNUM_11);
+ roundtrip!(unique, OP_PUSHNUM_12);
+ roundtrip!(unique, OP_PUSHNUM_13);
+ roundtrip!(unique, OP_PUSHNUM_14);
+ roundtrip!(unique, OP_PUSHNUM_15);
+ roundtrip!(unique, OP_PUSHNUM_16);
+ roundtrip!(unique, OP_NOP);
+ roundtrip!(unique, OP_VER);
+ roundtrip!(unique, OP_IF);
+ roundtrip!(unique, OP_NOTIF);
+ roundtrip!(unique, OP_VERIF);
+ roundtrip!(unique, OP_VERNOTIF);
+ roundtrip!(unique, OP_ELSE);
+ roundtrip!(unique, OP_ENDIF);
+ roundtrip!(unique, OP_VERIFY);
+ roundtrip!(unique, OP_RETURN);
+ roundtrip!(unique, OP_TOALTSTACK);
+ roundtrip!(unique, OP_FROMALTSTACK);
+ roundtrip!(unique, OP_2DROP);
+ roundtrip!(unique, OP_2DUP);
+ roundtrip!(unique, OP_3DUP);
+ roundtrip!(unique, OP_2OVER);
+ roundtrip!(unique, OP_2ROT);
+ roundtrip!(unique, OP_2SWAP);
+ roundtrip!(unique, OP_IFDUP);
+ roundtrip!(unique, OP_DEPTH);
+ roundtrip!(unique, OP_DROP);
+ roundtrip!(unique, OP_DUP);
+ roundtrip!(unique, OP_NIP);
+ roundtrip!(unique, OP_OVER);
+ roundtrip!(unique, OP_PICK);
+ roundtrip!(unique, OP_ROLL);
+ roundtrip!(unique, OP_ROT);
+ roundtrip!(unique, OP_SWAP);
+ roundtrip!(unique, OP_TUCK);
+ roundtrip!(unique, OP_CAT);
+ roundtrip!(unique, OP_SUBSTR);
+ roundtrip!(unique, OP_LEFT);
+ roundtrip!(unique, OP_RIGHT);
+ roundtrip!(unique, OP_SIZE);
+ roundtrip!(unique, OP_INVERT);
+ roundtrip!(unique, OP_AND);
+ roundtrip!(unique, OP_OR);
+ roundtrip!(unique, OP_XOR);
+ roundtrip!(unique, OP_EQUAL);
+ roundtrip!(unique, OP_EQUALVERIFY);
+ roundtrip!(unique, OP_RESERVED1);
+ roundtrip!(unique, OP_RESERVED2);
+ roundtrip!(unique, OP_1ADD);
+ roundtrip!(unique, OP_1SUB);
+ roundtrip!(unique, OP_2MUL);
+ roundtrip!(unique, OP_2DIV);
+ roundtrip!(unique, OP_NEGATE);
+ roundtrip!(unique, OP_ABS);
+ roundtrip!(unique, OP_NOT);
+ roundtrip!(unique, OP_0NOTEQUAL);
+ roundtrip!(unique, OP_ADD);
+ roundtrip!(unique, OP_SUB);
+ roundtrip!(unique, OP_MUL);
+ roundtrip!(unique, OP_DIV);
+ roundtrip!(unique, OP_MOD);
+ roundtrip!(unique, OP_LSHIFT);
+ roundtrip!(unique, OP_RSHIFT);
+ roundtrip!(unique, OP_BOOLAND);
+ roundtrip!(unique, OP_BOOLOR);
+ roundtrip!(unique, OP_NUMEQUAL);
+ roundtrip!(unique, OP_NUMEQUALVERIFY);
+ roundtrip!(unique, OP_NUMNOTEQUAL);
+ roundtrip!(unique, OP_LESSTHAN);
+ roundtrip!(unique, OP_GREATERTHAN);
+ roundtrip!(unique, OP_LESSTHANOREQUAL);
+ roundtrip!(unique, OP_GREATERTHANOREQUAL);
+ roundtrip!(unique, OP_MIN);
+ roundtrip!(unique, OP_MAX);
+ roundtrip!(unique, OP_WITHIN);
+ roundtrip!(unique, OP_RIPEMD160);
+ roundtrip!(unique, OP_SHA1);
+ roundtrip!(unique, OP_SHA256);
+ roundtrip!(unique, OP_HASH160);
+ roundtrip!(unique, OP_HASH256);
+ roundtrip!(unique, OP_CODESEPARATOR);
+ roundtrip!(unique, OP_CHECKSIG);
+ roundtrip!(unique, OP_CHECKSIGVERIFY);
+ roundtrip!(unique, OP_CHECKMULTISIG);
+ roundtrip!(unique, OP_CHECKMULTISIGVERIFY);
+ roundtrip!(unique, OP_NOP1);
+ roundtrip!(unique, OP_CLTV);
+ roundtrip!(unique, OP_CSV);
+ roundtrip!(unique, OP_NOP4);
+ roundtrip!(unique, OP_NOP5);
+ roundtrip!(unique, OP_NOP6);
+ roundtrip!(unique, OP_NOP7);
+ roundtrip!(unique, OP_NOP8);
+ roundtrip!(unique, OP_NOP9);
+ roundtrip!(unique, OP_NOP10);
+ roundtrip!(unique, OP_CHECKSIGADD);
+ roundtrip!(unique, OP_RETURN_187);
+ roundtrip!(unique, OP_RETURN_188);
+ roundtrip!(unique, OP_RETURN_189);
+ roundtrip!(unique, OP_RETURN_190);
+ roundtrip!(unique, OP_RETURN_191);
+ roundtrip!(unique, OP_RETURN_192);
+ roundtrip!(unique, OP_RETURN_193);
+ roundtrip!(unique, OP_RETURN_194);
+ roundtrip!(unique, OP_RETURN_195);
+ roundtrip!(unique, OP_RETURN_196);
+ roundtrip!(unique, OP_RETURN_197);
+ roundtrip!(unique, OP_RETURN_198);
+ roundtrip!(unique, OP_RETURN_199);
+ roundtrip!(unique, OP_RETURN_200);
+ roundtrip!(unique, OP_RETURN_201);
+ roundtrip!(unique, OP_RETURN_202);
+ roundtrip!(unique, OP_RETURN_203);
+ roundtrip!(unique, OP_RETURN_204);
+ roundtrip!(unique, OP_RETURN_205);
+ roundtrip!(unique, OP_RETURN_206);
+ roundtrip!(unique, OP_RETURN_207);
+ roundtrip!(unique, OP_RETURN_208);
+ roundtrip!(unique, OP_RETURN_209);
+ roundtrip!(unique, OP_RETURN_210);
+ roundtrip!(unique, OP_RETURN_211);
+ roundtrip!(unique, OP_RETURN_212);
+ roundtrip!(unique, OP_RETURN_213);
+ roundtrip!(unique, OP_RETURN_214);
+ roundtrip!(unique, OP_RETURN_215);
+ roundtrip!(unique, OP_RETURN_216);
+ roundtrip!(unique, OP_RETURN_217);
+ roundtrip!(unique, OP_RETURN_218);
+ roundtrip!(unique, OP_RETURN_219);
+ roundtrip!(unique, OP_RETURN_220);
+ roundtrip!(unique, OP_RETURN_221);
+ roundtrip!(unique, OP_RETURN_222);
+ roundtrip!(unique, OP_RETURN_223);
+ roundtrip!(unique, OP_RETURN_224);
+ roundtrip!(unique, OP_RETURN_225);
+ roundtrip!(unique, OP_RETURN_226);
+ roundtrip!(unique, OP_RETURN_227);
+ roundtrip!(unique, OP_RETURN_228);
+ roundtrip!(unique, OP_RETURN_229);
+ roundtrip!(unique, OP_RETURN_230);
+ roundtrip!(unique, OP_RETURN_231);
+ roundtrip!(unique, OP_RETURN_232);
+ roundtrip!(unique, OP_RETURN_233);
+ roundtrip!(unique, OP_RETURN_234);
+ roundtrip!(unique, OP_RETURN_235);
+ roundtrip!(unique, OP_RETURN_236);
+ roundtrip!(unique, OP_RETURN_237);
+ roundtrip!(unique, OP_RETURN_238);
+ roundtrip!(unique, OP_RETURN_239);
+ roundtrip!(unique, OP_RETURN_240);
+ roundtrip!(unique, OP_RETURN_241);
+ roundtrip!(unique, OP_RETURN_242);
+ roundtrip!(unique, OP_RETURN_243);
+ roundtrip!(unique, OP_RETURN_244);
+ roundtrip!(unique, OP_RETURN_245);
+ roundtrip!(unique, OP_RETURN_246);
+ roundtrip!(unique, OP_RETURN_247);
+ roundtrip!(unique, OP_RETURN_248);
+ roundtrip!(unique, OP_RETURN_249);
+ roundtrip!(unique, OP_RETURN_250);
+ roundtrip!(unique, OP_RETURN_251);
+ roundtrip!(unique, OP_RETURN_252);
+ roundtrip!(unique, OP_RETURN_253);
+ roundtrip!(unique, OP_RETURN_254);
+ roundtrip!(unique, OP_INVALIDOPCODE);
+ assert_eq!(unique.len(), 256);
+ }
+}
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index e1fe91b781..c048ba3670 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -12,10 +12,10 @@ use super::{
use crate::consensus::Encodable;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
-use crate::policy::DUST_RELAY_TX_FEE;
+use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
use crate::prelude::{sink, DisplayHex, String, ToString};
-use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _};
-use crate::FeeRate;
+use crate::taproot::{LeafVersion, TapLeafHash};
+use crate::{Amount, FeeRate};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
@@ -172,7 +172,7 @@ crate::internal_macros::define_extension_trait! {
instructions.next().is_none()
}
- /// Checks whether a script pubkey is a Segregated Witness (segwit) program.
+ /// Checks whether a script pubkey is a Segregated Witness (SegWit) program.
#[inline]
fn is_witness_program(&self) -> bool { self.witness_version().is_some() }
@@ -217,7 +217,7 @@ crate::internal_macros::define_extension_trait! {
/// What this function considers to be standard may change without warning pending Bitcoin Core
/// changes.
#[inline]
- fn is_standard_op_return(&self) -> bool { self.is_op_return() && self.len() <= 80 }
+ fn is_standard_op_return(&self) -> bool { self.is_op_return() && self.len() <= MAX_OP_RETURN_RELAY }
/// Checks whether a script is trivially known to have no satisfying input.
///
@@ -261,7 +261,7 @@ crate::internal_macros::define_extension_trait! {
/// Returns the minimum value an output with this script should have in order to be
/// broadcastable on today’s Bitcoin network.
#[deprecated(since = "0.32.0", note = "use `minimal_non_dust` etc. instead")]
- fn dust_value(&self) -> crate::Amount { self.minimal_non_dust() }
+ fn dust_value(&self) -> Option { self.minimal_non_dust() }
/// Returns the minimum value an output with this script should have in order to be
/// broadcastable on today's Bitcoin network.
@@ -272,7 +272,7 @@ crate::internal_macros::define_extension_trait! {
/// To use a custom value, use [`minimal_non_dust_custom`].
///
/// [`minimal_non_dust_custom`]: Script::minimal_non_dust_custom
- fn minimal_non_dust(&self) -> crate::Amount {
+ fn minimal_non_dust(&self) -> Option {
self.minimal_non_dust_internal(DUST_RELAY_TX_FEE.into())
}
@@ -287,7 +287,7 @@ crate::internal_macros::define_extension_trait! {
/// To use the default Bitcoin Core value, use [`minimal_non_dust`].
///
/// [`minimal_non_dust`]: Script::minimal_non_dust
- fn minimal_non_dust_custom(&self, dust_relay_fee: FeeRate) -> crate::Amount {
+ fn minimal_non_dust_custom(&self, dust_relay_fee: FeeRate) -> Option {
self.minimal_non_dust_internal(dust_relay_fee.to_sat_per_kwu() * 4)
}
@@ -394,7 +394,7 @@ mod sealed {
crate::internal_macros::define_extension_trait! {
pub(crate) trait ScriptExtPriv impl for Script {
- fn minimal_non_dust_internal(&self, dust_relay_fee: u64) -> crate::Amount {
+ fn minimal_non_dust_internal(&self, dust_relay_fee: u64) -> Option {
// This must never be lower than Bitcoin Core's GetDustThreshold() (as of v0.21) as it may
// otherwise allow users to create transactions which likely can never be broadcast/confirmed.
let sats = dust_relay_fee
@@ -408,13 +408,12 @@ crate::internal_macros::define_extension_trait! {
32 + 4 + 1 + 107 + 4 + // The spend cost copied from Core
8 + // The serialized size of the TxOut's amount field
self.consensus_encode(&mut sink()).expect("sinks don't error").to_u64() // The serialized size of this script_pubkey
- })
- .expect("dust_relay_fee or script length should not be absurdly large")
+ })?
/ 1000; // divide by 1000 like in Core to get value as it cancels out DEFAULT_MIN_RELAY_TX_FEE
// Note: We ensure the division happens at the end, since Core performs the division at the end.
// This will make sure none of the implicit floor operations mess with the value.
- crate::Amount::from_sat(sats)
+ Amount::from_sat(sats).ok()
}
fn count_sigops_internal(&self, accurate: bool) -> usize {
diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs
index 6e0c7875c5..b8a52ea91a 100644
--- a/bitcoin/src/blockdata/script/builder.rs
+++ b/bitcoin/src/blockdata/script/builder.rs
@@ -150,4 +150,6 @@ impl fmt::Display for Builder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
-internals::debug_from_display!(Builder);
+impl fmt::Debug for Builder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
+}
diff --git a/bitcoin/src/blockdata/script/instruction.rs b/bitcoin/src/blockdata/script/instruction.rs
index 8bf30cdd76..16cd1df458 100644
--- a/bitcoin/src/blockdata/script/instruction.rs
+++ b/bitcoin/src/blockdata/script/instruction.rs
@@ -31,7 +31,7 @@ impl Instruction<'_> {
}
}
- /// Returns the number interpretted by the script parser
+ /// Returns the number interpreted by the script parser
/// if it can be coerced into a number.
///
/// This does not require the script num to be minimal.
@@ -47,12 +47,8 @@ impl Instruction<'_> {
_ => None,
}
}
- Instruction::PushBytes(bytes) => {
- match super::read_scriptint_non_minimal(bytes.as_bytes()) {
- Ok(v) => Some(v),
- _ => None,
- }
- }
+ Instruction::PushBytes(bytes) =>
+ super::read_scriptint_non_minimal(bytes.as_bytes()).ok(),
}
}
@@ -78,10 +74,7 @@ impl Instruction<'_> {
_ => None,
}
}
- Instruction::PushBytes(bytes) => match bytes.read_scriptint() {
- Ok(v) => Some(v),
- _ => None,
- },
+ Instruction::PushBytes(bytes) => bytes.read_scriptint().ok(),
}
}
}
@@ -238,7 +231,7 @@ impl<'a> InstructionIndices<'a> {
let prev_remaining = self.remaining_bytes();
let prev_pos = self.pos;
let instruction = next_fn(self)?;
- // No underflow: there must be less remaining bytes now than previously
+ // No overflow: there must be less remaining bytes now than previously
let consumed = prev_remaining - self.remaining_bytes();
// No overflow: sum will never exceed slice length which itself can't exceed `usize`
self.pos += consumed;
diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs
index 55947e43a0..1bd2bddf35 100644
--- a/bitcoin/src/blockdata/script/mod.rs
+++ b/bitcoin/src/blockdata/script/mod.rs
@@ -57,17 +57,16 @@ mod tests;
pub mod witness_program;
pub mod witness_version;
+use core::convert::Infallible;
use core::fmt;
-use hashes::{hash160, sha256};
use io::{BufRead, Write};
-use primitives::opcodes::all::*;
-use primitives::opcodes::Opcode;
use crate::consensus::{encode, Decodable, Encodable};
-use crate::constants::{MAX_REDEEM_SCRIPT_SIZE, MAX_WITNESS_SCRIPT_SIZE};
use crate::internal_macros::impl_asref_push_bytes;
use crate::key::WPubkeyHash;
+use crate::opcodes::all::*;
+use crate::opcodes::Opcode;
use crate::prelude::Vec;
use crate::OutPoint;
@@ -81,130 +80,15 @@ pub use self::{
push_bytes::{PushBytes, PushBytesBuf, PushBytesError, PushBytesErrorReport},
};
#[doc(inline)]
-pub use primitives::script::*;
+pub use primitives::script::{
+ RedeemScriptSizeError, Script, ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError,
+};
pub(crate) use self::borrowed::ScriptExtPriv;
pub(crate) use self::owned::ScriptBufExtPriv;
-hashes::hash_newtype! {
- /// A hash of Bitcoin Script bytecode.
- pub struct ScriptHash(hash160::Hash);
- /// SegWit version of a Bitcoin Script bytecode hash.
- pub struct WScriptHash(sha256::Hash);
-}
-
-hashes::impl_hex_for_newtype!(ScriptHash, WScriptHash);
-#[cfg(feature = "serde")]
-hashes::impl_serde_for_newtype!(ScriptHash, WScriptHash);
-
impl_asref_push_bytes!(ScriptHash, WScriptHash);
-impl ScriptHash {
- /// Constructs a new `ScriptHash` after first checking the script size.
- ///
- /// # 520-byte limitation on serialized script size
- ///
- /// > As a consequence of the requirement for backwards compatibility the serialized script is
- /// > itself subject to the same rules as any other PUSHDATA operation, including the rule that
- /// > no data greater than 520 bytes may be pushed to the stack. Thus it is not possible to
- /// > spend a P2SH output if the redemption script it refers to is >520 bytes in length.
- ///
- /// ref: [BIP-16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki#user-content-520byte_limitation_on_serialized_script_size)
- pub fn from_script(redeem_script: &Script) -> Result {
- if redeem_script.len() > MAX_REDEEM_SCRIPT_SIZE {
- return Err(RedeemScriptSizeError { size: redeem_script.len() });
- }
-
- Ok(ScriptHash(hash160::Hash::hash(redeem_script.as_bytes())))
- }
-
- /// Constructs a new `ScriptHash` from any script irrespective of script size.
- ///
- /// If you hash a script that exceeds 520 bytes in size and use it to create a P2SH output
- /// then the output will be unspendable (see [BIP-16]).
- ///
- /// [BIP-16]:
- pub fn from_script_unchecked(script: &Script) -> Self {
- ScriptHash(hash160::Hash::hash(script.as_bytes()))
- }
-}
-
-impl WScriptHash {
- /// Constructs a new `WScriptHash` after first checking the script size.
- ///
- /// # 10,000-byte limit on the witness script
- ///
- /// > The witnessScript (≤ 10,000 bytes) is popped off the initial witness stack. SHA256 of the
- /// > witnessScript must match the 32-byte witness program.
- ///
- /// ref: [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
- pub fn from_script(witness_script: &Script) -> Result {
- if witness_script.len() > MAX_WITNESS_SCRIPT_SIZE {
- return Err(WitnessScriptSizeError { size: witness_script.len() });
- }
-
- Ok(WScriptHash(sha256::Hash::hash(witness_script.as_bytes())))
- }
-
- /// Constructs a new `WScriptHash` from any script irrespective of script size.
- ///
- /// If you hash a script that exceeds 10,000 bytes in size and use it to create a Segwit
- /// output then the output will be unspendable (see [BIP-141]).
- ///
- /// ref: [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
- pub fn from_script_unchecked(script: &Script) -> Self {
- WScriptHash(sha256::Hash::hash(script.as_bytes()))
- }
-}
-
-impl TryFrom for ScriptHash {
- type Error = RedeemScriptSizeError;
-
- fn try_from(redeem_script: ScriptBuf) -> Result {
- Self::from_script(&redeem_script)
- }
-}
-
-impl TryFrom<&ScriptBuf> for ScriptHash {
- type Error = RedeemScriptSizeError;
-
- fn try_from(redeem_script: &ScriptBuf) -> Result {
- Self::from_script(redeem_script)
- }
-}
-
-impl TryFrom<&Script> for ScriptHash {
- type Error = RedeemScriptSizeError;
-
- fn try_from(redeem_script: &Script) -> Result {
- Self::from_script(redeem_script)
- }
-}
-
-impl TryFrom for WScriptHash {
- type Error = WitnessScriptSizeError;
-
- fn try_from(witness_script: ScriptBuf) -> Result {
- Self::from_script(&witness_script)
- }
-}
-
-impl TryFrom<&ScriptBuf> for WScriptHash {
- type Error = WitnessScriptSizeError;
-
- fn try_from(witness_script: &ScriptBuf) -> Result {
- Self::from_script(witness_script)
- }
-}
-
-impl TryFrom<&Script> for WScriptHash {
- type Error = WitnessScriptSizeError;
-
- fn try_from(witness_script: &Script) -> Result {
- Self::from_script(witness_script)
- }
-}
-
/// Constructs a new [`ScriptBuf`] containing the script code used for spending a P2WPKH output.
///
/// The `scriptCode` is described in [BIP143].
@@ -351,7 +235,9 @@ pub enum Error {
Serialization,
}
-internals::impl_from_infallible!(Error);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -383,39 +269,3 @@ impl std::error::Error for Error {
}
}
}
-
-/// Error while hashing a redeem script.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct RedeemScriptSizeError {
- /// Invalid redeem script size (cannot exceed 520 bytes).
- pub size: usize,
-}
-
-internals::impl_from_infallible!(RedeemScriptSizeError);
-
-impl fmt::Display for RedeemScriptSizeError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "redeem script size exceeds {} bytes: {}", MAX_REDEEM_SCRIPT_SIZE, self.size)
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for RedeemScriptSizeError {}
-
-/// Error while hashing a witness script.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct WitnessScriptSizeError {
- /// Invalid witness script size (cannot exceed 10,000 bytes).
- pub size: usize,
-}
-
-internals::impl_from_infallible!(WitnessScriptSizeError);
-
-impl fmt::Display for WitnessScriptSizeError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "witness script size exceeds {} bytes: {}", MAX_WITNESS_SCRIPT_SIZE, self.size)
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for WitnessScriptSizeError {}
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index ccc82ca894..8b8b73e385 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -203,7 +203,7 @@ impl<'a> Extend> for ScriptBuf {
/// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer.
///
/// In reality the backing `Vec` is swapped with an empty one and this is holding both the
-/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not
+/// reference and the vec. The vec is put back when this drops so it also covers panics. (But not
/// leaks, which is OK since we never leak.)
pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec);
diff --git a/bitcoin/src/blockdata/script/push_bytes.rs b/bitcoin/src/blockdata/script/push_bytes.rs
index 59f6513149..bae7902b3b 100644
--- a/bitcoin/src/blockdata/script/push_bytes.rs
+++ b/bitcoin/src/blockdata/script/push_bytes.rs
@@ -413,7 +413,7 @@ mod error {
/// Error returned on attempt to create too large `PushBytes`.
#[allow(unused)]
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
pub struct PushBytesError {
never: core::convert::Infallible,
}
@@ -435,7 +435,7 @@ mod error {
use core::fmt;
/// Error returned on attempt to create too large `PushBytes`.
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[derive(Debug, Clone, PartialEq, Eq)]
pub struct PushBytesError {
/// How long the input was.
pub(super) len: usize,
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index 5a3409af6c..27dcd32ea3 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: CC0-1.0
use hex_lit::hex;
-use primitives::opcodes;
use super::*;
use crate::address::script_pubkey::{
@@ -9,7 +8,7 @@ use crate::address::script_pubkey::{
};
use crate::consensus::encode::{deserialize, serialize};
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
-use crate::FeeRate;
+use crate::{opcodes, Amount, FeeRate};
#[test]
#[rustfmt::skip]
@@ -416,8 +415,10 @@ fn standard_op_return() {
assert!(ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e")
.unwrap()
.is_standard_op_return());
-
- assert!(!ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21")
+ assert!(ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21")
+ .unwrap()
+ .is_standard_op_return());
+ assert!(!ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21524f42")
.unwrap()
.is_standard_op_return());
}
@@ -483,10 +484,6 @@ fn script_json_serialize() {
#[test]
fn script_asm() {
- assert_eq!(
- ScriptBuf::from_hex("6363636363686868686800").unwrap().to_string(),
- "OP_IF OP_IF OP_IF OP_IF OP_IF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_0"
- );
assert_eq!(
ScriptBuf::from_hex("6363636363686868686800").unwrap().to_string(),
"OP_IF OP_IF OP_IF OP_IF OP_IF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_0"
@@ -497,7 +494,7 @@ fn script_asm() {
assert_eq!(ScriptBuf::from_hex("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap().to_string(),
"OP_0 OP_PUSHBYTES_71 304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401 OP_0 OP_PUSHDATA1 552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae");
// Various weird scripts found in transaction 6d7ed9914625c73c0288694a6819196a27ef6c08f98e1270d975a8e65a3dc09a
- // which triggerred overflow bugs on 32-bit machines in script formatting in the past.
+ // which triggered overflow bugs on 32-bit machines in script formatting in the past.
assert_eq!(ScriptBuf::from_hex("01").unwrap().to_string(), "OP_PUSHBYTES_1 ");
assert_eq!(ScriptBuf::from_hex("0201").unwrap().to_string(), "OP_PUSHBYTES_2 ");
assert_eq!(ScriptBuf::from_hex("4c").unwrap().to_string(), "");
@@ -593,7 +590,7 @@ macro_rules! unwrap_all {
}
#[test]
-fn test_iterator() {
+fn iterator() {
let zero = ScriptBuf::from_hex("00").unwrap();
let zeropush = ScriptBuf::from_hex("0100").unwrap();
@@ -671,14 +668,14 @@ fn script_ord() {
#[test]
#[cfg(feature = "bitcoinconsensus")]
-fn test_bitcoinconsensus() {
+fn bitcoinconsensus() {
use crate::consensus_validation::ScriptExt as _;
- // a random segwit transaction from the blockchain using native segwit
+ // a random SegWit transaction from the blockchain using native SegWit
let spent_bytes = hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d");
let spent = Script::from_bytes(&spent_bytes);
let spending = hex!("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000");
- spent.verify(0, crate::Amount::from_sat(18393430), &spending).unwrap();
+ spent.verify(0, Amount::from_sat_u32(18393430), &spending).unwrap();
}
#[test]
@@ -687,10 +684,10 @@ fn default_dust_value() {
// well-known scriptPubKey types.
let script_p2wpkh = Builder::new().push_int_unchecked(0).push_slice([42; 20]).into_script();
assert!(script_p2wpkh.is_p2wpkh());
- assert_eq!(script_p2wpkh.minimal_non_dust(), crate::Amount::from_sat(294));
+ assert_eq!(script_p2wpkh.minimal_non_dust(), Some(Amount::from_sat_u32(294)));
assert_eq!(
script_p2wpkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
- crate::Amount::from_sat(588)
+ Some(Amount::from_sat_u32(588))
);
let script_p2pkh = Builder::new()
@@ -701,15 +698,15 @@ fn default_dust_value() {
.push_opcode(OP_CHECKSIG)
.into_script();
assert!(script_p2pkh.is_p2pkh());
- assert_eq!(script_p2pkh.minimal_non_dust(), crate::Amount::from_sat(546));
+ assert_eq!(script_p2pkh.minimal_non_dust(), Some(Amount::from_sat_u32(546)));
assert_eq!(
script_p2pkh.minimal_non_dust_custom(FeeRate::from_sat_per_vb_unchecked(6)),
- crate::Amount::from_sat(1092)
+ Some(Amount::from_sat_u32(1092))
);
}
#[test]
-fn test_script_get_sigop_count() {
+fn script_get_sigop_count() {
assert_eq!(
Builder::new()
.push_opcode(OP_DUP)
@@ -786,7 +783,7 @@ fn test_script_get_sigop_count() {
#[test]
#[cfg(feature = "serde")]
-fn test_script_serde_human_and_not() {
+fn script_serde_human_and_not() {
let script = ScriptBuf::from(vec![0u8, 1u8, 2u8]);
// Serialize
@@ -801,7 +798,7 @@ fn test_script_serde_human_and_not() {
}
#[test]
-fn test_instructions_are_fused() {
+fn instructions_are_fused() {
let script = ScriptBuf::new();
let mut instructions = script.instructions();
assert!(instructions.next().is_none());
diff --git a/bitcoin/src/blockdata/script/witness_program.rs b/bitcoin/src/blockdata/script/witness_program.rs
index 3b0cd4cad4..e0fe7bc60e 100644
--- a/bitcoin/src/blockdata/script/witness_program.rs
+++ b/bitcoin/src/blockdata/script/witness_program.rs
@@ -7,6 +7,7 @@
//!
//! [BIP141]:
+use core::convert::Infallible;
use core::fmt;
use internals::array_vec::ArrayVec;
@@ -24,6 +25,9 @@ pub const MIN_SIZE: usize = 2;
/// The maximum byte size of a segregated witness program.
pub const MAX_SIZE: usize = 40;
+/// The P2A program which is given by 0x4e73.
+pub(crate) const P2A_PROGRAM: [u8; 2] = [78, 115];
+
/// The segregated witness program.
///
/// The segregated witness program is technically only the program bytes _excluding_ the witness
@@ -31,7 +35,7 @@ pub const MAX_SIZE: usize = 40;
/// number, therefore we carry the version number around along with the program bytes.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WitnessProgram {
- /// The segwit version associated with this witness program.
+ /// The SegWit version associated with this witness program.
version: WitnessVersion,
/// The witness program (between 2 and 40 bytes).
program: ArrayVec,
@@ -47,7 +51,7 @@ impl WitnessProgram {
return Err(InvalidLength(program_len));
}
- // Specific segwit v0 check. These addresses can never spend funds sent to them.
+ // Specific SegWit v0 check. These addresses can never spend funds sent to them.
if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
return Err(InvalidSegwitV0Length(program_len));
}
@@ -104,6 +108,11 @@ impl WitnessProgram {
WitnessProgram::new_p2tr(pubkey)
}
+ /// Constructs a new pay to anchor address
+ pub const fn p2a() -> Self {
+ WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&P2A_PROGRAM) }
+ }
+
/// Returns the witness program version.
pub fn version(&self) -> WitnessVersion { self.version }
@@ -127,6 +136,11 @@ impl WitnessProgram {
/// Returns true if this witness program is for a P2TR output.
pub fn is_p2tr(&self) -> bool { self.version == WitnessVersion::V1 && self.program.len() == 32 }
+
+ /// Returns true if this is a pay to anchor output.
+ pub fn is_p2a(&self) -> bool {
+ self.version == WitnessVersion::V1 && self.program == P2A_PROGRAM
+ }
}
/// Witness program error.
@@ -139,7 +153,9 @@ pub enum Error {
InvalidSegwitV0Length(usize),
}
-internals::impl_from_infallible!(Error);
+impl From for Error {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/bitcoin/src/blockdata/script/witness_version.rs b/bitcoin/src/blockdata/script/witness_version.rs
index e9a1126d33..3c8f408dbc 100644
--- a/bitcoin/src/blockdata/script/witness_version.rs
+++ b/bitcoin/src/blockdata/script/witness_version.rs
@@ -7,6 +7,7 @@
//!
//! [BIP141]:
+use core::convert::Infallible;
use core::fmt;
use core::str::FromStr;
@@ -81,7 +82,7 @@ impl FromStr for WitnessVersion {
type Err = FromStrError;
fn from_str(s: &str) -> Result {
- let version: u8 = parse::int(s)?;
+ let version: u8 = parse::int_from_str(s)?;
Ok(WitnessVersion::try_from(version)?)
}
}
@@ -159,7 +160,9 @@ pub enum FromStrError {
Invalid(TryFromError),
}
-internals::impl_from_infallible!(FromStrError);
+impl From for FromStrError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for FromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -202,7 +205,9 @@ pub enum TryFromInstructionError {
DataPush,
}
-internals::impl_from_infallible!(TryFromInstructionError);
+impl From for TryFromInstructionError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
impl fmt::Display for TryFromInstructionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 0dd69f1c7c..0c60d3c654 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -115,8 +115,8 @@ crate::internal_macros::define_extension_trait! {
Weight::from_non_witness_data_size(self.base_size().to_u64())
}
- /// The weight of the TxIn when it's included in a segwit transaction (i.e., a transaction
- /// having at least one segwit input).
+ /// The weight of the TxIn when it's included in a SegWit transaction (i.e., a transaction
+ /// having at least one SegWit input).
///
/// This always takes into account the witness, even when empty, in which
/// case 1WU for the witness length varint (`00`) is included.
@@ -125,7 +125,7 @@ crate::internal_macros::define_extension_trait! {
/// might increase more than `TxIn::segwit_weight`. This happens when:
/// - the new input added causes the input length `VarInt` to increase its encoding length
/// - the new input is the first segwit input added - this will add an additional 2WU to the
- /// transaction weight to take into account the segwit marker
+ /// transaction weight to take into account the SegWit marker
fn segwit_weight(&self) -> Weight {
Weight::from_non_witness_data_size(self.base_size().to_u64())
+ Weight::from_witness_data_size(self.witness.size().to_u64())
@@ -162,7 +162,7 @@ crate::internal_macros::define_extension_trait! {
/// # Panics
///
/// If output size * 4 overflows, this should never happen under normal conditions. Use
- /// `Weght::from_vb_checked(self.size().to_u64())` if you are concerned.
+ /// `Weght::from_vb_checked(self.size() as u64)` if you are concerned.
fn weight(&self) -> Weight {
// Size is equivalent to virtual size since all bytes of a TxOut are non-witness bytes.
Weight::from_vb(self.size().to_u64())
@@ -183,8 +183,8 @@ crate::internal_macros::define_extension_trait! {
/// To use a custom value, use [`minimal_non_dust_custom`].
///
/// [`minimal_non_dust_custom`]: TxOut::minimal_non_dust_custom
- fn minimal_non_dust(script_pubkey: ScriptBuf) -> Self {
- TxOut { value: script_pubkey.minimal_non_dust(), script_pubkey }
+ fn minimal_non_dust(script_pubkey: ScriptBuf) -> Option {
+ Some(TxOut { value: script_pubkey.minimal_non_dust()?, script_pubkey })
}
/// Constructs a new `TxOut` with given script and the smallest possible `value` that is **not** dust
@@ -198,8 +198,8 @@ crate::internal_macros::define_extension_trait! {
/// To use the default Bitcoin Core value, use [`minimal_non_dust`].
///
/// [`minimal_non_dust`]: TxOut::minimal_non_dust
- fn minimal_non_dust_custom(script_pubkey: ScriptBuf, dust_relay_fee: FeeRate) -> Self {
- TxOut { value: script_pubkey.minimal_non_dust_custom(dust_relay_fee), script_pubkey }
+ fn minimal_non_dust_custom(script_pubkey: ScriptBuf, dust_relay_fee: FeeRate) -> Option {
+ Some(TxOut { value: script_pubkey.minimal_non_dust_custom(dust_relay_fee)?, script_pubkey })
}
}
}
@@ -213,23 +213,14 @@ fn size_from_script_pubkey(script_pubkey: &Script) -> usize {
/// Extension functionality for the [`Transaction`] type.
pub trait TransactionExt: sealed::Sealed {
/// Computes a "normalized TXID" which does not include any signatures.
- ///
- /// This method is deprecated. `ntxid` has been renamed to `compute_ntxid` to note that it's
- /// computationally expensive. Use `compute_ntxid` instead.
#[deprecated(since = "0.31.0", note = "use `compute_ntxid()` instead")]
fn ntxid(&self) -> sha256d::Hash;
/// Computes the [`Txid`].
- ///
- /// This method is deprecated. `txid` has been renamed to `compute_txid` to note that it's
- /// computationally expensive. Use `compute_txid` instead.
#[deprecated(since = "0.31.0", note = "use `compute_txid()` instead")]
fn txid(&self) -> Txid;
- /// Computes the segwit version of the transaction id.
- ///
- /// This method is deprecated. `wtxid` has been renamed to `compute_wtxid` to note that it's
- /// computationally expensive. Use `compute_wtxid` instead.
+ /// Computes the SegWit version of the transaction id.
#[deprecated(since = "0.31.0", note = "use `compute_wtxid()` instead")]
fn wtxid(&self) -> Wtxid;
@@ -243,13 +234,13 @@ pub trait TransactionExt: sealed::Sealed {
/// multiplied by three plus the with-witness consensus-serialized size.
///
/// For transactions with no inputs, this function will return a value 2 less than the actual
- /// weight of the serialized transaction. The reason is that zero-input transactions, post-segwit,
+ /// weight of the serialized transaction. The reason is that zero-input transactions, post-SegWit,
/// cannot be unambiguously serialized; we make a choice that adds two extra bytes. For more
/// details see [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
- /// which uses a "input count" of `0x00` as a `marker` for a Segwit-encoded transaction.
+ /// which uses a "input count" of `0x00` as a `marker` for a SegWit-encoded transaction.
///
/// If you need to use 0-input transactions, we strongly recommend you do so using the PSBT
- /// API. The unsigned transaction encoded within PSBT is always a non-segwit transaction
+ /// API. The unsigned transaction encoded within PSBT is always a non-SegWit transaction
/// and can therefore avoid this ambiguity.
fn weight(&self) -> Weight;
@@ -274,7 +265,7 @@ pub trait TransactionExt: sealed::Sealed {
/// > Virtual transaction size is defined as Transaction weight / 4 (rounded up to the next integer).
///
/// [`BIP141`]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
- /// [`policy`]: ../../policy/index.html
+ /// [`policy`]: crate::policy
fn vsize(&self) -> usize;
/// Checks if this is a coinbase transaction.
@@ -329,7 +320,7 @@ pub trait TransactionExt: sealed::Sealed {
/// The `spent` parameter is a closure/function that looks up the output being spent by each input
/// It takes in an [`OutPoint`] and returns a [`TxOut`]. If you can't provide this, a placeholder of
/// `|_| None` can be used. Without access to the previous [`TxOut`], any sigops in a redeemScript (P2SH)
- /// as well as any segwit sigops will not be counted for that input.
+ /// as well as any SegWit sigops will not be counted for that input.
fn total_sigop_cost(&self, spent: S) -> usize
where
S: FnMut(&OutPoint) -> Option;
@@ -352,7 +343,7 @@ impl TransactionExt for Transaction {
fn weight(&self) -> Weight {
// This is the exact definition of a weight unit, as defined by BIP-141 (quote above).
let wu = self.base_size() * 3 + self.total_size();
- Weight::from_wu_usize(wu)
+ Weight::from_wu(wu.to_u64())
}
fn base_size(&self) -> usize {
@@ -423,7 +414,7 @@ impl TransactionExt for Transaction {
// coinbase tx is correctly handled because `spent` will always returns None.
cost = cost.saturating_add(self.count_p2sh_sigops(&mut spent).saturating_mul(4));
- cost.saturating_add(self.count_witness_sigops(&mut spent))
+ cost.saturating_add(self.count_witness_sigops(spent))
}
#[inline]
@@ -442,7 +433,7 @@ impl TransactionExt for Transaction {
}
/// Iterates over transaction outputs and for each output yields the length of the scriptPubkey.
-// This exists to hardcode the type of the closure crated by `map`.
+// This exists to hardcode the type of the closure created by `map`.
pub struct TxOutToScriptPubkeyLengthIter<'a> {
inner: core::slice::Iter<'a, TxOut>,
}
@@ -461,13 +452,13 @@ trait TransactionExtPriv {
/// `count_p2sh_sigops` and `count_witness_sigops` respectively).
fn count_p2pk_p2pkh_sigops(&self) -> usize;
- /// Does not include wrapped segwit (see `count_witness_sigops`).
- fn count_p2sh_sigops(&self, spent: &mut S) -> usize
+ /// Does not include wrapped SegWit (see `count_witness_sigops`).
+ fn count_p2sh_sigops(&self, spent: S) -> usize
where
S: FnMut(&OutPoint) -> Option;
- /// Includes wrapped segwit (returns 0 for Taproot spends).
- fn count_witness_sigops(&self, spent: &mut S) -> usize
+ /// Includes wrapped SegWit (returns 0 for Taproot spends).
+ fn count_witness_sigops(&self, spent: S) -> usize
where
S: FnMut(&OutPoint) -> Option;
@@ -480,7 +471,7 @@ impl TransactionExtPriv for Transaction {
fn count_p2pk_p2pkh_sigops(&self) -> usize {
let mut count: usize = 0;
for input in &self.input {
- // 0 for p2wpkh, p2wsh, and p2sh (including wrapped segwit).
+ // 0 for p2wpkh, p2wsh, and p2sh (including wrapped SegWit).
count = count.saturating_add(input.script_sig.count_sigops_legacy());
}
for output in &self.output {
@@ -489,8 +480,8 @@ impl TransactionExtPriv for Transaction {
count
}
- /// Does not include wrapped segwit (see `count_witness_sigops`).
- fn count_p2sh_sigops(&self, spent: &mut S) -> usize
+ /// Does not include wrapped SegWit (see `count_witness_sigops`).
+ fn count_p2sh_sigops(&self, mut spent: S) -> usize
where
S: FnMut(&OutPoint) -> Option,
{
@@ -514,8 +505,8 @@ impl TransactionExtPriv for Transaction {
count
}
- /// Includes wrapped segwit (returns 0 for Taproot spends).
- fn count_witness_sigops(&self, spent: &mut S) -> usize
+ /// Includes wrapped SegWit (returns 0 for Taproot spends).
+ fn count_witness_sigops(&self, mut spent: S) -> usize
where
S: FnMut(&OutPoint) -> Option,
{
@@ -636,26 +627,15 @@ impl std::error::Error for IndexOutOfBoundsError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
-crate::internal_macros::define_extension_trait! {
- /// Extension functionality for the [`Version`] type.
- pub trait VersionExt impl for Version {
- /// Constructs a new non-standard transaction version.
- fn non_standard(version: i32) -> Version { Self(version) }
-
- /// Returns true if this transaction version number is considered standard.
- fn is_standard(&self) -> bool { *self == Version::ONE || *self == Version::TWO || *self == Version::THREE }
- }
-}
-
impl Encodable for Version {
fn consensus_encode(&self, w: &mut W) -> Result {
- self.0.consensus_encode(w)
+ self.to_u32().consensus_encode(w)
}
}
impl Decodable for Version {
fn consensus_decode(r: &mut R) -> Result {
- Decodable::consensus_decode(r).map(Version)
+ Decodable::consensus_decode(r).map(Version::maybe_non_standard)
}
}
@@ -721,7 +701,7 @@ impl Encodable for Transaction {
len += self.input.consensus_encode(w)?;
len += self.output.consensus_encode(w)?;
} else {
- // BIP-141 (segwit) transaction serialization also includes marker, flag, and witness data.
+ // BIP-141 (SegWit) transaction serialization also includes marker, flag, and witness data.
len += SEGWIT_MARKER.consensus_encode(w)?;
len += SEGWIT_FLAG.consensus_encode(w)?;
len += self.input.consensus_encode(w)?;
@@ -741,7 +721,7 @@ impl Decodable for Transaction {
) -> Result {
let version = Version::consensus_decode_from_finite_reader(r)?;
let input = Vec::::consensus_decode_from_finite_reader(r)?;
- // segwit
+ // SegWit
if input.is_empty() {
let segwit_flag = u8::consensus_decode_from_finite_reader(r)?;
match segwit_flag {
@@ -768,7 +748,7 @@ impl Decodable for Transaction {
// We don't support anything else
x => Err(encode::ParseError::UnsupportedSegwitFlag(x).into()),
}
- // non-segwit
+ // non-SegWit
} else {
Ok(Transaction {
version,
@@ -782,11 +762,11 @@ impl Decodable for Transaction {
/// Computes the value of an output accounting for the cost of spending it.
///
-/// The effective value is the value of an output value minus the amount to spend it. That is, the
+/// The effective value is the value of an output value minus the amount to spend it. That is, the
/// effective_value can be calculated as: value - (fee_rate * weight).
///
/// Note: the effective value of a [`Transaction`] may increase less than the effective value of
-/// a [`TxOut`] when adding another [`TxOut`] to the transaction. This happens when the new
+/// a [`TxOut`] when adding another [`TxOut`] to the transaction. This happens when the new
/// [`TxOut`] added causes the output length `VarInt` to increase its encoding length.
///
/// # Parameters
@@ -799,8 +779,8 @@ pub fn effective_value(
value: Amount,
) -> Option {
let weight = satisfaction_weight.checked_add(TX_IN_BASE_WEIGHT)?;
- let signed_input_fee = fee_rate.checked_mul_by_weight(weight)?.to_signed().ok()?;
- value.to_signed().ok()?.checked_sub(signed_input_fee)
+ let signed_input_fee = fee_rate.to_fee(weight)?.to_signed();
+ value.to_signed().checked_sub(signed_input_fee)
}
/// Predicts the weight of a to-be-constructed transaction.
@@ -851,38 +831,23 @@ where
I: IntoIterator- ,
O: IntoIterator
- ,
{
- // This fold() does three things:
- // 1) Counts the inputs and returns the sum as `input_count`.
- // 2) Sums all of the input weights and returns the sum as `partial_input_weight`
- // For every input: script_size * 4 + witness_size
- // Since script_size is non-witness data, it gets a 4x multiplier.
- // 3) Counts the number of inputs that have a witness data and returns the count as
- // `num_inputs_with_witnesses`.
- let (input_count, partial_input_weight, inputs_with_witnesses) = inputs.into_iter().fold(
- (0, 0, 0),
- |(count, partial_input_weight, inputs_with_witnesses), prediction| {
+ let (input_count, input_weight, inputs_with_witnesses) =
+ inputs.into_iter().fold((0, 0, 0), |(count, weight, with_witnesses), prediction| {
(
count + 1,
- partial_input_weight + prediction.weight().to_wu() as usize,
- inputs_with_witnesses + (prediction.witness_size > 0) as usize,
+ weight + prediction.total_weight().to_wu() as usize,
+ with_witnesses + (prediction.witness_size > 0) as usize,
)
- },
- );
-
- // This fold() does two things:
- // 1) Counts the outputs and returns the sum as `output_count`.
- // 2) Sums the output script sizes and returns the sum as `output_scripts_size`.
- // script_len + the length of a VarInt struct that stores the value of script_len
- let (output_count, output_scripts_size) = output_script_lens.into_iter().fold(
- (0, 0),
- |(output_count, total_scripts_size), script_len| {
- let script_size = script_len + compact_size::encoded_size(script_len);
- (output_count + 1, total_scripts_size + script_size)
- },
- );
+ });
+
+ let (output_count, output_scripts_size) =
+ output_script_lens.into_iter().fold((0, 0), |(count, scripts_size), script_len| {
+ (count + 1, scripts_size + script_len + compact_size::encoded_size(script_len))
+ });
+
predict_weight_internal(
input_count,
- partial_input_weight,
+ input_weight,
inputs_with_witnesses,
output_count,
output_scripts_size,
@@ -891,26 +856,18 @@ where
const fn predict_weight_internal(
input_count: usize,
- partial_input_weight: usize,
+ input_weight: usize,
inputs_with_witnesses: usize,
output_count: usize,
output_scripts_size: usize,
) -> Weight {
- // Lengths of txid, index and sequence: (32, 4, 4).
- // Multiply the lengths by 4 since the fields are all non-witness fields.
- let input_weight = partial_input_weight + input_count * 4 * (32 + 4 + 4);
-
// The value field of a TxOut is 8 bytes.
let output_size = 8 * output_count + output_scripts_size;
- let non_input_size =
- // version:
- 4 +
- // count varints:
- compact_size::encoded_size_const(input_count as u64) +
- compact_size::encoded_size_const(output_count as u64) +
- output_size +
- // lock_time
- 4;
+ let non_input_size = 4 // version
+ + compact_size::encoded_size_const(input_count as u64) // Can't use ToU64 in const context.
+ + compact_size::encoded_size_const(output_count as u64)
+ + output_size
+ + 4; // locktime
let weight = if inputs_with_witnesses == 0 {
non_input_size * 4 + input_weight
} else {
@@ -930,14 +887,14 @@ pub const fn predict_weight_from_slices(
inputs: &[InputWeightPrediction],
output_script_lens: &[usize],
) -> Weight {
- let mut partial_input_weight = 0;
+ let mut input_weight = 0;
let mut inputs_with_witnesses = 0;
// for loops not supported in const fn
let mut i = 0;
while i < inputs.len() {
let prediction = inputs[i];
- partial_input_weight += prediction.weight().to_wu() as usize;
+ input_weight += prediction.total_weight().to_wu() as usize;
inputs_with_witnesses += (prediction.witness_size > 0) as usize;
i += 1;
}
@@ -953,7 +910,7 @@ pub const fn predict_weight_from_slices(
predict_weight_internal(
inputs.len(),
- partial_input_weight,
+ input_weight,
inputs_with_witnesses,
output_script_lens.len(),
output_scripts_size,
@@ -1042,7 +999,7 @@ impl InputWeightPrediction {
///
/// # Panics
///
- /// The funcion panics in const context and debug builds if `bytes_to_grind` is higher than 62.
+ /// The function panics in const context and debug builds if `bytes_to_grind` is higher than 62.
///
/// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
pub const fn ground_p2wpkh(bytes_to_grind: usize) -> Self {
@@ -1062,7 +1019,7 @@ impl InputWeightPrediction {
///
/// # Panics
///
- /// The funcion panics in const context and debug builds if `bytes_to_grind` is higher than 62.
+ /// The function panics in const context and debug builds if `bytes_to_grind` is higher than 62.
///
/// [nested P2WPKH]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh-nested-in-bip16-p2sh
/// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
@@ -1083,7 +1040,7 @@ impl InputWeightPrediction {
///
/// # Panics
///
- /// The funcion panics in const context and debug builds if `bytes_to_grind` is higher than 62.
+ /// The function panics in const context and debug builds if `bytes_to_grind` is higher than 62.
///
/// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
pub const fn ground_p2pkh_compressed(bytes_to_grind: usize) -> Self {
@@ -1142,8 +1099,28 @@ impl InputWeightPrediction {
/// Computes the **signature weight** added to a transaction by an input with this weight prediction,
/// not counting the prevout (txid, index), sequence, potential witness flag bytes or the witness count varint.
- pub const fn weight(&self) -> Weight {
- Weight::from_wu_usize(self.script_size * 4 + self.witness_size)
+ #[deprecated(since = "TBD", note = "use `InputWeightPrediction::witness_weight()` instead")]
+ pub const fn weight(&self) -> Weight { Self::witness_weight(self) }
+
+ /// Computes the signature, prevout (txid, index), and sequence weights of this weight
+ /// prediction.
+ ///
+ /// See also [`InputWeightPrediction::witness_weight`]
+ pub const fn total_weight(&self) -> Weight {
+ // `impl const Trait` is currently unavailable: rust/issues/67792
+ // Convert to u64s because we can't use `Add` in const context.
+ let weight = TX_IN_BASE_WEIGHT.to_wu() + Self::witness_weight(self).to_wu();
+ Weight::from_wu(weight)
+ }
+
+ /// Computes the **signature weight** added to a transaction by an input with this weight prediction,
+ /// not counting the prevout (txid, index), sequence, potential witness flag bytes or the witness count varint.
+ ///
+ /// See also [`InputWeightPrediction::total_weight`]
+ pub const fn witness_weight(&self) -> Weight {
+ let wu = self.script_size * 4 + self.witness_size;
+ let wu = wu as u64; // Can't use `ToU64` in const context.
+ Weight::from_wu(wu)
}
}
@@ -1226,7 +1203,7 @@ mod tests {
assert_eq!(
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:lol"
.parse::(),
- Err(ParseOutPointError::Vout(parse::int::("lol").unwrap_err()))
+ Err(ParseOutPointError::Vout(parse::int_from_str::("lol").unwrap_err()))
);
assert_eq!(
@@ -1381,17 +1358,11 @@ mod tests {
#[test]
fn transaction_version() {
- let tx_bytes = hex!("ffffff7f0100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000");
+ let tx_bytes = hex!("ffffffff0100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000");
let tx: Result = deserialize(&tx_bytes);
assert!(tx.is_ok());
let realtx = tx.unwrap();
- assert_eq!(realtx.version, Version::non_standard(2147483647));
-
- let tx2_bytes = hex!("000000800100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000");
- let tx2: Result = deserialize(&tx2_bytes);
- assert!(tx2.is_ok());
- let realtx2 = tx2.unwrap();
- assert_eq!(realtx2.version, Version::non_standard(-2147483648));
+ assert_eq!(realtx.version, Version::maybe_non_standard(u32::MAX));
}
#[test]
@@ -1428,7 +1399,7 @@ mod tests {
#[test]
fn txid() {
- // segwit tx from Liquid integration tests, txid/hash from Core decoderawtransaction
+ // SegWit tx from Liquid integration tests, txid/hash from Core decoderawtransaction
let tx_bytes = hex!(
"01000000000102ff34f95a672bb6a4f6ff4a7e90fa8c7b3be7e70ffc39bc99be3bda67942e836c00000000\
23220020cde476664d3fa347b8d54ef3aee33dcb686a65ced2b5207cbf4ec5eda6b9b46e4f414d4c934ad8\
@@ -1472,7 +1443,7 @@ mod tests {
assert_eq!(format!("{:.10x}", tx.compute_txid()), "9652aa62b0");
assert_eq!(tx.weight(), Weight::from_wu(2718));
- // non-segwit tx from my mempool
+ // non-SegWit tx from my mempool
let tx_bytes = hex!(
"01000000010c7196428403d8b0c88fcb3ee8d64f56f55c8973c9ab7dd106bb4f3527f5888d000000006a47\
30440220503a696f55f2c00eee2ac5e65b17767cd88ed04866b5637d3c1d5d996a70656d02202c9aff698f\
@@ -1563,7 +1534,7 @@ mod tests {
use crate::consensus_validation::{TransactionExt as _, TxVerifyError};
use crate::witness::Witness;
- // a random recent segwit transaction from blockchain using both old and segwit inputs
+ // a random recent SegWit transaction from blockchain using both old and SegWit inputs
let mut spending: Transaction = deserialize(hex!("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")
.as_slice()).unwrap();
let spent1: Transaction = deserialize(hex!("020000000001040aacd2c49f5f3c0968cfa8caf9d5761436d95385252e3abb4de8f5dcf8a582f20000000017160014bcadb2baea98af0d9a902e53a7e9adff43b191e9feffffff96cd3c93cac3db114aafe753122bd7d1afa5aa4155ae04b3256344ecca69d72001000000171600141d9984579ceb5c67ebfbfb47124f056662fe7adbfeffffffc878dd74d3a44072eae6178bb94b9253177db1a5aaa6d068eb0e4db7631762e20000000017160014df2a48cdc53dae1aba7aa71cb1f9de089d75aac3feffffffe49f99275bc8363f5f593f4eec371c51f62c34ff11cc6d8d778787d340d6896c0100000017160014229b3b297a0587e03375ab4174ef56eeb0968735feffffff03360d0f00000000001976a9149f44b06f6ee92ddbc4686f71afe528c09727a5c788ac24281b00000000001976a9140277b4f68ff20307a2a9f9b4487a38b501eb955888ac227c0000000000001976a9148020cd422f55eef8747a9d418f5441030f7c9c7788ac0247304402204aa3bd9682f9a8e101505f6358aacd1749ecf53a62b8370b97d59243b3d6984f02200384ad449870b0e6e89c92505880411285ecd41cf11e7439b973f13bad97e53901210205b392ffcb83124b1c7ce6dd594688198ef600d34500a7f3552d67947bbe392802473044022033dfd8d190a4ae36b9f60999b217c775b96eb10dee3a1ff50fb6a75325719106022005872e4e36d194e49ced2ebcf8bb9d843d842e7b7e0eb042f4028396088d292f012103c9d7cbf369410b090480de2aa15c6c73d91b9ffa7d88b90724614b70be41e98e0247304402207d952de9e59e4684efed069797e3e2d993e9f98ec8a9ccd599de43005fe3f713022076d190cc93d9513fc061b1ba565afac574e02027c9efbfa1d7b71ab8dbb21e0501210313ad44bc030cc6cb111798c2bf3d2139418d751c1e79ec4e837ce360cc03b97a024730440220029e75edb5e9413eb98d684d62a077b17fa5b7cc19349c1e8cc6c4733b7b7452022048d4b9cae594f03741029ff841e35996ef233701c1ea9aa55c301362ea2e2f68012103590657108a72feb8dc1dec022cf6a230bb23dc7aaa52f4032384853b9f8388baf9d20700")
@@ -1685,7 +1656,7 @@ mod tests {
// 10 sat/kwu * (204wu + BASE_WEIGHT) = 4 sats
let expected_fee = "4 sats".parse::().unwrap();
- let expected_effective_value = value.to_signed().unwrap() - expected_fee;
+ let expected_effective_value = (value.to_signed() - expected_fee).unwrap();
assert_eq!(effective_value, expected_effective_value);
}
@@ -1701,25 +1672,19 @@ mod tests {
assert!(eff_value.is_none());
}
- #[test]
- fn effective_value_value_does_not_overflow() {
- let eff_value = effective_value(FeeRate::ZERO, Weight::ZERO, Amount::MAX);
- assert!(eff_value.is_none());
- }
-
#[test]
fn txin_txout_weight() {
// [(is_segwit, tx_hex, expected_weight)]
let txs = [
- // one segwit input (P2WPKH)
+ // one SegWit input (P2WPKH)
(true, "020000000001018a763b78d3e17acea0625bf9e52b0dc1beb2241b2502185348ba8ff4a253176e0100000000ffffffff0280d725000000000017a914c07ed639bd46bf7087f2ae1dfde63b815a5f8b488767fda20300000000160014869ec8520fa2801c8a01bfdd2e82b19833cd0daf02473044022016243edad96b18c78b545325aaff80131689f681079fb107a67018cb7fb7830e02205520dae761d89728f73f1a7182157f6b5aecf653525855adb7ccb998c8e6143b012103b9489bde92afbcfa85129a82ffa512897105d1a27ad9806bded27e0532fc84e700000000", Weight::from_wu(565)),
- // one segwit input (P2WSH)
+ // one SegWit input (P2WSH)
(true, "01000000000101a3ccad197118a2d4975fadc47b90eacfdeaf8268adfdf10ed3b4c3b7e1ad14530300000000ffffffff0200cc5501000000001976a91428ec6f21f4727bff84bb844e9697366feeb69f4d88aca2a5100d00000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220548f11130353b3a8f943d2f14260345fc7c20bde91704c9f1cbb5456355078cd0220383ed4ed39b079b618bcb279bbc1f2ca18cb028c4641cb522c9c5868c52a0dc20147304402203c332ecccb3181ca82c0600520ee51fee80d3b4a6ab110945e59475ec71e44ac0220679a11f3ca9993b04ccebda3c834876f353b065bb08f50076b25f5bb93c72ae1016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", Weight::from_wu(766)),
- // one segwit input (P2WPKH) and two legacy inputs (P2PKH)
+ // one SegWit input (P2WPKH) and two legacy inputs (P2PKH)
(true, "010000000001036b6b6ac7e34e97c53c1cc74c99c7948af2e6aac75d8778004ae458d813456764000000006a473044022001deec7d9075109306320b3754188f81a8236d0d232b44bc69f8309115638b8f02204e17a5194a519cf994d0afeea1268740bdc10616b031a521113681cc415e815c012103488d3272a9fad78ee887f0684cb8ebcfc06d0945e1401d002e590c7338b163feffffffffc75bd7aa6424aee972789ec28ba181254ee6d8311b058d165bd045154d7660b0000000006b483045022100c8641bcbee3e4c47a00417875015d8c5d5ea918fb7e96f18c6ffe51bc555b401022074e2c46f5b1109cd79e39a9aa203eadd1d75356415e51d80928a5fb5feb0efee0121033504b4c6dfc3a5daaf7c425aead4c2dbbe4e7387ce8e6be2648805939ecf7054ffffffff494df3b205cd9430a26f8e8c0dc0bb80496fbc555a524d6ea307724bc7e60eee0100000000ffffffff026d861500000000001976a9145c54ed1360072ebaf56e87693b88482d2c6a101588ace407000000000000160014761e31e2629c6e11936f2f9888179d60a5d4c1f900000247304402201fa38a67a63e58b67b6cfffd02f59121ca1c8a1b22e1efe2573ae7e4b4f06c2b022002b9b431b58f6e36b3334fb14eaecee7d2f06967a77ef50d8d5f90dda1057f0c01210257dc6ce3b1100903306f518ee8fa113d778e403f118c080b50ce079fba40e09a00000000", Weight::from_wu(1755)),
// three legacy inputs (P2PKH)
(false, "0100000003e4d7be4314204a239d8e00691128dca7927e19a7339c7948bde56f669d27d797010000006b483045022100b988a858e2982e2daaf0755b37ad46775d6132057934877a5badc91dee2f66ff022020b967c1a2f0916007662ec609987e951baafa6d4fda23faaad70715611d6a2501210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff9e22eb1b3f24c260187d716a8a6c2a7efb5af14a30a4792a6eeac3643172379c000000006a47304402207df07f0cd30dca2cf7bed7686fa78d8a37fe9c2254dfdca2befed54e06b779790220684417b8ff9f0f6b480546a9e90ecee86a625b3ea1e4ca29b080da6bd6c5f67e01210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff1123df3bfb503b59769731da103d4371bc029f57979ebce68067768b958091a1000000006a47304402207a016023c2b0c4db9a7d4f9232fcec2193c2f119a69125ad5bcedcba56dd525e02206a734b3a321286c896759ac98ebfd9d808df47f1ce1fbfbe949891cc3134294701210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff0200c2eb0b000000001976a914e5eb3e05efad136b1405f5c2f9adb14e15a35bb488ac88cfff1b000000001976a9144846db516db3130b7a3c92253599edec6bc9630b88ac00000000", Weight::from_wu(2080)),
- // one segwit input (P2TR)
+ // one SegWit input (P2TR)
(true, "01000000000101b5cee87f1a60915c38bb0bc26aaf2b67be2b890bbc54bb4be1e40272e0d2fe0b0000000000ffffffff025529000000000000225120106daad8a5cb2e6fc74783714273bad554a148ca2d054e7a19250e9935366f3033760000000000002200205e6d83c44f57484fd2ef2a62b6d36cdcd6b3e06b661e33fd65588a28ad0dbe060141df9d1bfce71f90d68bf9e9461910b3716466bfe035c7dbabaa7791383af6c7ef405a3a1f481488a91d33cd90b098d13cb904323a3e215523aceaa04e1bb35cdb0100000000", Weight::from_wu(617)),
// one legacy input (P2PKH)
(false, "0100000001c336895d9fa674f8b1e294fd006b1ac8266939161600e04788c515089991b50a030000006a47304402204213769e823984b31dcb7104f2c99279e74249eacd4246dabcf2575f85b365aa02200c3ee89c84344ae326b637101a92448664a8d39a009c8ad5d147c752cbe112970121028b1b44b4903c9103c07d5a23e3c7cf7aeb0ba45ddbd2cfdce469ab197381f195fdffffff040000000000000000536a4c5058325bb7b7251cf9e36cac35d691bd37431eeea426d42cbdecca4db20794f9a4030e6cb5211fabf887642bcad98c9994430facb712da8ae5e12c9ae5ff314127d33665000bb26c0067000bb0bf00322a50c300000000000017a9145ca04fdc0a6d2f4e3f67cfeb97e438bb6287725f8750c30000000000001976a91423086a767de0143523e818d4273ddfe6d9e4bbcc88acc8465003000000001976a914c95cbacc416f757c65c942f9b6b8a20038b9b12988ac00000000", Weight::from_wu(1396)),
@@ -1742,7 +1707,7 @@ mod tests {
+ tx.input.iter().fold(Weight::ZERO, |sum, i| sum + txin_weight(i))
+ tx.output.iter().fold(Weight::ZERO, |sum, o| sum + o.weight());
- // The empty tx uses segwit serialization but a legacy tx does not.
+ // The empty tx uses SegWit serialization but a legacy tx does not.
if !tx.uses_segwit_serialization() {
calculated_weight -= Weight::from_wu(2);
}
@@ -1941,19 +1906,34 @@ mod tests {
// Confirm signature grinding input weight predictions are aligned with constants.
assert_eq!(
- InputWeightPrediction::ground_p2wpkh(0).weight(),
- InputWeightPrediction::P2WPKH_MAX.weight()
+ InputWeightPrediction::ground_p2wpkh(0).witness_weight(),
+ InputWeightPrediction::P2WPKH_MAX.witness_weight()
);
assert_eq!(
- InputWeightPrediction::ground_nested_p2wpkh(0).weight(),
- InputWeightPrediction::NESTED_P2WPKH_MAX.weight()
+ InputWeightPrediction::ground_nested_p2wpkh(0).witness_weight(),
+ InputWeightPrediction::NESTED_P2WPKH_MAX.witness_weight()
);
assert_eq!(
- InputWeightPrediction::ground_p2pkh_compressed(0).weight(),
- InputWeightPrediction::P2PKH_COMPRESSED_MAX.weight()
+ InputWeightPrediction::ground_p2pkh_compressed(0).witness_weight(),
+ InputWeightPrediction::P2PKH_COMPRESSED_MAX.witness_weight()
);
}
+ #[test]
+ fn weight_prediction_const_from_slices() {
+ let predict = [
+ InputWeightPrediction::P2WPKH_MAX,
+ InputWeightPrediction::NESTED_P2WPKH_MAX,
+ InputWeightPrediction::P2PKH_COMPRESSED_MAX,
+ InputWeightPrediction::P2PKH_UNCOMPRESSED_MAX,
+ InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH,
+ InputWeightPrediction::P2TR_KEY_NON_DEFAULT_SIGHASH,
+ ];
+
+ let weight = predict_weight_from_slices(&predict, &[1]);
+ assert_eq!(weight, Weight::from_wu(2493));
+ }
+
#[test]
fn sequence_debug_output() {
let seq = Sequence::from_seconds_floor(1000);
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index 318baaf508..f8e8abe5ba 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -13,7 +13,10 @@ use crate::crypto::ecdsa;
use crate::prelude::Vec;
#[cfg(doc)]
use crate::script::ScriptExt as _;
-use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
+use crate::taproot::{
+ self, ControlBlock, LeafScript, LeafVersion, TAPROOT_ANNEX_PREFIX, TAPROOT_CONTROL_BASE_SIZE,
+ TAPROOT_LEAF_MASK, TaprootMerkleBranch,
+};
use crate::Script;
#[rustfmt::skip] // Keep public re-exports separate.
@@ -132,6 +135,15 @@ crate::internal_macros::define_extension_trait! {
witness
}
+ /// Finishes constructing the P2TR script spend witness by pushing the required items.
+ fn push_p2tr_script_spend(&mut self, script: &Script, control_block: &ControlBlock>, annex: Option<&[u8]>) {
+ self.push(script.as_bytes());
+ self.push(&*control_block.encode_to_arrayvec());
+ if let Some(annex) = annex {
+ self.push(annex);
+ }
+ }
+
/// Pushes, as a new element on the witness, an ECDSA signature.
///
/// Pushes the DER encoded signature + sighash_type, requires an allocation.
@@ -147,14 +159,28 @@ crate::internal_macros::define_extension_trait! {
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn tapscript(&self) -> Option<&Script> {
- if self.is_empty() {
- return None;
+ match P2TrSpend::from_witness(self) {
+ // Note: the method is named "tapscript" but historically it was actually returning
+ // leaf script. This is broken but we now keep the behavior the same to not subtly
+ // break someone.
+ Some(P2TrSpend::Script { leaf_script, .. }) => Some(leaf_script),
+ _ => None,
}
+ }
- if self.taproot_annex().is_some() {
- self.third_to_last().map(Script::from_bytes)
- } else {
- self.second_to_last().map(Script::from_bytes)
+ /// Returns the leaf script with its version but without the merkle proof.
+ ///
+ /// This does not guarantee that this represents a P2TR [`Witness`]. It
+ /// merely gets the second to last or third to last element depending on
+ /// the first byte of the last element being equal to 0x50 and the associated
+ /// version.
+ fn taproot_leaf_script(&self) -> Option> {
+ match P2TrSpend::from_witness(self) {
+ Some(P2TrSpend::Script { leaf_script, control_block, .. }) if control_block.len() >= TAPROOT_CONTROL_BASE_SIZE => {
+ let version = LeafVersion::from_consensus(control_block[0] & TAPROOT_LEAF_MASK).ok()?;
+ Some(LeafScript { version, script: leaf_script, })
+ },
+ _ => None,
}
}
@@ -166,14 +192,9 @@ crate::internal_macros::define_extension_trait! {
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn taproot_control_block(&self) -> Option<&[u8]> {
- if self.is_empty() {
- return None;
- }
-
- if self.taproot_annex().is_some() {
- self.second_to_last()
- } else {
- self.last()
+ match P2TrSpend::from_witness(self) {
+ Some(P2TrSpend::Script { control_block, .. }) => Some(control_block),
+ _ => None,
}
}
@@ -183,17 +204,7 @@ crate::internal_macros::define_extension_trait! {
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn taproot_annex(&self) -> Option<&[u8]> {
- self.last().and_then(|last| {
- // From BIP341:
- // If there are at least two witness elements, and the first byte of
- // the last element is 0x50, this last element is called annex a
- // and is removed from the witness stack.
- if self.len() >= 2 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
- Some(last)
- } else {
- None
- }
- })
+ P2TrSpend::from_witness(self)?.annex()
}
/// Get the p2wsh witness script following BIP141 rules.
@@ -206,6 +217,90 @@ crate::internal_macros::define_extension_trait! {
}
}
+/// Represents a possible Taproot spend.
+///
+/// Taproot can be spent as key spend or script spend and, depending on which it is, different data
+/// is in the witness. This type helps representing that data more cleanly when parsing the witness
+/// because there are a lot of conditions that make reasoning hard. It's better to parse it at one
+/// place and pass it along.
+///
+/// This type is so far private but it could be published eventually. The design is geared towards
+/// it but it's not fully finished.
+enum P2TrSpend<'a> {
+ Key {
+ // This field is technically present in witness in case of key spend but none of our code
+ // uses it yet. Rather than deleting it, it's kept here commented as documentation and as
+ // an easy way to add it if anything needs it - by just uncommenting.
+ // signature: &'a [u8],
+ annex: Option<&'a [u8]>,
+ },
+ Script {
+ leaf_script: &'a Script,
+ control_block: &'a [u8],
+ annex: Option<&'a [u8]>,
+ },
+}
+
+impl<'a> P2TrSpend<'a> {
+ /// Parses `Witness` to determine what kind of taproot spend this is.
+ ///
+ /// Note: this assumes `witness` is a taproot spend. The function cannot figure it out for sure
+ /// (without knowing the output), so it doesn't attempt to check anything other than what is
+ /// required for the program to not crash.
+ ///
+ /// In other words, if the caller is certain that the witness is a valid p2tr spend (e.g.
+ /// obtained from Bitcoin Core) then it's OK to unwrap this but not vice versa - `Some` does
+ /// not imply correctness.
+ fn from_witness(witness: &'a Witness) -> Option {
+ // BIP341 says:
+ // If there are at least two witness elements, and the first byte of
+ // the last element is 0x50, this last element is called annex a
+ // and is removed from the witness stack.
+ //
+ // However here we're not removing anything, so we have to adjust the numbers to account
+ // for the fact that annex is still there.
+ match witness.len() {
+ 0 => None,
+ 1 => Some(P2TrSpend::Key {
+ /* signature: witness.last().expect("len > 0") ,*/ annex: None,
+ }),
+ 2 if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => {
+ let spend = P2TrSpend::Key {
+ // signature: witness.get_back(1).expect("len > 1"),
+ annex: witness.last(),
+ };
+ Some(spend)
+ }
+ // 2 => this is script spend without annex - same as when there are 3+ elements and the
+ // last one does NOT start with TAPROOT_ANNEX_PREFIX. This is handled in the catchall
+ // arm.
+ 3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => {
+ let spend = P2TrSpend::Script {
+ leaf_script: Script::from_bytes(witness.get_back(2).expect("len > 2")),
+ control_block: witness.get_back(1).expect("len > 1"),
+ annex: witness.last(),
+ };
+ Some(spend)
+ }
+ _ => {
+ let spend = P2TrSpend::Script {
+ leaf_script: Script::from_bytes(witness.get_back(1).expect("len > 1")),
+ control_block: witness.last().expect("len > 0"),
+ annex: None,
+ };
+ Some(spend)
+ }
+ }
+ }
+
+ fn annex(&self) -> Option<&'a [u8]> {
+ match self {
+ P2TrSpend::Key { annex, .. } => *annex,
+ P2TrSpend::Script { annex, .. } => *annex,
+ }
+ }
+}
+
mod sealed {
pub trait Sealed {}
impl Sealed for super::Witness {}
@@ -246,7 +341,7 @@ mod test {
}
#[test]
- fn test_push_ecdsa_sig() {
+ fn push_ecdsa_sig() {
// The very first signature in block 734,958
let sig_bytes =
hex!("304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b541");
@@ -286,20 +381,14 @@ mod test {
}
#[test]
- fn test_get_tapscript() {
+ fn get_tapscript() {
let tapscript = hex!("deadbeef");
let control_block = hex!("02");
// annex starting with 0x50 causes the branching logic.
let annex = hex!("50");
- let witness_vec = vec![tapscript.clone(), control_block.clone()];
- let witness_vec_annex = vec![tapscript.clone(), control_block, annex];
-
- let witness_serialized: Vec = serialize(&witness_vec);
- let witness_serialized_annex: Vec = serialize(&witness_vec_annex);
-
- let witness = deserialize::(&witness_serialized[..]).unwrap();
- let witness_annex = deserialize::(&witness_serialized_annex[..]).unwrap();
+ let witness = Witness::from([&*tapscript, &control_block]);
+ let witness_annex = Witness::from([&*tapscript, &control_block, &annex]);
// With or without annex, the tapscript should be returned.
assert_eq!(witness.tapscript(), Some(Script::from_bytes(&tapscript[..])));
@@ -307,19 +396,32 @@ mod test {
}
#[test]
- fn test_get_tapscript_from_keypath() {
- let signature = hex!("deadbeef");
+ fn get_taproot_leaf_script() {
+ let tapscript = hex!("deadbeef");
+ let control_block =
+ hex!("c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// annex starting with 0x50 causes the branching logic.
let annex = hex!("50");
- let witness_vec = vec![signature.clone()];
- let witness_vec_annex = vec![signature.clone(), annex];
+ let witness = Witness::from([&*tapscript, &control_block]);
+ let witness_annex = Witness::from([&*tapscript, &control_block, &annex]);
+
+ let expected_leaf_script =
+ LeafScript { version: LeafVersion::TapScript, script: Script::from_bytes(&tapscript) };
+
+ // With or without annex, the tapscript should be returned.
+ assert_eq!(witness.taproot_leaf_script().unwrap(), expected_leaf_script);
+ assert_eq!(witness_annex.taproot_leaf_script().unwrap(), expected_leaf_script);
+ }
- let witness_serialized: Vec = serialize(&witness_vec);
- let witness_serialized_annex: Vec = serialize(&witness_vec_annex);
+ #[test]
+ fn get_tapscript_from_keypath() {
+ let signature = hex!("deadbeef");
+ // annex starting with 0x50 causes the branching logic.
+ let annex = hex!("50");
- let witness = deserialize::(&witness_serialized[..]).unwrap();
- let witness_annex = deserialize::(&witness_serialized_annex[..]).unwrap();
+ let witness = Witness::from([&*signature]);
+ let witness_annex = Witness::from([&*signature, &annex]);
// With or without annex, no tapscript should be returned.
assert_eq!(witness.tapscript(), None);
@@ -327,41 +429,32 @@ mod test {
}
#[test]
- fn test_get_control_block() {
+ fn get_control_block() {
let tapscript = hex!("deadbeef");
let control_block = hex!("02");
// annex starting with 0x50 causes the branching logic.
let annex = hex!("50");
+ let signature = vec![0xff; 64];
- let witness_vec = vec![tapscript.clone(), control_block.clone()];
- let witness_vec_annex = vec![tapscript.clone(), control_block.clone(), annex];
-
- let witness_serialized: Vec = serialize(&witness_vec);
- let witness_serialized_annex: Vec = serialize(&witness_vec_annex);
-
- let witness = deserialize::(&witness_serialized[..]).unwrap();
- let witness_annex = deserialize::(&witness_serialized_annex[..]).unwrap();
+ let witness = Witness::from([&*tapscript, &control_block]);
+ let witness_annex = Witness::from([&*tapscript, &control_block, &annex]);
+ let witness_key_spend_annex = Witness::from([&*signature, &annex]);
// With or without annex, the tapscript should be returned.
assert_eq!(witness.taproot_control_block(), Some(&control_block[..]));
assert_eq!(witness_annex.taproot_control_block(), Some(&control_block[..]));
+ assert!(witness_key_spend_annex.taproot_control_block().is_none())
}
#[test]
- fn test_get_annex() {
+ fn get_annex() {
let tapscript = hex!("deadbeef");
let control_block = hex!("02");
// annex starting with 0x50 causes the branching logic.
let annex = hex!("50");
- let witness_vec = vec![tapscript.clone(), control_block.clone()];
- let witness_vec_annex = vec![tapscript.clone(), control_block.clone(), annex.clone()];
-
- let witness_serialized: Vec = serialize(&witness_vec);
- let witness_serialized_annex: Vec = serialize(&witness_vec_annex);
-
- let witness = deserialize::(&witness_serialized[..]).unwrap();
- let witness_annex = deserialize::(&witness_serialized_annex[..]).unwrap();
+ let witness = Witness::from([&*tapscript, &control_block]);
+ let witness_annex = Witness::from([&*tapscript, &control_block, &annex]);
// With or without annex, the tapscript should be returned.
assert_eq!(witness.taproot_annex(), None);
@@ -372,14 +465,8 @@ mod test {
// annex starting with 0x50 causes the branching logic.
let annex = hex!("50");
- let witness_vec = vec![signature.clone()];
- let witness_vec_annex = vec![signature.clone(), annex.clone()];
-
- let witness_serialized: Vec = serialize(&witness_vec);
- let witness_serialized_annex: Vec = serialize(&witness_vec_annex);
-
- let witness = deserialize::