diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 97c7dedf..d309edc6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -github: [dr-orlovsky] +github: [lnp-bp, dr-orlovsky] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a91fee3..e784120e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,13 +10,26 @@ env: CARGO_TERM_COLOR: always jobs: + default: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Default build + uses: actions-rs/cargo@v1 + with: + command: check + args: --workspace features: runs-on: ubuntu-latest strategy: fail-fast: false matrix: feature: - - async - serde steps: - uses: actions/checkout@v2 @@ -40,33 +53,25 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, ubuntu-latest, macos-latest, windows-2019, windows-latest ] + os: [ ubuntu-20.04, ubuntu-22.04, macos-11, macos-12, windows-2019, windows-2022 ] steps: - uses: actions/checkout@v2 - - name: Install macos dependencies - if: startsWith(matrix.os, 'macos') - run: brew install pkg-config - name: Install rust stable uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - - name: Build with no features + - name: Build with all features uses: actions-rs/cargo@v1 with: command: check - args: --no-default-features - - name: Build with defaults - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features + args: --workspace --all-targets --all-features toolchains: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.59.0 ] + toolchain: [ nightly, beta, stable, 1.66.0 ] steps: - uses: actions/checkout@v2 - name: Install rust ${{ matrix.toolchain }} @@ -79,26 +84,3 @@ jobs: with: command: check args: --workspace --all-targets --all-features - dependency: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install latest stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - name: Create dependency - run: | - cargo new dep_test - cp test/depCargo.toml dep_test/Cargo.toml - cd dep_test - - name: Build dependency - uses: actions-rs/cargo@v1 - with: - command: check - args: --verbose - - name: Clean up - run: | - cd .. - rm -rf dep_test diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 4ba22c88..9fd96f0e 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -26,7 +26,7 @@ jobs: args: --workspace --all-features --no-fail-fast env: CARGO_INCREMENTAL: '0' - RUSTFLAGS: '--cfg codecov -Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off' + RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off' RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off' - id: coverage name: Generate coverage diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c37f2246..b8979f05 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -39,7 +39,7 @@ jobs: name: Clippy with: command: clippy - args: --workspace --all-features + args: --workspace --all-features --all-targets doc: runs-on: ubuntu-latest steps: @@ -51,7 +51,7 @@ jobs: override: true components: rust-docs - uses: actions-rs/cargo@v1 - name: Clippy + name: Doc with: command: doc args: --workspace --all-features diff --git a/.rustfmt.toml b/.rustfmt.toml index 58fbbb99..952a2846 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,14 +1,27 @@ -max_width = 80 +edition = "2021" +version = "Two" + +max_width = 100 +array_width = 100 +attr_fn_like_width = 100 +fn_call_width = 100 + format_code_in_doc_comments = true fn_single_line = true format_macro_matchers = true +format_macro_bodues = true format_strings = true merge_derives = false -imports_granularity = "Module" overflow_delimited_expr = true -group_imports = "StdExternalCrate" +reorder_modules = false use_field_init_shorthand = true use_try_shorthand = true wrap_comments = true -comment_width = 80 +where_single_line = true unstable_features = true +empty_item_single_line = true + +binop_separator = "Back" + +imports_granularity = "Module" +group_imports = "StdExternalCrate" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51d8aa01..66acff98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,12 @@ -Contributing to LNP/BP projects -=============================== +Contributing guidelines +======================= -:+1::tada: -First and foremost, thanks for taking the time to contribute! -:tada::+1: - -The following is a set of guidelines for contributing to [LNP/BP Standards -Association](https://lnp-bp.org) projects, which are hosted in the GitHub -organizations listed in [readme](https://github.com/LNP-BP#Working-groups). -These are mostly guidelines, not rules. Use your best judgment, and feel free to -propose changes to this document in a pull request. +Contributions are very welcome. When contributing code, please follow these +simple guidelines. #### Table Of Contents -- [General](#general) -- [Communication channels](#communication-channels) -- [Asking questions](#asking-questions) - [Contribution workflow](#contribution-workflow) + * [Proposing changes](#proposing-changes) * [Preparing PRs](#preparing-prs) * [Peer review](#peer-review) - [Coding conventions](#coding-conventions) @@ -23,41 +14,22 @@ propose changes to this document in a pull request. - [Testing](#testing) - [Going further](#going-further) +Overview +-------- -General -------- -The LNP/BP projects operate an open contributor model where anyone is welcome to -contribute towards development in the form of peer review, documentation, -testing and patches. - -Anyone is invited to contribute without regard to technical experience, -"expertise", OSS experience, age, or other concern. However, the development of -standards & reference implementations demands a high-level of rigor, adversarial -thinking, thorough testing and risk-minimization. Any bug may cost users real -money. That being said, we deeply welcome people contributing for the first time -to an open source project or pick up Rust while contributing. Don't be shy, -you'll learn. - -Communications Channels ------------------------ -Communication about LNP/BP standards & implementations happens primarily -on #lnp-pb IRC chat on Freenode with the logs available at - - -Discussion about code base improvements happens in GitHub issues and on pull -requests. - -Major projects are tracked [here](https://github.com/orgs/LNP-BP/projects). -Project roadmap is tracked in each repository GitHub milestones. +* Before adding any code dependencies, check with the maintainers if this is okay. +* Write properly formatted comments: they should be English sentences, eg: -Asking Questions ----------------- -> **Note:** Please don't file an issue to ask a question. Each repository - or -> GitHub organization has a "Discussions" with Q&A section; please post your -> questions there. You'll get faster results by using this channel. + // Return the current UNIX time. -Alternatively, we have a dedicated developer channel on IRC, #lnp-bp@libera.chat -where you may get helpful advice if you have questions. +* Read the DCO and make sure all commits are signed off, using `git commit -s`. +* Follow the guidelines when proposing code changes (see below). +* Write properly formatted git commits (see below). +* Run the tests with `cargo test --workspace --all-features`. +* Make sure you run `rustfmt` on your code (see below details). +* Please don't file an issue to ask a question. Each repository - or + GitHub organization has a "Discussions" with Q&A section; please post your + questions there. You'll get faster results by using this channel. Contribution Workflow --------------------- @@ -67,39 +39,49 @@ facilitates social contribution, easy testing and peer review. To contribute a patch, the workflow is a as follows: - 1. Fork Repository - 2. Create topic branch - 3. Commit patches +1. Fork Repository +2. Create topic branch +3. Commit patches -In general commits should be atomic and diffs should be easy to read. For this -reason do not mix any formatting fixes or code moves with actual code changes. -Further, each commit, individually, should compile and pass tests, in order to +In general commits should be atomic and diffs should be easy to read. For this +reason do not mix any formatting fixes or code moves with actual code changes. +Further, each commit, individually, should compile and pass tests, in order to ensure git bisect and other automated tools function properly. -When adding a new feature thought must be given to the long term technical debt. +When adding a new feature thought must be given to the long term technical debt. Every new features should be covered by unit tests. When refactoring, structure your PR to make it easy to review and don't hesitate to split it into multiple small, focused PRs. -The Minimal Supported Rust Version is nightly for the period of active -development; it is enforced by our Travis. Later we plan to fix to some specific -Rust version after the initial library release. - Commits should cover both the issue fixed and the solution's rationale. -These [guidelines](https://chris.beams.io/posts/git-commit/) should be kept in +These [guidelines](https://chris.beams.io/posts/git-commit/) should be kept in mind. -To facilitate communication with other contributors, the project is making use -of GitHub's "assignee" field. First check that no one is assigned and then -comment suggesting that you're working on it. If someone is already assigned, -don't hesitate to ask if the assigned party or previous commenters are still +To facilitate communication with other contributors, the project is making use +of GitHub's "assignee" field. First check that no one is assigned and then +comment suggesting that you're working on it. If someone is already assigned, +don't hesitate to ask if the assigned party or previous commenters are still working on it if it has been awhile. +### Proposing changes + +When proposing changes via a pull-request or patch: + +* Isolate changes in separate commits to make the review process easier. +* Don't make unrelated changes, unless it happens to be an obvious improvement to + code you are touching anyway ("boyscout rule"). +* Rebase on `master` when needed. +* Keep your changesets small, specific and uncontroversial, so that they can be + merged more quickly. +* If the change is substantial or requires re-architecting certain parts of the + codebase, write a proposal in English first, and get consensus on that before + proposing the code changes. + ### Preparing PRs -The main library development happens in the `master` branch. This branch must -always compile without errors (using Travis CI). All external contributions are +The main library development happens in the `master` branch. This branch must +always compile without errors (using Travis CI). All external contributions are made within PRs into this branch. Prerequisites that a PR must satisfy for merging into the `master` branch: @@ -122,20 +104,60 @@ Additionally, to the `master` branch some repositories may have `develop` branch for any experimental developments. This branch may not compile and should not be used by any projects depending on the library. +### Writing Git commit messages + +A properly formed git commit subject line should always be able to complete the +following sentence: + + If applied, this commit will _____ + +In addition, it should be capitalized and *must not* include a period. + +For example, the following message is well formed: + + Add support for .gif files + +While these ones are **not**: `Adding support for .gif files`, +`Added support for .gif files`. + +When it comes to formatting, here's a model git commit message[1]: + + Capitalized, short (50 chars or less) summary + + More detailed explanatory text, if necessary. Wrap it to about 72 + characters or so. In some contexts, the first line is treated as the + subject of an email and the rest of the text as the body. The blank + line separating the summary from the body is critical (unless you omit + the body entirely); tools like rebase can get confused if you run the + two together. + + Write your commit message in the imperative: "Fix bug" and not "Fixed bug" + or "Fixes bug." This convention matches up with commit messages generated + by commands like git merge and git revert. + + Further paragraphs come after blank lines. + + - Bullet points are okay, too. + + - Typically a hyphen or asterisk is used for the bullet, followed by a + single space, with blank lines in between, but conventions vary here. + + - Use a hanging indent. + ### Peer review Anyone may participate in peer review which is expressed by comments in the pull request. Typically reviewers will review the code for obvious errors, as well as test out the patch set and opine on the technical merits of the patch. PR should -be reviewed first on the conceptual level before focusing on code style or +be reviewed first on the conceptual level before focusing on code style or grammar fixes. Coding Conventions ------------------ -Our CI enforces [clippy's](https://github.com/rust-lang/rust-clippy) +Our CI enforces [clippy's](https://github.com/rust-lang/rust-clippy) [default linting](https://rust-lang.github.io/rust-clippy/rust-1.52.0/index.html) and [rustfmt](https://github.com/rust-lang/rustfmt) formatting defined by rules -in [.rustfmt.toml](./.rustfmt.toml). The linter should be run with current +in [.rustfmt.toml](./.rustfmt.toml). The linter should be run with current stable rust compiler, while formatter requires nightly version due to the use of unstable formatting parameters. @@ -150,35 +172,25 @@ cargo +nightly fmt --all Security -------- -Security is the primary focus of LNP/BP libraries; disclosure of security -vulnerabilities helps prevent user loss of funds. If you believe a vulnerability -may affect other implementations, please inform them. Guidelines for a -responsible disclosure can be found in [SECURITY.md](./SECURITY.md) file in the -project root. - -Note that some of LNP/BP projects are currently considered "pre-production". -Such projects can be distinguished by the absence of `SECURITY.md`. In such -cases there are no special handling of security issues; please simply open +Responsible disclosure of security vulnerabilities helps prevent user loss of +privacy. If you believe a vulnerability may affect other implementations, please +inform them. Guidelines for a responsible disclosure can be found in +[SECURITY.md](./SECURITY.md) file in the project root. + +Note that some of our projects are currently considered "pre-production". +Such projects can be distinguished by the absence of `SECURITY.md`. In such +cases there are no special handling of security issues; please simply open an issue on GitHub. -Testing -------- -Related to the security aspect, LNP/BP developers take testing very seriously. -Due to the modular nature of the project, writing new functional tests is easy -and good test coverage of the codebase is an important goal. - -Fuzzing is heavily encouraged: feel free to add related material under `fuzz/` - -Mutation testing is planned; any contribution there would be warmly welcomed. - Going further ------------- -You may be interested in Jon Atack guide on +You may be interested in Jon Atack guide on [How to review Bitcoin Core PRs][Review] and [How to make Bitcoin Core PRs][PR]. -While there are differences between the projects in terms of context and +While there are differences between the projects in terms of context and maturity, many of the suggestions offered apply to this project. Overall, have fun :) +[1]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [Review]: https://github.com/jonatack/bitcoin-development/blob/master/how-to-review-bitcoin-core-prs.md [PR]: https://github.com/jonatack/bitcoin-development/blob/master/how-to-make-bitcoin-core-prs.md diff --git a/Cargo.lock b/Cargo.lock index 6283a940..0b764199 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,27 +4,40 @@ version = 3 [[package]] name = "amplify" -version = "3.13.0" +version = "4.0.0-beta.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116019a174e912931d5b19ca7ab6a22596d12cdb1320358fad3368f0aba135a9" +checksum = "414f38fa6badd4440d87c4e3abb6abb2d99cce34cff3af50ad6ee83004f03716" dependencies = [ + "amplify_apfloat", "amplify_derive", "amplify_num", - "amplify_syn", + "amplify_syn 1.1.6", + "ascii", "serde", "serde_json", "serde_yaml", "stringly_conversions", "toml", + "wasm-bindgen", +] + +[[package]] +name = "amplify_apfloat" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a958199f6993a34e74b9d22b22709e5c6e527e901d7083b24f0a532445dbfc84" +dependencies = [ + "amplify_num", + "bitflags", ] [[package]] name = "amplify_derive" -version = "2.11.3" +version = "4.0.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3de270e75f27a4468a7c344070109046656e85cb522141f7d40ab4b83803ac" +checksum = "88541432753e17756ae72f8064335d2f2c048165ff4822094334d767108762ca" dependencies = [ - "amplify_syn", + "amplify_syn 2.0.0-beta.1", "proc-macro2", "quote", "syn", @@ -32,9 +45,9 @@ dependencies = [ [[package]] name = "amplify_num" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27d3d00d3d115395a7a8a4dc045feb7aa82b641e485f7e15f4e67ac16f4f56d" +checksum = "ddce3bc63e807ea02065e8d8b702695f3d302ae4158baddff8b0ce5c73947251" dependencies = [ "serde", ] @@ -51,19 +64,10 @@ dependencies = [ ] [[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "async-trait" -version = "0.1.63" +name = "amplify_syn" +version = "2.0.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +checksum = "5eba488aa4b78ca719aff57edfbe634efef62232110c35442cd9136c67cedf39" dependencies = [ "proc-macro2", "quote", @@ -71,57 +75,48 @@ dependencies = [ ] [[package]] -name = "atty" -version = "0.2.14" +name = "arrayref" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] -name = "autocfg" -version = "1.1.0" +name = "arrayvec" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] -name = "base64" -version = "0.13.1" +name = "ascii" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +dependencies = [ + "serde", +] [[package]] -name = "bech32" -version = "0.9.1" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "bitcoin" -version = "0.29.2" +name = "baid58" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" +checksum = "16db9f8a10cac6c74b78f8922bfb4b3eb5bbbed4734fb9fa8218af32b5e381f4" dependencies = [ - "base64", - "bech32", - "bitcoin_hashes", - "secp256k1", - "serde", + "base58", + "blake3", + "mnemonic", ] [[package]] -name = "bitcoin_blockchain" -version = "0.9.0" +name = "base58" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb38270e9c10c1858bf6c939a700ea249e7dd0e8b36e3258b55ce7c9bdd2499" -dependencies = [ - "amplify", - "chrono", - "strict_encoding", -] +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" [[package]] name = "bitcoin_hashes" @@ -133,99 +128,82 @@ dependencies = [ ] [[package]] -name = "bitcoin_hd" -version = "0.9.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae39db6b04c44c76c50b8f8dd09799adb71ae7807a1de2efe3d127169c04160" -dependencies = [ - "amplify", - "bitcoin", - "secp256k1", - "slip132", - "strict_encoding", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bitcoin_onchain" -version = "0.9.0" +name = "blake3" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4a6744365c862b8c74cb33a21532f91f1f32f4082361fd3aab5d22c1afd482" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ - "amplify", - "bitcoin", - "bitcoin_hd", - "chrono", - "strict_encoding", + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest", ] [[package]] -name = "bitcoin_scripts" -version = "0.9.0" +name = "block-buffer" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8b08389e5391cf8311fd4de09a340ed98b4b7c2f87c956125c22341b5d14fb" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "amplify", - "bitcoin", - "secp256k1", - "serde", - "serde_with", - "stability", - "strict_encoding", + "generic-array", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bp-core" -version = "0.9.0" +version = "0.10.0-beta.1" dependencies = [ "amplify", - "bitcoin", "bp-dbc", + "bp-primitives", "bp-seals", - "clap", - "colored", "commit_verify", - "electrum-client", - "psbt", "serde", - "serde_with", "single_use_seals", "strict_encoding", ] [[package]] name = "bp-dbc" -version = "0.9.0" +version = "0.10.0-beta.1" +dependencies = [ + "amplify", + "bp-primitives", + "commit_verify", + "secp256k1", + "serde", + "strict_encoding", +] + +[[package]] +name = "bp-primitives" +version = "0.10.0-beta.1" dependencies = [ "amplify", - "bitcoin", - "bitcoin_scripts", "commit_verify", - "psbt", "secp256k1", "serde", - "serde_with", "strict_encoding", ] [[package]] name = "bp-seals" -version = "0.9.0" +version = "0.10.0-beta.1" dependencies = [ "amplify", - "async-trait", - "bitcoin", - "bitcoin_onchain", + "baid58", "bp-dbc", + "bp-primitives", "commit_verify", - "lnpbp_bech32", + "rand", "serde", - "serde_with", "single_use_seals", "strict_encoding", ] @@ -244,9 +222,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -254,216 +232,56 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-integer", - "num-traits", - "time", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "commit_verify" -version = "0.9.0" +version = "0.10.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f422e4e3f14f628b3cc3a241c0ea53b6457d100dda2980e2d7e719f1e41cf7" +checksum = "f7ce27ce9d56cac2631f9701984dc4fd162abbd7b563d97e4440db56f7282cc9" dependencies = [ "amplify", "bitcoin_hashes", "rand", "serde", - "serde_with", "strict_encoding", ] [[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cxx" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" +name = "constant_time_eq" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" [[package]] -name = "darling_macro" -version = "0.13.4" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "darling_core", - "quote", - "syn", + "generic-array", + "typenum", ] [[package]] -name = "electrum-client" -version = "0.12.0" +name = "digest" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a232d46710f8064b7bb2029d82819fc1f5fba053687e0e3bb47cc762af24f6" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "bitcoin", - "byteorder", - "libc", - "log", - "rustls", - "serde", - "serde_json", - "webpki", - "webpki-roots", - "winapi", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] -name = "encoding_derive_helpers" -version = "0.8.1" +name = "generic-array" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2293d9a54744b73b753fad2e3c16295345504dc230696ee8aa5dbfd276ab4" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ - "amplify", - "proc-macro2", - "quote", - "syn", + "typenum", + "version_check", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "getrandom" version = "0.2.8" @@ -472,7 +290,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -483,54 +301,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "ident_case" -version = "1.0.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "indexmap" @@ -548,15 +321,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -569,35 +333,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "lnpbp_bech32" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecdbd10fe8d9d53febf413b4f8bbaa1911492597b19bfbb84a598f37062eab96" -dependencies = [ - "amplify", - "bech32", - "bitcoin_hashes", - "serde", - "serde_with", - "strict_encoding", -] - [[package]] name = "log" version = "0.4.17" @@ -608,22 +343,13 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" +name = "mnemonic" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "29fae0e4c0b155d3b019a7cbc27abe4a90e15c06814d27889ce9f5f44e2faf77" dependencies = [ - "autocfg", + "byteorder", + "lazy_static", ] [[package]] @@ -632,12 +358,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "paste" version = "1.0.11" @@ -650,30 +370,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.50" @@ -683,22 +379,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "psbt" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53ad01c5e6d2e9375b5594ce166a0374182562b1ec82318a7f039ed2efda759" -dependencies = [ - "amplify", - "bitcoin", - "bitcoin_blockchain", - "bitcoin_hd", - "bitcoin_onchain", - "bitcoin_scripts", - "commit_verify", - "strict_encoding", -] - [[package]] name = "quote" version = "1.0.23" @@ -738,62 +418,18 @@ dependencies = [ "getrandom", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "ryu" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" dependencies = [ - "bitcoin_hashes", "rand", "secp256k1-sys", "serde", @@ -801,9 +437,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "642a62736682fdd8c71da0eb273e453c8ac74e33b9fb310e22ba5b03ec7651ff" dependencies = [ "cc", ] @@ -849,99 +485,48 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "hex", - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "serde_yaml" -version = "0.8.26" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", + "itoa", "ryu", "serde", - "yaml-rust", + "unsafe-libyaml", ] [[package]] name = "single_use_seals" -version = "0.9.0" +version = "0.10.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e092f1d5411486816ec67cd7d0280acb0e05a1bd509721bc3dde3b1ffa5bfa0e" +checksum = "2ea7651417b98128263b5c3d2dc836400eecd7f4fba8470d4ca17f129f9a1b4a" dependencies = [ "amplify_derive", - "async-trait", -] - -[[package]] -name = "slip132" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a2947cb179006a73896fca01015ee5255c05b8b83e74c5e9d623ed4480abe2" -dependencies = [ - "amplify", - "bitcoin", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "stability" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" -dependencies = [ - "quote", - "syn", ] [[package]] name = "strict_encoding" -version = "0.9.0" +version = "2.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be7060b49729cd0b9b2391114632ef64c363a4055d91de049f5555b466193bb" +checksum = "e8ce22c5bba8139f2182001358188f145b40b177a9a442545cf15574335fba88" dependencies = [ "amplify", - "bitcoin", - "bitcoin_hashes", - "chrono", "strict_encoding_derive", ] [[package]] name = "strict_encoding_derive" -version = "0.9.0" +version = "2.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c9cabafb397fc1144463228ad4ba57c3c670a0117505fe59b15d8c74449716" +checksum = "4cb9b35c9d7429212d20ae38bb86ecbc6cd3b3ccf5db6a12d1e2aafa7cedf5b0" dependencies = [ - "amplify_syn", - "encoding_derive_helpers", + "amplify_syn 2.0.0-beta.1", + "heck", "proc-macro2", + "quote", "syn", ] @@ -956,10 +541,10 @@ dependencies = [ ] [[package]] -name = "strsim" -version = "0.10.0" +name = "subtle" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -972,32 +557,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "toml" version = "0.5.11" @@ -1008,22 +567,22 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.6" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] -name = "unicode-width" -version = "0.1.10" +name = "unicode-ident" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] -name = "untrusted" -version = "0.7.1" +name = "unsafe-libyaml" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" [[package]] name = "version_check" @@ -1031,12 +590,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1045,9 +598,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1055,9 +608,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -1070,9 +623,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1080,9 +633,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -1093,75 +646,6 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" diff --git a/Cargo.toml b/Cargo.toml index 8e3c5e50..1463cb8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,56 +1,62 @@ +[workspace] +members = [ + "primitives", + "dbc", + "seals", + "." +] +default-members = [ + "primitives", + "dbc", + "seals", + "." +] + +[workspace.package] +authors = ["Dr Maxim Orlovsky "] +homepage = "https://github.com/BP-WG" +repository = "https://github.com/BP-WG/bp-core" +rust-version = "1.66" # Due to strict encoding library (caused by GAD) +edition = "2021" +license = "Apache-2.0" + [package] name = "bp-core" -version = "0.9.0" -license = "Apache-2.0" -authors = ["Dr. Maxim Orlovsky "] -description = "Bitcoin Protocol Core Library (BP Core Lib)" -repository = "https://github.com/LNP-BP/bp-core" -homepage = "https://github.com/LNP-BP" -keywords = ["lnp-bp", "cryptocurrency", "smart-contracts", "bitcoin"] -categories = ["cryptography::cryptocurrencies", "encoding", "parsing"] +version = "0.10.0-beta.1" +description = "Bitcoin protocol core library (BP Core Lib)" +keywords = ["lnp-bp", "smart-contracts", "bitcoin", "blockchain"] +categories = ["cryptography"] +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } readme = "README.md" -edition = "2021" -rust-version = "1.59.0" -exclude = [".github", "dbc", "seals", "dbc-legacy", "seals-legacy"] +exclude = [".github", "primitives", "dbc", "seals"] [lib] name = "bp" path = "src/lib.rs" -[[bin]] -name = "dbc" -required-features = ["cli", "serde"] - -[[bin]] -name = "seals" -required-features = ["cli", "serde"] - [dependencies] -amplify = "3.13.0" -strict_encoding = "0.9.0" -commit_verify = "0.9.0" -single_use_seals = "0.9.0" -bitcoin = "0.29.2" -psbt = { version = "0.9.0", optional = true } -bp-dbc = { version = "0.9.0", path = "./dbc" } -bp-seals = { version = "0.9.0", path = "./seals" } +amplify = "4.0.0-beta.11" +strict_encoding = "2.0.0-beta.3" +commit_verify = "0.10.0-beta.1" +single_use_seals = "0.10.0-beta.1" +bp-primitives = { version = "0.10.0-beta.1", path = "./primitives" } +bp-dbc = { version = "0.10.0-beta.1", path = "./dbc" } +bp-seals = { version = "0.10.0-beta.1", path = "./seals" } serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } -serde_with = { version = "1.14", optional = true } -electrum-client = { version = "0.12.0", optional = true } -clap = { version = "~3.2.23", optional = true, features = ["derive"] } -colored = { version = "2", optional = true } [features] default = [] -all = ["async", "serde", "cli", "wallet"] -cli = ["clap", "colored", "electrum-client", "wallet"] -wallet = ["psbt", "bp-dbc/wallet"] -async = ["bp-seals/async"] -serde = ["amplify/serde", "bitcoin/serde", - "commit_verify/serde", "bp-dbc/serde", "bp-seals/serde", - "serde_crate", "serde_with"] +all = ["serde"] +serde = [ + "serde_crate", + "bp-dbc/serde", + #"bp-seals/serde", +] -[workspace] -members = [".", "dbc", "seals"] -default-members = [".", "dbc", "seals"] -exclude = ["dbc-legacy"] +[package.metadata.docs.rs] +features = [ "all" ] diff --git a/DCO b/DCO new file mode 100644 index 00000000..69175c98 --- /dev/null +++ b/DCO @@ -0,0 +1,28 @@ +Developer's Certificate of Origin 1.1 +Copyright © 2004, 2006 The Linux Foundation and its contributors. + +--- + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/LICENSE b/LICENSE index a6a3b8bc..e1720699 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019-2022 LNP/BP Standards Association, Switzerland + Copyright 2019-2023 LNP/BP Standards Association, Switzerland Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000..a3af1e08 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,6 @@ +Maxim Orlovsky +--------------- +- GitHub: [@dr-orlovsky](https://github.com/dr-orlovsky) +- GPG: `EAE730CEC0C663763F028A5860094BAF18A26EC9` +- SSH: `BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A` +- EMail: [dr@orlovsky.ch](mailto:dr@orlovsky.ch) diff --git a/MANIFEST.yml b/MANIFEST.yml new file mode 100644 index 00000000..6ec60bd5 --- /dev/null +++ b/MANIFEST.yml @@ -0,0 +1,14 @@ +Name: strict_encoding +Type: Library +Kind: Free software +License: Apache-2.0 +Language: Rust +Compiler: 1.66 +Author: Maxim Orlovsky +Maintained: UBIDECO Institute, Switzerland +Maintainers: + Maxim Orlovsky: + GitHub: @dr-orlovsky + GPG: EAE730CEC0C663763F028A5860094BAF18A26EC9 + SSH: BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A + EMail: dr@orlovsky.ch diff --git a/README.md b/README.md index 6bc724c5..e79e822f 100644 --- a/README.md +++ b/README.md @@ -10,72 +10,59 @@ [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) [![Apache-2 licensed](https://img.shields.io/crates/l/bp-core)](./LICENSE) -The library implements components necessary for [client-side-validation] in bitcoin -protocol, specifically +The library implements components necessary for [client-side-validation] in +bitcoin protocol, specifically -- deterministic bitcoin commitments API (LNPBP-14, 6 standards) -- bitcoin-based single-use-seal API (LNPBP-10 and LNPBP-39 standards) +- deterministic bitcoin commitments API ([LNPBP-1], [LNPBP-2], [LNPBP-3], [LNPBP-6], [LNPBP-11] & [LNPBP-12] standards) +- bitcoin-based single-use-seal API ([LNPBP-10] standards) Client-side-validation is a paradigm for distributed computing, based on top of proof-of-publication/commitment medium layer, which may be a bitcoin blockchain or other type of distributed consensus system. -The development of the library is supported by [LNP/BP Standards Association](https://lnp-bp.org). +The development of the library is supported by [LNP/BP Standards Association][lnpbp-web] +and is performed on its [GitHub page][lnpbp-github]. -## Usage - -To use libraries, you just need latest version of libraries, published to -[crates.io](https://crates.io) into `[dependencies]` section of your project -`Cargo.toml`. Here is the full list of available libraries from this repository: +The original idea of client-side-validation was proposed by Peter Todd with its +possible applications designed by Giacomo Zucco. It was shaped into the protocol +design by Dr Maxim Orlovsky with a big input from the community. -```toml -bp-dbc = "0.5" # Deterministic bitcoin commitments crate -bp-seals = "0.5" # Bitcoin single-use-seals crate -bp-core = "0.5" # Library including both of the previous crates -``` +Minimum supported rust version for the library (MSRV) is 1.66 and 2021 rust +edition. -`bp-core` crate is an "umbrella" library containing both deterministic bitcoin -commitments and bitcoin seals crates inside. -## Command-line utilities +## Documentation -One may install command-line utilities with the following command (requires -rust compiler and `rustup` tools to be already installed on a system): +Detailed developer & API documentation for all libraries can be accessed at: +- +- +- +- -```console -$ rustup default stable -$ rustup update -$ git clone https://github.com/BP-WG/bp-core -$ cd bp-core -$ cargo install --path . --bins --locked --all-features -``` +To learn about the technologies enabled by the library please check +[slides from our tech presentations][presentations] and +[LNP/BP tech talks videos][lnpbp-youtube]. -This will add `seals` and `dbc` commands to the system. -### Install with Docker +## Usage -#### Build +The repository contains rust libraries for client-side validation. -Clone the repository and checkout to the desired version (here `v0.8.0`): +### Use library in other projects -```console -$ git clone https://github.com/BP-WG/bp-core -$ cd bp-core -$ git checkout v0.8.0 -``` - -Build and tag the Docker image: +To use libraries, you just need latest version of libraries, published to +[crates.io](https://crates.io) into `[dependencies]` section of your project +`Cargo.toml`. Here is the full list of available libraries from this repository: -```console -$ docker build -t bp-core:v0.8.0 . +```toml +bp-primitives = "1" # Bitcoin protocol primitives crate +bp-dbc = "1" # Deterministic bitcoin commitments crate +bp-seals = "1" # Bitcoin single-use-seals crate +bp-core = "1" # Library including both of the previous crates ``` -#### Usage - -```console -$ docker run bp-core:v0.8.0 seals --help -$ docker run bp-core:v0.8.0 dbc --help -``` +`bp-core` crate is an "umbrella" library containing both deterministic bitcoin +commitments and bitcoin seals crates inside. ## Known applications @@ -94,7 +81,7 @@ Contribution guidelines can be found in [CONTRIBUTING](CONTRIBUTING.md) ### MSRV -This library requires minimum rust compiler version (MSRV) 1.41.1. +This library requires minimum rust compiler version (MSRV) 1.66.0. ### Policy on altcoins @@ -104,8 +91,19 @@ be declined. ### Licensing -See [LICENCE](LICENSE) file. +The libraries are distributed on the terms of Apache 2.0 opensource license. +See [LICENCE](LICENSE) file for the license details. + + +[lnpbp-web]: https://lnp-bp.org +[lnpbp-github]: https://github.com/LNP-BP +[lnpbp-youtube]: https://www.youtube.com/@LNPBP +[presentations]: https://github.com/LNP-BP/FAQ/blob/master/Presentation%20slides/ -[rust-bitcoin]: https://github.com/rust-bitcoin/rust-bitcoin -[descriptor-wallet]: https://github.com/LNP-BP/descriptor-wallet -[client-side-validation]: https://docs.rs/client_side_validation/ +[LNPBP-1]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0001.md +[LNPBP-2]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0002.md +[LNPBP-3]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0003.md +[LNPBP-6]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0006.md +[LNPBP-10]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0010.md +[LNPBP-11]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0011.md +[LNPBP-12]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0012.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..22101c1e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,59 @@ +# Security + +We take the security of our software products and services seriously, which +includes all source code repositories managed through our GitHub organizations. + +If you believe you have found a security vulnerability in any of our repository +that meets [definition of a security vulnerability][definition], please report +it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the repository maintainers by sending a **GPG +encrypted e-mail** to _all maintainers of a specific repo_ using their GPG keys. + +A list of repository maintainers and their keys and e-mail addresses are +provided inside MAINTANERS.md file and MANIFEST.yml, with the latter also +included in the README.md as a manifest block, which looks in the following way: + +```yaml +Name: +... +Maintained: +Maintainers: + : + GPG: + EMail: + : + ... +``` + +You should receive a response within 72 hours. If for some reason you do not, +please follow up via email to ensure we received your original message. + +Please include the requested information listed below (as much as you can +provide) to help us better understand the nature and scope of the possible +issue: + +* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +We follow the principle of [Coordinated Vulnerability Disclosure][disclosure]. + +[definition]: https://aka.ms/opensource/security/definition +[disclosure]: https://aka.ms/opensource/security/cvd diff --git a/codecov.yml b/codecov.yml index fcaf8f20..240d7310 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,9 +8,12 @@ coverage: status: project: default: - target: 85% + target: 75% threshold: 1% + branches: + - master patch: default: - target: 75% - threshold: 1% \ No newline at end of file + target: 60% + threshold: 1% + only_pulls: true diff --git a/dbc-legacy/Cargo.toml b/dbc-legacy/Cargo.toml deleted file mode 100644 index 562ccefe..00000000 --- a/dbc-legacy/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "bp-dbc" -version = "0.6.0-alpha.1" -license = "Apache-2.0" -authors = ["Dr. Maxim Orlovsky "] -description = "Deterministic bitcoin commitments library" -repository = "https://github.com/LNP-BP/bp-core" -homepage = "https://github.com/LNP-BP" -keywords = ["lnp-bp", "bitcoin", "cryptography", "smart-contracts", "single-use-seals"] -categories = ["cryptography::cryptocurrencies", "encoding"] -readme = "../README.md" -edition = "2018" - -[lib] -name = "dbc" -path = "src/lib.rs" - -[dependencies] -amplify = "3.12.0" -bitcoin = "0.28.0-rc.1" -secp256k1 = { version = "0.21.3", features = ["global-context"] } -bitcoin_scripts = "0.6.0-beta.2" -descriptors = { version = "0.6.0-beta.1", default-features = false } -strict_encoding = "1.8.0-rc.1" -commit_verify = "0.6.0-alpha.1" -miniscript_crate = { package = "miniscript", version = "6.0.1", git = "https://github.com/rust-bitcoin/rust-miniscript", optional = true } -serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } -serde_with = { version = "1.8", optional = true } - -[features] -default = [] -all = ["serde", "miniscript"] -miniscript = ["miniscript_crate", "bitcoin_scripts/miniscript", "descriptors/miniscript"] -serde = ["amplify/serde", "bitcoin/use-serde", "bitcoin_scripts/serde", "commit_verify/serde", "serde_crate", "serde_with"] diff --git a/dbc-legacy/src/container.rs b/dbc-legacy/src/container.rs deleted file mode 100644 index 11a6692c..00000000 --- a/dbc-legacy/src/container.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use crate::{Error, Proof}; - -pub trait Container: Sized { - type Supplement; - type Host; - - /// Reconstructs commitment container from the extra-transaction proof - /// and protocol-specific data. - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - host: &Self::Host, - ) -> Result; - - fn deconstruct(self) -> (Proof, Self::Supplement); - - fn to_proof(&self) -> Proof; - fn into_proof(self) -> Proof; -} diff --git a/dbc-legacy/src/error.rs b/dbc-legacy/src/error.rs deleted file mode 100644 index a0b36e20..00000000 --- a/dbc-legacy/src/error.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -#[cfg(feature = "miniscript")] -use miniscript::policy::compiler::CompilerError; - -use crate::spk::lnpbp1; - -#[cfg(not(feature = "miniscript"))] -#[derive( - Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error -)] -#[display(Debug)] -pub enum CompilerError {} - -/// Different error types which may happen during deterministic bitcoin -/// commitment generation procedures -#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum Error { - /// Indicates failure of applying commitment tweak to a public key - #[from] - Lnpbp1Commitment(lnpbp1::Error), - - /// Unable to verify commitment due to an incorrect proof data structure - InvalidProofStructure, - - /// LNPBP-2 standard requires OP_RETURN-based commitments to be produced - /// only if serialized version of a tweaked pubkey starts with `02` byte. - /// This error indicates that the provided public key does not satisfy this - /// condition - InvalidOpReturnKey, - - /// Can't deserealized public key from bitcoin script push op code - InvalidKeyData, - - /// Wrong witness version, may be you need to upgrade used library version - UnsupportedWitnessVersion, - - /// Miniscript was unable to parse provided script data; they are either - /// invalid or miniscript library contains a bug - #[cfg_attr( - feature = "miniscript", - from(bitcoin_scripts::PubkeyParseError) - )] - LockscriptParseError, - - /// Provided script contains no keys, so commitment or its verification is - /// impossible - LockscriptContainsNoKeys, - - /// Bitcoin script contains public key hashes with no matching public - /// keys provided. Commitment procedure fails since it can't ensure that - /// commitment include all public key. - LockscriptContainsUnknownHashes, - - /// Attempt to commit into LockScript has failed: the key that must contain - /// the commitment/tweak was not found either in plain nor hash form in - /// any of the script branches - LockscriptKeyNotFound, - - /// Policy compilation error - #[from] - #[display(inner)] - PolicyCompilation(CompilerError), - - /// Deterministic bitcoin commitments require use of compressed public keys - UncompressedKey, -} - -impl From for Error { - fn from(err: descriptors::Error) -> Self { - match err { - descriptors::Error::InvalidKeyData => Error::InvalidKeyData, - descriptors::Error::UnsupportedWitnessVersion => { - Error::UnsupportedWitnessVersion - } - #[cfg(feature = "miniscript")] - descriptors::Error::PolicyCompilation(err) => { - Error::PolicyCompilation(err) - } - #[cfg(not(feature = "miniscript"))] - descriptors::Error::PolicyCompilation(_) => unreachable!( - "policy compilation error when miniscript is disabled", - ), - descriptors::Error::UncompressedKeyInSegWitContext => { - Error::UncompressedKey - } - // Since we never parse strings, this error must not happen - descriptors::Error::CantParseDescriptor => { - unreachable!("miniscript parses string representation") - } - // If other errors appear this must crash so we know about that the - // new implementation is required - _ => unimplemented!("not all of descriptor errors is supported"), - } - } -} diff --git a/dbc-legacy/src/lib.rs b/dbc-legacy/src/lib.rs deleted file mode 100644 index a9ecebf4..00000000 --- a/dbc-legacy/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -// Coding conventions -#![recursion_limit = "256"] -#![deny(dead_code, /* missing_docs, */ warnings)] - -#[macro_use] -extern crate amplify; -#[macro_use] -extern crate strict_encoding; -#[cfg(feature = "serde")] -#[macro_use] -extern crate serde_crate as serde; -#[cfg(feature = "miniscript")] -extern crate miniscript_crate as miniscript; - -pub mod container; -mod error; -pub mod proof; -pub mod sigs; -pub mod spk; -pub mod tapret; -pub mod txout; - -pub use container::Container; -pub use error::Error; -pub use proof::Proof; diff --git a/dbc-legacy/src/pktweak/keyset.rs b/dbc-legacy/src/pktweak/keyset.rs deleted file mode 100644 index 0f32c785..00000000 --- a/dbc-legacy/src/pktweak/keyset.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! # LNPBP-1 related - -use std::collections::BTreeSet; - -use bitcoin::hashes::{sha256, Hmac}; -use commit_verify::EmbedCommitVerify; -use miniscript::Segwitv0; - -use crate::lnpbp1; -use crate::{Container, Error, Proof, ScriptEncodeData}; - -/// Container for LNPBP-1 commitments. In order to be constructed, commitment -/// requires an original public key and a protocol-specific tag, which -/// must be hashed during commitment process. Here we use pre-hashed version -/// of the tag in order to maximize performance for multiple commitments. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct KeysetContainer { - /// The original public key: host for the commitment - pub pubkey: secp256k1::PublicKey, - /// Other keys that will participate the commitment procedure - pub keyset: BTreeSet, - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, - /// Tweaking factor stored after [`KeysetCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl Container for KeysetContainer { - /// Out supplement is a protocol-specific tag in its hashed form - type Supplement = sha256::Hash; - - /// Proof contains both original public key and all participating keys - /// (inside it's script), so we don't need host here - type Host = Option<()>; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - _: &Self::Host, - ) -> Result { - if let ScriptEncodeData::LockScript(ref script) = proof.source { - Ok(Self { - pubkey: proof.pubkey, - keyset: script - .extract_pubkeyset::()? - .into_iter() - .map(|pk| pk.key) - .collect(), - tag: *supplement, - tweaking_factor: None, - }) - } else { - Err(Error::InvalidProofStructure) - } - } - - #[inline] - fn deconstruct(self) -> (Proof, Self::Supplement) { - (Proof::from(self.pubkey), self.tag) - } - - /// Important: this method should not be used. KeysetContainer does not - /// support proof generation, use more advanced structures like LockScript - /// container to generate the proof - #[inline] - fn to_proof(&self) -> Proof { - panic!("KeysetContainer does not support proof generation") - } - - /// Important: this method should not be used. KeysetContainer does not - /// support proof generation, use more advanced structures like LockScript - /// container to generate the proof - #[inline] - fn into_proof(self) -> Proof { - panic!("KeysetContainer does not support proof generation") - } -} - -/// Public key committed to some message plus a sum of other public keys via -/// LNPBP2-based tweaking procedure -#[derive(Wrapper, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, From)] -#[display("{0}", alt = "{_0:#}*")] -#[wrapper(FromStr, LowerHex)] -pub struct KeysetCommitment(secp256k1::PublicKey); - -impl EmbedCommitVerify for KeysetCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = KeysetContainer; - type Error = lnpbp1::Error; - - // #[consensus_critical("RGB")] - // #[standard_critical("LNPBP-1")] - fn embed_commit( - keyset_container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let mut keyset = keyset_container.keyset.clone(); - let mut pubkey = keyset_container.pubkey; - keyset.insert(pubkey); - - let tweaking_factor = lnpbp1::commit( - &mut keyset, - &mut pubkey, - &keyset_container.tag, - msg, - )?; - - keyset_container.tweaking_factor = Some(tweaking_factor); - - // Returning tweaked public key - Ok(KeysetCommitment(pubkey)) - } -} - -#[cfg(test)] -mod test { - use std::iter::FromIterator; - use std::str::FromStr; - - use amplify::hex::ToHex; - use amplify::Wrapper; - use bitcoin::hashes::{sha256, Hash}; - - use super::*; - use crate::lnpbp1::test_helpers::*; - use crate::pubkey::*; - - #[test] - fn test_lnpbp1_vs_lnpbp2() { - let tag = sha256::Hash::hash(b"TEST_TAG2"); - let msg = "test message"; - gen_secp_pubkeys(9).into_iter().for_each(|pubkey| { - let lnpbp1_commitment = PubkeyCommitment::embed_commit( - &mut PubkeyContainer { - pubkey, - tag, - tweaking_factor: None, - }, - &msg, - ) - .unwrap(); - let lnpbp2_commitment = KeysetCommitment::embed_commit( - &mut KeysetContainer { - pubkey, - keyset: BTreeSet::new(), - tag, - tweaking_factor: None, - }, - &msg, - ) - .unwrap(); - - assert_eq!( - lnpbp1_commitment.into_inner(), - lnpbp2_commitment.into_inner() - ); - }); - } - - #[test] - fn test_keyset_commitment() { - let tag = sha256::Hash::hash(b"TEST_TAG2"); - let pubkey = secp256k1::PublicKey::from_str( - "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(); - (1..9).into_iter().for_each(|n_keys| { - embed_commit_verify_suite::, KeysetCommitment>( - gen_messages(), - &mut KeysetContainer { - pubkey, - keyset: BTreeSet::from_iter(gen_secp_pubkeys(n_keys)), - tag, - tweaking_factor: None, - }, - ); - }); - } - - #[test] - fn test_keyset_tweaking_results() { - let tag = sha256::Hash::hash(b"TEST_TAG2"); - let msg = "test message"; - let pubkey = secp256k1::PublicKey::from_str( - "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(); - let keyset = BTreeSet::from_iter(vec![secp256k1::PublicKey::from_str( - "03cfb81a7609a4d40914dfd41860f501209c30468d91834c8af1af34ce73f4f3fd", - ) - .unwrap()]); - - let commitment = KeysetCommitment::embed_commit( - &mut KeysetContainer { - pubkey, - keyset, - tag, - tweaking_factor: None, - }, - &msg, - ) - .unwrap(); - assert_eq!( - commitment.as_inner().to_hex(), - "02e47bb42c041f158ecfcf1099018f08650ef569a9a51bbb317e8787cdf3e06890" - ); - } -} diff --git a/dbc-legacy/src/pktweak/lnpbp1.rs b/dbc-legacy/src/pktweak/lnpbp1.rs deleted file mode 100644 index a4fc63ce..00000000 --- a/dbc-legacy/src/pktweak/lnpbp1.rs +++ /dev/null @@ -1,628 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! # LNPBP-1 -//! -//! Module for Secp256k1 elliptic curve based collision-resistant commitments, -//! implementing [LNPBP-1](https://github.com/LNP-BP/lnpbps/blob/master/lnpbp-0001.md) - -use std::collections::BTreeSet; - -use bitcoin::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine}; - -/// Single SHA256 hash of "LNPBP1" string according to LNPBP-1 acting as a -/// prefix to the message in computing tweaking factor -pub static LNPBP1_HASHED_TAG: [u8; 32] = [ - 245, 8, 242, 142, 252, 192, 113, 82, 108, 168, 134, 200, 224, 124, 105, - 212, 149, 78, 46, 201, 252, 82, 171, 140, 204, 209, 41, 17, 12, 0, 64, 175, -]; - -/// Deterministically-organized set of all public keys used by this mod -/// internally -type Keyset = BTreeSet; - -/// Errors that may happen during LNPBP-1 commitment procedure or because of -/// incorrect arguments provided to [`commit()`] function. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum Error { - /// Keyset must include target public key, but no target key found it - /// the provided set. - NotKeysetMember, - - /// Elliptic curve point addition resulted in point in infinity; you - /// must select different source public keys - SumInfiniteResult, - - /// LNPBP-1 commitment either is outside of Secp256k1 order `n` (this event - /// has negligible probability <~2^-64), or, when added to the provided - /// keyset, results in point at infinity. You may try with a different - /// source message or public keys. - InvalidTweak, -} - -/// Function performs commitment procedure according to LNPBP-1. -/// -/// # Parameters -/// -/// - A set of public keys for committing during the LNPBP-1 procedure -/// - Target public key for tweaking. Must be a part of the keyset, otherwise -/// function will fail with [`Error::NotKeysetMember`] -/// - Protocol-specific tag in form of 32-byte hash -/// - Message to commit to, which must be representable as a byte slice using -/// [`AsRef::as_ref()`] -/// -/// # Returns -/// -/// Function mutates two of its parameters, -/// - `target_pubkey`, with a tweaked version of the public key containing -/// commitment to the message and the rest of keyset, -/// - `keyset`, by replacing original `target_pubkey` with its tweaked version -/// and returns `tweaking_factor` as a return parameter wrapped into -/// [`Result::Ok`]. -/// -/// If the function fails with any error, value for `target_pubkey` and `keyset` -/// is undefined and must be discarded. -/// -/// # Errors -/// -/// Function may fail because of one of the following circumstances: -/// - If `target_pubkey` is not a part of `keyset` ([`Error::NotKeysetMember`]) -/// - If keyset deliberately constructed in a way that sum of some of its keys -/// is equivalent to negation of some other keys. In this case function fails -/// with [`Error::SumInfiniteResult`] -/// - With negligible probability because of elliptic curve Secp256k1 point -/// addition overflow; in this case function returns either -/// [`Error::SumInfiniteResult`], if it happens during summation of public -/// keys from the `keyset`, or [`Error::InvalidTweak`], if it happens during -/// tweaking factor addition to the `target_pubkey`. -/// -/// # Protocol: -/// -/// Please refer to the original document for the verification: -/// - -// #[consensus_critical("RGB")] -// #[standard_critical("LNPBP-1")] -pub fn commit( - keyset: &mut Keyset, - target_pubkey: &mut secp256k1::PublicKey, - protocol_tag: &sha256::Hash, - message: &impl AsRef<[u8]>, -) -> Result, Error> { - if !keyset.remove(target_pubkey) { - return Err(Error::NotKeysetMember); - } - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: We commit to the sum of all public keys, - // not a single pubkey. For single key the set - // is represented by itself - let pubkey_sum = keyset - .iter() - .try_fold(*target_pubkey, |sum, pubkey| sum.combine(pubkey)) - .map_err(|_| Error::SumInfiniteResult)?; - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: HMAC engine is based on sha256 hash - let mut hmac_engine = - HmacEngine::::new(&pubkey_sum.serialize()); - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: Hash process started with consuming first - // protocol prefix: single SHA256 hash of - // ASCII "LNPBP1" string. - // NB: We use the same hash as in LNPBP-1 so when there is no other - // keys involved the commitment would not differ. - hmac_engine.input(&LNPBP1_HASHED_TAG[..]); - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: The second prefix comes from the upstream - // protocol as a part of the container - hmac_engine.input(&protocol_tag[..]); - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: Next we hash the message. The message must be - // prefixed with the protocol-specific prefix: - // another single SHA256 hash of protocol name. - // However this is not the part of this function, - // the function expect that the `msg` is already - // properly prefixed - hmac_engine.input(&sha256::Hash::hash(message.as_ref())); - - // Producing tweaking factor - let tweaking_factor = Hmac::from_engine(hmac_engine); - - // Applying tweaking factor to public key - target_pubkey - .add_exp_assign(secp256k1::SECP256K1, &tweaking_factor[..]) - .map_err(|_| Error::InvalidTweak)?; - - keyset.insert(*target_pubkey); - - // Returning tweaked public key - Ok(tweaking_factor) -} - -/// Function verifies commitment created according to LNPBP-1. -/// -/// # Parameters -/// -/// - `verified_pubkey`: public key containing LNPBP-1 commitment, i.e. the one -/// modified by [`commit()`] procedure as its second parameter `target_key` -/// - `original_keyset`: set of public keys provided to the [`commit()`] -/// procedure. This set must include orignal pubkey specified in the next -/// parameter `taget_pubkey` -/// - `target_pubkey`: one of public keys included into the original keyset and -/// that was provided to the [`commit()`] procedure as `target_pubkey`. This -/// must be an original version of public key from the `verified_pubkey` -/// parameter before the tweak was applied -/// - `protocol_tag`: protocol-specific tag in form of 32-byte hash -/// - `message`: message to commit to, which must be representable as a byte -/// slice using [`AsRef::as_ref()`] -/// -/// # Returns -/// -/// - `true`, if verification succeeds, -/// - `false`, if verification fails, indicating that the provided -/// `verified_pubkey` is not committed to the data given in the rest of -/// function parameters. -/// -/// # Procedure -/// -/// Please refer to the original document for the general algotirhm: -/// -/// -/// Function verifies commitment by running LNPBP-1 commitment procedure once -/// again with the provided data as a source data, and comparing the result of -/// the commitment to the `verified_pubkey`. If the commitment function fails, -/// it means that it was not able to commit with the provided data, meaning that -/// the commitment was not created. Thus, we return that verification have not -/// passed, and not a error. Verification succeeds if the commitment procedure -/// produces public key equivalent to the `verified_pubkey`. -pub fn verify( - verified_pubkey: secp256k1::PublicKey, - original_keyset: &Keyset, - mut target_pubkey: secp256k1::PublicKey, - protocol_tag: &sha256::Hash, - message: &impl AsRef<[u8]>, -) -> bool { - match commit( - &mut original_keyset.clone(), - &mut target_pubkey, - protocol_tag, - message, - ) { - // If the commitment function fails, it means that it was not able to - // commit with the provided data, meaning that the commitment was not - // created. Thus, we return that verification have not passed, and not - // a error. - Err(_) => false, - - // Verification succeeds if the commitment procedure produces public key - // equivalent to the verified one - Ok(_) => target_pubkey == verified_pubkey, - } -} - -/// Helpers for writing test functions working with commit-verify scheme -#[cfg(test)] -pub mod test_helpers { - use std::collections::HashSet; - use std::fmt::Debug; - - use amplify::hex::FromHex; - use commit_verify::EmbedCommitVerify; - - use super::*; - - /// Generates a set of messages for testing purposes - /// - /// All of these messages MUST produce different commitments, otherwise the - /// commitment algorithm is not collision-resistant - pub fn gen_messages() -> Vec> { - vec![ - // empty message - b"".to_vec(), - // zero byte message - b"\x00".to_vec(), - // text message - b"test".to_vec(), - // text length-extended message - b"test*".to_vec(), - // short binary message - Vec::from_hex("deadbeef").unwrap(), - // length-extended version - Vec::from_hex("deadbeef00").unwrap(), - // prefixed version - Vec::from_hex("00deadbeef").unwrap(), - // serialized public key as text - b"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".to_vec(), - // the same public key binary data - Vec::from_hex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798") - .unwrap(), - // different public key - Vec::from_hex("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9") - .unwrap(), - ] - } - - pub fn gen_secp_pubkeys(n: usize) -> Vec { - let mut ret = Vec::with_capacity(n); - let mut sk = [0; 32]; - - for i in 1..n + 1 { - sk[0] = i as u8; - sk[1] = (i >> 8) as u8; - sk[2] = (i >> 16) as u8; - - ret.push(secp256k1::PublicKey::from_secret_key( - &secp256k1::SECP256K1, - &secp256k1::SecretKey::from_slice(&sk[..]).unwrap(), - )); - } - ret - } - - /// Runs round-trip of commitment-embed-verify for a given set of messages - /// and provided container - pub fn embed_commit_verify_suite( - messages: Vec, - container: &mut CMT::Container, - ) where - MSG: AsRef<[u8]> + Eq, - CMT: EmbedCommitVerify + Eq + std::hash::Hash + Debug, - { - messages.iter().fold( - HashSet::::with_capacity(messages.len()), - |mut acc, msg| { - let commitment = CMT::embed_commit(container, msg).unwrap(); - - // Commitments MUST be deterministic: each message should - // produce unique commitment - (1..10).for_each(|_| { - assert_eq!( - CMT::embed_commit(container, msg).unwrap(), - commitment - ); - }); - - // Testing verification - assert!(commitment.verify(container, msg).unwrap()); - - messages.iter().for_each(|m| { - // Testing that commitment verification succeeds only - // for the original message and fails for the rest - assert_eq!( - commitment.verify(container, m).unwrap(), - m == msg - ); - }); - - acc.iter().for_each(|cmt| { - // Testing that verification against other commitments - // returns `false` - assert!(!cmt.verify(container, msg).unwrap()); - }); - - // Detecting collision - assert!(acc.insert(commitment)); - - acc - }, - ); - } -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use super::*; - use crate::lnpbp1::test_helpers::*; - - #[test] - fn test_lnpbp1_tag() { - assert_eq!( - sha256::Hash::hash(b"LNPBP1").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"LNPBP2").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"LNPBP-1").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"LNPBP_1").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"lnpbp1").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"lnpbp-1").into_inner(), - LNPBP1_HASHED_TAG - ); - assert_ne!( - sha256::Hash::hash(b"lnpbp_1").into_inner(), - LNPBP1_HASHED_TAG - ); - } - - #[test] - fn test_single_key() { - let tag = sha256::Hash::hash(b"ProtoTag"); - let tag2 = sha256::Hash::hash(b"Prototag"); - let messages = gen_messages(); - let all_keys = gen_secp_pubkeys(6); - let other_key = all_keys[0]; - for msg in &messages { - for mut pk in all_keys[1..].to_vec() { - let original = pk.clone(); - let mut keyset = bset![pk]; - let mut keyset2 = bset![pk]; - let mut pk2 = pk.clone(); - let factor1 = commit(&mut keyset, &mut pk, &tag, &msg).unwrap(); - let factor2 = - commit(&mut keyset2, &mut pk2, &tag2, &msg).unwrap(); - - // Ensure that changing tag changes commitment and tweaking - // factor (and tag is case-sensitive!) - assert_ne!(factor1, factor2); - assert_ne!(pk, pk2); - - // Ensure that factor value is not trivial - assert_ne!(factor1, Hmac::from_slice(&[0u8; 32]).unwrap()); - assert_ne!(factor1, Hmac::from_slice(&[1u8; 32]).unwrap()); - assert_ne!(factor1, Hmac::from_slice(&[0xFFu8; 32]).unwrap()); - assert_ne!(&factor1[..], &tag[..]); - assert_ne!(&factor1[..], &msg[..]); - - // Verify that the key was indeed tweaked - assert_ne!(pk, original); - - // Verify that the set updated - assert_ne!(bset![original], keyset); - assert_eq!(bset![pk], keyset); - - // Do commitment by hand - let mut engine = - HmacEngine::::new(&original.serialize()); - engine.input(&LNPBP1_HASHED_TAG); - engine.input(&tag.into_inner()); - engine.input(&sha256::Hash::hash(msg)); - let hmac = Hmac::from_engine(engine); - let tweaking_factor = *hmac.as_inner(); - let mut altkey = original; - altkey - .add_exp_assign(&secp256k1::SECP256K1, &tweaking_factor[..]) - .unwrap(); - assert_eq!(altkey, pk); - - // Now try commitment with a different key, but the same data - if other_key != original { - let mut other_commitment = other_key; - let mut other_keyset = bset![other_commitment]; - let factor3 = commit( - &mut other_keyset, - &mut other_commitment, - &tag, - &msg, - ) - .unwrap(); - - // Make sure we commit to the key value - assert_ne!(factor1, factor3); - - // Make sure commitment value is not the same - assert_ne!(pk, other_commitment); - - // Make sure we can't cross-verify - assert_eq!( - verify( - other_commitment, - &bset![original], - original, - &tag, - &msg - ), - false - ); - } - - // Verify commitment - assert!(verify(pk, &bset![original], original, &tag, &msg)); - - // Make sure we can't cross-verify with different tag - assert_eq!( - verify(pk, &bset![original], original, &tag2, &msg), - false - ); - - // Make sure we can't cross-verify with different message - assert_eq!( - verify( - pk, - &bset![original], - original, - &tag2, - &b"some other message" - ), - false - ); - } - } - } - - #[test] - fn test_keyset() { - let tag = sha256::Hash::hash(b"ProtoTag"); - let tag2 = sha256::Hash::hash(b"Prototag"); - let messages = gen_messages(); - let all_keys = gen_secp_pubkeys(6); - let other_key = all_keys[0]; - let original_keyset: BTreeSet<_> = - all_keys[1..].to_vec().into_iter().collect(); - for msg in &messages { - for mut pk in original_keyset.clone() { - let original = pk.clone(); - let mut keyset = original_keyset.clone(); - let mut keyset2 = original_keyset.clone(); - let mut pk2 = pk.clone(); - let factor1 = commit(&mut keyset, &mut pk, &tag, &msg).unwrap(); - let factor2 = - commit(&mut keyset2, &mut pk2, &tag2, &msg).unwrap(); - - // Ensure that changing tag changes commitment and tweaking - // factor (and tag is case-sensitive!) - assert_ne!(factor1, factor2); - assert_ne!(pk, pk2); - - // Ensure that factor value is not trivial - assert_ne!(factor1, Hmac::from_slice(&[0u8; 32]).unwrap()); - assert_ne!(factor1, Hmac::from_slice(&[1u8; 32]).unwrap()); - assert_ne!(factor1, Hmac::from_slice(&[0xFFu8; 32]).unwrap()); - assert_ne!(&factor1[..], &tag[..]); - assert_ne!(&factor1[..], &msg[..]); - - // Verify that the key was indeed tweaked - assert_ne!(pk, original); - - // Verify that the set updated - assert_ne!(original_keyset.clone(), keyset); - // ... but only original key is touched - let mut set = keyset.clone(); - set.remove(&pk); - set.insert(original); - assert_eq!(set, original_keyset); - - // Do commitment by hand - let mut engine = - HmacEngine::::new(&original.serialize()); - engine.input(&LNPBP1_HASHED_TAG); - engine.input(&tag.into_inner()); - engine.input(msg); - let hmac = Hmac::from_engine(engine); - let tweaking_factor = *hmac.as_inner(); - let mut altkey = original; - altkey - .add_exp_assign(&secp256k1::SECP256K1, &tweaking_factor[..]) - .unwrap(); - // It must not match because done with a single key, not - // their sum - assert_ne!(altkey, pk); - - // Now try commitment with a different key, but the same - // data - if other_key != original { - let mut other_pk = other_key; - let mut other_keyset = original_keyset.clone(); - assert!(!other_keyset.contains(&other_pk)); - other_keyset.remove(&pk); - other_keyset.insert(other_pk); - let factor3 = - commit(&mut other_keyset, &mut other_pk, &tag, &msg) - .unwrap(); - - // Make sure we commit to the key value - assert_ne!(factor1, factor3); - - // Make sure commitment value is not the same - assert_ne!(pk, other_pk); - - // Make sure we can't cross-verify - assert_eq!( - verify( - other_pk, - &bset![original], - original, - &tag, - &msg - ), - false - ); - assert_eq!( - verify( - other_pk, - &original_keyset, - original, - &tag, - &msg - ), - false - ); - } - - // Verify commitment - assert!(verify(pk, &original_keyset, original, &tag, &msg)); - - // Make sure we can't cross-verify with a single key in a set - assert_eq!( - verify(pk, &bset![original], original, &tag, &msg), - false - ); - - // Make sure we can't cross-verify with different tag - assert_eq!( - verify(pk, &original_keyset, original, &tag2, &msg), - false - ); - - // Make sure we can't cross-verify with different message - assert_eq!( - verify( - pk, - &original_keyset, - original, - &tag2, - &b"some other message" - ), - false - ); - } - } - } - - #[test] - #[should_panic(expected = "NotKeysetMember")] - fn test_failure_not_in_keyset() { - let tag = sha256::Hash::hash(b"ProtoTag"); - let all_keys = gen_secp_pubkeys(6); - let mut pk = all_keys[0]; - let mut keyset: BTreeSet<_> = - all_keys[1..].to_vec().into_iter().collect(); - let _ = commit(&mut keyset, &mut pk, &tag, b"Message").unwrap(); - } - - #[test] - #[should_panic(expected = "SumInfiniteResult")] - fn test_crafted_negation() { - let tag = sha256::Hash::hash(b"ProtoTag"); - let mut pubkey = secp256k1::PublicKey::from_str( - "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(); - let negkey = secp256k1::PublicKey::from_str( - "0318845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(); - let mut keyset = bset![pubkey, negkey]; - let _ = commit(&mut keyset, &mut pubkey, &tag, b"Message").unwrap(); - } -} diff --git a/dbc-legacy/src/pktweak/lockscript.rs b/dbc-legacy/src/pktweak/lockscript.rs deleted file mode 100644 index 0d31332a..00000000 --- a/dbc-legacy/src/pktweak/lockscript.rs +++ /dev/null @@ -1,503 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! # LNPBP-2 -//! -//! Module implementing LNPBP-2 standard: -//! Deterministic embedding of LNPBP1-type commitments into `scriptPubkey` of a -//! transaction output -//! [LNPBP-2](https://github.com/LNP-BP/lnpbps/blob/master/lnpbp-0002.md) -//! -//! The standard defines an algorithm for deterministic embedding and -//! verification of cryptographic commitments based on elliptic-curve public and -//! private key modifications (tweaks) inside all the existing types of Bitcoin -//! transaction output and arbitrary complex Bitcoin scripts. - -use core::cell::RefCell; -use std::collections::{BTreeSet, HashSet}; - -use bitcoin::hashes::{hash160, sha256, Hmac}; -use bitcoin::{secp256k1, PubkeyHash}; -use bitcoin_scripts::LockScript; -use commit_verify::EmbedCommitVerify; -use miniscript::Segwitv0; - -use super::{KeysetCommitment, KeysetContainer}; -use crate::{Container, Error, Proof}; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct LockscriptContainer { - pub script: LockScript, - pub pubkey: secp256k1::PublicKey, - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, - /// Tweaking factor stored after [`LockscriptCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl Container for LockscriptContainer { - /// Out supplement is a protocol-specific tag in its hashed form - type Supplement = sha256::Hash; - - type Host = Option<()>; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - _: &Self::Host, - ) -> Result { - if let ScriptEncodeData::LockScript(ref script) = proof.source { - Ok(Self { - pubkey: proof.pubkey, - script: script.clone(), - tag: *supplement, - tweaking_factor: None, - }) - } else { - Err(Error::InvalidProofStructure) - } - } - - #[inline] - fn deconstruct(self) -> (Proof, Self::Supplement) { - ( - Proof { - source: ScriptEncodeData::LockScript(self.script), - pubkey: self.pubkey, - }, - self.tag, - ) - } - - #[inline] - fn to_proof(&self) -> Proof { - Proof { - source: ScriptEncodeData::LockScript(self.script.clone()), - pubkey: self.pubkey, - } - } - - #[inline] - fn into_proof(self) -> Proof { - Proof { - source: ScriptEncodeData::LockScript(self.script), - pubkey: self.pubkey, - } - } -} - -/// [`LockScript`] containing public keys which sum is commit to some message -/// according to LNPBP-2 -#[derive( - Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, - Display, From -)] -#[display(inner)] -#[wrapper(LowerHex, UpperHex)] -pub struct LockscriptCommitment(LockScript); - -impl EmbedCommitVerify for LockscriptCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = LockscriptContainer; - type Error = Error; - - /// Function implements commitment procedure according to LNPBP-2. - /// - /// ## LNPBP-2 Specification extract: - /// - /// 1. The provided script MUST be parsed with Miniscript parser; if the - /// parser fails the procedure MUST fail. - /// 2. Iterate over all branches of the abstract syntax tree generated by - /// the Miniscript parser, running the following algorithm for each node: - /// - if a public key hash is met (`pk_h` Miniscript command) and it - /// can't be resolved against known public keys or other public keys - /// extracted from the script, fail the procedure; - /// - if a public key is found (`pk`) add it to the list of the collected - /// public keys; - /// - for all other types of Miniscript commands iterate over their - /// branches. - /// 3. Select unique public keys (i.e. if some public key is repeated in - /// different parts of the script/in different script branches, pick a - /// single instance of it). Compressed and uncompressed versions of the - /// same public key must be treaded as the same public key under this - /// procedure. - /// 4. If no public keys were found fail the procedure; return the collected - /// keys otherwise. - /// - /// **NB: SUBJECT TO CHANGE UPON RELEASE** - /// By "miniscript" we mean usage of `rust-miniscript` library at commit - /// `a5ba1219feb8b5a289c8f12176d632635eb8a959` - /// which may be found on - /// - // #[consensus_critical] - // #[standard_critical("LNPBP-1")] - fn embed_commit( - container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let original_hash = - bitcoin::PublicKey::new(container.pubkey).pubkey_hash(); - - let (keys, hashes) = - container.script.extract_pubkey_hash_set::()?; - if keys.is_empty() && hashes.is_empty() { - return Err(Error::LockscriptContainsNoKeys); - } - - let mut key_hashes: HashSet = - keys.iter().map(bitcoin::PublicKey::pubkey_hash).collect(); - key_hashes.insert(original_hash); - let keys: BTreeSet<_> = keys.into_iter().map(|pk| pk.key).collect(); - - if hashes.is_empty() { - keys.get(&container.pubkey) - .ok_or(Error::LockscriptKeyNotFound)?; - } else if hashes.into_iter().any(|hash| !key_hashes.contains(&hash)) { - return Err(Error::LockscriptContainsUnknownHashes); - } - - let mut keyset_container = KeysetContainer { - pubkey: container.pubkey, - keyset: keys, - tag: container.tag, - tweaking_factor: None, - }; - - let tweaked_pubkey = - KeysetCommitment::embed_commit(&mut keyset_container, msg)?; - - container.tweaking_factor = keyset_container.tweaking_factor; - - let tweaked_hash = - bitcoin::PublicKey::new(*tweaked_pubkey).pubkey_hash(); - - let found = RefCell::new(0); - - // ! [CONSENSUS-CRITICAL]: - // ! [STANDARD-CRITICAL]: Iterate over all branches of the abstract - // syntax tree generated by the Miniscript - // parser, running the following - // algorithm for each node: - let lockscript = container - .script - .replace_pubkeys_and_hashes::( - |pubkey: &bitcoin::PublicKey| match pubkey.key - == container.pubkey - { - true => { - *found.borrow_mut() += 1; - bitcoin::PublicKey::new(*tweaked_pubkey) - } - false => *pubkey, - }, - |hash: &hash160::Hash| match *hash == original_hash.as_hash() { - true => { - *found.borrow_mut() += 1; - tweaked_hash.as_hash() - } - false => *hash, - }, - )?; - - Ok(lockscript.into()) - } -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use bitcoin::hashes::{hash160, sha256, Hash}; - use miniscript::{Miniscript, Segwitv0}; - - use super::*; - use crate::Error; - - macro_rules! ms_str { - ($($arg:tt)*) => (Miniscript::::from_str_insane(&format!($($arg)*)).unwrap()) - } - - macro_rules! policy_str { - ($($arg:tt)*) => (miniscript::policy::Concrete::::from_str(&format!($($arg)*)).unwrap()) - } - - fn pubkeys(n: usize) -> Vec { - let mut ret = Vec::with_capacity(n); - let mut sk = [0; 32]; - for i in 1..n + 1 { - sk[0] = i as u8; - sk[1] = (i >> 8) as u8; - sk[2] = (i >> 16) as u8; - - let pk = - bitcoin::PublicKey::new(secp256k1::PublicKey::from_secret_key( - &secp256k1::SECP256K1, - &secp256k1::SecretKey::from_slice(&sk[..]) - .expect("secret key"), - )); - ret.push(pk); - } - ret - } - - fn gen_test_data( - ) -> (Vec, Vec, Vec) { - let keys = pubkeys(13); - let key_hashes = - keys.iter().map(bitcoin::PublicKey::pubkey_hash).collect(); - let dummy_hashes = (1..13) - .map(|i| hash160::Hash::from_inner([i; 20])) - .collect(); - (keys, key_hashes, dummy_hashes) - } - - #[test] - fn test_no_keys_and_hashes() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, dummy_hashes) = gen_test_data(); - let sha_hash = sha256::Hash::hash(&"(nearly)random string".as_bytes()); - - let ms = vec![ - ms_str!("older(921)"), - ms_str!("sha256({})", sha_hash), - ms_str!("hash256({})", sha_hash), - ms_str!("hash160({})", dummy_hashes[0]), - ms_str!("ripemd160({})", dummy_hashes[1]), - ms_str!("hash160({})", dummy_hashes[2]), - ]; - - ms.into_iter() - .map(|ms: Miniscript<_, _>| LockScript::from(ms.encode())) - .for_each(|ls| { - assert_eq!( - LockscriptCommitment::embed_commit( - &mut LockscriptContainer { - script: ls, - pubkey: keys[0].key, - tag, - tweaking_factor: None - }, - &"Test message" - ) - .err(), - Some(Error::LockscriptContainsNoKeys) - ); - }); - } - - #[test] - fn test_unknown_key() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let mut uncompressed = keys[5]; - uncompressed.compressed = false; - let ms = vec![ - ms_str!("c:pk_k({})", keys[1]), - ms_str!("c:pk_k({})", keys[2]), - ms_str!("c:pk_k({})", keys[3]), - ms_str!("c:pk_k({})", keys[4]), - //ms_str!("c:pk({})", uncompressed), - ]; - - ms.into_iter() - .map(|ms| LockScript::from(ms.encode())) - .for_each(|ls| { - assert_eq!( - LockscriptCommitment::embed_commit( - &mut LockscriptContainer { - script: ls, - pubkey: keys[0].key, - tag, - tweaking_factor: None - }, - &"Test message" - ) - .err(), - Some(Error::LockscriptKeyNotFound) - ); - }); - } - - #[test] - fn test_unknown_hash() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let ms = vec![ - ms_str!("c:pk_h({})", keys[1].pubkey_hash()), - ms_str!("c:pk_h({})", keys[2].pubkey_hash()), - ms_str!("c:pk_h({})", keys[3].pubkey_hash()), - ms_str!("c:pk_h({})", keys[4].pubkey_hash()), - ]; - - ms.into_iter() - .map(|ms| LockScript::from(ms.encode())) - .for_each(|ls| { - assert_eq!( - LockscriptCommitment::embed_commit( - &mut LockscriptContainer { - script: ls, - pubkey: keys[0].key, - tag, - tweaking_factor: None - }, - &"Test message" - ) - .err(), - Some(Error::LockscriptContainsUnknownHashes) - ); - }); - } - - #[test] - fn test_known_key() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let mut uncompressed = keys[5]; - uncompressed.compressed = false; - let ms = vec![ - ms_str!("c:pk_k({})", keys[0]), - ms_str!("c:pk_k({})", keys[1]), - ms_str!("c:pk_k({})", keys[2]), - ms_str!("c:pk_k({})", keys[3]), - //ms_str!("c:pk_k({})", uncompressed), - ]; - - ms.into_iter() - .map(|ms| LockScript::from(ms.encode())) - .enumerate() - .for_each(|(idx, ls)| { - let container = LockscriptContainer { - script: ls, - pubkey: keys[idx].key, - tag, - tweaking_factor: None, - }; - let msg = "Test message"; - let commitment = LockscriptCommitment::embed_commit( - &mut container.clone(), - &msg, - ) - .unwrap(); - assert!(commitment.verify(&container, &msg).unwrap()); - }); - } - - #[test] - fn test_known_hash() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let ms = vec![ - ms_str!("c:pk_h({})", keys[0].pubkey_hash()), - ms_str!("c:pk_h({})", keys[1].pubkey_hash()), - ms_str!("c:pk_h({})", keys[2].pubkey_hash()), - ms_str!("c:pk_h({})", keys[3].pubkey_hash()), - ]; - - ms.into_iter() - .map(|ms| LockScript::from(ms.encode())) - .enumerate() - .for_each(|(idx, ls)| { - let container = LockscriptContainer { - script: ls, - pubkey: keys[idx].key, - tag, - tweaking_factor: None, - }; - let msg = "Test message"; - let commitment = LockscriptCommitment::embed_commit( - &mut container.clone(), - &msg, - ) - .unwrap(); - assert!(commitment.verify(&container, &msg).unwrap()) - }); - } - - #[test] - fn test_multisig() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let ms: Vec> = vec![ - policy_str!("thresh(2,pk({}),pk({}))", keys[0], keys[1],), - policy_str!( - "thresh(3,pk({}),pk({}),pk({}),pk({}),pk({}))", - keys[0], - keys[1], - keys[2], - keys[3], - keys[4] - ), - ] - .into_iter() - .map(|p| p.compile().unwrap()) - .collect(); - - ms.into_iter() - .map(|ms| LockScript::from(ms.encode())) - .for_each(|ls| { - let container = LockscriptContainer { - script: ls, - pubkey: keys[1].key, - tag, - tweaking_factor: None, - }; - let msg = "Test message"; - let commitment = LockscriptCommitment::embed_commit( - &mut container.clone(), - &msg, - ) - .unwrap(); - assert!(commitment.verify(&container, &msg).unwrap()) - }); - } - - #[test] - fn test_complex_scripts_unique_key() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let (keys, _, _) = gen_test_data(); - - let ms = policy_str!( - "or(thresh(3,pk({}),pk({}),pk({})),and(thresh(2,pk({}),pk({})),\ - older(10000)))", - keys[0], - keys[1], - keys[2], - keys[3], - keys[4], - ) - .compile::() - .unwrap(); - - let container = LockscriptContainer { - script: LockScript::from(ms.encode()), - pubkey: keys[1].key, - tag, - tweaking_factor: None, - }; - let msg = "Test message"; - let commitment = - LockscriptCommitment::embed_commit(&mut container.clone(), &msg) - .unwrap(); - assert!(commitment.verify(&container, &msg).unwrap()) - } -} diff --git a/dbc-legacy/src/pktweak/mod.rs b/dbc-legacy/src/pktweak/mod.rs deleted file mode 100644 index 78e2f675..00000000 --- a/dbc-legacy/src/pktweak/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -#[cfg(feature = "miniscript")] -pub mod keyset; -pub mod lnpbp1; -#[cfg(feature = "miniscript")] -pub mod lockscript; -pub mod pubkey; -#[cfg(feature = "miniscript")] -pub mod scriptpubkey; -pub mod taproot; -#[cfg(feature = "miniscript")] -pub mod txout; - -use bitcoin::hashes::sha256; -use bitcoin_scripts::LockScript; -#[cfg(feature = "miniscript")] -pub use keyset::{KeysetCommitment, KeysetContainer}; -#[cfg(feature = "miniscript")] -pub use lockscript::{LockscriptCommitment, LockscriptContainer}; -pub use pubkey::{PubkeyCommitment, PubkeyContainer}; -#[cfg(feature = "miniscript")] -pub use scriptpubkey::{ - ScriptEncodeData, ScriptEncodeMethod, SpkCommitment, SpkContainer, -}; -pub use taproot::{TaprootCommitment, TaprootContainer}; -#[cfg(feature = "miniscript")] -pub use txout::{TxoutCommitment, TxoutContainer}; - -/// Structure keeping the minimum of information (bytewise) required to verify -/// deterministic bitcoin commitment given only the transaction source, its -/// fee and protocol-specific constants. It is a part of the [`Proof`] data. -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] -#[derive(StrictEncode, StrictDecode)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[display(doc_comments)] -pub enum ScriptEncodeData { - /// Public key. Since we keep the original public key as a part of a proof, - /// and value of the tweaked key can be reconstructed with DBC source data - /// and the original pubkey, so we do not need to keep any additional data - /// here). - SinglePubkey, - - /// Any output containing script information, aside from OP_RETURN outputs - /// (using [`ScriptEncodeData::SinglePubkey`]) and tapscript. - /// We have to store full original script in it's byte form since when - /// the deteministic bitcoin commitment is verified, the output may be - /// still unspent and we will not be able to reconstruct the script without - /// this data kept in the client-validated part. - LockScript(LockScript), - - // TODO: Add `WrappedWitnessScript(WitnessScript) variant - /// Taproot-based outputs. We need to keep only the hash of the taprscript - /// merkle tree root. - Taproot(sha256::Hash), -} diff --git a/dbc-legacy/src/pktweak/pubkey.rs b/dbc-legacy/src/pktweak/pubkey.rs deleted file mode 100644 index 44964855..00000000 --- a/dbc-legacy/src/pktweak/pubkey.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use bitcoin::hashes::{sha256, Hmac}; -use commit_verify::EmbedCommitVerify; - -use super::lnpbp1; -use crate::{Container, Error, Proof}; - -/// Container for LNPBP-1 commitments. In order to be constructed, commitment -/// requires an original public key and a protocol-specific tag, which -/// must be hashed during commitment process. Here we use pre-hashed version -/// of the tag in order to maximize performance for multiple commitments. -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct PubkeyContainer { - /// The original public key: host for commitment - pub pubkey: secp256k1::PublicKey, - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, - /// Tweaking factor stored after [`PubkeyCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl Container for PubkeyContainer { - /// Our supplement is a protocol-specific tag in its hashed form - type Supplement = sha256::Hash; - /// Our proof contains the host, so we don't need host here - type Host = Option<()>; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - _: &Self::Host, - ) -> Result { - Ok(Self { - pubkey: proof.public_key()?, - tag: *supplement, - tweaking_factor: None, - }) - } - - #[inline] - fn deconstruct(self) -> (Proof, Self::Supplement) { - (Proof::from(self.pubkey), self.tag) - } - - // A proof for the LNPBP-1 public key commitment is the original public key - // value, so the commitment container (original public key) just returns a - // copy of itself - #[inline] - fn to_proof(&self) -> Proof { - Proof::from(self.pubkey) - } - - #[inline] - fn into_proof(self) -> Proof { - Proof::from(self.pubkey) - } -} - -/// Public key committed to some message via LNPBP1-based tweaking procedure -#[derive(Wrapper, Clone, PartialEq, Eq, Hash, Debug, Display, From)] -#[display("{0}", alt = "{_0:#}*")] -#[wrapper(FromStr, LowerHex)] -pub struct PubkeyCommitment(secp256k1::PublicKey); - -impl EmbedCommitVerify for PubkeyCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = PubkeyContainer; - type Error = lnpbp1::Error; - - // #[consensus_critical("RGB")] - // #[standard_critical("LNPBP-1")] - fn embed_commit( - pubkey_container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let mut keyset = bset![pubkey_container.pubkey]; - let mut pubkey = pubkey_container.pubkey; - - let tweaking_factor = lnpbp1::commit( - &mut keyset, - &mut pubkey, - &pubkey_container.tag, - msg, - )?; - - pubkey_container.tweaking_factor = Some(tweaking_factor); - - // Returning tweaked public key - Ok(PubkeyCommitment(pubkey)) - } -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use amplify::hex::ToHex; - use amplify::Wrapper; - use bitcoin::hashes::{sha256, Hash}; - - use super::lnpbp1::test_helpers::*; - use super::*; - - #[test] - fn test_pubkey_commitment() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - gen_secp_pubkeys(9).into_iter().for_each(|pubkey| { - embed_commit_verify_suite::, PubkeyCommitment>( - gen_messages(), - &mut PubkeyContainer { - pubkey, - tag, - tweaking_factor: None, - }, - ); - }); - } - - #[test] - fn test_tweaking_results() { - let tag = sha256::Hash::hash(b"TEST_TAG"); - let msg = "test message"; - let pubkey = secp256k1::PublicKey::from_str( - "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(); - let commitment = PubkeyCommitment::embed_commit( - &mut PubkeyContainer { - pubkey, - tag, - tweaking_factor: None, - }, - &msg, - ) - .unwrap(); - assert_eq!( - commitment.as_inner().to_hex(), - "02de6531527f7a453e0b53e4b33a78c60f9bcdb69abbf59866e33de347ceda0bdf" - ); - } -} diff --git a/dbc-legacy/src/pktweak/scriptpubkey.rs b/dbc-legacy/src/pktweak/scriptpubkey.rs deleted file mode 100644 index 1067d122..00000000 --- a/dbc-legacy/src/pktweak/scriptpubkey.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use core::convert::TryFrom; - -use amplify::Wrapper; -use bitcoin::blockdata::script::Script; -use bitcoin::hashes::{sha256, Hmac}; -use bitcoin_scripts::convert::ToPubkeyScript; -use bitcoin_scripts::{Category, LockScript, PubkeyScript}; -use commit_verify::EmbedCommitVerify; - -use super::{ - LockscriptCommitment, LockscriptContainer, PubkeyCommitment, - PubkeyContainer, ScriptEncodeData, TaprootCommitment, TaprootContainer, -}; -use crate::{Container, Error, Proof}; - -/// Structure with a set of allowed transaction output-based commitment schema. -/// -/// Transaction output-based commitments can be created with a different -/// schemata, specific to a specific structure of `scriptPubkey`. Different -/// client-side-validation protocols, working with output-based commitments may -/// allow different forms of commitments; for instance RGBv1 requires that -/// only tapscript-based op_return commitments must be supported inside P2TR -/// outputs. This structure allows to specify the list of supported commitment -/// schemata as a set of flags. -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] -pub struct CommitmentSchema { - /// Tweaks of a single pubkey using LNPBP-1 schema inside P2PK output. - pub p2pk_tweak: bool, - - /// Tweaks of a single pubkey using LNPBP-1 schema inside P2PKH output. - pub p2pkh_tweak: bool, - - /// Tweaks of a single pubkey using LNPBP-1 schema inside P2WPKH output. - pub p2wpkh_tweak: bool, - - /// Tweaks of a single pubkey using LNPBP-1 schema inside a legacy - /// P2WPKH-in-P2SH output. - pub p2wpkh_sh_tweak: bool, - - /// Tweaks of a keyset according to LNPBP-1 schema inside a bare scripts - /// contained within `pubkeyScript`. The set of keys is extracted from the - /// script using LNPBP-2 schema. - pub bare_tweak: bool, - - /// Tweaks of a keyset according to LNPBP-1 schema inside a plain - /// (non-witness-nested) P2SH outputs. The set of keys is extracted from the - /// script using LNPBP-2 schema. - pub p2sh_tweak: bool, - - /// Tweaks of a keyset according to LNPBP-1 schema inside a non-nested/ - /// non-legacy P2WSH outputs. The set of keys is extracted from the - /// script using LNPBP-2 schema. - pub p2wsh_tweak: bool, - - /// Tweaks of a keyset according to LNPBP-1 schema inside the nested/ - /// legacy P2WSH-in-P2SH outputs. The set of keys is extracted from the - /// script using LNPBP-2 schema. - pub p2wsh_sh_tweak: bool, - - /// Commitment directly added to a single and alone `OP_RETURN` operation - /// contained in a bare `scriptPubkey` in transaction output. - pub p2pk_return: bool, - - /// Commitment put inside a single `OP_RETURN` tapscript code in the first - /// leaf of taproot script path spending according to LNPBP-6 schema. - pub p2tr_return: bool, -} - -impl CommitmentSchema { - /// Sets/clears flags for all commitment schemata based on tweaking of a - /// single public key (P2PK, P2PKH, P2WPKH, P2WPKH-in-P2SH). - pub fn update_pubkey_tweaks(&mut self, allow: bool) { - self.p2pk_tweak = allow; - self.p2pkh_tweak = allow; - self.p2wpkh_tweak = allow; - self.p2wpkh_sh_tweak = allow; - } - - /// Sets/clears flags for all commitment schemata based on tweaking of a - /// public key set extracted from a script (Bare, P2PSH, P2WSH, - /// P2WSH-in-P2SH). - pub fn update_script_tweaks(&mut self, allow: bool) { - self.bare_tweak = allow; - self.p2sh_tweak = allow; - self.p2wsh_tweak = allow; - self.p2wsh_sh_tweak = allow; - } -} - -/// Ephemeral/intermediary structure used as a container storing all data -/// participating in the creation or verification of a specific commitment. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct SpkContainer { - /// Extra-transaction proof of the commitment - pub proof: Proof, - - /// Set of allowed commitment schemata. - /// - /// The set is defined by the specific client-side-validation protocol. - pub allowed: CommitmentSchema, - - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, - - /// Tweaking factor stored after [`SpkCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl Container for SpkContainer { - /// Supplement is a protocol-specific tag in its hashed form and protocol- - /// defined set of allowed commitment schemata. - type Supplement = (sha256::Hash, CommitmentSchema); - - /// The host for the commitment is a `scriptPubkey` of a transaction output - /// which is a part of the [`Proof`], so we do not use it here. - type Host = (); - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - _host: &Self::Host, - ) -> Result { - Ok(SpkContainer { - proof: proof.clone(), - allowed: supplement.1, - tag: supplement.0, - tweaking_factor: None, - }) - } - - #[inline] - fn deconstruct(self) -> (Proof, Self::Supplement) { - (self.proof, (self.tag, self.allowed)) - } - - #[inline] - fn to_proof(&self) -> Proof { - self.proof.clone() - } - - #[inline] - fn into_proof(self) -> Proof { - self.proof - } -} - -/// [`PubkeyScript`] containing LNPBP-2 commitment -#[derive( - Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, - Display, From -)] -#[display(inner)] -#[wrapper(LowerHex, UpperHex)] -pub struct SpkCommitment(PubkeyScript); - -impl EmbedCommitVerify for SpkCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = SpkContainer; - type Error = super::Error; - - fn embed_commit( - container: &mut Self::Container, - msg: &MSG, - ) -> Result { - } -} diff --git a/dbc-legacy/src/pktweak/taproot.rs b/dbc-legacy/src/pktweak/taproot.rs deleted file mode 100644 index 0558d57a..00000000 --- a/dbc-legacy/src/pktweak/taproot.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use amplify::Wrapper; -use bitcoin::hashes::{sha256, Hmac}; -use bitcoin::{secp256k1, XOnlyPublicKey}; -use commit_verify::EmbedCommitVerify; - -use super::{PubkeyCommitment, PubkeyContainer}; -use crate::{Container, Error, Proof}; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct TaprootContainer { - pub script_root: sha256::Hash, - pub intermediate_key: XOnlyPublicKey, - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, - /// Tweaking factor stored after [`TaprootCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl Container for TaprootContainer { - /// Out supplement is a protocol-specific tag in its hashed form - type Supplement = sha256::Hash; - /// Our proof contains the host, so we don't need host here - type Host = Option<()>; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - _: &Self::Host, - ) -> Result { - match proof { - Proof::XOnlyKeyTaproot { - internal_key, - merkle_subroot, - } => Ok(Self { - script_root: *merkle_subroot, - intermediate_key: XOnlyPublicKey::from_slice( - internal_key.as_inner(), - ) - .map_err(|_| Error::InvalidProofStructure)?, - tag: *supplement, - tweaking_factor: None, - }), - _ => Err(Error::InvalidProofStructure), - } - } - - fn deconstruct(self) -> (Proof, Self::Supplement) { - (self.to_proof(), self.tag) - } - - #[inline] - fn to_proof(&self) -> Proof { - Proof::XOnlyKeyTaproot { - internal_key: self.intermediate_key.serialize().into(), - merkle_subroot: self.script_root, - } - } - - #[inline] - fn into_proof(self) -> Proof { - self.to_proof() - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct TaprootCommitment { - pub script_root: sha256::Hash, - pub intermediate_key_commitment: PubkeyCommitment, -} - -impl EmbedCommitVerify for TaprootCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = TaprootContainer; - type Error = Error; - - fn embed_commit( - container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let mut data: Vec = vec![0x02]; - data.extend(container.intermediate_key.serialize().iter()); - let pubkey = secp256k1::PublicKey::from_slice(&data).expect( - "Failed to construct 33 Publickey from 0x02 appended x-only key", - ); - - let mut pubkey_container = PubkeyContainer { - pubkey, - tag: container.tag, - tweaking_factor: None, - }; - - let cmt = PubkeyCommitment::embed_commit(&mut pubkey_container, msg)?; - - container.tweaking_factor = pubkey_container.tweaking_factor; - - Ok(Self { - script_root: container.script_root, - intermediate_key_commitment: cmt, - }) - } -} diff --git a/dbc-legacy/src/pktweak/txout.rs b/dbc-legacy/src/pktweak/txout.rs deleted file mode 100644 index 4fcc0ed8..00000000 --- a/dbc-legacy/src/pktweak/txout.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use amplify::Wrapper; -use bitcoin::hashes::{sha256, Hmac}; -use bitcoin::{secp256k1, TxOut}; -use bitcoin_scripts::PubkeyScript; -use commit_verify::EmbedCommitVerify; - -use super::{ - ScriptEncodeData, ScriptEncodeMethod, SpkCommitment, SpkContainer, -}; -use crate::{Container, Error, Proof}; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct TxoutContainer { - pub value: u64, - pub script_container: SpkContainer, - /// Tweaking factor stored after [`TxoutCommitment::embed_commit`] - /// procedure - pub tweaking_factor: Option>, -} - -impl TxoutContainer { - pub fn construct( - protocol_tag: &sha256::Hash, - value: u64, - pubkey: secp256k1::PublicKey, - source: ScriptEncodeData, - method: ScriptEncodeMethod, - ) -> Self { - Self { - value, - script_container: SpkContainer::construct( - protocol_tag, - pubkey, - source, - method, - ), - tweaking_factor: None, - } - } -} - -impl Container for TxoutContainer { - /// Out supplement is a protocol-specific tag in its hashed form - type Supplement = sha256::Hash; - type Host = TxOut; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - host: &Self::Host, - ) -> Result { - Ok(Self { - value: host.value, - script_container: SpkContainer::reconstruct( - proof, - supplement, - &PubkeyScript::from_inner(host.clone().script_pubkey), - )?, - tweaking_factor: None, - }) - } - - fn deconstruct(self) -> (Proof, Self::Supplement) { - self.script_container.deconstruct() - } - - fn to_proof(&self) -> Proof { - self.script_container.to_proof() - } - - fn into_proof(self) -> Proof { - self.script_container.into_proof() - } -} - -/// [`bitcoin::TxOut`] containing LNPBP-2 commitment -#[derive(Wrapper, Clone, PartialEq, Eq, Hash, Default, Debug, From)] -pub struct TxoutCommitment(TxOut); - -impl EmbedCommitVerify for TxoutCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = TxoutContainer; - type Error = Error; - - fn embed_commit( - container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let commitment = TxOut { - value: container.value, - script_pubkey: (**SpkCommitment::embed_commit( - &mut container.script_container, - msg, - )?) - .clone(), - }; - - container.tweaking_factor = container.script_container.tweaking_factor; - - Ok(commitment.into()) - } -} diff --git a/dbc-legacy/src/proof.rs b/dbc-legacy/src/proof.rs deleted file mode 100644 index 7c067a5f..00000000 --- a/dbc-legacy/src/proof.rs +++ /dev/null @@ -1,164 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use amplify::{Slice32, Wrapper}; -use bitcoin::hashes::sha256; - -use crate::Error; - -/// Extra-transaction proof of a deterministic bitcoin commitment. -/// -/// Encodes only extra-transaction data in the most compact form; without -/// information on which specific commitment type is used. The commitment type -/// must be determined by the protocol data and transaction structure. -/// -/// Proof does not stores the actual key data internally not to consume -/// resources on validating elliptic key points each time the proof is -/// deserialized (client-side-validated data may contain many thousands of -/// proofs and such validation may significantly reduce performance). -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] -#[derive(StrictEncode, StrictDecode)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[display("proof({pubkey}, {source}")] -pub enum Proof { - /// No extra-transaction data are required - #[strict_encoding(value = 0x01)] - Embedded, - - /// Extra-transaction proof consists of a single public key. - /// - /// This variant covers even public keys, prefixed with `0x02` as a part of - /// the proof type data. - #[strict_encoding(value = 0x02)] - EvenKey(Slice32), - - /// Extra-transaction proof consists of a single public key. - /// - /// This variant covers odd public keys, prefixed with `0x03` as a part of - /// the proof type data. - #[strict_encoding(value = 0x03)] - OddKey(Slice32), - - // 0x04 encoding is skipped to distinguish with a stored uncompressed keys - /// Extra-transaction proof consists of a single public key as a part of - /// P2WPKH-in-P2SH nested legacy structure. - /// - /// This variant covers even public keys. - #[strict_encoding(value = 0x05)] - NestedEvenKey(Slice32), - - /// Extra-transaction proof consists of a single public key as a part of - /// P2WPKH-in-P2SH nested legacy structure. - /// - /// This variant covers odd public keys. - #[strict_encoding(value = 0x06)] - NestedOddKey(Slice32), - - /// Extra-transaction proof consists of a single public key and script. - /// - /// This variant covers even public keys. - #[strict_encoding(value = 0x07)] - ScriptEvenKey { - target_key: Slice32, - script: Box<[u8]>, - }, - - /// Extra-transaction proof consists of a single public key and script. - /// - /// This variant covers odd public keys. - #[strict_encoding(value = 0x08)] - ScriptOddKey { - target_key: Slice32, - script: Box<[u8]>, - }, - - /// Extra-transaction proof consists of a single public key and witness - /// script structured as P2WSH-in-P2SH nested legacy structure. - /// - /// This variant covers even public keys. - #[strict_encoding(value = 0x09)] - NestedScriptEvenKey { - target_key: Slice32, - script: Box<[u8]>, - }, - - /// Extra-transaction proof consists of a single public key and witness - /// script structured as P2WSH-in-P2SH nested legacy structure. - /// - /// This variant covers odd public keys. - #[strict_encoding(value = 0x10)] - NestedScriptOddKey { - target_key: Slice32, - script: Box<[u8]>, - }, - - /// Extra-transaction proof consists of a taproot internal public key and - /// a merkle proof for a second branch of the merkle tree in the taproot - /// script tree. - #[strict_encoding(value = 0x11)] - XOnlyKeyTaproot { - internal_key: Slice32, - merkle_subroot: sha256::Hash, - }, -} - -impl From for Proof { - fn from(pubkey: secp256k1::PublicKey) -> Self { - let data = pubkey.serialize(); - let inner = - Slice32::from_slice(&data[1..]).expect("fixed-length slice"); - match data[0] { - 0x02 => Proof::EvenKey(inner), - 0x03 => Proof::OddKey(inner), - _ => unreachable!( - "Secp256k1 public key with non-0x02 and non-0x03 prefix" - ), - } - } -} - -impl Proof { - pub fn public_key(&self) -> Result { - let mut data: Vec = Vec::with_capacity(32); - - match self { - Proof::Embedded => return Err(Error::InvalidProofStructure), - Proof::EvenKey(pk) - | Proof::NestedEvenKey(pk) - | Proof::ScriptEvenKey { target_key: pk, .. } - | Proof::NestedScriptEvenKey { target_key: pk, .. } - | Proof::XOnlyKeyTaproot { - internal_key: pk, .. - } => { - data.extend([0x02]); - data.extend(pk.as_inner()); - } - Proof::OddKey(pk) - | Proof::NestedOddKey(pk) - | Proof::ScriptOddKey { target_key: pk, .. } - | Proof::NestedScriptOddKey { target_key: pk, .. } => { - data.extend([0x03]); - data.extend(pk.as_inner()); - } - } - - Ok(secp256k1::PublicKey::from_slice(&data) - .expect("fixed-size public key")) - } -} diff --git a/dbc-legacy/src/sigtweak/mod.rs b/dbc-legacy/src/sigtweak/mod.rs deleted file mode 100644 index 72b75294..00000000 --- a/dbc-legacy/src/sigtweak/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . diff --git a/dbc-legacy/src/tapret/mod.rs b/dbc-legacy/src/tapret/mod.rs deleted file mode 100644 index f66ad8b6..00000000 --- a/dbc-legacy/src/tapret/mod.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! Taproot OP_RETURN-based deterministic commitments. -//! -//! Option + message -> merkle_path* - -use bitcoin::util::taproot::{TaprootMerkleBranch, TaprootSpendInfo}; -use commit_verify::EmbedCommitVerify; -use secp256k1::XOnlyPublicKey; - -pub struct TapretProtocol; - -pub enum TaprootCommitError { - TooDeepTree, -} - -impl EmbedCommitVerify for TaprootMerkleBranch { - type Proof = TaprootMerkleBranch; - type Protocol = TapretProtocol; - type CommitError = TaprootCommitError; - - fn embed_commit( - &mut self, - msg: &AnchorId, - ) -> Result { - let proof = self.clone(); - self.0.push(msg.as_ref()); - Ok(proof) - } -} - -impl EmbedCommitVerify for TaprootSpendInfo { - type Proof = (TaprootMerkleBranch, XOnlyPublicKey); - type Protocol = TapretProtocol; - type CommitError = TaprootCommitError; - - fn embed_commit( - &mut self, - msg: &AnchorId, - ) -> Result { - self.push_script() - } -} diff --git a/dbc-legacy/src/tx/feeproto.rs b/dbc-legacy/src/tx/feeproto.rs deleted file mode 100644 index a327a578..00000000 --- a/dbc-legacy/src/tx/feeproto.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use amplify::Wrapper; -use bitcoin::hashes::{sha256, Hmac}; -use bitcoin::{secp256k1, Transaction, TxOut}; -use commit_verify::EmbedCommitVerify; - -use crate::spk::{ScriptEncodeData, TxoutCommitment, TxoutContainer}; -use crate::{Container, Error, Proof}; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct TxContainer { - pub protocol_factor: u32, - pub fee: u64, - pub txout_container: TxoutContainer, - pub tx: Transaction, - /// Tweaking factor stored after [`TxCommitment::embed_commit`] procedure - pub tweaking_factor: Option>, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct TxSupplement { - pub protocol_factor: u32, - pub fee: u64, - /// Single SHA256 hash of the protocol-specific tag - pub tag: sha256::Hash, -} - -impl TxContainer { - pub fn construct( - protocol_factor: u32, - protocol_tag: &sha256::Hash, - fee: u64, - tx: Transaction, - pubkey: secp256k1::PublicKey, - source: ScriptEncodeData, - method: ScriptEncodeMethod, - ) -> Self { - let mut me = Self { - tx, - fee, - protocol_factor, - txout_container: TxoutContainer::construct( - protocol_tag, - 0, - pubkey, - source, - method, - ), - tweaking_factor: None, - }; - me.txout_container.value = me.tx.output[me.vout()].value; - me - } - - pub fn vout(&self) -> usize { - let nouts = self.tx.output.len() as u16; - let vout = ((self.fee + (self.protocol_factor as u64)) % (nouts as u64)) - as u16; - vout as usize - } -} - -impl Container for TxContainer { - type Supplement = TxSupplement; - type Host = Transaction; - - fn reconstruct( - proof: &Proof, - supplement: &Self::Supplement, - host: &Self::Host, - ) -> Result { - let mut me = Self { - protocol_factor: supplement.protocol_factor, - fee: supplement.fee, - txout_container: TxoutContainer::reconstruct( - proof, - &supplement.tag, - &TxOut::default(), - )?, - tx: host.clone(), - tweaking_factor: None, - }; - me.txout_container = TxoutContainer::reconstruct( - proof, - &supplement.tag, - &host.output[me.vout()], - )?; - Ok(me) - } - - fn deconstruct(self) -> (Proof, Self::Supplement) { - ( - self.txout_container.clone().into_proof(), - TxSupplement { - protocol_factor: self.protocol_factor, - fee: self.fee, - tag: self.txout_container.script_container.tag, - }, - ) - } - - fn to_proof(&self) -> Proof { - self.txout_container.to_proof() - } - - fn into_proof(self) -> Proof { - self.txout_container.into_proof() - } -} - -/// [`bitcoin::Transaction`] containing LNPBP-3 commitment -#[derive(Wrapper, Clone, PartialEq, Eq, Hash, Debug, From)] -pub struct TxCommitment(Transaction); - -impl EmbedCommitVerify for TxCommitment -where - MSG: AsRef<[u8]>, -{ - type Container = TxContainer; - type Error = Error; - - fn embed_commit( - container: &mut Self::Container, - msg: &MSG, - ) -> Result { - let mut tx = container.tx.clone(); - - let txout_commitment = - TxoutCommitment::embed_commit(&mut container.txout_container, msg)?; - tx.output[container.vout()] = txout_commitment.into_inner(); - - container.tweaking_factor = container.txout_container.tweaking_factor; - - Ok(tx.into()) - } -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use bitcoin::consensus::encode::deserialize; - use bitcoin::hashes::hex::FromHex; - - use super::*; - use crate::spk::{ScriptEncodeData, ScriptEncodeMethod, SpkContainer}; - - #[test] - fn test_ability_to_commit() { - let tx = deserialize(Vec::from_hex( - "020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c9100000000\ - 6a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a802201395963\ - 2492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a\ - 7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818\ - dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffff\ - ffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d0\ - 4cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5a\ - b979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c58\ - 8ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b0000000000\ - 1976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a\ - 8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d47\ - 57de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03\ - b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80\ - 328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10d\ - a6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e\ - 10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322\ - a18b920a4dfa887d30700") - .unwrap().as_slice()).unwrap(); - - let mut container = TxContainer { - tx, - fee: 0, - protocol_factor: 0, - txout_container: TxoutContainer { - value: 0, - script_container: SpkContainer { - pubkey: secp256k1::PublicKey::from_str( - "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166", - ) - .unwrap(), - source: ScriptEncodeData::SinglePubkey, - method: ScriptEncodeMethod::PublicKey, - tag: Default::default(), - tweaking_factor: None, - }, - tweaking_factor: None, - }, - tweaking_factor: None, - }; - - let msg = "message to commit to"; - - let commitment = - TxCommitment::embed_commit(&mut container, &msg).unwrap(); - assert_eq!(commitment.verify(&container, &msg).unwrap(), true); - } -} diff --git a/dbc-legacy/src/tx/lnpbp3.rs b/dbc-legacy/src/tx/lnpbp3.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/dbc-legacy/src/tx/lnpbp9.rs b/dbc-legacy/src/tx/lnpbp9.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/dbc-legacy/src/tx/mod.rs b/dbc-legacy/src/tx/mod.rs deleted file mode 100644 index 6a3ddc30..00000000 --- a/dbc-legacy/src/tx/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -// `feeproto` requires support for tweaking arbitrary txout output; which -// implies requirement for miniscript-based script parsing for collecting -// all script keys from P2(W)SH outputs -#[cfg(feature = "miniscript")] -pub mod feeproto; diff --git a/dbc/Cargo.toml b/dbc/Cargo.toml index a5ff8719..c204e9cd 100644 --- a/dbc/Cargo.toml +++ b/dbc/Cargo.toml @@ -1,34 +1,38 @@ [package] name = "bp-dbc" -version = "0.9.0" -license = "Apache-2.0" -authors = ["Dr. Maxim Orlovsky "] +version = "0.10.0-beta.1" description = "Deterministic bitcoin commitments library" -repository = "https://github.com/LNP-BP/bp-core" -homepage = "https://github.com/LNP-BP" -keywords = ["lnp-bp", "bitcoin", "cryptography", "smart-contracts", "single-use-seals"] -categories = ["cryptography::cryptocurrencies", "encoding"] +keywords = ["lnp-bp", "bitcoin", "blockchain", "smart-contracts", "single-use-seals"] +categories = ["cryptography", "encoding"] +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } readme = "../README.md" -edition = "2021" -rust-version = "1.59.0" [lib] name = "dbc" path = "src/lib.rs" [dependencies] -amplify = "3.13.0" -bitcoin = "0.29.2" -secp256k1 = { version = "0.24.2", features = ["global-context", "rand-std"] } -bitcoin_scripts = "0.9.0" -psbt = { version = "0.9.0", default-features = false, optional = true } -strict_encoding = "0.9.0" -commit_verify = { version = "0.9.0", features = ["rand"] } +amplify = "4.0.0-beta.10" +bp-primitives = { version = "0.10.0-beta.1", path = "../primitives" } +secp256k1 = { version = "0.26.0", features = ["global-context", "rand-std"] } +strict_encoding = "2.0.0-beta.5" +commit_verify = { version = "0.10.0-beta.1", features = ["rand"] } serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } -serde_with = { version = "1.14", optional = true } [features] default = [] -all = ["serde", "wallet"] -wallet = ["psbt"] -serde = ["amplify/serde", "bitcoin/serde", "bitcoin_scripts/serde", "commit_verify/serde", "serde_crate", "serde_with"] +all = ["serde"] +serde = [ + "serde_crate", + "bp-primitives/serde", + "commit_verify/serde", + "secp256k1/serde" +] + +[package.metadata.docs.rs] +features = [ "all" ] diff --git a/dbc/src/anchor.rs b/dbc/src/anchor.rs index 55b89443..4805b51e 100644 --- a/dbc/src/anchor.rs +++ b/dbc/src/anchor.rs @@ -1,17 +1,23 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Anchors are data structures used in deterministic bitcoin commitments for //! keeping information about the proof of the commitment in connection to the @@ -19,83 +25,36 @@ //! defined by LNPBP-4. use std::cmp::Ordering; -use std::io::Write; - -use amplify::Wrapper; -use bitcoin::hashes::{sha256, sha256t}; -use bitcoin::{Script, Transaction, Txid}; -use commit_verify::convolve_commit::ConvolveCommitProof; -use commit_verify::lnpbp4::{self, Message, ProtocolId}; -use commit_verify::{ - CommitEncode, CommitVerify, ConsensusCommit, PrehashedProtocol, TaggedHash, -}; -#[cfg(feature = "wallet")] -use commit_verify::{EmbedCommitProof, EmbedCommitVerify, TryCommitVerify}; -#[cfg(feature = "wallet")] -use psbt::Psbt; -use strict_encoding::StrictEncode; - -#[cfg(feature = "wallet")] -use crate::tapret::{Lnpbp6, PsbtCommitError, PsbtVerifyError}; + +use amplify::{Bytes32, Wrapper}; +use bc::{ScriptPubkey, Tx, Txid, LIB_NAME_BP}; +use commit_verify::mpc::{self, Message, ProtocolId}; +use commit_verify::{strategies, CommitStrategy, CommitmentId, ConvolveCommitProof}; +use strict_encoding::{StrictDumb, StrictEncode}; + use crate::tapret::{TapretError, TapretProof}; /// Default depth of LNPBP-4 commitment tree pub const ANCHOR_MIN_LNPBP4_DEPTH: u8 = 3; -static MIDSTATE_ANCHOR_ID: [u8; 32] = [ - 148, 72, 59, 59, 150, 173, 163, 140, 159, 237, 69, 118, 104, 132, 194, 110, - 250, 108, 1, 140, 74, 248, 152, 205, 70, 32, 184, 87, 20, 102, 127, 20, -]; - -/// Tag used for [`AnchorId`] hash type -pub struct AnchorIdTag; - -impl sha256t::Tag for AnchorIdTag { - #[inline] - fn engine() -> sha256::HashEngine { - let midstate = sha256::Midstate::from_inner(MIDSTATE_ANCHOR_ID); - sha256::HashEngine::from_midstate(midstate, 64) - } -} - -/// Unique anchor identifier equivalent to the anchor commitment hash +/// Anchor identifier - a commitment to the anchor data. +#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)] +#[wrapper(Deref, BorrowSlice, Display, FromStr, Hex, Index, RangeOps)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate", transparent) )] -#[derive( - Wrapper, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, From -)] -#[wrapper( - Debug, Display, LowerHex, Index, IndexRange, IndexFrom, IndexTo, IndexFull -)] -pub struct AnchorId(sha256t::Hash); - -impl CommitVerify for AnchorId -where - Msg: AsRef<[u8]>, -{ - #[inline] - fn commit(msg: &Msg) -> AnchorId { AnchorId::hash(msg) } -} - -impl strict_encoding::Strategy for AnchorId { - type Strategy = strict_encoding::strategies::Wrapped; -} - -#[cfg(feature = "wallet")] -/// Errors working with anchors. -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)] -#[display(inner)] -pub enum Error { - /// Errors embedding LNPBP-4 commitment into PSBT +pub struct AnchorId( #[from] - EmbedCommit(PsbtCommitError), + #[from([u8; 32])] + Bytes32, +); - /// Errors constructing LNPBP-4 commitment - #[from] - Lnpbp4(lnpbp4::Error), +impl CommitStrategy for AnchorId { + type Strategy = strategies::Strict; } /// Errors verifying anchors. @@ -107,7 +66,7 @@ pub enum VerifyError { Tapret(TapretError), /// LNPBP-4 invalid proof. - #[from(lnpbp4::UnrelatedProof)] + #[from(mpc::UnrelatedProof)] Lnpbp4UnrelatedProtocol, } @@ -115,63 +74,45 @@ pub enum VerifyError { /// keeping information about the proof of the commitment in connection to the /// transaction which contains the commitment, and multi-protocol merkle tree as /// defined by LNPBP-4. -#[derive(Clone, PartialEq, Eq, Debug, StrictEncode, StrictDecode)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -pub struct Anchor { +#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct Anchor { /// Transaction containing deterministic bitcoin commitment. pub txid: Txid, /// Structured multi-protocol LNPBP-4 data the transaction commits to. - pub lnpbp4_proof: L, + pub mpc_proof: L, /// Proof of the DBC commitment. pub dbc_proof: Proof, } -impl CommitEncode for Anchor { - fn commit_encode(&self, mut e: E) -> usize { - let mut len = self - .txid - .strict_encode(&mut e) - .expect("memory encoders do not fail"); - len += self - .dbc_proof - .strict_encode(&mut e) - .expect("memory encoders do not fail"); - len + self.lnpbp4_proof.commit_encode(e) - } +impl CommitStrategy for Anchor { + type Strategy = strategies::Strict; } -impl ConsensusCommit for Anchor { - type Commitment = AnchorId; +impl CommitmentId for Anchor { + const TAG: [u8; 32] = *b"urn:lnpbp:lnpbp0011:anchor:v01#A"; + type Id = AnchorId; } -impl Ord for Anchor { - fn cmp(&self, other: &Self) -> Ordering { - self.anchor_id().cmp(&other.anchor_id()) - } +impl Ord for Anchor { + fn cmp(&self, other: &Self) -> Ordering { self.anchor_id().cmp(&other.anchor_id()) } } -impl PartialOrd for Anchor { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } +impl PartialOrd for Anchor { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } /// Error merging two [`Anchor`]s. -#[derive( - Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error, - From -)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error, From)] #[display(doc_comments)] pub enum MergeError { /// Error merging two LNPBP-4 proofs, which are unrelated. #[display(inner)] - #[from(lnpbp4::UnrelatedProof)] + #[from(mpc::UnrelatedProof)] Lnpbp4Mismatch, /// anchors can't be merged since they have different witness transactions @@ -181,34 +122,20 @@ pub enum MergeError { ProofMismatch, } -impl Anchor { +impl Anchor { /// Returns id of the anchor (commitment hash). #[inline] - pub fn anchor_id(&self) -> AnchorId { self.consensus_commit() } - - /// Convenience constructor for anchor, which also does embedding of LNPBP4 - /// commitment into PSBT. - #[cfg(feature = "wallet")] - pub fn commit( - psbt: &mut Psbt, - ) -> Result, Error> { - let anchor = psbt.embed_commit(&PsbtEmbeddedMessage)?; - Ok(Anchor { - txid: anchor.txid, - lnpbp4_proof: lnpbp4::MerkleBlock::from(anchor.lnpbp4_proof), - dbc_proof: anchor.dbc_proof, - }) - } + pub fn anchor_id(&self) -> AnchorId { self.commitment_id() } } -impl Anchor { +impl Anchor { /// Returns id of the anchor (commitment hash). #[inline] pub fn anchor_id( &self, protocol_id: impl Into, message: Message, - ) -> Result { + ) -> Result { Ok(self.to_merkle_block(protocol_id, message)?.anchor_id()) } @@ -217,15 +144,11 @@ impl Anchor { self, protocol_id: impl Into, message: Message, - ) -> Result, lnpbp4::UnrelatedProof> { - let lnpbp4_proof = lnpbp4::MerkleBlock::with( - &self.lnpbp4_proof, - protocol_id.into(), - message, - )?; + ) -> Result, mpc::UnrelatedProof> { + let lnpbp4_proof = mpc::MerkleBlock::with(&self.mpc_proof, protocol_id.into(), message)?; Ok(Anchor { txid: self.txid, - lnpbp4_proof, + mpc_proof: lnpbp4_proof, dbc_proof: self.dbc_proof, }) } @@ -235,7 +158,7 @@ impl Anchor { &self, protocol_id: impl Into, message: Message, - ) -> Result, lnpbp4::UnrelatedProof> { + ) -> Result, mpc::UnrelatedProof> { self.clone().into_merkle_block(protocol_id, message) } @@ -245,13 +168,10 @@ impl Anchor { &self, protocol_id: impl Into, message: Message, - tx: Transaction, + tx: Tx, ) -> Result { self.dbc_proof - .verify( - &self.lnpbp4_proof.convolve(protocol_id.into(), message)?, - tx, - ) + .verify(&self.mpc_proof.convolve(protocol_id.into(), message)?, tx) .map_err(VerifyError::from) } @@ -261,18 +181,18 @@ impl Anchor { &self, protocol_id: impl Into, message: Message, - ) -> Result { - self.lnpbp4_proof.convolve(protocol_id.into(), message) + ) -> Result { + self.mpc_proof.convolve(protocol_id.into(), message) } } -impl Anchor { +impl Anchor { /// Conceals all LNPBP-4 data except specific protocol and produces merkle /// proof anchor. pub fn to_merkle_proof( &self, protocol: impl Into, - ) -> Result, lnpbp4::LeafNotKnown> { + ) -> Result, mpc::LeafNotKnown> { self.clone().into_merkle_proof(protocol) } @@ -281,12 +201,11 @@ impl Anchor { pub fn into_merkle_proof( self, protocol: impl Into, - ) -> Result, lnpbp4::LeafNotKnown> { - let lnpbp4_proof = - self.lnpbp4_proof.to_merkle_proof(protocol.into())?; + ) -> Result, mpc::LeafNotKnown> { + let lnpbp4_proof = self.mpc_proof.to_merkle_proof(protocol.into())?; Ok(Anchor { txid: self.txid, - lnpbp4_proof, + mpc_proof: lnpbp4_proof, dbc_proof: self.dbc_proof, }) } @@ -295,8 +214,8 @@ impl Anchor { pub fn conceal_except( &mut self, protocols: impl AsRef<[ProtocolId]>, - ) -> Result { - self.lnpbp4_proof.conceal_except(protocols) + ) -> Result { + self.mpc_proof.conceal_except(protocols) } /// Merges two anchors keeping revealed data. @@ -307,112 +226,21 @@ impl Anchor { if self.dbc_proof != other.dbc_proof { return Err(MergeError::ProofMismatch); } - self.lnpbp4_proof.merge_reveal(other.lnpbp4_proof)?; + self.mpc_proof.merge_reveal(other.mpc_proof)?; Ok(self) } } -/// Empty type indicating that the message has to be taken from PSBT proprietary -/// keys -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -pub struct PsbtEmbeddedMessage; - -impl CommitEncode for PsbtEmbeddedMessage { - fn commit_encode(&self, _: E) -> usize { 0 } -} - -#[cfg(feature = "wallet")] -impl EmbedCommitProof - for Anchor -{ - fn restore_original_container( - &self, - psbt: &Psbt, - ) -> Result { - match self.dbc_proof { - Proof::OpretFirst => Ok(psbt.clone()), - Proof::TapretFirst(ref proof) => { - let mut psbt = psbt.clone(); - for output in &mut psbt.outputs { - if output.is_tapret_host() { - *output = EmbedCommitProof::<_, psbt::Output, Lnpbp6>::restore_original_container(proof, output)?; - return Ok(psbt); - } - } - Err(PsbtVerifyError::Commit( - PsbtCommitError::CommitmentImpossible, - )) - } - } - } -} - -#[cfg(feature = "wallet")] -impl EmbedCommitVerify for Psbt { - type Proof = Anchor; - type CommitError = PsbtCommitError; - type VerifyError = PsbtVerifyError; - - fn embed_commit( - &mut self, - _: &PsbtEmbeddedMessage, - ) -> Result { - let lnpbp4_tree = - |output: &mut psbt::Output| -> Result<_, PsbtCommitError> { - let messages = output.lnpbp4_message_map()?; - let min_depth = output - .lnpbp4_min_tree_depth()? - .unwrap_or(ANCHOR_MIN_LNPBP4_DEPTH); - let multi_source = lnpbp4::MultiSource { - min_depth, - messages, - }; - Ok(lnpbp4::MerkleTree::try_commit(&multi_source)?) - }; - - let (dbc_proof, lnpbp4_proof) = if let Some(output) = - self.outputs.iter_mut().find(|o| o.is_tapret_host()) - { - let tree = lnpbp4_tree(output)?; - let commitment = tree.consensus_commit(); - let proof = output.embed_commit(&commitment)?; - output.set_tapret_commitment(commitment.into_array(), &proof)?; - output.set_lnpbp4_entropy(tree.entropy())?; - (Proof::TapretFirst(proof), tree) - } else if let Some(output) = - self.outputs.iter_mut().find(|o| o.is_opret_host()) - { - let tree = lnpbp4_tree(output)?; - let commitment = tree.consensus_commit(); - output.script = Script::new_op_return(commitment.as_slice()).into(); - output.set_opret_commitment(commitment.into_array())?; - output.set_lnpbp4_entropy(tree.entropy())?; - (Proof::OpretFirst, tree) - } else { - return Err(PsbtCommitError::CommitmentImpossible); - }; - - Ok(Anchor { - txid: self.to_txid(), - lnpbp4_proof, - dbc_proof, - }) - } -} - /// Type and type-specific proof information of a deterministic bitcoin /// commitment. #[derive(Clone, PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode, StrictDecode)] -#[strict_encoding(by_order)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP, tags = order)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] #[non_exhaustive] pub enum Proof { /// Opret commitment (no extra-transaction proof is required). + #[strict_type(dumb)] OpretFirst, /// Tapret commitment and a proof of it. @@ -421,37 +249,17 @@ pub enum Proof { impl Proof { /// Verifies validity of the proof. - pub fn verify( - &self, - msg: &lnpbp4::CommitmentHash, - tx: Transaction, - ) -> Result { + pub fn verify(&self, msg: &mpc::Commitment, tx: Tx) -> Result { match self { Proof::OpretFirst => { - for txout in &tx.output { + for txout in &tx.outputs { if txout.script_pubkey.is_op_return() { - return Ok(txout.script_pubkey - == Script::new_op_return(msg.as_slice())); + return Ok(txout.script_pubkey == ScriptPubkey::op_return(msg.as_slice())); } } Ok(false) } - Proof::TapretFirst(proof) => { - ConvolveCommitProof::<_, Transaction, _>::verify(proof, msg, tx) - } + Proof::TapretFirst(proof) => ConvolveCommitProof::<_, Tx, _>::verify(proof, msg, tx), } } } - -#[cfg(test)] -mod test { - use commit_verify::tagged_hash; - - use super::*; - - #[test] - fn test_anchor_id_midstate() { - let midstate = tagged_hash::Midstate::with(b"bp:dbc:anchor"); - assert_eq!(midstate.into_inner().into_inner(), MIDSTATE_ANCHOR_ID); - } -} diff --git a/dbc/src/keytweak/mod.rs b/dbc/src/keytweak/mod.rs index cf984a2e..95e84c28 100644 --- a/dbc/src/keytweak/mod.rs +++ b/dbc/src/keytweak/mod.rs @@ -1,17 +1,23 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Homomorphic key tweaking-based deterministic commitment scheme. //! diff --git a/dbc/src/lib.rs b/dbc/src/lib.rs index 4794515c..f78d9ba1 100644 --- a/dbc/src/lib.rs +++ b/dbc/src/lib.rs @@ -1,21 +1,35 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // Coding conventions -#![recursion_limit = "256"] -#![deny(dead_code, missing_docs, warnings)] +#![deny( + non_upper_case_globals, + non_camel_case_types, + non_snake_case, + unused_mut, + unused_imports, + dead_code, + missing_docs +)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] //! Deterministic bitcoin commitments library. //! @@ -27,8 +41,6 @@ #[macro_use] extern crate amplify; -#[cfg(feature = "miniscript")] -extern crate miniscript_crate as miniscript; #[cfg(feature = "serde")] #[macro_use] extern crate serde_crate as serde; diff --git a/dbc/src/opret/mod.rs b/dbc/src/opret/mod.rs index 9b0b0c2e..a087c9e2 100644 --- a/dbc/src/opret/mod.rs +++ b/dbc/src/opret/mod.rs @@ -1,17 +1,23 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! ScriptPubkey-based OP_RETURN commitments. //! diff --git a/dbc/src/sigtweak/mod.rs b/dbc/src/sigtweak/mod.rs index 1d697b02..76749956 100644 --- a/dbc/src/sigtweak/mod.rs +++ b/dbc/src/sigtweak/mod.rs @@ -1,17 +1,23 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Signature tweaking-based deterministic commitment scheme. //! diff --git a/dbc/src/tapret/mod.rs b/dbc/src/tapret/mod.rs index c761805d..97263bb4 100644 --- a/dbc/src/tapret/mod.rs +++ b/dbc/src/tapret/mod.rs @@ -1,17 +1,23 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Taproot OP_RETURN-based deterministic bitcoin commitment scheme ("tapret"). //! @@ -55,44 +61,26 @@ //! b) `TapretProof` / `TweakedPublicKey'` //! b) `XOnlyPublicKey` / `TapretProof` -#[cfg(feature = "wallet")] -mod psbtout; mod tapscript; -mod taptree; mod tx; mod txout; mod xonlypk; -#[cfg(feature = "wallet")] -pub use psbtout::{PsbtCommitError, PsbtVerifyError}; -pub use tapscript::TAPRET_SCRIPT_COMMITMENT_PREFIX; -pub use taptree::TapretTreeError; +pub use bc::LIB_NAME_BP; pub use tx::TapretError; +pub use xonlypk::TapretKeyError; -/// Marker non-instantiable enum defining LNPBP-6 taproot OP_RETURN (`tapret`) +/// Marker non-instantiable enum defining LNPBP-12 taproot OP_RETURN (`tapret`) /// protocol. -pub enum Lnpbp6 {} - -use std::io::Read; +pub enum Lnpbp12 {} -use bitcoin::hashes::sha256::Midstate; -use bitcoin::hashes::Hash; -use bitcoin::schnorr::UntweakedPublicKey; -use bitcoin::util::taproot::{TapBranchHash, TaprootMerkleBranch}; -use bitcoin::Script; -use bitcoin_scripts::taproot::TreeNode; -use bitcoin_scripts::{IntoNodeHash, LeafScript, PubkeyScript, TapNodeHash}; +use bc::{InternalPk, IntoTapHash, LeafScript, ScriptPubkey, TapBranchHash, TapNodeHash}; use commit_verify::CommitmentProtocol; -use secp256k1::SECP256K1; -use strict_encoding::{self, StrictDecode}; - -impl CommitmentProtocol for Lnpbp6 { - // TaggedHash("LNPBP6") - const HASH_TAG_MIDSTATE: Option = Some(Midstate([ - 38, 117, 83, 113, 201, 197, 124, 94, 152, 111, 62, 165, 154, 239, 157, - 166, 10, 195, 217, 29, 15, 182, 55, 211, 190, 230, 184, 41, 241, 198, - 65, 54, - ])); + +pub use self::tapscript::TAPRET_SCRIPT_COMMITMENT_PREFIX; + +impl CommitmentProtocol for Lnpbp12 { + const HASH_TAG_MIDSTATE: Option<[u8; 32]> = Some(*b"urn:lnpbp:lnpbp0012:v01#20230203"); } /// Errors in constructing tapret path proof [`TapretPathProof`]. @@ -112,12 +100,9 @@ pub enum TapretPathError { /// [`TapretNodePartner::RightBranch`] to ensure correct consensus ordering of /// the child elements. #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] #[display("{left_node_hash}:{right_node_hash}")] pub struct TapretRightBranch { left_node_hash: TapNodeHash, @@ -147,14 +132,11 @@ impl TapretRightBranch { /// Computes node hash of the partner node defined by this proof. pub fn node_hash(&self) -> TapNodeHash { - TapBranchHash::from_node_hashes( - self.left_node_hash, - self.right_node_hash, - ) - .into_node_hash() + TapBranchHash::with_nodes(self.left_node_hash, self.right_node_hash).into_tap_hash() } } +/* impl StrictDecode for TapretRightBranch { fn strict_decode( mut d: D, @@ -173,19 +155,17 @@ impl StrictDecode for TapretRightBranch { } } } + */ /// Information proving step of a tapret path in determined way within a given -/// original [`bitcoin::psbt::TapTree`]. +/// tap tree. /// /// The structure hosts proofs that the right-side partner at the taproot script /// tree node does not contain an alternative OP-RETURN commitment script. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] -#[derive(StrictEncode, StrictDecode)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP, tags = order, dumb = Self::RightLeaf(default!()))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] #[display(inner)] pub enum TapretNodePartner { /// Tapret commitment is on the right side of the tree; i.e the node @@ -216,24 +196,19 @@ impl TapretNodePartner { /// Checks that the sibling data does not contain another tapret commitment. /// - /// The check ensures that if the sibling data are present, their first 32 + /// The check ensures that if the sibling data are present, their first 31 /// bytes are not equal to [`TAPRET_SCRIPT_COMMITMENT_PREFIX`], and if /// the sibling is another node, the hash of its first child in the proof /// is smaller than the hash of the other. pub fn check_no_commitment(&self) -> bool { match self { TapretNodePartner::LeftNode(_) => true, - TapretNodePartner::RightLeaf(LeafScript { script, .. }) - if script.len() < 32 => - { - true - } + TapretNodePartner::RightLeaf(LeafScript { script, .. }) if script.len() < 64 => true, TapretNodePartner::RightLeaf(LeafScript { script, .. }) => { - script[0..32] != TAPRET_SCRIPT_COMMITMENT_PREFIX[..] + script[..31] != TAPRET_SCRIPT_COMMITMENT_PREFIX[..] } TapretNodePartner::RightBranch(right_branch) => { - right_branch.left_node_hash()[..] - != TAPRET_SCRIPT_COMMITMENT_PREFIX[..] + right_branch.left_node_hash()[..31] != TAPRET_SCRIPT_COMMITMENT_PREFIX[..] } } } @@ -244,7 +219,7 @@ impl TapretNodePartner { match self { TapretNodePartner::LeftNode(left_node) => *left_node <= other_node, TapretNodePartner::RightLeaf(leaf_script) => { - let right_node = leaf_script.tap_leaf_hash().into_node_hash(); + let right_node = leaf_script.tap_leaf_hash().into_tap_hash(); other_node <= right_node } TapretNodePartner::RightBranch(right_branch) => { @@ -255,30 +230,13 @@ impl TapretNodePartner { } /// Computes node hash of the partner node defined by this proof. - pub fn node_hash(&self) -> TapNodeHash { + pub fn tap_node_hash(&self) -> TapNodeHash { match self { TapretNodePartner::LeftNode(hash) => *hash, TapretNodePartner::RightLeaf(leaf_script) => { - leaf_script.tap_leaf_hash().into_node_hash() - } - TapretNodePartner::RightBranch(right_branch) => { - right_branch.node_hash() - } - } - } - - /// Constructs [`TreeNode`] for the node partner. - pub fn to_tree_node(&self) -> TreeNode { - match self { - TapretNodePartner::LeftNode(left_node) => { - TreeNode::Hidden(*left_node, 1) - } - TapretNodePartner::RightLeaf(leaf_script) => { - TreeNode::Leaf(leaf_script.clone(), 0) - } - TapretNodePartner::RightBranch(partner_branch) => { - TreeNode::Hidden(partner_branch.node_hash(), 1) + leaf_script.tap_leaf_hash().into_tap_hash() } + TapretNodePartner::RightBranch(right_branch) => right_branch.node_hash(), } } } @@ -288,13 +246,10 @@ impl TapretNodePartner { /// /// Holds information about the sibling at level 1 of the tree in form of /// [`TapretNodePartner`]. -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode, StrictDecode)] +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct TapretPathProof { /// Information about the sibling at level 1 of the tree partner_node: Option, @@ -307,13 +262,15 @@ pub struct TapretPathProof { impl TapretPathProof { /// Construct new empty path proof. #[inline] - pub fn new() -> TapretPathProof { TapretPathProof::default() } + pub fn root() -> TapretPathProof { + TapretPathProof { + partner_node: None, + nonce: 0, + } + } /// Adds element to the path proof. - pub fn with( - elem: TapretNodePartner, - nonce: u8, - ) -> Result { + pub fn with(elem: TapretNodePartner, nonce: u8) -> Result { if !elem.check_no_commitment() { return Err(TapretPathError::InvalidNodePartner(elem)); } @@ -340,7 +297,7 @@ impl TapretPathProof { pub fn original_merkle_root(&self) -> Option { self.partner_node .as_ref() - .map(|partner| partner.node_hash()) + .map(|partner| partner.tap_node_hash()) } } @@ -372,12 +329,9 @@ impl<'data> IntoIterator for &'data TapretPathProof { /// Used both in the commitment procedure for PSBTs and in /// client-side-validation of the commitment. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode, StrictDecode)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct TapretProof { /// A merkle path to the commitment inside the taproot script tree. For /// each node it also must hold information about the sibling in form of @@ -388,44 +342,15 @@ pub struct TapretProof { /// /// We need to keep this information client-side since it can't be /// retrieved from the mined transaction. - pub internal_key: UntweakedPublicKey, + pub internal_pk: InternalPk, } impl TapretProof { /// Restores original scripPubkey before deterministic bitcoin commitment /// applied. #[inline] - pub fn original_pubkey_script(&self) -> PubkeyScript { - let merkle_root = self - .path_proof - .original_merkle_root() - .map(TapNodeHash::into_inner) - .map(TapBranchHash::from_inner); - Script::new_v1_p2tr(SECP256K1, self.internal_key, merkle_root).into() - } -} - -/// Tapret value: a final tweak applied to the internal taproot key which -/// includes commitment to both initial taptree merkle root and the OP_RETURN -/// commitment branch. Represents the taptree merkle root of the modified -/// taptree. -#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] -#[derive(StrictEncode, StrictDecode)] -pub struct TapretTweak(TaprootMerkleBranch); - -#[cfg(test)] -mod test { - use amplify::Wrapper; - use commit_verify::tagged_hash; - - use super::*; - - #[test] - fn test_lnpbp6_midstate() { - let midstate = tagged_hash::Midstate::with(b"LNPBP6"); - assert_eq!( - midstate.into_inner().into_inner(), - Lnpbp6::HASH_TAG_MIDSTATE.unwrap().into_inner() - ); + pub fn original_pubkey_script(&self) -> ScriptPubkey { + let merkle_root = self.path_proof.original_merkle_root(); + ScriptPubkey::p2tr(self.internal_pk, merkle_root) } } diff --git a/dbc/src/tapret/psbtout.rs b/dbc/src/tapret/psbtout.rs deleted file mode 100644 index b8f4d403..00000000 --- a/dbc/src/tapret/psbtout.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use bitcoin::hashes::Hash; -use bitcoin::psbt::TapTree; -use bitcoin::util::taproot::TapBranchHash; -use bitcoin::Script; -use bitcoin_scripts::taproot::{Node, TaprootScriptTree, TreeNode}; -use bitcoin_scripts::TapNodeHash; -use commit_verify::convolve_commit::ConvolveCommitVerify; -use commit_verify::{lnpbp4, EmbedCommitProof, EmbedCommitVerify}; -use psbt::commit::{ - DfsPathEncodeError, Lnpbp4KeyError, OpretKeyError, TapretKeyError, -}; -use secp256k1::SECP256K1; - -use super::{Lnpbp6, TapretProof}; -use crate::tapret::taptree::{ - TapretProofError, TapretSourceError, TapretSourceInfo, -}; - -/// Errors during tapret PSBT commitment process. -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum PsbtCommitError { - /// Invalid taproot script tree source information. - #[from] - #[display(inner)] - SourceError(TapretSourceError), - - /// it is impossible to create neither Tapret nor Opret commitment for the - /// given PSBT file. - CommitmentImpossible, - - /// Error in LNPBP4 PBST data - #[from] - #[display(inner)] - PsbtLnpbp4(Lnpbp4KeyError), - - /// Error in LNPBP4 PBST data - #[from] - #[display(inner)] - TapretLnpbp4(TapretKeyError), - - /// Error in LNPBP4 PBST data - #[from] - #[display(inner)] - OpretLnpbp4(OpretKeyError), - - /// LNPBP4 commitment creation error - #[from] - #[display(inner)] - Lnpbp4(lnpbp4::Error), - - /// tapret commitment can't be made in a transaction lacking any taproot - /// outputs. - NoTaprootOutput, - - /// tapret commitment can't be made due to an absent taproot internal key - /// in PSBT data. - InternalKeyMissed, - - /// tapret commitment does not change internal key, but the key in PSBT - /// data and key from the tapret proof differ. - InternalKeyMismatch, - - /// invalid tapret commitment path in PSBT data. - #[from(DfsPathEncodeError)] - TapretPathInvalid, - - /// PSBT output misses tapret path information. - TapretPathMissed, - - /// producing taptree structure - TapTreeError, -} - -/// Errors during tapret PSBT commitment process. -#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)] -#[display(inner)] -pub enum PsbtVerifyError { - /// Error during commitment process. - #[from] - #[from(DfsPathEncodeError)] - #[from(TapretSourceError)] - Commit(PsbtCommitError), - - /// Error during verification process. - #[from] - Proof(TapretProofError), -} - -impl EmbedCommitProof - for TapretProof -{ - fn restore_original_container( - &self, - commit_container: &psbt::Output, - ) -> Result { - let mut original_container = commit_container.clone(); - - let internal_key = original_container - .tap_internal_key - .ok_or(PsbtCommitError::InternalKeyMissed)?; - if internal_key != self.internal_key { - return Err(PsbtCommitError::InternalKeyMismatch) - .map_err(PsbtVerifyError::from); - } - - let tap_tree = original_container.tap_tree.map(TaprootScriptTree::from); - let source = TapretSourceInfo::::with(tap_tree)?; - let source = self.path_proof.restore_original_container(&source)?; - - let merkle_root = source - .as_root_node() - .map(TreeNode::node_hash) - .map(TapNodeHash::into_inner) - .map(TapBranchHash::from_inner); - original_container.script = - Script::new_v1_p2tr(SECP256K1, self.internal_key, merkle_root) - .into(); - - original_container.tap_tree = source.into_tap_tree(); - - Ok(original_container) - } -} - -impl EmbedCommitVerify for psbt::Output { - type Proof = TapretProof; - type CommitError = PsbtCommitError; - type VerifyError = PsbtVerifyError; - - fn embed_commit( - &mut self, - msg: &lnpbp4::CommitmentHash, - ) -> Result { - // TODO: Check TAPRET_COMMITABLE key - - let internal_key = if let Some(internal_key) = self.tap_internal_key { - internal_key - } else { - return Err(PsbtCommitError::InternalKeyMissed); - }; - - let mut source = - TapretSourceInfo::::with(self.tap_tree.clone())?; - - let path_proof = source.embed_commit(msg)?; - - self.tap_tree = source.into_tap_tree(); - - let (output_key, _) = internal_key - .convolve_commit(&path_proof, msg) - .map_err(|_| PsbtCommitError::TapTreeError)?; - - self.script = Script::new_v1_p2tr_tweaked(output_key).into(); - - let proof = TapretProof { - path_proof, - internal_key, - }; - - Ok(proof) - } -} diff --git a/dbc/src/tapret/tapscript.rs b/dbc/src/tapret/tapscript.rs index cf3e7bc6..6db6cd28 100644 --- a/dbc/src/tapret/tapscript.rs +++ b/dbc/src/tapret/tapscript.rs @@ -1,48 +1,99 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use bitcoin::blockdata::opcodes::all; -use bitcoin::blockdata::script; -use bitcoin_scripts::TapScript; -use commit_verify::{lnpbp4, CommitEncode, CommitVerify}; - -use super::Lnpbp6; - -/// Hardcoded tapret script prefix consisting of 30 `OP_RESERVED` pushes, -/// followed by `OP_RETURN` and `OP_PUSHBYTES_32`. -pub const TAPRET_SCRIPT_COMMITMENT_PREFIX: [u8; 32] = [ - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x6a, 0x20, +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::io; + +use bc::{TapCode, TapScript, LIB_NAME_BP}; +use commit_verify::{mpc, strategies, CommitEncode, CommitStrategy, CommitVerify}; + +use super::Lnpbp12; + +/// Hardcoded tapret script prefix consisting of 29 `OP_RESERVED` pushes, +/// followed by `OP_RETURN` and `OP_PUSHBYTES_33`. +pub const TAPRET_SCRIPT_COMMITMENT_PREFIX: [u8; 31] = [ + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x6a, 0x21, ]; -impl CommitVerify<(lnpbp4::CommitmentHash, u8), Lnpbp6> for TapScript { - fn commit(msg: &(lnpbp4::CommitmentHash, u8)) -> Self { - let (msg, nonce) = msg; - let mut builder = script::Builder::new(); - for _ in 0..30 { - // Filling first 30 bytes with OP_RESERVED in order to avoid - // representation of sibling partner script as child hashes. - builder = builder.push_opcode(all::OP_RESERVED); +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct TapretCommitment { + /// LNPBP-4 multi-protocol commitment. + pub mpc: mpc::Commitment, + /// Nonce is used to put the commitment into the correct side of the tree. + pub nonce: u8, +} + +impl TapretCommitment { + pub fn with(mpc: mpc::Commitment, nonce: u8) -> Self { Self { mpc, nonce } } +} + +impl CommitStrategy for TapretCommitment { + type Strategy = strategies::Strict; +} + +impl CommitVerify for TapScript { + /// Tapret script consists of 29 `OP_RESERVED` pushes, followed by + /// `OP_RETURN`, `OP_PUSHBYTES_33` and serialized commitment data (MPC + /// commitment + nonce as a single slice). + fn commit(commitment: &TapretCommitment) -> Self { + let mut tapret = TapScript::with_capacity(64); + for _ in 0..29 { + tapret.push_opcode(TapCode::Reserved); } - let mut data = msg.commit_serialize(); - data.push(*nonce); - builder - .push_opcode(all::OP_RETURN) - .push_slice(&data) - .into_script() - .into() + tapret.push_opcode(TapCode::Return); + let mut data = io::Cursor::new([0u8; 33]); + commitment.commit_encode(&mut data); + tapret.push_slice(&data.into_inner()); + tapret + } +} + +#[cfg(test)] +mod test { + use strict_encoding::StrictDumb; + + use super::*; + + pub fn commitment() -> TapretCommitment { + TapretCommitment { + mpc: mpc::Commitment::strict_dumb(), + nonce: 8, + } + } + + #[test] + pub fn commitment_prefix() { + let script = TapScript::commit(&commitment()); + assert_eq!(TAPRET_SCRIPT_COMMITMENT_PREFIX, script[0..31]); + } + + #[test] + pub fn commiment_serialization() { + let commitment = commitment(); + let script = TapScript::commit(&commitment); + eprintln!("{script:x}"); + assert_eq!(script[63], commitment.nonce); + assert_eq!(&script[31..63], commitment.mpc.as_slice()); } } diff --git a/dbc/src/tapret/taptree.rs b/dbc/src/tapret/taptree.rs deleted file mode 100644 index 021ebd98..00000000 --- a/dbc/src/tapret/taptree.rs +++ /dev/null @@ -1,379 +0,0 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! `EmbedCommit: TapTree, Msg -> TapTree', TapNode` - -use core::fmt::Debug; -use core::hash::Hash; - -use bitcoin::psbt::{IncompleteTapTree, TapTree}; -use bitcoin::util::taproot::TaprootBuilderError; -use bitcoin_scripts::taproot::{ - self, Branch, CutError, DfsOrder, DfsOrdering, DfsPath, DfsTraversalError, - InstillError, MaxDepthExceeded, Node, TaprootScriptTree, TreeNode, - UnsplittableTree, -}; -use bitcoin_scripts::{LeafScript, TapNodeHash, TapScript}; -use commit_verify::{ - lnpbp4, CommitVerify, EmbedCommitProof, EmbedCommitVerify, -}; - -use super::{Lnpbp6, TapretNodePartner, TapretPathProof}; -use crate::tapret::TapretPathError; - -// TODO: Re-check the use of all error variants -/// Errors during tapret commitment embedding into tapscript tree. -#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum TapretTreeError { - /// the provided commitment data can't be strict encoded. Details: {0} - #[from] - StrictEncoding(strict_encoding::Error), - - /// unable to update tap tree with the commitment. Details: {0} - #[from] - TapTree(TaprootBuilderError), - - /// the taproot script tree is invalid. Details: {0} - #[from] - TreeBuilder(IncompleteTapTree), - - /// the tapret commitment is impossible since the taproot script tree - /// already has the maximal depth. - #[from(MaxDepthExceeded)] - MaxDepthExceeded, - - /// the provided taproot script tree has no revealed nodes to prove the - /// commitment. - IncompleteTree(TaprootScriptTree), - - /// tapret node partner {0} contains alternative commitment - AlternativeCommitment(TapretNodePartner), - - /// tapret node partner {0} has an invalid order with the commitment node - /// {1} - IncorrectOrdering(TapretNodePartner, TreeNode), -} - -/// Errors during taproot script tree tapret commitment verification and -/// restoration of the original container structure. -#[derive( - Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Display, Error, From -)] -#[display(doc_comments)] -pub enum TapretProofError { - /// the provided tapret proof does not contain a taproot script tree, i.e. - /// can't contain a commitment - EmptyTree, - - /// the provided tapret proof consists of a single node - #[from(UnsplittableTree)] - UnsplittableTree, - - /// Errors in the taproot script tree and tapret path proof - /// correspondences. See [`TapretSourceError`] for details. - #[from] - #[display(inner)] - SourceError(TapretSourceError), -} - -impl From for TapretProofError { - fn from(err: CutError) -> Self { - match err { - CutError::UnsplittableTree => Self::UnsplittableTree, - CutError::DfsTraversal(e) => Self::SourceError(e.into()), - } - } -} - -/// Errors during taproot script tree tapret commitment ebmedding. -#[derive( - Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Display, Error, From -)] -#[display(doc_comments)] -pub enum TapretSourceError { - /// the length of the constructed tapret path proof exceeds taproot path - /// length limit. - #[from(MaxDepthExceeded)] - MaxDepthExceeded, - - /// the node partner {0} at the level 1 can't be proven not to contain an - /// alternative tapret commitment. - InvalidNodePartner(TapretNodePartner), - - /// unable to produce tapret commitment since the commitment path {0} does - /// not exist within the tree. - PathNotExists(DfsPath), - - /// the provided taproot script tree contains hidden node {0} at path {1} - /// and can't be used for tapret commit instillation. - HiddenNode(TapNodeHash, DfsPath), - - /// the provided tapret commitment path {1} points at the leaf node {0} - /// and can't be used for tapret commit instillation. - LeafNode(LeafScript, DfsPath), -} - -impl From for TapretSourceError { - fn from(err: InstillError) -> Self { - match err { - InstillError::MaxDepthExceeded => Self::MaxDepthExceeded, - InstillError::DfsTraversal(e) => e.into(), - } - } -} - -impl From for TapretSourceError { - fn from(err: DfsTraversalError) -> Self { - match err { - DfsTraversalError::PathNotExists(path) => Self::PathNotExists(path), - DfsTraversalError::HiddenNode { - node_hash, - failed_path, - path_leftover: _, - } => Self::HiddenNode(node_hash, failed_path), - DfsTraversalError::LeafNode { - leaf_script, - failed_path, - path_leftover: _, - } => Self::LeafNode(leaf_script, failed_path), - } - } -} - -impl From for TapretSourceError { - fn from(err: TapretPathError) -> Self { - match err { - TapretPathError::MaxDepthExceeded => Self::MaxDepthExceeded, - TapretPathError::InvalidNodePartner(partner) => { - Self::InvalidNodePartner(partner) - } - } - } -} - -/// Structure wrapping concrete taproot script tree, acting as a source of the -/// tapret commitment, keeping information about DFS path which should be used -/// for embedding tapret commit. -/// -/// The structure can be used with [`TapTree`] and [`TaprootScriptTree`]. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct TapretSourceInfo(Option) -where - Tree: Clone + Eq + Debug; - -impl TapretSourceInfo { - /// Validates that the provided path can be used for a tapret commitment - /// embedding and constructs [`SourceTree`] from the provided - /// [`TaprootScriptTree`] and a valid path. The path must point to a branch - /// node in the tree. - /// - /// # Errors - /// - /// The following errors may happen: - /// - [`DfsPathError::PathNotExists`], if the path does not exists within - /// the tree; - /// - [`DfsPathError::HiddenNode`], if the path passes through a hidden node - /// of the tree; - /// - [`DfsPathError::LeafNode`], if the path points at a leaf node. - pub fn with( - tap_tree: Option, - ) -> Result { - Ok(TapretSourceInfo(tap_tree)) - } - - /// Returns reference to the script tree root node, if taproot script tree - /// is present in the source data. - pub fn as_root_node(&self) -> Option<&TreeNode> { - self.0.as_ref().map(TaprootScriptTree::as_root_node) - } - - /// Releases internal [`TapTree`] data, if present. - #[inline] - pub fn into_tap_tree(self) -> Option { self.0.map(TapTree::from) } -} - -impl TapretSourceInfo { - /// Validates that the provided path can be used for a tapret commitment - /// embedding and constructs [`SourceTree`] from the provided [`TapTree`] - /// and a valid path. The path must point to a branch node in the tree. - /// - /// # Errors - /// - /// The following errors may happen: - /// - [`DfsPathError::PathNotExists`], if the path does not exists within - /// the tree; - /// - [`DfsPathError::HiddenNode`], if the path passes through a hidden node - /// of the tree; - /// - [`DfsPathError::LeafNode`], if the path points at a leaf node. - #[inline] - pub fn with(tap_tree: Option) -> Result { - TapretSourceInfo::::with( - tap_tree.map(TaprootScriptTree::from), - ) - .map(TapretSourceInfo::from) - } - - /// Releases internal [`TapTree`] data, if present. - #[inline] - pub fn into_tap_tree(self) -> Option { self.0 } -} - -impl From<&TapretSourceInfo> for TapretSourceInfo { - fn from(source: &TapretSourceInfo) -> Self { - let tap_tree = source.0.as_ref().cloned().map(TaprootScriptTree::from); - TapretSourceInfo(tap_tree) - } -} - -impl From> for TapretSourceInfo { - fn from(source: TapretSourceInfo) -> Self { - let tap_tree = source.0.map(TapTree::from); - TapretSourceInfo(tap_tree) - } -} - -impl - EmbedCommitProof< - lnpbp4::CommitmentHash, - TapretSourceInfo, - Lnpbp6, - > for TapretPathProof -{ - fn restore_original_container( - &self, - modified_tree: &TapretSourceInfo, - ) -> Result, TapretProofError> { - let tap_tree = modified_tree - .0 - .as_ref() - .cloned() - .ok_or(TapretProofError::EmptyTree)?; - - match self.partner_node { - // Taproot has key-only spending - None => Ok(TapretSourceInfo(None)), - // Taproot has script spendings - Some(_) => { - let (original_tree, _) = tap_tree.split()?; - Ok(TapretSourceInfo(Some(original_tree))) - } - } - } -} - -impl EmbedCommitVerify - for TapretSourceInfo -{ - type Proof = TapretPathProof; - type CommitError = TapretSourceError; - type VerifyError = TapretProofError; - - fn embed_commit( - &mut self, - msg: &lnpbp4::CommitmentHash, - ) -> Result { - for nonce in 0..=u8::MAX { - let commitment_script = TapScript::commit(&(*msg, nonce)); - - let commitment_node = - TreeNode::with_tap_script(commitment_script, 0); - let commitment_subtree = TaprootScriptTree::with(commitment_node) - .expect("invalid commitment node construction"); - - let tap_tree = if let Some(ref mut tap_tree) = self.0 { - tap_tree - } else { - self.0 = Some(commitment_subtree); - return Ok(TapretPathProof::new()); - }; - - let original_tree = tap_tree.clone(); - *tap_tree = - original_tree.join(commitment_subtree, DfsOrder::Last)?; - - let branch = tap_tree - .as_root_node() - .as_branch() - .expect("instill algorithm is broken"); - let partner = branch.as_dfs_child_node(DfsOrder::First); - - let partner_is_left_node = - branch.dfs_ordering() == DfsOrdering::LeftRight; - - let partner_proof = match (partner_is_left_node, partner) { - (true, node) => TapretNodePartner::LeftNode(node.node_hash()), - (false, TreeNode::Leaf(script, _)) => { - TapretNodePartner::RightLeaf(script.clone()) - } - (false, TreeNode::Hidden(partner_hash, _)) => { - return Err(TapretSourceError::HiddenNode( - *partner_hash, - vec![DfsOrder::First].into(), - )) - } - (false, TreeNode::Branch(partner_branch, _)) => { - TapretNodePartner::right_branch( - partner_branch.as_left_node().node_hash(), - partner_branch.as_right_node().node_hash(), - ) - } - }; - - if partner_is_left_node || nonce == u8::MAX { - return TapretPathProof::with(partner_proof, nonce) - .map_err(TapretSourceError::from); - } - } - unreachable!("for cycle always returns before exiting") - } -} - -impl EmbedCommitProof, Lnpbp6> - for TapretPathProof -{ - fn restore_original_container( - &self, - modified_container: &TapretSourceInfo, - ) -> Result, TapretProofError> { - EmbedCommitProof::< - _, - TapretSourceInfo, - _, - >::restore_original_container( - self, &modified_container.into() - ).map(TapretSourceInfo::into) - } -} - -impl EmbedCommitVerify - for TapretSourceInfo -{ - type Proof = TapretPathProof; - type CommitError = TapretSourceError; - type VerifyError = TapretProofError; - - fn embed_commit( - &mut self, - msg: &lnpbp4::CommitmentHash, - ) -> Result { - let mut source = TapretSourceInfo::::from( - self as &TapretSourceInfo<_>, - ); - let proof = source.embed_commit(msg)?; - *self = source.into(); - Ok(proof) - } -} diff --git a/dbc/src/tapret/tx.rs b/dbc/src/tapret/tx.rs index ce9d7b15..acddc595 100644 --- a/dbc/src/tapret/tx.rs +++ b/dbc/src/tapret/tx.rs @@ -1,50 +1,51 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -use bitcoin::Transaction; -use commit_verify::convolve_commit::{ - ConvolveCommitProof, ConvolveCommitVerify, -}; -use commit_verify::lnpbp4; +use bc::Tx; +use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp6, TapretProof, TapretTreeError}; +use super::{Lnpbp12, TapretKeyError, TapretProof}; /// Errors during tapret commitment. #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] pub enum TapretError { - /// Error embedding tapret commitment into taproot script tree. + /// Error embedding tapret commitment into x-only key. #[from] #[display(inner)] - TreeEmbedding(TapretTreeError), + KeyEmbedding(TapretKeyError), /// tapret commitment in a transaction lacking any taproot outputs. #[display(doc_comments)] NoTaprootOutput, } -impl ConvolveCommitProof - for TapretProof -{ +impl ConvolveCommitProof for TapretProof { type Suppl = Self; - fn restore_original(&self, commitment: &Transaction) -> Transaction { + fn restore_original(&self, commitment: &Tx) -> Tx { let mut tx = commitment.clone(); - for txout in &mut tx.output { - if txout.script_pubkey.is_v1_p2tr() { - txout.script_pubkey = self.original_pubkey_script().into(); + for txout in &mut tx.outputs { + if txout.script_pubkey.is_p2tr() { + txout.script_pubkey = self.original_pubkey_script(); } } tx @@ -53,21 +54,19 @@ impl ConvolveCommitProof fn extract_supplement(&self) -> &Self::Suppl { self } } -impl ConvolveCommitVerify - for Transaction -{ - type Commitment = Transaction; +impl ConvolveCommit for Tx { + type Commitment = Tx; type CommitError = TapretError; fn convolve_commit( &self, supplement: &TapretProof, - msg: &lnpbp4::CommitmentHash, - ) -> Result<(Transaction, TapretProof), Self::CommitError> { + msg: &mpc::Commitment, + ) -> Result<(Tx, TapretProof), Self::CommitError> { let mut tx = self.clone(); - for txout in &mut tx.output { - if txout.script_pubkey.is_v1_p2tr() { + for txout in &mut tx.outputs { + if txout.script_pubkey.is_p2tr() { let (commitment, proof) = txout .convolve_commit(supplement, msg) .map_err(TapretError::from)?; diff --git a/dbc/src/tapret/txout.rs b/dbc/src/tapret/txout.rs index bcc218d6..d94db6ed 100644 --- a/dbc/src/tapret/txout.rs +++ b/dbc/src/tapret/txout.rs @@ -1,57 +1,56 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -use bitcoin::{Script, TxOut}; -use commit_verify::convolve_commit::{ - ConvolveCommitProof, ConvolveCommitVerify, -}; -use commit_verify::lnpbp4; +use bc::{ScriptPubkey, TxOut}; +use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof}; -use super::{Lnpbp6, TapretProof, TapretTreeError}; +use super::{Lnpbp12, TapretKeyError, TapretProof}; -impl ConvolveCommitProof - for TapretProof -{ +impl ConvolveCommitProof for TapretProof { type Suppl = Self; fn restore_original(&self, commitment: &TxOut) -> TxOut { TxOut { value: commitment.value, - script_pubkey: self.original_pubkey_script().into(), + script_pubkey: self.original_pubkey_script(), } } fn extract_supplement(&self) -> &Self::Suppl { self } } -impl ConvolveCommitVerify - for TxOut -{ +impl ConvolveCommit for TxOut { type Commitment = TxOut; - type CommitError = TapretTreeError; + type CommitError = TapretKeyError; fn convolve_commit( &self, supplement: &TapretProof, - msg: &lnpbp4::CommitmentHash, + msg: &mpc::Commitment, ) -> Result<(TxOut, TapretProof), Self::CommitError> { let (output_key, _) = supplement - .internal_key + .internal_pk .convolve_commit(&supplement.path_proof, msg)?; - let script_pubkey = Script::new_v1_p2tr_tweaked(output_key); + let script_pubkey = ScriptPubkey::p2tr_tweaked(output_key); let commitment = TxOut { value: self.value, diff --git a/dbc/src/tapret/xonlypk.rs b/dbc/src/tapret/xonlypk.rs index 19d46797..4b640004 100644 --- a/dbc/src/tapret/xonlypk.rs +++ b/dbc/src/tapret/xonlypk.rs @@ -1,92 +1,85 @@ -// Deterministic bitcoin commitments library, implementing LNPBP standards -// Part of bitcoin protocol core library (BP Core Lib) +// Deterministic bitcoin commitments library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use bitcoin::hashes::Hash; -use bitcoin::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey}; -use bitcoin::util::taproot::TapBranchHash; -use bitcoin_scripts::taproot::{Node, TreeNode}; -use bitcoin_scripts::TapScript; -use commit_verify::convolve_commit::{ - ConvolveCommitProof, ConvolveCommitVerify, -}; -use commit_verify::{lnpbp4, CommitVerify}; -use secp256k1::SECP256K1; - -use super::{Lnpbp6, TapretPathProof, TapretProof, TapretTreeError}; - -impl ConvolveCommitProof - for TapretProof -{ +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bc::{InternalPk, TapBranchHash, TapLeafHash, TapNodeHash, TapScript}; +use commit_verify::{mpc, CommitVerify, ConvolveCommit, ConvolveCommitProof}; +use secp256k1::XOnlyPublicKey; + +use super::{Lnpbp12, TapretNodePartner, TapretPathProof, TapretProof}; +use crate::tapret::tapscript::TapretCommitment; + +/// Errors during tapret commitment embedding into x-only public key. +#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum TapretKeyError { + /// tapret node partner {0} contains alternative commitment + AlternativeCommitment(TapretNodePartner), + + /// tapret node partner {0} has an invalid order with the commitment node + /// {1} + IncorrectOrdering(TapretNodePartner, TapLeafHash), +} + +impl ConvolveCommitProof for TapretProof { type Suppl = TapretPathProof; - fn restore_original(&self, _: &TweakedPublicKey) -> UntweakedPublicKey { - self.internal_key - } + fn restore_original(&self, _: &XOnlyPublicKey) -> InternalPk { self.internal_pk } fn extract_supplement(&self) -> &Self::Suppl { &self.path_proof } } -impl ConvolveCommitVerify - for UntweakedPublicKey -{ - type Commitment = TweakedPublicKey; - type CommitError = TapretTreeError; +impl ConvolveCommit for InternalPk { + type Commitment = XOnlyPublicKey; + type CommitError = TapretKeyError; fn convolve_commit( &self, supplement: &TapretPathProof, - msg: &lnpbp4::CommitmentHash, - ) -> Result<(TweakedPublicKey, TapretProof), Self::CommitError> { - let script_commitment = TapScript::commit(&(*msg, supplement.nonce)); + msg: &mpc::Commitment, + ) -> Result<(XOnlyPublicKey, TapretProof), Self::CommitError> { + let tapret_commitment = TapretCommitment::with(*msg, supplement.nonce); + let script_commitment = TapScript::commit(&tapret_commitment); - let root = if let Some(ref partner) = supplement.partner_node { + let merkle_root: TapNodeHash = if let Some(ref partner) = supplement.partner_node { if !partner.check_no_commitment() { - return Err(TapretTreeError::AlternativeCommitment( - partner.clone(), - )); + return Err(TapretKeyError::AlternativeCommitment(partner.clone())); } - let commitment_node = - TreeNode::with_tap_script(script_commitment, 1); + let commitment_leaf = TapLeafHash::with_tap_script(&script_commitment); + let commitment_hash = TapNodeHash::from(commitment_leaf); - if !partner.check_ordering(commitment_node.node_hash()) { - return Err(TapretTreeError::IncorrectOrdering( - partner.clone(), - commitment_node, - )); + if !partner.check_ordering(commitment_hash) { + return Err(TapretKeyError::IncorrectOrdering(partner.clone(), commitment_leaf)); } - TreeNode::with_branch(commitment_node, partner.to_tree_node(), 0) + TapBranchHash::with_nodes(commitment_hash, partner.tap_node_hash()).into() } else { - TreeNode::with_tap_script(script_commitment, 0) + TapLeafHash::with_tap_script(&script_commitment).into() }; - // rust-bitcoin API has this inefficiency: while `TapLeafHash` can be - // a valid merkle root (for script trees with a single leaf), it is not - // accepted by the tap_tweak API. - // - // Details: - let merkle_root = - TapBranchHash::from_inner(root.node_hash().into_inner()); - // TODO: Use secp instance from Lnpbp6 - let (output_key, _parity_not_used) = - self.tap_tweak(SECP256K1, Some(merkle_root)); + let output_key = self.to_output_key(Some(merkle_root)); let proof = TapretProof { path_proof: supplement.clone(), - internal_key: *self, + internal_pk: *self, }; Ok((output_key, proof)) @@ -97,105 +90,93 @@ impl ConvolveCommitVerify mod test { use std::str::FromStr; - use amplify::Wrapper; - use bitcoin::hashes::Hash; - use bitcoin_scripts::LeafScript; - use commit_verify::lnpbp4::CommitmentHash; - use secp256k1::XOnlyPublicKey; + use bc::LeafScript; + use commit_verify::mpc::Commitment; use super::*; use crate::tapret::TapretNodePartner; #[test] fn key_path() { - let internal_key = XOnlyPublicKey::from_str( + let internal_pk = InternalPk::from_str( "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3", ) .unwrap(); - let msg = CommitmentHash::from_inner(Hash::hash(b"")); - let path_proof = TapretPathProof::new(); + let msg = mpc::Commitment::from([8u8; 32]); + let path_proof = TapretPathProof::root(); - let (outer_key, proof) = - internal_key.convolve_commit(&path_proof, &msg).unwrap(); + // Do via API + let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap(); - let script_commitment = TapScript::commit(&(msg, 0)); - let root = TreeNode::with_tap_script(script_commitment, 0); - let merkle_root = - TapBranchHash::from_inner(root.node_hash().into_inner()); - let (real_key, _) = - internal_key.tap_tweak(SECP256K1, Some(merkle_root)); + // Do manually + let tapret_commitment = TapretCommitment::with(msg, path_proof.nonce); + let script_commitment = TapScript::commit(&tapret_commitment); + let script_leaf = TapLeafHash::with_tap_script(&script_commitment); + let real_key = internal_pk.to_output_key(Some(script_leaf)); assert_eq!(outer_key, real_key); assert_eq!(proof, TapretProof { path_proof, - internal_key + internal_pk }); - assert!(ConvolveCommitProof::< - CommitmentHash, - UntweakedPublicKey, - Lnpbp6, - >::verify(&proof, &msg, outer_key) - .unwrap()); + assert!( + ConvolveCommitProof::::verify(&proof, &msg, outer_key) + .unwrap() + ); } #[test] fn single_script() { - let internal_key = XOnlyPublicKey::from_str( + let internal_pk = InternalPk::from_str( "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3", ) .unwrap(); - let msg = CommitmentHash::from_inner(Hash::hash(b"")); + let msg = mpc::Commitment::from([8u8; 32]); let path_proof = TapretPathProof::with( - TapretNodePartner::RightLeaf(LeafScript::tapscript(default!())), - 88, + TapretNodePartner::RightLeaf(LeafScript::from_tap_script(default!())), + 22, ) .unwrap(); - let (outer_key, proof) = - internal_key.convolve_commit(&path_proof, &msg).unwrap(); + let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap(); assert_eq!(proof, TapretProof { path_proof, - internal_key + internal_pk }); - assert!(ConvolveCommitProof::< - CommitmentHash, - UntweakedPublicKey, - Lnpbp6, - >::verify(&proof, &msg, outer_key) - .unwrap()); + assert!( + ConvolveCommitProof::::verify(&proof, &msg, outer_key) + .unwrap() + ); } #[test] #[should_panic(expected = "IncorrectOrdering")] fn invalid_partner_ordering() { - let internal_key = XOnlyPublicKey::from_str( + let internal_pk = InternalPk::from_str( "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3", ) .unwrap(); - let msg = CommitmentHash::from_inner(Hash::hash(b"")); + let msg = mpc::Commitment::from([8u8; 32]); let path_proof = TapretPathProof::with( - TapretNodePartner::RightLeaf(LeafScript::tapscript(default!())), - 1, + TapretNodePartner::RightLeaf(LeafScript::from_tap_script(default!())), + 11, ) .unwrap(); - let (outer_key, proof) = - internal_key.convolve_commit(&path_proof, &msg).unwrap(); + let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap(); assert_eq!(proof, TapretProof { path_proof, - internal_key + internal_pk }); - assert!(ConvolveCommitProof::< - CommitmentHash, - UntweakedPublicKey, - Lnpbp6, - >::verify(&proof, &msg, outer_key) - .unwrap()); + assert!( + ConvolveCommitProof::::verify(&proof, &msg, outer_key) + .unwrap() + ); } } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml new file mode 100644 index 00000000..c721fcbd --- /dev/null +++ b/primitives/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "bp-primitives" +version = "0.10.0-beta.1" +description = "Bitcoin protocol primitives library" +keywords = ["lnp-bp", "smart-contracts", "bitcoin", "blockchain"] +categories = ["cryptography"] +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } +readme = "../README.md" + +[lib] +name = "bc" + +[dependencies] +amplify = "4.0.0-beta.10" +secp256k1 = { version = "0.26.0", features = ["global-context"] } +strict_encoding = "2.0.0-beta.5" +commit_verify = "0.10.0-beta.1" +serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } + +[features] +serde = [ + "serde_crate", + "amplify/serde", + "secp256k1/serde" +] + +[package.metadata.docs.rs] +features = [ "all" ] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs new file mode 100644 index 00000000..f148ebaf --- /dev/null +++ b/primitives/src/lib.rs @@ -0,0 +1,158 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Coding conventions + +#![deny( + non_upper_case_globals, + non_camel_case_types, + non_snake_case, + unused_mut, + unused_imports, + dead_code, + // TODO: Uncomment missing_docs +)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +#[macro_use] +extern crate amplify; +#[macro_use] +extern crate strict_encoding; +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde_crate as serde; + +pub extern crate secp256k1; + +pub mod opcodes; +mod script; +mod segwit; +mod sha256; +mod taproot; +mod tx; + +pub use script::{OpCode, ScriptPubkey, SigScript}; +pub use segwit::*; +pub use sha256::Sha256; +pub use taproot::*; +pub use tx::{LockTime, Outpoint, Sats, SeqNo, Tx, TxIn, TxOut, TxVer, Txid, Vout}; +pub use types::{ScriptBytes, VarIntArray}; + +pub const LIB_NAME_BP: &str = "BP"; + +mod types { + use std::fmt::{Formatter, LowerHex, UpperHex}; + + use amplify::confinement::Confined; + use amplify::hex::ToHex; + + use super::LIB_NAME_BP; + use crate::opcodes::*; + + pub type VarIntArray = Confined, 0, { u64::MAX as usize }>; + + #[derive( + Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, From + )] + #[derive(StrictType, StrictEncode, StrictDecode)] + #[strict_type(lib = LIB_NAME_BP)] + #[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) + )] + #[wrapper(Deref, Index, RangeOps, BorrowSlice)] + #[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)] + pub struct ScriptBytes(VarIntArray); + + impl From> for ScriptBytes { + fn from(value: Vec) -> Self { Self(Confined::try_from(value).expect("u64 >= usize")) } + } + + impl LowerHex for ScriptBytes { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0.as_inner().to_hex()) + } + } + + impl UpperHex for ScriptBytes { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0.as_inner().to_hex().to_uppercase()) + } + } + + impl ScriptBytes { + /// Adds instructions to push some arbitrary data onto the stack. + /// + /// ## Panics + /// + /// The method panics if `data` length is greater or equal to + /// 0x100000000. + pub fn push_slice(&mut self, data: &[u8]) { + // Start with a PUSH opcode + match data.len() as u64 { + n if n < OP_PUSHDATA1 as u64 => { + self.push(n as u8); + } + n if n < 0x100 => { + self.push(OP_PUSHDATA1); + self.push(n as u8); + } + n if n < 0x10000 => { + self.push(OP_PUSHDATA2); + self.push((n % 0x100) as u8); + self.push((n / 0x100) as u8); + } + n if n < 0x100000000 => { + self.push(OP_PUSHDATA4); + self.push((n % 0x100) as u8); + self.push(((n / 0x100) % 0x100) as u8); + self.push(((n / 0x10000) % 0x100) as u8); + self.push((n / 0x1000000) as u8); + } + _ => panic!("tried to put a 4bn+ sized object into a script!"), + } + // Then push the raw bytes + self.extend(data); + } + + #[inline] + fn push(&mut self, data: u8) { self.0.push(data).expect("script exceeds 4GB") } + + #[inline] + fn extend(&mut self, data: &[u8]) { + self.0 + .extend(data.iter().copied()) + .expect("script exceeds 4GB") + } + + /// Computes the sum of `len` and the lenght of an appropriate push + /// opcode. + pub fn len_for_slice(len: usize) -> usize { + len + match len { + 0..=0x4b => 1, + 0x4c..=0xff => 2, + 0x100..=0xffff => 3, + // we don't care about oversized, the other fn will panic anyway + _ => 5, + } + } + } +} diff --git a/primitives/src/opcodes.rs b/primitives/src/opcodes.rs new file mode 100644 index 00000000..3ae85497 --- /dev/null +++ b/primitives/src/opcodes.rs @@ -0,0 +1,550 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Push an empty array onto the stack. +pub const OP_PUSHBYTES_0: u8 = 0x00; +/// Push the next byte as an array onto the stack. +pub const OP_PUSHBYTES_1: u8 = 0x01; +/// Push the next 2 bytes as an array onto the stack. +pub const OP_PUSHBYTES_2: u8 = 0x02; +/// Push the next 3 bytes as an array onto the stack. +pub const OP_PUSHBYTES_3: u8 = 0x03; +/// Push the next 4 bytes as an array onto the stack. +pub const OP_PUSHBYTES_4: u8 = 0x04; +/// Push the next 5 bytes as an array onto the stack. +pub const OP_PUSHBYTES_5: u8 = 0x05; +/// Push the next 6 bytes as an array onto the stack. +pub const OP_PUSHBYTES_6: u8 = 0x06; +/// Push the next 7 bytes as an array onto the stack. +pub const OP_PUSHBYTES_7: u8 = 0x07; +/// Push the next 8 bytes as an array onto the stack. +pub const OP_PUSHBYTES_8: u8 = 0x08; +/// Push the next 9 bytes as an array onto the stack. +pub const OP_PUSHBYTES_9: u8 = 0x09; +/// Push the next 10 bytes as an array onto the stack. +pub const OP_PUSHBYTES_10: u8 = 0x0a; +/// Push the next 11 bytes as an array onto the stack. +pub const OP_PUSHBYTES_11: u8 = 0x0b; +/// Push the next 12 bytes as an array onto the stack. +pub const OP_PUSHBYTES_12: u8 = 0x0c; +/// Push the next 13 bytes as an array onto the stack. +pub const OP_PUSHBYTES_13: u8 = 0x0d; +/// Push the next 14 bytes as an array onto the stack. +pub const OP_PUSHBYTES_14: u8 = 0x0e; +/// Push the next 15 bytes as an array onto the stack. +pub const OP_PUSHBYTES_15: u8 = 0x0f; +/// Push the next 16 bytes as an array onto the stack. +pub const OP_PUSHBYTES_16: u8 = 0x10; +/// Push the next 17 bytes as an array onto the stack. +pub const OP_PUSHBYTES_17: u8 = 0x11; +/// Push the next 18 bytes as an array onto the stack. +pub const OP_PUSHBYTES_18: u8 = 0x12; +/// Push the next 19 bytes as an array onto the stack. +pub const OP_PUSHBYTES_19: u8 = 0x13; +/// Push the next 20 bytes as an array onto the stack. +pub const OP_PUSHBYTES_20: u8 = 0x14; +/// Push the next 21 bytes as an array onto the stack. +pub const OP_PUSHBYTES_21: u8 = 0x15; +/// Push the next 22 bytes as an array onto the stack. +pub const OP_PUSHBYTES_22: u8 = 0x16; +/// Push the next 23 bytes as an array onto the stack. +pub const OP_PUSHBYTES_23: u8 = 0x17; +/// Push the next 24 bytes as an array onto the stack. +pub const OP_PUSHBYTES_24: u8 = 0x18; +/// Push the next 25 bytes as an array onto the stack. +pub const OP_PUSHBYTES_25: u8 = 0x19; +/// Push the next 26 bytes as an array onto the stack. +pub const OP_PUSHBYTES_26: u8 = 0x1a; +/// Push the next 27 bytes as an array onto the stack. +pub const OP_PUSHBYTES_27: u8 = 0x1b; +/// Push the next 28 bytes as an array onto the stack. +pub const OP_PUSHBYTES_28: u8 = 0x1c; +/// Push the next 29 bytes as an array onto the stack. +pub const OP_PUSHBYTES_29: u8 = 0x1d; +/// Push the next 30 bytes as an array onto the stack. +pub const OP_PUSHBYTES_30: u8 = 0x1e; +/// Push the next 31 bytes as an array onto the stack. +pub const OP_PUSHBYTES_31: u8 = 0x1f; +/// Push the next 32 bytes as an array onto the stack. +pub const OP_PUSHBYTES_32: u8 = 0x20; +/// Push the next 33 bytes as an array onto the stack. +pub const OP_PUSHBYTES_33: u8 = 0x21; +/// Push the next 34 bytes as an array onto the stack. +pub const OP_PUSHBYTES_34: u8 = 0x22; +/// Push the next 35 bytes as an array onto the stack. +pub const OP_PUSHBYTES_35: u8 = 0x23; +/// Push the next 36 bytes as an array onto the stack. +pub const OP_PUSHBYTES_36: u8 = 0x24; +/// Push the next 37 bytes as an array onto the stack. +pub const OP_PUSHBYTES_37: u8 = 0x25; +/// Push the next 38 bytes as an array onto the stack. +pub const OP_PUSHBYTES_38: u8 = 0x26; +/// Push the next 39 bytes as an array onto the stack. +pub const OP_PUSHBYTES_39: u8 = 0x27; +/// Push the next 40 bytes as an array onto the stack. +pub const OP_PUSHBYTES_40: u8 = 0x28; +/// Push the next 41 bytes as an array onto the stack. +pub const OP_PUSHBYTES_41: u8 = 0x29; +/// Push the next 42 bytes as an array onto the stack. +pub const OP_PUSHBYTES_42: u8 = 0x2a; +/// Push the next 43 bytes as an array onto the stack. +pub const OP_PUSHBYTES_43: u8 = 0x2b; +/// Push the next 44 bytes as an array onto the stack. +pub const OP_PUSHBYTES_44: u8 = 0x2c; +/// Push the next 45 bytes as an array onto the stack. +pub const OP_PUSHBYTES_45: u8 = 0x2d; +/// Push the next 46 bytes as an array onto the stack. +pub const OP_PUSHBYTES_46: u8 = 0x2e; +/// Push the next 47 bytes as an array onto the stack. +pub const OP_PUSHBYTES_47: u8 = 0x2f; +/// Push the next 48 bytes as an array onto the stack. +pub const OP_PUSHBYTES_48: u8 = 0x30; +/// Push the next 49 bytes as an array onto the stack. +pub const OP_PUSHBYTES_49: u8 = 0x31; +/// Push the next 50 bytes as an array onto the stack. +pub const OP_PUSHBYTES_50: u8 = 0x32; +/// Push the next 51 bytes as an array onto the stack. +pub const OP_PUSHBYTES_51: u8 = 0x33; +/// Push the next 52 bytes as an array onto the stack. +pub const OP_PUSHBYTES_52: u8 = 0x34; +/// Push the next 53 bytes as an array onto the stack. +pub const OP_PUSHBYTES_53: u8 = 0x35; +/// Push the next 54 bytes as an array onto the stack. +pub const OP_PUSHBYTES_54: u8 = 0x36; +/// Push the next 55 bytes as an array onto the stack. +pub const OP_PUSHBYTES_55: u8 = 0x37; +/// Push the next 56 bytes as an array onto the stack. +pub const OP_PUSHBYTES_56: u8 = 0x38; +/// Push the next 57 bytes as an array onto the stack. +pub const OP_PUSHBYTES_57: u8 = 0x39; +/// Push the next 58 bytes as an array onto the stack. +pub const OP_PUSHBYTES_58: u8 = 0x3a; +/// Push the next 59 bytes as an array onto the stack. +pub const OP_PUSHBYTES_59: u8 = 0x3b; +/// Push the next 60 bytes as an array onto the stack. +pub const OP_PUSHBYTES_60: u8 = 0x3c; +/// Push the next 61 bytes as an array onto the stack. +pub const OP_PUSHBYTES_61: u8 = 0x3d; +/// Push the next 62 bytes as an array onto the stack. +pub const OP_PUSHBYTES_62: u8 = 0x3e; +/// Push the next 63 bytes as an array onto the stack. +pub const OP_PUSHBYTES_63: u8 = 0x3f; +/// Push the next 64 bytes as an array onto the stack. +pub const OP_PUSHBYTES_64: u8 = 0x40; +/// Push the next 65 bytes as an array onto the stack. +pub const OP_PUSHBYTES_65: u8 = 0x41; +/// Push the next 66 bytes as an array onto the stack. +pub const OP_PUSHBYTES_66: u8 = 0x42; +/// Push the next 67 bytes as an array onto the stack. +pub const OP_PUSHBYTES_67: u8 = 0x43; +/// Push the next 68 bytes as an array onto the stack. +pub const OP_PUSHBYTES_68: u8 = 0x44; +/// Push the next 69 bytes as an array onto the stack. +pub const OP_PUSHBYTES_69: u8 = 0x45; +/// Push the next 70 bytes as an array onto the stack. +pub const OP_PUSHBYTES_70: u8 = 0x46; +/// Push the next 71 bytes as an array onto the stack. +pub const OP_PUSHBYTES_71: u8 = 0x47; +/// Push the next 72 bytes as an array onto the stack. +pub const OP_PUSHBYTES_72: u8 = 0x48; +/// Push the next 73 bytes as an array onto the stack. +pub const OP_PUSHBYTES_73: u8 = 0x49; +/// Push the next 74 bytes as an array onto the stack. +pub const OP_PUSHBYTES_74: u8 = 0x4a; +/// Push the next 75 bytes as an array onto the stack. +pub const OP_PUSHBYTES_75: u8 = 0x4b; +/// Read the next byte as N; push the next N bytes as an array onto the stack. +pub const OP_PUSHDATA1: u8 = 0x4c; +/// Read the next 2 bytes as N; push the next N bytes as an array onto the +/// stack. +pub const OP_PUSHDATA2: u8 = 0x4d; +/// Read the next 4 bytes as N; push the next N bytes as an array onto the +/// stack. +pub const OP_PUSHDATA4: u8 = 0x4e; +/// Push the array `0x81` onto the stack. +pub const OP_PUSHNUM_NEG1: u8 = 0x4f; +/// Synonym for OP_RETURN. +pub const OP_RESERVED: u8 = 0x50; +/// Push the array `0x01` onto the stack. +pub const OP_PUSHNUM_1: u8 = 0x51; +/// the array `0x02` onto the stack. +pub const OP_PUSHNUM_2: u8 = 0x52; +/// Push the array `0x03` onto the stack. +pub const OP_PUSHNUM_3: u8 = 0x53; +/// Push the array `0x04` onto the stack. +pub const OP_PUSHNUM_4: u8 = 0x54; +/// Push the array `0x05` onto the stack. +pub const OP_PUSHNUM_5: u8 = 0x55; +/// Push the array `0x06` onto the stack. +pub const OP_PUSHNUM_6: u8 = 0x56; +/// Push the array `0x07` onto the stack. +pub const OP_PUSHNUM_7: u8 = 0x57; +/// Push the array `0x08` onto the stack. +pub const OP_PUSHNUM_8: u8 = 0x58; +/// Push the array `0x09` onto the stack. +pub const OP_PUSHNUM_9: u8 = 0x59; +/// Push the array `0x0a` onto the stack. +pub const OP_PUSHNUM_10: u8 = 0x5a; +/// Push the array `0x0b` onto the stack. +pub const OP_PUSHNUM_11: u8 = 0x5b; +/// Push the array `0x0c` onto the stack. +pub const OP_PUSHNUM_12: u8 = 0x5c; +/// Push the array `0x0d` onto the stack. +pub const OP_PUSHNUM_13: u8 = 0x5d; +/// Push the array `0x0e` onto the stack. +pub const OP_PUSHNUM_14: u8 = 0x5e; +/// Push the array `0x0f` onto the stack. +pub const OP_PUSHNUM_15: u8 = 0x5f; +/// Push the array `0x10` onto the stack. +pub const OP_PUSHNUM_16: u8 = 0x60; +/// Does nothing. +pub const OP_NOP: u8 = 0x61; +/// Synonym for OP_RETURN. +pub const OP_VER: u8 = 0x62; +/// Pop and execute the next statements if a nonzero element was popped. +pub const OP_IF: u8 = 0x63; +/// Pop and execute the next statements if a zero element was popped. +pub const OP_NOTIF: u8 = 0x64; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_VERIF: u8 = 0x65; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_VERNOTIF: u8 = 0x66; +/// Execute statements if those after the previous OP_IF were not, and +/// vice-versa. If there is no previous OP_IF, this acts as an OP_RETURN. +pub const OP_ELSE: u8 = 0x67; +/// Pop and execute the next statements if a zero element was popped. +pub const OP_ENDIF: u8 = 0x68; +/// If the top value is zero or the stack is empty, fail; otherwise, pop the +/// stack. +pub const OP_VERIFY: u8 = 0x69; +/// Fail the script immediately. (Must be executed.). +pub const OP_RETURN: u8 = 0x6a; +/// Pop one element from the main stack onto the alt stack. +pub const OP_TOALTSTACK: u8 = 0x6b; +/// Pop one element from the alt stack onto the main stack. +pub const OP_FROMALTSTACK: u8 = 0x6c; +/// Drops the top two stack items. +pub const OP_2DROP: u8 = 0x6d; +/// Duplicates the top two stack items as AB -> ABAB. +pub const OP_2DUP: u8 = 0x6e; +/// Duplicates the two three stack items as ABC -> ABCABC. +pub const OP_3DUP: u8 = 0x6f; +/// Copies the two stack items of items two spaces back to the front, as xxAB -> +/// ABxxAB. +pub const OP_2OVER: u8 = 0x70; +/// Moves the two stack items four spaces back to the front, as xxxxAB -> +/// ABxxxx. +pub const OP_2ROT: u8 = 0x71; +/// Swaps the top two pairs, as ABCD -> CDAB. +pub const OP_2SWAP: u8 = 0x72; +/// Duplicate the top stack element unless it is zero. +pub const OP_IFDUP: u8 = 0x73; +/// Push the current number of stack items onto the stack. +pub const OP_DEPTH: u8 = 0x74; +/// Drops the top stack item. +pub const OP_DROP: u8 = 0x75; +/// Duplicates the top stack item. +pub const OP_DUP: u8 = 0x76; +/// Drops the second-to-top stack item. +pub const OP_NIP: u8 = 0x77; +/// Copies the second-to-top stack item, as xA -> AxA. +pub const OP_OVER: u8 = 0x78; +/// Pop the top stack element as N. Copy the Nth stack element to the top. +pub const OP_PICK: u8 = 0x79; +/// Pop the top stack element as N. Move the Nth stack element to the top. +pub const OP_ROLL: u8 = 0x7a; +/// Rotate the top three stack items, as [top next1 next2] -> [next2 top next1]. +pub const OP_ROT: u8 = 0x7b; +/// Swap the top two stack items. +pub const OP_SWAP: u8 = 0x7c; +/// Copy the top stack item to before the second item, as [top next] -> [top +/// next top]. +pub const OP_TUCK: u8 = 0x7d; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_CAT: u8 = 0x7e; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_SUBSTR: u8 = 0x7f; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_LEFT: u8 = 0x80; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_RIGHT: u8 = 0x81; +/// Pushes the length of the top stack item onto the stack. +pub const OP_SIZE: u8 = 0x82; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_INVERT: u8 = 0x83; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_AND: u8 = 0x84; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_OR: u8 = 0x85; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_XOR: u8 = 0x86; +/// Pushes 1 if the inputs are exactly equal, 0 otherwise. +pub const OP_EQUAL: u8 = 0x87; +/// Returns success if the inputs are exactly equal, failure otherwise. +pub const OP_EQUALVERIFY: u8 = 0x88; +/// Synonym for OP_RETURN. +pub const OP_RESERVED1: u8 = 0x89; +/// Synonym for OP_RETURN. +pub const OP_RESERVED2: u8 = 0x8a; +/// Increment the top stack element in place. +pub const OP_1ADD: u8 = 0x8b; +/// Decrement the top stack element in place. +pub const OP_1SUB: u8 = 0x8c; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_2MUL: u8 = 0x8d; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_2DIV: u8 = 0x8e; +/// Multiply the top stack item by -1 in place. +pub const OP_NEGATE: u8 = 0x8f; +/// Absolute value the top stack item in place. +pub const OP_ABS: u8 = 0x90; +/// Map 0 to 1 and everything else to 0, in place. +pub const OP_NOT: u8 = 0x91; +/// Map 0 to 0 and everything else to 1, in place. +pub const OP_0NOTEQUAL: u8 = 0x92; +/// Pop two stack items and push their sum. +pub const OP_ADD: u8 = 0x93; +/// Pop two stack items and push the second minus the top. +pub const OP_SUB: u8 = 0x94; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_MUL: u8 = 0x95; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_DIV: u8 = 0x96; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_MOD: u8 = 0x97; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_LSHIFT: u8 = 0x98; +/// Fail the script unconditionally, does not even need to be executed. +pub const OP_RSHIFT: u8 = 0x99; +/// Pop the top two stack items and push 1 if both are nonzero, else push 0. +pub const OP_BOOLAND: u8 = 0x9a; +/// Pop the top two stack items and push 1 if either is nonzero, else push 0. +pub const OP_BOOLOR: u8 = 0x9b; +/// Pop the top two stack items and push 1 if both are numerically equal, else +/// push 0. +pub const OP_NUMEQUAL: u8 = 0x9c; +/// Pop the top two stack items and return success if both are numerically +/// equal, else return failure. +pub const OP_NUMEQUALVERIFY: u8 = 0x9d; +/// Pop the top two stack items and push 0 if both are numerically equal, else +/// push 1. +pub const OP_NUMNOTEQUAL: u8 = 0x9e; +/// Pop the top two items; push 1 if the second is less than the top, 0 +/// otherwise. +pub const OP_LESSTHAN: u8 = 0x9f; +/// Pop the top two items; push 1 if the second is greater than the top, 0 +/// otherwise. +pub const OP_GREATERTHAN: u8 = 0xa0; +/// Pop the top two items; push 1 if the second is <= the top, 0 otherwise. +pub const OP_LESSTHANOREQUAL: u8 = 0xa1; +/// Pop the top two items; push 1 if the second is >= the top, 0 otherwise. +pub const OP_GREATERTHANOREQUAL: u8 = 0xa2; +/// Pop the top two items; push the smaller. +pub const OP_MIN: u8 = 0xa3; +/// Pop the top two items; push the larger. +pub const OP_MAX: u8 = 0xa4; +/// Pop the top three items; if the top is >= the second and < the third, push +/// 1, otherwise push 0. +pub const OP_WITHIN: u8 = 0xa5; +/// Pop the top stack item and push its RIPEMD160 hash. +pub const OP_RIPEMD160: u8 = 0xa6; +/// Pop the top stack item and push its SHA1 hash. +pub const OP_SHA1: u8 = 0xa7; +/// Pop the top stack item and push its SHA256 hash. +pub const OP_SHA256: u8 = 0xa8; +/// Pop the top stack item and push its RIPEMD(SHA256) hash. +pub const OP_HASH160: u8 = 0xa9; +/// Pop the top stack item and push its SHA256(SHA256) hash. +pub const OP_HASH256: u8 = 0xaa; +/// Ignore this and everything preceding when deciding what to sign when +/// signature-checking. +pub const OP_CODESEPARATOR: u8 = 0xab; +/// pushing 1/0 for success/failure. +pub const OP_CHECKSIG: u8 = 0xac; +/// returning success/failure. +pub const OP_CHECKSIGVERIFY: u8 = 0xad; +/// 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. +pub const OP_CHECKMULTISIG: u8 = 0xae; +/// Like the above but return success/failure. +pub const OP_CHECKMULTISIGVERIFY: u8 = 0xaf; +/// Does nothing. +pub const OP_NOP1: u8 = 0xb0; +/// +pub const OP_CLTV: u8 = 0xb1; +/// +pub const OP_CSV: u8 = 0xb2; +/// Does nothing. +pub const OP_NOP4: u8 = 0xb3; +/// Does nothing. +pub const OP_NOP5: u8 = 0xb4; +/// Does nothing. +pub const OP_NOP6: u8 = 0xb5; +/// Does nothing. +pub const OP_NOP7: u8 = 0xb6; +/// Does nothing. +pub const OP_NOP8: u8 = 0xb7; +/// Does nothing. +pub const OP_NOP9: u8 = 0xb8; +/// Does nothing. +pub const OP_NOP10: u8 = 0xb9; +// Every other opcode acts as OP_RETURN +/// OP_CHECKSIGADD post tapscript. +pub const OP_CHECKSIGADD: u8 = 0xba; +/// Synonym for OP_RETURN. +pub const OP_RETURN_187: u8 = 0xbb; +/// Synonym for OP_RETURN. +pub const OP_RETURN_188: u8 = 0xbc; +/// Synonym for OP_RETURN. +pub const OP_RETURN_189: u8 = 0xbd; +/// Synonym for OP_RETURN. +pub const OP_RETURN_190: u8 = 0xbe; +/// Synonym for OP_RETURN. +pub const OP_RETURN_191: u8 = 0xbf; +/// Synonym for OP_RETURN. +pub const OP_RETURN_192: u8 = 0xc0; +/// Synonym for OP_RETURN. +pub const OP_RETURN_193: u8 = 0xc1; +/// Synonym for OP_RETURN. +pub const OP_RETURN_194: u8 = 0xc2; +/// Synonym for OP_RETURN. +pub const OP_RETURN_195: u8 = 0xc3; +/// Synonym for OP_RETURN. +pub const OP_RETURN_196: u8 = 0xc4; +/// Synonym for OP_RETURN. +pub const OP_RETURN_197: u8 = 0xc5; +/// Synonym for OP_RETURN. +pub const OP_RETURN_198: u8 = 0xc6; +/// Synonym for OP_RETURN. +pub const OP_RETURN_199: u8 = 0xc7; +/// Synonym for OP_RETURN. +pub const OP_RETURN_200: u8 = 0xc8; +/// Synonym for OP_RETURN. +pub const OP_RETURN_201: u8 = 0xc9; +/// Synonym for OP_RETURN. +pub const OP_RETURN_202: u8 = 0xca; +/// Synonym for OP_RETURN. +pub const OP_RETURN_203: u8 = 0xcb; +/// Synonym for OP_RETURN. +pub const OP_RETURN_204: u8 = 0xcc; +/// Synonym for OP_RETURN. +pub const OP_RETURN_205: u8 = 0xcd; +/// Synonym for OP_RETURN. +pub const OP_RETURN_206: u8 = 0xce; +/// Synonym for OP_RETURN. +pub const OP_RETURN_207: u8 = 0xcf; +/// Synonym for OP_RETURN. +pub const OP_RETURN_208: u8 = 0xd0; +/// Synonym for OP_RETURN. +pub const OP_RETURN_209: u8 = 0xd1; +/// Synonym for OP_RETURN. +pub const OP_RETURN_210: u8 = 0xd2; +/// Synonym for OP_RETURN. +pub const OP_RETURN_211: u8 = 0xd3; +/// Synonym for OP_RETURN. +pub const OP_RETURN_212: u8 = 0xd4; +/// Synonym for OP_RETURN. +pub const OP_RETURN_213: u8 = 0xd5; +/// Synonym for OP_RETURN. +pub const OP_RETURN_214: u8 = 0xd6; +/// Synonym for OP_RETURN. +pub const OP_RETURN_215: u8 = 0xd7; +/// Synonym for OP_RETURN. +pub const OP_RETURN_216: u8 = 0xd8; +/// Synonym for OP_RETURN. +pub const OP_RETURN_217: u8 = 0xd9; +/// Synonym for OP_RETURN. +pub const OP_RETURN_218: u8 = 0xda; +/// Synonym for OP_RETURN. +pub const OP_RETURN_219: u8 = 0xdb; +/// Synonym for OP_RETURN. +pub const OP_RETURN_220: u8 = 0xdc; +/// Synonym for OP_RETURN. +pub const OP_RETURN_221: u8 = 0xdd; +/// Synonym for OP_RETURN. +pub const OP_RETURN_222: u8 = 0xde; +/// Synonym for OP_RETURN. +pub const OP_RETURN_223: u8 = 0xdf; +/// Synonym for OP_RETURN. +pub const OP_RETURN_224: u8 = 0xe0; +/// Synonym for OP_RETURN. +pub const OP_RETURN_225: u8 = 0xe1; +/// Synonym for OP_RETURN. +pub const OP_RETURN_226: u8 = 0xe2; +/// Synonym for OP_RETURN. +pub const OP_RETURN_227: u8 = 0xe3; +/// Synonym for OP_RETURN. +pub const OP_RETURN_228: u8 = 0xe4; +/// Synonym for OP_RETURN. +pub const OP_RETURN_229: u8 = 0xe5; +/// Synonym for OP_RETURN. +pub const OP_RETURN_230: u8 = 0xe6; +/// Synonym for OP_RETURN. +pub const OP_RETURN_231: u8 = 0xe7; +/// Synonym for OP_RETURN. +pub const OP_RETURN_232: u8 = 0xe8; +/// Synonym for OP_RETURN. +pub const OP_RETURN_233: u8 = 0xe9; +/// Synonym for OP_RETURN. +pub const OP_RETURN_234: u8 = 0xea; +/// Synonym for OP_RETURN. +pub const OP_RETURN_235: u8 = 0xeb; +/// Synonym for OP_RETURN. +pub const OP_RETURN_236: u8 = 0xec; +/// Synonym for OP_RETURN. +pub const OP_RETURN_237: u8 = 0xed; +/// Synonym for OP_RETURN. +pub const OP_RETURN_238: u8 = 0xee; +/// Synonym for OP_RETURN. +pub const OP_RETURN_239: u8 = 0xef; +/// Synonym for OP_RETURN. +pub const OP_RETURN_240: u8 = 0xf0; +/// Synonym for OP_RETURN. +pub const OP_RETURN_241: u8 = 0xf1; +/// Synonym for OP_RETURN. +pub const OP_RETURN_242: u8 = 0xf2; +/// Synonym for OP_RETURN. +pub const OP_RETURN_243: u8 = 0xf3; +/// Synonym for OP_RETURN. +pub const OP_RETURN_244: u8 = 0xf4; +/// Synonym for OP_RETURN. +pub const OP_RETURN_245: u8 = 0xf5; +/// Synonym for OP_RETURN. +pub const OP_RETURN_246: u8 = 0xf6; +/// Synonym for OP_RETURN. +pub const OP_RETURN_247: u8 = 0xf7; +/// Synonym for OP_RETURN. +pub const OP_RETURN_248: u8 = 0xf8; +/// Synonym for OP_RETURN. +pub const OP_RETURN_249: u8 = 0xf9; +/// Synonym for OP_RETURN. +pub const OP_RETURN_250: u8 = 0xfa; +/// Synonym for OP_RETURN. +pub const OP_RETURN_251: u8 = 0xfb; +/// Synonym for OP_RETURN. +pub const OP_RETURN_252: u8 = 0xfc; +/// Synonym for OP_RETURN. +pub const OP_RETURN_253: u8 = 0xfd; +/// Synonym for OP_RETURN. +pub const OP_RETURN_254: u8 = 0xfe; +/// Synonym for OP_RETURN. +pub const OP_INVALIDOPCODE: u8 = 0xff; diff --git a/primitives/src/script.rs b/primitives/src/script.rs new file mode 100644 index 00000000..183387b0 --- /dev/null +++ b/primitives/src/script.rs @@ -0,0 +1,103 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use amplify::confinement::Confined; + +use crate::opcodes::*; +use crate::{ScriptBytes, LIB_NAME_BP}; + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +// TODO: Replace `try_from` with `from` since opcodes cover whole range of u8 +#[strict_type(lib = LIB_NAME_BP, tags = repr, into_u8, try_from_u8)] +#[repr(u8)] +pub enum OpCode { + /// Push the next 32 bytes as an array onto the stack. + #[display("OP_PUSH_BYTES32")] + PushBytes32 = OP_PUSHBYTES_32, + + /// Synonym for OP_RETURN. + Reserved = OP_RESERVED, + + /// Fail the script immediately. + #[display("OP_RETURN")] + #[strict_type(dumb)] + Return = OP_RETURN, + + /// Read the next byte as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA1")] + PushData1 = OP_PUSHDATA1, + /// Read the next 2 bytes as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA2")] + PushData2 = OP_PUSHDATA2, + /// Read the next 4 bytes as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA3")] + PushData4 = OP_PUSHDATA4, +} + +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)] +#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)] +#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct SigScript(ScriptBytes); + +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)] +#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)] +#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct ScriptPubkey(ScriptBytes); + +impl ScriptPubkey { + pub fn new() -> Self { Self::default() } + + pub fn with_capacity(capacity: usize) -> Self { + Self(ScriptBytes::from(Confined::with_capacity(capacity))) + } + + pub fn op_return(data: &[u8]) -> Self { + let mut script = Self::with_capacity(ScriptBytes::len_for_slice(data.len()) + 1); + script.push_opcode(OpCode::Return); + script.0.push_slice(data); + script + } + + pub fn is_op_return(&self) -> bool { self[0] == OpCode::Return as u8 } + + /// Adds a single opcode to the script. + pub fn push_opcode(&mut self, op_code: OpCode) { + self.0.push(op_code as u8).expect("script exceeds 4GB"); + } +} diff --git a/primitives/src/segwit.rs b/primitives/src/segwit.rs new file mode 100644 index 00000000..b7fce0b8 --- /dev/null +++ b/primitives/src/segwit.rs @@ -0,0 +1,203 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::opcodes::*; +use crate::{OpCode, ScriptBytes, ScriptPubkey}; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)] +#[display(doc_comments)] +pub enum SegwitError { + /// Script version must be 0 to 16 inclusive. + InvalidWitnessVersion(u8), + /// Bitcoin script opcode does not match any known witness version, the + /// script is malformed. + MalformedWitnessVersion, + /// The witness program must be between 2 and 40 bytes in length. + InvalidWitnessProgramLength(usize), + /// A v0 witness program must be either of length 20 or 32. + InvalidSegwitV0ProgramLength(usize), + /// An uncompressed pubkey was used where it is not allowed. + UncompressedPubkey, +} + +/// Version of the witness program. +/// +/// First byte of `scriptPubkey` in transaction output for transactions starting +/// with 0 and 0x51-0x60 (inclusive). +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] +#[repr(u8)] +pub enum WitnessVer { + /// Initial version of witness program. Used for P2WPKH and P2WPK outputs + #[display("segwit0")] + V0 = OP_PUSHBYTES_0, + + /// Version of witness program used for Taproot P2TR outputs. + #[display("segwit1")] + V1 = OP_PUSHNUM_1, + + /// Future (unsupported) version of witness program. + #[display("segwit2")] + V2 = OP_PUSHNUM_2, + + /// Future (unsupported) version of witness program. + #[display("segwit3")] + V3 = OP_PUSHNUM_3, + + /// Future (unsupported) version of witness program. + #[display("segwit4")] + V4 = OP_PUSHNUM_4, + + /// Future (unsupported) version of witness program. + #[display("segwit5")] + V5 = OP_PUSHNUM_5, + + /// Future (unsupported) version of witness program. + #[display("segwit6")] + V6 = OP_PUSHNUM_6, + + /// Future (unsupported) version of witness program. + #[display("segwit7")] + V7 = OP_PUSHNUM_7, + + /// Future (unsupported) version of witness program. + #[display("segwit8")] + V8 = OP_PUSHNUM_8, + + /// Future (unsupported) version of witness program. + #[display("segwit9")] + V9 = OP_PUSHNUM_9, + + /// Future (unsupported) version of witness program. + #[display("segwit10")] + V10 = OP_PUSHNUM_10, + + /// Future (unsupported) version of witness program. + #[display("segwit11")] + V11 = OP_PUSHNUM_11, + + /// Future (unsupported) version of witness program. + #[display("segwit12")] + V12 = OP_PUSHNUM_12, + + /// Future (unsupported) version of witness program. + #[display("segwit13")] + V13 = OP_PUSHNUM_13, + + /// Future (unsupported) version of witness program. + #[display("segwit14")] + V14 = OP_PUSHNUM_14, + + /// Future (unsupported) version of witness program. + #[display("segwit15")] + V15 = OP_PUSHNUM_15, + + /// Future (unsupported) version of witness program. + #[display("segwit16")] + V16 = OP_PUSHNUM_16, +} + +impl WitnessVer { + /// Converts bitcoin script opcode into [`WitnessVer`] variant. + /// + /// # Returns + /// Version of the Witness program. + /// + /// # Errors + /// If the opcode does not correspond to any witness version, errors with + /// [`SegwitError::MalformedWitnessVersion`]. + pub fn from_op_code(op_code: OpCode) -> Result { + match op_code as u8 { + 0 => Ok(WitnessVer::V0), + OP_PUSHNUM_1 => Ok(WitnessVer::V1), + OP_PUSHNUM_2 => Ok(WitnessVer::V2), + OP_PUSHNUM_3 => Ok(WitnessVer::V3), + OP_PUSHNUM_4 => Ok(WitnessVer::V4), + OP_PUSHNUM_5 => Ok(WitnessVer::V5), + OP_PUSHNUM_6 => Ok(WitnessVer::V6), + OP_PUSHNUM_7 => Ok(WitnessVer::V7), + OP_PUSHNUM_8 => Ok(WitnessVer::V8), + OP_PUSHNUM_9 => Ok(WitnessVer::V9), + OP_PUSHNUM_10 => Ok(WitnessVer::V10), + OP_PUSHNUM_11 => Ok(WitnessVer::V11), + OP_PUSHNUM_12 => Ok(WitnessVer::V12), + OP_PUSHNUM_13 => Ok(WitnessVer::V13), + OP_PUSHNUM_14 => Ok(WitnessVer::V14), + OP_PUSHNUM_15 => Ok(WitnessVer::V15), + OP_PUSHNUM_16 => Ok(WitnessVer::V16), + _ => Err(SegwitError::MalformedWitnessVersion), + } + } + + /// Converts [`WitnessVer`] instance into corresponding Bitcoin op-code. + // TODO: Replace `try_from` with `from` since opcodes cover whole range of + // u8 + pub fn op_code(self) -> OpCode { + OpCode::try_from(self as u8).expect("full range of u8 is covered") + } +} + +/// Witness program as defined in BIP141. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WitnessProgram { + /// The witness program version. + version: WitnessVer, + /// The witness program. (Between 2 and 40 bytes) + program: Vec, +} + +impl WitnessProgram { + /// Creates a new witness program. + pub fn new(version: WitnessVer, program: Vec) -> Result { + if program.len() < 2 || program.len() > 40 { + return Err(SegwitError::InvalidWitnessProgramLength(program.len())); + } + + // Specific segwit v0 check. These addresses can never spend funds sent + // to them. + if version == WitnessVer::V0 && (program.len() != 20 && program.len() != 32) { + return Err(SegwitError::InvalidSegwitV0ProgramLength(program.len())); + } + + Ok(WitnessProgram { version, program }) + } + + /// Returns the witness program version. + pub fn version(&self) -> WitnessVer { self.version } + + /// Returns the witness program. + pub fn program(&self) -> &[u8] { &self.program } +} + +impl ScriptPubkey { + /// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`]. + pub fn from_witness_program(witness_program: &WitnessProgram) -> Self { + Self::with_segwit_unchecked(witness_program.version, witness_program.program()) + } + + /// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and + /// the program bytes. Does not do any checks on version or program length. + pub(crate) fn with_segwit_unchecked(ver: WitnessVer, prog: &[u8]) -> Self { + let mut script = Self::with_capacity(ScriptBytes::len_for_slice(prog.len()) + 2); + script.push_opcode(ver.op_code()); + script.push_slice(prog); + script + } +} diff --git a/primitives/src/sha256.rs b/primitives/src/sha256.rs new file mode 100644 index 00000000..7e17520a --- /dev/null +++ b/primitives/src/sha256.rs @@ -0,0 +1,258 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{cmp, io}; + +const BLOCK_SIZE: usize = 64; + +macro_rules! ch ( ($x:expr, $y:expr, $z:expr) => ($z ^ ($x & ($y ^ $z))) ); +macro_rules! maj ( ($x:expr, $y:expr, $z:expr) => (($x & $y) | ($z & ($x | $y))) ); +macro_rules! _sigma0 ( ($x:expr) => ($x.rotate_left(30) ^ $x.rotate_left(19) ^ $x.rotate_left(10)) ); +macro_rules! _sigma1 ( ($x:expr) => ($x.rotate_left(26) ^ $x.rotate_left(21) ^ $x.rotate_left(7)) ); +macro_rules! sigma0 ( ($x:expr) => ($x.rotate_left(25) ^ $x.rotate_left(14) ^ ($x >> 3)) ); +macro_rules! sigma1 ( ($x:expr) => ($x.rotate_left(15) ^ $x.rotate_left(13) ^ ($x >> 10)) ); + +macro_rules! round( + // first round + ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr) => ( + let t1 = $h.wrapping_add(_sigma1!($e)).wrapping_add(ch!($e, $f, $g)).wrapping_add($k).wrapping_add($w); + let t2 = _sigma0!($a).wrapping_add(maj!($a, $b, $c)); + $d = $d.wrapping_add(t1); + $h = t1.wrapping_add(t2); + ); + // later rounds we reassign $w before doing the first-round computation + ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr, $w1:expr, $w2:expr, $w3:expr) => ( + $w = $w.wrapping_add(sigma1!($w1)).wrapping_add($w2).wrapping_add(sigma0!($w3)); + round!($a, $b, $c, $d, $e, $f, $g, $h, $k, $w); + ) +); + +/// Engine to compute SHA256 hash function. +#[derive(Clone)] +pub struct Sha256 { + buffer: [u8; BLOCK_SIZE], + h: [u32; 8], + length: usize, +} + +impl Default for Sha256 { + fn default() -> Self { + Sha256 { + h: [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, + 0x5be0cd19, + ], + length: 0, + buffer: [0; BLOCK_SIZE], + } + } +} + +impl Sha256 { + pub fn digest(inp: &[u8]) -> [u8; 32] { + let mut engine = Self::default(); + engine.input_raw(inp); + engine.finish() + } + + pub fn from_tag(midstate: [u8; 32]) -> Self { + let mut ret = [0; 8]; + for (ret_val, midstate_bytes) in ret.iter_mut().zip(midstate[..].chunks_exact(4)) { + *ret_val = u32::from_be_bytes(midstate_bytes.try_into().expect("4 byte slice")); + } + + Self { + buffer: [0; BLOCK_SIZE], + h: ret, + length: 64, + } + } + + pub fn midstate(&self) -> [u8; 32] { + let mut ret = [0; 32]; + for (val, ret_bytes) in self.h.iter().zip(ret.chunks_exact_mut(4)) { + ret_bytes.copy_from_slice(&val.to_be_bytes()); + } + ret + } + + pub fn input_raw(&mut self, mut inp: &[u8]) { + while !inp.is_empty() { + let buf_idx = self.length % BLOCK_SIZE; + let rem_len = BLOCK_SIZE - buf_idx; + let write_len = cmp::min(rem_len, inp.len()); + + self.buffer[buf_idx..buf_idx + write_len].copy_from_slice(&inp[..write_len]); + self.length += write_len; + if self.length % BLOCK_SIZE == 0 { + self.process_block(); + } + inp = &inp[write_len..]; + } + } + + pub fn input_with_len(&mut self, inp: &[u8]) { + let len = inp.len(); + match len { + 0..=0xFC => { + self.input_raw(&[len as u8]); + } + 0xFD..=0xFFFF => { + self.input_raw(&[0xFD]); + self.input_raw(&(len as u16).to_le_bytes()); + } + 0x10000..=0xFFFFFFFF => { + self.input_raw(&[0xFE]); + self.input_raw(&(len as u32).to_le_bytes()); + } + _ => { + self.input_raw(&[0xFF]); + self.input_raw(&(len as u64).to_le_bytes()); + } + }; + self.input_raw(inp) + } + + pub fn finish(mut self) -> [u8; 32] { + // pad buffer with a single 1-bit then all 0s, until there are exactly 8 + // bytes remaining + let data_len = self.length as u64; + + let zeroes = [0; BLOCK_SIZE - 8]; + self.input_raw(&[0x80]); + if self.length % BLOCK_SIZE > zeroes.len() { + self.input_raw(&zeroes); + } + let pad_length = zeroes.len() - (self.length % BLOCK_SIZE); + self.input_raw(&zeroes[..pad_length]); + debug_assert_eq!(self.length % BLOCK_SIZE, zeroes.len()); + + self.input_raw(&(8 * data_len).to_be_bytes()); + debug_assert_eq!(self.length % BLOCK_SIZE, 0); + + self.midstate() + } + + // Algorithm copied from libsecp256k1 + fn process_block(&mut self) { + debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); + + let mut w = [0u32; 16]; + for (w_val, buff_bytes) in w.iter_mut().zip(self.buffer.chunks_exact(4)) { + *w_val = u32::from_be_bytes(buff_bytes.try_into().expect("4 byte slice")); + } + + let mut a = self.h[0]; + let mut b = self.h[1]; + let mut c = self.h[2]; + let mut d = self.h[3]; + let mut e = self.h[4]; + let mut f = self.h[5]; + let mut g = self.h[6]; + let mut h = self.h[7]; + + round!(a, b, c, d, e, f, g, h, 0x428a2f98, w[0]); + round!(h, a, b, c, d, e, f, g, 0x71374491, w[1]); + round!(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w[2]); + round!(f, g, h, a, b, c, d, e, 0xe9b5dba5, w[3]); + round!(e, f, g, h, a, b, c, d, 0x3956c25b, w[4]); + round!(d, e, f, g, h, a, b, c, 0x59f111f1, w[5]); + round!(c, d, e, f, g, h, a, b, 0x923f82a4, w[6]); + round!(b, c, d, e, f, g, h, a, 0xab1c5ed5, w[7]); + round!(a, b, c, d, e, f, g, h, 0xd807aa98, w[8]); + round!(h, a, b, c, d, e, f, g, 0x12835b01, w[9]); + round!(g, h, a, b, c, d, e, f, 0x243185be, w[10]); + round!(f, g, h, a, b, c, d, e, 0x550c7dc3, w[11]); + round!(e, f, g, h, a, b, c, d, 0x72be5d74, w[12]); + round!(d, e, f, g, h, a, b, c, 0x80deb1fe, w[13]); + round!(c, d, e, f, g, h, a, b, 0x9bdc06a7, w[14]); + round!(b, c, d, e, f, g, h, a, 0xc19bf174, w[15]); + + round!(a, b, c, d, e, f, g, h, 0xe49b69c1, w[0], w[14], w[9], w[1]); + round!(h, a, b, c, d, e, f, g, 0xefbe4786, w[1], w[15], w[10], w[2]); + round!(g, h, a, b, c, d, e, f, 0x0fc19dc6, w[2], w[0], w[11], w[3]); + round!(f, g, h, a, b, c, d, e, 0x240ca1cc, w[3], w[1], w[12], w[4]); + round!(e, f, g, h, a, b, c, d, 0x2de92c6f, w[4], w[2], w[13], w[5]); + round!(d, e, f, g, h, a, b, c, 0x4a7484aa, w[5], w[3], w[14], w[6]); + round!(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w[6], w[4], w[15], w[7]); + round!(b, c, d, e, f, g, h, a, 0x76f988da, w[7], w[5], w[0], w[8]); + round!(a, b, c, d, e, f, g, h, 0x983e5152, w[8], w[6], w[1], w[9]); + round!(h, a, b, c, d, e, f, g, 0xa831c66d, w[9], w[7], w[2], w[10]); + round!(g, h, a, b, c, d, e, f, 0xb00327c8, w[10], w[8], w[3], w[11]); + round!(f, g, h, a, b, c, d, e, 0xbf597fc7, w[11], w[9], w[4], w[12]); + round!(e, f, g, h, a, b, c, d, 0xc6e00bf3, w[12], w[10], w[5], w[13]); + round!(d, e, f, g, h, a, b, c, 0xd5a79147, w[13], w[11], w[6], w[14]); + round!(c, d, e, f, g, h, a, b, 0x06ca6351, w[14], w[12], w[7], w[15]); + round!(b, c, d, e, f, g, h, a, 0x14292967, w[15], w[13], w[8], w[0]); + + round!(a, b, c, d, e, f, g, h, 0x27b70a85, w[0], w[14], w[9], w[1]); + round!(h, a, b, c, d, e, f, g, 0x2e1b2138, w[1], w[15], w[10], w[2]); + round!(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w[2], w[0], w[11], w[3]); + round!(f, g, h, a, b, c, d, e, 0x53380d13, w[3], w[1], w[12], w[4]); + round!(e, f, g, h, a, b, c, d, 0x650a7354, w[4], w[2], w[13], w[5]); + round!(d, e, f, g, h, a, b, c, 0x766a0abb, w[5], w[3], w[14], w[6]); + round!(c, d, e, f, g, h, a, b, 0x81c2c92e, w[6], w[4], w[15], w[7]); + round!(b, c, d, e, f, g, h, a, 0x92722c85, w[7], w[5], w[0], w[8]); + round!(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w[8], w[6], w[1], w[9]); + round!(h, a, b, c, d, e, f, g, 0xa81a664b, w[9], w[7], w[2], w[10]); + round!(g, h, a, b, c, d, e, f, 0xc24b8b70, w[10], w[8], w[3], w[11]); + round!(f, g, h, a, b, c, d, e, 0xc76c51a3, w[11], w[9], w[4], w[12]); + round!(e, f, g, h, a, b, c, d, 0xd192e819, w[12], w[10], w[5], w[13]); + round!(d, e, f, g, h, a, b, c, 0xd6990624, w[13], w[11], w[6], w[14]); + round!(c, d, e, f, g, h, a, b, 0xf40e3585, w[14], w[12], w[7], w[15]); + round!(b, c, d, e, f, g, h, a, 0x106aa070, w[15], w[13], w[8], w[0]); + + round!(a, b, c, d, e, f, g, h, 0x19a4c116, w[0], w[14], w[9], w[1]); + round!(h, a, b, c, d, e, f, g, 0x1e376c08, w[1], w[15], w[10], w[2]); + round!(g, h, a, b, c, d, e, f, 0x2748774c, w[2], w[0], w[11], w[3]); + round!(f, g, h, a, b, c, d, e, 0x34b0bcb5, w[3], w[1], w[12], w[4]); + round!(e, f, g, h, a, b, c, d, 0x391c0cb3, w[4], w[2], w[13], w[5]); + round!(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w[5], w[3], w[14], w[6]); + round!(c, d, e, f, g, h, a, b, 0x5b9cca4f, w[6], w[4], w[15], w[7]); + round!(b, c, d, e, f, g, h, a, 0x682e6ff3, w[7], w[5], w[0], w[8]); + round!(a, b, c, d, e, f, g, h, 0x748f82ee, w[8], w[6], w[1], w[9]); + round!(h, a, b, c, d, e, f, g, 0x78a5636f, w[9], w[7], w[2], w[10]); + round!(g, h, a, b, c, d, e, f, 0x84c87814, w[10], w[8], w[3], w[11]); + round!(f, g, h, a, b, c, d, e, 0x8cc70208, w[11], w[9], w[4], w[12]); + round!(e, f, g, h, a, b, c, d, 0x90befffa, w[12], w[10], w[5], w[13]); + round!(d, e, f, g, h, a, b, c, 0xa4506ceb, w[13], w[11], w[6], w[14]); + round!(c, d, e, f, g, h, a, b, 0xbef9a3f7, w[14], w[12], w[7], w[15]); + round!(b, c, d, e, f, g, h, a, 0xc67178f2, w[15], w[13], w[8], w[0]); + + self.h[0] = self.h[0].wrapping_add(a); + self.h[1] = self.h[1].wrapping_add(b); + self.h[2] = self.h[2].wrapping_add(c); + self.h[3] = self.h[3].wrapping_add(d); + self.h[4] = self.h[4].wrapping_add(e); + self.h[5] = self.h[5].wrapping_add(f); + self.h[6] = self.h[6].wrapping_add(g); + self.h[7] = self.h[7].wrapping_add(h); + } +} + +impl io::Write for Sha256 { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.input_raw(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} diff --git a/primitives/src/taproot.rs b/primitives/src/taproot.rs new file mode 100644 index 00000000..c46796d4 --- /dev/null +++ b/primitives/src/taproot.rs @@ -0,0 +1,419 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_braces)] // required due to strict dumb derivation and compiler bug + +use std::borrow::Borrow; +use std::fmt::{self, Formatter, LowerHex, UpperHex}; +use std::{cmp, io}; + +use amplify::confinement::{Confined, TinyVec}; +use amplify::{Bytes32, Wrapper}; +use secp256k1::{Scalar, XOnlyPublicKey}; +use strict_encoding::{ + DecodeError, ReadTuple, StrictDecode, StrictEncode, StrictProduct, StrictTuple, StrictType, + TypeName, TypedRead, TypedWrite, WriteTuple, +}; + +use crate::opcodes::*; +use crate::{ScriptBytes, ScriptPubkey, Sha256, WitnessVer, LIB_NAME_BP}; + +/// The SHA-256 midstate value for the TapLeaf hash. +pub const MIDSTATE_TAPLEAF: [u8; 32] = [ + 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147, 108, + 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201, +]; +// 9ce0e4e67c116c3938b3caf2c30f5089d3f3936c47636e607db33eeaddc6f0c9 + +/// The SHA-256 midstate value for the TapBranch hash. +pub const MIDSTATE_TAPBRANCH: [u8; 32] = [ + 35, 168, 101, 169, 184, 164, 13, 167, 151, 124, 30, 4, 196, 158, 36, 111, 181, 190, 19, 118, + 157, 36, 201, 183, 181, 131, 181, 212, 168, 210, 38, 210, +]; +// 23a865a9b8a40da7977c1e04c49e246fb5be13769d24c9b7b583b5d4a8d226d2 + +/// The SHA-256 midstate value for the TapTweak hash. +pub const MIDSTATE_TAPTWEAK: [u8; 32] = [ + 209, 41, 162, 243, 112, 28, 101, 93, 101, 131, 182, 195, 185, 65, 151, 39, 149, 244, 226, 50, + 148, 253, 84, 244, 162, 174, 141, 133, 71, 202, 89, 11, +]; +// d129a2f3701c655d6583b6c3b941972795f4e23294fd54f4a2ae8d8547ca590b + +/// The SHA-256 midstate value for the TapSig hash. +pub const MIDSTATE_TAPSIGHASH: [u8; 32] = [ + 245, 4, 164, 37, 215, 248, 120, 59, 19, 99, 134, 138, 227, 229, 86, 88, 110, 238, 148, 93, 188, + 120, 136, 221, 2, 166, 226, 195, 24, 115, 254, 159, +]; +// f504a425d7f8783b1363868ae3e556586eee945dbc7888dd02a6e2c31873fe9f + +#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Deref, LowerHex, Display, FromStr)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictDumb)] +#[strict_type(lib = LIB_NAME_BP, dumb = { Self(XOnlyPublicKey::from_slice(&[1u8; 32]).unwrap()) })] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct InternalPk(XOnlyPublicKey); + +impl InternalPk { + pub fn to_output_key(&self, merkle_root: Option) -> XOnlyPublicKey { + let mut engine = Sha256::from_tag(MIDSTATE_TAPTWEAK); + // always hash the key + engine.input_raw(&self.0.serialize()); + if let Some(merkle_root) = merkle_root { + engine.input_raw(merkle_root.into_tap_hash().as_slice()); + } + let tweak = + Scalar::from_be_bytes(engine.finish()).expect("hash value greater than curve order"); + let (output_key, tweaked_parity) = self + .0 + .add_tweak(secp256k1::SECP256K1, &tweak) + .expect("hash collision"); + debug_assert!(self.tweak_add_check( + secp256k1::SECP256K1, + &output_key, + tweaked_parity, + tweak + )); + output_key + } +} + +impl StrictEncode for InternalPk { + fn strict_encode(&self, writer: W) -> io::Result { + let bytes = Bytes32::from(self.0.serialize()); + writer.write_newtype::(&bytes) + } +} + +impl StrictDecode for InternalPk { + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_tuple(|r| { + let bytes: Bytes32 = r.read_field()?; + XOnlyPublicKey::from_slice(bytes.as_slice()) + .map(Self) + .map_err(|_| { + DecodeError::DataIntegrityError(format!( + "invalid x-only public key value '{bytes:x}'" + )) + }) + }) + } +} + +pub trait IntoTapHash { + fn into_tap_hash(self) -> TapNodeHash; +} + +#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)] +pub struct TapLeafHash( + #[from] + #[from([u8; 32])] + Bytes32, +); + +impl TapLeafHash { + pub fn with_leaf_script(leaf_script: &LeafScript) -> Self { + let mut engine = Sha256::from_tag(MIDSTATE_TAPLEAF); + engine.input_raw(&[leaf_script.version.to_consensus()]); + engine.input_with_len(leaf_script.script.as_slice()); + Self(engine.finish().into()) + } + + pub fn with_tap_script(tap_script: &TapScript) -> Self { + let mut engine = Sha256::from_tag(MIDSTATE_TAPLEAF); + engine.input_raw(&[TAPROOT_LEAF_TAPSCRIPT]); + engine.input_with_len(tap_script.as_slice()); + Self(engine.finish().into()) + } +} + +impl IntoTapHash for TapLeafHash { + fn into_tap_hash(self) -> TapNodeHash { TapNodeHash(self.0) } +} + +#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)] +pub struct TapBranchHash( + #[from] + #[from([u8; 32])] + Bytes32, +); + +impl TapBranchHash { + pub fn with_nodes(node1: TapNodeHash, node2: TapNodeHash) -> Self { + let mut engine = Sha256::from_tag(MIDSTATE_TAPBRANCH); + engine.input_raw(cmp::min(&node1, &node2).borrow()); + engine.input_raw(cmp::max(&node1, &node2).borrow()); + Self(engine.finish().into()) + } +} + +impl IntoTapHash for TapBranchHash { + fn into_tap_hash(self) -> TapNodeHash { TapNodeHash(self.0) } +} + +#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Deref, Index, RangeOps, BorrowSlice, Hex, Display, FromStr)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct TapNodeHash( + #[from] + #[from([u8; 32])] + #[from(TapLeafHash)] + #[from(TapBranchHash)] + Bytes32, +); + +impl IntoTapHash for TapNodeHash { + fn into_tap_hash(self) -> TapNodeHash { self } +} + +#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] +#[wrapper(Deref)] +pub struct TapMerklePath(TinyVec); + +/// Taproot annex prefix. +pub const TAPROOT_ANNEX_PREFIX: u8 = 0x50; + +/// Tapscript leaf version. +// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L226 +pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc0; + +/// Tapleaf mask for getting the leaf version from first byte of control block. +// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L225 +pub const TAPROOT_LEAF_MASK: u8 = 0xfe; + +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Display, Error)] +#[display(doc_comments)] +/// invalid taproot leaf version {0}. +pub struct InvalidLeafVer(u8); + +/// The leaf version for tapleafs. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub enum LeafVer { + /// BIP-342 tapscript. + #[default] + TapScript, + + /// Future leaf version. + Future(FutureLeafVer), +} + +impl StrictType for LeafVer { + const STRICT_LIB_NAME: &'static str = LIB_NAME_BP; + fn strict_name() -> Option { Some(tn!("LeafVer")) } +} +impl StrictProduct for LeafVer {} +impl StrictTuple for LeafVer { + const FIELD_COUNT: u8 = 1; +} +impl StrictEncode for LeafVer { + fn strict_encode(&self, writer: W) -> std::io::Result { + writer.write_tuple::(|w| Ok(w.write_field(&self.to_consensus())?.complete())) + } +} +impl StrictDecode for LeafVer { + fn strict_decode(reader: &mut impl TypedRead) -> Result { + reader.read_tuple(|r| { + let version = r.read_field()?; + Self::from_consensus(version) + .map_err(|err| DecodeError::DataIntegrityError(err.to_string())) + }) + } +} + +impl LeafVer { + /// Creates a [`LeafVer`] from consensus byte representation. + /// + /// # Errors + /// + /// - If the last bit of the `version` is odd. + /// - If the `version` is 0x50 ([`TAPROOT_ANNEX_PREFIX`]). + pub fn from_consensus(version: u8) -> Result { + match version { + TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVer::TapScript), + TAPROOT_ANNEX_PREFIX => Err(InvalidLeafVer(TAPROOT_ANNEX_PREFIX)), + future => FutureLeafVer::from_consensus(future).map(LeafVer::Future), + } + } + + /// Returns the consensus representation of this [`LeafVer`]. + pub fn to_consensus(self) -> u8 { + match self { + LeafVer::TapScript => TAPROOT_LEAF_TAPSCRIPT, + LeafVer::Future(version) => version.to_consensus(), + } + } +} + +impl LowerHex for LeafVer { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { LowerHex::fmt(&self.to_consensus(), f) } +} + +impl UpperHex for LeafVer { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { UpperHex::fmt(&self.to_consensus(), f) } +} + +/// Inner type representing future (non-tapscript) leaf versions. See +/// [`LeafVer::Future`]. +/// +/// NB: NO PUBLIC CONSTRUCTOR! +/// The only way to construct this is by converting `u8` to [`LeafVer`] and then +/// extracting it. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP, dumb = { Self(0x51) })] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct FutureLeafVer(u8); + +impl FutureLeafVer { + pub(self) fn from_consensus(version: u8) -> Result { + match version { + TAPROOT_LEAF_TAPSCRIPT => unreachable!( + "FutureLeafVersion::from_consensus should be never called for 0xC0 value" + ), + TAPROOT_ANNEX_PREFIX => Err(InvalidLeafVer(TAPROOT_ANNEX_PREFIX)), + odd if odd & 0xFE != odd => Err(InvalidLeafVer(odd)), + even => Ok(FutureLeafVer(even)), + } + } + + /// Returns the consensus representation of this [`FutureLeafVer`]. + #[inline] + pub fn to_consensus(self) -> u8 { self.0 } +} + +impl LowerHex for FutureLeafVer { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { LowerHex::fmt(&self.0, f) } +} + +impl UpperHex for FutureLeafVer { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { UpperHex::fmt(&self.0, f) } +} + +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, Display)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +#[display("{version:04x} {script:x}")] +pub struct LeafScript { + pub version: LeafVer, + pub script: ScriptBytes, +} + +// TODO: Impl Hex and FromStr for LeafScript + +impl From for LeafScript { + fn from(tap_script: TapScript) -> Self { + LeafScript { + version: LeafVer::TapScript, + script: tap_script.into_inner(), + } + } +} + +impl LeafScript { + pub fn from_tap_script(tap_script: TapScript) -> Self { Self::from(tap_script) } + pub fn tap_leaf_hash(&self) -> TapLeafHash { TapLeafHash::with_leaf_script(self) } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP, tags = repr, into_u8, try_from_u8)] +#[repr(u8)] +pub enum TapCode { + /// Push the next 32 bytes as an array onto the stack. + #[display("OP_PUSH_BYTES32")] + PushBytes32 = OP_PUSHBYTES_32, + + /// Synonym for OP_RETURN. + Reserved = OP_RESERVED, + + /// Fail the script immediately. + #[display("OP_RETURN")] + #[strict_type(dumb)] + Return = OP_RETURN, + + /// Read the next byte as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA1")] + PushData1 = OP_PUSHDATA1, + /// Read the next 2 bytes as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA2")] + PushData2 = OP_PUSHDATA2, + /// Read the next 4 bytes as N; push the next N bytes as an array onto the + /// stack. + #[display("OP_PUSH_DATA3")] + PushData4 = OP_PUSHDATA4, +} + +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)] +#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)] +#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct TapScript(ScriptBytes); +// TODO: impl Display/FromStr for TapScript providing correct opcodes + +impl TapScript { + pub fn new() -> Self { Self::default() } + + pub fn with_capacity(capacity: usize) -> Self { + Self(ScriptBytes::from(Confined::with_capacity(capacity))) + } + + /// Adds a single opcode to the script. + pub fn push_opcode(&mut self, op_code: TapCode) { + self.0.push(op_code as u8).expect("script exceeds 4GB"); + } +} + +impl ScriptPubkey { + pub fn p2tr(internal_key: InternalPk, merkle_root: Option) -> Self { + let output_key = internal_key.to_output_key(merkle_root); + Self::p2tr_tweaked(output_key) + } + + pub fn p2tr_tweaked(output_key: XOnlyPublicKey) -> Self { + // output key is 32 bytes long, so it's safe to use + // `new_witness_program_unchecked` (Segwitv1) + Self::with_segwit_unchecked(WitnessVer::V1, &output_key.serialize()) + } + + pub fn is_p2tr(&self) -> bool { + self.len() == 34 && self[0] == WitnessVer::V1.op_code() as u8 && self[1] == OP_PUSHBYTES_32 + } +} diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs new file mode 100644 index 00000000..627c6a0d --- /dev/null +++ b/primitives/src/tx.rs @@ -0,0 +1,156 @@ +// Bitcoin protocol primitives library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::num::ParseIntError; +use std::str::FromStr; + +use amplify::Bytes32; + +use super::{VarIntArray, LIB_NAME_BP}; +use crate::{ScriptPubkey, SigScript}; + +#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, From)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)] +// all-zeros used in coinbase +pub struct Txid( + #[from] + #[from([u8; 32])] + Bytes32, +); + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +#[display(inner)] +// 0xFFFFFFFF used in coinbase +pub struct Vout(u32); + +impl Vout { + pub fn into_u32(self) -> u32 { self.0 } +} + +impl FromStr for Vout { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { s.parse().map(Self) } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +#[display("{txid}:{vout}")] +pub struct Outpoint { + pub txid: Txid, + pub vout: Vout, +} + +impl Outpoint { + pub fn new(txid: Txid, vout: impl Into) -> Self { + Self { + txid, + vout: vout.into(), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct SeqNo(u32); + +#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct TxIn { + pub prev_output: Outpoint, + pub sig_script: SigScript, + pub sequence: SeqNo, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct Sats(u64); + +#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct TxOut { + pub value: Sats, + pub script_pubkey: ScriptPubkey, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP, tags = repr, into_u8, try_from_u8)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +#[repr(u8)] +pub enum TxVer { + #[strict_type(dumb)] + V1 = 1, + V2 = 2, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct LockTime(u32); + +#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +pub struct Tx { + pub version: TxVer, + pub inputs: VarIntArray, + pub outputs: VarIntArray, + pub lock_time: LockTime, +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..292fe499 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/seals/Cargo.toml b/seals/Cargo.toml index c9578332..f33a750f 100644 --- a/seals/Cargo.toml +++ b/seals/Cargo.toml @@ -1,36 +1,36 @@ [package] name = "bp-seals" -version = "0.9.0" -license = "Apache-2.0" -authors = ["Dr. Maxim Orlovsky "] -description = "Bitcoin single-use-seals library" -repository = "https://github.com/LNP-BP/bp-core" -homepage = "https://github.com/LNP-BP" -keywords = ["lnp-bp", "bitcoin", "cryptography", "smart-contracts", "single-use-seals"] -categories = ["cryptography::cryptocurrencies", "encoding"] +version = "0.10.0-beta.1" +description = "Bitcoin protocol single-use-seals library" +keywords = ["lnp-bp", "bitcoin", "blockchain", "smart-contracts", "single-use-seals"] +categories = ["cryptography", "encoding"] +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } readme = "../README.md" -edition = "2021" -rust-version = "1.59.0" [lib] name = "seals" path = "src/lib.rs" [dependencies] -amplify = "3.13.0" -bitcoin = { version = "0.29.2", features = ["rand"] } -single_use_seals = "0.9.0" -commit_verify = "0.9.0" -strict_encoding = "0.9.0" -lnpbp_bech32 = "0.9.0" -bp-dbc = { version = "0.9.0", path = "../dbc" } -bitcoin_onchain = "0.9.0" -async-trait = { version = "0.1.51", optional = true } +amplify = "4.0.0-beta.10" +single_use_seals = "0.10.0-beta.1" +commit_verify = "0.10.0-beta.1" +strict_encoding = "2.0.0-beta.5" +baid58 = "0.1.0" +bp-primitives = { version = "0.10.0-beta.1", path = "../primitives" } +bp-dbc = { version = "0.10.0-beta.1", path = "../dbc" } +rand = "0.8.5" serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true } -serde_with = { version = "1.14", optional = true } [features] default = [] -all = ["async", "serde"] -async = ["single_use_seals/async", "async-trait"] -serde = ["amplify/serde", "bitcoin/serde", "commit_verify/serde", "lnpbp_bech32/serde", "bp-dbc/serde", "serde_crate", "serde_with"] +all = ["serde"] +serde = ["amplify/serde", "commit_verify/serde", "bp-dbc/serde", "serde_crate"] + +[package.metadata.docs.rs] +features = [ "all" ] diff --git a/seals/src/lib.rs b/seals/src/lib.rs index 8b33a790..4935702e 100644 --- a/seals/src/lib.rs +++ b/seals/src/lib.rs @@ -1,33 +1,49 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Coding conventions +#![deny( + non_upper_case_globals, + non_camel_case_types, + non_snake_case, + unused_mut, + unused_imports, + dead_code, + missing_docs +)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] //! The library provides single-use-seal implementations for bitcoin protocol. // Coding conventions #![recursion_limit = "256"] -#![deny(dead_code, missing_docs, warnings)] +#![deny(dead_code, /* todo: missing_docs, */)] #[macro_use] extern crate amplify; #[macro_use] extern crate strict_encoding; -#[cfg(feature = "async")] -// #[macro_use] -extern crate async_trait; #[cfg(feature = "serde")] #[macro_use] extern crate serde_crate as serde; +pub mod resolver; pub mod txout; diff --git a/seals/src/resolver.rs b/seals/src/resolver.rs new file mode 100644 index 00000000..d22bfcaa --- /dev/null +++ b/seals/src/resolver.rs @@ -0,0 +1,42 @@ +// Bitcoin protocol single-use-seals library. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Written in 2019-2023 by +// Dr. Maxim Orlovsky +// +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! API for resolving single-use-seals. + +use bc::{Tx, Txid}; + +/// Error resolving single-use-seal +#[derive(Debug, Display)] +#[display(doc_comments)] +pub enum Error { + /// Resolver implementation-specific error. + #[display(inner)] + Connection(Box), + + /// transaction with id {0} is not known to the resolver. + UnknownTx(Txid), +} + +/// API which must be provided by a resolver to operate with single-use-seal. +pub trait Resolver { + /// Return transaction data for a given transaction id. + fn tx_by_id(&self, txid: Txid) -> Result; +} diff --git a/seals/src/txout/blind.rs b/seals/src/txout/blind.rs index d93724bf..fa05b86e 100644 --- a/seals/src/txout/blind.rs +++ b/seals/src/txout/blind.rs @@ -1,17 +1,23 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! TxOut seals which are blinded with additional entropy. @@ -19,12 +25,13 @@ use std::convert::{TryFrom, TryInto}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -use bitcoin::hashes::{sha256, sha256t, Hash, HashEngine}; -use bitcoin::secp256k1::rand::{thread_rng, RngCore}; -use bitcoin::{OutPoint, Txid}; -use commit_verify::{commit_encode, CommitConceal, CommitVerify, TaggedHash}; -use dbc::tapret::Lnpbp6; -use lnpbp_bech32::{FromBech32Str, ToBech32String}; +use amplify::hex::FromHex; +use amplify::{hex, Bytes32, Wrapper}; +use baid58::ToBaid58; +use bc::{Outpoint, Sha256, Txid, Vout}; +use commit_verify::{CommitVerify, Conceal}; +use dbc::tapret::Lnpbp12; +use rand::{thread_rng, RngCore}; use super::{CloseMethod, MethodParseError, WitnessVoutError}; use crate::txout::{ExplicitSeal, TxoSeal}; @@ -35,12 +42,9 @@ use crate::txout::{ExplicitSeal, TxoSeal}; /// Revealed seal means that the seal definition containing explicit information /// about the bitcoin transaction output. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(StrictEncode, StrictDecode)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = bc::LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct RevealedSeal { /// Commitment to the specific seal close method [`CloseMethod`] which must /// be used to close this seal. @@ -55,7 +59,7 @@ pub struct RevealedSeal { pub txid: Option, /// Tx output number, which should be always known. - pub vout: u32, + pub vout: Vout, /// Blinding factor providing confidentiality of the seal definition. /// Prevents rainbow table bruteforce attack based on the existing @@ -63,30 +67,28 @@ pub struct RevealedSeal { pub blinding: u64, } -impl TryFrom<&RevealedSeal> for OutPoint { +impl TryFrom<&RevealedSeal> for Outpoint { type Error = WitnessVoutError; #[inline] fn try_from(reveal: &RevealedSeal) -> Result { reveal .txid - .map(|txid| OutPoint::new(txid, reveal.vout)) + .map(|txid| Outpoint::new(txid, reveal.vout)) .ok_or(WitnessVoutError) } } -impl TryFrom for OutPoint { +impl TryFrom for Outpoint { type Error = WitnessVoutError; #[inline] - fn try_from(reveal: RevealedSeal) -> Result { - OutPoint::try_from(&reveal) - } + fn try_from(reveal: RevealedSeal) -> Result { Outpoint::try_from(&reveal) } } -impl From<&OutPoint> for RevealedSeal { +impl From<&Outpoint> for RevealedSeal { #[inline] - fn from(outpoint: &OutPoint) -> Self { + fn from(outpoint: &Outpoint) -> Self { Self { method: CloseMethod::TapretFirst, blinding: thread_rng().next_u64(), @@ -96,9 +98,9 @@ impl From<&OutPoint> for RevealedSeal { } } -impl From for RevealedSeal { +impl From for RevealedSeal { #[inline] - fn from(outpoint: OutPoint) -> Self { RevealedSeal::from(&outpoint) } + fn from(outpoint: Outpoint) -> Self { RevealedSeal::from(&outpoint) } } impl From<&ExplicitSeal> for RevealedSeal { @@ -118,17 +120,11 @@ impl From for RevealedSeal { fn from(seal: ExplicitSeal) -> Self { RevealedSeal::from(&seal) } } -impl CommitConceal for RevealedSeal { - type ConcealedCommitment = ConcealedSeal; +impl Conceal for RevealedSeal { + type Concealed = ConcealedSeal; #[inline] - fn commit_conceal(&self) -> Self::ConcealedCommitment { - ConcealedSeal::commit(self) - } -} - -impl commit_encode::Strategy for RevealedSeal { - type Strategy = commit_encode::strategies::UsingConceal; + fn conceal(&self) -> Self::Concealed { ConcealedSeal::commit(self) } } impl TxoSeal for RevealedSeal { @@ -139,19 +135,17 @@ impl TxoSeal for RevealedSeal { fn txid(&self) -> Option { self.txid } #[inline] - fn vout(&self) -> usize { self.vout as usize } + fn vout(&self) -> Vout { self.vout } #[inline] - fn outpoint(&self) -> Option { self.try_into().ok() } + fn outpoint(&self) -> Option { self.try_into().ok() } #[inline] - fn txid_or(&self, default_txid: Txid) -> Txid { - self.txid.unwrap_or(default_txid) - } + fn txid_or(&self, default_txid: Txid) -> Txid { self.txid.unwrap_or(default_txid) } #[inline] - fn outpoint_or(&self, default_txid: Txid) -> OutPoint { - OutPoint::new(self.txid.unwrap_or(default_txid), self.vout) + fn outpoint_or(&self, default_txid: Txid) -> Outpoint { + Outpoint::new(self.txid.unwrap_or(default_txid), self.vout) } } @@ -159,7 +153,7 @@ impl RevealedSeal { /// Constructs seal for the provided outpoint and seal closing method. Uses /// `thread_rng` to initialize blinding factor. #[inline] - pub fn new(method: CloseMethod, outpoint: OutPoint) -> RevealedSeal { + pub fn new(method: CloseMethod, outpoint: Outpoint) -> RevealedSeal { Self { method, blinding: thread_rng().next_u64(), @@ -174,20 +168,20 @@ impl RevealedSeal { pub fn with( method: CloseMethod, txid: Option, - vout: u32, + vout: impl Into, rng: &mut impl RngCore, ) -> RevealedSeal { RevealedSeal { method, txid, - vout, + vout: vout.into(), blinding: rng.next_u64(), } } /// Converts revealed seal into concealed. #[inline] - pub fn to_concealed_seal(&self) -> ConcealedSeal { self.commit_conceal() } + pub fn to_concealed_seal(&self) -> ConcealedSeal { self.conceal() } } /// Errors happening during parsing string representation of different forms of @@ -228,9 +222,9 @@ pub enum ParseError { /// starting with `0x` and not with a decimal NonHexBlinding, - /// wrong Bech32 representation of the blinded TxOut seal – {0} + /// wrong representation of the blinded TxOut seal – {0} #[from] - Bech32(lnpbp_bech32::Error), + Hex(hex::Error), } impl FromStr for RevealedSeal { @@ -238,49 +232,27 @@ impl FromStr for RevealedSeal { fn from_str(s: &str) -> Result { let mut split = s.split(&[':', '#'][..]); - match ( - split.next(), - split.next(), - split.next(), - split.next(), - split.next(), - ) { + match (split.next(), split.next(), split.next(), split.next(), split.next()) { (Some("~"), ..) | (Some(""), ..) => Err(ParseError::MethodRequired), (Some(_), Some(""), ..) => Err(ParseError::TxidRequired), - (Some(_), Some(_), None, ..) if s.contains(':') => { - Err(ParseError::BlindingRequired) - } - (Some(_), Some(_), Some(_), Some(blinding), None) - if !blinding.starts_with("0x") => - { + (Some(_), Some(_), None, ..) if s.contains(':') => Err(ParseError::BlindingRequired), + (Some(_), Some(_), Some(_), Some(blinding), None) if !blinding.starts_with("0x") => { Err(ParseError::NonHexBlinding) } - (Some(method), Some("~"), Some(vout), Some(blinding), None) => { - Ok(RevealedSeal { - method: method.parse()?, - blinding: u64::from_str_radix( - blinding.trim_start_matches("0x"), - 16, - ) + (Some(method), Some("~"), Some(vout), Some(blinding), None) => Ok(RevealedSeal { + method: method.parse()?, + blinding: u64::from_str_radix(blinding.trim_start_matches("0x"), 16) .map_err(|_| ParseError::WrongBlinding)?, - txid: None, - vout: vout.parse().map_err(|_| ParseError::WrongVout)?, - }) - } - (Some(method), Some(txid), Some(vout), Some(blinding), None) => { - Ok(RevealedSeal { - method: method.parse()?, - blinding: u64::from_str_radix( - blinding.trim_start_matches("0x"), - 16, - ) + txid: None, + vout: vout.parse().map_err(|_| ParseError::WrongVout)?, + }), + (Some(method), Some(txid), Some(vout), Some(blinding), None) => Ok(RevealedSeal { + method: method.parse()?, + blinding: u64::from_str_radix(blinding.trim_start_matches("0x"), 16) .map_err(|_| ParseError::WrongBlinding)?, - txid: Some( - txid.parse().map_err(|_| ParseError::WrongTxid)?, - ), - vout: vout.parse().map_err(|_| ParseError::WrongVout)?, - }) - } + txid: Some(txid.parse().map_err(|_| ParseError::WrongTxid)?), + vout: vout.parse().map_err(|_| ParseError::WrongVout)?, + }), _ => Err(ParseError::WrongStructure), } } @@ -303,175 +275,75 @@ impl Display for RevealedSeal { } static MIDSTATE_CONCEALED_SEAL: [u8; 32] = [ - 250, 13, 163, 5, 178, 220, 248, 173, 139, 222, 67, 198, 134, 127, 63, 153, - 147, 236, 172, 33, 17, 167, 176, 30, 70, 99, 185, 129, 217, 110, 183, 27, + 250, 13, 163, 5, 178, 220, 248, 173, 139, 222, 67, 198, 134, 127, 63, 153, 147, 236, 172, 33, + 17, 167, 176, 30, 70, 99, 185, 129, 217, 110, 183, 27, ]; -/// Tag used for [`ConcealedSeal`] hash type -pub struct ConcealedSealTag; - -impl sha256t::Tag for ConcealedSealTag { - #[inline] - fn engine() -> sha256::HashEngine { - let midstate = sha256::Midstate::from_inner(MIDSTATE_CONCEALED_SEAL); - sha256::HashEngine::from_midstate(midstate, 64) - } -} - -impl lnpbp_bech32::Strategy for ConcealedSealTag { - const HRP: &'static str = "txob"; - type Strategy = lnpbp_bech32::strategies::UsingStrictEncoding; -} - /// Blind version of transaction outpoint-based single-use-seal -#[derive( - Wrapper, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, - Display, From -)] -#[wrapper(Debug, LowerHex, Index, IndexRange, IndexFrom, IndexTo, IndexFull)] -#[display(ConcealedSeal::to_bech32_string)] -pub struct ConcealedSeal(sha256t::Hash); - -// TODO: Make this part of `lnpbp::bech32` -#[cfg(feature = "serde")] -impl serde::Serialize for ConcealedSeal { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if serializer.is_human_readable() { - serializer.serialize_str(&self.to_bech32_string()) - } else { - serializer.serialize_bytes(&self[..]) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for ConcealedSeal { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct Visitor; - impl serde::de::Visitor<'_> for Visitor { - type Value = ConcealedSeal; - - fn expecting( - &self, - formatter: &mut std::fmt::Formatter, - ) -> std::fmt::Result { - formatter.write_str("Bech32 string with `txob` HRP") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - ConcealedSeal::from_str(v).map_err(serde::de::Error::custom) - } +#[derive(Wrapper, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From)] +#[wrapper(Index, RangeOps, BorrowSlice, Hex)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = bc::LIB_NAME_BP)] +#[display(Self::to_baid58)] +pub struct ConcealedSeal( + #[from] + #[from([u8; 32])] + Bytes32, +); - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&v) - } +impl ToBaid58<32> for ConcealedSeal { + const HRP: &'static str = "utxob"; - fn visit_byte_buf(self, v: Vec) -> Result - where - E: serde::de::Error, - { - ConcealedSeal::from_bytes(&v).map_err(|_| { - serde::de::Error::invalid_length(v.len(), &"32 bytes") - }) - } - } - - if deserializer.is_human_readable() { - deserializer.deserialize_str(Visitor) - } else { - deserializer.deserialize_byte_buf(Visitor) - } - } + fn to_baid58_payload(&self) -> [u8; 32] { self.0.into_inner() } } impl FromStr for ConcealedSeal { type Err = ParseError; - fn from_str(s: &str) -> Result { - Ok(ConcealedSeal::from_bech32_str(s)?) - } + // TODO: Use Baid58 format + fn from_str(s: &str) -> Result { Ok(ConcealedSeal::from_hex(s)?) } } -impl From for ConcealedSeal { +impl From for ConcealedSeal { #[inline] - fn from(outpoint: OutPoint) -> Self { - RevealedSeal::from(outpoint).commit_conceal() - } -} - -impl strict_encoding::Strategy for ConcealedSeal { - type Strategy = strict_encoding::strategies::Wrapped; + fn from(outpoint: Outpoint) -> Self { RevealedSeal::from(outpoint).conceal() } } -impl commit_encode::Strategy for ConcealedSeal { - type Strategy = commit_encode::strategies::UsingStrict; -} - -impl CommitVerify for ConcealedSeal { +impl CommitVerify for ConcealedSeal { fn commit(reveal: &RevealedSeal) -> Self { - let mut engine = sha256t::Hash::::engine(); - engine.input(&[reveal.method as u8]); - engine.input( - &reveal.txid.unwrap_or_else(|| { - Txid::from_slice(&[0u8; 32]).expect("hardcoded") - })[..], - ); - engine.input(&reveal.vout.to_le_bytes()[..]); - engine.input(&reveal.blinding.to_le_bytes()[..]); - let inner = sha256t::Hash::::from_engine(engine); - ConcealedSeal::from_hash(inner) + let mut engine = Sha256::from_tag(MIDSTATE_CONCEALED_SEAL); + engine.input_raw(&[reveal.method as u8]); + engine.input_raw(&reveal.txid.unwrap_or_else(|| Txid::from([0u8; 32]))[..]); + engine.input_raw(&reveal.vout.into_u32().to_le_bytes()[..]); + engine.input_raw(&reveal.blinding.to_le_bytes()[..]); + ConcealedSeal::from_inner(engine.finish().into()) } } -impl lnpbp_bech32::Strategy for ConcealedSeal { - const HRP: &'static str = "txob"; - type Strategy = lnpbp_bech32::strategies::UsingStrictEncoding; -} - #[cfg(test)] mod test { use amplify::Wrapper; - use bitcoin::hashes::hex::FromHex; - use commit_verify::tagged_hash; use super::*; - #[test] - fn outpoint_hash_midstate() { - let midstate = tagged_hash::Midstate::with(b"bp:txout:concealed"); - assert_eq!(midstate.into_inner().into_inner(), MIDSTATE_CONCEALED_SEAL); - } - #[test] fn outpoint_hash_is_sha256d() { let reveal = RevealedSeal { method: CloseMethod::TapretFirst, blinding: 54683213134637, - txid: Some(Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839").unwrap()), - vout: 2, + txid: Some( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), }; let outpoint_hash = reveal.to_concealed_seal(); - let mut engine = sha256t::Hash::::engine(); - engine.input(&[reveal.method as u8]); - engine.input(&reveal.txid.unwrap()[..]); - engine.input(&reveal.vout.to_le_bytes()[..]); - engine.input(&reveal.blinding.to_le_bytes()[..]); - assert_eq!( - **outpoint_hash, - *sha256t::Hash::::from_engine(engine) - ) + let mut engine = Sha256::from_tag(MIDSTATE_CONCEALED_SEAL); + engine.input_raw(&[reveal.method as u8]); + engine.input_raw(&reveal.txid.unwrap()[..]); + engine.input_raw(&reveal.vout.into_u32().to_le_bytes()[..]); + engine.input_raw(&reveal.blinding.to_le_bytes()[..]); + assert_eq!(outpoint_hash.as_inner().as_slice(), &engine.finish()) } #[test] @@ -479,15 +351,21 @@ mod test { let outpoint_hash = RevealedSeal { method: CloseMethod::TapretFirst, blinding: 54683213134637, - txid: Some(Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839").unwrap()), - vout: 2, - }.to_concealed_seal(); - let bech32 = - "txob1a9peq6yx9x6ajt584qp5ge4jk9v7tmtgs3x2gntk2nf425cvpdgszt65je"; - assert_eq!(bech32, outpoint_hash.to_string()); - assert_eq!(outpoint_hash.to_string(), outpoint_hash.to_bech32_string()); - let reconstructed = ConcealedSeal::from_str(bech32).unwrap(); - assert_eq!(reconstructed, outpoint_hash); + txid: Some( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(2), + } + .to_concealed_seal(); + + let baid58 = "9PWpM7PxnNKQznyUgZD2Zt2FTpUQY8vLRPiKsaPLmBk3"; + assert_eq!(baid58, outpoint_hash.to_string()); + assert_eq!(outpoint_hash.to_string(), outpoint_hash.to_baid58().to_string()); + /* TODO: uncomment when Baid58::from_str would work + let reconstructed = ConcealedSeal::from_str(bech32).unwrap(); + assert_eq!(reconstructed, outpoint_hash); + */ } #[test] @@ -495,15 +373,18 @@ mod test { let mut outpoint_reveal = RevealedSeal { method: CloseMethod::TapretFirst, blinding: 54683213134637, - txid: Some(Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839").unwrap()), - vout: 21, + txid: Some( + Txid::from_hex("646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839") + .unwrap(), + ), + vout: Vout::from(21), }; let s = outpoint_reveal.to_string(); assert_eq!( &s, - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:\ - 21#0x31bbed7e7b2d" + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:21#\ + 0x31bbed7e7b2d" ); // round-trip assert_eq!(RevealedSeal::from_str(&s).unwrap(), outpoint_reveal); @@ -515,74 +396,120 @@ mod test { assert_eq!(RevealedSeal::from_str(&s).unwrap(), outpoint_reveal); // wrong method - assert_eq!(RevealedSeal::from_str( - "tapret:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:0x765#0x78ca95" - ), Err(ParseError::WrongMethod(MethodParseError(s!("tapret"))))); + assert_eq!( + RevealedSeal::from_str( + "tapret:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:0x765#\ + 0x78ca95" + ), + Err(ParseError::WrongMethod(MethodParseError(s!("tapret")))) + ); // wrong vout value - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:0x765#0x78ca95" - ), Err(ParseError::WrongVout)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:i9#0x78ca95" - ), Err(ParseError::WrongVout)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:-5#0x78ca95" - ), Err(ParseError::WrongVout)); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:0x765#\ + 0x78ca95" + ), + Err(ParseError::WrongVout) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:i9#\ + 0x78ca95" + ), + Err(ParseError::WrongVout) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:-5#\ + 0x78ca95" + ), + Err(ParseError::WrongVout) + ); // wrong blinding secret value - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#0x78cs" - ), Err(ParseError::WrongBlinding)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#78ca95" - ), Err(ParseError::NonHexBlinding)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#857" - ), Err(ParseError::NonHexBlinding)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#-5" - ), Err(ParseError::NonHexBlinding)); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#\ + 0x78cs" + ), + Err(ParseError::WrongBlinding) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#\ + 78ca95" + ), + Err(ParseError::NonHexBlinding) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#857" + ), + Err(ParseError::NonHexBlinding) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#-5" + ), + Err(ParseError::NonHexBlinding) + ); // wrong txid value - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d607719dfd820551fb773e4dc8c4ed67965a8d1fae839:5#0x78ca69" - ), Err(ParseError::WrongTxid)); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d607719dfd820551fb773e4dc8c4ed67965a8d1fae839:5#\ + 0x78ca69" + ), + Err(ParseError::WrongTxid) + ); assert_eq!( RevealedSeal::from_str("tapret1st:rvgbdg:5#0x78ca69"), Err(ParseError::WrongTxid) ); - assert_eq!(RevealedSeal::from_str( - "tapret1st:10@646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#0x78ca69" - ), Err(ParseError::WrongTxid)); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:10@646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:5#\ + 0x78ca69" + ), + Err(ParseError::WrongTxid) + ); // wrong structure - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:1" - ), Err(ParseError::WrongStructure)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839#0x78ca" - ), Err(ParseError::WrongStructure)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839" - ), Err(ParseError::BlindingRequired)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839##0x78ca" - ), Err(ParseError::WrongVout)); - assert_eq!(RevealedSeal::from_str( - "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:#0x78ca95" - ), Err(ParseError::WrongVout)); assert_eq!( - RevealedSeal::from_str("tapret1st:_:5#0x78ca"), - Err(ParseError::WrongTxid) + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:1" + ), + Err(ParseError::WrongStructure) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839#0x78ca" + ), + Err(ParseError::WrongStructure) + ); + assert_eq!( + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839" + ), + Err(ParseError::BlindingRequired) ); assert_eq!( - RevealedSeal::from_str(":5#0x78ca"), - Err(ParseError::MethodRequired) + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839##\ + 0x78ca" + ), + Err(ParseError::WrongVout) ); assert_eq!( - RevealedSeal::from_str("~:5#0x78ca"), - Err(ParseError::MethodRequired) + RevealedSeal::from_str( + "tapret1st:646ca5c1062619e2a2d60771c9dfd820551fb773e4dc8c4ed67965a8d1fae839:#\ + 0x78ca95" + ), + Err(ParseError::WrongVout) ); + assert_eq!(RevealedSeal::from_str("tapret1st:_:5#0x78ca"), Err(ParseError::WrongTxid)); + assert_eq!(RevealedSeal::from_str(":5#0x78ca"), Err(ParseError::MethodRequired)); + assert_eq!(RevealedSeal::from_str("~:5#0x78ca"), Err(ParseError::MethodRequired)); } } diff --git a/seals/src/txout/error.rs b/seals/src/txout/error.rs index df5e97f3..a8a08853 100644 --- a/seals/src/txout/error.rs +++ b/seals/src/txout/error.rs @@ -1,20 +1,27 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bc::{Outpoint, Txid}; -use bitcoin::{OutPoint, Txid}; -use bitcoin_onchain::TxResolverError; +use crate::resolver; /// Seal verification errors. #[derive(Debug, Display, From, Error)] @@ -28,7 +35,7 @@ pub enum VerifyError { WitnessTxUnknown(Txid), /// the provided witness transaction {0} does not closes seal {1}. - WitnessNotClosingSeal(Txid, OutPoint), + WitnessNotClosingSeal(Txid, Outpoint), /// tapret commitment is invalid. /// @@ -39,16 +46,14 @@ pub enum VerifyError { /// unable to access commitment publication medium. #[from] #[display(inner)] - TxResolverError(TxResolverError), + TxResolverError(resolver::Error), } /// Error happening if the seal data holds only witness transaction output /// number and thus can't be used alone for constructing full bitcoin /// transaction output data which must include the witness transaction id /// (unknown to the seal). -#[derive( - Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error)] #[display("witness txid is unknown; unable to reconstruct full outpoint data")] pub struct WitnessVoutError; diff --git a/seals/src/txout/explicit.rs b/seals/src/txout/explicit.rs index 36f8c687..f718570d 100644 --- a/seals/src/txout/explicit.rs +++ b/seals/src/txout/explicit.rs @@ -1,17 +1,23 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! TxOut single-use-seals. @@ -19,8 +25,7 @@ use std::convert::{TryFrom, TryInto}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -use bitcoin::{OutPoint, Txid}; -use commit_verify::commit_encode; +use bc::{Outpoint, Txid, Vout}; use crate::txout::{CloseMethod, MethodParseError, TxoSeal, WitnessVoutError}; @@ -31,12 +36,9 @@ use crate::txout::{CloseMethod, MethodParseError, TxoSeal, WitnessVoutError}; /// commitment and conceal procedures (since without knowing a blinding factor /// we can't perform them). #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode, StrictDecode)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = bc::LIB_NAME_BP)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct ExplicitSeal { /// Commitment to the specific seal close method [`CloseMethod`] which must /// be used to close this seal. @@ -51,33 +53,31 @@ pub struct ExplicitSeal { pub txid: Option, /// Tx output number, which should be always known. - pub vout: u32, + pub vout: Vout, } -impl TryFrom<&ExplicitSeal> for OutPoint { +impl TryFrom<&ExplicitSeal> for Outpoint { type Error = WitnessVoutError; #[inline] fn try_from(reveal: &ExplicitSeal) -> Result { reveal .txid - .map(|txid| OutPoint::new(txid, reveal.vout)) + .map(|txid| Outpoint::new(txid, reveal.vout)) .ok_or(WitnessVoutError) } } -impl TryFrom for OutPoint { +impl TryFrom for Outpoint { type Error = WitnessVoutError; #[inline] - fn try_from(reveal: ExplicitSeal) -> Result { - OutPoint::try_from(&reveal) - } + fn try_from(reveal: ExplicitSeal) -> Result { Outpoint::try_from(&reveal) } } -impl From<&OutPoint> for ExplicitSeal { +impl From<&Outpoint> for ExplicitSeal { #[inline] - fn from(outpoint: &OutPoint) -> Self { + fn from(outpoint: &Outpoint) -> Self { Self { method: CloseMethod::TapretFirst, txid: Some(outpoint.txid), @@ -86,13 +86,9 @@ impl From<&OutPoint> for ExplicitSeal { } } -impl From for ExplicitSeal { +impl From for ExplicitSeal { #[inline] - fn from(outpoint: OutPoint) -> Self { ExplicitSeal::from(&outpoint) } -} - -impl commit_encode::Strategy for ExplicitSeal { - type Strategy = commit_encode::strategies::UsingStrict; + fn from(outpoint: Outpoint) -> Self { ExplicitSeal::from(&outpoint) } } impl TxoSeal for ExplicitSeal { @@ -103,26 +99,24 @@ impl TxoSeal for ExplicitSeal { fn txid(&self) -> Option { self.txid } #[inline] - fn vout(&self) -> usize { self.vout as usize } + fn vout(&self) -> Vout { self.vout } #[inline] - fn outpoint(&self) -> Option { self.try_into().ok() } + fn outpoint(&self) -> Option { self.try_into().ok() } #[inline] - fn txid_or(&self, default_txid: Txid) -> Txid { - self.txid.unwrap_or(default_txid) - } + fn txid_or(&self, default_txid: Txid) -> Txid { self.txid.unwrap_or(default_txid) } #[inline] - fn outpoint_or(&self, default_txid: Txid) -> OutPoint { - OutPoint::new(self.txid.unwrap_or(default_txid), self.vout) + fn outpoint_or(&self, default_txid: Txid) -> Outpoint { + Outpoint::new(self.txid.unwrap_or(default_txid), self.vout) } } impl ExplicitSeal { /// Constructs seal for the provided outpoint and seal closing method. #[inline] - pub fn new(method: CloseMethod, outpoint: OutPoint) -> ExplicitSeal { + pub fn new(method: CloseMethod, outpoint: Outpoint) -> ExplicitSeal { Self { method, txid: Some(outpoint.txid), @@ -132,12 +126,12 @@ impl ExplicitSeal { /// Constructs seal. #[inline] - pub fn with( - method: CloseMethod, - txid: Option, - vout: u32, - ) -> ExplicitSeal { - ExplicitSeal { method, txid, vout } + pub fn with(method: CloseMethod, txid: Option, vout: impl Into) -> ExplicitSeal { + ExplicitSeal { + method, + txid, + vout: vout.into(), + } } } @@ -167,10 +161,6 @@ pub enum ParseError { /// wrong structure of seal string representation WrongStructure, - - /// wrong Bech32 representation of the blinded TxOut seal – {0} - #[from] - Bech32(lnpbp_bech32::Error), } impl FromStr for ExplicitSeal { diff --git a/seals/src/txout/mod.rs b/seals/src/txout/mod.rs index bbf6bbef..b2a76d70 100644 --- a/seals/src/txout/mod.rs +++ b/seals/src/txout/mod.rs @@ -1,17 +1,23 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Bitcoin single-use-seals defined by a transaction output and closed by //! spending that output ("TxOut seals"). diff --git a/seals/src/txout/proto.rs b/seals/src/txout/proto.rs index 230b9364..05fe2eec 100644 --- a/seals/src/txout/proto.rs +++ b/seals/src/txout/proto.rs @@ -1,39 +1,40 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -use bitcoin::Txid; -use bitcoin_onchain::ResolveTx; -use commit_verify::lnpbp4; +use bc::Txid; +use commit_verify::mpc; use dbc::{Anchor, Proof}; use single_use_seals::{SealProtocol, SealStatus, VerifySeal}; +use strict_encoding::StrictDumb; +use crate::resolver::Resolver; use crate::txout::{TxoSeal, VerifyError}; -// TODO: #8 Implement proper operations with SealMedium -// TODO: #9 Do asynchronous version -// #[cfg(feature = "async")] -// use single_use_seals::SealMediumAsync; - pub struct Witness { pub txid: Txid, pub proof: Proof, } impl From> for Witness -where - L: lnpbp4::Proof, +where L: mpc::Proof + StrictDumb { fn from(anchor: Anchor) -> Self { Witness { @@ -44,29 +45,27 @@ where } /// Txo single-use-seal engine. -pub struct TxoProtocol { - resolver: Resolver, +pub struct TxoProtocol { + resolver: R, } -impl SealProtocol for TxoProtocol +impl SealProtocol for TxoProtocol where Seal: TxoSeal, - Resolver: ResolveTx, + R: Resolver, { type Witness = Witness; - type Message = lnpbp4::CommitmentHash; + type Message = mpc::Commitment; type PublicationId = Txid; type Error = VerifyError; - fn get_seal_status(&self, _seal: &Seal) -> Result { - todo!() - } + fn get_seal_status(&self, _seal: &Seal) -> Result { todo!() } } -impl<'seal, Seal, Resolver> VerifySeal<'seal, Seal> for TxoProtocol +impl<'seal, Seal, R> VerifySeal<'seal, Seal> for TxoProtocol where Seal: TxoSeal + 'seal, - Resolver: ResolveTx, + R: Resolver, { fn verify_seal( &self, @@ -75,15 +74,12 @@ where witness: &Self::Witness, ) -> Result { // 1. Get tx - let tx = self.resolver.resolve_tx(witness.txid)?; + let tx = self.resolver.tx_by_id(witness.txid)?; // 2. The seal must match tx inputs let outpoint = seal.outpoint_or(witness.txid); - if !tx.input.iter().any(|txin| txin.previous_output == outpoint) { - return Err(VerifyError::WitnessNotClosingSeal( - witness.txid, - outpoint, - )); + if !tx.inputs.iter().any(|txin| txin.prev_output == outpoint) { + return Err(VerifyError::WitnessNotClosingSeal(witness.txid, outpoint)); } // 3. Verify DBC with the giving closing method @@ -97,7 +93,7 @@ where witness: &Self::Witness, ) -> Result { // 1. Get tx - let tx = self.resolver.resolve_tx(witness.txid)?; + let tx = self.resolver.tx_by_id(witness.txid)?; let mut method = None; for seal in seals { @@ -112,11 +108,8 @@ where // 3. Each seal must match tx inputs let outpoint = seal.outpoint_or(witness.txid); - if !tx.input.iter().any(|txin| txin.previous_output == outpoint) { - return Err(VerifyError::WitnessNotClosingSeal( - witness.txid, - outpoint, - )); + if !tx.inputs.iter().any(|txin| txin.prev_output == outpoint) { + return Err(VerifyError::WitnessNotClosingSeal(witness.txid, outpoint)); } } diff --git a/seals/src/txout/seal.rs b/seals/src/txout/seal.rs index a4cbfcf3..aa96f23c 100644 --- a/seals/src/txout/seal.rs +++ b/seals/src/txout/seal.rs @@ -1,21 +1,27 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol single-use-seals library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use std::str::FromStr; -use bitcoin::{OutPoint, Txid}; +use bc::{Outpoint, Txid, Vout}; use super::MethodParseError; @@ -28,35 +34,32 @@ pub trait TxoSeal { fn txid(&self) -> Option; /// Returns transaction output number containing the defined seal. - fn vout(&self) -> usize; + fn vout(&self) -> Vout; - /// Returns [`OutPoint`] defining the seal, if txid is known. - fn outpoint(&self) -> Option; + /// Returns [`Outpoint`] defining the seal, if txid is known. + fn outpoint(&self) -> Option; /// Returns [`Txid`] part of the seal definition, if known, or the provided /// `default_txid`. fn txid_or(&self, default_txid: Txid) -> Txid; - /// Returns [`OutPoint`] defining the seal, if txid is known, or constructs + /// Returns [`Outpoint`] defining the seal, if txid is known, or constructs /// one using the provided `default_txid`. - fn outpoint_or(&self, default_txid: Txid) -> OutPoint; + fn outpoint_or(&self, default_txid: Txid) -> Outpoint; } /// Method of single-use-seal closing. #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate") -)] -#[derive(StrictEncode, StrictDecode)] -#[strict_encoding(by_value)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = bc::LIB_NAME_BP, tags = repr, into_u8, try_from_u8)] #[repr(u8)] #[non_exhaustive] pub enum CloseMethod { /// Seal is closed over the message in form of OP_RETURN commitment present /// in the first OP_RETURN-containing transaction output. #[display("opret1st")] + #[strict_type(dumb)] OpretFirst = 0x00, /// Seal is closed over the message in form of Taproot-based OP_RETURN @@ -70,12 +73,8 @@ impl FromStr for CloseMethod { fn from_str(s: &str) -> Result { Ok(match s.to_lowercase() { - s if s == CloseMethod::OpretFirst.to_string() => { - CloseMethod::OpretFirst - } - s if s == CloseMethod::TapretFirst.to_string() => { - CloseMethod::TapretFirst - } + s if s == CloseMethod::OpretFirst.to_string() => CloseMethod::OpretFirst, + s if s == CloseMethod::TapretFirst.to_string() => CloseMethod::TapretFirst, _ => return Err(MethodParseError(s.to_owned())), }) } diff --git a/src/bin/dbc.rs b/src/bin/dbc.rs deleted file mode 100644 index 16157ecb..00000000 --- a/src/bin/dbc.rs +++ /dev/null @@ -1,114 +0,0 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -#[macro_use] -extern crate clap; -#[macro_use] -extern crate amplify; - -use std::path::PathBuf; -use std::{fs, io}; - -use amplify::IoError; -use bitcoin::psbt::serialize::{Deserialize, Serialize}; -use bitcoin::psbt::PsbtParseError; -use clap::Parser; -use colored::Colorize; -use commit_verify::EmbedCommitVerify; -use dbc::anchor::PsbtEmbeddedMessage; -use dbc::tapret::PsbtCommitError; -use psbt::Psbt; - -/// Command-line arguments -#[derive(Parser)] -#[derive(Clone, Eq, PartialEq, Debug)] -#[clap( - author, - version, - name = "dbc", - about = "Command-line tool for deterministic bitcoin commitments" -)] -pub struct Args { - /// Command to execute - #[clap(subcommand)] - pub command: Command, -} - -/// Wallet command to execute -#[allow(clippy::large_enum_variant)] -#[derive(Subcommand)] -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum Command { - /// Adds commitment to the PSBT file in positions marked with - /// `LNPBP_CAN_HOST_COMMITMENT` proprietary key. - /// - /// If operation succeeds, removes the flag from the output where the - /// commitment was added, replacing it with `LNPBP_COMMITMENT` proprietary - /// key. - Commit { - /// Input file containing PSBT of the transfer witness transaction. - psbt_in: PathBuf, - - /// Output file to save the PSBT updated with state transition(s) - /// information. If not given, the source PSBT file is overwritten. - psbt_out: Option, - }, -} - -impl Args { - pub fn exec(self) -> Result<(), Error> { - match self.command { - Command::Commit { psbt_in, psbt_out } => { - let psbt_bytes = fs::read(&psbt_in)?; - let mut psbt = Psbt::deserialize(&psbt_bytes)?; - - let anchor = psbt.embed_commit(&PsbtEmbeddedMessage)?; - eprintln!("Anchor: {:?}", anchor); - - let psbt_bytes = psbt.serialize(); - fs::write(psbt_out.unwrap_or(psbt_in), psbt_bytes)?; - } - } - - Ok(()) - } -} - -#[derive(Debug, Display, Error, From)] -#[display(inner)] -pub enum Error { - #[from(io::Error)] - Io(IoError), - - #[from] - BitcoinEncoding(bitcoin::consensus::encode::Error), - - #[from] - StrictEncoding(strict_encoding::Error), - - #[from] - PsbtBase58(PsbtParseError), - - #[from] - #[display(inner)] - Commitment(PsbtCommitError), -} - -fn main() { - let args = Args::parse(); - if let Err(err) = args.exec() { - eprintln!("{}: {}\n", "Error".bright_red(), err); - } -} diff --git a/src/bin/seals.rs b/src/bin/seals.rs deleted file mode 100644 index b0252c03..00000000 --- a/src/bin/seals.rs +++ /dev/null @@ -1,74 +0,0 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -#[macro_use] -extern crate clap; -#[macro_use] -extern crate amplify; - -use std::io; - -use amplify::IoError; -use clap::Parser; -use colored::Colorize; - -/// Command-line arguments -#[derive(Parser)] -#[derive(Clone, Eq, PartialEq, Debug)] -#[clap( - author, - version, - name = "seals", - about = "Command-line tool for working with bitcoin-based single-use-seals" -)] -pub struct Args { - /// Command to execute - #[clap(subcommand)] - pub command: Command, -} - -/// Wallet command to execute -#[derive(Subcommand)] -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum Command { - #[clap()] - Noop, -} - -impl Args { - pub fn exec(&self) -> Result<(), Error> { - match self.command { - Command::Noop => {} - } - Ok(()) - } -} - -#[derive(Debug, Display, Error, From)] -#[display(inner)] -pub enum Error { - #[from(io::Error)] - Io(IoError), - - #[from] - StrictEncoding(strict_encoding::Error), -} - -fn main() { - let args = Args::parse(); - if let Err(err) = args.exec() { - eprintln!("{}: {}\n", "Error".bright_red(), err); - } -} diff --git a/src/lib.rs b/src/lib.rs index 4db06759..6a7c2493 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,23 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol +// Bitcoin protocol core library. // -// Written in 2020-2022 by -// Dr. Maxim Orlovsky +// SPDX-License-Identifier: Apache-2.0 // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. +// Written in 2019-2023 by +// Dr. Maxim Orlovsky // -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . +// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // Coding conventions #![recursion_limit = "256"] @@ -19,25 +25,17 @@ //! Primitives module defines core strict interfaces from informational LNPBP //! standards specifying secure and robust practices for function calls -//! used in main LNP/BP development paradigms: -//! * Cryptographic commitments and verification -//! * Single-use seals -//! * Client-side validation -//! * Strict binary data serialization used by client-side validation +//! used in main bitcoin protocols: +//! - consensus-level primitives; +//! - deterministic bitcoin commitments; +//! - single-use-seals. //! //! The goal of this module is to maximally reduce the probability of errors and //! mistakes within particular implementations of this paradigms by //! standardizing typical workflow processes in a form of interfaces that //! will be nearly impossible to use in the wrong form. -#[macro_use] -extern crate amplify; -#[macro_use] -extern crate strict_encoding; - pub extern crate dbc; pub extern crate seals; -/// pub short_id module -/// allows efficient representation of protocol entities -pub mod short_id; +pub use bc::*; diff --git a/src/short_id.rs b/src/short_id.rs deleted file mode 100644 index fa9c699b..00000000 --- a/src/short_id.rs +++ /dev/null @@ -1,677 +0,0 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -use std::convert::{TryFrom, TryInto}; -use std::fmt::Debug; - -use bitcoin::{BlockHash, Txid}; - -#[derive(Copy, Clone, Debug, Display, PartialEq, Eq)] -#[display(doc_comments)] -/// Errors from descriptor validation and parsing -pub enum Error { - /// invalid block height - BlockHeightOutOfRange, - /// invalid tx input index - InputIndexOutOfRange, - /// invalid tx output index - OutputIndexOutOfRange, - /// invalid tx checksum - ChecksumOutOfRange, - /// tx dimension not defined - DimensionRequired, - /// descriptor upgrade - UpgradeImpossible, - /// descriptor downgrade - DowngradeImpossible, -} - -/// Checksum for block id data used by the LNPBP-5 -#[derive( - Wrapper, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, - Display, From -)] -#[display("{0}", alt = "block_checksum:{0:#}")] -#[wrapper(FromStr, LowerHex, UpperHex, Octal)] -pub struct BlockChecksum(u8); - -impl From for BlockChecksum { - fn from(block_hash: BlockHash) -> Self { - let mut xor: u8 = 0; - for byte in &block_hash[..] { - xor ^= byte; - } - Self::from(xor) - } -} - -/// Checksum for transaction id data used by the LNPBP-5 -#[derive( - Wrapper, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, - Display, From -)] -#[display("{0}", alt = "tx_checksum:{0:#}")] -#[wrapper(FromStr, LowerHex, UpperHex, Octal)] -pub struct TxChecksum(u64); - -impl From for TxChecksum { - fn from(txid: Txid) -> Self { - let mut checksum: u64 = 0; - for (shift, byte) in txid.to_vec()[0..5].iter().enumerate() { - checksum ^= (*byte as u64) << (shift * 8); - } - Self::from(checksum) - } -} - -/// Descriptor enum defines the onchain/offchain entity type -#[derive(Copy, Clone, Debug)] -pub enum Descriptor { - /// Block included onchain - OnchainBlock { - /// Height of onchain block - block_height: u32, - /// Checksum of onchain block - block_checksum: BlockChecksum, - }, - /// Tx included onchain - OnchainTransaction { - /// Height of the block tx belongs to - block_height: u32, - /// Checksum of the block tx belongs to - block_checksum: BlockChecksum, - /// Index in the block tx belongs to - tx_index: u16, - }, - /// Tx input included onchain - OnchainTxInput { - /// Height of the block the tx input belongs to - block_height: u32, - /// Checksum of the block the tx input belongs to - block_checksum: BlockChecksum, - /// Index in the block tx belongs to - tx_index: u16, - /// Index in the tx the input belongs to - input_index: u16, - }, - /// Tx output included onchain - OnchainTxOutput { - /// Height of the block the tx input belongs to - block_height: u32, - /// Checksum of the block the tx input belongs to - block_checksum: BlockChecksum, - /// Index in the block tx belongs to - tx_index: u16, - /// Index in the tx the output belongs to - output_index: u16, - }, - /// Tx not included onchain - OffchainTransaction { - /// Offchain tx checksum - tx_checksum: TxChecksum, - }, - /// Tx input not included onchain - OffchainTxInput { - /// Offchain tx checksum - tx_checksum: TxChecksum, - /// Index in the tx the input belongs to - input_index: u16, - }, - /// Tx output not included onchain - OffchainTxOutput { - /// Offchain tx checksum - tx_checksum: TxChecksum, - /// Index in the tx the output belongs to - output_index: u16, - }, -} - -/// Dimension enum defines tx dimension scope in terms of input/output relation -#[derive(Copy, Clone, Debug, Display, PartialEq, Eq)] -pub enum Dimension { - /// Tx input - #[display("input")] - Input, - /// Tx output - #[display("output")] - Output, -} - -impl Default for Descriptor { - fn default() -> Self { - Descriptor::OnchainBlock { - block_height: 0, - block_checksum: BlockChecksum::default(), - } - } -} - -impl Descriptor { - /// Verifies if Descriptor type has valid properties otherwise returns - /// validation error - pub fn try_validity(&self) -> Result<(), Error> { - match *self { - Descriptor::OnchainTransaction { block_height, .. } - | Descriptor::OnchainTxInput { block_height, .. } - | Descriptor::OnchainTxOutput { block_height, .. } - if block_height >= (2u32 << 22) => - { - Err(Error::BlockHeightOutOfRange) - } - Descriptor::OnchainTxInput { input_index, .. } - | Descriptor::OffchainTxInput { input_index, .. } - if input_index + 1 >= (2u16 << 14) => - { - Err(Error::InputIndexOutOfRange) - } - Descriptor::OnchainTxOutput { output_index, .. } - | Descriptor::OffchainTxOutput { output_index, .. } - if output_index + 1 >= (2u16 << 14) => - { - Err(Error::OutputIndexOutOfRange) - } - Descriptor::OffchainTransaction { tx_checksum, .. } - | Descriptor::OffchainTxInput { tx_checksum, .. } - | Descriptor::OffchainTxOutput { tx_checksum, .. } - if *tx_checksum >= (2u64 << 46) => - { - Err(Error::ChecksumOutOfRange) - } - _ => Ok(()), - } - } - - /// Returns true if Descriptor type is either OnchainBlock or - /// OnchainTransaction or OnchainTxInput or OnchainTxOutput - pub fn is_onchain(&self) -> bool { - matches!( - self, - Descriptor::OnchainBlock { .. } - | Descriptor::OnchainTransaction { .. } - | Descriptor::OnchainTxInput { .. } - | Descriptor::OnchainTxOutput { .. } - ) - } - - /// Returns true if Descriptor type is not onchain type - pub fn is_offchain(&self) -> bool { !self.is_onchain() } - - /// Upgraded returns the "wrapped descriptor" based on provided parameters. - /// for instance, tx is returned in case descriptor is a block, as well as - /// input/out is returned in case descriptor is a tx and dimension is - /// specified - pub fn upgraded( - &self, - index: u16, - dimension: Option, - ) -> Result { - use Dimension::*; - - match (*self, dimension) { - ( - Descriptor::OnchainBlock { - block_height, - block_checksum, - }, - None, - ) => Ok(Descriptor::OnchainTransaction { - block_height, - block_checksum, - tx_index: index, - }), - ( - Descriptor::OnchainTransaction { - block_height, - block_checksum, - tx_index, - }, - Some(dim), - ) if dim == Input => Ok(Descriptor::OnchainTxInput { - block_height, - block_checksum, - tx_index, - input_index: index, - }), - ( - Descriptor::OnchainTransaction { - block_height, - block_checksum, - tx_index, - }, - Some(dim), - ) if dim == Output => Ok(Descriptor::OnchainTxOutput { - block_height, - block_checksum, - tx_index, - output_index: index, - }), - (Descriptor::OffchainTransaction { tx_checksum }, Some(dim)) - if dim == Input => - { - Ok(Descriptor::OffchainTxInput { - tx_checksum, - input_index: index, - }) - } - (Descriptor::OffchainTransaction { tx_checksum }, Some(dim)) - if dim == Output => - { - Ok(Descriptor::OffchainTxOutput { - tx_checksum, - output_index: index, - }) - } - (Descriptor::OnchainTransaction { .. }, None) - | (Descriptor::OffchainTransaction { .. }, None) => { - Err(Error::DimensionRequired) - } - _ => Err(Error::UpgradeImpossible), - } - } - - /// Downgraded returns the "wrapper descriptor", i.e. in case the descriptor - /// is an onchain tx, the onchain block is returned, as well as the onchain - /// tx is returned for onchain input/output - pub fn downgraded(self) -> Result { - match self { - Descriptor::OnchainTransaction { - block_height, - block_checksum, - .. - } => Ok(Descriptor::OnchainBlock { - block_height, - block_checksum, - }), - Descriptor::OnchainTxInput { - block_height, - block_checksum, - tx_index, - .. - } - | Descriptor::OnchainTxOutput { - block_height, - block_checksum, - tx_index, - .. - } => Ok(Descriptor::OnchainTransaction { - block_height, - block_checksum, - tx_index, - }), - Descriptor::OffchainTxInput { tx_checksum, .. } - | Descriptor::OffchainTxOutput { tx_checksum, .. } => { - Ok(Descriptor::OffchainTransaction { tx_checksum }) - } - _ => Err(Error::DowngradeImpossible), - } - } - - /// Get block height extracting from Descriptor - pub fn get_block_height(&self) -> Option { - match self { - Descriptor::OnchainBlock { block_height, .. } - | Descriptor::OnchainTransaction { block_height, .. } - | Descriptor::OnchainTxInput { block_height, .. } - | Descriptor::OnchainTxOutput { block_height, .. } => { - Some(*block_height) - } - _ => None, - } - } - - /// Get block checksum extracting from Descriptor - pub fn get_block_checksum(&self) -> Option { - match self { - Descriptor::OnchainBlock { block_checksum, .. } - | Descriptor::OnchainTransaction { block_checksum, .. } - | Descriptor::OnchainTxInput { block_checksum, .. } - | Descriptor::OnchainTxOutput { block_checksum, .. } => { - Some(**block_checksum) - } - _ => None, - } - } - - /// Get tx checksum extracting from Descriptor - pub fn get_tx_checksum(&self) -> Option { - match self { - Descriptor::OffchainTransaction { tx_checksum, .. } - | Descriptor::OffchainTxInput { tx_checksum, .. } - | Descriptor::OffchainTxOutput { tx_checksum, .. } => { - Some(**tx_checksum) - } - _ => None, - } - } - - /// Get tx index extracting from Descriptor - pub fn get_tx_index(&self) -> Option { - match self { - Descriptor::OnchainTransaction { tx_index, .. } - | Descriptor::OnchainTxInput { tx_index, .. } - | Descriptor::OnchainTxOutput { tx_index, .. } => Some(*tx_index), - _ => None, - } - } - - /// Get input index extracting from Descriptor - pub fn get_input_index(&self) -> Option { - match self { - Descriptor::OnchainTxInput { input_index, .. } - | Descriptor::OffchainTxInput { input_index, .. } => { - Some(*input_index) - } - _ => None, - } - } - - /// Get output index extracting from Descriptor - pub fn get_output_index(&self) -> Option { - match self { - Descriptor::OnchainTxOutput { output_index, .. } - | Descriptor::OffchainTxOutput { output_index, .. } => { - Some(*output_index) - } - _ => None, - } - } - - /// Tries to convert short id from Descriptor to u64 - pub fn try_into_u64(self) -> Result { - ShortId::try_from(self).map(ShortId::into_u64) - } -} - -/// Short id is a descriptor representation that allows to build a Descriptor -/// starting from a specific mask -#[derive( - Copy, - Clone, - PartialOrd, - Ord, - PartialEq, - Eq, - Hash, - Debug, - Display, - StrictEncode, - StrictDecode -)] -#[display("{0:016X}")] -pub struct ShortId(u64); - -impl ShortId { - /// Specifies offchain descriptor - pub const FLAG_OFFCHAIN: u64 = 0x8000_0000_0000_0000; - /// Specifies block descriptor and allows block height definition - pub const MASK_BLOCK: u64 = 0x7FFF_FF00_0000_0000; - /// Allows block checksum definition - pub const MASK_BLOCKCHECK: u64 = 0x0000_00FF_0000_0000; - /// Allows tx index definition - pub const MASK_TXIDX: u64 = 0x0000_0000_FFFF_0000; - /// Allows tx checksum definition - pub const MASK_TXCHECK: u64 = 0x7FFF_FFFF_FFFF_0000; - /// Specifies tx input or output descriptor - pub const FLAG_INOUT: u64 = 0x0000_0000_0000_8000; - /// Allows tx index definition and specifies tx descriptor - pub const MASK_INOUT: u64 = 0x0000_0000_0000_7FFF; - - /// Operation for block height definition - pub const SHIFT_BLOCK: u64 = 40; - /// Operation for block checksum definition - pub const SHIFT_BLOCKCHECK: u64 = 32; - /// Operation for tx id and tx checksum definition - pub const SHIFT_TXIDX: u64 = 16; - - /// Returns if onchain descriptor is represented - pub fn is_onchain(&self) -> bool { - self.0 & Self::FLAG_OFFCHAIN != Self::FLAG_OFFCHAIN - } - /// Returns if offchain descriptor is represented - pub fn is_offchain(&self) -> bool { - self.0 & Self::FLAG_OFFCHAIN == Self::FLAG_OFFCHAIN - } - - /// Generates descriptor from short id definition - pub fn get_descriptor(&self) -> Descriptor { - #[inline] - fn iconv(val: u64) -> T - where - T: TryFrom, - >::Error: Debug, - { - val.try_into() - .expect("Conversion from existing ShortId can't fail") - } - - let index: u16 = iconv(self.0 & Self::MASK_INOUT); - - if self.is_onchain() { - let block_height: u32 = - iconv((self.0 & Self::MASK_BLOCK) >> Self::SHIFT_BLOCK); - let block_checksum = BlockChecksum::from(iconv::( - (self.0 & Self::MASK_BLOCKCHECK) >> Self::SHIFT_BLOCKCHECK, - )); - if (self.0 & (!Self::MASK_BLOCK)) == 0 { - return Descriptor::OnchainBlock { - block_height, - block_checksum, - }; - } - let tx_index: u16 = - iconv((self.0 & Self::MASK_TXIDX) >> Self::SHIFT_TXIDX); - if (self.0 & (!Self::MASK_INOUT)) == 0 { - return Descriptor::OnchainTransaction { - block_height, - block_checksum, - tx_index, - }; - } - if (self.0 & Self::FLAG_INOUT) == 0 { - Descriptor::OnchainTxInput { - block_height, - block_checksum, - tx_index, - input_index: index - 1, - } - } else { - Descriptor::OnchainTxOutput { - block_height, - block_checksum, - tx_index, - output_index: index - 1, - } - } - } else { - let tx_checksum = TxChecksum::from( - (self.0 & Self::MASK_TXCHECK) >> Self::SHIFT_TXIDX, - ); - if (self.0 & (!Self::MASK_INOUT)) == 0 { - return Descriptor::OffchainTransaction { tx_checksum }; - } - if (self.0 & Self::FLAG_INOUT) == 0 { - Descriptor::OffchainTxInput { - tx_checksum, - input_index: index - 1, - } - } else { - Descriptor::OffchainTxOutput { - tx_checksum, - output_index: index - 1, - } - } - } - } - - /// Converts short id into inner u64 - pub fn into_u64(self) -> u64 { self.into() } -} - -impl From for Descriptor { - fn from(short_id: ShortId) -> Self { short_id.get_descriptor() } -} - -impl TryFrom for ShortId { - type Error = self::Error; - - fn try_from(descriptor: Descriptor) -> Result { - use Descriptor::*; - - descriptor.try_validity()?; - - let block_height: u64 = match descriptor { - OnchainBlock { block_height, .. } - | OnchainTransaction { block_height, .. } - | OnchainTxInput { block_height, .. } - | OnchainTxOutput { block_height, .. } => block_height, - _ => 0, - } as u64; - let block_checksum = *match descriptor { - OnchainBlock { block_checksum, .. } - | OnchainTransaction { block_checksum, .. } - | OnchainTxInput { block_checksum, .. } - | OnchainTxOutput { block_checksum, .. } => block_checksum, - _ => BlockChecksum::default(), - } as u64; - let tx_index = match descriptor { - OnchainTransaction { tx_index, .. } - | OnchainTxInput { tx_index, .. } - | OnchainTxOutput { tx_index, .. } => tx_index, - _ => 0, - } as u64; - let tx_checksum = match descriptor { - OffchainTransaction { tx_checksum } - | OffchainTxInput { tx_checksum, .. } - | OffchainTxOutput { tx_checksum, .. } => tx_checksum, - _ => TxChecksum::default(), - }; - let inout_index: u64 = match descriptor { - OnchainTxInput { input_index, .. } - | OffchainTxInput { input_index, .. } => input_index + 1, - OnchainTxOutput { output_index, .. } - | OffchainTxOutput { output_index, .. } => output_index + 1, - _ => 0, - } as u64; - - let mut short_id = 0u64; - short_id |= inout_index; - if descriptor.is_offchain() { - short_id |= Self::FLAG_OFFCHAIN; - short_id |= - (*tx_checksum << Self::SHIFT_TXIDX) & Self::MASK_TXCHECK; - } else { - short_id |= (block_height << 40) & Self::MASK_BLOCK; - short_id |= (block_checksum << Self::SHIFT_BLOCKCHECK) - & Self::MASK_BLOCKCHECK; - short_id |= (tx_index << 16) & Self::MASK_TXIDX; - } - - match descriptor { - OnchainTxOutput { .. } | OffchainTxOutput { .. } => { - short_id |= Self::FLAG_INOUT << Self::SHIFT_TXIDX - } - _ => (), - } - - Ok(Self(short_id)) - } -} - -impl From for ShortId { - fn from(val: u64) -> Self { Self(val) } -} - -impl From for u64 { - fn from(short_id: ShortId) -> Self { short_id.0 } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn short_id_is_onchain() { - let test_cases = vec![ - 0, - 1, - 100, - 16, - 32, - 40, - 0x7FFF_FF00_0000_0000, - 0x0000_0000_0000_8000, - ]; - for c in &test_cases { - let sid = ShortId(*c); - assert!(sid.is_onchain()); - } - } - - #[test] - fn short_id_is_offchain() { - let test_cases = vec![ - 0x8000_0000_0000_0000, - 0x8000_0000_0000_0001, - 0x9000_0000_0000_0000, - 0xFFFF_0000_0000_0000, - ]; - for c in &test_cases { - let sid = ShortId(*c); - assert!(sid.is_offchain()); - } - } - - #[test] - fn short_id_into() { - let test_cases = [0, 1]; - for c in &test_cases { - let sid = ShortId(*c); - assert_eq!(sid.into_u64(), *c); - } - } - - #[test] - fn short_id_get_descriptor_empty() { - let sid = ShortId(0); - let descriptor = sid.get_descriptor(); - match descriptor.get_block_height() { - Some(h) => assert_eq!(h, 0), - None => {} - } - } - - #[test] - fn short_id_get_descriptor_block_height_valid() { - let test_cases = [ - [0x0000_0100_0000_0000, 1], - [0x0000_1000_0000_0000, 16], - [0x0001_0000_0000_0000, 256], - ]; - for c in &test_cases { - let sid = ShortId(c[0]); - match sid.get_descriptor().get_block_height() { - Some(h) => assert_eq!(u64::from(h), c[1]), - None => {} - } - } - } - - #[test] - #[cfg(not(codecov))] - #[should_panic(expected = "attempt to subtract with overflow")] - fn short_id_get_descriptor_block_height_overflow() { - let sid = ShortId(0x0000_0000_1000_0000); - sid.get_descriptor().get_block_height(); - } -} diff --git a/src/trust_resolvers.rs b/src/trust_resolvers.rs deleted file mode 100644 index a06320df..00000000 --- a/src/trust_resolvers.rs +++ /dev/null @@ -1,75 +0,0 @@ -// BP Core Library implementing LNP/BP specifications & standards related to -// bitcoin protocol -// -// Written in 2020-2022 by -// Dr. Maxim Orlovsky -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the Apache 2.0 License -// along with this software. -// If not, see . - -//! This is a planned API for v0.5.0 that will help structuring RGB validation -//! into a more formal process - -/// This simple trait MUST be used by all parties implementing client-side -/// validation paradigm. The core concept of this paradigm is that a client -/// must have a complete and uniform set of data, which can be represented -/// or accessed through a single structure; and MUST be able to -/// deterministically validate this set giving an external validation function, -/// that is able to provide validator with -pub trait ClientSideValidate -where - Resolver: TrustResolver, -{ - type ClientData: ClientData; - type ValidationError: FromTrustProblem - + FromInternalInconsistency; - - fn new() -> Self; - - fn client_side_validate( - client_data: Self::ClientData, - trust_resolver: Resolver, - ) -> Result<(), Self::ValidationError> { - let validator = Self::new(); - client_data.validate_internal_consistency()?; - client_data.validation_iter().try_for_each(|item| { - trust_resolver - .resolve_trust(item, validator.get_context_for_atom(item))?; - item.client_side_validate() - }) - } - - fn get_context_for_item( - &self, - data_item: Self::ClientData::ValidationItem, - ) -> Ctx; -} - -pub trait ClientData { - type ValidationItem: ClientData; -} - -/// Trust resolver for a given client data type MUST work with a single type -/// of [`TrustResolver::Context`], defined by an associated type. Trust -/// resolution MUST always produce a singular success type (defined by `()`) or -/// fail with a well-defined type of [`TrustResolver::TrustProblem`]. -/// -/// Trust resolver may have an internal state (represented by `self` reference) -/// and it does not require to produce a deterministic result for the same -/// given data piece and context: the trust resolver may depend on previous -/// operation history and depend on type and other external parameters. -pub trait TrustResolver { - type TrustProblem: std::error::Error; - type Context; - fn resolve_trust( - &self, - data_piece: &T, - context: &Self::Context, - ) -> Result<(), Self::TrustProblem>; -} diff --git a/test/depCargo.toml b/test/depCargo.toml deleted file mode 100644 index fa697f06..00000000 --- a/test/depCargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -# This is an add-on that must be added to any dependency using this library - -bp-core = { path = "../bp-core" } - -[workspace]