Make zebra-script wrap the C++ script implementation in zcashconsensus (#708)

* attempt to use zcashconsensus crate in zebra-script

* boop

* update verify fn to use zebra types

* a bit more cleanup

* cleanup

* more

* beep boop

* fix renamed member

* cleaning

* get a real branch id

* remove as of yet unneeded api

* Update zebra-chain/src/transaction.rs

* Update zebra-chain/src/transaction.rs

* more cleanup

* oops wrong dep section

* use a tuple to communicate arg association

* update to use published version of zcash_script

* fix new compiler error

* install llvm on windows

* fix bindgen bug????

* try to get docker file to win

* okay try everything

* fix windows build maybe

* always download choco

* fix paths for moved types

* try a different error message

* try convenience script

* try installing just llvm

* add back one more

* try installing some headers

* try a diff package

* try everything

* remove the minimum

* try newer docker builder image

* cleanup docker image

* cleanup extra ci step
This commit is contained in:
Jane Lusby 2020-08-18 11:08:53 -07:00 committed by GitHub
parent 8539302ad8
commit 1d6183ef84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 387 additions and 12 deletions

View File

@ -24,6 +24,9 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: fetch
- name: install LLVM on Windows
if: matrix.os == 'windows-latest'
run: choco install llvm -y
- name: Run tests
uses: actions-rs/cargo@v1
with:
@ -43,6 +46,9 @@ jobs:
with:
toolchain: stable
override: true
- name: install LLVM on Windows
if: matrix.os == 'windows-latest'
run: choco install llvm -y
- name: cargo fetch
uses: actions-rs/cargo@v1
with:

147
Cargo.lock generated
View File

@ -182,6 +182,30 @@ dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.54.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4d49b80beb70d76cdac92f5681e666f9a697c737c4f4117a67229a0386dc736"
dependencies = [
"bitflags",
"cexpr",
"cfg-if",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2 1.0.19",
"quote 1.0.7",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
name = "bit-set"
version = "0.5.2"
@ -334,6 +358,15 @@ version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff"
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -352,6 +385,17 @@ dependencies = [
"time",
]
[[package]]
name = "clang-sys"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "2.33.3"
@ -689,6 +733,19 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "equihash"
version = "0.1.0"
@ -914,6 +971,12 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gumdrop"
version = "0.7.0"
@ -1023,6 +1086,15 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "hyper"
version = "0.13.7"
@ -1155,12 +1227,28 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "libc"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
[[package]]
name = "libloading"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
dependencies = [
"cc",
"winapi 0.3.9",
]
[[package]]
name = "linked-hash-map"
version = "0.5.3"
@ -1418,6 +1506,16 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "num-format"
version = "0.4.0"
@ -1552,6 +1650,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pin-project"
version = "0.4.23"
@ -1940,6 +2044,12 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hex"
version = "2.1.0"
@ -2102,6 +2212,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]]
name = "signal-hook"
version = "0.1.16"
@ -2820,6 +2936,15 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]]
name = "winapi"
version = "0.2.8"
@ -2894,6 +3019,19 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "zcash_script"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "536376d3d5181455cb7df832cf1c7f519b3d2fe0a097091f891ffef96b9b4f09"
dependencies = [
"bindgen",
"blake2b_simd",
"cc",
"color-eyre",
"libc",
]
[[package]]
name = "zebra-chain"
version = "3.0.0-alpha.0"
@ -2999,6 +3137,15 @@ version = "3.0.0-alpha.0"
[[package]]
name = "zebra-script"
version = "3.0.0-alpha.0"
dependencies = [
"displaydoc",
"hex",
"lazy_static",
"thiserror",
"zcash_script",
"zebra-chain",
"zebra-test",
]
[[package]]
name = "zebra-state"

View File

@ -1,8 +1,8 @@
FROM rust:stretch as builder
FROM rust:buster as builder
RUN apt-get update && \
apt-get install -y --no-install-recommends \
make cmake g++ gcc
make cmake g++ gcc llvm libclang-dev
RUN mkdir /zebra
WORKDIR /zebra

View File

