Merge remote-tracking branch 'origin/master' into nv2

# Conflicts:
#	Cargo.lock
This commit is contained in:
NikVolf 2018-12-27 17:59:32 +03:00
commit 899b499466
34 changed files with 723 additions and 361 deletions

12
Cargo.lock generated
View File

@ -126,7 +126,7 @@ dependencies = [
"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",
"rust-crypto 0.2.36 (git+https://github.com/nikvolf/rust-crypto?branch=no-pad)",
"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)",
@ -807,6 +807,7 @@ version = "0.1.0"
dependencies = [
"bitcrypto 0.1.0",
"chain 0.1.0",
"keys 0.1.0",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0",
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1152,6 +1153,7 @@ dependencies = [
[[package]]
name = "rust-crypto"
version = "0.2.36"
source = "git+https://github.com/nikvolf/rust-crypto?branch=no-pad#4acc67cb46bb4e1b5b30d9c787a9eab5c8ac9a34"
dependencies = [
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1340,7 +1342,6 @@ name = "storage"
version = "0.1.0"
dependencies = [
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitcrypto 0.1.0",
"chain 0.1.0",
"display_derive 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1446,6 +1447,7 @@ name = "test-data"
version = "0.1.0"
dependencies = [
"chain 0.1.0",
"network 0.1.0",
"primitives 0.1.0",
"script 0.1.0",
"serialization 0.1.0",
@ -1585,6 +1587,7 @@ dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"chain 0.1.0",
"db 0.1.0",
"keys 0.1.0",
"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",
@ -1675,6 +1678,10 @@ name = "yaml-rust"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[patch.unused]]
name = "rust-crypto"
version = "0.2.36"
[metadata]
"checksum abstract-ns 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2f451afbdf8ed8c8f8a98433055bb9a6b7a72aef4baff16227d2a43dd547f43b"
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
@ -1787,6 +1794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5"
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rust-crypto 0.2.36 (git+https://github.com/nikvolf/rust-crypto?branch=no-pad)" = "<none>"
"checksum rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b03280c2813907a030785570c577fb27d3deec8da4c18566751ade94de0ace"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69"

View File

@ -25,6 +25,7 @@ pub fn main(benchmark: &mut Benchmark) {
// test setup
let genesis = test_data::genesis();
let consensus = ConsensusParams::new(Network::Unitest);
let mut rolling_hash = genesis.hash();
let mut blocks: Vec<IndexedBlock> = Vec::new();
@ -44,6 +45,7 @@ pub fn main(benchmark: &mut Benchmark) {
.merkled_header()
.parent(rolling_hash.clone())
.nonce((x as u8).into())
.time(consensus.pow_target_spacing * 7 * (x as u32))
.build()
.build();
rolling_hash = next_block.hash();
@ -88,6 +90,7 @@ pub fn main(benchmark: &mut Benchmark) {
builder
.merkled_header()
.parent(rolling_hash.clone())
.time(consensus.pow_target_spacing * 7 * ((b + BLOCKS_INITIAL) as u32))
.build()
.build()
.into());
@ -96,7 +99,7 @@ pub fn main(benchmark: &mut Benchmark) {
assert_eq!(store.best_block().hash, rolling_hash);
let chain_verifier = ChainVerifier::new(store.clone(), ConsensusParams::new(Network::Unitest));
let chain_verifier = ChainVerifier::new(store.clone(), consensus);
// bench
benchmark.start();

View File

@ -7,7 +7,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
bellman = "0.1"
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" }
pairing = "0.14.2"
rust-crypto = "0.2.36"
rust-crypto = { git = "https://github.com/nikvolf/rust-crypto", branch = "no-pad" }
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" }
serde_json = "1.0"
siphasher = "0.1.1"

View File

@ -162,6 +162,20 @@ pub fn sha256(input: &[u8]) -> H256 {
result
}
/// SHA-256
#[inline]
pub fn sha256_compress(left: &[u8], right: &[u8]) -> H256 {
assert_eq!(left.len(), 32, "sha-256-compress 1st argument should be 32-byte length (half-block)");
assert_eq!(right.len(), 32, "sha-256-compress 2nd argument should be 32-byte length (half-block)");
let mut result = H256::default();
let mut hasher = Sha256::new();
hasher.input(left);
hasher.input(right);
hasher.result_no_padding(&mut *result);
result
}
/// SHA-256 and RIPEMD160
#[inline]
pub fn dhash160(input: &[u8]) -> H160 {
@ -215,7 +229,8 @@ impl ::std::fmt::Debug for Groth16VerifyingKey {
#[cfg(test)]
mod tests {
use primitives::bytes::Bytes;
use super::{ripemd160, sha1, sha256, dhash160, dhash256, siphash24, checksum};
use primitives::hash::H256;
use super::{ripemd160, sha1, sha256, dhash160, dhash256, siphash24, checksum, sha256_compress};
#[test]
fn test_ripemd160() {
@ -268,4 +283,83 @@ mod tests {
fn test_checksum() {
assert_eq!(checksum(b"hello"), "9595c9df".into());
}
#[test]
fn half_empty_compress() {
let vectors = vec![
"da5698be17b9b46962335799779fbeca8ce5d491c0d26243bafef9ea1837a9d8",
"dc766fab492ccf3d1e49d4f374b5235fa56506aac2224d39f943fcd49202974c",
"3f0a406181105968fdaee30679e3273c66b72bf9a7f5debbf3b5a0a26e359f92",
"26b0052694fc42fdff93e6fb5a71d38c3dd7dc5b6ad710eb048c660233137fab",
"0109ecc0722659ff83450b8f7b8846e67b2859f33c30d9b7acd5bf39cae54e31",
"3f909b8ce3d7ffd8a5b30908f605a03b0db85169558ddc1da7bbbcc9b09fd325",
"40460fa6bc692a06f47521a6725a547c028a6a240d8409f165e63cb54da2d23f",
"8c085674249b43da1b9a31a0e820e81e75f342807b03b6b9e64983217bc2b38e",
"a083450c1ba2a3a7be76fad9d13bc37be4bf83bd3e59fc375a36ba62dc620298",
"1ddddabc2caa2de9eff9e18c8c5a39406d7936e889bc16cfabb144f5c0022682",
"c22d8f0b5e4056e5f318ba22091cc07db5694fbeb5e87ef0d7e2c57ca352359e",
"89a434ae1febd7687eceea21d07f20a2512449d08ce2eee55871cdb9d46c1233",
"7333dbffbd11f09247a2b33a013ec4c4342029d851e22ba485d4461851370c15",
"5dad844ab9466b70f745137195ca221b48f346abd145fb5efc23a8b4ba508022",
"507e0dae81cbfbe457fd370ef1ca4201c2b6401083ddab440e4a038dc1e358c4",
"bdcdb3293188c9807d808267018684cfece07ac35a42c00f2c79b4003825305d",
"bab5800972a16c2c22530c66066d0a5867e987bed21a6d5a450b683cf1cfd709",
"11aa0b4ad29b13b057a31619d6500d636cd735cdd07d811ea265ec4bcbbbd058",
"5145b1b055c2df02b95675e3797b91de1b846d25003c0a803d08900728f2cd6a",
"0323f2850bf3444f4b4c5c09a6057ec7169190f45acb9e46984ab3dfcec4f06a",
"671546e26b1da1af754531e26d8a6a51073a57ddd72dc472efb43fcb257cffff",
"bb23a9bba56de57cb284b0d2b01c642cf79c9a5563f0067a21292412145bd78a",
"f30cc836b9f71b4e7ee3c72b1fd253268af9a27e9d7291a23d02821b21ddfd16",
"58a2753dade103cecbcda50b5ebfce31e12d41d5841dcc95620f7b3d50a1b9a1",
"925e6d474a5d8d3004f29da0dd78d30ae3824ce79dfe4934bb29ec3afaf3d521",
"08f279618616bcdd4eadc9c7a9062691a59b43b07e2c1e237f17bd189cd6a8fe",
"c92b32db42f42e2bf0a59df9055be5c669d3242df45357659b75ae2c27a76f50",
"c0db2a74998c50eb7ba6534f6d410efc27c4bb88acb0222c7906ea28a327b511",
"d7c612c817793191a1e68652121876d6b3bde40f4fa52bc314145ce6e5cdd259",
"b22370106c67a17209f6130bc09f735d83aa2c04fc4fe72ea5d80b216723e7ce",
"9f67d5f664664c901940eee3d02dd5b3e4b92e7b42820c42fc5159e91b41172a",
"ac58cd1388fec290d398f1944b564449a63c815880566bd1d189f7839e3b0c8c",
"5698eae7c8515ed05a70339bdf7c1028e7acca13a4fa97d9538f01ac8d889ae3",
"2d4995770a76fb93314ca74b3524ea1db5688ad0a76183ea17204a8f024a9f3b",
"5e8992c1b072c16e9e28a85358fb5fb6901a81587766dadb7aa0b973ded2f264",
"e95db71e1f7291ba5499461bc715203e29b84bfa4283e3bb7f470a15d0e1584e",
"41f078bd1824c8a4b71964f394aa595084d8eb17b97a3630433af70d10e0eff6",
"a1913fe6b20132312f8c1f00ddd63cec7a03f5f1d7d83492fa284c0b5d6320b0",
"ba9440c4dbfcf55ceb605a5b8990fc11f8ef22870d8d12e130f986491eae84b3",
"49db2d5e22b8015cae4810d75e54014c5469862738e161ec96ec20218718828a",
"d4851fb8431edfbb8b1e85ada6895967c2dac87df344992a05faf1ecf836eec9",
"e4ab9f4470f00cd196d47c75c82e7adaf06fe17e042e3953d93bb5d56d8cd8fb",
"7e4320434849ecb357f1afaaba21a54400ef2d11cff83b937d87fdafa49f8199",
"020adc98d96cfbbcca15fc3aa03760ed286686c35b5d92c7cb64a999b394a854",
"3a26b29fe1acfdd6c6a151bcc3dbcb95a10ebe2f0553f80779569b67b7244e77",
"ec2d0986e6a0ddf43897b2d4f23bb034f538ffe00827f310dc4963f3267f0bfb",
"d48073f8819f81f0358e3fc35a047cc74082ae1cb7ee22fb609c01649342d0e6",
"ad8037601793f172441ecb00dc138d9fc5957125ecc382ec65e36f817dc799fb",
"ca500a5441f36f4df673d6b8ed075d36dae2c7e6481428c70a5a76b7a9bebce8",
"422b6ddd473231dc4d56fe913444ccd56f7c61f747ba57ca946d5fef72d840a0",
"ab41f4ecb7d7089615800e19fcc53b8379ed05ee35c82567095583fd90ff3035",
"bbf7618248354ceb1bc1fc9dbc42c426a4e2c1e0d443c5683a9256c62ecdc26f",
"e50ae71479fc8ec569192a13072e011afc249f471af09500ea39f75d0af856bf",
"e74c0b9220147db2d50a3b58d413775d16c984690be7d90f0bc43d99dba1b689",
"29324a0a48d11657a51ba08b004879bfcfc66a1acb7ce36dfe478d2655484b48",
"88952e3d0ac06cb16b665201122249659a22325e01c870f49e29da6b1757e082",
"cdf879f2435b95af042a3bf7b850f7819246c805285803d67ffbf4f295bed004",
"e005e324200b4f428c62bc3331e695c373607cd0faa9790341fa3ba1ed228bc5",
"354447727aa9a53dd8345b6b6c693443e56ef4aeba13c410179fc8589e7733d5",
"da52dda91f2829c15c0e58d29a95360b86ab30cf0cac8101832a29f38c3185f1",
"c7da7814e228e1144411d78b536092fe920bcdfcc36cf19d1259047b267d58b5",
"aba1f68b6c2b4db6cc06a7340e12313c4b4a4ea6deb17deb3e1e66cd8eacf32b",
"c160ae4f64ab764d864a52ad5e33126c4b5ce105a47deedd75bc70199a5247ef",
"eadf23fc99d514dd8ea204d223e98da988831f9b5d1940274ca520b7fb173d8a",
"5b8e14facac8a7c7a3bfee8bae71f2f7793d3ad5fe3383f93ab6061f2a11bb02"
];
let mut next = H256::from(&[0u8; 32][..]);
for idx in 0..vectors.len() {
next = sha256_compress(&next[..], &next[..]);
assert_eq!(next, H256::from(vectors[idx]));
}
}
}

View File

@ -13,7 +13,7 @@ use crypto::checksum;
use network::Network;
use {DisplayLayout, Error, AddressHash};
/// There are two address formats currently in use.
/// There are two transparent address formats currently in use.
/// https://bitcoin.org/en/developer-reference#address-conversion
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Type {
@ -38,7 +38,7 @@ pub struct Address {
pub hash: AddressHash,
}
pub struct AddressDisplayLayout([u8; 25]);
pub struct AddressDisplayLayout([u8; 26]);
impl Deref for AddressDisplayLayout {
type Target = [u8];
@ -52,41 +52,41 @@ impl DisplayLayout for Address {
type Target = AddressDisplayLayout;
fn layout(&self) -> Self::Target {
let mut result = [0u8; 25];
let mut result = [0u8; 26];
result[0] = match (self.network, self.kind) {
(Network::Mainnet, Type::P2PKH) => 0,
(Network::Mainnet, Type::P2SH) => 5,
(Network::Testnet, Type::P2PKH) => 111,
(Network::Testnet, Type::P2SH) => 196,
};
result[..2].copy_from_slice(&match (self.network, self.kind) {
(Network::Mainnet, Type::P2PKH) => [0x1C, 0xB8],
(Network::Testnet, Type::P2PKH) => [0x1D, 0x25],
(Network::Mainnet, Type::P2SH) => [0x1C, 0xBD],
(Network::Testnet, Type::P2SH) => [0x1C, 0xBA],
});
result[1..21].copy_from_slice(&*self.hash);
let cs = checksum(&result[0..21]);
result[21..25].copy_from_slice(&*cs);
result[2..22].copy_from_slice(&*self.hash);
let cs = checksum(&result[0..22]);
result[22..].copy_from_slice(&*cs);
AddressDisplayLayout(result)
}
fn from_layout(data: &[u8]) -> Result<Self, Error> where Self: Sized {
if data.len() != 25 {
if data.len() != 26 {
return Err(Error::InvalidAddress);
}
let cs = checksum(&data[0..21]);
if &data[21..] != &*cs {
let cs = checksum(&data[..22]);
if &data[22..] != &*cs {
return Err(Error::InvalidChecksum);
}
let (network, kind) = match data[0] {
0 => (Network::Mainnet, Type::P2PKH),
5 => (Network::Mainnet, Type::P2SH),
111 => (Network::Testnet, Type::P2PKH),
196 => (Network::Testnet, Type::P2SH),
let (network, kind) = match (data[0], data[1]) {
(0x1C, 0xB8) => (Network::Mainnet, Type::P2PKH),
(0x1C, 0xBD) => (Network::Mainnet, Type::P2SH),
(0x1D, 0x25) => (Network::Testnet, Type::P2PKH),
(0x1C, 0xBA) => (Network::Testnet, Type::P2SH),
_ => return Err(Error::InvalidAddress),
};
let mut hash = AddressHash::default();
hash.copy_from_slice(&data[1..21]);
hash.copy_from_slice(&data[2..22]);
let address = Address {
kind: kind,
@ -129,10 +129,10 @@ mod tests {
let address = Address {
kind: Type::P2PKH,
network: Network::Mainnet,
hash: "3f4aa1fedf1f54eeb03b759deadb36676b184911".into(),
hash: "ff197b14e502ab41f3bc8ccb48c4abac9eab35bc".into(),
};
assert_eq!("16meyfSoQV6twkAAxPe51RtMVz7PGRmWna".to_owned(), address.to_string());
assert_eq!("t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".to_owned(), address.to_string());
}
#[test]
@ -140,9 +140,9 @@ mod tests {
let address = Address {
kind: Type::P2PKH,
network: Network::Mainnet,
hash: "3f4aa1fedf1f54eeb03b759deadb36676b184911".into(),
hash: "ff197b14e502ab41f3bc8ccb48c4abac9eab35bc".into(),
};
assert_eq!(address, "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna".into());
assert_eq!(address, "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into());
}
}

View File

@ -92,17 +92,15 @@ mod tests {
use super::KeyPair;
/// Tests from:
/// https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/test/key_tests.cpp
const SECRET_0: &'static str = "5KSCKP8NUyBZPCCQusxRwgmz9sfvJQEgbGukmmHepWw5Bzp95mu";
/// https://github.com/zcash/zcash/blob/66e39a0dd6ffd77bcbede1943195dd456f859cd6/src/test/key_tests.cpp#L25
const SECRET_1: &'static str = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
const SECRET_2: &'static str = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
const SECRET_1C: &'static str = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
const SECRET_2C: &'static str = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
const ADDRESS_0: &'static str = "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna";
const ADDRESS_1: &'static str = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ";
const ADDRESS_2: &'static str = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ";
const ADDRESS_1C: &'static str = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs";
const ADDRESS_2C: &'static str = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs";
const ADDRESS_1: &'static str = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe";
const ADDRESS_2: &'static str = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst";
const ADDRESS_1C: &'static str = "t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC";
const ADDRESS_2C: &'static str = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6";
const SIGN_1: &'static str = "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
const SIGN_2: &'static str = "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d";
const SIGN_COMPACT_1: &'static str = "1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
@ -148,7 +146,6 @@ mod tests {
#[test]
fn test_keypair_address() {
assert!(check_addresses(SECRET_0, ADDRESS_0));
assert!(check_addresses(SECRET_1, ADDRESS_1));
assert!(check_addresses(SECRET_2, ADDRESS_2));
assert!(check_addresses(SECRET_1C, ADDRESS_1C));
@ -157,7 +154,6 @@ mod tests {
#[test]
fn test_keypair_is_compressed() {
assert!(check_compressed(SECRET_0, false));
assert!(check_compressed(SECRET_1, false));
assert!(check_compressed(SECRET_2, false));
assert!(check_compressed(SECRET_1C, true));
@ -197,7 +193,6 @@ mod tests {
#[test]
fn test_recover_compact() {
let message = b"Very deterministic message";
assert!(check_recover_compact(SECRET_0, message));
assert!(check_recover_compact(SECRET_1, message));
assert!(check_recover_compact(SECRET_1C, message));
assert!(check_recover_compact(SECRET_2, message));

View File

@ -5,7 +5,7 @@ use chain::{OutPoint, TransactionOutput, IndexedTransaction};
use storage::{SharedStore, TransactionOutputProvider};
use network::ConsensusParams;
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
use verification::{work_required, block_reward_satoshi, transaction_sigops};
use verification::{work_required, transaction_sigops};
const BLOCK_VERSION: u32 = 0x20000000;
const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4;
@ -252,10 +252,11 @@ impl BlockAssembler {
let best_block = store.best_block();
let previous_header_hash = best_block.hash;
let height = best_block.number + 1;
let bits = work_required(previous_header_hash.clone(), height, store.as_block_header_provider(), consensus);
let bits = work_required(previous_header_hash.clone(), time, height, store.as_block_header_provider(), consensus);
let version = BLOCK_VERSION;
let mut coinbase_value = block_reward_satoshi(height);
// TODO: sync with ZCash RPC - need to return founder reward?
let mut miner_reward = consensus.miner_reward(height);
let mut transactions = Vec::new();
let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore);
@ -270,7 +271,7 @@ impl BlockAssembler {
for entry in tx_iter {
// miner_fee is i64, but we can safely cast it to u64
// memory pool should restrict miner fee to be positive
coinbase_value += entry.miner_fee as u64;
miner_reward += entry.miner_fee as u64;
let tx = IndexedTransaction::new(entry.hash.clone(), entry.transaction.clone());
transactions.push(tx);
}
@ -282,7 +283,7 @@ impl BlockAssembler {
bits: bits,
height: height,
transactions: transactions,
coinbase_value: coinbase_value,
coinbase_value: miner_reward,
size_limit: self.max_block_size,
sigop_limit: self.max_block_sigops,
}

View File

@ -6,6 +6,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
[dependencies]
lazy_static = "1.0"
chain = { path = "../chain" }
keys = { path = "../keys" }
primitives = { path = "../primitives" }
serialization = { path = "../serialization" }
bitcrypto = { path = "../crypto" }

View File

@ -1,3 +1,4 @@
use keys::Address;
use {Network, Magic, Deployment, crypto};
lazy_static! {
@ -46,6 +47,25 @@ pub struct ConsensusParams {
pub pow_max_adjust_up: u32,
/// Optimal blocks interval (in seconds).
pub pow_target_spacing: u32,
/// Allow minimal difficulty after block at given height.
pub pow_allow_min_difficulty_after_height: Option<u32>,
/// 'Slow start' interval parameter.
///
/// For details on how (and why) ZCash 'slow start' works, refer to:
/// https://z.cash/support/faq/#what-is-slow-start-mining
/// https://github.com/zcash/zcash/issues/762
pub subsidy_slow_start_interval: u32,
/// Block subsidy halving interval.
///
/// Block subsidy is halved every `subsidy_halving_interval` blocks.
/// There are 64 halving intervals in total.
pub subsidy_halving_interval: u32,
/// The vector of addresses where founders reward goes.
///
/// For details on what's founders' reward, refer to:
/// https://z.cash/support/faq/#founders-reward
pub founders_addresses: Vec<Address>,
/// Equihash (N, K) parameters.
pub equihash_params: Option<(u32, u32)>,
@ -143,6 +163,60 @@ impl ConsensusParams {
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
pow_allow_min_difficulty_after_height: None,
subsidy_slow_start_interval: 20_000,
subsidy_halving_interval: 840_000,
founders_addresses: vec![
"t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".into(),
"t3cL9AucCajm3HXDhb5jBnJK2vapVoXsop3".into(),
"t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR".into(),
"t3TgZ9ZT2CTSK44AnUPi6qeNaHa2eC7pUyF".into(),
"t3SpkcPQPfuRYHsP5vz3Pv86PgKo5m9KVmx".into(),
"t3Xt4oQMRPagwbpQqkgAViQgtST4VoSWR6S".into(),
"t3ayBkZ4w6kKXynwoHZFUSSgXRKtogTXNgb".into(),
"t3adJBQuaa21u7NxbR8YMzp3km3TbSZ4MGB".into(),
"t3K4aLYagSSBySdrfAGGeUd5H9z5Qvz88t2".into(),
"t3RYnsc5nhEvKiva3ZPhfRSk7eyh1CrA6Rk".into(),
"t3Ut4KUq2ZSMTPNE67pBU5LqYCi2q36KpXQ".into(),
"t3ZnCNAvgu6CSyHm1vWtrx3aiN98dSAGpnD".into(),
"t3fB9cB3eSYim64BS9xfwAHQUKLgQQroBDG".into(),
"t3cwZfKNNj2vXMAHBQeewm6pXhKFdhk18kD".into(),
"t3YcoujXfspWy7rbNUsGKxFEWZqNstGpeG4".into(),
"t3bLvCLigc6rbNrUTS5NwkgyVrZcZumTRa4".into(),
"t3VvHWa7r3oy67YtU4LZKGCWa2J6eGHvShi".into(),
"t3eF9X6X2dSo7MCvTjfZEzwWrVzquxRLNeY".into(),
"t3esCNwwmcyc8i9qQfyTbYhTqmYXZ9AwK3X".into(),
"t3M4jN7hYE2e27yLsuQPPjuVek81WV3VbBj".into(),
"t3gGWxdC67CYNoBbPjNvrrWLAWxPqZLxrVY".into(),
"t3LTWeoxeWPbmdkUD3NWBquk4WkazhFBmvU".into(),
"t3P5KKX97gXYFSaSjJPiruQEX84yF5z3Tjq".into(),
"t3f3T3nCWsEpzmD35VK62JgQfFig74dV8C9".into(),
"t3Rqonuzz7afkF7156ZA4vi4iimRSEn41hj".into(),
"t3fJZ5jYsyxDtvNrWBeoMbvJaQCj4JJgbgX".into(),
"t3Pnbg7XjP7FGPBUuz75H65aczphHgkpoJW".into(),
"t3WeKQDxCijL5X7rwFem1MTL9ZwVJkUFhpF".into(),
"t3Y9FNi26J7UtAUC4moaETLbMo8KS1Be6ME".into(),
"t3aNRLLsL2y8xcjPheZZwFy3Pcv7CsTwBec".into(),
"t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm".into(),
"t3Rbykhx1TUFrgXrmBYrAJe2STxRKFL7G9r".into(),
"t3aaW4aTdP7a8d1VTE1Bod2yhbeggHgMajR".into(),
"t3YEiAa6uEjXwFL2v5ztU1fn3yKgzMQqNyo".into(),
"t3g1yUUwt2PbmDvMDevTCPWUcbDatL2iQGP".into(),
"t3dPWnep6YqGPuY1CecgbeZrY9iUwH8Yd4z".into(),
"t3QRZXHDPh2hwU46iQs2776kRuuWfwFp4dV".into(),
"t3enhACRxi1ZD7e8ePomVGKn7wp7N9fFJ3r".into(),
"t3PkLgT71TnF112nSwBToXsD77yNbx2gJJY".into(),
"t3LQtHUDoe7ZhhvddRv4vnaoNAhCr2f4oFN".into(),
"t3fNcdBUbycvbCtsD2n9q3LuxG7jVPvFB8L".into(),
"t3dKojUU2EMjs28nHV84TvkVEUDu1M1FaEx".into(),
"t3aKH6NiWN1ofGd8c19rZiqgYpkJ3n679ME".into(),
"t3MEXDF9Wsi63KwpPuQdD6by32Mw2bNTbEa".into(),
"t3WDhPfik343yNmPTqtkZAoQZeqA83K7Y3f".into(),
"t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M".into(),
"t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv".into(),
"t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN".into(),
],
equihash_params: Some((200, 9)),
@ -168,6 +242,60 @@ impl ConsensusParams {
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
pow_allow_min_difficulty_after_height: Some(299187),
subsidy_slow_start_interval: 20_000,
subsidy_halving_interval: 840_000,
founders_addresses: vec![
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(),
"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(),
"t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2".into(),
"t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy".into(),
"t2BkYdVCHzvTJJUTx4yZB8qeegD8QsPx8bo".into(),
"t2J8q1xH1EuigJ52MfExyyjYtN3VgvshKDf".into(),
"t2Crq9mydTm37kZokC68HzT6yez3t2FBnFj".into(),
"t2EaMPUiQ1kthqcP5UEkF42CAFKJqXCkXC9".into(),
"t2F9dtQc63JDDyrhnfpzvVYTJcr57MkqA12".into(),
"t2LPirmnfYSZc481GgZBa6xUGcoovfytBnC".into(),
"t26xfxoSw2UV9Pe5o3C8V4YybQD4SESfxtp".into(),
"t2D3k4fNdErd66YxtvXEdft9xuLoKD7CcVo".into(),
"t2DWYBkxKNivdmsMiivNJzutaQGqmoRjRnL".into(),
"t2C3kFF9iQRxfc4B9zgbWo4dQLLqzqjpuGQ".into(),
"t2MnT5tzu9HSKcppRyUNwoTp8MUueuSGNaB".into(),
"t2AREsWdoW1F8EQYsScsjkgqobmgrkKeUkK".into(),
"t2Vf4wKcJ3ZFtLj4jezUUKkwYR92BLHn5UT".into(),
"t2K3fdViH6R5tRuXLphKyoYXyZhyWGghDNY".into(),
"t2VEn3KiKyHSGyzd3nDw6ESWtaCQHwuv9WC".into(),
"t2F8XouqdNMq6zzEvxQXHV1TjwZRHwRg8gC".into(),
"t2BS7Mrbaef3fA4xrmkvDisFVXVrRBnZ6Qj".into(),
"t2FuSwoLCdBVPwdZuYoHrEzxAb9qy4qjbnL".into(),
"t2SX3U8NtrT6gz5Db1AtQCSGjrpptr8JC6h".into(),
"t2V51gZNSoJ5kRL74bf9YTtbZuv8Fcqx2FH".into(),
"t2FyTsLjjdm4jeVwir4xzj7FAkUidbr1b4R".into(),
"t2EYbGLekmpqHyn8UBF6kqpahrYm7D6N1Le".into(),
"t2NQTrStZHtJECNFT3dUBLYA9AErxPCmkka".into(),
"t2GSWZZJzoesYxfPTWXkFn5UaxjiYxGBU2a".into(),
"t2RpffkzyLRevGM3w9aWdqMX6bd8uuAK3vn".into(),
"t2JzjoQqnuXtTGSN7k7yk5keURBGvYofh1d".into(),
"t2AEefc72ieTnsXKmgK2bZNckiwvZe3oPNL".into(),
"t2NNs3ZGZFsNj2wvmVd8BSwSfvETgiLrD8J".into(),
"t2ECCQPVcxUCSSQopdNquguEPE14HsVfcUn".into(),
"t2JabDUkG8TaqVKYfqDJ3rqkVdHKp6hwXvG".into(),
"t2FGzW5Zdc8Cy98ZKmRygsVGi6oKcmYir9n".into(),
"t2DUD8a21FtEFn42oVLp5NGbogY13uyjy9t".into(),
"t2UjVSd3zheHPgAkuX8WQW2CiC9xHQ8EvWp".into(),
"t2TBUAhELyHUn8i6SXYsXz5Lmy7kDzA1uT5".into(),
"t2Tz3uCyhP6eizUWDc3bGH7XUC9GQsEyQNc".into(),
"t2NysJSZtLwMLWEJ6MH3BsxRh6h27mNcsSy".into(),
"t2KXJVVyyrjVxxSeazbY9ksGyft4qsXUNm9".into(),
"t2J9YYtH31cveiLZzjaE4AcuwVho6qjTNzp".into(),
"t2QgvW4sP9zaGpPMH1GRzy7cpydmuRfB4AZ".into(),
"t2NDTJP9MosKpyFPHJmfjc5pGCvAU58XGa4".into(),
"t29pHDBWq7qN4EjwSEHg8wEqYe9pkmVrtRP".into(),
"t2Ez9KM8VJLuArcxuEkNRAkhNvidKkzXcjJ".into(),
"t2D5y7J5fpXajLbGrMBQkFg2mFN8fo3n8cX".into(),
"t2UV2wr1PTaUiybpkV3FdSdGxUJeZdZztyt".into(),
],
equihash_params: Some((200, 9)),
@ -193,6 +321,13 @@ impl ConsensusParams {
pow_max_adjust_down: 0,
pow_max_adjust_up: 0,
pow_target_spacing: (2.5 * 60.0) as u32,
pow_allow_min_difficulty_after_height: Some(0),
subsidy_slow_start_interval: 0,
subsidy_halving_interval: 150,
founders_addresses: vec![
"t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg".into(),
],
equihash_params: Some((200, 9)),
@ -218,6 +353,13 @@ impl ConsensusParams {
pow_max_adjust_down: 0,
pow_max_adjust_up: 0,
pow_target_spacing: (2.5 * 60.0) as u32,
pow_allow_min_difficulty_after_height: Some(0),
subsidy_slow_start_interval: 0,
subsidy_halving_interval: 150,
founders_addresses: vec![
"t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg".into(),
],
equihash_params: None,
@ -285,6 +427,45 @@ impl ConsensusParams {
height >= self.sapling_height
}
/// Block reward (goes to miner) at given height.
pub fn miner_reward(&self, height: u32) -> u64 {
let mut reward = 1_250_000_000u64;
if height < self.subsidy_slow_start_interval / 2 {
reward /= self.subsidy_slow_start_interval as u64;
reward *= height as u64;
} else if height < self.subsidy_slow_start_interval {
reward /= self.subsidy_slow_start_interval as u64;
reward *= height as u64 + 1;
} else {
let halvings = (height - self.subsidy_slow_start_interval / 2) / self.subsidy_halving_interval;
if halvings >= 64 {
return 0;
}
reward >>= halvings as u64;
}
reward
}
/// Founders reward (goes to founders) at given height.
pub fn founder_reward(&self, height: u32) -> u64 {
self.miner_reward(height) / 5
}
/// Address (transparent) where founders reward goes at given height.
pub fn founder_address(&self, height: u32) -> Option<Address> {
let last_founder_reward_block_height = self.subsidy_halving_interval + self.subsidy_slow_start_interval / 2 - 1;
if height == 0 || height > last_founder_reward_block_height {
return None;
}
let founders_len = self.founders_addresses.len() as u32;
let address_change_interval = (last_founder_reward_block_height + founders_len) / founders_len;
let address_index = height / address_change_interval;
Some(self.founders_addresses[address_index as usize].clone())
}
pub fn consensus_branch_id(&self, height: u32) -> u32 {
// sapling upgrade
if height >= self.sapling_height {
@ -300,3 +481,22 @@ impl ConsensusParams {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn miner_reward() {
let consensus = ConsensusParams::new(Network::Mainnet);
assert_eq!(consensus.miner_reward(1), 62_500);
assert_eq!(consensus.miner_reward(10_000), 625_062_500);
assert_eq!(consensus.miner_reward(20_000), 1_250_000_000);
assert_eq!(consensus.miner_reward(1_000_000), 625_000_000);
assert_eq!(consensus.miner_reward(2_000_000), 312_500_000);
assert_eq!(consensus.miner_reward(3_000_000), 156_250_000);
assert_eq!(consensus.miner_reward(4_000_000), 78_125_000);
assert_eq!(consensus.miner_reward(20_000_000), 149);
assert_eq!(consensus.miner_reward(30_000_000), 0);
}
}

View File

@ -5,6 +5,7 @@ extern crate chain;
extern crate primitives;
extern crate serialization;
extern crate bitcrypto as crypto;
extern crate keys;
extern crate rustc_hex as hex;
mod consensus;

View File

@ -75,7 +75,7 @@ impl Network {
pub fn genesis_block(&self) -> Block {
match *self {
Network::Mainnet | Network::Other(_) => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac4000000000000000000000000000000000000000000000000000000000000000090041358ffff071f5712000000000000000000000000000000000000000000000000000000000000fd4005000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce1570101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
Network::Testnet => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac4000000000000000000000000000000000000000000000000000000000000000090041358ffff071f5712000000000000000000000000000000000000000000000000000000000000fd4005000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce1570101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
Network::Testnet => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac40000000000000000000000000000000000000000000000000000000000000000a11e1358ffff07200600000000000000000000000000000000000000000000000000000000000000fd400500a6a51259c3f6732481e2d035197218b7a69504461d04335503cd69759b2d02bd2b53a9653f42cb33c608511c953673fa9da76170958115fe92157ad3bb5720d927f18e09459bf5c6072973e143e20f9bdf0584058c96b7c2234c7565f100d5eea083ba5d3dbaff9f0681799a113e7beff4a611d2b49590563109962baa149b628aae869af791f2f70bb041bd7ebfa658570917f6654a142b05e7ec0289a4f46470be7be5f693b90173eaaa6e84907170f32602204f1f4e1c04b1830116ffd0c54f0b1caa9a5698357bd8aa1f5ac8fc93b405265d824ba0e49f69dab5446653927298e6b7bdc61ee86ff31c07bde86331b4e500d42e4e50417e285502684b7966184505b885b42819a88469d1e9cf55072d7f3510f85580db689302eab377e4e11b14a91fdd0df7627efc048934f0aff8e7eb77eb17b3a95de13678004f2512293891d8baf8dde0ef69be520a58bbd6038ce899c9594cf3e30b8c3d9c7ecc832d4c19a6212747b50724e6f70f6451f78fd27b58ce43ca33b1641304a916186cfbe7dbca224f55d08530ba851e4df22baf7ab7078e9cbea46c0798b35a750f54103b0cdd08c81a6505c4932f6bfbd492a9fced31d54e98b6370d4c96600552fcf5b37780ed18c8787d03200963600db297a8f05dfa551321d17b9917edadcda51e274830749d133ad226f8bb6b94f13b4f77e67b35b71f52112ce9ba5da706ad9573584a2570a4ff25d29ab9761a06bdcf2c33638bf9baf2054825037881c14adf3816ba0cbd0fca689aad3ce16f2fe362c98f48134a9221765d939f0b49677d1c2447e56b46859f1810e2cf23e82a53e0d44f34dae932581b3b7f49eaec59af872cf9de757a964f7b33d143a36c270189508fcafe19398e4d2966948164d40556b05b7ff532f66f5d1edc41334ef742f78221dfe0c7ae2275bb3f24c89ae35f00afeea4e6ed187b866b209dc6e83b660593fce7c40e143beb07ac86c56f39e895385924667efe3a3f031938753c7764a2dbeb0a643fd359c46e614873fd0424e435fa7fac083b9a41a9d6bf7e284eee537ea7c50dd239f359941a43dc982745184bf3ee31a8dc850316aa9c6b66d6985acee814373be3458550659e1a06287c3b3b76a185c5cb93e38c1eebcf34ff072894b6430aed8d34122dafd925c46a515cca79b0269c92b301890ca6b0dc8b679cdac0f23318c105de73d7a46d16d2dad988d49c22e9963c117960bdc70ef0db6b091cf09445a516176b7f6d58ec29539166cc8a38bbff387acefffab2ea5faad0e8bb70625716ef0edf61940733c25993ea3de9f0be23d36e7cb8da10505f9dc426cd0e6e5b173ab4fff8c37e1f1fb56d1ea372013d075e0934c6919393cfc21395eea20718fad03542a4162a9ded66c814ad8320b2d7c2da3ecaf206da34c502db2096d1c46699a91dd1c432f019ad434e2c1ce507f91104f66f491fed37b225b8e0b2888c37276cfa0468fc13b8d593fd9a2675f0f5b20b8a15f8fa7558176a530d6865738ddb25d3426dab905221681cf9da0e0200eea5b2eba3ad3a5237d2a391f9074bf1779a2005cee43eec2b058511532635e0fea61664f531ac2b356f40db5c5d275a4cf5c82d468976455af4e3362cc8f71aa95e71d394aff3ead6f7101279f95bcd8a0fedce1d21cb3c9f6dd3b182fce0db5d6712981b651f29178a24119968b14783cafa713bc5f2a65205a42e4ce9dc7ba462bdb1f3e4553afc15f5f39998fdb53e7e231e3e520a46943734a007c2daa1eda9f495791657eefcac5c32833936e568d06187857ed04d7b97167ae207c5c5ae54e528c36016a984235e9c5b2f0718d7b3aa93c7822ccc772580b6599671b3c02ece8a21399abd33cfd3028790133167d0a97e7de53dc8ff0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
Network::Regtest | Network::Unitest => "TODO".into(),
}
}

View File

@ -310,7 +310,7 @@ pub mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
},
version: 33,
coinbase: false,
@ -615,7 +615,7 @@ pub mod tests {
"id": 1
}"#)).unwrap();
assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bestblock":"0000000000000000000000000000000000000000000000000000000000000056","coinbase":false,"confirmations":777,"scriptPubKey":{"addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"],"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig"},"value":100000.56,"version":33},"id":1}"#);
assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bestblock":"0000000000000000000000000000000000000000000000000000000000000056","coinbase":false,"confirmations":777,"scriptPubKey":{"addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"],"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig"},"value":100000.56,"version":33},"id":1}"#);
}
#[test]

View File

@ -222,7 +222,7 @@ pub mod tests {
{
"jsonrpc": "2.0",
"method": "createrawtransaction",
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}],
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}],
"id": 1
}"#)
).unwrap();
@ -240,7 +240,7 @@ pub mod tests {
{
"jsonrpc": "2.0",
"method": "createrawtransaction",
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}],
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}],
"id": 1
}"#)
).unwrap();

View File

@ -66,13 +66,13 @@ mod tests {
#[test]
fn address_serialize() {
let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into());
assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#);
let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into());
assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#);
}
#[test]
fn address_deserialize() {
let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into());
assert_eq!(serde_json::from_str::<TestStruct>(r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#).unwrap(), test);
let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into());
assert_eq!(serde_json::from_str::<TestStruct>(r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#).unwrap(), test);
}
}

View File

@ -40,12 +40,12 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
},
version: 33,
coinbase: false,
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#);
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]},"version":33,"coinbase":false}"#);
}
#[test]
@ -59,13 +59,13 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
},
version: 33,
coinbase: false,
};
assert_eq!(
serde_json::from_str::<GetTxOutResponse>(r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#).unwrap(),
serde_json::from_str::<GetTxOutResponse>(r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]},"version":33,"coinbase":false}"#).unwrap(),
txout);
}
}

View File

@ -253,11 +253,11 @@ mod tests {
let txout = TransactionOutputs {
outputs: vec![
TransactionOutput::Address(TransactionOutputWithAddress {
address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(),
address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(),
amount: 123.45,
}),
TransactionOutput::Address(TransactionOutputWithAddress {
address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(),
address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(),
amount: 67.89,
}),
TransactionOutput::ScriptData(TransactionOutputWithScriptData {
@ -268,7 +268,7 @@ mod tests {
}),
]
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#);
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#);
}
#[test]
@ -276,11 +276,11 @@ mod tests {
let txout = TransactionOutputs {
outputs: vec![
TransactionOutput::Address(TransactionOutputWithAddress {
address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(),
address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(),
amount: 123.45,
}),
TransactionOutput::Address(TransactionOutputWithAddress {
address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(),
address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(),
amount: 67.89,
}),
TransactionOutput::ScriptData(TransactionOutputWithScriptData {
@ -292,7 +292,7 @@ mod tests {
]
};
assert_eq!(
serde_json::from_str::<TransactionOutputs>(r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#).unwrap(),
serde_json::from_str::<TransactionOutputs>(r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#).unwrap(),
txout);
}
@ -323,9 +323,9 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#);
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#);
}
#[test]
@ -335,11 +335,11 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
};
assert_eq!(
serde_json::from_str::<TransactionOutputScript>(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#).unwrap(),
serde_json::from_str::<TransactionOutputScript>(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#).unwrap(),
txout);
}
@ -383,10 +383,10 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
},
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#);
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#);
}
#[test]
@ -399,11 +399,11 @@ mod tests {
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
},
};
assert_eq!(
serde_json::from_str::<SignedTransactionOutput>(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#).unwrap(),
serde_json::from_str::<SignedTransactionOutput>(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#).unwrap(),
txout);
}

View File

@ -617,7 +617,7 @@ OP_ADD
#[test]
fn test_extract_destinations_pub_key_hash() {
let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash;
let address = Address::from("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi").hash;
let script = Builder::build_p2pkh(&address);
assert_eq!(script.script_type(), ScriptType::PubKeyHash);
assert_eq!(script.extract_destinations(), Ok(vec![
@ -627,7 +627,7 @@ OP_ADD
#[test]
fn test_extract_destinations_script_hash() {
let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash;
let address = Address::from("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi").hash;
let script = Builder::build_p2sh(&address);
assert_eq!(script.script_type(), ScriptType::ScriptHash);
assert_eq!(script.extract_destinations(), Ok(vec![

View File

@ -461,20 +461,17 @@ mod tests {
use ser::deserialize;
use super::{Sighash, UnsignedTransactionInput, TransactionInputSigner, SighashBase};
// http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
// https://blockchain.info/rawtx/81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48
// https://blockchain.info/rawtx/3f285f083de7c0acabd9f106a43ec42687ab0bebe2e6f0d529db696794540fea
#[test]
fn test_signature_hash_simple() {
let private: Private = "5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD".into();
let private: Private = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj".into();
let previous_tx_hash = H256::from_reversed_str("81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48");
let previous_output_index = 0;
let from: Address = "1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5".into();
let to: Address = "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa".into();
let from: Address = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into();
let to: Address = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst".into();
let previous_output = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into();
let current_output: Bytes = "76a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac".into();
let current_output: Bytes = "76a9149a823b698f778ece90b094dc3f12a81f5e3c334588ac".into();
let value = 91234;
let expected_signature_hash = "5fda68729a6312e17e641e9a49fac2a4a6a680126610af573caab270d232f850".into();
let expected_signature_hash = "f6d326b3b48fd8f6d6e29b590d76507aebe647043b1588a35605e9405234e391".into();
// this is irrelevant
let kp = KeyPair::from_private(private).unwrap();

View File

@ -1,6 +1,5 @@
use EpochTag;
use hash::H256;
use crypto::{Sha256, Digest};
/// Trait to query sequence of blockchain commitments;
pub trait CommitmentProvider : Sync {

View File

@ -8,7 +8,6 @@ extern crate display_derive;
extern crate primitives;
extern crate serialization as ser;
extern crate chain;
extern crate bitcrypto as crypto;
mod best_block;
mod block_ancestors;

View File

@ -2230,6 +2230,8 @@ pub mod tests {
#[test]
fn when_transaction_double_spends_during_reorg() {
let consensus = ConsensusParams::new(Network::Unitest);
let b0 = test_data::block_builder().header().build()
.transaction().coinbase()
.output().value(10).build()
@ -2250,7 +2252,7 @@ pub mod tests {
// in-storage spends b0[1] && b0[2]
let b1 = test_data::block_builder()
.transaction().coinbase()
.transaction().coinbase().founder_reward(&consensus, 1)
.output().value(50).build()
.build()
.transaction().version(10)
@ -2280,7 +2282,7 @@ pub mod tests {
// in-storage [side] spends b0[3]
let b2 = test_data::block_builder().header().parent(b0.hash()).build()
.transaction().coinbase()
.transaction().coinbase().founder_reward(&consensus, 1)
.output().value(5555).build()
.build()
.transaction().version(20)
@ -2290,7 +2292,8 @@ pub mod tests {
.build();
// in-storage [causes reorg to b2 + b3] spends b0[1]
let b3 = test_data::block_builder()
.transaction().coinbase().version(40)
.transaction().coinbase().founder_reward(&consensus, 2)
.version(40)
.output().value(50).build()
.build()
.transaction().version(30)

View File

@ -264,7 +264,8 @@ pub mod tests {
use std::collections::{HashSet, HashMap};
use db::BlockChainDatabase;
use network::{Network, ConsensusParams};
use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError};
use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError, TransactionError};
use script::Error as ScriptError;
use synchronization_client_core::CoreVerificationSink;
use synchronization_executor::tests::DummyTaskExecutor;
use primitives::hash::H256;
@ -371,18 +372,21 @@ pub mod tests {
#[test]
fn verification_level_header_accept_incorrect_transaction() {
let consensus = ConsensusParams::new(Network::Unitest);
let mut blocks: Vec<IndexedBlock> = vec![test_data::genesis().into()];
let mut rolling_hash = blocks[0].hash().clone();
for i in 1..101 {
let next_block = test_data::block_builder()
.transaction()
.coinbase()
.version(i)
.founder_reward(&consensus, i)
.version(i as i32)
.output().value(5000000000).build()
.build()
.merkled_header()
.parent(rolling_hash.clone())
.bits(Network::Unitest.max_bits().into())
.time(consensus.pow_target_spacing * 7 * i)
.build()
.build();
rolling_hash = next_block.hash();
@ -394,7 +398,10 @@ pub mod tests {
let storage: StorageRef = Arc::new(BlockChainDatabase::init_test_chain(blocks));
let verifier = Arc::new(ChainVerifier::new(storage.clone(), ConsensusParams::new(Network::Unitest)));
let bad_transaction_block: IndexedBlock = test_data::block_builder()
.transaction().coinbase().output().value(50).build().build()
.transaction().coinbase()
.founder_reward(&consensus, 101)
.output().value(50).build()
.build()
.transaction()
.input().hash(coinbase_transaction_hash).build()
.output().value(1000).build()
@ -402,6 +409,7 @@ pub mod tests {
.merkled_header()
.parent(last_block_hash)
.bits(Network::Unitest.max_bits().into())
.time(consensus.pow_target_spacing * 7 * 102)
.build()
.build().into();
@ -414,12 +422,11 @@ pub mod tests {
assert_eq!(wrapper.verify_block(&bad_transaction_block), Ok(()));
// Error when tx script is checked
/* TODO: fixme
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
verification_level: VerificationLevel::Full,
verification_edge: 1.into(),
});
assert_eq!(wrapper.verify_block(&bad_transaction_block), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation))));*/
assert_eq!(wrapper.verify_block(&bad_transaction_block), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation))));
}
#[test]

View File

@ -6,7 +6,8 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
[dependencies]
time = "0.1"
chain = { path = "../chain" }
chain = { path = "../chain" }
network = { path = "../network" }
primitives = { path = "../primitives" }
serialization = { path = "../serialization" }
script = { path = "../script" }

View File

@ -6,6 +6,7 @@ use primitives::bytes::Bytes;
use primitives::compact::Compact;
use ser::{Serializable, serialized_list_size};
use chain;
use network::ConsensusParams;
use script::{Builder as ScriptBuilder, Opcode};
use invoke::{Invoke, Identity};
use super::genesis;
@ -331,6 +332,10 @@ impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
self.input().coinbase().build()
}
pub fn founder_reward(self, consensus: &ConsensusParams, height: u32) -> Self {
self.output().founder_reward(consensus, height).build()
}
pub fn output(self) -> TransactionOutputBuilder<Self> {
TransactionOutputBuilder::with_callback(self)
}
@ -470,6 +475,12 @@ impl<F> TransactionOutputBuilder<F> where F: Invoke<chain::TransactionOutput> {
}
}
pub fn founder_reward(mut self, consensus: &ConsensusParams, height: u32) -> Self {
self.script_pubkey = ScriptBuilder::build_p2sh(&consensus.founder_address(height).unwrap().hash).into();
self.value = consensus.founder_reward(height);
self
}
pub fn value(mut self, value: u64) -> Self {
self.value = value;
self

View File

@ -3,6 +3,7 @@
extern crate time;
extern crate chain;
extern crate network;
extern crate primitives;
extern crate serialization as ser;
extern crate script;

View File

@ -10,6 +10,7 @@ rayon = "1.0"
parking_lot = "0.4"
byteorder = "1.2"
lazy_static = "1.2.0"
keys = { path = "../keys" }
primitives = { path = "../primitives" }
chain = { path = "../chain" }
serialization = { path = "../serialization" }

View File

@ -1,8 +1,8 @@
use keys::Address;
use network::{ConsensusParams};
use storage::{TransactionOutputProvider, BlockHeaderProvider};
use script;
use script::{self, Builder};
use sigops::{transaction_sigops};
use work::block_reward_satoshi;
use duplex_store::DuplexTransactionOutputProvider;
use deployments::BlockDeployments;
use canon::CanonBlock;
@ -14,7 +14,8 @@ pub struct BlockAcceptor<'a> {
pub finality: BlockFinality<'a>,
pub serialized_size: BlockSerializedSize<'a>,
pub sigops: BlockSigops<'a>,
pub coinbase_claim: BlockCoinbaseClaim<'a>,
pub miner_reward: BlockCoinbaseMinerReward<'a>,
pub founder_reward: BlockFounderReward<'a>,
pub coinbase_script: BlockCoinbaseScript<'a>,
}
@ -31,7 +32,8 @@ impl<'a> BlockAcceptor<'a> {
finality: BlockFinality::new(block, height, deployments, headers),
serialized_size: BlockSerializedSize::new(block, consensus),
coinbase_script: BlockCoinbaseScript::new(block, consensus, height),
coinbase_claim: BlockCoinbaseClaim::new(block, store, height),
miner_reward: BlockCoinbaseMinerReward::new(block, store, consensus, height),
founder_reward: BlockFounderReward::new(block, consensus, height),
sigops: BlockSigops::new(block, store, consensus),
}
}
@ -40,7 +42,8 @@ impl<'a> BlockAcceptor<'a> {
self.finality.check()?;
self.sigops.check()?;
self.serialized_size.check()?;
self.coinbase_claim.check()?;
self.miner_reward.check()?;
self.founder_reward.check()?;
self.coinbase_script.check()?;
Ok(())
}
@ -144,22 +147,23 @@ impl<'a> BlockSigops<'a> {
}
}
pub struct BlockCoinbaseClaim<'a> {
pub struct BlockCoinbaseMinerReward<'a> {
block: CanonBlock<'a>,
store: &'a TransactionOutputProvider,
height: u32,
max_reward: u64,
}
impl<'a> BlockCoinbaseClaim<'a> {
impl<'a> BlockCoinbaseMinerReward<'a> {
fn new(
block: CanonBlock<'a>,
store: &'a TransactionOutputProvider,
consensus: &ConsensusParams,
height: u32,
) -> Self {
BlockCoinbaseClaim {
BlockCoinbaseMinerReward {
block: block,
store: store,
height: height,
max_reward: consensus.miner_reward(height),
}
}
@ -210,13 +214,13 @@ impl<'a> BlockCoinbaseClaim<'a> {
let claim = self.block.transactions[0].raw.total_spends();
let (reward, overflow) = fees.overflowing_add(block_reward_satoshi(self.height));
let (max_reward, overflow) = fees.overflowing_add(self.max_reward);
if overflow {
return Err(Error::TransactionFeeAndRewardOverflow);
}
if claim > reward {
Err(Error::CoinbaseOverspend { expected_max: reward, actual: claim })
if claim > max_reward {
Err(Error::CoinbaseOverspend { expected_max: max_reward, actual: claim })
} else {
Ok(())
}
@ -260,6 +264,39 @@ impl<'a> BlockCoinbaseScript<'a> {
}
}
pub struct BlockFounderReward<'a> {
block: CanonBlock<'a>,
founder_address: Option<Address>,
founder_reward: u64,
}
impl<'a> BlockFounderReward<'a> {
fn new(block: CanonBlock<'a>, consensus_params: &ConsensusParams, height: u32) -> Self {
BlockFounderReward {
block: block,
founder_address: consensus_params.founder_address(height),
founder_reward: consensus_params.founder_reward(height),
}
}
fn check(&self) -> Result<(), Error> {
if let Some(ref founder_address) = self.founder_address {
let script = Builder::build_p2sh(&founder_address.hash);
let has_founder_reward = self.block.transactions.first()
.map(|tx| tx.raw.outputs.iter().any(|output|
**output.script_pubkey == *script &&
output.value == self.founder_reward))
.unwrap_or(false);
if !has_founder_reward {
return Err(Error::MissingFoundersReward);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate test_data;

View File

@ -17,14 +17,14 @@ pub struct ChainAcceptor<'a> {
}
impl<'a> ChainAcceptor<'a> {
pub fn new(store: &'a Store, consensus: &'a ConsensusParams, verification_level: VerificationLevel, block: CanonBlock<'a>, height: u32, deployments: &'a BlockDeployments) -> Self {
pub fn new(store: &'a Store, consensus: &'a ConsensusParams, verification_level: VerificationLevel, block: CanonBlock<'a>, height: u32, time: u32, deployments: &'a BlockDeployments) -> Self {
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
let headers = store.as_block_header_provider();
ChainAcceptor {
block: BlockAcceptor::new(store.as_transaction_output_provider(), consensus, block, height, deployments, headers),
header: HeaderAcceptor::new(headers, consensus, block.header(), height, deployments),
header: HeaderAcceptor::new(headers, consensus, block.header(), height, time, deployments),
transactions: block.transactions()
.into_iter()
.enumerate()

View File

@ -18,11 +18,12 @@ impl<'a> HeaderAcceptor<'a> {
consensus: &'a ConsensusParams,
header: CanonHeader<'a>,
height: u32,
time: u32,
deployments: D,
) -> Self {
let csv_active = deployments.as_ref().csv(height, store, consensus);
HeaderAcceptor {
work: HeaderWork::new(header, store, height, consensus),
work: HeaderWork::new(header, store, height, time, consensus),
median_timestamp: HeaderMedianTimestamp::new(header, store, csv_active),
version: HeaderVersion::new(header, height, consensus),
}
@ -66,22 +67,24 @@ pub struct HeaderWork<'a> {
header: CanonHeader<'a>,
store: &'a BlockHeaderProvider,
height: u32,
time: u32,
consensus: &'a ConsensusParams,
}
impl<'a> HeaderWork<'a> {
fn new(header: CanonHeader<'a>, store: &'a BlockHeaderProvider, height: u32, consensus: &'a ConsensusParams) -> Self {
fn new(header: CanonHeader<'a>, store: &'a BlockHeaderProvider, height: u32, time: u32, consensus: &'a ConsensusParams) -> Self {
HeaderWork {
header: header,
store: store,
height: height,
time: time,
consensus: consensus,
}
}
fn check(&self) -> Result<(), Error> {
let previous_header_hash = self.header.raw.previous_header_hash.clone();
let work = work_required(previous_header_hash, self.height, self.store, self.consensus);
let work = work_required(previous_header_hash, self.time, self.height, self.store, self.consensus);
if work == self.header.raw.bits {
Ok(())
} else {

View File

@ -54,7 +54,7 @@ impl BackwardsCompatibleChainVerifier {
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
let canon_block = CanonBlock::new(block);
let chain_acceptor = ChainAcceptor::new(self.store.as_store(), &self.consensus, verification_level,
canon_block, block_number, &deployments);
canon_block, block_number, block.header.raw.time, &deployments);
chain_acceptor.check()?;
},
BlockOrigin::SideChain(origin) => {
@ -64,7 +64,7 @@ impl BackwardsCompatibleChainVerifier {
let fork = self.store.fork(origin)?;
let canon_block = CanonBlock::new(block);
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
block_number, &deployments);
block_number, block.header.raw.time, &deployments);
chain_acceptor.check()?;
},
BlockOrigin::SideChainBecomingCanonChain(origin) => {
@ -74,7 +74,7 @@ impl BackwardsCompatibleChainVerifier {
let fork = self.store.fork(origin)?;
let canon_block = CanonBlock::new(block);
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
block_number, &deployments);
block_number, block.header.raw.time, &deployments);
chain_acceptor.check()?;
},
}
@ -185,6 +185,7 @@ mod tests {
#[test]
fn coinbase_maturity() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -199,6 +200,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(1).build()
.build()
.transaction()
@ -208,7 +210,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
let expected = Err(Error::Transaction(
1,
@ -220,6 +222,8 @@ mod tests {
#[test]
fn non_coinbase_happy() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -237,6 +241,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(2).build()
.build()
.transaction()
@ -246,12 +251,14 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert_eq!(verifier.verify(VerificationLevel::Full, &block.into()), Ok(()));
}
#[test]
fn transaction_references_same_block_happy() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -269,6 +276,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(2).build()
.build()
.transaction()
@ -282,7 +290,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok());
}
@ -426,7 +434,7 @@ mod tests {
let block: IndexedBlock = test_data::block_builder()
.transaction()
.coinbase()
.output().value(5000000001).build()
.output().value(1250000001).build()
.build()
.merkled_header().parent(genesis.hash()).build()
.build()
@ -435,8 +443,8 @@ mod tests {
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let expected = Err(Error::CoinbaseOverspend {
expected_max: 5000000000,
actual: 5000000001
expected_max: 1250000000,
actual: 1250000001,
});
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));

View File

@ -59,6 +59,8 @@ pub enum Error {
InvalidEquihashSolution,
/// Invalid block version
InvalidVersion,
/// Block' coinbase is missing founders reward output.
MissingFoundersReward,
}
impl From<DBError> for Error {

View File

@ -65,6 +65,7 @@ extern crate rustc_hex as hex;
extern crate storage;
extern crate chain;
extern crate keys;
extern crate network;
extern crate primitives;
extern crate serialization as ser;
@ -87,7 +88,6 @@ mod sapling;
mod sigops;
mod timestamp;
mod work;
mod work_zcash;
// pre-verification
mod verify_block;
@ -121,7 +121,7 @@ pub use chain_verifier::BackwardsCompatibleChainVerifier;
pub use error::{Error, TransactionError};
pub use sigops::transaction_sigops;
pub use timestamp::{median_timestamp, median_timestamp_inclusive};
pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash, block_reward_satoshi};
pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash};
pub use deployments::Deployments;
#[derive(Debug, Clone, Copy, PartialEq)]

View File

@ -1,10 +1,9 @@
use primitives::compact::Compact;
use primitives::hash::H256;
use primitives::bigint::U256;
use chain::IndexedBlockHeader;
use primitives::bigint::{Uint, U256};
use network::ConsensusParams;
use storage::BlockHeaderProvider;
use work_zcash::work_required_zcash;
use storage::{BlockHeaderProvider, BlockAncestors};
use timestamp::median_timestamp_inclusive;
/// Returns true if hash is lower or equal than target represented by compact bits
pub fn is_valid_proof_of_work_hash(bits: Compact, hash: &H256) -> bool {
@ -35,39 +34,246 @@ pub fn is_valid_proof_of_work(max_work_bits: Compact, bits: Compact, hash: &H256
}
/// Returns work required for given header
pub fn work_required(parent_hash: H256, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams) -> Compact {
pub fn work_required(parent_hash: H256, time: u32, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams) -> Compact {
let max_bits = consensus.network.max_bits().into();
// chain starts with has minimal difficulty
if height == 0 {
return max_bits;
}
let parent_header = store.block_header(parent_hash.clone().into()).expect("self.height != 0; qed");
work_required_zcash(IndexedBlockHeader {
hash: parent_hash,
raw: parent_header
}, store, consensus, max_bits)
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 6 * 2.5 minutes
// then allow mining of a min-difficulty block.
if let Some(allow_min_difficulty_after_height) = consensus.pow_allow_min_difficulty_after_height {
if height >= allow_min_difficulty_after_height {
if time > parent_header.time + consensus.pow_target_spacing * 6 {
return max_bits;
}
}
}
// Find the first block in the averaging interval + calculate total difficulty for blocks in the interval
let (count, oldest_hash, bits_total) = BlockAncestors::new(parent_header.previous_header_hash.into(), store)
.take(consensus.pow_averaging_window as usize - 1)
.fold((1, Default::default(), U256::from(parent_header.bits)), |(count, _, bits_total), header|
(count + 1, header.previous_header_hash, bits_total.overflowing_add(header.bits.into()).0));
if count != consensus.pow_averaging_window {
return max_bits;
}
let bits_avg = bits_total / consensus.pow_averaging_window.into();
let parent_mtp = median_timestamp_inclusive(parent_header.hash(), store);
let oldest_mtp = median_timestamp_inclusive(oldest_hash, store);
calculate_work_required(bits_avg, parent_mtp, oldest_mtp, consensus, max_bits)
}
pub fn block_reward_satoshi(block_height: u32) -> u64 {
let mut res = 50 * 100 * 1000 * 1000;
for _ in 0..block_height / 210000 { res /= 2 }
res
fn calculate_work_required(bits_avg: U256, parent_mtp: u32, oldest_mtp: u32, consensus: &ConsensusParams, max_bits: Compact) -> Compact {
// Limit adjustment step
// Use medians to prevent time-warp attacks
let actual_timespan = parent_mtp - oldest_mtp;
let mut actual_timespan = consensus.averaging_window_timespan() as i64 +
(actual_timespan as i64 - consensus.averaging_window_timespan() as i64) / 4;
if actual_timespan < consensus.min_actual_timespan() as i64 {
actual_timespan = consensus.min_actual_timespan() as i64;
}
if actual_timespan > consensus.max_actual_timespan() as i64 {
actual_timespan = consensus.max_actual_timespan() as i64;
}
// Retarget
let actual_timespan = actual_timespan as u32;
let mut bits_new = bits_avg / consensus.averaging_window_timespan().into();
bits_new = bits_new * actual_timespan.into();
if bits_new > max_bits.into() {
return max_bits;
}
bits_new.into()
}
#[cfg(test)]
mod tests {
use super::{block_reward_satoshi};
extern crate test_data;
use std::collections::HashMap;
use primitives::bytes::Bytes;
use primitives::compact::Compact;
use primitives::bigint::U256;
use primitives::hash::H256;
use network::{Network, ConsensusParams};
use chain::BlockHeader;
use storage::{BlockHeaderProvider, BlockRef};
use timestamp::median_timestamp_inclusive;
use super::{work_required, calculate_work_required};
#[derive(Default)]
pub struct MemoryBlockHeaderProvider {
pub by_height: Vec<BlockHeader>,
pub by_hash: HashMap<H256, usize>,
}
impl MemoryBlockHeaderProvider {
pub fn last(&self) -> &BlockHeader {
self.by_height.last().unwrap()
}
pub fn insert(&mut self, header: BlockHeader) {
self.by_hash.insert(header.hash(), self.by_height.len());
self.by_height.push(header);
}
pub fn replace_last(&mut self, header: BlockHeader) {
let idx = self.by_height.len() - 1;
self.by_hash.remove(&self.by_height[idx].hash());
self.by_hash.insert(header.hash(), idx);
self.by_height[idx] = header;
}
pub fn next_height(&self) -> u32 {
self.by_height.len() as u32
}
pub fn next_time(&self) -> u32 {
self.last().time + (self.last().time - self.by_height[self.by_height.len() - 2].time)
}
}
impl BlockHeaderProvider for MemoryBlockHeaderProvider {
fn block_header_bytes(&self, _block_ref: BlockRef) -> Option<Bytes> {
unimplemented!()
}
fn block_header(&self, block_ref: BlockRef) -> Option<BlockHeader> {
match block_ref {
BlockRef::Hash(ref hash) => self.by_hash.get(hash).map(|h| &self.by_height[*h]).cloned(),
BlockRef::Number(height) => self.by_height.get(height as usize).cloned(),
}
}
}
#[test]
fn reward() {
assert_eq!(block_reward_satoshi(0), 5000000000);
assert_eq!(block_reward_satoshi(209999), 5000000000);
assert_eq!(block_reward_satoshi(210000), 2500000000);
assert_eq!(block_reward_satoshi(420000), 1250000000);
assert_eq!(block_reward_satoshi(420001), 1250000000);
assert_eq!(block_reward_satoshi(629999), 1250000000);
assert_eq!(block_reward_satoshi(630000), 625000000);
assert_eq!(block_reward_satoshi(630001), 625000000);
fn main_chain_required_work_works() {
let consensus = ConsensusParams::new(Network::Mainnet);
// insert genesis block
let mut header_provider = MemoryBlockHeaderProvider::default();
let genesis = test_data::genesis().block_header;
header_provider.insert(genesis.clone());
// assert block#1 work
let h1 = test_data::block_h1();
let expected = h1.block_header.bits;
let actual = work_required(genesis.hash(), h1.block_header.time, 1, &header_provider, &consensus);
assert_eq!(expected, actual);
}
// original test link:
// https://github.com/Bitcoin-ABC/bitcoin-abc/blob/d8eac91f8d16716eed0ad11ccac420122280bb13/src/test/pow_tests.cpp#L193
#[test]
fn work_required_works() {
let consensus = ConsensusParams::new(Network::Mainnet);
let max_bits = Network::Mainnet.max_bits();
let last_block = 2 * consensus.pow_averaging_window;
let first_block = last_block - consensus.pow_averaging_window;
// insert genesis block
let mut header_provider = MemoryBlockHeaderProvider::default();
header_provider.insert(BlockHeader {
time: 1269211443,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: 0.into(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
reserved_hash: Default::default(),
solution: Default::default(),
});
// Start with blocks evenly-spaced and equal difficulty
for i in 1..last_block+1 {
let header = BlockHeader {
time: header_provider.last().time + consensus.pow_target_spacing,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: header_provider.by_height[i as usize - 1].hash(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
reserved_hash: Default::default(),
solution: Default::default(),
};
header_provider.insert(header);
}
// Result should be the same as if last difficulty was used
let bits_avg: U256 = header_provider.by_height[last_block as usize].bits.into();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus);
assert_eq!(actual, expected);
// Result should be unchanged, modulo integer division precision loss
let mut bits_expected: U256 = Compact::new(0x1e7fffff).into();
bits_expected = bits_expected / consensus.averaging_window_timespan().into();
bits_expected = bits_expected * consensus.averaging_window_timespan().into();
assert_eq!(work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus),
bits_expected.into());
// Randomise the final block time (plus 1 to ensure it is always different)
use rand::{thread_rng, Rng};
let mut last_header = header_provider.by_height[last_block as usize].clone();
last_header.time += thread_rng().gen_range(1, consensus.pow_target_spacing / 2);
header_provider.replace_last(last_header);
// Result should be the same as if last difficulty was used
let bits_avg: U256 = header_provider.by_height[last_block as usize].bits.into();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus);
assert_eq!(actual, expected);
// Result should not be unchanged
let bits_expected = Compact::new(0x1e7fffff);
assert!(work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus) != bits_expected);
// Change the final block difficulty
let mut last_header = header_provider.by_height[last_block as usize].clone();
last_header.bits = Compact::new(0x1e0fffff);
header_provider.replace_last(last_header);
// Result should not be the same as if last difficulty was used
let bits_avg = header_provider.by_height[last_block as usize].bits;
let expected = calculate_work_required(bits_avg.into(),
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus);
assert!(actual != expected);
// Result should be the same as if the average difficulty was used
let bits_avg = "0000796968696969696969696969696969696969696969696969696969696969".parse().unwrap();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required(header_provider.last().hash(), header_provider.next_time(), header_provider.next_height(),
&header_provider, &consensus);
assert_eq!(actual, expected);
}
}

View File

@ -1,216 +0,0 @@
use primitives::compact::Compact;
use primitives::bigint::{U256, Uint};
use chain::IndexedBlockHeader;
use network::ConsensusParams;
use storage::BlockHeaderProvider;
use timestamp::median_timestamp_inclusive;
/// Returns work required for given header for the ZCash block
pub fn work_required_zcash(parent_header: IndexedBlockHeader, store: &BlockHeaderProvider, consensus: &ConsensusParams, max_bits: Compact) -> Compact {
// TODO: special testnet case!
// Find the first block in the averaging interval
let parent_hash = parent_header.hash.clone();
let mut oldest_hash = parent_header.raw.previous_header_hash;
let mut bits_total: U256 = parent_header.raw.bits.into();
for _ in 1..consensus.pow_averaging_window {
let previous_header = match store.block_header(oldest_hash.into()) {
Some(previous_header) => previous_header,
None => return max_bits,
};
// TODO: check this
bits_total = match bits_total.overflowing_add(previous_header.bits.into()) {
(bits_total, false) => bits_total,
(_, true) => return max_bits,
};
oldest_hash = previous_header.previous_header_hash;
}
let bits_avg = bits_total / consensus.pow_averaging_window.into();
let parent_mtp = median_timestamp_inclusive(parent_hash, store);
let oldest_mtp = median_timestamp_inclusive(oldest_hash, store);
calculate_work_required(bits_avg, parent_mtp, oldest_mtp, consensus, max_bits)
}
fn calculate_work_required(bits_avg: U256, parent_mtp: u32, oldest_mtp: u32, consensus: &ConsensusParams, max_bits: Compact) -> Compact {
// Limit adjustment step
// Use medians to prevent time-warp attacks
let actual_timespan = parent_mtp - oldest_mtp;
let mut actual_timespan = consensus.averaging_window_timespan() as i64 +
(actual_timespan as i64 - consensus.averaging_window_timespan() as i64) / 4;
if actual_timespan < consensus.min_actual_timespan() as i64 {
actual_timespan = consensus.min_actual_timespan() as i64;
}
if actual_timespan > consensus.max_actual_timespan() as i64 {
actual_timespan = consensus.max_actual_timespan() as i64;
}
// Retarget
let actual_timespan = actual_timespan as u32;
let mut bits_new = bits_avg / consensus.averaging_window_timespan().into();
bits_new = bits_new * actual_timespan.into();
if bits_new > max_bits.into() {
return max_bits;
}
bits_new.into()
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use primitives::bytes::Bytes;
use primitives::compact::Compact;
use primitives::bigint::U256;
use primitives::hash::H256;
use network::{Network, ConsensusParams};
use chain::BlockHeader;
use storage::{BlockHeaderProvider, BlockRef};
use timestamp::median_timestamp_inclusive;
use super::{work_required_zcash, calculate_work_required};
#[derive(Default)]
pub struct MemoryBlockHeaderProvider {
pub by_height: Vec<BlockHeader>,
pub by_hash: HashMap<H256, usize>,
}
impl MemoryBlockHeaderProvider {
pub fn last(&self) -> &BlockHeader {
self.by_height.last().unwrap()
}
pub fn insert(&mut self, header: BlockHeader) {
self.by_hash.insert(header.hash(), self.by_height.len());
self.by_height.push(header);
}
pub fn replace_last(&mut self, header: BlockHeader) {
let idx = self.by_height.len() - 1;
self.by_hash.remove(&self.by_height[idx].hash());
self.by_hash.insert(header.hash(), idx);
self.by_height[idx] = header;
}
}
impl BlockHeaderProvider for MemoryBlockHeaderProvider {
fn block_header_bytes(&self, _block_ref: BlockRef) -> Option<Bytes> {
unimplemented!()
}
fn block_header(&self, block_ref: BlockRef) -> Option<BlockHeader> {
match block_ref {
BlockRef::Hash(ref hash) => self.by_hash.get(hash).map(|h| &self.by_height[*h]).cloned(),
BlockRef::Number(height) => self.by_height.get(height as usize).cloned(),
}
}
}
// original test link:
// https://github.com/Bitcoin-ABC/bitcoin-abc/blob/d8eac91f8d16716eed0ad11ccac420122280bb13/src/test/pow_tests.cpp#L193
#[test]
fn zcash_work_required_works() {
let consensus = ConsensusParams::new(Network::Mainnet);
let max_bits = Network::Mainnet.max_bits();
let last_block = 2 * consensus.pow_averaging_window;
let first_block = last_block - consensus.pow_averaging_window;
// insert genesis block
let mut header_provider = MemoryBlockHeaderProvider::default();
header_provider.insert(BlockHeader {
time: 1269211443,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: 0.into(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
reserved_hash: Default::default(),
solution: Default::default(),
});
// Start with blocks evenly-spaced and equal difficulty
for i in 1..last_block+1 {
let header = BlockHeader {
time: header_provider.last().time + consensus.pow_target_spacing,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: header_provider.by_height[i as usize - 1].hash(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
reserved_hash: Default::default(),
solution: Default::default(),
};
header_provider.insert(header);
}
// Result should be the same as if last difficulty was used
let bits_avg: U256 = header_provider.by_height[last_block as usize].bits.into();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into());
assert_eq!(actual, expected);
// Result should be unchanged, modulo integer division precision loss
let mut bits_expected: U256 = Compact::new(0x1e7fffff).into();
bits_expected = bits_expected / consensus.averaging_window_timespan().into();
bits_expected = bits_expected * consensus.averaging_window_timespan().into();
assert_eq!(work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into()),
bits_expected.into());
// Randomise the final block time (plus 1 to ensure it is always different)
use rand::{thread_rng, Rng};
let mut last_header = header_provider.by_height[last_block as usize].clone();
last_header.time += thread_rng().gen_range(1, consensus.pow_target_spacing / 2);
header_provider.replace_last(last_header);
// Result should be the same as if last difficulty was used
let bits_avg: U256 = header_provider.by_height[last_block as usize].bits.into();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into());
assert_eq!(actual, expected);
// Result should not be unchanged
let bits_expected = Compact::new(0x1e7fffff);
assert!(work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into()) != bits_expected);
// Change the final block difficulty
let mut last_header = header_provider.by_height[last_block as usize].clone();
last_header.bits = Compact::new(0x1e0fffff);
header_provider.replace_last(last_header);
// Result should not be the same as if last difficulty was used
let bits_avg = header_provider.by_height[last_block as usize].bits;
let expected = calculate_work_required(bits_avg.into(),
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into());
assert!(actual != expected);
// Result should be the same as if the average difficulty was used
let bits_avg = "0000796968696969696969696969696969696969696969696969696969696969".parse().unwrap();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&consensus, max_bits.into());
let actual = work_required_zcash(header_provider.last().clone().into(),
&header_provider, &consensus, max_bits.into());
assert_eq!(actual, expected);
}
}