Merge branch 'master' of github.com:paritytech/parity-zcash
This commit is contained in:
commit
d789957924
|
@ -40,6 +40,11 @@ dependencies = [
|
|||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_matches"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.3"
|
||||
|
@ -116,11 +121,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "bitcrypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)",
|
||||
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
||||
"bn 0.4.4 (git+https://github.com/paritytech/bn)",
|
||||
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"primitives 0.1.0",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)",
|
||||
"serde 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -142,16 +150,6 @@ name = "bitflags"
|
|||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "blake2-rfc"
|
||||
version = "0.2.18"
|
||||
source = "git+https://github.com/gtank/blake2-rfc.git?branch=persona#c7c458429c429b81fea845421f5ab859710fa8af"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2-rfc"
|
||||
version = "0.2.18"
|
||||
|
@ -1582,7 +1580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "verification"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitcrypto 0.1.0",
|
||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chain 0.1.0",
|
||||
|
@ -1590,13 +1588,11 @@ dependencies = [
|
|||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"network 0.1.0",
|
||||
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"primitives 0.1.0",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)",
|
||||
"script 0.1.0",
|
||||
"serialization 0.1.0",
|
||||
"storage 0.1.0",
|
||||
|
@ -1685,6 +1681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
||||
"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "<none>"
|
||||
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
|
||||
"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
|
||||
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
|
||||
"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
|
||||
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
|
||||
|
@ -1694,7 +1691,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||
"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)" = "<none>"
|
||||
"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "<none>"
|
||||
"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "<none>"
|
||||
"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,14 +4,15 @@ version = "0.1.0"
|
|||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", branch = "persona" }
|
||||
bellman = "0.1"
|
||||
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" }
|
||||
pairing = "0.14.2"
|
||||
rust-crypto = "0.2.36"
|
||||
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" }
|
||||
serde_json = "1.0"
|
||||
siphasher = "0.1.1"
|
||||
primitives = { path = "../primitives" }
|
||||
bn = { git = "https://github.com/paritytech/bn" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-hex = "2"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0"
|
|
@ -0,0 +1,155 @@
|
|||
use std::fmt;
|
||||
use hex::FromHex;
|
||||
use bellman::groth16::{prepare_verifying_key, VerifyingKey as BellmanVerifyingKey};
|
||||
use pairing::{EncodedPoint, bls12_381::{Bls12, G1Uncompressed, G2Uncompressed}};
|
||||
use serde::de::{self, Visitor, Deserialize, Deserializer};
|
||||
|
||||
use json::pghr13::clean_0x;
|
||||
use Groth16VerifyingKey;
|
||||
|
||||
/// Load Sapling spend verification key.
|
||||
pub fn load_sapling_spend_verifying_key() -> Result<Groth16VerifyingKey, String> {
|
||||
let spend_vk_json = include_bytes!("../../../res/sapling-spend-verifying-key.json");
|
||||
let spend_vk = serde_json::from_slice::<VerifyingKey>(&spend_vk_json[..]).unwrap();
|
||||
Ok(Groth16VerifyingKey(prepare_verifying_key(&spend_vk.into())))
|
||||
}
|
||||
|
||||
/// Load Sapling output verification key.
|
||||
pub fn load_sapling_output_verifying_key() -> Result<Groth16VerifyingKey, String> {
|
||||
let output_vk_json = include_bytes!("../../../res/sapling-output-verifying-key.json");
|
||||
let output_vk = serde_json::from_slice::<VerifyingKey>(&output_vk_json[..]).unwrap();
|
||||
Ok(Groth16VerifyingKey(prepare_verifying_key(&output_vk.into())))
|
||||
}
|
||||
|
||||
type G1 = Point<G1Uncompressed>;
|
||||
type G2 = Point<G2Uncompressed>;
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct VerifyingKey {
|
||||
#[serde(rename = "alphaG1")]
|
||||
pub alpha_g1: G1,
|
||||
#[serde(rename = "betaG1")]
|
||||
pub beta_g1: G1,
|
||||
#[serde(rename = "betaG2")]
|
||||
pub beta_g2: G2,
|
||||
#[serde(rename = "gammaG2")]
|
||||
pub gamma_g2: G2,
|
||||
#[serde(rename = "deltaG1")]
|
||||
pub delta_g1: G1,
|
||||
#[serde(rename = "deltaG2")]
|
||||
pub delta_g2: G2,
|
||||
#[serde(rename = "ic")]
|
||||
pub ic: Vec<G1>,
|
||||
}
|
||||
|
||||
impl From<VerifyingKey> for BellmanVerifyingKey<Bls12> {
|
||||
fn from(vk: VerifyingKey) -> BellmanVerifyingKey<Bls12> {
|
||||
BellmanVerifyingKey {
|
||||
alpha_g1: vk.alpha_g1.0,
|
||||
beta_g1: vk.beta_g1.0,
|
||||
beta_g2: vk.beta_g2.0,
|
||||
gamma_g2: vk.gamma_g2.0,
|
||||
delta_g1: vk.delta_g1.0,
|
||||
delta_g2: vk.delta_g2.0,
|
||||
ic: vk.ic.into_iter().map(|p| p.0).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Point<EP: EncodedPoint>(EP::Affine);
|
||||
|
||||
impl<'de, EP: EncodedPoint> Deserialize<'de> for Point<EP> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct EncodedPointVisitor<EP: EncodedPoint>(::std::marker::PhantomData<EP>);
|
||||
|
||||
impl<'de, EP: EncodedPoint> Visitor<'de> for EncodedPointVisitor<EP> {
|
||||
type Value = Point<EP>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a hex string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let mut point = EP::empty();
|
||||
let point_raw = clean_0x(value).from_hex::<Vec<_>>()
|
||||
.map_err(|e| de::Error::custom(format!("Expected hex string: {}", e)))?;
|
||||
if point.as_ref().len() != point_raw.len() {
|
||||
return Err(de::Error::custom(format!("Expected hex string of length {}", point.as_ref().len())));
|
||||
}
|
||||
|
||||
point.as_mut().copy_from_slice(&point_raw);
|
||||
point.into_affine()
|
||||
.map_err(|e| de::Error::custom(format!("Invalid curve point: {}", e)))
|
||||
.map(Point)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(EncodedPointVisitor::<EP>(Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn g1() {
|
||||
let valid = r#""0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#;
|
||||
serde_json::from_str::<G1>(valid).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g1_messed() {
|
||||
// too few chars
|
||||
let invalid = r#""0xb882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#;
|
||||
serde_json::from_str::<G1>(invalid).unwrap_err();
|
||||
|
||||
// too much chars
|
||||
let invalid = r#""0xFF0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#;
|
||||
serde_json::from_str::<G1>(invalid).unwrap_err();
|
||||
|
||||
// invalid curve point
|
||||
let invalid = r#""0x19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a78970db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e""#;
|
||||
serde_json::from_str::<G1>(invalid).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2() {
|
||||
let valid = r#""0x050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#;
|
||||
serde_json::from_str::<G2>(valid).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2_messed() {
|
||||
// too few chars
|
||||
let invalid = r#""0x0302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#;
|
||||
serde_json::from_str::<G2>(invalid).unwrap_err();
|
||||
|
||||
// too much chars
|
||||
let invalid = r#""0xFF050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#;
|
||||
serde_json::from_str::<G2>(invalid).unwrap_err();
|
||||
|
||||
// invalid curve point
|
||||
let invalid = r#""0x09c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d2050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b120c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c0f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c""#;
|
||||
serde_json::from_str::<G2>(invalid).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_key() {
|
||||
let output_vk_json = include_bytes!("../../../res/sapling-output-verifying-key.json");
|
||||
serde_json::from_slice::<VerifyingKey>(&output_vk_json[..]).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spend_key() {
|
||||
let spend_vk_json = include_bytes!("../../../res/sapling-spend-verifying-key.json");
|
||||
serde_json::from_slice::<VerifyingKey>(&spend_vk_json[..]).unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod groth16;
|
||||
pub mod pghr13;
|
|
@ -10,7 +10,7 @@ pub struct G1(bn::G1);
|
|||
|
||||
struct G1Visitor;
|
||||
|
||||
fn clean_0x(s: &str) -> &str {
|
||||
pub(crate) fn clean_0x(s: &str) -> &str {
|
||||
if s.starts_with("0x") {
|
||||
&s[2..]
|
||||
} else {
|
||||
|
@ -120,8 +120,6 @@ pub struct VerifyingKey {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
extern crate serde_json;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
|
@ -1,15 +1,20 @@
|
|||
extern crate blake2_rfc;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate primitives;
|
||||
extern crate serde_json;
|
||||
extern crate siphasher;
|
||||
extern crate bn;
|
||||
extern crate serde;
|
||||
extern crate rustc_hex as hex;
|
||||
|
||||
pub extern crate bellman;
|
||||
pub extern crate pairing;
|
||||
pub extern crate sapling_crypto;
|
||||
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
mod pghr13;
|
||||
mod json;
|
||||
mod pghr13;
|
||||
|
||||
pub use rcrypto::digest::Digest;
|
||||
pub use blake2_rfc::blake2b::Blake2b;
|
||||
|
@ -21,11 +26,17 @@ use rcrypto::ripemd160::Ripemd160;
|
|||
use siphasher::sip::SipHasher24;
|
||||
use primitives::hash::{H32, H160, H256};
|
||||
|
||||
pub use json::groth16::{
|
||||
load_sapling_spend_verifying_key, load_sapling_output_verifying_key,
|
||||
};
|
||||
|
||||
pub use pghr13::{
|
||||
VerifyingKey as Pghr13VerifyingKey, Proof as Pghr13Proof, verify as pghr13_verify,
|
||||
G1, G2, Fr, Group,
|
||||
};
|
||||
|
||||
pub struct Groth16VerifyingKey(pub bellman::groth16::PreparedVerifyingKey<pairing::bls12_381::Bls12>);
|
||||
|
||||
pub struct DHash160 {
|
||||
sha256: Sha256,
|
||||
ripemd: Ripemd160,
|
||||
|
@ -195,6 +206,12 @@ pub fn checksum(data: &[u8]) -> H32 {
|
|||
result
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for Groth16VerifyingKey {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.write_str("Groth16VerifyingKey")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use primitives::bytes::Bytes;
|
||||
|
|
|
@ -7,7 +7,7 @@ use bytes::Bytes;
|
|||
use primitives::compact::Compact;
|
||||
use chain::{
|
||||
IndexedBlock, IndexedBlockHeader, IndexedTransaction, BlockHeader, Block, Transaction,
|
||||
OutPoint, TransactionOutput, SAPLING_TX_VERSION_GROUP_ID,
|
||||
OutPoint, TransactionOutput,
|
||||
};
|
||||
use ser::{
|
||||
deserialize, serialize, List
|
||||
|
@ -287,15 +287,13 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
|
|||
}
|
||||
|
||||
for tx in block.transactions.iter().skip(1) {
|
||||
let is_sapling_group = tx.raw.version_group_id == SAPLING_TX_VERSION_GROUP_ID;
|
||||
|
||||
modified_meta.insert(tx.hash.clone(), TransactionMeta::new(new_best_block.number, tx.raw.outputs.len()));
|
||||
|
||||
if let Some(ref js) = tx.raw.join_split {
|
||||
for js_descriptor in js.descriptions.iter() {
|
||||
for nullifier in &js_descriptor.nullifiers[..] {
|
||||
let nullifier_key = Nullifier::new(
|
||||
if is_sapling_group { NullifierTag::Sapling } else { NullifierTag::Sprout },
|
||||
NullifierTag::Sprout,
|
||||
H256::from(&nullifier[..])
|
||||
);
|
||||
if self.contains_nullifier(nullifier_key) {
|
||||
|
@ -307,6 +305,20 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(ref sapling) = tx.raw.sapling {
|
||||
for spend in &sapling.spends {
|
||||
let nullifier_key = Nullifier::new(
|
||||
NullifierTag::Sapling,
|
||||
H256::from(&spend.nullifier[..])
|
||||
);
|
||||
if self.contains_nullifier(nullifier_key) {
|
||||
trace!(target: "db", "Duplicate nullifer during canonization: {:?}", nullifier_key);
|
||||
return Err(Error::CannotCanonize);
|
||||
}
|
||||
update.insert(KeyValue::Nullifier(nullifier_key));
|
||||
}
|
||||
}
|
||||
|
||||
for input in &tx.raw.inputs {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
|
@ -363,12 +375,11 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
|
|||
|
||||
let mut modified_meta: HashMap<H256, TransactionMeta> = HashMap::new();
|
||||
for tx in block.transactions.iter().skip(1) {
|
||||
let is_sapling_group = tx.raw.version_group_id == SAPLING_TX_VERSION_GROUP_ID;
|
||||
if let Some(ref js) = tx.raw.join_split {
|
||||
for js_descriptor in js.descriptions.iter() {
|
||||
for nullifier in &js_descriptor.nullifiers[..] {
|
||||
let nullifier_key = Nullifier::new(
|
||||
if is_sapling_group { NullifierTag::Sapling } else { NullifierTag::Sprout },
|
||||
NullifierTag::Sprout,
|
||||
H256::from(&nullifier[..])
|
||||
);
|
||||
if !self.contains_nullifier(nullifier_key) {
|
||||
|
@ -380,6 +391,20 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(ref sapling) = tx.raw.sapling {
|
||||
for spend in &sapling.spends {
|
||||
let nullifier_key = Nullifier::new(
|
||||
NullifierTag::Sapling,
|
||||
H256::from(&spend.nullifier[..])
|
||||
);
|
||||
if !self.contains_nullifier(nullifier_key) {
|
||||
warn!(target: "db", "cannot decanonize, no nullifier: {:?}", nullifier_key);
|
||||
return Err(Error::CannotDecanonize);
|
||||
}
|
||||
update.delete(Key::Nullifier(nullifier_key));
|
||||
}
|
||||
}
|
||||
|
||||
for input in &tx.raw.inputs {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
|
|
|
@ -115,10 +115,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_inventory_type_conversion() {
|
||||
assert_eq!(0u32, InventoryType::Error.into());
|
||||
assert_eq!(1u32, InventoryType::MessageTx.into());
|
||||
assert_eq!(2u32, InventoryType::MessageBlock.into());
|
||||
assert_eq!(3u32, InventoryType::MessageFilteredBlock.into());
|
||||
assert_eq!(0u32, u32::from(InventoryType::Error));
|
||||
assert_eq!(1u32, u32::from(InventoryType::MessageTx));
|
||||
assert_eq!(2u32, u32::from(InventoryType::MessageBlock));
|
||||
assert_eq!(3u32, u32::from(InventoryType::MessageFilteredBlock));
|
||||
|
||||
assert_eq!(InventoryType::from_u32(0).unwrap(), InventoryType::Error);
|
||||
assert_eq!(InventoryType::from_u32(1).unwrap(), InventoryType::MessageTx);
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
use {Network, Magic, Deployment, crypto};
|
||||
|
||||
lazy_static! {
|
||||
static ref SAPLING_SPEND_VK: crypto::Groth16VerifyingKey = crypto::load_sapling_spend_verifying_key()
|
||||
.expect("hardcoded value should load without errors");
|
||||
static ref SAPLING_OUTPUT_VK: crypto::Groth16VerifyingKey = crypto::load_sapling_output_verifying_key()
|
||||
.expect("hardcoded value should load without errors");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Parameters that influence chain consensus.
|
||||
pub struct ConsensusParams {
|
||||
|
@ -45,6 +52,11 @@ pub struct ConsensusParams {
|
|||
|
||||
/// Active key for pghr13 joinsplit verification
|
||||
pub joinsplit_verification_key: crypto::Pghr13VerifyingKey,
|
||||
|
||||
/// Sapling spend verification key.
|
||||
pub sapling_spend_verifying_key: &'static crypto::Groth16VerifyingKey,
|
||||
/// Sapling output verification key.
|
||||
pub sapling_output_verifying_key: &'static crypto::Groth16VerifyingKey,
|
||||
}
|
||||
|
||||
fn mainnet_pghr_verification_key() -> crypto::Pghr13VerifyingKey {
|
||||
|
@ -135,6 +147,9 @@ impl ConsensusParams {
|
|||
equihash_params: Some((200, 9)),
|
||||
|
||||
joinsplit_verification_key: mainnet_pghr_verification_key(),
|
||||
|
||||
sapling_spend_verifying_key: &SAPLING_SPEND_VK,
|
||||
sapling_output_verifying_key: &SAPLING_OUTPUT_VK,
|
||||
},
|
||||
Network::Testnet => ConsensusParams {
|
||||
network: network,
|
||||
|
@ -157,6 +172,9 @@ impl ConsensusParams {
|
|||
equihash_params: Some((200, 9)),
|
||||
|
||||
joinsplit_verification_key: testnet_pghr_verification_key(),
|
||||
|
||||
sapling_spend_verifying_key: &SAPLING_SPEND_VK,
|
||||
sapling_output_verifying_key: &SAPLING_OUTPUT_VK,
|
||||
},
|
||||
Network::Regtest => ConsensusParams {
|
||||
network: network,
|
||||
|
@ -179,6 +197,9 @@ impl ConsensusParams {
|
|||
equihash_params: Some((200, 9)),
|
||||
|
||||
joinsplit_verification_key: regtest_pghr_verification_key(),
|
||||
|
||||
sapling_spend_verifying_key: &SAPLING_SPEND_VK,
|
||||
sapling_output_verifying_key: &SAPLING_OUTPUT_VK,
|
||||
},
|
||||
Network::Unitest => ConsensusParams {
|
||||
network: network,
|
||||
|
@ -201,6 +222,9 @@ impl ConsensusParams {
|
|||
equihash_params: None,
|
||||
|
||||
joinsplit_verification_key: unitest_pghr_verification_key(),
|
||||
|
||||
sapling_spend_verifying_key: &SAPLING_SPEND_VK,
|
||||
sapling_output_verifying_key: &SAPLING_OUTPUT_VK,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"alphaG1": "0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897",
|
||||
"betaG1": "0x014a78a8d17180a37c4ca8fb231f264ab89bd14863777fc1ffe901fd92444365d18f78237612ac38e39f419c32f0824515219ec45c26c1fad530514ed891a0d0043acedf348922102e95b3e6d07e0afa94c58aa41480631fc1ca36e55aae51fd",
|
||||
"betaG2": "0x0a416b8187450b28f025c421e3ff14d38f9abd9af2f1046b914b53ab37e9aebba683cb25284e5c22fa341129985250a103547de5d005df48265f7cb258162253d56fbc682d106a1ecb07666ebf7524a364e512c37aa62f82d6e7dd4ed8838478104376a98072766c29959358e9cde6a4985618f65ea257e8f288974f4aedde52e5dac2fb7ae5d30eab7cd828a2c8b15f15b16f139f2c33ef33d63befe404e696c97077d17ea42f4ff9d82ec456aaf43914a3d07968111a3a348f157e64c0278a",
|
||||
"gammaG2": "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801",
|
||||
"deltaG1": "0x0f61208393fe783b523d23c911eda2b295231684a8c3b3898d8fce83fa949fd6a25aee7fe4d0d37f7a892bf7c2639d5b1571bb257e41dab703d78f54a71dadec6f37e40d21bb29f5eca47c91078a016cf95fb7e6aa31c1321450202037c69ee7",
|
||||
"deltaG2": "0x050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c",
|
||||
"ic": [
|
||||
"0x04fd67184c37c5240183cff73f52544ecd6b629e775ad6648696f99a431a63ca8b386bdcb94cb1e4c53c4ec0276d3dc50c2bd5d31bf904b30b9d0508d59c915bfaea64830425366257fe599f2eaf2f6650bea1abe4bab1955da88a6860c31b4d",
|
||||
"0x073143b431c7e8e9323f3693c470fd9a3adf199a31b6e3006c64189bde7a07a316f9f7252d042147ac4c68be3a62a367012534e285c0ac0e01c7c8f9d013f13aa1da0c06777564d76b27ab788fd46ca5a9a9854424c325eab85298044b258da3",
|
||||
"0x13cca7cdbcfb681f36c0a3d9ab957960b57dd73002f042f7222eec23d5d60a30bf93bd8d0564113c0be8f1baf577ca570088e381355d3f7e8dab653e7a2e758724b39a9d63e4905fbc2927d5246d2e6072ad142f81498e6d5aecdf36200de579",
|
||||
"0x0ffb9b423f4631fbbeb423d81fe9b244953a2b701eb2f5d96bf97981eb50227705128a401c10789582bbc8648150270f0d77cb44de2f73bdea9e4405174ef3034ce88bc4b02d4e0886b3fa5fb5e6b0b1f404bfa18ad308aadddd2634c9765d5b",
|
||||
"0x0f91ffe602148dd9a6404f1701af31a48481ef23bf05beed476428178c2f972082d533348904348831ea61ce33bf6ccc0b04fae453962cb4c2634bc75eaecaaf9a034cb30d82968bb08a46403b308ac0be82f5964c0a9631032c363147097ad2",
|
||||
"0x0936d64376b8cff46bf7a9c3db65b47ad83c1cdf2cea84f2671278d597f8867a3a4a955aaab958169bf96839b923b27a08e7d52742571a73eb842f9c31b8972505079760a63c909f0033ba1331c8131c3eafb8945f2112c405869b54721b014e"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"alphaG1": "0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897",
|
||||
"betaG1": "0x014a78a8d17180a37c4ca8fb231f264ab89bd14863777fc1ffe901fd92444365d18f78237612ac38e39f419c32f0824515219ec45c26c1fad530514ed891a0d0043acedf348922102e95b3e6d07e0afa94c58aa41480631fc1ca36e55aae51fd",
|
||||
"betaG2": "0x0a416b8187450b28f025c421e3ff14d38f9abd9af2f1046b914b53ab37e9aebba683cb25284e5c22fa341129985250a103547de5d005df48265f7cb258162253d56fbc682d106a1ecb07666ebf7524a364e512c37aa62f82d6e7dd4ed8838478104376a98072766c29959358e9cde6a4985618f65ea257e8f288974f4aedde52e5dac2fb7ae5d30eab7cd828a2c8b15f15b16f139f2c33ef33d63befe404e696c97077d17ea42f4ff9d82ec456aaf43914a3d07968111a3a348f157e64c0278a",
|
||||
"gammaG2": "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801",
|
||||
"deltaG1": "0x0b9d259333f82bb97fd52701bc1e2bf04755c376a19aaadef0ebe8466a4f30862accb63e627e73fab29b929d6106795f0fd0296b4a6945694850071bf5ca887cd39fd803f34c00cf2965cf5218cf58d66fb7eecaf1d17055eb465bf3584fa638",
|
||||
"deltaG2": "0x077284b5eb4a16c08a9b4810bcb80336367adbc7425bb37cb5dfb41977a78e152125fad6cb858a38edbe25ad2a34f7f019841965c9831072efe2b9739766edd008283f835d09206e51bfc1f97d9b2eb1c8b726195b2ffb5e5506fccb8f93e6060bdfb00b4aa6237784e645e0cda032243c65b73634f508403ae8297e4b894d313bce5b96db0a3311c28182d3a798a02f185e77ca7a24fe2a1a9447c93de31bd3e481fa9f623ae8d27baf78e6547e83b3d99a6eda5b455b09469c0d6ecd442ba2",
|
||||
"ic": [
|
||||
"0x12daedb4014a51305d017ac2b895d7dae97d813b243a7d33ed0135ad58a7fbc80b8ebc25ee1ff1af7c9c21b4e4fdf5b213567233a168b9ec9b81ee411aa9937f9f89e847c26810bd5a177dfb9ad3a6a3ac1b46ddffcc32ce3b8277200d9bfb08",
|
||||
"0x175e64129e1d2394bca57349b2845248b7e92f41acdbb23ca1532898ca29fe08ce2a0ff8795c35fe711d6959ab1ba1051617085c03e25b57a689f5ecfa2ecda5ab0f5eff00079ea957283a482cf7de40839ed0683603af30768a3206ea816fd0",
|
||||
"0x02198d34ecd17c02740fed470c026a5e4e5fe2ad22fb5ba7b68eac3fc82ef37dd921f7ae8e1773f4c164b447f550760101a1e6108959ce1c98a64b96bd76c474360c630cc5977267855288c0f9fca4a205604a77aa655507169929d7fb52eb95",
|
||||
"0x071151c8ae74d6bcb6e24b466c59d242009deaf65cd82fb9b662c867a7c88377c115e90f8bb9bda528b9f1541ac15d850befd35dd79811c12a7065a075bb2a412e28592cdd307e75c4231c8ce3e3f8b6ac1a5a87666c55f4f8bc2d770ea5259b",
|
||||
"0x0f008453cef037d48e211c6eea1610304c9e1aedc28b8208b9f16ba471156a9ab870bbba871f9b26c3a31a67268df26b11c9e64194deb7b9b095903271d42753f1d66fe66d11a82f8e12096333b06d6f35e675ff650189cbb9a010776fd73a95",
|
||||
"0x0f51fec047929e2802c2840269daa7a7f082db690e6a49c16a749df8ab82a02eb7356f70dc8c7e68682695dfc815491e0172a3966c1e2effc8bc7d0fb462f033cdb93065c9e09d2865c99b494b5fabbddc86194a4b0109a4da58152990e68b93",
|
||||
"0x183d15a395bd62e4844440f3e9612db6315fd16f0b395155205464ecd9546477d797d353bc9c045acd18c5d9498071c416637dd87e22f4e26bfbf7b94d6cc073ab5cda9423f9f8a30cc024529ff8fea2c7338d34fec14a5f390b4fee213d4e41",
|
||||
"0x02d64a3c00693baa032b101608c51fc37aa1e99bf255bd4f293592ffd6bf2ffadfc842fc1dcf3b7142eeb3f8edce90e61885b1f5779fc3c818df56d05a48f96f9710652a8b43833a35fd947b9cf3081bc245bf6cf4c380c9b702e7b0b8338d86"
|
||||
]
|
||||
}
|
|
@ -8,11 +8,8 @@ time = "0.1"
|
|||
log = "0.4"
|
||||
rayon = "1.0"
|
||||
parking_lot = "0.4"
|
||||
bellman = "0.1"
|
||||
byteorder = "1.2"
|
||||
lazy_static = "1.2.0"
|
||||
pairing = "0.14.2"
|
||||
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" }
|
||||
primitives = { path = "../primitives" }
|
||||
chain = { path = "../chain" }
|
||||
serialization = { path = "../serialization" }
|
||||
|
@ -26,3 +23,4 @@ rustc-hex = "2"
|
|||
rand = "0.4"
|
||||
test-data = { path = "../test-data" }
|
||||
db = { path = "../db" }
|
||||
assert_matches = "1.3.0"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ser::Serializable;
|
||||
use crypto::Groth16VerifyingKey;
|
||||
use storage::{TransactionMetaProvider, TransactionOutputProvider, Nullifier, NullifierTag, NullifierTracker};
|
||||
use network::{ConsensusParams};
|
||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, SighashBase};
|
||||
|
@ -10,7 +11,6 @@ use canon::CanonTransaction;
|
|||
use constants::COINBASE_MATURITY;
|
||||
use error::TransactionError;
|
||||
use primitives::hash::H256;
|
||||
use chain::SAPLING_TX_VERSION_GROUP_ID;
|
||||
use VerificationLevel;
|
||||
|
||||
pub struct TransactionAcceptor<'a> {
|
||||
|
@ -21,8 +21,8 @@ pub struct TransactionAcceptor<'a> {
|
|||
pub overspent: TransactionOverspent<'a>,
|
||||
pub double_spent: TransactionDoubleSpend<'a>,
|
||||
pub eval: TransactionEval<'a>,
|
||||
pub join_split: Option<JoinSplitVerification<'a>>,
|
||||
pub sapling_valid: TransactionSaplingValid<'a>,
|
||||
pub join_split: JoinSplitVerification<'a>,
|
||||
pub sapling: SaplingVerification<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TransactionAcceptor<'a> {
|
||||
|
@ -47,13 +47,16 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
bip30: TransactionBip30::new_for_sync(transaction, meta_store),
|
||||
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
|
||||
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||
sapling_valid: TransactionSaplingValid::new(transaction),
|
||||
overspent: TransactionOverspent::new(transaction, output_store),
|
||||
double_spent: TransactionDoubleSpend::new(transaction, output_store),
|
||||
eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments),
|
||||
join_split: transaction.join_split().map(|js| {
|
||||
JoinSplitVerification::new(transaction.raw.version_group_id, js, nullifier_tracker)
|
||||
}),
|
||||
join_split: JoinSplitVerification::new(transaction, nullifier_tracker),
|
||||
sapling: SaplingVerification::new(
|
||||
nullifier_tracker,
|
||||
consensus.sapling_spend_verifying_key,
|
||||
consensus.sapling_output_verifying_key,
|
||||
transaction,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +71,8 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
// to make sure we're using the sighash-cache, let's make all sighash-related
|
||||
// calls from single checker && pass sighash to other checkers
|
||||
let sighash = self.eval.check()?;
|
||||
self.sapling_valid.check(sighash)?;
|
||||
self.join_split.check()?;
|
||||
self.sapling.check(sighash)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -472,11 +476,11 @@ impl<'a> TransactionSize<'a> {
|
|||
|
||||
/// Check the joinsplit proof of the transaction
|
||||
pub struct JoinSplitProof<'a> {
|
||||
_join_split: &'a chain::JoinSplit,
|
||||
_transaction: CanonTransaction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> JoinSplitProof<'a> {
|
||||
fn new(join_split: &'a chain::JoinSplit) -> Self { JoinSplitProof { _join_split: join_split }}
|
||||
fn new(transaction: CanonTransaction<'a>) -> Self { JoinSplitProof { _transaction: transaction }}
|
||||
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
// TODO: Zero-knowledge proof
|
||||
|
@ -484,49 +488,28 @@ impl<'a> JoinSplitProof<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if nullifiers are unique
|
||||
pub struct Nullifiers<'a> {
|
||||
tag: NullifierTag,
|
||||
/// Check if join split nullifiers are unique
|
||||
pub struct JoinSplitNullifiers<'a> {
|
||||
tracker: &'a NullifierTracker,
|
||||
join_split: &'a chain::JoinSplit,
|
||||
}
|
||||
|
||||
impl<'a> Nullifiers<'a> {
|
||||
fn new(tag: NullifierTag, tracker: &'a NullifierTracker, join_split: &'a chain::JoinSplit) -> Self {
|
||||
Nullifiers { tag: tag, tracker: tracker, join_split: join_split }
|
||||
}
|
||||
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
for description in self.join_split.descriptions.iter() {
|
||||
for nullifier in &description.nullifiers[..] {
|
||||
let check = Nullifier::new(self.tag, H256::from(&nullifier[..]));
|
||||
|
||||
if self.tracker.contains_nullifier(check) {
|
||||
return Err(TransactionError::JoinSplitDeclared(*check.hash()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that sapling signatures/proofs are valid.
|
||||
pub struct TransactionSaplingValid<'a> {
|
||||
transaction: CanonTransaction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TransactionSaplingValid<'a> {
|
||||
fn new(transaction: CanonTransaction<'a>) -> Self {
|
||||
TransactionSaplingValid {
|
||||
transaction: transaction,
|
||||
}
|
||||
impl<'a> JoinSplitNullifiers<'a> {
|
||||
fn new(tracker: &'a NullifierTracker, transaction: CanonTransaction<'a>) -> Self {
|
||||
JoinSplitNullifiers { tracker: tracker, transaction: transaction }
|
||||
}
|
||||
|
||||
fn check(&self, sighash: H256) -> Result<(), TransactionError> {
|
||||
if let Some(sapling) = self.transaction.raw.sapling.as_ref() {
|
||||
accept_sapling(&sighash, sapling)
|
||||
.map_err(|_| TransactionError::InvalidSapling)?;
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
if let Some(ref join_split) = self.transaction.raw.join_split {
|
||||
for description in join_split.descriptions.iter() {
|
||||
for nullifier in &description.nullifiers[..] {
|
||||
let check = Nullifier::new(NullifierTag::Sprout, H256::from(&nullifier[..]));
|
||||
|
||||
if self.tracker.contains_nullifier(check) {
|
||||
return Err(TransactionError::JoinSplitDeclared(*check.hash()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -536,19 +519,16 @@ impl<'a> TransactionSaplingValid<'a> {
|
|||
/// Join split verification
|
||||
pub struct JoinSplitVerification<'a> {
|
||||
proof: JoinSplitProof<'a>,
|
||||
nullifiers: Nullifiers<'a>,
|
||||
nullifiers: JoinSplitNullifiers<'a>,
|
||||
}
|
||||
|
||||
impl<'a> JoinSplitVerification<'a> {
|
||||
pub fn new(tx_version_group: u32, join_split: &'a chain::JoinSplit, tracker: &'a NullifierTracker)
|
||||
pub fn new(transaction: CanonTransaction<'a>, tracker: &'a NullifierTracker)
|
||||
-> Self
|
||||
{
|
||||
let tag = if tx_version_group == SAPLING_TX_VERSION_GROUP_ID
|
||||
{ NullifierTag::Sapling } else { NullifierTag::Sprout };
|
||||
|
||||
JoinSplitVerification {
|
||||
proof: JoinSplitProof::new(join_split),
|
||||
nullifiers: Nullifiers::new(tag, tracker, join_split),
|
||||
proof: JoinSplitProof::new(transaction),
|
||||
nullifiers: JoinSplitNullifiers::new(tracker, transaction),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,11 +538,96 @@ impl<'a> JoinSplitVerification<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if Sapling nullifiers are unique
|
||||
pub struct SaplingNullifiers<'a> {
|
||||
tracker: &'a NullifierTracker,
|
||||
transaction: CanonTransaction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SaplingNullifiers<'a> {
|
||||
fn new(tracker: &'a NullifierTracker, transaction: CanonTransaction<'a>) -> Self {
|
||||
SaplingNullifiers { tracker: tracker, transaction: transaction }
|
||||
}
|
||||
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
if let Some(ref sapling) = self.transaction.raw.sapling {
|
||||
for spend in &sapling.spends {
|
||||
let check = Nullifier::new(NullifierTag::Sapling, H256::from(&spend.nullifier[..]));
|
||||
|
||||
if self.tracker.contains_nullifier(check) {
|
||||
return Err(TransactionError::SaplingDeclared(*check.hash()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Checks that sapling signatures/proofs are valid.
|
||||
pub struct SaplingProof<'a> {
|
||||
spend_vk: &'a Groth16VerifyingKey,
|
||||
output_vk: &'a Groth16VerifyingKey,
|
||||
transaction: CanonTransaction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SaplingProof<'a> {
|
||||
fn new(
|
||||
spend_vk: &'a Groth16VerifyingKey,
|
||||
output_vk: &'a Groth16VerifyingKey,
|
||||
transaction: CanonTransaction<'a>,
|
||||
) -> Self {
|
||||
SaplingProof {
|
||||
spend_vk,
|
||||
output_vk,
|
||||
transaction: transaction,
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&self, sighash: H256) -> Result<(), TransactionError> {
|
||||
if let Some(sapling) = self.transaction.raw.sapling.as_ref() {
|
||||
accept_sapling(self.spend_vk, self.output_vk, &sighash, sapling)
|
||||
.map_err(|_| TransactionError::InvalidSapling)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sapling verification
|
||||
pub struct SaplingVerification<'a> {
|
||||
proof: SaplingProof<'a>,
|
||||
nullifiers: SaplingNullifiers<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SaplingVerification<'a> {
|
||||
pub fn new(
|
||||
tracker: &'a NullifierTracker,
|
||||
spend_vk: &'a Groth16VerifyingKey,
|
||||
output_vk: &'a Groth16VerifyingKey,
|
||||
transaction: CanonTransaction<'a>
|
||||
) -> Self
|
||||
{
|
||||
SaplingVerification {
|
||||
proof: SaplingProof::new(spend_vk, output_vk, transaction),
|
||||
nullifiers: SaplingNullifiers::new(tracker, transaction),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(&self, sighash: H256) -> Result<(), TransactionError> {
|
||||
self.proof.check(sighash)?;
|
||||
self.nullifiers.check()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use chain::Transaction;
|
||||
use chain::{Transaction, Sapling};
|
||||
use db::BlockChainDatabase;
|
||||
use script::{Script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, verify_script};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn join_split() {
|
||||
|
@ -593,4 +658,34 @@ mod tests {
|
|||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input_script, &output_script, &flags, &mut checker), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sapling_nullifiers_works() {
|
||||
let storage = BlockChainDatabase::init_test_chain(vec![test_data::genesis().into()]);
|
||||
|
||||
let tx: Transaction = test_data::TransactionBuilder::with_sapling(Sapling {
|
||||
spends: vec![Default::default()],
|
||||
..Default::default()
|
||||
}).into();
|
||||
let block = test_data::block_builder()
|
||||
.header().parent(test_data::genesis().hash()).build()
|
||||
.transaction().coinbase().build()
|
||||
.with_transaction(tx.clone())
|
||||
.build();
|
||||
let tx = tx.into();
|
||||
let block_hash = block.hash();
|
||||
|
||||
// when nullifier is not in the db
|
||||
assert_eq!(SaplingNullifiers::new(&storage, CanonTransaction::new(&tx)).check(), Ok(()));
|
||||
|
||||
// insert nullifier into db
|
||||
storage.insert(block.into()).unwrap();
|
||||
storage.canonize(&block_hash).unwrap();
|
||||
|
||||
// when nullifier is in the db
|
||||
assert_eq!(
|
||||
SaplingNullifiers::new(&storage, CanonTransaction::new(&tx)).check(),
|
||||
Err(TransactionError::SaplingDeclared(Default::default()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,5 +136,7 @@ pub enum TransactionError {
|
|||
JoinSplitVersionInvalid,
|
||||
/// Transaction sapling verification has failed.
|
||||
InvalidSapling,
|
||||
/// Sapling nullifier already revealed earlier in the chain.
|
||||
SaplingDeclared(H256),
|
||||
}
|
||||
|
||||
|
|
|
@ -56,15 +56,12 @@ extern crate time;
|
|||
extern crate log;
|
||||
extern crate parking_lot;
|
||||
extern crate rayon;
|
||||
extern crate bellman;
|
||||
extern crate byteorder;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
extern crate rustc_hex as hex;
|
||||
extern crate pairing;
|
||||
extern crate sapling_crypto;
|
||||
|
||||
extern crate storage;
|
||||
extern crate chain;
|
||||
|
@ -76,6 +73,10 @@ extern crate bitcrypto as crypto;
|
|||
#[cfg(test)]
|
||||
extern crate db;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate assert_matches;
|
||||
|
||||
pub mod constants;
|
||||
mod canon;
|
||||
mod deployments;
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
use std::io::Error as IoError;
|
||||
use chain::{Sapling, SaplingSpendDescription, SaplingOutputDescription};
|
||||
use pairing::{bls12_381::{Bls12, Fr, FrRepr}, PrimeField, PrimeFieldRepr, PrimeFieldDecodingError};
|
||||
use bellman::{SynthesisError, groth16::{verify_proof, PreparedVerifyingKey, Proof,}};
|
||||
|
||||
use sapling_crypto::{circuit::multipack, redjubjub::{self, Signature}};
|
||||
use sapling_crypto::jubjub::{edwards,fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown};
|
||||
use crypto::{
|
||||
Groth16VerifyingKey,
|
||||
pairing::{bls12_381::{Bls12, Fr, FrRepr}, PrimeField, PrimeFieldRepr, PrimeFieldDecodingError},
|
||||
bellman::{SynthesisError, groth16::{verify_proof, Proof}},
|
||||
sapling_crypto::{circuit::multipack, redjubjub::{self, Signature}},
|
||||
sapling_crypto::jubjub::{edwards,fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown}
|
||||
};
|
||||
|
||||
type Point = edwards::Point<Bls12, Unknown>;
|
||||
|
||||
lazy_static! {
|
||||
static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
|
||||
static ref SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
|
||||
static ref SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
|
||||
}
|
||||
|
||||
/// Errors that could occur during sapling verification.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Spend description verification error.
|
||||
Spend(usize, SpendError),
|
||||
|
@ -22,25 +23,27 @@ pub enum Error {
|
|||
Output(usize, OutputError),
|
||||
/// Invalid balance value.
|
||||
InvalidBalanceValue,
|
||||
/// Error deserializing/verifying binding_sig.
|
||||
BindingSig(SignatureError),
|
||||
/// Error verifying binding_sig.
|
||||
BadBindingSignature,
|
||||
}
|
||||
|
||||
/// Errors that can occur during spend description verification.
|
||||
#[derive(Debug)]
|
||||
pub enum SpendError {
|
||||
/// Error deserializing value commitment.
|
||||
ValueCommitment(PointError),
|
||||
/// Error deserializing anchor.
|
||||
Anchor(PrimeFieldDecodingError),
|
||||
/// Error deserializing randomized key.
|
||||
RandomizedKey(PublicKeyError),
|
||||
/// Error deserializing/verifying spend_auth_sig.
|
||||
SpendAuthSig(SignatureError),
|
||||
RandomizedKey(PointError),
|
||||
/// Error verifying spend_auth_sig.
|
||||
BadSpendAuthSig,
|
||||
/// Error deserializing/verifying zk-proof.
|
||||
Proof(ProofError),
|
||||
}
|
||||
|
||||
/// Errors that can occur during output description verification.
|
||||
#[derive(Debug)]
|
||||
pub enum OutputError {
|
||||
/// Error deserializing value commitment.
|
||||
ValueCommitment(PointError),
|
||||
|
@ -53,6 +56,7 @@ pub enum OutputError {
|
|||
}
|
||||
|
||||
/// Errors that can occur during point deserialization.
|
||||
#[derive(Debug)]
|
||||
pub enum PointError {
|
||||
/// The point is invalid.
|
||||
Invalid(IoError),
|
||||
|
@ -60,23 +64,8 @@ pub enum PointError {
|
|||
SmallOrder,
|
||||
}
|
||||
|
||||
/// Errors that can occur during public key deserialization.
|
||||
pub enum PublicKeyError {
|
||||
/// The public key is invalid.
|
||||
Invalid(IoError),
|
||||
/// The point corresponding to the public key MUST NOT be small order.
|
||||
SmallOrder,
|
||||
}
|
||||
|
||||
/// Error that can occur during signature deserialization/verification.
|
||||
pub enum SignatureError {
|
||||
/// The signature is invalid.
|
||||
Invalid(IoError),
|
||||
/// The signature verifciation has failed.
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// Proof verification error.
|
||||
#[derive(Debug)]
|
||||
pub enum ProofError {
|
||||
/// The proof is invalid.
|
||||
Invalid(IoError),
|
||||
|
@ -87,19 +76,24 @@ pub enum ProofError {
|
|||
}
|
||||
|
||||
/// Verify sapling proofs/signatures validity.
|
||||
pub fn accept_sapling(sighash: &[u8; 32], sapling: &Sapling) -> Result<(), Error> {
|
||||
pub fn accept_sapling(
|
||||
spend_vk: &Groth16VerifyingKey,
|
||||
output_vk: &Groth16VerifyingKey,
|
||||
sighash: &[u8; 32],
|
||||
sapling: &Sapling,
|
||||
) -> Result<(), Error> {
|
||||
// binding verification key is not encoded explicitly in transaction and must be recalculated
|
||||
let mut total = edwards::Point::zero();
|
||||
|
||||
// verify each spend description
|
||||
for (idx, spend) in sapling.spends.iter().enumerate() {
|
||||
accept_spend(sighash, &mut total, spend)
|
||||
accept_spend(spend_vk, sighash, &mut total, spend)
|
||||
.map_err(|err| Error::Spend(idx, err))?;
|
||||
}
|
||||
|
||||
// verify each output description
|
||||
for (idx, output) in sapling.outputs.iter().enumerate() {
|
||||
accept_output(&mut total, output)
|
||||
accept_output(output_vk, &mut total, output)
|
||||
.map_err(|err| Error::Output(idx, err))?;
|
||||
}
|
||||
|
||||
|
@ -108,7 +102,12 @@ pub fn accept_sapling(sighash: &[u8; 32], sapling: &Sapling) -> Result<(), Error
|
|||
}
|
||||
|
||||
/// Verify sapling spend description.
|
||||
fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescription) -> Result<(), SpendError> {
|
||||
fn accept_spend(
|
||||
spend_vk: &Groth16VerifyingKey,
|
||||
sighash: &[u8; 32],
|
||||
total: &mut Point,
|
||||
spend: &SaplingSpendDescription,
|
||||
) -> Result<(), SpendError> {
|
||||
// deserialize and check value commitment
|
||||
let value_commitment = require_non_small_order_point(&spend.value_commitment)
|
||||
.map_err(SpendError::ValueCommitment)?;
|
||||
|
@ -127,18 +126,18 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr
|
|||
|
||||
// deserialize and check randomized key
|
||||
let randomized_key = redjubjub::PublicKey::<Bls12>::read(&spend.randomized_key[..], &JUBJUB)
|
||||
.map_err(|err| SpendError::RandomizedKey(PublicKeyError::Invalid(err)))?;
|
||||
.map_err(|err| SpendError::RandomizedKey(PointError::Invalid(err)))?;
|
||||
if is_small_order(&randomized_key.0) {
|
||||
return Err(SpendError::RandomizedKey(PublicKeyError::SmallOrder));
|
||||
return Err(SpendError::RandomizedKey(PointError::SmallOrder));
|
||||
}
|
||||
|
||||
// deserialize the signature
|
||||
let spend_auth_sig = Signature::read(&spend.spend_auth_sig[..])
|
||||
.map_err(|err| SpendError::SpendAuthSig(SignatureError::Invalid(err)))?;
|
||||
.expect("only could fail if length of passed buffer != 64; qed");
|
||||
|
||||
// verify the spend_auth_sig
|
||||
if !randomized_key.verify(&data_to_be_signed, &spend_auth_sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB) {
|
||||
return Err(SpendError::SpendAuthSig(SignatureError::Failed));
|
||||
return Err(SpendError::BadSpendAuthSig);
|
||||
}
|
||||
|
||||
// Add the nullifier through multiscalar packing
|
||||
|
@ -164,8 +163,7 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr
|
|||
.map_err(|err| SpendError::Proof(ProofError::Invalid(err)))?;
|
||||
|
||||
// check the proof
|
||||
let verification_key = SAPLING_SPEND_VK.as_ref().expect("TODO");
|
||||
let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..])
|
||||
let is_verification_ok = verify_proof(&spend_vk.0, &zkproof, &public_input[..])
|
||||
.map_err(|err| SpendError::Proof(ProofError::Synthesis(err)))?;
|
||||
if !is_verification_ok {
|
||||
return Err(SpendError::Proof(ProofError::Failed));
|
||||
|
@ -174,7 +172,11 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn accept_output(total: &mut Point, output: &SaplingOutputDescription) -> Result<(), OutputError> {
|
||||
fn accept_output(
|
||||
output_vk: &Groth16VerifyingKey,
|
||||
total: &mut Point,
|
||||
output: &SaplingOutputDescription,
|
||||
) -> Result<(), OutputError> {
|
||||
// deserialize and check value commitment
|
||||
let value_commitment = require_non_small_order_point(&output.value_commitment)
|
||||
.map_err(OutputError::ValueCommitment)?;
|
||||
|
@ -206,8 +208,7 @@ fn accept_output(total: &mut Point, output: &SaplingOutputDescription) -> Result
|
|||
.map_err(|err| OutputError::Proof(ProofError::Invalid(err)))?;
|
||||
|
||||
// check the proof
|
||||
let verification_key = SAPLING_OUTPUT_VK.as_ref().expect("TODO");
|
||||
let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..])
|
||||
let is_verification_ok = verify_proof(&output_vk.0, &zkproof, &public_input[..])
|
||||
.map_err(|err| OutputError::Proof(ProofError::Synthesis(err)))?;
|
||||
if !is_verification_ok {
|
||||
return Err(OutputError::Proof(ProofError::Failed));
|
||||
|
@ -234,13 +235,13 @@ fn accept_sapling_final(sighash: &[u8; 32], total: Point, sapling: &Sapling) ->
|
|||
|
||||
// deserialize the binding signature
|
||||
let binding_sig = Signature::read(&sapling.binding_sig[..])
|
||||
.map_err(|err| Error::BindingSig(SignatureError::Invalid(err)))?;
|
||||
.expect("only could fail if length of passed buffer != 64; qed");
|
||||
|
||||
// check the binding signature
|
||||
let is_verification_ok = binding_verification_key
|
||||
.verify(&data_to_be_signed, &binding_sig, FixedGenerators::ValueCommitmentRandomness, &JUBJUB);
|
||||
if !is_verification_ok {
|
||||
return Err(Error::BindingSig(SignatureError::Failed));
|
||||
return Err(Error::BadBindingSignature);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -296,5 +297,239 @@ fn is_small_order(point: &Point) -> bool {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// TODO: detailed tests when sighash + verification keys are available
|
||||
extern crate test_data;
|
||||
|
||||
use chain::Transaction;
|
||||
use script::{TransactionInputSigner, SighashBase};
|
||||
use super::*;
|
||||
|
||||
// tx: https://zcash.blockexplorer.com/tx/bd4fe81c15cfbd125f5ca6fe51fb5ac4ef340e64a36f576a6a09f7528eb2e176
|
||||
fn test_tx() -> Transaction {
|
||||
"0400008085202f8900000000000072da060010270000000000000148b1c0668fce604361fbb1b89bbd76f8fee09b51a9dc0fdfcf6c6720cd596083d970234fcc0e9a70fdfed82d32fbb9ca92c9c5c3bad5daad9ac62b5bf4255817ee5bc95a9af453bb9cc7e2c544aa29efa20011a65b624998369c849aa8f0bc83d60e7902a3cfe6eeaeb8d583a491de5982c5ded29e64cd8f8fac594a5bb4f2838e6c30876e36a18d8d935238815c8d9205a4f1f523ff76b51f614bff1064d1c5fa0a27ec0c43c8a6c2714e7234d32e9a8934a3e9c0f74f1fdac2ddf6be3b13bc933b0478cae556a2d387cc23b05e8b0bd53d9e838ad2d2cb31daccefe256087511b044dfae665f0af0fa968edeea4cbb437a8099724159471adf7946eec434cccc1129f4d1e31d7f3f8be524226c65f28897d3604c14efb64bea6a889b2705617432927229dfa382e78c0ace31cc158fbf3ec1597242955e45af1ee5cfaffd789cc80dc53d6b18d42033ec2c327170e2811fe8ec00feadeb1033eb48ab24a6dce2480ad428be57c4619466fc3181ece69b914fed30566ff853250ef19ef7370601f4c24b0125e4059eec61f63ccbe277363172f2bdee384412ea073c5aca06b94e402ba3a43e15bd9c65bbfb194c561c24a031dec43be95c59eb6b568c176b1038d5b7b057dc032488335284adebfb6607e6a995b7fa418f13c8a61b343e5df44faa1050d9d76550748d9efebe01da97ade5937afd5f007ed26e0af03f283611655e91bc6a4857f66a57a1584ff687c4baf725f4a1b32fae53a3e6e8b98bca319bb1badb704c9c1a04f401f33d813d605eef6943c2c52dbc85ab7081d1f8f69d3202aae281bf42336a949a12a7dbbd22abdd6e92996282ebd69033c22cb0539d97f83636d6a8232209a7411e8b03bef180d83e608563ea2d0becff56dc996c2049df054961bfb21b7cbef5049a7dacc18f2c977aa1b2d48291abc19c3c8ea25d2e61901048354b17ce952f6f2248cf3a0eb54c19b507b41d7281c3d227e2b142ff695d8b925a4bb942ed9492a73a17468a8332a367fd16295420bdca6c04d380271f40440709998fce3a3af3e1e505f5402e5dd464dd179cb0eede3d494a95b84d2fb2eb5abb425cf2c712af999c65259c4782a5ec97388324c67738908a5ba43b6db62a10f50cddf9b5039123437c74165921ac8cf4f13292a216baef9d00bd544106b52755986c98a462ade1149f69367e926d88eb92798c0e56cd19a1bcf264fd93293033b758da65c7901eb5b4a17ee265a3312dbc477868da0057e1b3cbf47726dead6ecfcc8e1044c6f311ff0fc83192dc2f75a89626ba33364dac747b63ff3c8337e00332c8783ba9c8dc13cdf0750d7adc3926fbe1279017d50adba35c38c5b810f73abe5d759cd7fb650f6b0a1f78dc1f62fd017090ff4de4cf54c883752ddda68083d4617ed2c38bab8da313965dd3f7b755aec23a2d9e2965d08d2134827a72ffb3bd65b1fd5410da105bfba7a74ddff0928a654aca1ee211ac9dce8019ddcbb52263ce44b2544a314355c1e8c8543f3ed3e883e7a7a8f9e3c7c11f41ab9069854fb21e9b3660a860df19d289d54b29d82522b32d187cde6261eb0a429c3994dff6f37b9ab9102281223e3cd584790a909e05ba0ea1a2d9aef8e571986e98e09312dccaf8e739d718a1edd217dc4c8a5c8a650015405b592a7c674a451d7d1686c7ea6d93e74a8fe4ade12b679ac780457f08a79bfbf96dcf7eefe9a39b99f1ae39d2c5f86aadf156b7d5ce4b2733f307cfe1e1ff6de0ff2006d9cba535b0c40dfb7a98399cdff8e681fc38c7b9aa94ee5eb89432e28d94ee27f238776ba964a87caf58eddbb64771e64de094305a8eb848d2d9ad6373903687d22170f48f1ae8d714514034ee2733857af4747312bb006e6ce3918ede8c730bacc7821b81c1b93bb50b219e79e8e0d74531ed18c1145632d9847d38783b49141ac5353aaa7d125fb2934e681467e16b28090978e74e0b".into()
|
||||
}
|
||||
|
||||
fn compute_sighash(tx: Transaction) -> [u8; 32] {
|
||||
let signer: TransactionInputSigner = tx.into();
|
||||
signer.signature_hash(&mut None, None, 0, &From::from(vec![]), SighashBase::All.into(), 0x76b809bb).into()
|
||||
}
|
||||
|
||||
fn run_accept_sapling(tx: Transaction) -> Result<(), Error> {
|
||||
let spend_vk = crypto::load_sapling_spend_verifying_key().unwrap();
|
||||
let output_vk = crypto::load_sapling_output_verifying_key().unwrap();
|
||||
|
||||
let sighash = compute_sighash(tx.clone());
|
||||
let sapling = tx.sapling.unwrap();
|
||||
|
||||
accept_sapling(&spend_vk, &output_vk, &sighash, &sapling)
|
||||
}
|
||||
|
||||
fn swap_xy(point: [u8; 32]) -> [u8; 32] {
|
||||
let mut new_point = [0; 32];
|
||||
new_point[..16].copy_from_slice(&point[16..]);
|
||||
new_point[16..].copy_from_slice(&point[..16]);
|
||||
new_point
|
||||
}
|
||||
|
||||
fn small_order_point() -> [u8; 32] {
|
||||
[0; 32]
|
||||
}
|
||||
|
||||
fn not_in_field_number() -> [u8; 32] {
|
||||
[0xFF; 32]
|
||||
}
|
||||
|
||||
fn bad_signature() -> [u8; 64] {
|
||||
[0; 64]
|
||||
}
|
||||
|
||||
fn bad_proof() -> [u8; 192] {
|
||||
[0; 192]
|
||||
}
|
||||
|
||||
fn bad_verifying_key() -> Groth16VerifyingKey {
|
||||
use crypto::pairing::{CurveAffine, bls12_381::{G1Affine, G2Affine}};
|
||||
use crypto::bellman::groth16::{VerifyingKey, prepare_verifying_key};
|
||||
|
||||
Groth16VerifyingKey(prepare_verifying_key(&VerifyingKey {
|
||||
alpha_g1: G1Affine::zero(),
|
||||
beta_g1: G1Affine::zero(),
|
||||
beta_g2: G2Affine::zero(),
|
||||
gamma_g2: G2Affine::zero(),
|
||||
delta_g1: G1Affine::zero(),
|
||||
delta_g2: G2Affine::zero(),
|
||||
ic: vec![],
|
||||
}))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_sapling_works() {
|
||||
run_accept_sapling(test_tx()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_spend_fails() {
|
||||
let spend_vk = crypto::load_sapling_spend_verifying_key().unwrap();
|
||||
let sighash = compute_sighash(test_tx());
|
||||
let sapling = test_tx().sapling.unwrap();
|
||||
let mut total = edwards::Point::zero();
|
||||
|
||||
// when value commitment isn't an on-curve point
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.value_commitment = swap_xy(spend.value_commitment);
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::ValueCommitment(PointError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when value commitment is a small order point
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.value_commitment = small_order_point();
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::ValueCommitment(PointError::SmallOrder))
|
||||
);
|
||||
|
||||
// when anchor is not in field
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.anchor = not_in_field_number();
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::Anchor(_))
|
||||
);
|
||||
|
||||
// when randomized key isn't represented by an on-curve point
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.randomized_key = swap_xy(spend.randomized_key);
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::RandomizedKey(PointError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when randomized key is represented by a small order point
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.randomized_key = small_order_point();
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::RandomizedKey(PointError::SmallOrder))
|
||||
);
|
||||
|
||||
// when spend auth signature verification fails
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.spend_auth_sig = bad_signature();
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::BadSpendAuthSig)
|
||||
);
|
||||
|
||||
// when proof is failed to deserialize
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.zkproof = bad_proof();
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::Proof(ProofError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when proof isn't compatible with verifying key
|
||||
assert_matches!(
|
||||
accept_spend(&bad_verifying_key(), &sighash, &mut total, &sapling.spends[0]),
|
||||
Err(SpendError::Proof(ProofError::Synthesis(_)))
|
||||
);
|
||||
|
||||
// when proof verification has failed
|
||||
let mut spend = sapling.spends[0].clone();
|
||||
spend.nullifier = [0; 32];
|
||||
assert_matches!(
|
||||
accept_spend(&spend_vk, &sighash, &mut total, &spend),
|
||||
Err(SpendError::Proof(ProofError::Failed))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_output_fails() {
|
||||
let output_vk = crypto::load_sapling_output_verifying_key().unwrap();
|
||||
let sapling = test_tx().sapling.unwrap();
|
||||
let mut total = edwards::Point::zero();
|
||||
|
||||
// when value commitment isn't an on-curve point
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.value_commitment = swap_xy(sapling.spends[0].value_commitment);
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::ValueCommitment(PointError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when value commitment is a small order point
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.value_commitment = small_order_point();
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::ValueCommitment(PointError::SmallOrder))
|
||||
);
|
||||
|
||||
// when note commitment is not in field
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.note_commitment = not_in_field_number();
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::NoteCommitment(_))
|
||||
);
|
||||
|
||||
// when empeheral key isn't represented by an on-curve point
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.ephemeral_key = swap_xy(output.ephemeral_key);
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::EphemeralKey(PointError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when empeheral key is represented by a small order point
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.ephemeral_key = small_order_point();
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::EphemeralKey(PointError::SmallOrder))
|
||||
);
|
||||
|
||||
// when proof is failed to deserialize
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.zkproof = bad_proof();
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::Proof(ProofError::Invalid(_)))
|
||||
);
|
||||
|
||||
// when proof isn't compatible with verifying key
|
||||
assert_matches!(
|
||||
accept_output(&bad_verifying_key(), &mut total, &sapling.outputs[0]),
|
||||
Err(OutputError::Proof(ProofError::Synthesis(_)))
|
||||
);
|
||||
|
||||
// when proof verification has failed
|
||||
let mut output = sapling.outputs[0].clone();
|
||||
output.note_commitment = output.value_commitment.clone();
|
||||
assert_matches!(
|
||||
accept_output(&output_vk, &mut total, &output),
|
||||
Err(OutputError::Proof(ProofError::Failed))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_sapling_final_fails() {
|
||||
let sighash = compute_sighash(test_tx().clone());
|
||||
let sapling = test_tx().sapling.unwrap();
|
||||
|
||||
// when total value is -i64::MAX
|
||||
let mut bad_sapling = sapling.clone();
|
||||
bad_sapling.balancing_value = ::std::i64::MIN;
|
||||
assert_matches!(
|
||||
accept_sapling_final(&sighash, Point::zero(), &bad_sapling),
|
||||
Err(Error::InvalidBalanceValue)
|
||||
);
|
||||
|
||||
// when proof verification has failed
|
||||
assert_matches!(
|
||||
accept_sapling_final(&sighash, Point::zero(), &sapling),
|
||||
Err(Error::BadBindingSignature)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue