From a5b66187d064e92fd8fe734fe86921a54b1af7c5 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 24 Aug 2018 18:28:56 +0000 Subject: [PATCH] initial 0.1 commit --- .gitignore | 7 + .travis.yml | 17 + Cargo.lock | 187 +++++ Cargo.toml | 23 + LICENSE | 122 +++ README.md | 9 + fuzz/Cargo.lock | 356 +++++++++ fuzz/Cargo.toml | 34 + fuzz/fuzz_targets/deserialize_block.rs | 61 ++ fuzz/fuzz_targets/deserialize_transaction.rs | 72 ++ fuzz/travis-fuzz.sh | 19 + src/block.rs | 330 ++++++++ src/confidential.rs | 236 ++++++ src/internal_macros.rs | 196 +++++ src/lib.rs | 39 + src/transaction.rs | 791 +++++++++++++++++++ 16 files changed, 2499 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 fuzz/Cargo.lock create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/deserialize_block.rs create mode 100644 fuzz/fuzz_targets/deserialize_transaction.rs create mode 100755 fuzz/travis-fuzz.sh create mode 100644 src/block.rs create mode 100644 src/confidential.rs create mode 100644 src/internal_macros.rs create mode 100644 src/lib.rs create mode 100644 src/transaction.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ed09e331 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +target +**/*.rs.bk + +#fuzz +fuzz/hfuzz_target +fuzz/hfuzz_workspace + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..b8a318d5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: rust +rust: + - stable + - beta + - nightly + - 1.14.0 + +before_install: + - sudo apt-get -qq update + - sudo apt-get install -y binutils-dev libunwind8-dev + +script: + - cargo build --verbose + - cargo test --verbose + - cargo build --verbose --features=serde-feature + - cargo test --verbose --features=serde-feature + - if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..b0d2ecbd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,187 @@ +[[package]] +name = "bech32" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitcoin" +version = "0.14.0" +dependencies = [ + "bitcoin-bech32 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitcoin-bech32" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bech32 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "elements" +version = "0.1.0" +dependencies = [ + "bitcoin 0.14.0", + "secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "secp256k1" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bech32 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad20b907fd16610c3960c7fe9dae13dd243343409bab80299774c9a8b5d7bed8" +"checksum bitcoin-bech32 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab22a4c40d68ebb8cb3a820273e46ec3ed959556214967e88d33d8665f07016e" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8389c509ec62b9fe8eca58c502a0acaf017737355615243496cde4994f8fa4f9" +"checksum cc 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "aa525e34328b15853ac443f0b9048fc6d807389d873faaa19524775df274e6c1" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09b9a83dad1ca45195bf788cba1bb4bef2415133cd6f0a97a4da56462e4b824d" +"checksum serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)" = "f218becd0d51dd24297ef804cb9b2de179dcdc2a3ddf8a73b04b4d595d9e6338" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..1b67fedb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "elements" +version = "0.1.0" +authors = ["Andrew Poelstra "] + +[features] +"serde-feature" = [ + "bitcoin/serde", + "serde" +] +"fuzztarget" = [] + +[dependencies] +secp256k1 = "0.11.0" + +[dependencies.bitcoin] +version = "0.14.0" +path = "../../rust-bitcoin/rust-bitcoin/" + +[dependencies.serde] +version = "1.0" +optional = true + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6ca207ef --- /dev/null +++ b/LICENSE @@ -0,0 +1,122 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + diff --git a/README.md b/README.md new file mode 100644 index 00000000..a72554f7 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +[![Status](https://travis-ci.org/rust-bitcoin/rust-bitcoin.png?branch=master)](https://travis-ci.org/rust-bitcoin/rust-bitcoin) + +# Rust Elements Library + +Library with support for de/serialization, parsing and executing on data +structures and network messages related to Elements + +[Documentation](https://docs.rs/elements/) + diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock new file mode 100644 index 00000000..e35756db --- /dev/null +++ b/fuzz/Cargo.lock @@ -0,0 +1,356 @@ +[[package]] +name = "afl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bech32" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitcoin" +version = "0.14.0" +dependencies = [ + "bitcoin-bech32 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitcoin-bech32" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bech32 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "elements" +version = "0.1.0" +dependencies = [ + "bitcoin 0.14.0", + "secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "elements-fuzz" +version = "0.0.1" +dependencies = [ + "afl 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitcoin 0.14.0", + "elements 0.1.0", + "honggfuzz 0.5.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "honggfuzz" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "secp256k1" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "xdg" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum afl 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1cb95efcc9c56c128693f3c2fd2c7cef51237b2aa3e65f4ac6a391b8ac2cec8" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum bech32 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad20b907fd16610c3960c7fe9dae13dd243343409bab80299774c9a8b5d7bed8" +"checksum bitcoin-bech32 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab22a4c40d68ebb8cb3a820273e46ec3ed959556214967e88d33d8665f07016e" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8389c509ec62b9fe8eca58c502a0acaf017737355615243496cde4994f8fa4f9" +"checksum cc 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3cc738b39aea615873af94099619ed6915a82575880ed5cdfbf17b63307f1b14" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum honggfuzz 0.5.26 (registry+https://github.com/rust-lang/crates.io-index)" = "79b9bdb5c0dd1ccb0a727ae3bb1d7c3d865915d79384b6ea272dcfd6135afd43" +"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum secp256k1 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09b9a83dad1ca45195bf788cba1bb4bef2415133cd6f0a97a4da56462e4b824d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.74 (registry+https://github.com/rust-lang/crates.io-index)" = "f218becd0d51dd24297ef804cb9b2de179dcdc2a3ddf8a73b04b4d595d9e6338" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 00000000..ce6670c0 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "elements-fuzz" +version = "0.0.1" +authors = ["Automatically generated"] +publish = false + +[package.metadata] +cargo-fuzz = true + +[features] +afl_fuzz = ["afl"] +honggfuzz_fuzz = ["honggfuzz"] + +[dependencies] +honggfuzz = { version = "0.5", optional = true } +afl = { version = "0.3", optional = true } +elements = { path = "..", features = ["fuzztarget", "serde-feature"] } + +[dependencies.bitcoin] +version = "0.14" +path = "../../../rust-bitcoin/rust-bitcoin/" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "deserialize_transaction" +path = "fuzz_targets/deserialize_transaction.rs" + +[[bin]] +name = "deserialize_block" +path = "fuzz_targets/deserialize_block.rs" + diff --git a/fuzz/fuzz_targets/deserialize_block.rs b/fuzz/fuzz_targets/deserialize_block.rs new file mode 100644 index 00000000..e2db7a82 --- /dev/null +++ b/fuzz/fuzz_targets/deserialize_block.rs @@ -0,0 +1,61 @@ + +extern crate bitcoin; +extern crate elements; + +fn do_test(data: &[u8]) { + let block_result: Result = bitcoin::network::serialize::deserialize(data); + match block_result { + Err(_) => {}, + Ok(block) => { + let reser = bitcoin::network::serialize::serialize(&block).unwrap(); + assert_eq!(data, &reser[..]); + }, + } +} + +#[cfg(feature = "afl")] +extern crate afl; +#[cfg(feature = "afl")] +fn main() { + afl::read_stdio_bytes(|data| { + do_test(&data); + }); +} + +#[cfg(feature = "honggfuzz")] +#[macro_use] extern crate honggfuzz; +#[cfg(feature = "honggfuzz")] +fn main() { + loop { + fuzz!(|data| { + do_test(data); + }); + } +} + +#[cfg(test)] +mod tests { + fn extend_vec_from_hex(hex: &str, out: &mut Vec) { + let mut b = 0; + for (idx, c) in hex.as_bytes().iter().enumerate() { + b <<= 4; + match *c { + b'A'...b'F' => b |= c - b'A' + 10, + b'a'...b'f' => b |= c - b'a' + 10, + b'0'...b'9' => b |= c - b'0', + _ => panic!("Bad hex"), + } + if (idx & 1) == 1 { + out.push(b); + b = 0; + } + } + } + + #[test] + fn duplicate_crash() { + let mut a = Vec::new(); + extend_vec_from_hex("a202569152bfae5279ada872812d36363437b3b3b3b3b3b3b3b3b3b3b3b3b2b3b3b3b300000000000000ff0000000000005e320b000000015e6381903619adddde7df62eacee7218f657ef31000001000000000000fe0000000000014006", &mut a); + super::do_test(&a); + } +} diff --git a/fuzz/fuzz_targets/deserialize_transaction.rs b/fuzz/fuzz_targets/deserialize_transaction.rs new file mode 100644 index 00000000..263992a7 --- /dev/null +++ b/fuzz/fuzz_targets/deserialize_transaction.rs @@ -0,0 +1,72 @@ + +extern crate bitcoin; +extern crate elements; + +fn do_test(data: &[u8]) { + let tx_result: Result = bitcoin::network::serialize::deserialize(data); + match tx_result { + Err(_) => {}, + Ok(mut tx) => { + let reser = bitcoin::network::serialize::serialize(&tx).unwrap(); + assert_eq!(data, &reser[..]); + let len = reser.len(); + let calculated_weight = tx.get_weight(); + for input in &mut tx.input { + input.witness = elements::TxInWitness::default(); + } + for output in &mut tx.output { + output.witness = elements::TxOutWitness::default(); + } + assert_eq!(tx.has_witness(), false); + let no_witness_len = bitcoin::network::serialize::serialize(&tx).unwrap().len(); + assert_eq!(no_witness_len * 3 + len, calculated_weight); + }, + } +} + +#[cfg(feature = "afl")] +extern crate afl; +#[cfg(feature = "afl")] +fn main() { + afl::read_stdio_bytes(|data| { + do_test(&data); + }); +} + +#[cfg(feature = "honggfuzz")] +#[macro_use] extern crate honggfuzz; +#[cfg(feature = "honggfuzz")] +fn main() { + loop { + fuzz!(|data| { + do_test(data); + }); + } +} + +#[cfg(test)] +mod tests { + fn extend_vec_from_hex(hex: &str, out: &mut Vec) { + let mut b = 0; + for (idx, c) in hex.as_bytes().iter().enumerate() { + b <<= 4; + match *c { + b'A'...b'F' => b |= c - b'A' + 10, + b'a'...b'f' => b |= c - b'a' + 10, + b'0'...b'9' => b |= c - b'0', + _ => panic!("Bad hex"), + } + if (idx & 1) == 1 { + out.push(b); + b = 0; + } + } + } + + #[test] + fn duplicate_crash() { + let mut a = Vec::new(); + extend_vec_from_hex("00", &mut a); + super::do_test(&a); + } +} diff --git a/fuzz/travis-fuzz.sh b/fuzz/travis-fuzz.sh new file mode 100755 index 00000000..ae85ea91 --- /dev/null +++ b/fuzz/travis-fuzz.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e +cargo install --force honggfuzz +for TARGET in fuzz_targets/*; do + FILENAME=$(basename $TARGET) + FILE="${FILENAME%.*}" + if [ -d hfuzz_input/$FILE ]; then + HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input" + fi + HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run $FILE + + if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then + cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT + for CASE in hfuzz_workspace/$FILE/SIG*; do + cat $CASE | xxd -p + done + exit 1 + fi +done diff --git a/src/block.rs b/src/block.rs new file mode 100644 index 00000000..d69f74b4 --- /dev/null +++ b/src/block.rs @@ -0,0 +1,330 @@ +// Rust Elements Library +// Written in 2018 by +// Andrew Poelstra +// +// 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 CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Blocks +//! + +use bitcoin::blockdata::script::Script; +use bitcoin::network::serialize::BitcoinHash; +use bitcoin::util::hash::Sha256dHash; + +use Transaction; + +/// Block proof, which replaces PoW with an arbitrary script satisfaction +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct Proof { + /// Block "public key" + challenge: Script, + /// Satisfying witness to the above challenge, or nothing + solution: Script, +} +serde_struct_impl!(Proof, challenge, solution); +impl_consensus_encoding!(Proof, challenge, solution); + +/// Elements block header +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct BlockHeader { + /// Version - should be 0x20000000 except when versionbits signalling + version: u32, + /// Previous blockhash + prev_block: Sha256dHash, + /// Transaction Merkle root + merkle_root: Sha256dHash, + /// Block timestamp + time: u32, + /// Block height + height: u32, + /// Block height + proof: Proof, + +} +serde_struct_impl!(BlockHeader, version, prev_block, merkle_root, time, height, proof); +impl_consensus_encoding!(BlockHeader, version, prev_block, merkle_root, time, height, proof); + +impl BitcoinHash for BlockHeader { + fn bitcoin_hash(&self) -> Sha256dHash { + use bitcoin::network::encodable::ConsensusEncodable; + use bitcoin::util::hash::Sha256dEncoder; + + // Everything except proof.solution goes into the hash + let mut enc = Sha256dEncoder::new(); + self.version.consensus_encode(&mut enc).unwrap(); + self.prev_block.consensus_encode(&mut enc).unwrap(); + self.merkle_root.consensus_encode(&mut enc).unwrap(); + self.time.consensus_encode(&mut enc).unwrap(); + self.height.consensus_encode(&mut enc).unwrap(); + self.proof.challenge.consensus_encode(&mut enc).unwrap(); + enc.into_hash() + } +} + +/// Elements block +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct Block { + /// Header of the block + pub header: BlockHeader, + /// Complete list of transaction in the block + pub tx: Vec, +} +serde_struct_impl!(Block, header, tx); +impl_consensus_encoding!(Block, header, tx); + +impl BitcoinHash for Block { + fn bitcoin_hash(&self) -> Sha256dHash { + self.header.bitcoin_hash() + } +} + +#[cfg(test)] +mod tests { + use bitcoin::network::serialize::BitcoinHash; + + use Block; + + #[test] + fn transaction() { + // Simple block with only coinbase output + let block: Block = hex_deserialize!( + "00000020a66e4a4baff69735267346d12e59e8a0da848b593813554deb16a6f3\ + 6cd035e9aab0e2451724598471dd4e45f0dca40ca5f4ac62e61957e50925af08\ + 59891fcc8842805b020000000151000102000000010100000000000000000000\ + 00000000000000000000000000000000000000000000ffffffff03520101ffff\ + ffff0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a\ + 0d5de1b201000000000000000000016a01230f4f5d4b7c6fa845806ee4f67713\ + 459e1b69e8e60fcee2e4940c7a0d5de1b201000000000000000000266a24aa21\ + a9ed94f15ed3a62165e4a0b99699cc28b48e19cb5bc1b1f47155db62d63f1e04\ + 7d45000000000000012000000000000000000000000000000000000000000000\ + 000000000000000000000000000000" + ); + + assert_eq!( + block.bitcoin_hash().to_string(), + "287ca47e8da47eb8c28d870663450bb026922eadb30a1b2f8293e6e9d1ca5322" + ); + assert_eq!(block.header.version, 0x20000000); + assert_eq!(block.header.height, 2); + assert_eq!(block.tx.len(), 1); + + // Block with 3 transactions ... the rangeproofs are very large :) + let block: Block = hex_deserialize!( + "000000207e3dba98460e4136659f0fccf3e59338dfe53ed5f094fb0bb94d771c\ + 48341854d875900105c87e5dd46c740cb1129c06f8f4007e868f61b25e37cffa9\ + 46c718d8742805b01000000015100030200000001010000000000000000000000\ + 000000000000000000000000000000000000000000ffffffff03510101fffffff\ + f0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5d\ + e1b2010000000000009b64001976a914608c0ea8194a8ceb57f0196f44a6b48a5\ + 4fc065988ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e494\ + 0c7a0d5de1b201000000000000000000266a24aa21a9ed8f8a98e5623643b2416\ + 7266c2648ead4a50d18b0491c6f34e11398aaee0ca6e800000000000001200000\ + 00000000000000000000000000000000000000000000000000000000000000000\ + 00000020000000001eb04b68e9a26d116046c76e8ff47332fb71dda90ff4bef53\ + 70f25226d3bc09fc0000000000feffffff0201230f4f5d4b7c6fa845806ee4f67\ + 713459e1b69e8e60fcee2e4940c7a0d5de1b20100000002540bd71c001976a914\ + 48633e2c0ee9495dd3f9c43732c47f4702a362c888ac01230f4f5d4b7c6fa8458\ + 06ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000ce400\ + 0000000000020000000101f23ceddac67cfbbc997199daa651384d0746fb2a548\ + 2b8c8629ba8df4b788f75000000006b483045022100e0feb3e2f292000d67e24b\ + 821d87c9532230dac1de428d6a0068c9f416583abf02200e76f072788dd411b23\ + 27267cd91c6b1659809598cd4fae35be475efe1e4bbad01210201e15c23c02165\ + 2d07c1557b607ea0379fca0462aca840d6c33c4d4927524547feffffff030b604\ + 24a423335923c15ae387d95d4f80d944722020bfa55b9f0a0e67579e3c13c081c\ + 4f215239c77456d121eb73bd9914a9a6398fe369b4eb8f88a5f78e257fcaa3033\ + 01ee46349950886ae115c9556607fcda9381c2f72368f4b5286488c62aa0b0819\ + 76a9148bb6c4d5814d43fefb9e330575e326632136389c88ac0bd436b0539f549\ + 7af792d7cb281f09b73d8a5abc198b3ce6239d79e68893e5e5d0923899fd35071\ + ba8a209d85b556d5747b6c35539c3b2f8631a27c0d477a1f45a603d1d350b8cbf\ + 900f7666da66541bf6252fc4c162141ad49c670884c93c57db6ba1976a9148c7a\ + b6e0fca387d03643d4846f708bf39d47c1e988ac01230f4f5d4b7c6fa845806ee\ + 4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000008e80000000\ + 0000000000000043010001dc65ae13f76fde4a7172e0fb380b1a5cc8dc88eaa06\ + 59e638a25eac8ae30d79bf93eb7e487eeee323e4ac8e3a2fe6523bdeba6acce32\ + b9b085f2286174c04655fd6c0a6020000000000000000178ad016b3e5d8165423\ + e56d8b37e3eaee96009b2f970043ccf65d61b5c3c1e1ef343e0c479bdba442717\ + dc861c9591566010240b9d4607efb9252a5fcef05edf640e0bb6b606729246ad0\ + 7baa49d0d3b52042c65a03ca737744e45b2d2d6d177c36569ae9d6eb4437305b1\ + 69bbc59f85cabff3bc49a2d6d08c177cce3121a509d3c47961bd22e35c932b79d\ + 4ec5ccaf913fac04034bfebdadbc4ff3127af96344b02ee6b967bb08326cbe6a4\ + e1c924485e64a8c0fdf70b98c99f38acaa15aa0adb2b5b7335ed5502443891bcd\ + 657310347cbd928f40f38f1dec087a2b947c9cf7d304798f77bbc4a2c843796b2\ + d49acce91de4e88a0a9c261277df28ffc3320d7f7d64790f592ddded48a1068ef\ + 88271395fa5606389ef90856ddd6bd6710a8d27e0147983b5dde2a7efae44e83a\ + d02a3c3da04be43d5f2c05c205f1e17b48554c2177670f46dbb6600bd2e6c75dd\ + 5ea2e1072c5f22483dcf05d8124e3f9063a5ddb179a29c23a2d15d6e89f2192f0\ + 3dae5938f66fcdcff000c5a96ffd2920f23881880af72153c96a56dd80c218bb4\ + 8b44a18e54a8050ff32c869c1264ee574cdb4002f86e0779c724d11dc4a768dbe\ + c1bd22054886f1fdf2e7347e4c247b829159d1375f881c6ce0a5c4da8534000e7\ + fec3a980afb1edc99b725c29de80f260dcf144c873bf589ae1812ef6cb05f2234\ + f9c66c23e874a0d5d0dc52f2209e015bbcf74ee449a397f6b0318c915b7e58dea\ + 5904abbe35285e90ccf548ad1f3f52f60c3b19b3cd67644d633e68aef42d8ef17\ + 82f22a8edd0620f55f29070720ca7a078ac83e87b9ebd2783ecad17dd854ef1bb\ + d319f1a6d3a1e4931f9097422f5a3c4af037b99e06c7610ee61102c6eea763af1\ + 08e9a16b93b2dc0891658d5c6a197df6aae9b306b2c895d21c79cb6cb6dd85b40\ + 18b0a9fe7468336e3907eb4adcaf930cacc97e8e951d2d6b25744a4143679bad1\ + f31b210c9a2ed54b80d8f5d7dc1f1c985681534c1926920cd683d95dca7e8ea28\ + 5f9906d2e89cd8bfa76a98e38ee4b5152522d55f79610fe8d5278fe6ed5866b5d\ + a4dcf330ea84307c34f30e1a66eb1934dafebb0074fc27c2ff73d8c0bae8416cc\ + 87bf611f81119aba9e2a911beaf3ac9507e621fc1ed1cf15dfb31408cf55e2bfd\ + d2880db2d3489a336d6f8348347648d882f9f376331e469e809115c6cc82468f3\ + 63c910673e9ded172ded90a369e1cdd135676f623e11a1531ed221177812b1ef0\ + c65e5ca92c0df8de7fe664710f3228a226e019c99607fe1395ecd5643e1c7ad8a\ + 132bf5131737cb970a7f0dabc00029755bf71b3f47bd69ba39b3ab104c74f0423\ + 9f4919dca1dfce7c9c41cba9d449073e106ebabe3c313b598ee8b11702ec46e9e\ + e53fb9422f0326371898b8fa4c21a951684c687398e0bebd6f6fd91b829e8666b\ + 9a19a4273cfda0f34b8ecb902f7adc6539fb9a0cba6f87a63a957acfb2dfa1897\ + 3f4a3063668767b2be780311513c63f1814f082176f6a953f2ffaa49ec9b39fec\ + c2eab603be7a969bb4c1dbebf8d39fa90f802d5ea52378b5025a19b64a8c2c2dd\ + 6a6133bd8d29730bd5724b5bf50c158b238d1137082937ad91a176aaf91577868\ + db7581b457c917e612b242ce0065ad47e11dcdc1fc6158687142249bcf312497a\ + 547b6f43e795af7d4ae8cd022e44e417987e35e83de21e39dcdf86b97bd421e6e\ + 61881a432fa2284f20be80e32459443736b875d9036468ceb881589394441e2d1\ + 0aa10b6c93332951e8ba56f89fac70baf415b4511873c0f3e418ca4fe8954a28f\ + 1f7b5f590d34470119f694e2712f184882d90396c8e6aa850eaa3c2ae51990543\ + 638c46c59512167a2c5ad593532dc2142ffb6560476e4159213b9ef017ec75310\ + d2e4624a405bb26f7192a485a94890674928c9caa4a5819ca4ddcba8fa71afc1a\ + 6baf63f039452c8fe994f8b63d58c876dfddd61a476345eaed4f66bdc0fcfc38d\ + 485c6a5b0e27d0fbc50427ff591ba38d63445c01642cfbd7d4c032f2546a6fe80\ + bc3b598362502c552049523fe360c3bcf1cc572feb04386f97d55871dd8cea039\ + 3cdd964e724082adc98126e6f2fe1d576be4bf911e9aca70e35538175f8382bbc\ + d614bbecc97c9607ef25da2ff08a6e5b6f76cbe9ccb0e0fdc3528e3e2c3675a5c\ + 897d295bb76524ec8a73a70b97909368f44d92f9aceaef0b03f3dafa1faa89fc6\ + 63a92da3c19b4952463fac0e825e78cf046e266cfb9975af72e9d50d2c2cafee8\ + 8fe2cecae2b1465fc07b280d83b66062dc9e7a372f81aec8e0bb9e97877814a5a\ + 6813c67746e35cd068d45d8664528bd00d5a306a5319e1bea7f38345da92d3a10\ + d91476a26aed6b8441f0f72fbbad5d5e0f8ae5cabc9f4f08e6be7902b5c53632d\ + b5264afee7422c87b3237a32d5213ad0eb807b61977d9d90666cbb0c70500526b\ + 0eb762c99351796db41166b0aa2f221b5607e0d629fac4e938488245c11557381\ + a4f8addcc49913b11d42481cf8668e37bacbad4a20509e4fe4ccbcee7aea2909a\ + 2abe59052f7f28b9340cd92f69729d615b8d3b530941c0b30506498cd4e561a9c\ + 82d915266bb7115967bc76c5593c06d094bdf4294b868afc5fa52742d3bdbd593\ + 2df599f0e1187c49f0dba8679c771a514cc9da75e03506957800bf470d4a07c4b\ + b8918d6085499bb8ceeaba23c0b465863327e9ab8b6b8cf8b3ca530ca7b02cfad\ + f85437b750f305e8fbc8855c95bee8595a7e9e1f0993a03adbadc68665a18936c\ + c99b6530b4518c0754990d7bfdfdac76f88cfcbcb7b3d9a71ee10cbd3a1bdbc2e\ + 50b642c1fef56511962f845bbec6eab727b1d4add335db8d80c4c07e8356ad05a\ + dad68b012489fa5bb5d9019a667778ddf7f5edd80f1d3c4abd64397a89e554c80\ + 07809336ddc2b2e7d5219c39fdf39aad33b9350f6b18fe3b98c690b9068f36d4b\ + 7669530fd216373842fbf70fe9bbe80854b31eed4bd515d6caeb065d6c609846c\ + 9bfae1b3fce3db70b5bfb448ec69512e7f25019c789301b77a75f2a0f81c65ec2\ + 9f41bf96d597a00c310e8ba4b48ac82b5a735c1e83f22394eb2fc9b35d42a3553\ + 3c938f26290a5860175637982f1733c99be39c44ac4a09187406306bde2fd3d28\ + e4e7bda73719912c338804dea03987757dac4d73def665e11da126f9414f71624\ + a3b753797eb0472bd334094515c4f9fe57fdd8d185f22b4bf82e4b5f6b800870c\ + ce19a0c8174dc11ee9f1cb9ffe0ac6f6fff1ebf7c915c7ae20172bb70390e3759\ + 912e0e0a4e83a0a2d2318f4386314a89f6438ccb331f89377ff7947fe4b24f788\ + aef85c1656ca87ee41c959f1b09bde09f20c2a51ac481646b28e9b0fc2ff49cfe\ + 8cf28577bf5bf6f261f54f97fcd2875da4210c6dfe685450280b68e378d9a4862\ + 43cc682ed4ec747c37de1fde848e4a8f70498d22e40c462c469c884cd67330e77\ + b694e759232313f31a1624e0e1960f23ddae47b68ff553d0de0910c8abe2e8e5f\ + b063aa744ff77465fc731c7af79a84dcaa9b3f741a46dd3c932877d49242c6d88\ + 3e14392b8c4530986605812b636a73590ef437f27e40d1af37ed1cbd68fb4e9ca\ + 5b0b41e5daee0142c1bf59c9d71f6c19b25e6148dfbb9fb142107aabe3701e366\ + 11a7e0b13ea32d3c5f8a51f63c5f34415baa15f6ca77300eb323241ffe73c5acd\ + 97fcb682c21dc8911392979e9cb81be5218acf452b5b93f6681d323b7989fdd10\ + efe6fe9e2ac88d0d76a4cf3ee45e3b5c430100014142c1fc7e8a658eff437594a\ + 25cf34d269556d8511918f27fdc7e9d6dd73f0e4790b91f225e9d131e6abb3dbf\ + b66549a9aa57948fbd2f183fcd951b1d2305bffd6c0a602000000000000000016\ + f5cdf9fb6c1b5e98a36befdc2c55bd4fd8793d554b2506f51c909362495e1216e\ + e83cd270ddb0a00785600ba23bd3363f0798e3a7a117990415adec88e61be6517\ + 0bd587ab4d2ee38edb22a91e5c29afa397dd5a73465c51c6263f5fbde47fa801c\ + e84464acc32589acaafadfe44d6558774b7085612a88f3424b6dca3c6f07217d1\ + cbd5c41bda46a6a492a0119c1de4d25b58c94250bee3fba6b8223777535673a2f\ + 4da6af27598030f88144f408120f07ca9c98d5d9edcdf6cdc9073f118fce55e6c\ + 9d0be80b5e87992ddaa9c22053b3a00d42bdedc9768de25c0b37a5c4fb4e86710\ + b33cebed5588d88adde607f6bca14f0279ce35126d403ffa50f288c87f528c197\ + 49ed43bd846c513fcd92c173fe76d8f2e69770439d3d075cb19b1094a42ee07ae\ + 1de197e8c136e2bc688a75a74db24adb0fbb73872dc80074f61c9cce9bd33861b\ + dd921ee3edacab1d6e7cec325c172b6b6e82ada11687e4fc931225074dd1f20a0\ + f9342dbce1fc3fdbf5bb6cb74ab6475e574e9f5f247a2f7e4fcfcc354d4da8c80\ + 66e574642c7fccbbb9ef0aa592ecab5366fe87eb8e14cd64aee34578aa48f68f8\ + f4c5372df2c3fc429f5a3e39ef6c034c87f9c52b2ea35e28c7bf3be737c3817ef\ + d6569466dc859e8ff8965c5249b6f045934d3d08b0ffd388aec58df8194ac2c4f\ + ec2152942d2626595e65664b1fa33b5dae8ee796a840a56d885cbf7ae6483fad0\ + 5e507ada3f075ebce0d791b626c6dfe93f8492c4dd3b34aafc33d7644c5c8e38b\ + fd8c19194f65be88fcb4538778632e489a626896372fdd2498b16e64daa7d3c5c\ + fac688d6f9cdf3717261b0a1f25be1bdd6be6558ddb826fa04b5f668810a291ae\ + a51a6f05ff7c34dcf81c74849a8015bad5e4e416989b10ef01de304775db725fa\ + 0b665f4330dc9c540dc29aab144837362a97d6bb0165cb3272338c2d32386cd95\ + ee3e66d876b591a25a6907237523cf908f736d2fdc8e54ea8d9c7562697161d1f\ + 72fc4d7b775052415cd0e5ae5bdf6edfab5776b6ff75ce5e1f8f2beea6ec74252\ + b63966cca58abd638279dc5c998a1068079f3e5dcc8a69165c304c3d8c362ccfa\ + dab05ad12208a5655ab389eb727e8ed5f86b300331a13be26e2fbabf89fbfd2b9\ + 8481dd5edb52ed456a0e03a84b6f89761f91ff251412f5cfa286e35fb9f48ef0e\ + 044c4742b6e860a08767ecb80548c2f3df3b371cdb40e86dbe118f64e84faf45e\ + cb78d73364e9e31e3412ca2a3fad0a35983370ea9e6264a222edd1fd4aca30e3c\ + 169d7ca2d07609262e786ecd019c1417a06b7dfa32a54e0897afdc6492f266115\ + 55cbff47dba3b76381f239d597a8f687669333e0b47b53d5bcc4fea1919490bad\ + 3c6f0b6a58a50aca7ddeb9745ead454e0a38d9486fb52aefe0dbb92bf7fd6c215\ + 078aba3482b11274ec8cddff92c359bbc6d20bd823ad0bbf859cfaadf8e775b3d\ + 37b3078319f46c6d2a112cf60a673fee467538c70f1687d97fbe9d9f8a0856061\ + 592a4e00b6d10e979e674dd2cd0ba8b853f733877cd508062d5f723d58d215ad6\ + 9c2be6be742496aef54eb87338622eb36a9bbc5a7a602d280a45e095b1e078dab\ + 54479e783a513c722066acaae44ccc15f9560da91ed053ec05c36d82f68097668\ + 76c45c4fbeb2321d50f48f7995437d0c5fc365974a571fb0352d28cb1cdbd21d6\ + 9fab576a2e68d6b881776027bcdb7f01be22b1c847d91f26e680ef6ab2c128a89\ + b59432383d9bd661b0b01432cf8a25319426d38ac2e2114825f59b4250569c798\ + b1094920bb31130728313ff56a6eef2e6c4b275215dce3786d0f9024952b5f572\ + 566c53597e7ef4ab1f75743e605a564054d667f48906b5481d924769ef65751e3\ + 49891d725a2c1bf8b102fea4c25c874d2fc2ce1bfec4b39bea76fbf7a28855725\ + d52b595a4fc96892c3f1f961d46310ebd5221df729c02060035c559baf0fd7efa\ + 73a2213ca29642857aeb8ebf7efdf9d2f5c84746b6fc35ab355a8dca56e7dde48\ + 31e47ca1be6b62af30cfcf807c384e56ab84ff03bbe786251e6c4b932c9217bf6\ + 71046217bd0511fdc06aa69050c1480281e4843eb73d80095a2fb8e68a2c0c98c\ + 9aea637b99d87ad847a3a76d59ea308c751f9cb4a4fce2989822bd6ba2f901f09\ + df647536dc30730ea3160dd35b8c6dcc9aa815b79ed492a8a299a298ccdf784b9\ + b0211ca877ec1723817c98529acaa4d3727162b5740b0fc9b498dfb2212a3cbf0\ + c63dc4f7663fafad7905643a792862b651e8497b0f0da632b897ecf9ee63f2b20\ + b54fa5eb2f2e424dcce5a075f50b856af266655be3a815fc83ed8027508b25369\ + 76982196b160e2219ffdb5c7a56dd3e6b700860c711f4439dbf72973f4f26fe32\ + 60ec43a3446fe14444b9787d877e107be610147eec4a3574745e95a1f424aff06\ + 2f84c559d13b1e6b59e8dc2221515c229f07db8eb39c515a321d8bd07b1bd6c9a\ + 79dac6d951c04415553c7a2ce1eb77495c7f89c4d5b4cffd289435b69bc535850\ + 95083cc5a1b191781342266e204e1566aca8175e2ae84a8bd711d188b666dfb65\ + a6442776d3e23c1b5192af09ec712537f2157d0ccbc1bb3b3a1969d9705671f16\ + bdc266e615ad2e50a8cbd666f3ee7465cc430c6cd69d30c91e717b12f7094b6f0\ + ef89134d6c1620d28d8f238c181146448b348e4ca2e93c737210350f18fb878fb\ + 91b70ecc5689e5b6101ecfc545f6a1c903115b0c6419c91a50fb2dbe2edd362f2\ + 815f0c75070974507c34130ac9b29747ff7efbe6e37ee4c62be3ecfedfa817fdf\ + 3309163aaff677775b77f0d288c9858cfe59cb0fa18afa591e7d574eaef43c82e\ + 79d71542c4177de4e5bd724b18cfd33c68530665728a9d5ef192772094acbf3d8\ + 85d5146c1634e74754e3fbcb94fa349eac8280cfd7d1f46a0813b57a83bd078b1\ + f7cb5a60a59b59380fe04e1c600c33b33d1add69a9ff1be546f0ec5c0083979fc\ + e940b23711f382ac0d011c1103f02cb6082c18e39cf7a9c3bf4c081f905ae7b87\ + 951a7880b57e934465ccd634e5a17fd8d8866abfdfebd33b2c3d2c5be58144900\ + c04e9c18de0c80270660e62a3c185277555f89da4c41bd33cec1359f4ed21abdb\ + 586e1d97f720a92d16014d7f1822f1836f74c97cb7f7b38e073477c6ab064fde8\ + 35916c1e624de81f2ad90f6260073c5e1848582860f033630bde225821b39c257\ + 2b30c36adf8fdb8317c33df05f6413447f4985d12e9012629df09dc8f43373a6d\ + 0db4b0048453a6f1ec662472c77a30d5cf4ac7084f736d0d598c251f2aefc9860\ + 52fbf12a657885d7140ad36b07c63ab86388a2be12d943747f3f29ef9f2e11e14\ + 44cc873df0ed7826eef675389a0d5a0388a8504fe89c4791ea4a572bfd406d5f0\ + 1418b4f888c9a7a566e32811936bf6950bbf786b86c41c28f2045d31953fcd15f\ + 179e7bc00c72870890537921f7deff82270b0e44b88720aa738f60a85567deb7c\ + 90b0c2444467621e53e1c079436d31d3d0b34dd237fc281eb9d87175237a9a433\ + 142db4bb7f8c4cb6a34e2dc73f074045d216695ce88ef68e18564c935c9cbd902\ + e939655c258de2ab78def8746bffd972083afce3b6881b7147262e1a44e022468\ + 9fafa1a3cb823c8da6eb7df091bec0638bf728b7b10aa95f2bce512ec8d325293\ + 8d2eb77b44ace7a2f976588032cac5af670f9e5ca25cb0721bc1baec26f9c3a9f\ + 41b02fb62997d6cb0a01314845e9d0e78139ea49f2ead8736e0000" + ); + + assert_eq!( + block.bitcoin_hash().to_string(), + "e935d06cf3a616eb4d551338598b84daa0e8592ed14673263597f6af4b4a6ea6" + ); + assert_eq!(block.header.version, 0x20000000); + assert_eq!(block.header.height, 1); + assert_eq!(block.tx.len(), 3); + } +} + diff --git a/src/confidential.rs b/src/confidential.rs new file mode 100644 index 00000000..95f4f843 --- /dev/null +++ b/src/confidential.rs @@ -0,0 +1,236 @@ +// Rust Elements Library +// Written in 2018 by +// Andrew Poelstra +// +// 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 CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Confidential Commitments +//! +//! Structures representing Pedersen commitments of various types +//! + +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use std::fmt; + +use bitcoin::network::encodable::{ConsensusEncodable, ConsensusDecodable}; +use bitcoin::network::serialize::{self, SimpleEncoder, SimpleDecoder}; +use bitcoin::util::hash::Sha256dHash; + +// Helper macro to implement various things for the various confidential +// commitment types +macro_rules! impl_confidential_commitment { + ($name:ident, $prefixA:expr, $prefixB:expr) => ( + impl_confidential_commitment!($name, $prefixA, $prefixB, |x|x); + ); + ($name:ident, $prefixA:expr, $prefixB:expr, $explicit_fn:expr) => ( + impl Default for $name { + fn default() -> Self { + $name::Null + } + } + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + $name::Null => f.write_str("null"), + $name::Explicit(n) => write!(f, "{}", n), + $name::Confidential(prefix, bytes) => { + write!(f, "{:02x}", prefix)?; + for b in bytes.iter() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } + } + } + } + + impl ConsensusEncodable for $name { + fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> { + match *self { + $name::Null => 0u8.consensus_encode(s), + $name::Explicit(n) => { + 1u8.consensus_encode(s)?; + // Apply $explicit_fn to allow `Value` to swap the amount bytes + $explicit_fn(n).consensus_encode(s) + } + $name::Confidential(prefix, bytes) => { + prefix.consensus_encode(s)?; + bytes.consensus_encode(s) + } + } + } + } + + impl ConsensusDecodable for $name { + fn consensus_decode(d: &mut D) -> Result<$name, serialize::Error> { + let prefix = u8::consensus_decode(d)?; + match prefix { + 0 => Ok($name::Null), + 1 => { + // Apply $explicit_fn to allow `Value` to swap the amount bytes + let explicit = $explicit_fn(ConsensusDecodable::consensus_decode(d)?); + Ok($name::Explicit(explicit)) + } + x => { + let commitment = <[u8; 32]>::consensus_decode(d)?; + Ok($name::Confidential(x, commitment)) + } + } + } + } + + #[cfg(feature = "serde")] + impl Serialize for $name { + fn serialize(&self, s: S) -> Result { + use serde::ser::SerializeSeq; + + let seq_len = if *self == $name::Null { 1 } else { 2 }; + let mut seq = s.serialize_seq(Some(seq_len))?; + + match *self { + $name::Null => seq.serialize_element(&0u8)?, + $name::Explicit(n) => { + seq.serialize_element(&1u8)?; + // Apply $explicit_fn to allow `Value` to swap the amount bytes + seq.serialize_element(&$explicit_fn(n))?; + } + $name::Confidential(prefix, bytes) => { + seq.serialize_element(&prefix)?; + seq.serialize_element(&bytes)?; + } + } + seq.end() + } + } + + #[cfg(feature = "serde")] + impl<'de> Deserialize<'de> for $name { + fn deserialize>(d: D) -> Result { + use serde::de::{Error, Visitor, SeqAccess}; + struct CommitVisitor; + + impl <'de> Visitor<'de> for CommitVisitor { + type Value = $name; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a committed value") + } + + fn visit_seq>(self, mut access: A) -> Result { + let prefix: u8 = if let Some(x) = access.next_element()? { + x + } else { + return Err(A::Error::custom("missing prefix")); + }; + + match prefix { + 0 => Ok($name::Null), + 1 => { + // Apply $explicit_fn to allow `Value` to swap the amount bytes + match access.next_element()? { + Some(x) => Ok($name::Explicit($explicit_fn(x))), + None => Err(A::Error::custom("missing commitment")), + } + } + x => { + match access.next_element()? { + Some(y) => Ok($name::Confidential(x, y)), + None => Err(A::Error::custom("missing commitment")), + } + } + } + } + } + + d.deserialize_seq(CommitVisitor) + } + } + ); +} + +/// A CT commitment to an amount +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Value { + /// No value + Null, + /// Value is explicitly encoded + Explicit(u64), + // Split commitments into a 1-byte prefix and 32-byte commitment, because + // they're easy enough to separate and Rust stdlib treats 32-byte arrays + // much much better than 33-byte arrays. + /// Value is committed + Confidential(u8, [u8; 32]), +} +impl_confidential_commitment!(Value, 0x08, 0x09, u64::swap_bytes); + +impl Value { + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Value::Null => 1, + Value::Explicit(..) => 9, + Value::Confidential(..) => 33, + } + } +} + +/// A CT commitment to an asset +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Asset { + /// No value + Null, + /// Asset entropy is explicitly encoded + Explicit(Sha256dHash), + /// Asset is committed + Confidential(u8, [u8; 32]), +} +impl_confidential_commitment!(Asset, 0x0a, 0x0b); + +impl Asset { + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Asset::Null => 1, + Asset::Explicit(..) => 33, + Asset::Confidential(..) => 33, + } + } +} + + +/// A CT commitment to an output nonce (i.e. a public key) +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Nonce { + /// No value + Null, + /// There should be no such thing as an "explicit nonce", but Elements will deserialize + /// such a thing (and insists that its size be 32 bytes). So we stick a 32-byte type here + /// that implements all the traits we need. + Explicit(Sha256dHash), + /// Nonce is committed + Confidential(u8, [u8; 32]), +} +impl_confidential_commitment!(Nonce, 0x02, 0x03); + +impl Nonce { + /// Serialized length, in bytes + pub fn encoded_length(&self) -> usize { + match *self { + Nonce::Null => 1, + Nonce::Explicit(..) => 33, + Nonce::Confidential(..) => 33, + } + } +} + diff --git a/src/internal_macros.rs b/src/internal_macros.rs new file mode 100644 index 00000000..ee024037 --- /dev/null +++ b/src/internal_macros.rs @@ -0,0 +1,196 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// 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 CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +macro_rules! impl_consensus_encoding { + ($thing:ident, $($field:ident),+) => ( + impl $crate::bitcoin::network::encodable::ConsensusEncodable for $thing { + #[inline] + fn consensus_encode(&self, s: &mut S) -> Result<(), $crate::bitcoin::network::serialize::Error> { + $( self.$field.consensus_encode(s)?; )+ + Ok(()) + } + } + + impl $crate::bitcoin::network::encodable::ConsensusDecodable for $thing { + #[inline] + fn consensus_decode(d: &mut D) -> Result<$thing, $crate::bitcoin::network::serialize::Error> { + use $crate::bitcoin::network::encodable::ConsensusDecodable; + Ok($thing { + $( $field: ConsensusDecodable::consensus_decode(d)?, )+ + }) + } + } + ); +} + +macro_rules! serde_struct_impl { + ($name:ident, $($fe:ident),*) => ( + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result<$name, D::Error> + where + D: $crate::serde::de::Deserializer<'de>, + { + use $crate::std::fmt::{self, Formatter}; + use $crate::serde::de::IgnoredAny; + + #[allow(non_camel_case_types)] + enum Enum { Unknown__Field, $($fe),* } + + struct EnumVisitor; + impl<'de> $crate::serde::de::Visitor<'de> for EnumVisitor { + type Value = Enum; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a field name") + } + + fn visit_str(self, v: &str) -> Result + where + E: $crate::serde::de::Error, + { + match v { + $( + stringify!($fe) => Ok(Enum::$fe) + ),*, + _ => Ok(Enum::Unknown__Field) + } + } + } + + impl<'de> $crate::serde::Deserialize<'de> for Enum { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::de::Deserializer<'de>, + { + deserializer.deserialize_str(EnumVisitor) + } + } + + struct Visitor; + + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a struct") + } + + fn visit_map(self, mut map: A) -> Result + where + A: $crate::serde::de::MapAccess<'de>, + { + use $crate::serde::de::Error; + + $(let mut $fe = None;)* + + loop { + match map.next_key::()? { + Some(Enum::Unknown__Field) => { + map.next_value::()?; + } + $( + Some(Enum::$fe) => { + $fe = Some(map.next_value()?); + } + )* + None => { break; } + } + } + + $( + let $fe = match $fe { + Some(x) => x, + None => return Err(A::Error::missing_field(stringify!($fe))), + }; + )* + + let ret = $name { + $($fe: $fe),* + }; + + Ok(ret) + } + } + // end type defs + + static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; + + deserializer.deserialize_struct(stringify!($name), FIELDS, Visitor) + } + } + + #[cfg(feature = "serde")] + impl $crate::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer, + { + use $crate::serde::ser::SerializeStruct; + + // Only used to get the struct length. + static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; + + let mut st = serializer.serialize_struct(stringify!($name), FIELDS.len())?; + + $( + st.serialize_field(stringify!($fe), &self.$fe)?; + )* + + st.end() + } + } + ) +} + +#[cfg(test)] +macro_rules! hex_deserialize( + ($e:expr) => ({ + use ::bitcoin::network::serialize::deserialize; + + fn hex_char(c: char) -> u8 { + match c { + '0' => 0, + '1' => 1, + '2' => 2, + '3' => 3, + '4' => 4, + '5' => 5, + '6' => 6, + '7' => 7, + '8' => 8, + '9' => 9, + 'a' | 'A' => 10, + 'b' | 'B' => 11, + 'c' | 'C' => 12, + 'd' | 'D' => 13, + 'e' | 'E' => 14, + 'f' | 'F' => 15, + x => panic!("Invalid character {} in hex string", x), + } + } + + let mut ret = Vec::with_capacity($e.len() / 2); + let mut byte = 0; + for (ch, store) in $e.chars().zip([false, true].iter().cycle()) { + byte = (byte << 4) + hex_char(ch); + if *store { + ret.push(byte); + byte = 0; + } + } + deserialize(&ret).expect("deserialize object") + }); +); + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..ce735570 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,39 @@ +// Rust Elements Library +// Written in 2018 by +// Andrew Poelstra +// +// 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 CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Rust Elements Library +//! +//! Extensions to `rust-bitcoin` to support deserialization and serialization +//! of Elements transactions and blocks. +//! + +// Coding conventions +#![deny(non_upper_case_globals)] +#![deny(non_camel_case_types)] +#![deny(non_snake_case)] +#![deny(unused_mut)] +#![deny(missing_docs)] + +extern crate bitcoin; +#[cfg(feature = "serde")] extern crate serde; + +#[macro_use] mod internal_macros; +mod block; +pub mod confidential; +mod transaction; + +// export everything at the top level so it can be used as `elements::Transaction` etc. +pub use transaction::{OutPoint, TxIn, TxOut, TxInWitness, TxOutWitness, Transaction}; +pub use block::{BlockHeader, Block, Proof}; + diff --git a/src/transaction.rs b/src/transaction.rs new file mode 100644 index 00000000..a1437682 --- /dev/null +++ b/src/transaction.rs @@ -0,0 +1,791 @@ +// Rust Elements Library +// Written in 2018 by +// Andrew Poelstra +// +// 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 CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Transactions +//! + +use std::fmt; + +use bitcoin::network::encodable::{ConsensusEncodable, ConsensusDecodable, VarInt}; +use bitcoin::network::serialize::{self, BitcoinHash, SimpleEncoder, SimpleDecoder}; +use bitcoin::blockdata::opcodes; +use bitcoin::blockdata::script::{Script, Instruction}; +use bitcoin::util::hash::Sha256dHash; + +use confidential; + +/// Description of an asset issuance in a transaction input +#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct AssetIssuance { + /// Zero for a new asset issuance; otherwise a blinding factor for the input + asset_blinding_nonce: [u8; 32], + /// Freeform entropy field + asset_entropy: [u8; 32], + /// Amount of asset to issue + amount: confidential::Value, + /// Amount of inflation keys to issue + inflation_keys: confidential::Value, +} +serde_struct_impl!(AssetIssuance, asset_blinding_nonce, asset_entropy, amount, inflation_keys); +impl_consensus_encoding!(AssetIssuance, asset_blinding_nonce, asset_entropy, amount, inflation_keys); + +/// A reference to a transaction output +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct OutPoint { + /// The referenced transaction's txid + pub txid: Sha256dHash, + /// The index of the referenced output in its transaction's vout + pub vout: u32, + /// Flag indicating that this outpoint refers to something on the main chain + pub is_pegin: bool, + /// Flag indicating that this outpoint has an asset issuance attached + pub has_issuance: bool, +} +serde_struct_impl!(OutPoint, txid, vout, is_pegin, has_issuance); + +impl Default for OutPoint { + /// Coinbase outpoint + fn default() -> OutPoint { + OutPoint { + txid: Sha256dHash::default(), + vout: 0xffffffff, + is_pegin: false, + has_issuance: false, + } + } +} + +impl ConsensusEncodable for OutPoint { + fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> { + let mut vout = self.vout; + if self.is_pegin { + vout |= 1 << 30; + } + if self.has_issuance { + vout |= 1 << 31; + } + self.txid.consensus_encode(s)?; + vout.consensus_encode(s) + } +} + +impl ConsensusDecodable for OutPoint { + fn consensus_decode(d: &mut D) -> Result { + let txid = Sha256dHash::consensus_decode(d)?; + let mut vout = u32::consensus_decode(d)?; + let is_pegin; + let has_issuance; + + // Pegin/issuance flags are encoded into the high bits of `vout`, *except* + // if vout is all 1's; this indicates a coinbase transaction + if vout == 0xffffffff { + is_pegin = false; + has_issuance = false; + } else { + is_pegin = vout & (1 << 30) != 0; + has_issuance = vout & (1 << 31) != 0; + vout &= !(3 << 30); + } + + Ok(OutPoint { + txid: txid, + vout: vout, + is_pegin: is_pegin, + has_issuance: has_issuance, + }) + } +} + +impl fmt::Display for OutPoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match (self.is_pegin, self.has_issuance) { + (false, false) => f.write_str("[ ]")?, + (false, true) => f.write_str("[ I]")?, + (true, false) => f.write_str("[P ]")?, + (true, true) => f.write_str("[PI]")?, + } + write!(f, "{}:{}", self.txid, self.vout) + } +} + +/// Transaction input witness +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +pub struct TxInWitness { + /// Amount rangeproof + amount_rangeproof: Vec, + /// Rangeproof for inflation keys + inflation_keys_rangeproof: Vec, + /// Traditional script witness + script_witness: Vec>, + /// Pegin witness, basically the same thing + pegin_witness: Vec>, +} +serde_struct_impl!(TxInWitness, amount_rangeproof, inflation_keys_rangeproof, script_witness, pegin_witness); +impl_consensus_encoding!(TxInWitness, amount_rangeproof, inflation_keys_rangeproof, script_witness, pegin_witness); + +impl TxInWitness { + /// Whether this witness is null + pub fn is_empty(&self) -> bool { + self.amount_rangeproof.is_empty() && + self.inflation_keys_rangeproof.is_empty() && + self.script_witness.is_empty() && + self.pegin_witness.is_empty() + } +} + +/// A transaction input, which defines old coins to be consumed +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct TxIn { + /// The reference to the previous output that is being used an an input + pub previous_output: OutPoint, + /// The script which pushes values on the stack which will cause + /// the referenced output's script to accept + pub script_sig: Script, + /// The sequence number, which suggests to miners which of two + /// conflicting transactions should be preferred, or 0xFFFFFFFF + /// to ignore this feature. This is generally never used since + /// the miner behaviour cannot be enforced. + pub sequence: u32, + /// Asset issuance data + pub asset_issuance: AssetIssuance, + /// Witness data - not deserialized/serialized as part of a `TxIn` object + /// (rather as part of its containing transaction, if any) but is logically + /// part of the txin. + pub witness: TxInWitness, +} +serde_struct_impl!(TxIn, previous_output, script_sig, sequence, asset_issuance, witness); + +impl ConsensusEncodable for TxIn { + fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> { + self.previous_output.consensus_encode(s)?; + self.script_sig.consensus_encode(s)?; + self.sequence.consensus_encode(s)?; + if self.has_issuance() { + self.asset_issuance.consensus_encode(s)?; + } + Ok(()) + } +} + +impl ConsensusDecodable for TxIn { + fn consensus_decode(d: &mut D) -> Result { + let outp = OutPoint::consensus_decode(d)?; + let script_sig = Script::consensus_decode(d)?; + let sequence = u32::consensus_decode(d)?; + let issuance; + if outp.has_issuance { + issuance = AssetIssuance::consensus_decode(d)?; + } else { + issuance = AssetIssuance::default(); + } + Ok(TxIn { + previous_output: outp, + script_sig: script_sig, + sequence: sequence, + asset_issuance: issuance, + witness: TxInWitness::default(), + }) + } +} + + +impl TxIn { + /// Whether the input is a coinbase + pub fn is_coinbase(&self) -> bool { + self.previous_output == OutPoint::default() + } + + /// Whether the input is a pegin + pub fn is_pegin(&self) -> bool { + self.previous_output.is_pegin + } + + /// Helper to determine whether an input has an asset issuance attached + pub fn has_issuance(&self) -> bool { + self.previous_output.has_issuance + } +} + +/// Transaction output witness +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +pub struct TxOutWitness { + /// Surjection proof showing that the asset commitment is legitimate + surjection_proof: Vec, + /// Rangeproof showing that the value commitment is legitimate + rangeproof: Vec, +} +serde_struct_impl!(TxOutWitness, surjection_proof, rangeproof); +impl_consensus_encoding!(TxOutWitness, surjection_proof, rangeproof); + +impl TxOutWitness { + /// Whether this witness is null + pub fn is_empty(&self) -> bool { + self.surjection_proof.is_empty() && self.rangeproof.is_empty() + } +} + +/// Transaction output +#[derive(Clone, Default, PartialEq, Eq, Debug, Hash)] +pub struct TxOut { + /// Committed asset + pub asset: confidential::Asset, + /// Committed amount + pub value: confidential::Value, + /// Nonce (ECDH key passed to recipient) + pub nonce: confidential::Nonce, + /// Scriptpubkey + pub script_pubkey: Script, + /// Witness data - not deserialized/serialized as part of a `TxIn` object + /// (rather as part of its containing transaction, if any) but is logically + /// part of the txin. + pub witness: TxOutWitness, +} +serde_struct_impl!(TxOut, asset, value, nonce, script_pubkey, witness); + +impl ConsensusEncodable for TxOut { + fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> { + self.asset.consensus_encode(s)?; + self.value.consensus_encode(s)?; + self.nonce.consensus_encode(s)?; + self.script_pubkey.consensus_encode(s) + } +} + +impl ConsensusDecodable for TxOut { + fn consensus_decode(d: &mut D) -> Result { + Ok(TxOut { + asset: ConsensusDecodable::consensus_decode(d)?, + value: ConsensusDecodable::consensus_decode(d)?, + nonce: ConsensusDecodable::consensus_decode(d)?, + script_pubkey: ConsensusDecodable::consensus_decode(d)?, + witness: TxOutWitness::default(), + }) + } +} + +impl TxOut { + /// Whether this data represents nulldata (OP_RETURN followed by pushes) + pub fn is_null_data(&self) -> bool { + let mut iter = self.script_pubkey.iter(false); + if iter.next() == Some(Instruction::Op(opcodes::All::OP_RETURN)) { + for push in iter { + match push { + Instruction::Op(op) if op as u8 > opcodes::All::OP_PUSHNUM_16 as u8 => {} + Instruction::PushBytes(..) => {}, + _ => return false + } + } + true + } else { + false + } + } + + /// Whether or not this output is a fee output + pub fn is_fee(&self) -> bool { + self.script_pubkey.is_empty() + } + + /// Extracts the minimum value from the rangeproof, if there is one, or returns 0. + pub fn minimum_value(&self) -> u64 { + let min_value = if self.script_pubkey.is_op_return() { 0 } else { 1 }; + + match self.value { + confidential::Value::Null => min_value, + confidential::Value::Explicit(n) => n, + confidential::Value::Confidential(..) => { + if self.witness.rangeproof.is_empty() { + min_value + } else { + debug_assert!(self.witness.rangeproof.len() > 10); + + let has_nonzero_range = self.witness.rangeproof[0] & 64 == 64; + let has_min = self.witness.rangeproof[0] & 32 == 32; + + if !has_min { + min_value + } else if has_nonzero_range { + serialize::deserialize::(&self.witness.rangeproof[2..10]) + .expect("any 8 bytes is a u64") + .swap_bytes() // min-value is BE + } else { + serialize::deserialize::(&self.witness.rangeproof[1..9]) + .expect("any 8 bytes is a u64") + .swap_bytes() // min-value is BE + } + } + } + } + } +} + +/// Elements transaction +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +pub struct Transaction { + /// Transaction version field (should always be 2) + pub version: u32, + /// Transaction locktime + pub lock_time: u32, + /// Vector of inputs + pub input: Vec, + /// Vector of outputs + pub output: Vec, +} +serde_struct_impl!(Transaction, version, lock_time, input, output); + +impl Transaction { + /// Whether the transaction is a coinbase tx + pub fn is_coinbase(&self) -> bool { + self.input.len() == 1 && self.input[0].is_coinbase() + } + + /// Determines whether a transaction has any non-null witnesses + pub fn has_witness(&self) -> bool { + self.input.iter().any(|i| !i.witness.is_empty()) || + self.output.iter().any(|o| !o.witness.is_empty()) + } + + /// Get the "weight" of this transaction; roughly equivalent to BIP141, in that witness data is + /// counted as 1 while non-witness data is counted as 4. + pub fn get_weight(&self) -> usize { + let witness_flag = self.has_witness(); + + let input_weight = self.input.iter().map(|input| { + 4 * ( + 32 + 4 + 4 + // output + nSequence + VarInt(input.script_sig.len() as u64).encoded_length() as usize + + input.script_sig.len() + if input.has_issuance() { + 64 + + input.asset_issuance.amount.encoded_length() + + input.asset_issuance.inflation_keys.encoded_length() + } else { + 0 + } + ) + if witness_flag { + VarInt(input.witness.amount_rangeproof.len() as u64).encoded_length() as usize + + input.witness.amount_rangeproof.len() + + VarInt(input.witness.inflation_keys_rangeproof.len() as u64).encoded_length() as usize + + input.witness.inflation_keys_rangeproof.len() + + VarInt(input.witness.script_witness.len() as u64).encoded_length() as usize + + input.witness.script_witness.iter().map(|wit| + VarInt(wit.len() as u64).encoded_length() as usize + + wit.len() + ).sum::() + + VarInt(input.witness.pegin_witness.len() as u64).encoded_length() as usize + + input.witness.pegin_witness.iter().map(|wit| + VarInt(wit.len() as u64).encoded_length() as usize + + wit.len() + ).sum::() + } else { + 0 + } + }).sum::(); + + let output_weight = self.output.iter().map(|output| { + 4 * ( + output.asset.encoded_length() + + output.value.encoded_length() + + output.nonce.encoded_length() + + VarInt(output.script_pubkey.len() as u64).encoded_length() as usize + + output.script_pubkey.len() + ) + if witness_flag { + VarInt(output.witness.surjection_proof.len() as u64).encoded_length() as usize + + output.witness.surjection_proof.len() + + VarInt(output.witness.rangeproof.len() as u64).encoded_length() as usize + + output.witness.rangeproof.len() + } else { + 0 + } + }).sum::(); + + 4 * ( + 4 + // version + 4 + // locktime + VarInt(self.input.len() as u64).encoded_length() as usize + + VarInt(self.output.len() as u64).encoded_length() as usize + + 1 // segwit flag byte (note this is *not* witness data in Elements) + ) + input_weight + output_weight + } + + /// The txid of the transaction. To get its hash, use `BitcoinHash::bitcoin_hash()`. + pub fn txid(&self) -> Sha256dHash { + use bitcoin::util::hash::Sha256dEncoder; + + let mut enc = Sha256dEncoder::new(); + self.version.consensus_encode(&mut enc).unwrap(); + 0u8.consensus_encode(&mut enc).unwrap(); + self.input.consensus_encode(&mut enc).unwrap(); + self.output.consensus_encode(&mut enc).unwrap(); + self.lock_time.consensus_encode(&mut enc).unwrap(); + enc.into_hash() + } +} + +impl BitcoinHash for Transaction { + /// To get a transaction's txid, which is usually what you want, use the `txid` method. + fn bitcoin_hash(&self) -> Sha256dHash { + use bitcoin::util::hash::Sha256dEncoder; + + let mut enc = Sha256dEncoder::new(); + self.consensus_encode(&mut enc).unwrap(); + enc.into_hash() + } +} + +impl ConsensusEncodable for Transaction { + fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> { + self.version.consensus_encode(s)?; + + let wit_flag = self.has_witness(); + if wit_flag { + 1u8.consensus_encode(s)?; + } else { + 0u8.consensus_encode(s)?; + } + self.input.consensus_encode(s)?; + self.output.consensus_encode(s)?; + self.lock_time.consensus_encode(s)?; + + if wit_flag { + for i in &self.input { + i.witness.consensus_encode(s)?; + } + for o in &self.output { + o.witness.consensus_encode(s)?; + } + } + Ok(()) + } +} + +impl ConsensusDecodable for Transaction { + fn consensus_decode(d: &mut D) -> Result { + let version = u32::consensus_decode(d)?; + let wit_flag = u8::consensus_decode(d)?; + let mut input = Vec::::consensus_decode(d)?; + let mut output = Vec::::consensus_decode(d)?; + let lock_time = u32::consensus_decode(d)?; + + match wit_flag { + 0 => Ok(Transaction { + version: version, + input: input, + output: output, + lock_time: lock_time, + }), + 1 => { + for i in &mut input { + i.witness = ConsensusDecodable::consensus_decode(d)?; + } + for o in &mut output { + o.witness = ConsensusDecodable::consensus_decode(d)?; + } + if input.iter().all(|input| input.witness.is_empty()) && + output.iter().all(|output| output.witness.is_empty()) { + Err(serialize::Error::ParseFailed("witness flag set but no witnesses were given")) + } else { + Ok(Transaction { + version: version, + input: input, + output: output, + lock_time: lock_time, + }) + } + } + _ => Err(serialize::Error::ParseFailed("bad witness flag in tx")), + } + } +} + +#[cfg(test)] +mod tests { + use bitcoin::network::serialize::BitcoinHash; + + use Transaction; + use confidential; + + #[test] + fn transaction() { + // Simple transaction with explicit input (no scriptsig/witness) and explicit outputs + let tx: Transaction = hex_deserialize!( + "020000000001eb04b68e9a26d116046c76e8ff47332fb71dda90ff4bef5370f2\ + 5226d3bc09fc0000000000feffffff0201230f4f5d4b7c6fa845806ee4f67713\ + 459e1b69e8e60fcee2e4940c7a0d5de1b20100000002540bd71c001976a91448\ + 633e2c0ee9495dd3f9c43732c47f4702a362c888ac01230f4f5d4b7c6fa84580\ + 6ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000ce400\ + 0000000000" + ); + + assert_eq!( + tx.bitcoin_hash().to_string(), + "758f784bdfa89b62c8b882542afb46074d3851a6da997199bcfb7cc6daed3cf2" + ); + assert_eq!( + tx.txid().to_string(), + "758f784bdfa89b62c8b882542afb46074d3851a6da997199bcfb7cc6daed3cf2" + ); + assert_eq!(tx.input.len(), 1); + assert_eq!(tx.output.len(), 2); + assert_eq!(tx.output[0].is_fee(), false); + assert_eq!(tx.output[1].is_fee(), true); + assert_eq!(tx.output[0].value, confidential::Value::Explicit(9999996700)); + assert_eq!(tx.output[1].value, confidential::Value::Explicit( 3300)); + assert_eq!(tx.output[0].minimum_value(), 9999996700); + assert_eq!(tx.output[1].minimum_value(), 3300); + + // CT transaction with explicit input (with script witness) and confidential outputs + let tx: Transaction = hex_deserialize!( + "020000000101f23ceddac67cfbbc997199daa651384d0746fb2a5482b8c8629b\ + a8df4b788f75000000006b483045022100e0feb3e2f292000d67e24b821d87c9\ + 532230dac1de428d6a0068c9f416583abf02200e76f072788dd411b2327267cd\ + 91c6b1659809598cd4fae35be475efe1e4bbad01210201e15c23c021652d07c1\ + 557b607ea0379fca0462aca840d6c33c4d4927524547feffffff030b60424a42\ + 3335923c15ae387d95d4f80d944722020bfa55b9f0a0e67579e3c13c081c4f21\ + 5239c77456d121eb73bd9914a9a6398fe369b4eb8f88a5f78e257fcaa303301e\ + e46349950886ae115c9556607fcda9381c2f72368f4b5286488c62aa0b081976\ + a9148bb6c4d5814d43fefb9e330575e326632136389c88ac0bd436b0539f5497\ + af792d7cb281f09b73d8a5abc198b3ce6239d79e68893e5e5d0923899fd35071\ + ba8a209d85b556d5747b6c35539c3b2f8631a27c0d477a1f45a603d1d350b8cb\ + f900f7666da66541bf6252fc4c162141ad49c670884c93c57db6ba1976a9148c\ + 7ab6e0fca387d03643d4846f708bf39d47c1e988ac01230f4f5d4b7c6fa84580\ + 6ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000008e8000\ + 00000000000000000043010001dc65ae13f76fde4a7172e0fb380b1a5cc8dc88\ + eaa0659e638a25eac8ae30d79bf93eb7e487eeee323e4ac8e3a2fe6523bdeba6\ + acce32b9b085f2286174c04655fd6c0a6020000000000000000178ad016b3e5d\ + 8165423e56d8b37e3eaee96009b2f970043ccf65d61b5c3c1e1ef343e0c479bd\ + ba442717dc861c9591566010240b9d4607efb9252a5fcef05edf640e0bb6b606\ + 729246ad07baa49d0d3b52042c65a03ca737744e45b2d2d6d177c36569ae9d6e\ + b4437305b169bbc59f85cabff3bc49a2d6d08c177cce3121a509d3c47961bd22\ + e35c932b79d4ec5ccaf913fac04034bfebdadbc4ff3127af96344b02ee6b967b\ + b08326cbe6a4e1c924485e64a8c0fdf70b98c99f38acaa15aa0adb2b5b7335ed\ + 5502443891bcd657310347cbd928f40f38f1dec087a2b947c9cf7d304798f77b\ + bc4a2c843796b2d49acce91de4e88a0a9c261277df28ffc3320d7f7d64790f59\ + 2ddded48a1068ef88271395fa5606389ef90856ddd6bd6710a8d27e0147983b5\ + dde2a7efae44e83ad02a3c3da04be43d5f2c05c205f1e17b48554c2177670f46\ + dbb6600bd2e6c75dd5ea2e1072c5f22483dcf05d8124e3f9063a5ddb179a29c2\ + 3a2d15d6e89f2192f03dae5938f66fcdcff000c5a96ffd2920f23881880af721\ + 53c96a56dd80c218bb48b44a18e54a8050ff32c869c1264ee574cdb4002f86e0\ + 779c724d11dc4a768dbec1bd22054886f1fdf2e7347e4c247b829159d1375f88\ + 1c6ce0a5c4da8534000e7fec3a980afb1edc99b725c29de80f260dcf144c873b\ + f589ae1812ef6cb05f2234f9c66c23e874a0d5d0dc52f2209e015bbcf74ee449\ + a397f6b0318c915b7e58dea5904abbe35285e90ccf548ad1f3f52f60c3b19b3c\ + d67644d633e68aef42d8ef1782f22a8edd0620f55f29070720ca7a078ac83e87\ + b9ebd2783ecad17dd854ef1bbd319f1a6d3a1e4931f9097422f5a3c4af037b99\ + e06c7610ee61102c6eea763af108e9a16b93b2dc0891658d5c6a197df6aae9b3\ + 06b2c895d21c79cb6cb6dd85b4018b0a9fe7468336e3907eb4adcaf930cacc97\ + e8e951d2d6b25744a4143679bad1f31b210c9a2ed54b80d8f5d7dc1f1c985681\ + 534c1926920cd683d95dca7e8ea285f9906d2e89cd8bfa76a98e38ee4b515252\ + 2d55f79610fe8d5278fe6ed5866b5da4dcf330ea84307c34f30e1a66eb1934da\ + febb0074fc27c2ff73d8c0bae8416cc87bf611f81119aba9e2a911beaf3ac950\ + 7e621fc1ed1cf15dfb31408cf55e2bfdd2880db2d3489a336d6f8348347648d8\ + 82f9f376331e469e809115c6cc82468f363c910673e9ded172ded90a369e1cdd\ + 135676f623e11a1531ed221177812b1ef0c65e5ca92c0df8de7fe664710f3228\ + a226e019c99607fe1395ecd5643e1c7ad8a132bf5131737cb970a7f0dabc0002\ + 9755bf71b3f47bd69ba39b3ab104c74f04239f4919dca1dfce7c9c41cba9d449\ + 073e106ebabe3c313b598ee8b11702ec46e9ee53fb9422f0326371898b8fa4c2\ + 1a951684c687398e0bebd6f6fd91b829e8666b9a19a4273cfda0f34b8ecb902f\ + 7adc6539fb9a0cba6f87a63a957acfb2dfa18973f4a3063668767b2be7803115\ + 13c63f1814f082176f6a953f2ffaa49ec9b39fecc2eab603be7a969bb4c1dbeb\ + f8d39fa90f802d5ea52378b5025a19b64a8c2c2dd6a6133bd8d29730bd5724b5\ + bf50c158b238d1137082937ad91a176aaf91577868db7581b457c917e612b242\ + ce0065ad47e11dcdc1fc6158687142249bcf312497a547b6f43e795af7d4ae8c\ + d022e44e417987e35e83de21e39dcdf86b97bd421e6e61881a432fa2284f20be\ + 80e32459443736b875d9036468ceb881589394441e2d10aa10b6c93332951e8b\ + a56f89fac70baf415b4511873c0f3e418ca4fe8954a28f1f7b5f590d34470119\ + f694e2712f184882d90396c8e6aa850eaa3c2ae51990543638c46c59512167a2\ + c5ad593532dc2142ffb6560476e4159213b9ef017ec75310d2e4624a405bb26f\ + 7192a485a94890674928c9caa4a5819ca4ddcba8fa71afc1a6baf63f039452c8\ + fe994f8b63d58c876dfddd61a476345eaed4f66bdc0fcfc38d485c6a5b0e27d0\ + fbc50427ff591ba38d63445c01642cfbd7d4c032f2546a6fe80bc3b598362502\ + c552049523fe360c3bcf1cc572feb04386f97d55871dd8cea0393cdd964e7240\ + 82adc98126e6f2fe1d576be4bf911e9aca70e35538175f8382bbcd614bbecc97\ + c9607ef25da2ff08a6e5b6f76cbe9ccb0e0fdc3528e3e2c3675a5c897d295bb7\ + 6524ec8a73a70b97909368f44d92f9aceaef0b03f3dafa1faa89fc663a92da3c\ + 19b4952463fac0e825e78cf046e266cfb9975af72e9d50d2c2cafee88fe2ceca\ + e2b1465fc07b280d83b66062dc9e7a372f81aec8e0bb9e97877814a5a6813c67\ + 746e35cd068d45d8664528bd00d5a306a5319e1bea7f38345da92d3a10d91476\ + a26aed6b8441f0f72fbbad5d5e0f8ae5cabc9f4f08e6be7902b5c53632db5264\ + afee7422c87b3237a32d5213ad0eb807b61977d9d90666cbb0c70500526b0eb7\ + 62c99351796db41166b0aa2f221b5607e0d629fac4e938488245c11557381a4f\ + 8addcc49913b11d42481cf8668e37bacbad4a20509e4fe4ccbcee7aea2909a2a\ + be59052f7f28b9340cd92f69729d615b8d3b530941c0b30506498cd4e561a9c8\ + 2d915266bb7115967bc76c5593c06d094bdf4294b868afc5fa52742d3bdbd593\ + 2df599f0e1187c49f0dba8679c771a514cc9da75e03506957800bf470d4a07c4\ + bb8918d6085499bb8ceeaba23c0b465863327e9ab8b6b8cf8b3ca530ca7b02cf\ + adf85437b750f305e8fbc8855c95bee8595a7e9e1f0993a03adbadc68665a189\ + 36cc99b6530b4518c0754990d7bfdfdac76f88cfcbcb7b3d9a71ee10cbd3a1bd\ + bc2e50b642c1fef56511962f845bbec6eab727b1d4add335db8d80c4c07e8356\ + ad05adad68b012489fa5bb5d9019a667778ddf7f5edd80f1d3c4abd64397a89e\ + 554c8007809336ddc2b2e7d5219c39fdf39aad33b9350f6b18fe3b98c690b906\ + 8f36d4b7669530fd216373842fbf70fe9bbe80854b31eed4bd515d6caeb065d6\ + c609846c9bfae1b3fce3db70b5bfb448ec69512e7f25019c789301b77a75f2a0\ + f81c65ec29f41bf96d597a00c310e8ba4b48ac82b5a735c1e83f22394eb2fc9b\ + 35d42a35533c938f26290a5860175637982f1733c99be39c44ac4a0918740630\ + 6bde2fd3d28e4e7bda73719912c338804dea03987757dac4d73def665e11da12\ + 6f9414f71624a3b753797eb0472bd334094515c4f9fe57fdd8d185f22b4bf82e\ + 4b5f6b800870cce19a0c8174dc11ee9f1cb9ffe0ac6f6fff1ebf7c915c7ae201\ + 72bb70390e3759912e0e0a4e83a0a2d2318f4386314a89f6438ccb331f89377f\ + f7947fe4b24f788aef85c1656ca87ee41c959f1b09bde09f20c2a51ac481646b\ + 28e9b0fc2ff49cfe8cf28577bf5bf6f261f54f97fcd2875da4210c6dfe685450\ + 280b68e378d9a486243cc682ed4ec747c37de1fde848e4a8f70498d22e40c462\ + c469c884cd67330e77b694e759232313f31a1624e0e1960f23ddae47b68ff553\ + d0de0910c8abe2e8e5fb063aa744ff77465fc731c7af79a84dcaa9b3f741a46d\ + d3c932877d49242c6d883e14392b8c4530986605812b636a73590ef437f27e40\ + d1af37ed1cbd68fb4e9ca5b0b41e5daee0142c1bf59c9d71f6c19b25e6148dfb\ + b9fb142107aabe3701e36611a7e0b13ea32d3c5f8a51f63c5f34415baa15f6ca\ + 77300eb323241ffe73c5acd97fcb682c21dc8911392979e9cb81be5218acf452\ + b5b93f6681d323b7989fdd10efe6fe9e2ac88d0d76a4cf3ee45e3b5c43010001\ + 4142c1fc7e8a658eff437594a25cf34d269556d8511918f27fdc7e9d6dd73f0e\ + 4790b91f225e9d131e6abb3dbfb66549a9aa57948fbd2f183fcd951b1d2305bf\ + fd6c0a602000000000000000016f5cdf9fb6c1b5e98a36befdc2c55bd4fd8793\ + d554b2506f51c909362495e1216ee83cd270ddb0a00785600ba23bd3363f0798\ + e3a7a117990415adec88e61be65170bd587ab4d2ee38edb22a91e5c29afa397d\ + d5a73465c51c6263f5fbde47fa801ce84464acc32589acaafadfe44d6558774b\ + 7085612a88f3424b6dca3c6f07217d1cbd5c41bda46a6a492a0119c1de4d25b5\ + 8c94250bee3fba6b8223777535673a2f4da6af27598030f88144f408120f07ca\ + 9c98d5d9edcdf6cdc9073f118fce55e6c9d0be80b5e87992ddaa9c22053b3a00\ + d42bdedc9768de25c0b37a5c4fb4e86710b33cebed5588d88adde607f6bca14f\ + 0279ce35126d403ffa50f288c87f528c19749ed43bd846c513fcd92c173fe76d\ + 8f2e69770439d3d075cb19b1094a42ee07ae1de197e8c136e2bc688a75a74db2\ + 4adb0fbb73872dc80074f61c9cce9bd33861bdd921ee3edacab1d6e7cec325c1\ + 72b6b6e82ada11687e4fc931225074dd1f20a0f9342dbce1fc3fdbf5bb6cb74a\ + b6475e574e9f5f247a2f7e4fcfcc354d4da8c8066e574642c7fccbbb9ef0aa59\ + 2ecab5366fe87eb8e14cd64aee34578aa48f68f8f4c5372df2c3fc429f5a3e39\ + ef6c034c87f9c52b2ea35e28c7bf3be737c3817efd6569466dc859e8ff8965c5\ + 249b6f045934d3d08b0ffd388aec58df8194ac2c4fec2152942d2626595e6566\ + 4b1fa33b5dae8ee796a840a56d885cbf7ae6483fad05e507ada3f075ebce0d79\ + 1b626c6dfe93f8492c4dd3b34aafc33d7644c5c8e38bfd8c19194f65be88fcb4\ + 538778632e489a626896372fdd2498b16e64daa7d3c5cfac688d6f9cdf371726\ + 1b0a1f25be1bdd6be6558ddb826fa04b5f668810a291aea51a6f05ff7c34dcf8\ + 1c74849a8015bad5e4e416989b10ef01de304775db725fa0b665f4330dc9c540\ + dc29aab144837362a97d6bb0165cb3272338c2d32386cd95ee3e66d876b591a2\ + 5a6907237523cf908f736d2fdc8e54ea8d9c7562697161d1f72fc4d7b7750524\ + 15cd0e5ae5bdf6edfab5776b6ff75ce5e1f8f2beea6ec74252b63966cca58abd\ + 638279dc5c998a1068079f3e5dcc8a69165c304c3d8c362ccfadab05ad12208a\ + 5655ab389eb727e8ed5f86b300331a13be26e2fbabf89fbfd2b98481dd5edb52\ + ed456a0e03a84b6f89761f91ff251412f5cfa286e35fb9f48ef0e044c4742b6e\ + 860a08767ecb80548c2f3df3b371cdb40e86dbe118f64e84faf45ecb78d73364\ + e9e31e3412ca2a3fad0a35983370ea9e6264a222edd1fd4aca30e3c169d7ca2d\ + 07609262e786ecd019c1417a06b7dfa32a54e0897afdc6492f26611555cbff47\ + dba3b76381f239d597a8f687669333e0b47b53d5bcc4fea1919490bad3c6f0b6\ + a58a50aca7ddeb9745ead454e0a38d9486fb52aefe0dbb92bf7fd6c215078aba\ + 3482b11274ec8cddff92c359bbc6d20bd823ad0bbf859cfaadf8e775b3d37b30\ + 78319f46c6d2a112cf60a673fee467538c70f1687d97fbe9d9f8a0856061592a\ + 4e00b6d10e979e674dd2cd0ba8b853f733877cd508062d5f723d58d215ad69c2\ + be6be742496aef54eb87338622eb36a9bbc5a7a602d280a45e095b1e078dab54\ + 479e783a513c722066acaae44ccc15f9560da91ed053ec05c36d82f680976687\ + 6c45c4fbeb2321d50f48f7995437d0c5fc365974a571fb0352d28cb1cdbd21d6\ + 9fab576a2e68d6b881776027bcdb7f01be22b1c847d91f26e680ef6ab2c128a8\ + 9b59432383d9bd661b0b01432cf8a25319426d38ac2e2114825f59b4250569c7\ + 98b1094920bb31130728313ff56a6eef2e6c4b275215dce3786d0f9024952b5f\ + 572566c53597e7ef4ab1f75743e605a564054d667f48906b5481d924769ef657\ + 51e349891d725a2c1bf8b102fea4c25c874d2fc2ce1bfec4b39bea76fbf7a288\ + 55725d52b595a4fc96892c3f1f961d46310ebd5221df729c02060035c559baf0\ + fd7efa73a2213ca29642857aeb8ebf7efdf9d2f5c84746b6fc35ab355a8dca56\ + e7dde4831e47ca1be6b62af30cfcf807c384e56ab84ff03bbe786251e6c4b932\ + c9217bf671046217bd0511fdc06aa69050c1480281e4843eb73d80095a2fb8e6\ + 8a2c0c98c9aea637b99d87ad847a3a76d59ea308c751f9cb4a4fce2989822bd6\ + ba2f901f09df647536dc30730ea3160dd35b8c6dcc9aa815b79ed492a8a299a2\ + 98ccdf784b9b0211ca877ec1723817c98529acaa4d3727162b5740b0fc9b498d\ + fb2212a3cbf0c63dc4f7663fafad7905643a792862b651e8497b0f0da632b897\ + ecf9ee63f2b20b54fa5eb2f2e424dcce5a075f50b856af266655be3a815fc83e\ + d8027508b2536976982196b160e2219ffdb5c7a56dd3e6b700860c711f4439db\ + f72973f4f26fe3260ec43a3446fe14444b9787d877e107be610147eec4a35747\ + 45e95a1f424aff062f84c559d13b1e6b59e8dc2221515c229f07db8eb39c515a\ + 321d8bd07b1bd6c9a79dac6d951c04415553c7a2ce1eb77495c7f89c4d5b4cff\ + d289435b69bc53585095083cc5a1b191781342266e204e1566aca8175e2ae84a\ + 8bd711d188b666dfb65a6442776d3e23c1b5192af09ec712537f2157d0ccbc1b\ + b3b3a1969d9705671f16bdc266e615ad2e50a8cbd666f3ee7465cc430c6cd69d\ + 30c91e717b12f7094b6f0ef89134d6c1620d28d8f238c181146448b348e4ca2e\ + 93c737210350f18fb878fb91b70ecc5689e5b6101ecfc545f6a1c903115b0c64\ + 19c91a50fb2dbe2edd362f2815f0c75070974507c34130ac9b29747ff7efbe6e\ + 37ee4c62be3ecfedfa817fdf3309163aaff677775b77f0d288c9858cfe59cb0f\ + a18afa591e7d574eaef43c82e79d71542c4177de4e5bd724b18cfd33c6853066\ + 5728a9d5ef192772094acbf3d885d5146c1634e74754e3fbcb94fa349eac8280\ + cfd7d1f46a0813b57a83bd078b1f7cb5a60a59b59380fe04e1c600c33b33d1ad\ + d69a9ff1be546f0ec5c0083979fce940b23711f382ac0d011c1103f02cb6082c\ + 18e39cf7a9c3bf4c081f905ae7b87951a7880b57e934465ccd634e5a17fd8d88\ + 66abfdfebd33b2c3d2c5be58144900c04e9c18de0c80270660e62a3c18527755\ + 5f89da4c41bd33cec1359f4ed21abdb586e1d97f720a92d16014d7f1822f1836\ + f74c97cb7f7b38e073477c6ab064fde835916c1e624de81f2ad90f6260073c5e\ + 1848582860f033630bde225821b39c2572b30c36adf8fdb8317c33df05f64134\ + 47f4985d12e9012629df09dc8f43373a6d0db4b0048453a6f1ec662472c77a30\ + d5cf4ac7084f736d0d598c251f2aefc986052fbf12a657885d7140ad36b07c63\ + ab86388a2be12d943747f3f29ef9f2e11e1444cc873df0ed7826eef675389a0d\ + 5a0388a8504fe89c4791ea4a572bfd406d5f01418b4f888c9a7a566e32811936\ + bf6950bbf786b86c41c28f2045d31953fcd15f179e7bc00c72870890537921f7\ + deff82270b0e44b88720aa738f60a85567deb7c90b0c2444467621e53e1c0794\ + 36d31d3d0b34dd237fc281eb9d87175237a9a433142db4bb7f8c4cb6a34e2dc7\ + 3f074045d216695ce88ef68e18564c935c9cbd902e939655c258de2ab78def87\ + 46bffd972083afce3b6881b7147262e1a44e0224689fafa1a3cb823c8da6eb7d\ + f091bec0638bf728b7b10aa95f2bce512ec8d3252938d2eb77b44ace7a2f9765\ + 88032cac5af670f9e5ca25cb0721bc1baec26f9c3a9f41b02fb62997d6cb0a01\ + 314845e9d0e78139ea49f2ead8736e0000" + ); + + assert_eq!( + tx.bitcoin_hash().to_string(), + "7ac6c1400003162ab667406221656f06dad902c70f96ee703f3f5f9f09df4bb9" + ); + assert_eq!( + tx.txid().to_string(), + "d606b563122409191e3b114a41d5611332dc58237ad5d2dccded302664fd56c4" + ); + assert_eq!(tx.input.len(), 1); + assert_eq!(tx.input[0].is_coinbase(), false); + assert_eq!(tx.is_coinbase(), false); + + assert_eq!(tx.output.len(), 3); + assert_eq!(tx.output[0].is_fee(), false); + assert_eq!(tx.output[1].is_fee(), false); + assert_eq!(tx.output[2].is_fee(), true); + + assert_eq!(tx.output[0].minimum_value(), 1); + assert_eq!(tx.output[1].minimum_value(), 1); + assert_eq!(tx.output[2].minimum_value(), 36480); + + assert_eq!(tx.output[0].is_null_data(), false); + assert_eq!(tx.output[1].is_null_data(), false); + assert_eq!(tx.output[2].is_null_data(), false); + + // Coinbase tx + let tx: Transaction = hex_deserialize!( + "0200000001010000000000000000000000000000000000000000000000000000\ + 000000000000ffffffff03520101ffffffff0201230f4f5d4b7c6fa845806ee4\ + f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000000000000000016a\ + 01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1\ + b201000000000000000000266a24aa21a9ed94f15ed3a62165e4a0b99699cc28\ + b48e19cb5bc1b1f47155db62d63f1e047d450000000000000120000000000000\ + 00000000000000000000000000000000000000000000000000000000000000" + ); + + assert_eq!( + tx.bitcoin_hash().to_string(), + "69e214ecad13b954208084572a6dedc264a016b953d71901f5aa1706d5f4916a" + ); + assert_eq!( + tx.txid().to_string(), + "cc1f895908af2509e55719e662acf4a50ca4dcf0454edd718459241745e2b0aa" + ); + assert_eq!(tx.input.len(), 1); + assert_eq!(tx.input[0].is_coinbase(), true); + assert_eq!(tx.is_coinbase(), true); + + assert_eq!(tx.output.len(), 2); + assert_eq!(tx.output[0].is_null_data(), true); + assert_eq!(tx.output[1].is_null_data(), true); + + } +} +