@ -72,6 +72,29 @@ pub(crate) const TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct ConsensusBranchId(u32);
impl ConsensusBranchId {
/// Consensus branch ID for the overwinter network branch
pub const OVERWINTER: ConsensusBranchId = ConsensusBranchId(0x5ba81b19);
/// Consensus branch ID for the sapling network branch
pub const SAPLING: ConsensusBranchId = ConsensusBranchId(0x76b809bb);
/// Consensus branch ID for the blossom network branch
pub const BLOSSOM: ConsensusBranchId = ConsensusBranchId(0x2bb40e60);
/// Consensus branch ID for the heartwood network branch
pub const HEARTWOOD: ConsensusBranchId = ConsensusBranchId(0xf5b9230b);
/// Consensus branch ID for the canopy network branch
pub const CANOPY: ConsensusBranchId = ConsensusBranchId(0xe9ff75a6);
}
impl From<ConsensusBranchId> for u32 {
fn from(branch: ConsensusBranchId) -> u32 {
branch.0
}
}
/// Network Upgrade Consensus Branch Ids.
///
/// Branch ids are the same for mainnet and testnet. If there is a testnet
@ -84,13 +107,13 @@ pub struct ConsensusBranchId(u32);
/// do the uniqueness check in the unit tests.
pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = &[
// TODO(teor): byte order?
(Overwinter, ConsensusBranchId(0x5ba81b19)),
(Sapling, ConsensusBranchId(0x76b809bb)),
(Blossom, ConsensusBranchId(0x2bb40e60)),
(Heartwood, ConsensusBranchId(0xf5b9230b)),
(Overwinter, ConsensusBranchId::OVERWINTER),
(Sapling, ConsensusBranchId::SAPLING),
(Blossom, ConsensusBranchId::BLOSSOM),
(Heartwood, ConsensusBranchId::HEARTWOOD),
// As of 21 July 2020. Could change before mainnet activation.
// See ZIP 251 for updates.
(Canopy, ConsensusBranchId(0xe9ff75a6)),
(Canopy, ConsensusBranchId::CANOPY),
];
impl NetworkUpgrade {

View File

@ -8,3 +8,12 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zcash_script = "0.1.0"
zebra-chain = { path = "../zebra-chain" }
thiserror = "1.0.20"
displaydoc = "0.1.7"
[dev-dependencies]
hex = "0.4.2"
lazy_static = "1.4.0"
zebra-test = { path = "../zebra-test" }

View File

@ -1,11 +1,197 @@
//! Zebra script verification wrapping zcashd's zcash_script library
#![doc(html_favicon_url = "https://www.zfnd.org/images/zebra-favicon-128.png")]
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_script")]
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
use displaydoc::Display;
#[cfg(windows)]
use std::convert::TryInto;
use std::sync::Arc;
use thiserror::Error;
use zcash_script::{
zcash_script_error_t, zcash_script_error_t_zcash_script_ERR_OK,
zcash_script_error_t_zcash_script_ERR_TX_DESERIALIZE,
zcash_script_error_t_zcash_script_ERR_TX_INDEX,
zcash_script_error_t_zcash_script_ERR_TX_SIZE_MISMATCH,
};
use zebra_chain::{
parameters::ConsensusBranchId, serialization::ZcashSerialize, transaction::Transaction,
transparent,
};
#[derive(Debug, Display, Error)]
#[non_exhaustive]
/// An Error type representing the error codes returned from zcash_script.
pub enum Error {
/// script failed to verify
#[non_exhaustive]
ScriptInvalid,
/// could not to deserialize tx
#[non_exhaustive]
TxDeserialize,
/// input index out of bounds for transaction's inputs
#[non_exhaustive]
TxIndex,
/// tx is an invalid size for it's protocol
#[non_exhaustive]
TxSizeMismatch,
/// encountered unknown error kind from zcash_script: {0}
#[non_exhaustive]
Unknown(zcash_script_error_t),
}
impl From<zcash_script_error_t> for Error {
#[allow(non_upper_case_globals)]
fn from(err_code: zcash_script_error_t) -> Error {
match err_code {
zcash_script_error_t_zcash_script_ERR_OK => Error::ScriptInvalid,
zcash_script_error_t_zcash_script_ERR_TX_DESERIALIZE => Error::TxDeserialize,
zcash_script_error_t_zcash_script_ERR_TX_INDEX => Error::TxIndex,
zcash_script_error_t_zcash_script_ERR_TX_SIZE_MISMATCH => Error::TxSizeMismatch,
unknown => Error::Unknown(unknown),
}
}
}
/// Thin safe wrapper around ffi interface provided by libzcash_script
fn verify_script(
script_pub_key: impl AsRef<[u8]>,
amount: i64,
tx_to: impl AsRef<[u8]>,
n_in: u32,
consensus_branch_id: u32,
) -> Result<(), Error> {
let script_pub_key = script_pub_key.as_ref();
let tx_to = tx_to.as_ref();
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 flags = zcash_script::zcash_script_SCRIPT_FLAGS_VERIFY_P2SH
| zcash_script::zcash_script_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY;
let ret = unsafe {
zcash_script::zcash_script_verify(
script_ptr,
script_len as u32,
amount,
tx_to_ptr,
tx_to_len as u32,
n_in,
#[cfg(not(windows))]
flags,
#[cfg(windows)]
flags.try_into().expect("why bindgen whyyy"),
consensus_branch_id,
&mut err,
)
};
if ret == 1 {
Ok(())
} else {
Err(Error::from(err))
}
}
/// Verify a script within a transaction given the corresponding
/// `transparent::Output` it is spending and the `ConsensusBranchId` of the block
/// containing the transaction.
///
/// # Details
///
/// input index corresponds to the index of the `TransparentInput` which in
/// `transaction` used to identify the `previous_output`
pub fn is_valid(
transaction: Arc<Transaction>,
branch_id: ConsensusBranchId,
(input_index, previous_output): (u32, transparent::Output),
) -> Result<(), Error> {
assert!((input_index as usize) < transaction.inputs().len());
let tx_to = transaction
.zcash_serialize_to_vec()
.expect("serialization into a vec is infallible");
let transparent::Output { value, lock_script } = previous_output;
verify_script(
&lock_script.0,
value.into(),
&tx_to,
input_index as _,
branch_id.into(),
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
use std::convert::TryInto;
use std::sync::Arc;
use zebra_chain::{serialization::ZcashDeserializeInto, transparent};
use zebra_test::prelude::*;
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 verify_valid_script_parsed() -> Result<()> {
zebra_test::init();
let transaction =
SCRIPT_TX.zcash_deserialize_into::<Arc<zebra_chain::transaction::Transaction>>()?;
let coin = u64::pow(10, 8);
let amount = 212 * coin;
let output = transparent::Output {
value: amount.try_into()?,
lock_script: transparent::Script(SCRIPT_PUBKEY.clone()),
};
let input_index = 0;
let branch_id = ConsensusBranchId::BLOSSOM;
is_valid(transaction, branch_id, (input_index, output))?;
Ok(())
}
#[test]
fn verify_valid_script() -> Result<()> {
zebra_test::init();
let coin = i64::pow(10, 8);
let script_pub_key = &*SCRIPT_PUBKEY;
let amount = 212 * coin;
let tx_to = &*SCRIPT_TX;
let n_in = 0;
let branch_id = 0x2bb40e60;
verify_script(script_pub_key, amount, tx_to, n_in, branch_id)?;
Ok(())
}
#[test]
fn dont_verify_invalid_script() -> Result<()> {
zebra_test::init();
let coin = i64::pow(10, 8);
let script_pub_key = &*SCRIPT_PUBKEY;
let amount = 212 * coin;
let tx_to = &*SCRIPT_TX;
let n_in = 0;
let branch_id = 0x2bb40e61;
verify_script(script_pub_key, amount, tx_to, n_in, branch_id).unwrap_err();
Ok(())
}
}

View File

@ -2,3 +2,7 @@ pub use crate::command::{tempdir, test_cmd, CommandExt, TestChild};
pub use std::process::Stdio;
pub use tempdir::TempDir;
pub use color_eyre;
pub use color_eyre::eyre;
pub use eyre::Result;