Remove dependencies by switching to simplified API with a callback interface (#157)
* created zcash_script_verify_prehashed; compiles * removed unneded code; callback API * revert formatting changes to interpreter.cpp/.h * remove prehashed code * moving code around * use #if 0 instead of commenting out to make diff clearer * tmp * remove debug prints * cleanups * deleted unneeded rust files * remove unneeded dependencies * remove more unneeded files * add new tests and remove old ones * remove unneded params; add sighashLen to callback * remove unneeded code * msvc fixes
This commit is contained in:
parent
ef2a7fdd5d
commit
4924030059
File diff suppressed because it is too large
Load Diff
79
Cargo.toml
79
Cargo.toml
|
@ -16,29 +16,40 @@ include = [
|
||||||
"build.rs",
|
"build.rs",
|
||||||
"src/*.rs",
|
"src/*.rs",
|
||||||
"/depend/check_uint128_t.c",
|
"/depend/check_uint128_t.c",
|
||||||
"/depend/zcash/src/script/zcash_script.h",
|
"/depend/zcash/src/amount.cpp",
|
||||||
"/depend/zcash/src/script/zcash_script.cpp",
|
"/depend/zcash/src/amount.h",
|
||||||
"/depend/zcash/src/utilstrencodings.cpp",
|
"/depend/zcash/src/compat/byteswap.h",
|
||||||
"/depend/zcash/src/uint256.cpp",
|
"/depend/zcash/src/compat/endian.h",
|
||||||
"/depend/zcash/src/pubkey.cpp",
|
"/depend/zcash/src/consensus/consensus.h",
|
||||||
"/depend/zcash/src/hash.cpp",
|
"/depend/zcash/src/crypto/common.h",
|
||||||
"/depend/zcash/src/primitives/transaction.cpp",
|
|
||||||
"/depend/zcash/src/crypto/ripemd160.cpp",
|
"/depend/zcash/src/crypto/ripemd160.cpp",
|
||||||
|
"/depend/zcash/src/crypto/ripemd160.h",
|
||||||
"/depend/zcash/src/crypto/sha1.cpp",
|
"/depend/zcash/src/crypto/sha1.cpp",
|
||||||
|
"/depend/zcash/src/crypto/sha1.h",
|
||||||
"/depend/zcash/src/crypto/sha256.cpp",
|
"/depend/zcash/src/crypto/sha256.cpp",
|
||||||
"/depend/zcash/src/crypto/sha512.cpp",
|
"/depend/zcash/src/crypto/sha256.h",
|
||||||
"/depend/zcash/src/crypto/hmac_sha512.cpp",
|
"/depend/zcash/src/hash.h",
|
||||||
|
"/depend/zcash/src/key_constants.h",
|
||||||
|
"/depend/zcash/src/prevector.h",
|
||||||
|
"/depend/zcash/src/primitives/transaction.h",
|
||||||
|
"/depend/zcash/src/pubkey.cpp",
|
||||||
|
"/depend/zcash/src/pubkey.h",
|
||||||
"/depend/zcash/src/script/interpreter.cpp",
|
"/depend/zcash/src/script/interpreter.cpp",
|
||||||
"/depend/zcash/src/script/script.cpp",
|
"/depend/zcash/src/script/interpreter.h",
|
||||||
"/depend/zcash/src/script/script_error.cpp",
|
"/depend/zcash/src/script/script_error.cpp",
|
||||||
"/depend/zcash/src/",
|
"/depend/zcash/src/script/script_error.h",
|
||||||
"/depend/zcash/src/rust/include/",
|
"/depend/zcash/src/script/script.cpp",
|
||||||
"/depend/zcash/src/secp256k1/include/",
|
"/depend/zcash/src/script/script.h",
|
||||||
|
"/depend/zcash/src/script/zcash_script.cpp",
|
||||||
|
"/depend/zcash/src/script/zcash_script.h",
|
||||||
"/depend/zcash/src/secp256k1/",
|
"/depend/zcash/src/secp256k1/",
|
||||||
"/depend/zcash/src/support/cleanse.cpp",
|
"/depend/zcash/src/serialize.h",
|
||||||
"/depend/zcash/src/support/cleanse.h",
|
"/depend/zcash/src/tinyformat.h",
|
||||||
"/depend/zcash/src/rust/gen/",
|
"/depend/zcash/src/uint256.cpp",
|
||||||
"/depend/expected/include/",
|
"/depend/zcash/src/uint256.h",
|
||||||
|
"/depend/zcash/src/util/strencodings.cpp",
|
||||||
|
"/depend/zcash/src/util/strencodings.h",
|
||||||
|
"/depend/zcash/src/version.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -49,35 +60,6 @@ path = "src/lib.rs"
|
||||||
external-secp = []
|
external-secp = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# All these dependencies must match the versions in:
|
|
||||||
# https://github.com/zcash/zcash/blob/<git subtree version>/Cargo.toml
|
|
||||||
bellman = "0.14"
|
|
||||||
blake2b_simd = "1"
|
|
||||||
blake2s_simd = "1"
|
|
||||||
bls12_381 = "0.8"
|
|
||||||
byteorder = "1"
|
|
||||||
crossbeam-channel = "0.5"
|
|
||||||
cxx = { version = "=1.0.113", features = ["c++17"] }
|
|
||||||
group = "0.13"
|
|
||||||
incrementalmerkletree = "0.5"
|
|
||||||
jubjub = "0.10"
|
|
||||||
libc = "0.2"
|
|
||||||
memuse = "0.2"
|
|
||||||
metrics = "0.21"
|
|
||||||
orchard = "0.7"
|
|
||||||
rand_core = "0.6"
|
|
||||||
rayon = "1.5"
|
|
||||||
redjubjub = "0.7"
|
|
||||||
sapling = { package = "sapling-crypto", version = "0.1", features = ["temporary-zcashd"] }
|
|
||||||
subtle = "2.2"
|
|
||||||
tracing = "0.1"
|
|
||||||
zcash_address = "0.3"
|
|
||||||
zcash_encoding = "0.2"
|
|
||||||
zcash_note_encryption = "0.4"
|
|
||||||
zcash_primitives = { version = "=0.14.0", features = ["temporary-zcashd", "transparent-inputs"] }
|
|
||||||
zcash_proofs = { version = "=0.14.0", features = ["directories"] }
|
|
||||||
bridgetree = "0.4"
|
|
||||||
rand = "0.8"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
# The `bindgen` dependency should automatically upgrade to match the version used by zebra-state's `rocksdb` dependency in:
|
# The `bindgen` dependency should automatically upgrade to match the version used by zebra-state's `rocksdb` dependency in:
|
||||||
|
@ -90,9 +72,6 @@ bindgen = ">= 0.64.0"
|
||||||
# so they are configured to automatically upgrade to match Zebra.
|
# so they are configured to automatically upgrade to match Zebra.
|
||||||
# But we try to use the latest versions here, to catch any bugs in `zcash_script`'s CI.
|
# But we try to use the latest versions here, to catch any bugs in `zcash_script`'s CI.
|
||||||
cc = { version = "1.0.94", features = ["parallel"] }
|
cc = { version = "1.0.94", features = ["parallel"] }
|
||||||
# Treat minor versions with a zero major version as compatible (cargo doesn't by default).
|
|
||||||
cxx-gen = ">= 0.7.107"
|
|
||||||
syn = { version = "1.0.109", features = ["full", "printing"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# These dependencies are shared with a lot of other Zebra dependencies.
|
# These dependencies are shared with a lot of other Zebra dependencies.
|
||||||
|
@ -101,8 +80,6 @@ syn = { version = "1.0.109", features = ["full", "printing"] }
|
||||||
# Treat minor versions with a zero major version as compatible (cargo doesn't by default).
|
# Treat minor versions with a zero major version as compatible (cargo doesn't by default).
|
||||||
hex = ">= 0.4.3"
|
hex = ">= 0.4.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
incrementalmerkletree = { version = "0.5", features = ["test-dependencies"] }
|
|
||||||
zcash_primitives = { version = "=0.14.0", features = ["temporary-zcashd", "transparent-inputs", "test-dependencies"] }
|
|
||||||
|
|
||||||
[[package.metadata.release.pre-release-replacements]]
|
[[package.metadata.release.pre-release-replacements]]
|
||||||
file = "CHANGELOG.md"
|
file = "CHANGELOG.md"
|
||||||
|
|
145
build.rs
145
build.rs
|
@ -1,8 +1,6 @@
|
||||||
//! Build script for zcash_script.
|
//! Build script for zcash_script.
|
||||||
|
|
||||||
use std::{env, fmt, fs, io::Read, path::PathBuf};
|
use std::{env, fmt, path::PathBuf};
|
||||||
|
|
||||||
use syn::__private::ToTokens;
|
|
||||||
|
|
||||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
@ -47,125 +45,8 @@ fn bindgen_headers() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use cxx_gen to generate headers and source files for FFI bindings,
|
|
||||||
/// just like zcash does (see depend/zcash/src/Makefile.am).
|
|
||||||
/// (Note that zcash uses the cxxbridge-cmd binary, while we use the
|
|
||||||
/// cxx_gen library, but the result is the same.)
|
|
||||||
///
|
|
||||||
/// We could use [`cxx_build`](https://cxx.rs/tutorial.html#compiling-the-c-code-with-cargo)
|
|
||||||
/// to do this automatically. But zcash uses the
|
|
||||||
/// [manual approach](https://cxx.rs/tutorial.html#c-generated-code) which
|
|
||||||
/// creates the headers with non-standard names and paths (e.g. "blake2b.h",
|
|
||||||
/// instead of "blake2b.rs.h" which what cxx_build would create). This would
|
|
||||||
/// requires us to rename/link files which is awkward.
|
|
||||||
///
|
|
||||||
/// Note that we must generate the files in the target dir (OUT_DIR) and not in
|
|
||||||
/// any source folder, because `cargo package` does not allow that.
|
|
||||||
/// (This is in contrast to zcash which generates in `depend/zcash/src/rust/gen/`)
|
|
||||||
fn gen_cxxbridge() -> Result<()> {
|
|
||||||
let out_path = env::var("OUT_DIR").map_err(Error::Env)?;
|
|
||||||
let out_path = PathBuf::from(out_path).join("gen");
|
|
||||||
let src_out_path = PathBuf::from(&out_path).join("src");
|
|
||||||
let header_out_path = PathBuf::from(&out_path).join("include").join("rust");
|
|
||||||
|
|
||||||
// These must match `CXXBRIDGE_RS` in depend/zcash/src/Makefile.am
|
|
||||||
let filenames = [
|
|
||||||
"blake2b",
|
|
||||||
"ed25519",
|
|
||||||
"equihash",
|
|
||||||
"streams",
|
|
||||||
"bridge",
|
|
||||||
"sapling/zip32",
|
|
||||||
];
|
|
||||||
|
|
||||||
// The output folder must exist
|
|
||||||
fs::create_dir_all(&src_out_path).unwrap();
|
|
||||||
fs::create_dir_all(&header_out_path).unwrap();
|
|
||||||
|
|
||||||
// Generate the generic header file
|
|
||||||
fs::write(header_out_path.join("cxx.h"), cxx_gen::HEADER).unwrap();
|
|
||||||
|
|
||||||
// Generate the source and header for each bridge file
|
|
||||||
for filename in filenames {
|
|
||||||
println!(
|
|
||||||
"cargo:rerun-if-changed=depend/zcash/src/rust/src/{}.rs",
|
|
||||||
filename
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut file =
|
|
||||||
fs::File::open(format!("depend/zcash/src/rust/src/{}.rs", filename).as_str()).unwrap();
|
|
||||||
let mut content = String::new();
|
|
||||||
file.read_to_string(&mut content).unwrap();
|
|
||||||
|
|
||||||
let ast = syn::parse_file(&content).unwrap();
|
|
||||||
let token_stream = ast.to_token_stream();
|
|
||||||
let mut opt = cxx_gen::Opt::default();
|
|
||||||
opt.include.push(cxx_gen::Include {
|
|
||||||
path: "rust/cxx.h".to_string(),
|
|
||||||
kind: cxx_gen::IncludeKind::Quoted,
|
|
||||||
});
|
|
||||||
let output = cxx_gen::generate_header_and_cc(token_stream, &opt).unwrap_or_else(|err| {
|
|
||||||
panic!(
|
|
||||||
"invalid bridge file {filename}: {err}. Try updating `filenames` to match zcashd"
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let header_path = header_out_path.join(format!("{}.h", filename));
|
|
||||||
// Create output dir if does not exist (since `filename` can have a subdir)
|
|
||||||
fs::create_dir_all(header_path.parent().unwrap()).unwrap();
|
|
||||||
fs::write(header_path, output.header).unwrap();
|
|
||||||
|
|
||||||
let src_path = src_out_path.join(format!("{}.cpp", filename));
|
|
||||||
// Create output dir if does not exist (since `filename` can have a subdir)
|
|
||||||
fs::create_dir_all(src_path.parent().unwrap()).unwrap();
|
|
||||||
fs::write(src_path, output.implementation).unwrap();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
bindgen_headers()?;
|
bindgen_headers()?;
|
||||||
gen_cxxbridge()?;
|
|
||||||
|
|
||||||
let rust_path = env::var("OUT_DIR").map_err(Error::Env)?;
|
|
||||||
let rust_path = PathBuf::from(rust_path).join("rust");
|
|
||||||
|
|
||||||
// We want to compile `depend/zcash/src/rust/src/sapling.rs`, which we used
|
|
||||||
// to do in `src/sapling.rs` by just including it. However, now that it has
|
|
||||||
// submodules, that approach doesn't work because for some reason Rust
|
|
||||||
// searches for the submodules in `depend/zcash/src/rust/src/` instead of
|
|
||||||
// `depend/zcash/src/rust/src/sapling/` where they are located. This can
|
|
||||||
// be solved if `depend/zcash/src/rust/src/sapling.rs` is renamed to
|
|
||||||
// `depend/zcash/src/rust/src/sapling/mod.rs`. But we can't do that directly
|
|
||||||
// because we can't change the source tree inside `build.rs`. Therefore we
|
|
||||||
// copy the required files to OUT_DIR, with a renamed sapling.rs, and include
|
|
||||||
// the copied file instead (see src/sapling.rs).
|
|
||||||
// See also https://stackoverflow.com/questions/77310390/how-to-include-a-source-file-that-has-modules
|
|
||||||
fs::create_dir_all(rust_path.join("sapling")).unwrap();
|
|
||||||
for filename in &["sapling.rs", "sapling/spec.rs", "sapling/zip32.rs"] {
|
|
||||||
println!(
|
|
||||||
"cargo:rerun-if-changed=depend/zcash/src/rust/src/{}.rs",
|
|
||||||
filename
|
|
||||||
);
|
|
||||||
}
|
|
||||||
fs::copy(
|
|
||||||
"depend/zcash/src/rust/src/sapling.rs",
|
|
||||||
rust_path.join("sapling/mod.rs"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
fs::copy(
|
|
||||||
"depend/zcash/src/rust/src/sapling/spec.rs",
|
|
||||||
rust_path.join("sapling/spec.rs"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
fs::copy(
|
|
||||||
"depend/zcash/src/rust/src/sapling/zip32.rs",
|
|
||||||
rust_path.join("sapling/zip32.rs"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let gen_path = env::var("OUT_DIR").map_err(Error::Env)?;
|
|
||||||
let gen_path = PathBuf::from(gen_path).join("gen");
|
|
||||||
|
|
||||||
let target = env::var("TARGET").expect("TARGET was not set");
|
let target = env::var("TARGET").expect("TARGET was not set");
|
||||||
let mut base_config = cc::Build::new();
|
let mut base_config = cc::Build::new();
|
||||||
|
@ -177,7 +58,6 @@ fn main() -> Result<()> {
|
||||||
.include("depend/zcash/src/rust/include/")
|
.include("depend/zcash/src/rust/include/")
|
||||||
.include("depend/zcash/src/secp256k1/include/")
|
.include("depend/zcash/src/secp256k1/include/")
|
||||||
.include("depend/expected/include/")
|
.include("depend/expected/include/")
|
||||||
.include(&gen_path.join("include"))
|
|
||||||
.flag_if_supported("-Wno-implicit-fallthrough")
|
.flag_if_supported("-Wno-implicit-fallthrough")
|
||||||
.flag_if_supported("-Wno-catch-value")
|
.flag_if_supported("-Wno-catch-value")
|
||||||
.flag_if_supported("-Wno-reorder")
|
.flag_if_supported("-Wno-reorder")
|
||||||
|
@ -201,30 +81,17 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
base_config
|
base_config
|
||||||
.file("depend/zcash/src/script/zcash_script.cpp")
|
|
||||||
.file("depend/zcash/src/util/strencodings.cpp")
|
|
||||||
.file("depend/zcash/src/amount.cpp")
|
.file("depend/zcash/src/amount.cpp")
|
||||||
.file("depend/zcash/src/uint256.cpp")
|
|
||||||
.file("depend/zcash/src/pubkey.cpp")
|
|
||||||
.file("depend/zcash/src/hash.cpp")
|
|
||||||
.file("depend/zcash/src/streams_rust.cpp")
|
|
||||||
.file("depend/zcash/src/zip317.cpp")
|
|
||||||
.file("depend/zcash/src/primitives/transaction.cpp")
|
|
||||||
.file("depend/zcash/src/crypto/ripemd160.cpp")
|
.file("depend/zcash/src/crypto/ripemd160.cpp")
|
||||||
.file("depend/zcash/src/crypto/sha1.cpp")
|
.file("depend/zcash/src/crypto/sha1.cpp")
|
||||||
.file("depend/zcash/src/crypto/sha256.cpp")
|
.file("depend/zcash/src/crypto/sha256.cpp")
|
||||||
.file("depend/zcash/src/crypto/sha512.cpp")
|
.file("depend/zcash/src/pubkey.cpp")
|
||||||
.file("depend/zcash/src/crypto/hmac_sha512.cpp")
|
|
||||||
.file("depend/zcash/src/script/interpreter.cpp")
|
.file("depend/zcash/src/script/interpreter.cpp")
|
||||||
.file("depend/zcash/src/script/script.cpp")
|
|
||||||
.file("depend/zcash/src/script/script_error.cpp")
|
.file("depend/zcash/src/script/script_error.cpp")
|
||||||
.file("depend/zcash/src/support/cleanse.cpp")
|
.file("depend/zcash/src/script/script.cpp")
|
||||||
.file("depend/zcash/src/zcash/cache.cpp")
|
.file("depend/zcash/src/script/zcash_script.cpp")
|
||||||
// A subset of the files generated by gen_cxxbridge
|
.file("depend/zcash/src/uint256.cpp")
|
||||||
// which are required by zcash_script.
|
.file("depend/zcash/src/util/strencodings.cpp")
|
||||||
.file(gen_path.join("src/blake2b.cpp"))
|
|
||||||
.file(gen_path.join("src/bridge.cpp"))
|
|
||||||
.file(gen_path.join("src/streams.cpp"))
|
|
||||||
.compile("libzcash_script.a");
|
.compile("libzcash_script.a");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -16,10 +16,6 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <rust/blake2b.h>
|
|
||||||
#include <rust/constants.h>
|
|
||||||
#include <rust/cxx.h>
|
|
||||||
|
|
||||||
typedef uint256 ChainCode;
|
typedef uint256 ChainCode;
|
||||||
|
|
||||||
/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
|
/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
|
||||||
|
@ -157,50 +153,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** A writer stream (for serialization) that computes a 256-bit BLAKE2b hash. */
|
|
||||||
class CBLAKE2bWriter
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
rust::Box<blake2b::State> state;
|
|
||||||
|
|
||||||
public:
|
|
||||||
int nType;
|
|
||||||
int nVersion;
|
|
||||||
|
|
||||||
CBLAKE2bWriter(int nTypeIn, int nVersionIn, const unsigned char* personal) :
|
|
||||||
nType(nTypeIn),
|
|
||||||
nVersion(nVersionIn),
|
|
||||||
state(blake2b::init(32, {personal, blake2b::PERSONALBYTES})) {}
|
|
||||||
|
|
||||||
int GetType() const { return nType; }
|
|
||||||
int GetVersion() const { return nVersion; }
|
|
||||||
|
|
||||||
void write_u8(const unsigned char* pch, size_t size)
|
|
||||||
{
|
|
||||||
state->update({pch, size});
|
|
||||||
}
|
|
||||||
|
|
||||||
CBLAKE2bWriter& write(const char *pch, size_t size) {
|
|
||||||
state->update({(const unsigned char*)pch, size});
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidates the object
|
|
||||||
uint256 GetHash() {
|
|
||||||
uint256 result;
|
|
||||||
state->finalize({result.begin(), result.size()});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
CBLAKE2bWriter& operator<<(const T& obj) {
|
|
||||||
// Serialize to this stream
|
|
||||||
::Serialize(*this, obj);
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** Compute the 256-bit hash of an object's serialization. */
|
/** Compute the 256-bit hash of an object's serialization. */
|
||||||
template<typename T>
|
template<typename T>
|
||||||
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
|
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
|
||||||
|
|
|
@ -41,6 +41,8 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS
|
||||||
return secp256k1_ecdsa_verify(secp256k1_context_static, &sig, hash.begin(), &pubkey);
|
return secp256k1_ecdsa_verify(secp256k1_context_static, &sig, hash.begin(), &pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
|
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
|
||||||
if (vchSig.size() != COMPACT_SIGNATURE_SIZE)
|
if (vchSig.size() != COMPACT_SIGNATURE_SIZE)
|
||||||
return false;
|
return false;
|
||||||
|
@ -138,6 +140,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
|
||||||
out.nChild = _nChild;
|
out.nChild = _nChild;
|
||||||
return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode);
|
return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) {
|
/* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) {
|
||||||
secp256k1_ecdsa_signature sig;
|
secp256k1_ecdsa_signature sig;
|
||||||
|
@ -149,6 +152,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
|
||||||
return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_static, NULL, &sig));
|
return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_static, NULL, &sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* static */ std::optional<CChainablePubKey> CChainablePubKey::FromParts(ChainCode chaincode, CPubKey pubkey) {
|
/* static */ std::optional<CChainablePubKey> CChainablePubKey::FromParts(ChainCode chaincode, CPubKey pubkey) {
|
||||||
if (pubkey.IsCompressed()) {
|
if (pubkey.IsCompressed()) {
|
||||||
return CChainablePubKey(chaincode, pubkey);
|
return CChainablePubKey(chaincode, pubkey);
|
||||||
|
@ -156,3 +160,5 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -173,8 +173,10 @@ public:
|
||||||
return size() > 0;
|
return size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
//! fully validate whether this is a valid public key (more expensive than IsValid())
|
//! fully validate whether this is a valid public key (more expensive than IsValid())
|
||||||
bool IsFullyValid() const;
|
bool IsFullyValid() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
//! Check whether this is a compressed public key.
|
//! Check whether this is a compressed public key.
|
||||||
bool IsCompressed() const
|
bool IsCompressed() const
|
||||||
|
@ -193,6 +195,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static bool CheckLowS(const std::vector<unsigned char>& vchSig);
|
static bool CheckLowS(const std::vector<unsigned char>& vchSig);
|
||||||
|
|
||||||
|
#if 0
|
||||||
//! Recover a public key from a compact signature.
|
//! Recover a public key from a compact signature.
|
||||||
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
|
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
|
||||||
|
|
||||||
|
@ -201,8 +204,10 @@ public:
|
||||||
|
|
||||||
//! Derive BIP32 child pubkey.
|
//! Derive BIP32 child pubkey.
|
||||||
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
|
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
class CChainablePubKey {
|
class CChainablePubKey {
|
||||||
private:
|
private:
|
||||||
ChainCode chaincode;
|
ChainCode chaincode;
|
||||||
|
@ -303,5 +308,6 @@ struct CExtPubKey {
|
||||||
Decode(code);
|
Decode(code);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // BITCOIN_PUBKEY_H
|
#endif // BITCOIN_PUBKEY_H
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include "interpreter.h"
|
#include "interpreter.h"
|
||||||
|
|
||||||
#include "consensus/upgrades.h"
|
|
||||||
#include "primitives/transaction.h"
|
|
||||||
#include "crypto/ripemd160.h"
|
#include "crypto/ripemd160.h"
|
||||||
#include "crypto/sha1.h"
|
#include "crypto/sha1.h"
|
||||||
#include "crypto/sha256.h"
|
#include "crypto/sha256.h"
|
||||||
|
@ -15,10 +13,6 @@
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
||||||
#include <librustzcash.h>
|
|
||||||
#include <rust/constants.h>
|
|
||||||
#include <rust/transaction.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef vector<unsigned char> valtype;
|
typedef vector<unsigned char> valtype;
|
||||||
|
@ -962,428 +956,15 @@ bool EvalScript(
|
||||||
return set_success(serror);
|
return set_success(serror);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
bool CallbackTransactionSignatureChecker::VerifySignature(
|
||||||
|
const std::vector<unsigned char>& vchSig,
|
||||||
/**
|
const CPubKey& pubkey,
|
||||||
* Wrapper that serializes like CTransaction, but with the modifications
|
const uint256& sighash) const
|
||||||
* required for the signature hash done in-place
|
|
||||||
*/
|
|
||||||
class CTransactionSignatureSerializer {
|
|
||||||
private:
|
|
||||||
const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized)
|
|
||||||
const CScript& scriptCode; //!< output script being consumed
|
|
||||||
const unsigned int nIn; //!< input index of txTo being signed
|
|
||||||
const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set
|
|
||||||
const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE
|
|
||||||
const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE
|
|
||||||
|
|
||||||
public:
|
|
||||||
CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
|
|
||||||
txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
|
|
||||||
fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
|
|
||||||
fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
|
|
||||||
fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {}
|
|
||||||
|
|
||||||
/** Serialize the passed scriptCode */
|
|
||||||
template<typename S>
|
|
||||||
void SerializeScriptCode(S &s) const {
|
|
||||||
auto size = scriptCode.size();
|
|
||||||
::WriteCompactSize(s, size);
|
|
||||||
s.write((char*)&scriptCode.begin()[0], size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize an input of txTo */
|
|
||||||
template<typename S>
|
|
||||||
void SerializeInput(S &s, unsigned int nInput) const {
|
|
||||||
// In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
|
|
||||||
if (fAnyoneCanPay)
|
|
||||||
nInput = nIn;
|
|
||||||
// Serialize the prevout
|
|
||||||
::Serialize(s, txTo.vin[nInput].prevout);
|
|
||||||
// Serialize the script
|
|
||||||
assert(nInput != NOT_AN_INPUT);
|
|
||||||
if (nInput != nIn)
|
|
||||||
// Blank out other inputs' signatures
|
|
||||||
::Serialize(s, CScriptBase());
|
|
||||||
else
|
|
||||||
SerializeScriptCode(s);
|
|
||||||
// Serialize the nSequence
|
|
||||||
if (nInput != nIn && (fHashSingle || fHashNone))
|
|
||||||
// let the others update at will
|
|
||||||
::Serialize(s, (int)0);
|
|
||||||
else
|
|
||||||
::Serialize(s, txTo.vin[nInput].nSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize an output of txTo */
|
|
||||||
template<typename S>
|
|
||||||
void SerializeOutput(S &s, unsigned int nOutput) const {
|
|
||||||
if (fHashSingle && nOutput != nIn)
|
|
||||||
// Do not lock-in the txout payee at other indices as txin
|
|
||||||
::Serialize(s, CTxOut());
|
|
||||||
else
|
|
||||||
::Serialize(s, txTo.vout[nOutput]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Serialize txTo */
|
|
||||||
template<typename S>
|
|
||||||
void Serialize(S &s) const {
|
|
||||||
// Serialize nVersion
|
|
||||||
::Serialize(s, txTo.nVersion);
|
|
||||||
// Serialize vin
|
|
||||||
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
|
|
||||||
::WriteCompactSize(s, nInputs);
|
|
||||||
for (unsigned int nInput = 0; nInput < nInputs; nInput++)
|
|
||||||
SerializeInput(s, nInput);
|
|
||||||
// Serialize vout
|
|
||||||
unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
|
|
||||||
::WriteCompactSize(s, nOutputs);
|
|
||||||
for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
|
|
||||||
SerializeOutput(s, nOutput);
|
|
||||||
// Serialize nLockTime
|
|
||||||
::Serialize(s, txTo.nLockTime);
|
|
||||||
|
|
||||||
// Serialize vJoinSplit
|
|
||||||
if (txTo.nVersion >= 2) {
|
|
||||||
//
|
|
||||||
// SIGHASH_* functions will hash portions of
|
|
||||||
// the transaction for use in signatures. This
|
|
||||||
// keeps the JoinSplit cryptographically bound
|
|
||||||
// to the transaction.
|
|
||||||
//
|
|
||||||
::Serialize(s, txTo.vJoinSplit);
|
|
||||||
if (txTo.vJoinSplit.size() > 0) {
|
|
||||||
::Serialize(s, txTo.joinSplitPubKey);
|
|
||||||
|
|
||||||
CTransaction::joinsplit_sig_t nullSig = {};
|
|
||||||
::Serialize(s, nullSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char ZCASH_PREVOUTS_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','P','r','e','v','o','u','t','H','a','s','h'};
|
|
||||||
const unsigned char ZCASH_SEQUENCE_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','S','e','q','u','e','n','c','H','a','s','h'};
|
|
||||||
const unsigned char ZCASH_OUTPUTS_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'};
|
|
||||||
const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'};
|
|
||||||
const unsigned char ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','S','S','p','e','n','d','s','H','a','s','h'};
|
|
||||||
const unsigned char ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION[blake2b::PERSONALBYTES] =
|
|
||||||
{'Z','c','a','s','h','S','O','u','t','p','u','t','H','a','s','h'};
|
|
||||||
|
|
||||||
uint256 GetPrevoutHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION);
|
|
||||||
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
|
||||||
ss << txTo.vin[n].prevout;
|
|
||||||
}
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetSequenceHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SEQUENCE_HASH_PERSONALIZATION);
|
|
||||||
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
|
||||||
ss << txTo.vin[n].nSequence;
|
|
||||||
}
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetOutputsHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION);
|
|
||||||
for (unsigned int n = 0; n < txTo.vout.size(); n++) {
|
|
||||||
ss << txTo.vout[n];
|
|
||||||
}
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetJoinSplitsHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, static_cast<int>(txTo.GetHeader()), ZCASH_JOINSPLITS_HASH_PERSONALIZATION);
|
|
||||||
for (unsigned int n = 0; n < txTo.vJoinSplit.size(); n++) {
|
|
||||||
ss << txTo.vJoinSplit[n];
|
|
||||||
}
|
|
||||||
ss << txTo.joinSplitPubKey;
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetShieldedSpendsHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION);
|
|
||||||
for (const auto& spend : txTo.GetSaplingSpends()) {
|
|
||||||
ss << spend.cv();
|
|
||||||
ss << spend.anchor();
|
|
||||||
ss << spend.nullifier();
|
|
||||||
ss << spend.rk();
|
|
||||||
ss << spend.zkproof();
|
|
||||||
}
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetShieldedOutputsHash(const CTransaction& txTo) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION);
|
|
||||||
auto ssRs = ToRustStream(ss);
|
|
||||||
for (const auto& output : txTo.GetSaplingOutputs()) {
|
|
||||||
output.serialize_v4(*ssRs);
|
|
||||||
}
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anon namespace
|
|
||||||
|
|
||||||
PrecomputedTransactionData::PrecomputedTransactionData(
|
|
||||||
const CTransaction& txTo,
|
|
||||||
const std::vector<CTxOut>& allPrevOutputs) :
|
|
||||||
preTx(nullptr, zcash_transaction_precomputed_free)
|
|
||||||
{
|
|
||||||
CDataStream sAllPrevOutputs(SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
sAllPrevOutputs << allPrevOutputs;
|
|
||||||
SetPrecomputed(
|
|
||||||
txTo,
|
|
||||||
reinterpret_cast<const unsigned char*>(sAllPrevOutputs.data()),
|
|
||||||
sAllPrevOutputs.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
PrecomputedTransactionData::PrecomputedTransactionData(
|
|
||||||
const CTransaction& txTo,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
size_t allPrevOutputsLen) :
|
|
||||||
preTx(nullptr, zcash_transaction_precomputed_free)
|
|
||||||
{
|
|
||||||
SetPrecomputed(txTo, allPrevOutputs, allPrevOutputsLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrecomputedTransactionData::SetPrecomputed(
|
|
||||||
const CTransaction& txTo,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
size_t allPrevOutputsLen)
|
|
||||||
{
|
|
||||||
bool isOverwinterV3 =
|
|
||||||
txTo.fOverwintered &&
|
|
||||||
txTo.nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
|
||||||
txTo.nVersion == OVERWINTER_TX_VERSION;
|
|
||||||
bool isSaplingV4 =
|
|
||||||
txTo.fOverwintered &&
|
|
||||||
txTo.nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
|
||||||
txTo.nVersion == SAPLING_TX_VERSION;
|
|
||||||
|
|
||||||
if (!txTo.fOverwintered || isOverwinterV3 || isSaplingV4) {
|
|
||||||
hashPrevouts = GetPrevoutHash(txTo);
|
|
||||||
hashSequence = GetSequenceHash(txTo);
|
|
||||||
hashOutputs = GetOutputsHash(txTo);
|
|
||||||
hashJoinSplits = GetJoinSplitsHash(txTo);
|
|
||||||
hashShieldedSpends = GetShieldedSpendsHash(txTo);
|
|
||||||
hashShieldedOutputs = GetShieldedOutputsHash(txTo);
|
|
||||||
} else {
|
|
||||||
// TODO: If we already have this serialized, use it.
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
ss << txTo;
|
|
||||||
preTx.reset(zcash_transaction_precomputed_init(
|
|
||||||
reinterpret_cast<const unsigned char*>(ss.data()),
|
|
||||||
ss.size(), allPrevOutputs, allPrevOutputsLen));
|
|
||||||
if (preTx == nullptr) {
|
|
||||||
throw std::ios_base::failure("Invalid arguments to PrecomputedTransactionData");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SigVersion SignatureHashVersion(const CTransaction& txTo)
|
|
||||||
{
|
|
||||||
if (txTo.fOverwintered) {
|
|
||||||
if (txTo.nVersionGroupId == ZIP225_VERSION_GROUP_ID) {
|
|
||||||
return SIGVERSION_ZIP244;
|
|
||||||
} else if (txTo.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
|
||||||
return SIGVERSION_SAPLING;
|
|
||||||
} else {
|
|
||||||
return SIGVERSION_OVERWINTER;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return SIGVERSION_SPROUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 SignatureHash(
|
|
||||||
const CScript& scriptCode,
|
|
||||||
const CTransaction& txTo,
|
|
||||||
unsigned int nIn,
|
|
||||||
int nHashType,
|
|
||||||
const CAmount& amount,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
const PrecomputedTransactionData& txdata)
|
|
||||||
{
|
|
||||||
if (nIn >= txTo.vin.size() && nIn != NOT_AN_INPUT) {
|
|
||||||
// nIn out of range
|
|
||||||
throw logic_error("input index is out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sigversion = SignatureHashVersion(txTo);
|
|
||||||
|
|
||||||
if (sigversion == SIGVERSION_ZIP244) {
|
|
||||||
// ZIP 244, S.2: transparent_sig_digest
|
|
||||||
//
|
|
||||||
// If we are producing a hash for either a coinbase transaction, or a
|
|
||||||
// non-coinbase transaction that has no transparent inputs, the value of
|
|
||||||
// transparent_sig_digest is identical to the value specified in section
|
|
||||||
// T.2.
|
|
||||||
//
|
|
||||||
// https://zips.z.cash/zip-0244#s-2-transparent-sig-digest
|
|
||||||
if (txTo.IsCoinBase() || txTo.vin.empty()) {
|
|
||||||
// This situation only applies to shielded signatures.
|
|
||||||
assert(nIn == NOT_AN_INPUT);
|
|
||||||
assert(nHashType == SIGHASH_ALL);
|
|
||||||
// The signature digest is just the txid! No need to cross the FFI.
|
|
||||||
return txTo.GetHash();
|
|
||||||
} else {
|
|
||||||
// S.2a: hash_type
|
|
||||||
//
|
|
||||||
// The following restrictions apply, which cause validation failure
|
|
||||||
// if violated:
|
|
||||||
// - Using any undefined hash_type (not 0x01, 0x02, 0x03, 0x81,
|
|
||||||
// 0x82, or 0x83).
|
|
||||||
// - Using SIGHASH_SINGLE without a "corresponding output" (an
|
|
||||||
// output with the same index as the input being verified).
|
|
||||||
switch (nHashType) {
|
|
||||||
case SIGHASH_SINGLE:
|
|
||||||
case SIGHASH_ANYONECANPAY | SIGHASH_SINGLE:
|
|
||||||
if (nIn >= txTo.vout.size()) {
|
|
||||||
throw std::logic_error(
|
|
||||||
"ZIP 244: Used SIGHASH_SINGLE without a corresponding output");
|
|
||||||
}
|
|
||||||
case SIGHASH_ALL:
|
|
||||||
case SIGHASH_NONE:
|
|
||||||
case SIGHASH_ANYONECANPAY | SIGHASH_ALL:
|
|
||||||
case SIGHASH_ANYONECANPAY | SIGHASH_NONE:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw std::logic_error("ZIP 244: Used undefined hash_type");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The amount parameter is ignored; we extract it from allPrevOutputs.
|
|
||||||
// - Note to future developers: if we ever replace the C++ logic for
|
|
||||||
// pre-v5 transactions with Rust logic, make sure signrawtransaction
|
|
||||||
// is updated to know about it!
|
|
||||||
// The consensusBranchId parameter is ignored; we use the value stored
|
|
||||||
// in the transaction itself.
|
|
||||||
uint256 hash;
|
|
||||||
if (!zcash_transaction_zip244_signature_digest(
|
|
||||||
txdata.preTx.get(),
|
|
||||||
nHashType,
|
|
||||||
nIn,
|
|
||||||
hash.begin()))
|
|
||||||
{
|
|
||||||
throw std::logic_error("We should not reach here.");
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
} else if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {
|
|
||||||
uint256 hashPrevouts;
|
|
||||||
uint256 hashSequence;
|
|
||||||
uint256 hashOutputs;
|
|
||||||
uint256 hashJoinSplits;
|
|
||||||
uint256 hashShieldedSpends;
|
|
||||||
uint256 hashShieldedOutputs;
|
|
||||||
|
|
||||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
|
||||||
hashPrevouts = txdata.hashPrevouts;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
|
||||||
hashSequence = txdata.hashSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
|
||||||
hashOutputs = txdata.hashOutputs;
|
|
||||||
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION);
|
|
||||||
ss << txTo.vout[nIn];
|
|
||||||
hashOutputs = ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!txTo.vJoinSplit.empty()) {
|
|
||||||
hashJoinSplits = txdata.hashJoinSplits;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txTo.GetSaplingSpendsCount() > 0) {
|
|
||||||
hashShieldedSpends = txdata.hashShieldedSpends;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txTo.GetSaplingOutputsCount() > 0) {
|
|
||||||
hashShieldedOutputs = txdata.hashShieldedOutputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t leConsensusBranchId = htole32(consensusBranchId);
|
|
||||||
unsigned char personalization[16] = {};
|
|
||||||
memcpy(personalization, "ZcashSigHash", 12);
|
|
||||||
memcpy(personalization+12, &leConsensusBranchId, 4);
|
|
||||||
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, personalization);
|
|
||||||
// Header
|
|
||||||
ss << txTo.GetHeader();
|
|
||||||
// Version group ID
|
|
||||||
ss << txTo.nVersionGroupId;
|
|
||||||
// Input prevouts/nSequence (none/all, depending on flags)
|
|
||||||
ss << hashPrevouts;
|
|
||||||
ss << hashSequence;
|
|
||||||
// Outputs (none/one/all, depending on flags)
|
|
||||||
ss << hashOutputs;
|
|
||||||
// JoinSplits
|
|
||||||
ss << hashJoinSplits;
|
|
||||||
if (sigversion == SIGVERSION_SAPLING) {
|
|
||||||
// Spend descriptions
|
|
||||||
ss << hashShieldedSpends;
|
|
||||||
// Output descriptions
|
|
||||||
ss << hashShieldedOutputs;
|
|
||||||
}
|
|
||||||
// Locktime
|
|
||||||
ss << txTo.nLockTime;
|
|
||||||
// Expiry height
|
|
||||||
ss << txTo.nExpiryHeight;
|
|
||||||
if (sigversion == SIGVERSION_SAPLING) {
|
|
||||||
// Sapling value balance
|
|
||||||
ss << txTo.GetValueBalanceSapling();
|
|
||||||
}
|
|
||||||
// Sighash type
|
|
||||||
ss << nHashType;
|
|
||||||
|
|
||||||
// If this hash is for a transparent input signature
|
|
||||||
// (i.e. not for txTo.joinSplitSig):
|
|
||||||
if (nIn != NOT_AN_INPUT){
|
|
||||||
// The input being signed (replacing the scriptSig with scriptCode + amount)
|
|
||||||
// The prevout may already be contained in hashPrevout, and the nSequence
|
|
||||||
// may already be contained in hashSequence.
|
|
||||||
ss << txTo.vin[nIn].prevout;
|
|
||||||
ss << static_cast<const CScriptBase&>(scriptCode);
|
|
||||||
ss << amount;
|
|
||||||
ss << txTo.vin[nIn].nSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for invalid use of SIGHASH_SINGLE
|
|
||||||
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
|
|
||||||
if (nIn >= txTo.vout.size()) {
|
|
||||||
// nOut out of range
|
|
||||||
throw logic_error("no matching output for SIGHASH_SINGLE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper to serialize only the necessary parts of the transaction being signed
|
|
||||||
CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
|
|
||||||
|
|
||||||
// Serialize and hash
|
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
|
||||||
ss << txTmp << nHashType;
|
|
||||||
return ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TransactionSignatureChecker::VerifySignature(
|
|
||||||
const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
|
||||||
{
|
{
|
||||||
return pubkey.Verify(sighash, vchSig);
|
return pubkey.Verify(sighash, vchSig);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransactionSignatureChecker::CheckSig(
|
bool CallbackTransactionSignatureChecker::CheckSig(
|
||||||
const vector<unsigned char>& vchSigIn,
|
const vector<unsigned char>& vchSigIn,
|
||||||
const vector<unsigned char>& vchPubKey,
|
const vector<unsigned char>& vchPubKey,
|
||||||
const CScript& scriptCode,
|
const CScript& scriptCode,
|
||||||
|
@ -1402,7 +983,10 @@ bool TransactionSignatureChecker::CheckSig(
|
||||||
|
|
||||||
uint256 sighash;
|
uint256 sighash;
|
||||||
try {
|
try {
|
||||||
sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata);
|
std::array<uint8_t, 32> sighashArray;
|
||||||
|
auto scriptBase = static_cast<const CScriptBase&>(scriptCode);
|
||||||
|
this->sighash(sighashArray.data(), sighashArray.size(), this->tx, &scriptBase[0], scriptBase.size(), nHashType);
|
||||||
|
sighash = uint256::FromRawBytes(sighashArray);
|
||||||
} catch (logic_error ex) {
|
} catch (logic_error ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1413,7 +997,7 @@ bool TransactionSignatureChecker::CheckSig(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const
|
bool CallbackTransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const
|
||||||
{
|
{
|
||||||
// There are two times of nLockTime: lock-by-blockheight
|
// There are two times of nLockTime: lock-by-blockheight
|
||||||
// and lock-by-blocktime, distinguished by whether
|
// and lock-by-blocktime, distinguished by whether
|
||||||
|
@ -1423,14 +1007,13 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
|
||||||
// unless the type of nLockTime being tested is the same as
|
// unless the type of nLockTime being tested is the same as
|
||||||
// the nLockTime in the transaction.
|
// the nLockTime in the transaction.
|
||||||
if (!(
|
if (!(
|
||||||
(txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) ||
|
(this->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) ||
|
||||||
(txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD)
|
(this->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD)))
|
||||||
))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Now that we know we're comparing apples-to-apples, the
|
// Now that we know we're comparing apples-to-apples, the
|
||||||
// comparison is a simple numeric one.
|
// comparison is a simple numeric one.
|
||||||
if (nLockTime > (int64_t)txTo->nLockTime)
|
if (nLockTime > this->nLockTime)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Finally the nLockTime feature can be disabled and thus
|
// Finally the nLockTime feature can be disabled and thus
|
||||||
|
@ -1443,13 +1026,12 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
|
||||||
// prevent this condition. Alternatively we could test all
|
// prevent this condition. Alternatively we could test all
|
||||||
// inputs, but testing just this input minimizes the data
|
// inputs, but testing just this input minimizes the data
|
||||||
// required to prove correct CHECKLOCKTIMEVERIFY execution.
|
// required to prove correct CHECKLOCKTIMEVERIFY execution.
|
||||||
if (txTo->vin[nIn].IsFinal())
|
if (this->isFinal)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool VerifyScript(
|
bool VerifyScript(
|
||||||
const CScript& scriptSig,
|
const CScript& scriptSig,
|
||||||
const CScript& scriptPubKey,
|
const CScript& scriptPubKey,
|
||||||
|
|
|
@ -8,18 +8,17 @@
|
||||||
#define BITCOIN_SCRIPT_INTERPRETER_H
|
#define BITCOIN_SCRIPT_INTERPRETER_H
|
||||||
|
|
||||||
#include "script_error.h"
|
#include "script_error.h"
|
||||||
#include "primitives/transaction.h"
|
|
||||||
|
|
||||||
#include <rust/transaction.h>
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
|
#include "script/script.h"
|
||||||
|
#include "amount.h"
|
||||||
|
|
||||||
class CPubKey;
|
class CPubKey;
|
||||||
class CScript;
|
class CScript;
|
||||||
class CTransaction;
|
|
||||||
class uint256;
|
class uint256;
|
||||||
|
|
||||||
/** Special case nIn for signing JoinSplits. */
|
/** Special case nIn for signing JoinSplits. */
|
||||||
|
@ -93,28 +92,6 @@ enum
|
||||||
|
|
||||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||||
|
|
||||||
struct PrecomputedTransactionData
|
|
||||||
{
|
|
||||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits, hashShieldedSpends, hashShieldedOutputs;
|
|
||||||
/** Precomputed transaction parts. */
|
|
||||||
std::unique_ptr<PrecomputedTxParts, decltype(&zcash_transaction_precomputed_free)> preTx;
|
|
||||||
|
|
||||||
PrecomputedTransactionData(
|
|
||||||
const CTransaction& tx,
|
|
||||||
const std::vector<CTxOut>& allPrevOutputs);
|
|
||||||
|
|
||||||
PrecomputedTransactionData(
|
|
||||||
const CTransaction& tx,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
size_t allPrevOutputsLen);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void SetPrecomputed(
|
|
||||||
const CTransaction& tx,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
size_t allPrevOutputsLen);
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SigVersion
|
enum SigVersion
|
||||||
{
|
{
|
||||||
SIGVERSION_SPROUT = 0,
|
SIGVERSION_SPROUT = 0,
|
||||||
|
@ -123,15 +100,6 @@ enum SigVersion
|
||||||
SIGVERSION_ZIP244 = 3,
|
SIGVERSION_ZIP244 = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint256 SignatureHash(
|
|
||||||
const CScript &scriptCode,
|
|
||||||
const CTransaction& txTo,
|
|
||||||
unsigned int nIn,
|
|
||||||
int nHashType,
|
|
||||||
const CAmount& amount,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
const PrecomputedTransactionData& txdata);
|
|
||||||
|
|
||||||
class BaseSignatureChecker
|
class BaseSignatureChecker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -152,31 +120,23 @@ public:
|
||||||
virtual ~BaseSignatureChecker() {}
|
virtual ~BaseSignatureChecker() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TransactionSignatureChecker : public BaseSignatureChecker
|
class CallbackTransactionSignatureChecker : public BaseSignatureChecker
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const CTransaction* txTo;
|
const void* tx;
|
||||||
unsigned int nIn;
|
void (*sighash)(unsigned char* sighash, unsigned int sighashLen, const void* tx, const unsigned char* scriptCode, unsigned int scriptCodeLen, int hashType);
|
||||||
const CAmount amount;
|
const CScriptNum& nLockTime;
|
||||||
const PrecomputedTransactionData& txdata;
|
bool isFinal;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TransactionSignatureChecker(const CTransaction* txToIn, const PrecomputedTransactionData& txdataIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(txdataIn) {}
|
CallbackTransactionSignatureChecker(const void* tx, void (*sighash)(unsigned char* sighash, unsigned int sighashLen, const void* tx, const unsigned char* scriptCode, unsigned int scriptCodeLen, int hashType), const CScriptNum& nLockTime, bool isFinal) : tx(tx), sighash(sighash), nLockTime(nLockTime), isFinal(isFinal) {}
|
||||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const;
|
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const;
|
||||||
bool CheckLockTime(const CScriptNum& nLockTime) const;
|
bool CheckLockTime(const CScriptNum& nLockTime) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MutableTransactionSignatureChecker : public TransactionSignatureChecker
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
const CTransaction txTo;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, const PrecomputedTransactionData& txdataIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, txdataIn, nInIn, amount), txTo(*txToIn) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool EvalScript(
|
bool EvalScript(
|
||||||
std::vector<std::vector<unsigned char> >& stack,
|
std::vector<std::vector<unsigned char> >& stack,
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
|
|
||||||
#include "zcash_script.h"
|
#include "zcash_script.h"
|
||||||
|
|
||||||
#include "consensus/upgrades.h"
|
|
||||||
#include "primitives/transaction.h"
|
|
||||||
#include "pubkey.h"
|
|
||||||
#include "script/interpreter.h"
|
#include "script/interpreter.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
@ -19,262 +16,6 @@ inline int set_error(zcash_script_error* ret, zcash_script_error serror)
|
||||||
*ret = serror;
|
*ret = serror;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy of GetLegacySigOpCount from main.cpp commit c4b2ef7c4.
|
|
||||||
// Replace with the copy from src/consensus/tx_verify.{cpp,h} after backporting that refactor.
|
|
||||||
unsigned int GetLegacySigOpCount(const CTransaction& tx)
|
|
||||||
{
|
|
||||||
unsigned int nSigOps = 0;
|
|
||||||
for (const CTxIn& txin : tx.vin)
|
|
||||||
{
|
|
||||||
nSigOps += txin.scriptSig.GetSigOpCount(false);
|
|
||||||
}
|
|
||||||
for (const CTxOut& txout : tx.vout)
|
|
||||||
{
|
|
||||||
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
|
|
||||||
}
|
|
||||||
return nSigOps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PrecomputedTransaction {
|
|
||||||
const CTransaction tx;
|
|
||||||
const PrecomputedTransactionData txdata;
|
|
||||||
|
|
||||||
PrecomputedTransaction(
|
|
||||||
CTransaction txIn,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
size_t allPrevOutputsLen) : tx(txIn), txdata(txIn, allPrevOutputs, allPrevOutputsLen) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
void* zcash_script_new_precomputed_tx(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const char* txToEnd = (const char *)(txTo + txToLen);
|
|
||||||
RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
CTransaction tx;
|
|
||||||
stream >> tx;
|
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (tx.nVersion >= ZIP225_TX_VERSION) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_VERSION);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserializing the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
// This is a pre-v5 tx, so the PrecomputedTransactionData constructor
|
|
||||||
// field `allPrevOutputs` is not used.
|
|
||||||
auto preTx = new PrecomputedTransaction(tx, nullptr, 0);
|
|
||||||
return preTx;
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void* zcash_script_new_precomputed_tx_v5(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
unsigned int allPrevOutputsLen,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
CTransaction tx;
|
|
||||||
try {
|
|
||||||
const char* txToEnd = (const char *)(txTo + txToLen);
|
|
||||||
RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
stream >> tx;
|
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto preTx = new PrecomputedTransaction(tx, allPrevOutputs, allPrevOutputsLen);
|
|
||||||
// Deserializing the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
return preTx;
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
// We had some error when parsing allPrevOutputs inside the
|
|
||||||
// PrecomputedTransactionData constructor.
|
|
||||||
set_error(err, zcash_script_ERR_ALL_PREV_OUTPUTS_DESERIALIZE);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void zcash_script_free_precomputed_tx(void* pre_preTx)
|
|
||||||
{
|
|
||||||
PrecomputedTransaction* preTx = static_cast<PrecomputedTransaction*>(pre_preTx);
|
|
||||||
delete preTx;
|
|
||||||
preTx = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int zcash_script_verify_precomputed(
|
|
||||||
const void* pre_preTx,
|
|
||||||
unsigned int nIn,
|
|
||||||
const unsigned char* scriptPubKey,
|
|
||||||
unsigned int scriptPubKeyLen,
|
|
||||||
int64_t amount,
|
|
||||||
unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
const PrecomputedTransaction* preTx = static_cast<const PrecomputedTransaction*>(pre_preTx);
|
|
||||||
if (nIn >= preTx->tx.vin.size())
|
|
||||||
return set_error(err, zcash_script_ERR_TX_INDEX);
|
|
||||||
|
|
||||||
// Regardless of the verification result, the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
return VerifyScript(
|
|
||||||
preTx->tx.vin[nIn].scriptSig,
|
|
||||||
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
|
|
||||||
flags,
|
|
||||||
TransactionSignatureChecker(&preTx->tx, preTx->txdata, nIn, amount),
|
|
||||||
consensusBranchId,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int zcash_script_verify(
|
|
||||||
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
|
||||||
int64_t amount,
|
|
||||||
const unsigned char *txTo, unsigned int txToLen,
|
|
||||||
unsigned int nIn, unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const char* txToEnd = (const char *)(txTo + txToLen);
|
|
||||||
RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
CTransaction tx;
|
|
||||||
stream >> tx;
|
|
||||||
if (nIn >= tx.vin.size())
|
|
||||||
return set_error(err, zcash_script_ERR_TX_INDEX);
|
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
|
||||||
return set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
|
||||||
if (tx.nVersion >= ZIP225_TX_VERSION) {
|
|
||||||
return set_error(err, zcash_script_ERR_TX_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regardless of the verification result, the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
// This is a pre-v5 tx, so the PrecomputedTransactionData constructor
|
|
||||||
// field `allPrevOutputs` is not used.
|
|
||||||
PrecomputedTransactionData txdata(tx, {});
|
|
||||||
return VerifyScript(
|
|
||||||
tx.vin[nIn].scriptSig,
|
|
||||||
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
|
|
||||||
flags,
|
|
||||||
TransactionSignatureChecker(&tx, txdata, nIn, amount),
|
|
||||||
consensusBranchId,
|
|
||||||
NULL);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
return set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int zcash_script_verify_v5(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
unsigned int allPrevOutputsLen,
|
|
||||||
unsigned int nIn,
|
|
||||||
unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
CTransaction tx;
|
|
||||||
try {
|
|
||||||
const char* txToEnd = (const char *)(txTo + txToLen);
|
|
||||||
RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
stream >> tx;
|
|
||||||
if (nIn >= tx.vin.size())
|
|
||||||
return set_error(err, zcash_script_ERR_TX_INDEX);
|
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
|
||||||
return set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
return set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<CTxOut> prevOutputs;
|
|
||||||
try {
|
|
||||||
// TODO: we can swap this second deserialization for an FFI call by
|
|
||||||
// fetching this through PrecomputedTransactionData. Simplicity for now.
|
|
||||||
CDataStream sAllPrevOutputs(
|
|
||||||
reinterpret_cast<const char*>(allPrevOutputs),
|
|
||||||
reinterpret_cast<const char*>(allPrevOutputs + allPrevOutputsLen),
|
|
||||||
SER_NETWORK,
|
|
||||||
PROTOCOL_VERSION);
|
|
||||||
sAllPrevOutputs >> prevOutputs;
|
|
||||||
if (!(tx.IsCoinBase() ? prevOutputs.empty() : tx.vin.size() == prevOutputs.size())) {
|
|
||||||
return set_error(err, zcash_script_ERR_ALL_PREV_OUTPUTS_SIZE_MISMATCH);
|
|
||||||
}
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
// We had some error when parsing allPrevOutputs inside the
|
|
||||||
// PrecomputedTransactionData constructor.
|
|
||||||
return set_error(err, zcash_script_ERR_ALL_PREV_OUTPUTS_DESERIALIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Regardless of the verification result, the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
PrecomputedTransactionData txdata(tx, allPrevOutputs, allPrevOutputsLen);
|
|
||||||
return VerifyScript(
|
|
||||||
tx.vin[nIn].scriptSig,
|
|
||||||
prevOutputs[nIn].scriptPubKey,
|
|
||||||
flags,
|
|
||||||
TransactionSignatureChecker(&tx, txdata, nIn, prevOutputs[nIn].nValue),
|
|
||||||
consensusBranchId,
|
|
||||||
NULL);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
return set_error(err, zcash_script_ERR_VERIFY_SCRIPT); // Error during script verification
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int zcash_script_legacy_sigop_count_precomputed(
|
|
||||||
const void* pre_preTx,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
const PrecomputedTransaction* preTx = static_cast<const PrecomputedTransaction*>(pre_preTx);
|
|
||||||
|
|
||||||
// The current implementation of this method never errors.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
|
|
||||||
return GetLegacySigOpCount(preTx->tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int zcash_script_legacy_sigop_count(
|
|
||||||
const unsigned char *txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
zcash_script_error* err)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
const char* txToEnd = (const char *)(txTo + txToLen);
|
|
||||||
RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
CTransaction tx;
|
|
||||||
stream >> tx;
|
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
|
||||||
return UINT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserializing the tx did not error.
|
|
||||||
set_error(err, zcash_script_ERR_OK);
|
|
||||||
|
|
||||||
return GetLegacySigOpCount(tx);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
|
||||||
return UINT_MAX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int zcash_script_version()
|
unsigned int zcash_script_version()
|
||||||
|
@ -282,3 +23,41 @@ unsigned int zcash_script_version()
|
||||||
// Just use the API version for now
|
// Just use the API version for now
|
||||||
return ZCASH_SCRIPT_API_VER;
|
return ZCASH_SCRIPT_API_VER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int zcash_script_legacy_sigop_count_script(
|
||||||
|
const unsigned char* script,
|
||||||
|
unsigned int scriptLen)
|
||||||
|
{
|
||||||
|
CScript cscript = CScript(script, script + scriptLen);
|
||||||
|
return cscript.GetSigOpCount(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int zcash_script_verify_callback(
|
||||||
|
const void* ctx,
|
||||||
|
void (*sighash)(unsigned char* sighash, unsigned int sighashLen, const void* ctx, const unsigned char* scriptCode, unsigned int scriptCodeLen, int hashType),
|
||||||
|
int64_t nLockTime,
|
||||||
|
uint8_t isFinal,
|
||||||
|
const unsigned char* scriptPubKey,
|
||||||
|
unsigned int scriptPubKeyLen,
|
||||||
|
const unsigned char* scriptSig,
|
||||||
|
unsigned int scriptSigLen,
|
||||||
|
unsigned int flags,
|
||||||
|
zcash_script_error* err)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
set_error(err, zcash_script_ERR_OK);
|
||||||
|
CScriptNum nLockTimeNum = CScriptNum(nLockTime);
|
||||||
|
ScriptError script_err = SCRIPT_ERR_OK;
|
||||||
|
return VerifyScript(
|
||||||
|
CScript(scriptSig, scriptSig + scriptSigLen),
|
||||||
|
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
|
||||||
|
flags,
|
||||||
|
CallbackTransactionSignatureChecker(ctx, sighash, nLockTimeNum, isFinal != 0),
|
||||||
|
// consensusBranchId is not longer used with the callback API; the argument
|
||||||
|
// was left there to minimize changes to interpreter.cpp
|
||||||
|
0,
|
||||||
|
&script_err);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return set_error(err, zcash_script_ERR_VERIFY_SCRIPT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ZCASH_SCRIPT_API_VER 3
|
#define ZCASH_SCRIPT_API_VER 4
|
||||||
|
|
||||||
typedef enum zcash_script_error_t
|
typedef enum zcash_script_error_t
|
||||||
{
|
{
|
||||||
|
@ -57,130 +57,56 @@ enum
|
||||||
zcash_script_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
|
zcash_script_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Deserializes the given transaction and precomputes values to improve
|
|
||||||
/// script verification performance.
|
|
||||||
///
|
|
||||||
/// This API cannot be used for v5+ transactions, and will return an error.
|
|
||||||
///
|
|
||||||
/// Returns a pointer to the precomputed transaction. Free this with
|
|
||||||
/// zcash_script_free_precomputed_tx once you are done.
|
|
||||||
/// The precomputed transaction is guaranteed to not keep a reference to any
|
|
||||||
/// part of the input buffers, so they can be safely overwritten or deallocated
|
|
||||||
/// after this function returns.
|
|
||||||
///
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
void* zcash_script_new_precomputed_tx(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Deserializes the given transaction and precomputes values to improve
|
|
||||||
/// script verification performance. Must be used for V5 transactions;
|
|
||||||
/// may also be used for previous versions.
|
|
||||||
///
|
|
||||||
/// allPrevOutputs must point to the encoding of the vector of all outputs
|
|
||||||
/// from previous transactions that are spent by the inputs of the given transaction.
|
|
||||||
/// The outputs must be encoded as specified by Bitcoin. The full encoding will thus
|
|
||||||
/// consist of a CompactSize prefix containing the number of outputs, followed by
|
|
||||||
/// the concatenated outputs, each of them encoded as (value, CompactSize, pk_script).
|
|
||||||
///
|
|
||||||
/// Returns a pointer to the precomputed transaction. Free this with
|
|
||||||
/// zcash_script_free_precomputed_tx once you are done.
|
|
||||||
///
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
void* zcash_script_new_precomputed_tx_v5(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
unsigned int allPrevOutputsLen,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Frees a precomputed transaction previously created with
|
|
||||||
/// zcash_script_new_precomputed_tx.
|
|
||||||
void zcash_script_free_precomputed_tx(void* preTx);
|
|
||||||
|
|
||||||
/// Returns 1 if the input nIn of the precomputed transaction pointed to by
|
|
||||||
/// preTx correctly spends the scriptPubKey pointed to by scriptPubKey under
|
|
||||||
/// the additional constraints specified by flags.
|
|
||||||
///
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
/// Note that script verification failure is indicated by err being set to
|
|
||||||
/// zcash_script_ERR_OK and a return value of 0.
|
|
||||||
EXPORT_SYMBOL int zcash_script_verify_precomputed(
|
|
||||||
const void* preTx,
|
|
||||||
unsigned int nIn,
|
|
||||||
const unsigned char* scriptPubKey,
|
|
||||||
unsigned int scriptPubKeyLen,
|
|
||||||
int64_t amount,
|
|
||||||
unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Returns 1 if the input nIn of the serialized transaction pointed to by
|
|
||||||
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
|
|
||||||
/// the additional constraints specified by flags.
|
|
||||||
///
|
|
||||||
/// This API cannot be used for v5+ transactions, and will return an error.
|
|
||||||
///
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
/// Note that script verification failure is indicated by err being set to
|
|
||||||
/// zcash_script_ERR_OK and a return value of 0.
|
|
||||||
EXPORT_SYMBOL int zcash_script_verify(
|
|
||||||
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
|
||||||
int64_t amount,
|
|
||||||
const unsigned char *txTo, unsigned int txToLen,
|
|
||||||
unsigned int nIn, unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Returns 1 if the input nIn of the serialized transaction pointed to by
|
|
||||||
/// txTo correctly spends the matching output in allPrevOutputs under
|
|
||||||
/// the additional constraints specified by flags. Must be used for V5 transactions;
|
|
||||||
/// may also be used for previous versions.
|
|
||||||
///
|
|
||||||
/// allPrevOutputs must point to the encoding of the vector of all outputs
|
|
||||||
/// from previous transactions that are spent by the inputs of the given transaction.
|
|
||||||
/// The outputs must be encoded as specified by Bitcoin. The full encoding will thus
|
|
||||||
/// consist of a CompactSize prefix containing the number of outputs, followed by
|
|
||||||
/// the concatenated outputs, each of them encoded as (value, CompactSize, pk_script).
|
|
||||||
///
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
/// Note that script verification failure is indicated by err being set to
|
|
||||||
/// zcash_script_ERR_OK and a return value of 0.
|
|
||||||
EXPORT_SYMBOL int zcash_script_verify_v5(
|
|
||||||
const unsigned char* txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
const unsigned char* allPrevOutputs,
|
|
||||||
unsigned int allPrevOutputsLen,
|
|
||||||
unsigned int nIn,
|
|
||||||
unsigned int flags,
|
|
||||||
uint32_t consensusBranchId,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Returns the number of transparent signature operations in the
|
|
||||||
/// transparent inputs and outputs of the precomputed transaction
|
|
||||||
/// pointed to by preTx.
|
|
||||||
///
|
|
||||||
/// Returns UINT_MAX on error, so that invalid transactions don't pass the Zcash consensus rules.
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count_precomputed(
|
|
||||||
const void* preTx,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Returns the number of transparent signature operations in the
|
|
||||||
/// transparent inputs and outputs of the serialized transaction
|
|
||||||
/// pointed to by txTo.
|
|
||||||
///
|
|
||||||
/// Returns UINT_MAX on error.
|
|
||||||
/// If not NULL, err will contain an error/success code for the operation.
|
|
||||||
EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count(
|
|
||||||
const unsigned char *txTo,
|
|
||||||
unsigned int txToLen,
|
|
||||||
zcash_script_error* err);
|
|
||||||
|
|
||||||
/// Returns the current version of the zcash_script library.
|
/// Returns the current version of the zcash_script library.
|
||||||
EXPORT_SYMBOL unsigned int zcash_script_version();
|
EXPORT_SYMBOL unsigned int zcash_script_version();
|
||||||
|
|
||||||
|
/// Returns the number of transparent signature operations in the input or
|
||||||
|
/// output script pointed to by script.
|
||||||
|
EXPORT_SYMBOL unsigned int zcash_script_legacy_sigop_count_script(
|
||||||
|
const unsigned char* script,
|
||||||
|
unsigned int scriptLen);
|
||||||
|
|
||||||
|
/// Returns 1 if the a transparent input correctly spends the matching output
|
||||||
|
/// under the additional constraints specified by flags. This function
|
||||||
|
/// receives only the required information to validate the spend and not
|
||||||
|
/// the transaction itself. In particular, the sighash for the spend
|
||||||
|
/// is obtained using a callback function.
|
||||||
|
///
|
||||||
|
/// - ctx: an opaque pointer which is forwarded to the callback. It can be used
|
||||||
|
/// to store context regarding the spend (i.e. the transaction itself,
|
||||||
|
/// and any precomputed data).
|
||||||
|
/// - sighash: a callback function which is called to obtain the sighash.
|
||||||
|
/// - sighash: pointer to a buffer where the sighash must be written to.
|
||||||
|
/// - sighashLen: the length of the buffer. Will be 32.
|
||||||
|
/// - ctx: the same opaque pointer
|
||||||
|
/// - scriptCode: the scriptCode being validated. Note that this not always
|
||||||
|
/// matches scriptSig, i.e. for P2SH.
|
||||||
|
/// - scriptCodeLen: the length of the script.
|
||||||
|
/// - hashType: the hash type being used.
|
||||||
|
/// - nLockTime: the lock time of the transaction being validated.
|
||||||
|
/// - isFinal: a boolean indicating whether the input being validated is final
|
||||||
|
/// (i.e. its sequence number is 0xFFFFFFFF).
|
||||||
|
/// - scriptPubKey: the scriptPubKey of the output being spent.
|
||||||
|
/// - scriptPubKeyLen: the length of scriptPubKey.
|
||||||
|
/// - scriptSig: the scriptSig of the input being validated.
|
||||||
|
/// - scriptSigLen: the length of scriptSig.
|
||||||
|
/// - flags: the script verification flags to use.
|
||||||
|
/// - err: if not NULL, err will contain an error/success code for the operation.
|
||||||
|
///
|
||||||
|
/// Note that script verification failure is indicated by err being set to
|
||||||
|
/// zcash_script_ERR_OK and a return value of 0.
|
||||||
|
EXPORT_SYMBOL int zcash_script_verify_callback(
|
||||||
|
const void* ctx,
|
||||||
|
void (*sighash)(unsigned char* sighash, unsigned int sighashLen, const void* ctx, const unsigned char* scriptCode, unsigned int scriptCodeLen, int hashType),
|
||||||
|
int64_t nLockTime,
|
||||||
|
uint8_t isFinal,
|
||||||
|
const unsigned char* scriptPubKey,
|
||||||
|
unsigned int scriptPubKeyLen,
|
||||||
|
const unsigned char* scriptSig,
|
||||||
|
unsigned int scriptSigLen,
|
||||||
|
unsigned int flags,
|
||||||
|
zcash_script_error* err);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
#include <rust/ed25519.h>
|
|
||||||
|
|
||||||
#include "prevector.h"
|
#include "prevector.h"
|
||||||
|
|
||||||
static const unsigned int MAX_SIZE = 0x02000000;
|
static const unsigned int MAX_SIZE = 0x02000000;
|
||||||
|
@ -587,25 +585,6 @@ template<typename Stream, typename T> void Unserialize(Stream& os, std::shared_p
|
||||||
template<typename Stream, typename T> void Serialize(Stream& os, const std::unique_ptr<const T>& p);
|
template<typename Stream, typename T> void Serialize(Stream& os, const std::unique_ptr<const T>& p);
|
||||||
template<typename Stream, typename T> void Unserialize(Stream& os, std::unique_ptr<const T>& p);
|
template<typename Stream, typename T> void Unserialize(Stream& os, std::unique_ptr<const T>& p);
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::SigningKey
|
|
||||||
*/
|
|
||||||
template<typename Stream> void Serialize(Stream& os, const ed25519::SigningKey& item);
|
|
||||||
template<typename Stream> void Unserialize(Stream& is, ed25519::SigningKey& item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::VerificationKey
|
|
||||||
*/
|
|
||||||
template<typename Stream> void Serialize(Stream& os, const ed25519::VerificationKey& item);
|
|
||||||
template<typename Stream> void Unserialize(Stream& is, ed25519::VerificationKey& item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::Signature
|
|
||||||
*/
|
|
||||||
template<typename Stream> void Serialize(Stream& os, const ed25519::Signature& item);
|
|
||||||
template<typename Stream> void Unserialize(Stream& is, ed25519::Signature& item);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If none of the specialized versions above matched, default to calling member function.
|
* If none of the specialized versions above matched, default to calling member function.
|
||||||
|
@ -975,57 +954,6 @@ void Unserialize(Stream& is, std::shared_ptr<const T>& p)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::SigningKey
|
|
||||||
*/
|
|
||||||
template<typename Stream>
|
|
||||||
void Serialize(Stream& os, const ed25519::SigningKey& sk)
|
|
||||||
{
|
|
||||||
Serialize(os, sk.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
void Unserialize(Stream& is, ed25519::SigningKey& sk)
|
|
||||||
{
|
|
||||||
Unserialize(is, sk.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::VerificationKey
|
|
||||||
*/
|
|
||||||
template<typename Stream>
|
|
||||||
void Serialize(Stream& os, const ed25519::VerificationKey& vk)
|
|
||||||
{
|
|
||||||
Serialize(os, vk.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
void Unserialize(Stream& is, ed25519::VerificationKey& vk)
|
|
||||||
{
|
|
||||||
Unserialize(is, vk.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ed25519::Signature
|
|
||||||
*/
|
|
||||||
template<typename Stream>
|
|
||||||
void Serialize(Stream& os, const ed25519::Signature& sig)
|
|
||||||
{
|
|
||||||
Serialize(os, sig.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
void Unserialize(Stream& is, ed25519::Signature& sig)
|
|
||||||
{
|
|
||||||
Unserialize(is, sig.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for ADD_SERIALIZE_METHODS and READWRITE macro
|
* Support for ADD_SERIALIZE_METHODS and READWRITE macro
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/blake2b.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/bridge.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/builder_ffi.rs");
|
|
|
@ -1,3 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
include!("../depend/zcash/src/rust/src/bundlecache.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/incremental_merkle_tree.rs");
|
|
313
src/lib.rs
313
src/lib.rs
|
@ -13,266 +13,105 @@
|
||||||
|
|
||||||
// Use the generated C++ bindings
|
// Use the generated C++ bindings
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
|
|
||||||
// Include the items from depend/zcash/src/rust/src/rustzcash.rs (librustzcash/lib.rs)
|
|
||||||
// that we need
|
|
||||||
|
|
||||||
use ::sapling::circuit::{
|
|
||||||
OutputParameters, OutputVerifyingKey, SpendParameters, SpendVerifyingKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut SAPLING_SPEND_VK: Option<SpendVerifyingKey> = None;
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut SAPLING_OUTPUT_VK: Option<OutputVerifyingKey> = None;
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut SAPLING_SPEND_PARAMS: Option<SpendParameters> = None;
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut SAPLING_OUTPUT_PARAMS: Option<OutputParameters> = None;
|
|
||||||
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut ORCHARD_PK: Option<orchard::circuit::ProvingKey> = None;
|
|
||||||
/// The code that uses this constant is not called by zcash_script.
|
|
||||||
static mut ORCHARD_VK: Option<orchard::circuit::VerifyingKey> = None;
|
|
||||||
|
|
||||||
/// Converts CtOption<t> into Option<T>
|
|
||||||
fn de_ct<T>(ct: subtle::CtOption<T>) -> Option<T> {
|
|
||||||
if ct.is_some().into() {
|
|
||||||
Some(ct.unwrap())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The size of a Groth16 Sapling proof.
|
|
||||||
const GROTH_PROOF_SIZE: usize = 48 // π_A
|
|
||||||
+ 96 // π_B
|
|
||||||
+ 48; // π_C
|
|
||||||
|
|
||||||
// Include the modules from depend/zcash/src/rust (librustzcash) that we need
|
|
||||||
mod blake2b;
|
|
||||||
mod bridge;
|
|
||||||
mod bundlecache;
|
|
||||||
mod incremental_merkle_tree;
|
|
||||||
mod merkle_frontier;
|
|
||||||
mod note_encryption;
|
|
||||||
mod orchard_bundle;
|
|
||||||
mod params;
|
|
||||||
mod sapling;
|
|
||||||
mod streams;
|
|
||||||
mod test_harness_ffi;
|
|
||||||
mod wallet;
|
|
||||||
mod wallet_scanner;
|
|
||||||
mod zcashd_orchard;
|
|
||||||
|
|
||||||
mod builder_ffi;
|
|
||||||
mod orchard_ffi;
|
|
||||||
mod streams_ffi;
|
|
||||||
mod transaction_ffi;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::ffi::{c_int, c_uint, c_void};
|
||||||
|
|
||||||
pub use super::zcash_script_error_t;
|
pub use super::zcash_script_error_t;
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref SCRIPT_PUBKEY: Vec<u8> = <Vec<u8>>::from_hex("76a914f47cac1e6fec195c055994e8064ffccce0044dd788ac").unwrap();
|
pub static ref SCRIPT_PUBKEY: Vec<u8> = <Vec<u8>>::from_hex("a914c117756dcbe144a12a7c33a77cfa81aa5aeeb38187").unwrap();
|
||||||
pub static ref SCRIPT_TX: Vec<u8> = <Vec<u8>>::from_hex("0400008085202f8901fcaf44919d4a17f6181a02a7ebe0420be6f7dad1ef86755b81d5a9567456653c010000006a473044022035224ed7276e61affd53315eca059c92876bc2df61d84277cafd7af61d4dbf4002203ed72ea497a9f6b38eb29df08e830d99e32377edb8a574b8a289024f0241d7c40121031f54b095eae066d96b2557c1f99e40e967978a5fd117465dbec0986ca74201a6feffffff020050d6dc0100000017a9141b8a9bda4b62cd0d0582b55455d0778c86f8628f870d03c812030000001976a914e4ff5512ffafe9287992a1cd177ca6e408e0300388ac62070d0095070d000000000000000000000000").expect("Block bytes are in valid hex representation");
|
pub static ref SCRIPT_SIG: Vec<u8> = <Vec<u8>>::from_hex("00483045022100d2ab3e6258fe244fa442cfb38f6cef9ac9a18c54e70b2f508e83fa87e20d040502200eead947521de943831d07a350e45af8e36c2166984a8636f0a8811ff03ed09401473044022013e15d865010c257eef133064ef69a780b4bc7ebe6eda367504e806614f940c3022062fdbc8c2d049f91db2042d6c9771de6f1ef0b3b1fea76c1ab5542e44ed29ed8014c69522103b2cc71d23eb30020a4893982a1e2d352da0d20ee657fa02901c432758909ed8f21029d1e9a9354c0d2aee9ffd0f0cea6c39bbf98c4066cf143115ba2279d0ba7dabe2103e32096b63fd57f3308149d238dcbb24d8d28aad95c0e4e74e3e5e6a11b61bcc453ae").expect("Block bytes are in valid hex representation");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manually encode all previous outputs for a single output.
|
extern "C" fn sighash(
|
||||||
fn encode_all_prev_outputs(amount: i64, script_pub_key: &[u8]) -> (Vec<u8>, *const u8) {
|
sighash_out: *mut u8,
|
||||||
// Number of transactions (CompactSize)
|
sighash_out_len: c_uint,
|
||||||
let mut all_prev_outputs = vec![1];
|
ctx: *const c_void,
|
||||||
// Amount as 8 little-endian bytes
|
_script_code: *const u8,
|
||||||
all_prev_outputs.extend(amount.to_le_bytes().iter().cloned());
|
_script_code_len: c_uint,
|
||||||
// Length of the pub key script (CompactSize)
|
_hash_type: c_int,
|
||||||
all_prev_outputs.push(script_pub_key.len() as u8);
|
) {
|
||||||
// Pub key script
|
unsafe {
|
||||||
all_prev_outputs.extend(script_pub_key.iter().cloned());
|
assert!(ctx.is_null());
|
||||||
let all_prev_outputs_ptr = all_prev_outputs.as_ptr();
|
let sighash =
|
||||||
(all_prev_outputs, all_prev_outputs_ptr)
|
hex::decode("e8c7bdac77f6bb1f3aba2eaa1fada551a9c8b3b5ecd1ef86e6e58a5f1aab952c")
|
||||||
}
|
.unwrap();
|
||||||
|
assert!(sighash_out_len == sighash.len() as c_uint);
|
||||||
pub fn verify_script(
|
std::ptr::copy_nonoverlapping(sighash.as_ptr(), sighash_out, sighash.len());
|
||||||
script_pub_key: &[u8],
|
|
||||||
amount: i64,
|
|
||||||
tx_to: &[u8],
|
|
||||||
nIn: u32,
|
|
||||||
flags: u32,
|
|
||||||
consensus_branch_id: u32,
|
|
||||||
) -> Result<(), zcash_script_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::zcash_script_verify(
|
|
||||||
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 {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also test with the V5 API
|
|
||||||
|
|
||||||
let (all_prev_outputs, all_prev_outputs_ptr) =
|
|
||||||
encode_all_prev_outputs(amount, script_pub_key);
|
|
||||||
|
|
||||||
let ret = unsafe {
|
|
||||||
super::zcash_script_verify_v5(
|
|
||||||
tx_to_ptr,
|
|
||||||
tx_to_len as u32,
|
|
||||||
all_prev_outputs_ptr,
|
|
||||||
all_prev_outputs.len() as _,
|
|
||||||
nIn,
|
|
||||||
flags,
|
|
||||||
consensus_branch_id,
|
|
||||||
&mut err,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret == 1 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_script_precompute(
|
extern "C" fn invalid_sighash(
|
||||||
script_pub_key: &[u8],
|
sighash_out: *mut u8,
|
||||||
amount: i64,
|
sighash_out_len: c_uint,
|
||||||
tx_to: &[u8],
|
ctx: *const c_void,
|
||||||
nIn: u32,
|
_script_code: *const u8,
|
||||||
flags: u32,
|
_script_code_len: c_uint,
|
||||||
consensus_branch_id: u32,
|
_hash_type: c_int,
|
||||||
) -> Result<(), zcash_script_error_t> {
|
) {
|
||||||
let script_ptr = script_pub_key.as_ptr();
|
unsafe {
|
||||||
let script_len = script_pub_key.len();
|
assert!(ctx.is_null());
|
||||||
let tx_to_ptr = tx_to.as_ptr();
|
let sighash =
|
||||||
let tx_to_len = tx_to.len();
|
hex::decode("08c7bdac77f6bb1f3aba2eaa1fada551a9c8b3b5ecd1ef86e6e58a5f1aab952c")
|
||||||
let mut err = 0;
|
.unwrap();
|
||||||
|
assert!(sighash_out_len == sighash.len() as c_uint);
|
||||||
let precomputed =
|
std::ptr::copy_nonoverlapping(sighash.as_ptr(), sighash_out, sighash.len());
|
||||||
unsafe { super::zcash_script_new_precomputed_tx(tx_to_ptr, tx_to_len as _, &mut err) };
|
|
||||||
|
|
||||||
let ret = unsafe {
|
|
||||||
super::zcash_script_verify_precomputed(
|
|
||||||
precomputed,
|
|
||||||
nIn,
|
|
||||||
script_ptr,
|
|
||||||
script_len as _,
|
|
||||||
amount,
|
|
||||||
flags,
|
|
||||||
consensus_branch_id,
|
|
||||||
&mut err,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { super::zcash_script_free_precomputed_tx(precomputed) };
|
|
||||||
|
|
||||||
if ret != 1 {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also test with the V5 API
|
|
||||||
|
|
||||||
let (all_prev_outputs, all_prev_outputs_ptr) =
|
|
||||||
encode_all_prev_outputs(amount, script_pub_key);
|
|
||||||
|
|
||||||
let precomputed = unsafe {
|
|
||||||
super::zcash_script_new_precomputed_tx_v5(
|
|
||||||
tx_to_ptr,
|
|
||||||
tx_to_len as _,
|
|
||||||
all_prev_outputs_ptr,
|
|
||||||
all_prev_outputs.len() as _,
|
|
||||||
&mut err,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = unsafe {
|
|
||||||
super::zcash_script_verify_precomputed(
|
|
||||||
precomputed,
|
|
||||||
nIn,
|
|
||||||
script_ptr,
|
|
||||||
script_len as u32,
|
|
||||||
amount,
|
|
||||||
flags,
|
|
||||||
consensus_branch_id,
|
|
||||||
&mut err,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { super::zcash_script_free_precomputed_tx(precomputed) };
|
|
||||||
|
|
||||||
if ret == 1 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
let coin = i64::pow(10, 8);
|
let nLockTime: i64 = 2410374;
|
||||||
|
let isFinal: u8 = 1;
|
||||||
let script_pub_key = &*SCRIPT_PUBKEY;
|
let script_pub_key = &*SCRIPT_PUBKEY;
|
||||||
let amount = 212 * coin;
|
let script_sig = &*SCRIPT_SIG;
|
||||||
let tx_to = &*SCRIPT_TX;
|
let flags: c_uint = 513;
|
||||||
let nIn = 0;
|
let mut err = 0;
|
||||||
let flags = 1;
|
|
||||||
let branch_id = 0x2bb40e60;
|
|
||||||
|
|
||||||
verify_script(script_pub_key, amount, tx_to, nIn, flags, branch_id).unwrap();
|
let ret = unsafe {
|
||||||
|
super::zcash_script_verify_callback(
|
||||||
|
std::ptr::null(),
|
||||||
|
Some(sighash),
|
||||||
|
nLockTime,
|
||||||
|
isFinal,
|
||||||
|
script_pub_key.as_ptr(),
|
||||||
|
script_pub_key.len() as c_uint,
|
||||||
|
script_sig.as_ptr(),
|
||||||
|
script_sig.len() as c_uint,
|
||||||
|
flags,
|
||||||
|
&mut err,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(ret == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works_precomputed() {
|
fn it_fails_on_invalid_sighash() {
|
||||||
let coin = i64::pow(10, 8);
|
let nLockTime: i64 = 2410374;
|
||||||
|
let isFinal: u8 = 1;
|
||||||
let script_pub_key = &*SCRIPT_PUBKEY;
|
let script_pub_key = &*SCRIPT_PUBKEY;
|
||||||
let amount = 212 * coin;
|
let script_sig = &*SCRIPT_SIG;
|
||||||
let tx_to = &*SCRIPT_TX;
|
let flags: c_uint = 513;
|
||||||
let nIn = 0;
|
let mut err = 0;
|
||||||
let flags = 1;
|
|
||||||
let branch_id = 0x2bb40e60;
|
|
||||||
|
|
||||||
verify_script_precompute(script_pub_key, amount, tx_to, nIn, flags, branch_id).unwrap();
|
let ret = unsafe {
|
||||||
}
|
super::zcash_script_verify_callback(
|
||||||
|
std::ptr::null(),
|
||||||
|
Some(invalid_sighash),
|
||||||
|
nLockTime,
|
||||||
|
isFinal,
|
||||||
|
script_pub_key.as_ptr(),
|
||||||
|
script_pub_key.len() as c_uint,
|
||||||
|
script_sig.as_ptr(),
|
||||||
|
script_sig.len() as c_uint,
|
||||||
|
flags,
|
||||||
|
&mut err,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
assert!(ret != 1);
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_doesnt_work_precomputed() {
|
|
||||||
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_precompute(script_pub_key, amount, tx_to, nIn, flags, branch_id).unwrap_err();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/merkle_frontier.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/note_encryption.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/orchard_bundle.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/orchard_ffi.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/params.rs");
|
|
|
@ -1,3 +0,0 @@
|
||||||
// Note that don't include the original file directly; but rather
|
|
||||||
// a copy of sapling.rs with name changed. See build.rs for the explanation.
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/rust/sapling/mod.rs"));
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/streams.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/streams_ffi.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/test_harness_ffi.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/transaction_ffi.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/wallet.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/wallet_scanner.rs");
|
|
|
@ -1 +0,0 @@
|
||||||
include!("../depend/zcash/src/rust/src/zcashd_orchard.rs");
|
|
Loading…
Reference in New Issue