initial working version on cc (#1)

* initial working version on cc

* add basic actions automation

* checkout submodules

* don't require bindgen generation

* use newest zcash changes

* pause work on ripping out sodium

* push hacks to some version of the repo

* fix path in build.rs

* add job for testing different OSs

* str4d is the fucking greatest

* cleanup unused code

* remove symlink

* cleanup cruft

* remove old automation files

* Apply suggestions from code review

Co-authored-by: Saleem Rashid <dev@saleemrashid.com>

* add failure case test

* always generate bindings

* install llvm dep on windows

* cleanup unused bindings

* try messing with CI

* use actual branch name

* use correct remote for gitmodules

* Update build.rs

Co-authored-by: Saleem Rashid <dev@saleemrashid.com>

Co-authored-by: Saleem Rashid <dev@saleemrashid.com>
This commit is contained in:
Jane Lusby 2020-07-22 14:02:44 -07:00 committed by GitHub
parent 20d5c11746
commit c2252b2a07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 274 additions and 365 deletions

119
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,119 @@
name: Continuous integration
on:
push:
branches:
- master
pull_request:
branches-ignore:
- master
jobs:
check:
name: Check
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- uses: actions-rs/cargo@v1
with:
command: check
test-versions:
name: Test Suite
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
- nightly
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- uses: actions-rs/cargo@v1
with:
command: test
test-os:
name: Test Suite
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
with:
submodules: true
# - name: install LLVM on Linux
# if: matrix.os == 'ubuntu-latest'
# run: sudo apt-get install llvm-dev libclang-dev clang
# - name: install LLVM on Mac
# if: matrix.os == 'macOS-latest'
# run: brew install llvm
- name: install LLVM on Windows
if: matrix.os == 'windows-latest'
run: choco install llvm -y
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- uses: actions-rs/cargo@v1
with:
command: test
fmt:
name: Rustfmt
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings

8
.gitmodules vendored
View File

@ -1,4 +1,4 @@
[submodule "depend/bitcoin"]
path = depend/bitcoin
url = https://github.com/bitcoin/bitcoin.git
branch = 0.19
[submodule "depend/zcash"]
path = depend/zcash
url = https://github.com/str4d/zcash.git
branch = zcashconsensus-fixes

View File

@ -1,7 +0,0 @@
language: rust
rust:
- stable
- beta
- nightly
- 1.22.0

View File

@ -1,18 +1,14 @@
[package]
name = "bitcoinconsensus"
version = "0.19.0-1"
authors = ["Tamas Blummer <tamas.blummer@gmail.com>"]
name = "zcashconsensus"
version = "0.1.0"
authors = ["Tamas Blummer <tamas.blummer@gmail.com>", "Zcash Foundation <zebra@zfnd.org>"]
license = "Apache-2.0"
homepage = "https://github.com/rust-bitcoin/rust-bitcoinconsensus/"
repository = "https://github.com/rust-bitcoin/rust-bitcoinconsensus/"
description = "Bitcoin's libbitcoinconsensus with Rust binding."
keywords = [ "bitcoin", "bitcoinconsensus", "libbitcoinconsensus" ]
readme = "README.md"
exclude = ["/bitcoin/.github/**", "/bitcoin/.tx/**", "/bitcoin/build-aux/**", "/bitcoin/contrib/**", "/bitcoin/depends/**", "/bitcoin/doc/**", "/bitcoin/share/**", "/bitcoin/test/**", "/bitcoin/src/bench/**", "/bitcoin/src/config/**", "/bitcoin/src/consensus/**", "/bitcoin/src/leveldb/**", "/bitcoin/src/objtest/**", "/bitcoin/src/obj/**", "/bitcoin/src/policy/**", "/bitcoin/src/qt/**", "/bitcoin/src/rpc/**", "/bitcoin/src/support/**", "/bitcoin/src/test/**", "/bitcoin/src/univalue/**", "/bitcoin/src/wallet/**", "/bitcoin/src/zmq/**"]
build = "build.rs"
edition = "2018"
[lib]
name = "bitcoinconsensus"
name = "zcashconsensus"
path = "src/lib.rs"
[features]
@ -20,9 +16,14 @@ external-secp = []
[dependencies]
libc="0.2"
blake2b_simd = "0.5.10"
[build-dependencies]
cc = ">= 1.0.36, <= 1.0.41"
color-eyre = "0.5.0"
bindgen = "0.54.1"
[dev-dependencies]
rustc-serialize = "0.3"
hex = "0.4.2"
lazy_static = "1.4.0"

View File

@ -1,76 +1,14 @@
[![Status](https://travis-ci.org/rust-bitcoin/rust-bitcoinconsensus.png?branch=master)](https://travis-ci.org/rust-bitcoin/rust-bitcoinconsensus)
## zcashconsensus
# Bitcoin's libbitcoinconsensus with Rust binding
[![Build Status][actions-badge]][actions-url]
[![Latest Version][version-badge]][version-url]
[![Rust Documentation][docs-badge]][docs-url]
This project builds libbitcoinconsensus library from Bitcoin's C++ sources using cargo and offers Rust binding to its API.
[actions-badge]: https://github.com/ZcashFoundation/zcashconsensus/workflows/Continuous%20integration/badge.svg
[actions-url]: https://github.com/ZcashFoundation/zcashconsensus/actions?query=workflow%3A%22Continuous+integration%22
[version-badge]: https://img.shields.io/crates/v/zcashconsensus.svg
[version-url]: https://crates.io/crates/zcashconsensus
[docs-badge]: https://img.shields.io/badge/docs-latest-blue.svg
[docs-url]: https://docs.rs/zcashconsensus
Libbitcoinconsenus allows transaction verification using Bitcoins unique script engine.
Bitcoin enabled applications SHOULD use libbitcoinconsensus library to avoid accepting transactions that the Bitcoin network nodes would not.
This project simplifies Rust developer's life by creating the libbitcoinconsensus library with cargo.
No need to deal with the archaic C++ toolchain directly.
This also simplifies cross-compiling the consenus library e.g. for a mobile application.
Libbitcoinconsenus refers to code from another library [secp256k1](https://github.com/bitcoin-core/secp256k1).
A snapshot of that library is also included into Bitcoin sources, therefore it could be backed into libbitcoinconsenus.
A typical Bitcoin enabled application will however want to access further secp256k1 functions.
The project [rustc-secp256k1](https://github.com/rust-bitcoin/rust-secp256k1) offers a cargo build and Rust bindings,
therefore decided to depend on that instead of compiling the Bitcoin embedded sources into libbitcoinconsensus.
This introduces a risk, since a difference between the two secp256k1 sources could break consensus with Bitcoin.
## Build
This project has a submodule (the Bitcoin Core sources), you have to clone it using:
`
git clone --recurse-submodules git@github.com:rust-bitcoin/rust-bitcoinconsensus.git
`
then build it simple with:
`
cargo build
`
I verified the build for Linux and OSX. Aleksey Sidorov contributed the windows build. PRs are welcome to extend support for other platforms.
## API
The API is very basic, exposing Bitcoin's as is. This is intentional to keep this project minimal footprint and no further runtime dependencies. You will need another Rust library to serialize Bitcoin transactions and scripts.
Verify a single spend (input) of a Bitcoin transaction:
`
verify (spent_output_script: &[u8], amount: u64, spending_transaction: &[u8], input_index: usize) -> Result<(), Error>
`
### Arguments
* spend_output_script: a Bitcoin transaction output script to be spent
* amount: The spent output amount in satoshis
* spending_transaction: spending Bitcoin transaction, serialized in Bitcoin's on wire format
* input_index: index of the input within spending_transaction
### Example
The (randomly choosen) Bitcoin transaction [aca326a724eda9a461c10a876534ecd5ae7b27f10f26c3862fb996f80ea2d45d](https://blockchain.info/tx/aca326a724eda9a461c10a876534ecd5ae7b27f10f26c3862fb996f80ea2d45d)
spends one input, that is the first output of [95da344585fcf2e5f7d6cbf2c3df2dcce84f9196f7a7bb901a43275cd6eb7c3f](https://blockchain.info/tx/95da344585fcf2e5f7d6cbf2c3df2dcce84f9196f7a7bb901a43275cd6eb7c3f) with a value of 630482530 satoshis
The spending transaction in wire format is:
`
spending = 02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700
`
The script of the first output of the spent transaction is:
`
spent = 76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac
`
The (pseudo code) call:
`
verify(spent, 630482530, spending, 0)
`
should return OK(())
**Note** that spent amount will only be checked for Segwit transactions. Above example is not segwit therefore verify will succeed with any amount.
Rust bindings to the ECC's `zcashconsensus` c++ library.

View File

@ -1,47 +0,0 @@
os: Visual Studio 2015
## Build Matrix ##
environment:
matrix:
- channel: 1.22.0
target: x86_64-pc-windows-msvc
- channel: 1.22.0
target: i686-pc-windows-msvc
- channel: stable
target: x86_64-pc-windows-msvc
- channel: stable
target: i686-pc-windows-msvc
- channel: beta
target: x86_64-pc-windows-msvc
- channel: beta
target: i686-pc-windows-msvc
- channel: nightly
target: x86_64-pc-windows-msvc
- channel: nightly
target: i686-pc-windows-msvc
matrix:
allow_failures:
- channel: nightly
install:
- git submodule update --init --recursive
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %CHANNEL%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
## Build Script ##
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
# the "directory does not contain a project or solution file" error.
build: false
test_script:
- cargo test

View File

@ -1,8 +1,33 @@
extern crate cc;
use color_eyre::eyre::{eyre, Context, Result};
use std::{env, path::PathBuf};
use std::env;
fn bindgen_headers() -> Result<()> {
println!("cargo:rerun-if-changed=depend/zcash/src/script/zcashconsensus.h");
let bindings = bindgen::Builder::default()
.header("depend/zcash/src/script/zcashconsensus.h")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
// Finish the builder and generate the bindings.
.generate()
.map_err(|_| eyre!("Unable to generate bindings"))?;
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = env::var("OUT_DIR")?;
let out_path = PathBuf::from(out_path);
bindings
.write_to_file(out_path.join("bindings.rs"))
.wrap_err("unable to write bindings")?;
Ok(())
}
fn main() -> Result<()> {
color_eyre::install()?;
bindgen_headers()?;
fn main() {
// Check whether we can use 64-bit compilation
let use_64bit_compilation = if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "64" {
let check = cc::Build::new()
@ -20,26 +45,33 @@ fn main() {
let target = env::var("TARGET").expect("TARGET was not set");
let is_big_endian = env::var("CARGO_CFG_TARGET_ENDIAN").expect("No endian is set") == "big";
let mut base_config = cc::Build::new();
base_config
.cpp(true)
.include("depend/bitcoin/src")
.include("depend/bitcoin/src/secp256k1/include")
.include("depend/zcash/src")
.include("depend/zcash/src/rust/include/")
.include("depend/zcash/src/secp256k1/include")
.flag_if_supported("-Wno-implicit-fallthrough")
.flag_if_supported("-Wno-catch-value")
.flag_if_supported("-Wno-reorder")
.flag_if_supported("-Wno-deprecated-copy")
.define("HAVE_DECL_STRNLEN", "1")
.define("__STDC_FORMAT_MACROS", None);
// **Secp256k1**
if !cfg!(feature = "external-secp") {
base_config
.include("depend/bitcoin/src/secp256k1")
.include("depend/zcash/src/secp256k1")
.flag_if_supported("-Wno-unused-function") // some ecmult stuff is defined but not used upstream
.define("SECP256K1_BUILD", "1")
// Bitcoin core defines libsecp to *not* use libgmp.
// zcash core defines libsecp to *not* use libgmp.
.define("USE_NUM_NONE", "1")
.define("USE_FIELD_INV_BUILTIN", "1")
.define("USE_SCALAR_INV_BUILTIN", "1")
// Technically libconsensus doesn't require the recovery feautre, but `pubkey.cpp` does.
.define("ENABLE_MODULE_RECOVERY", "1")
// The actual libsecp256k1 C code.
.file("depend/bitcoin/src/secp256k1/src/secp256k1.c");
.file("depend/zcash/src/secp256k1/src/secp256k1.c");
if is_big_endian {
base_config.define("WORDS_BIGENDIAN", "1");
@ -59,28 +91,31 @@ fn main() {
let tool = base_config.get_compiler();
if tool.is_like_msvc() {
base_config.flag("/std:c++14").flag("/wd4100");
base_config.flag("/std:c++17").flag("/wd4100");
} else if tool.is_like_clang() || tool.is_like_gnu() {
base_config.flag("-std=c++11").flag("-Wno-unused-parameter");
base_config.flag("-std=c++17").flag("-Wno-unused-parameter");
}
if target.contains("windows") {
base_config.define("WIN32", "1");
}
base_config
.file("depend/bitcoin/src/util/strencodings.cpp")
.file("depend/bitcoin/src/uint256.cpp")
.file("depend/bitcoin/src/pubkey.cpp")
.file("depend/bitcoin/src/hash.cpp")
.file("depend/bitcoin/src/primitives/transaction.cpp")
.file("depend/bitcoin/src/crypto/ripemd160.cpp")
.file("depend/bitcoin/src/crypto/sha1.cpp")
.file("depend/bitcoin/src/crypto/sha256.cpp")
.file("depend/bitcoin/src/crypto/sha512.cpp")
.file("depend/bitcoin/src/crypto/hmac_sha512.cpp")
.file("depend/bitcoin/src/script/bitcoinconsensus.cpp")
.file("depend/bitcoin/src/script/interpreter.cpp")
.file("depend/bitcoin/src/script/script.cpp")
.file("depend/bitcoin/src/script/script_error.cpp")
.compile("libbitcoinconsensus.a");
.file("depend/zcash/src/script/zcashconsensus.cpp")
.file("depend/zcash/src/utilstrencodings.cpp")
.file("depend/zcash/src/uint256.cpp")
.file("depend/zcash/src/pubkey.cpp")
.file("depend/zcash/src/hash.cpp")
.file("depend/zcash/src/primitives/transaction.cpp")
.file("depend/zcash/src/crypto/ripemd160.cpp")
.file("depend/zcash/src/crypto/sha1.cpp")
.file("depend/zcash/src/crypto/sha256.cpp")
.file("depend/zcash/src/crypto/sha512.cpp")
.file("depend/zcash/src/crypto/hmac_sha512.cpp")
.file("depend/zcash/src/script/interpreter.cpp")
.file("depend/zcash/src/script/script.cpp")
.file("depend/zcash/src/script/script_error.cpp")
.compile("libzcashconsensus.a");
Ok(())
}

@ -1 +0,0 @@
Subproject commit febf04841fd94429ad13c9c1548bd052cdf82833

1
depend/zcash Submodule

@ -0,0 +1 @@
Subproject commit e5300f875eca2060e233babfd70032427dea4736

1
src/blake2b.rs Normal file
View File

@ -0,0 +1 @@
include!("../depend/zcash/src/rust/src/blake2b.rs");

View File

@ -1,212 +1,81 @@
//
// Copyright 2018 Tamas Blummer
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//
extern crate libc;
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_script")]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use libc::{c_int,c_uchar, c_uint, uint64_t};
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq, Debug, Clone)]
#[repr(C)]
pub enum Error {
ERR_SCRIPT = 0,
#[allow(dead_code)]
ERR_TX_INDEX,
#[allow(dead_code)]
ERR_TX_SIZE_MISMATCH,
#[allow(dead_code)]
ERR_TX_DESERIALIZE,
#[allow(dead_code)]
ERR_AMOUNT_REQUIRED
}
#[allow(dead_code)]
pub const VERIFY_NONE : c_uint = 0;
// evaluate P2SH (BIP16) subscripts
pub const VERIFY_P2SH : c_uint = (1 << 0);
// enforce strict DER (BIP66) compliance
pub const VERIFY_DERSIG : c_uint = (1 << 2);
// enforce NULLDUMMY (BIP147)
pub const VERIFY_NULLDUMMY : c_uint = (1 << 4);
// enable CHECKLOCKTIMEVERIFY (BIP65)
pub const VERIFY_CHECKLOCKTIMEVERIFY : c_uint = (1 << 9);
// enable CHECKSEQUENCEVERIFY (BIP112)
pub const VERIFY_CHECKSEQUENCEVERIFY : c_uint = (1 << 10);
// enable WITNESS (BIP141)
pub const VERIFY_WITNESS : c_uint = (1 << 11);
pub const VERIFY_ALL : c_uint = VERIFY_P2SH | VERIFY_DERSIG | VERIFY_NULLDUMMY |
VERIFY_CHECKLOCKTIMEVERIFY | VERIFY_CHECKSEQUENCEVERIFY | VERIFY_WITNESS;
extern "C" {
pub fn bitcoinconsensus_version() -> c_int;
pub fn bitcoinconsensus_verify_script_with_amount(
script_pubkey: *const c_uchar,
script_pubkeylen: c_uint,
amount: uint64_t,
tx_to: *const c_uchar,
tx_tolen: c_uint,
n_in: c_uint,
flags: c_uint,
err: *mut Error) -> c_int;
}
/// Compute flags for soft fork activation heights on the Bitcoin network
pub fn height_to_flags(height: u32) -> u32 {
let mut flag = VERIFY_NONE;
if height > 170059 {
flag |= VERIFY_P2SH;
}
if height > 363724 {
flag |= VERIFY_DERSIG;
}
if height > 388381 {
flag |= VERIFY_CHECKLOCKTIMEVERIFY;
}
if height > 419328 {
flag |= VERIFY_CHECKSEQUENCEVERIFY;
}
if height > 481824 {
flag |= VERIFY_NULLDUMMY | VERIFY_WITNESS
}
flag as u32
}
/// Return libbitcoinconsenus version
pub fn version () -> u32 {
unsafe { bitcoinconsensus_version() as u32 }
}
/// Verify a single spend (input) of a Bitcoin transaction.
/// # Arguments
/// * spend_output_script: a Bitcoin transaction output script to be spent, serialized in Bitcoin's on wire format
/// * amount: The spent output amount in satoshis
/// * spending_transaction: spending Bitcoin transaction, serialized in Bitcoin's on wire format
/// * input_index: index of the input within spending_transaction
/// # Returns
/// OK or Err. Note that amount will only be checked for Segwit transactions.
///
/// # Example
///
/// The (randomly choosen) Bitcoin transaction [aca326a724eda9a461c10a876534ecd5ae7b27f10f26c3862fb996f80ea2d45d](https://blockchain.info/tx/aca326a724eda9a461c10a876534ecd5ae7b27f10f26c3862fb996f80ea2d45d)
/// spends one input, that is the first output of [95da344585fcf2e5f7d6cbf2c3df2dcce84f9196f7a7bb901a43275cd6eb7c3f](https://blockchain.info/tx/95da344585fcf2e5f7d6cbf2c3df2dcce84f9196f7a7bb901a43275cd6eb7c3f) with a value of 630482530 satoshis
///
/// The spending transaction in wire format is:
///
/// `
/// spending = 02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700
/// `
///
/// The script of the first output of the spent transaction is:
///
/// `
/// spent = 76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac
/// `
///
/// The (pseudo code) call:
///
/// `
/// verify(spent, 630482530, spending, 0)
/// `
/// should return OK(())
///
/// **Note** that spent amount will only be checked for Segwit transactions. Above example is not segwit, therefore verify will succeed with any amount.
///
pub fn verify (spent_output: &[u8], amount: u64, spending_transaction: &[u8], input_index: usize) -> Result<(), Error> {
verify_with_flags (spent_output, amount, spending_transaction, input_index, VERIFY_ALL)
}
/// Same as verify but with flags that turn past soft fork features on or off
pub fn verify_with_flags (spent_output_script: &[u8], amount: u64, spending_transaction: &[u8], input_index: usize, flags: u32) -> Result<(), Error> {
unsafe {
let mut error = Error::ERR_SCRIPT;
let ret = bitcoinconsensus_verify_script_with_amount(
spent_output_script.as_ptr(),
spent_output_script.len() as c_uint,
amount as uint64_t,
spending_transaction.as_ptr(),
spending_transaction.len() as c_uint,
input_index as c_uint,
flags as c_uint,
&mut error
);
if ret != 1 {
Err(error)
} else {
Ok(())
}
}
}
mod blake2b;
#[cfg(test)]
mod tests {
extern crate rustc_serialize as serialize;
pub use super::zcashconsensus_error_t;
use super::*;
use self::serialize::hex::FromHex;
pub fn verify_script(
script_pub_key: &[u8],
amount: i64,
tx_to: &[u8],
nIn: u32,
flags: u32,
consensus_branch_id: u32,
) -> Result<(), zcashconsensus_error_t> {
let script_ptr = script_pub_key.as_ptr();
let script_len = script_pub_key.len();
let tx_to_ptr = tx_to.as_ptr();
let tx_to_len = tx_to.len();
let mut err = 0;
let ret = unsafe {
super::zcashconsensus_verify_script(
script_ptr,
script_len as u32,
amount,
tx_to_ptr,
tx_to_len as u32,
nIn,
flags,
consensus_branch_id,
&mut err,
)
};
if ret == 1 {
Ok(())
} else {
Err(err)
}
}
use hex::FromHex;
lazy_static::lazy_static! {
pub static ref SCRIPT_PUBKEY: Vec<u8> = <Vec<u8>>::from_hex("76a914f47cac1e6fec195c055994e8064ffccce0044dd788ac").unwrap();
pub static ref SCRIPT_TX: Vec<u8> = <Vec<u8>>::from_hex("0400008085202f8901fcaf44919d4a17f6181a02a7ebe0420be6f7dad1ef86755b81d5a9567456653c010000006a473044022035224ed7276e61affd53315eca059c92876bc2df61d84277cafd7af61d4dbf4002203ed72ea497a9f6b38eb29df08e830d99e32377edb8a574b8a289024f0241d7c40121031f54b095eae066d96b2557c1f99e40e967978a5fd117465dbec0986ca74201a6feffffff020050d6dc0100000017a9141b8a9bda4b62cd0d0582b55455d0778c86f8628f870d03c812030000001976a914e4ff5512ffafe9287992a1cd177ca6e408e0300388ac62070d0095070d000000000000000000000000").expect("Block bytes are in valid hex representation");
}
#[test]
fn bitcoinconsensus_test() {
// a random old-style transaction from the blockchain
verify_test (
"76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac",
"02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700",
0, 0
).unwrap();
// a random segwit transaction from the blockchain using P2SH
verify_test (
"a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87",
"01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000",
1900000, 0
).unwrap();
// a random segwit transaction from the blockchain using native segwit
verify_test(
"0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d",
"010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000",
18393430 , 0
).unwrap();
// a random old-style transaction from the blockchain - WITH WRONG SIGNATURE for the address
assert!(verify_test (
"76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff",
"02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700",
0, 0
).is_err());
// a random segwit transaction from the blockchain using P2SH - WITH WRONG AMOUNT
assert!(verify_test (
"a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87",
"01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000",
900000, 0).is_err());
// a random segwit transaction from the blockchain using native segwit - WITH WRONG SEGWIT
assert!(verify_test(
"0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58f",
"010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000",
18393430 , 0
).is_err());
fn it_works() {
let coin = i64::pow(10, 8);
let script_pub_key = &*SCRIPT_PUBKEY;
let amount = 212 * coin;
let tx_to = &*SCRIPT_TX;
let nIn = 0;
let flags = 1;
let branch_id = 0x2bb40e60;
verify_script(script_pub_key, amount, tx_to, nIn, flags, branch_id).unwrap();
}
fn verify_test (spent : &str, spending :&str, amount :u64, input: usize) -> Result<(),Error> {
verify (spent.from_hex().unwrap().as_slice(), amount, spending.from_hex().unwrap().as_slice(), input)
#[test]
fn it_doesnt_work() {
let coin = i64::pow(10, 8);
let script_pub_key = &*SCRIPT_PUBKEY;
let amount = 212 * coin;
let tx_to = &*SCRIPT_TX;
let nIn = 0;
let flags = 1;
let branch_id = 0x2bb40e61;
verify_script(script_pub_key, amount, tx_to, nIn, flags, branch_id).unwrap_err();
}
}