Merge remote-tracking branch 'origin/master' into nv2
# Conflicts: # Cargo.lock
This commit is contained in:
commit
899b499466
|
@ -126,7 +126,7 @@ dependencies = [
|
||||||
"bn 0.4.4 (git+https://github.com/paritytech/bn)",
|
"bn 0.4.4 (git+https://github.com/paritytech/bn)",
|
||||||
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"primitives 0.1.0",
|
"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)",
|
"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)",
|
"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 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -807,6 +807,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcrypto 0.1.0",
|
"bitcrypto 0.1.0",
|
||||||
"chain 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)",
|
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"primitives 0.1.0",
|
"primitives 0.1.0",
|
||||||
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1152,6 +1153,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-crypto"
|
name = "rust-crypto"
|
||||||
version = "0.2.36"
|
version = "0.2.36"
|
||||||
|
source = "git+https://github.com/nikvolf/rust-crypto?branch=no-pad#4acc67cb46bb4e1b5b30d9c787a9eab5c8ac9a34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1340,7 +1342,6 @@ name = "storage"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitcrypto 0.1.0",
|
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"display_derive 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
|
"network 0.1.0",
|
||||||
"primitives 0.1.0",
|
"primitives 0.1.0",
|
||||||
"script 0.1.0",
|
"script 0.1.0",
|
||||||
"serialization 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)",
|
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"db 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)",
|
"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)",
|
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"network 0.1.0",
|
"network 0.1.0",
|
||||||
|
@ -1675,6 +1678,10 @@ name = "yaml-rust"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[patch.unused]]
|
||||||
|
name = "rust-crypto"
|
||||||
|
version = "0.2.36"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum abstract-ns 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2f451afbdf8ed8c8f8a98433055bb9a6b7a72aef4baff16227d2a43dd547f43b"
|
"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"
|
"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 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 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 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-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-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"
|
"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69"
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub fn main(benchmark: &mut Benchmark) {
|
||||||
|
|
||||||
// test setup
|
// test setup
|
||||||
let genesis = test_data::genesis();
|
let genesis = test_data::genesis();
|
||||||
|
let consensus = ConsensusParams::new(Network::Unitest);
|
||||||
|
|
||||||
let mut rolling_hash = genesis.hash();
|
let mut rolling_hash = genesis.hash();
|
||||||
let mut blocks: Vec<IndexedBlock> = Vec::new();
|
let mut blocks: Vec<IndexedBlock> = Vec::new();
|
||||||
|
@ -44,6 +45,7 @@ pub fn main(benchmark: &mut Benchmark) {
|
||||||
.merkled_header()
|
.merkled_header()
|
||||||
.parent(rolling_hash.clone())
|
.parent(rolling_hash.clone())
|
||||||
.nonce((x as u8).into())
|
.nonce((x as u8).into())
|
||||||
|
.time(consensus.pow_target_spacing * 7 * (x as u32))
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
rolling_hash = next_block.hash();
|
rolling_hash = next_block.hash();
|
||||||
|
@ -88,6 +90,7 @@ pub fn main(benchmark: &mut Benchmark) {
|
||||||
builder
|
builder
|
||||||
.merkled_header()
|
.merkled_header()
|
||||||
.parent(rolling_hash.clone())
|
.parent(rolling_hash.clone())
|
||||||
|
.time(consensus.pow_target_spacing * 7 * ((b + BLOCKS_INITIAL) as u32))
|
||||||
.build()
|
.build()
|
||||||
.build()
|
.build()
|
||||||
.into());
|
.into());
|
||||||
|
@ -96,7 +99,7 @@ pub fn main(benchmark: &mut Benchmark) {
|
||||||
|
|
||||||
assert_eq!(store.best_block().hash, rolling_hash);
|
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
|
// bench
|
||||||
benchmark.start();
|
benchmark.start();
|
||||||
|
|
|
@ -7,7 +7,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||||
bellman = "0.1"
|
bellman = "0.1"
|
||||||
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" }
|
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" }
|
||||||
pairing = "0.14.2"
|
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" }
|
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
siphasher = "0.1.1"
|
siphasher = "0.1.1"
|
||||||
|
|
|
@ -162,6 +162,20 @@ pub fn sha256(input: &[u8]) -> H256 {
|
||||||
result
|
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
|
/// SHA-256 and RIPEMD160
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dhash160(input: &[u8]) -> H160 {
|
pub fn dhash160(input: &[u8]) -> H160 {
|
||||||
|
@ -215,7 +229,8 @@ impl ::std::fmt::Debug for Groth16VerifyingKey {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use primitives::bytes::Bytes;
|
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]
|
#[test]
|
||||||
fn test_ripemd160() {
|
fn test_ripemd160() {
|
||||||
|
@ -268,4 +283,83 @@ mod tests {
|
||||||
fn test_checksum() {
|
fn test_checksum() {
|
||||||
assert_eq!(checksum(b"hello"), "9595c9df".into());
|
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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crypto::checksum;
|
||||||
use network::Network;
|
use network::Network;
|
||||||
use {DisplayLayout, Error, AddressHash};
|
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
|
/// https://bitcoin.org/en/developer-reference#address-conversion
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
@ -38,7 +38,7 @@ pub struct Address {
|
||||||
pub hash: AddressHash,
|
pub hash: AddressHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AddressDisplayLayout([u8; 25]);
|
pub struct AddressDisplayLayout([u8; 26]);
|
||||||
|
|
||||||
impl Deref for AddressDisplayLayout {
|
impl Deref for AddressDisplayLayout {
|
||||||
type Target = [u8];
|
type Target = [u8];
|
||||||
|
@ -52,41 +52,41 @@ impl DisplayLayout for Address {
|
||||||
type Target = AddressDisplayLayout;
|
type Target = AddressDisplayLayout;
|
||||||
|
|
||||||
fn layout(&self) -> Self::Target {
|
fn layout(&self) -> Self::Target {
|
||||||
let mut result = [0u8; 25];
|
let mut result = [0u8; 26];
|
||||||
|
|
||||||
result[0] = match (self.network, self.kind) {
|
result[..2].copy_from_slice(&match (self.network, self.kind) {
|
||||||
(Network::Mainnet, Type::P2PKH) => 0,
|
(Network::Mainnet, Type::P2PKH) => [0x1C, 0xB8],
|
||||||
(Network::Mainnet, Type::P2SH) => 5,
|
(Network::Testnet, Type::P2PKH) => [0x1D, 0x25],
|
||||||
(Network::Testnet, Type::P2PKH) => 111,
|
(Network::Mainnet, Type::P2SH) => [0x1C, 0xBD],
|
||||||
(Network::Testnet, Type::P2SH) => 196,
|
(Network::Testnet, Type::P2SH) => [0x1C, 0xBA],
|
||||||
};
|
});
|
||||||
|
|
||||||
result[1..21].copy_from_slice(&*self.hash);
|
result[2..22].copy_from_slice(&*self.hash);
|
||||||
let cs = checksum(&result[0..21]);
|
let cs = checksum(&result[0..22]);
|
||||||
result[21..25].copy_from_slice(&*cs);
|
result[22..].copy_from_slice(&*cs);
|
||||||
AddressDisplayLayout(result)
|
AddressDisplayLayout(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_layout(data: &[u8]) -> Result<Self, Error> where Self: Sized {
|
fn from_layout(data: &[u8]) -> Result<Self, Error> where Self: Sized {
|
||||||
if data.len() != 25 {
|
if data.len() != 26 {
|
||||||
return Err(Error::InvalidAddress);
|
return Err(Error::InvalidAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cs = checksum(&data[0..21]);
|
let cs = checksum(&data[..22]);
|
||||||
if &data[21..] != &*cs {
|
if &data[22..] != &*cs {
|
||||||
return Err(Error::InvalidChecksum);
|
return Err(Error::InvalidChecksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (network, kind) = match data[0] {
|
let (network, kind) = match (data[0], data[1]) {
|
||||||
0 => (Network::Mainnet, Type::P2PKH),
|
(0x1C, 0xB8) => (Network::Mainnet, Type::P2PKH),
|
||||||
5 => (Network::Mainnet, Type::P2SH),
|
(0x1C, 0xBD) => (Network::Mainnet, Type::P2SH),
|
||||||
111 => (Network::Testnet, Type::P2PKH),
|
(0x1D, 0x25) => (Network::Testnet, Type::P2PKH),
|
||||||
196 => (Network::Testnet, Type::P2SH),
|
(0x1C, 0xBA) => (Network::Testnet, Type::P2SH),
|
||||||
_ => return Err(Error::InvalidAddress),
|
_ => return Err(Error::InvalidAddress),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hash = AddressHash::default();
|
let mut hash = AddressHash::default();
|
||||||
hash.copy_from_slice(&data[1..21]);
|
hash.copy_from_slice(&data[2..22]);
|
||||||
|
|
||||||
let address = Address {
|
let address = Address {
|
||||||
kind: kind,
|
kind: kind,
|
||||||
|
@ -129,10 +129,10 @@ mod tests {
|
||||||
let address = Address {
|
let address = Address {
|
||||||
kind: Type::P2PKH,
|
kind: Type::P2PKH,
|
||||||
network: Network::Mainnet,
|
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]
|
#[test]
|
||||||
|
@ -140,9 +140,9 @@ mod tests {
|
||||||
let address = Address {
|
let address = Address {
|
||||||
kind: Type::P2PKH,
|
kind: Type::P2PKH,
|
||||||
network: Network::Mainnet,
|
network: Network::Mainnet,
|
||||||
hash: "3f4aa1fedf1f54eeb03b759deadb36676b184911".into(),
|
hash: "ff197b14e502ab41f3bc8ccb48c4abac9eab35bc".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(address, "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna".into());
|
assert_eq!(address, "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,17 +92,15 @@ mod tests {
|
||||||
use super::KeyPair;
|
use super::KeyPair;
|
||||||
|
|
||||||
/// Tests from:
|
/// Tests from:
|
||||||
/// https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/test/key_tests.cpp
|
/// https://github.com/zcash/zcash/blob/66e39a0dd6ffd77bcbede1943195dd456f859cd6/src/test/key_tests.cpp#L25
|
||||||
const SECRET_0: &'static str = "5KSCKP8NUyBZPCCQusxRwgmz9sfvJQEgbGukmmHepWw5Bzp95mu";
|
|
||||||
const SECRET_1: &'static str = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
|
const SECRET_1: &'static str = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
|
||||||
const SECRET_2: &'static str = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
|
const SECRET_2: &'static str = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
|
||||||
const SECRET_1C: &'static str = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
|
const SECRET_1C: &'static str = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
|
||||||
const SECRET_2C: &'static str = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
|
const SECRET_2C: &'static str = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
|
||||||
const ADDRESS_0: &'static str = "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna";
|
const ADDRESS_1: &'static str = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe";
|
||||||
const ADDRESS_1: &'static str = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ";
|
const ADDRESS_2: &'static str = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst";
|
||||||
const ADDRESS_2: &'static str = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ";
|
const ADDRESS_1C: &'static str = "t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC";
|
||||||
const ADDRESS_1C: &'static str = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs";
|
const ADDRESS_2C: &'static str = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6";
|
||||||
const ADDRESS_2C: &'static str = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs";
|
|
||||||
const SIGN_1: &'static str = "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
|
const SIGN_1: &'static str = "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
|
||||||
const SIGN_2: &'static str = "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d";
|
const SIGN_2: &'static str = "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d";
|
||||||
const SIGN_COMPACT_1: &'static str = "1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
|
const SIGN_COMPACT_1: &'static str = "1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6";
|
||||||
|
@ -148,7 +146,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_address() {
|
fn test_keypair_address() {
|
||||||
assert!(check_addresses(SECRET_0, ADDRESS_0));
|
|
||||||
assert!(check_addresses(SECRET_1, ADDRESS_1));
|
assert!(check_addresses(SECRET_1, ADDRESS_1));
|
||||||
assert!(check_addresses(SECRET_2, ADDRESS_2));
|
assert!(check_addresses(SECRET_2, ADDRESS_2));
|
||||||
assert!(check_addresses(SECRET_1C, ADDRESS_1C));
|
assert!(check_addresses(SECRET_1C, ADDRESS_1C));
|
||||||
|
@ -157,7 +154,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_is_compressed() {
|
fn test_keypair_is_compressed() {
|
||||||
assert!(check_compressed(SECRET_0, false));
|
|
||||||
assert!(check_compressed(SECRET_1, false));
|
assert!(check_compressed(SECRET_1, false));
|
||||||
assert!(check_compressed(SECRET_2, false));
|
assert!(check_compressed(SECRET_2, false));
|
||||||
assert!(check_compressed(SECRET_1C, true));
|
assert!(check_compressed(SECRET_1C, true));
|
||||||
|
@ -197,7 +193,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recover_compact() {
|
fn test_recover_compact() {
|
||||||
let message = b"Very deterministic message";
|
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_1, message));
|
||||||
assert!(check_recover_compact(SECRET_1C, message));
|
assert!(check_recover_compact(SECRET_1C, message));
|
||||||
assert!(check_recover_compact(SECRET_2, message));
|
assert!(check_recover_compact(SECRET_2, message));
|
||||||
|
|
|
@ -5,7 +5,7 @@ use chain::{OutPoint, TransactionOutput, IndexedTransaction};
|
||||||
use storage::{SharedStore, TransactionOutputProvider};
|
use storage::{SharedStore, TransactionOutputProvider};
|
||||||
use network::ConsensusParams;
|
use network::ConsensusParams;
|
||||||
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
|
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_VERSION: u32 = 0x20000000;
|
||||||
const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4;
|
const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4;
|
||||||
|
@ -252,10 +252,11 @@ impl BlockAssembler {
|
||||||
let best_block = store.best_block();
|
let best_block = store.best_block();
|
||||||
let previous_header_hash = best_block.hash;
|
let previous_header_hash = best_block.hash;
|
||||||
let height = best_block.number + 1;
|
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 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 mut transactions = Vec::new();
|
||||||
|
|
||||||
let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore);
|
let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore);
|
||||||
|
@ -270,7 +271,7 @@ impl BlockAssembler {
|
||||||
for entry in tx_iter {
|
for entry in tx_iter {
|
||||||
// miner_fee is i64, but we can safely cast it to u64
|
// miner_fee is i64, but we can safely cast it to u64
|
||||||
// memory pool should restrict miner fee to be positive
|
// 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());
|
let tx = IndexedTransaction::new(entry.hash.clone(), entry.transaction.clone());
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
}
|
}
|
||||||
|
@ -282,7 +283,7 @@ impl BlockAssembler {
|
||||||
bits: bits,
|
bits: bits,
|
||||||
height: height,
|
height: height,
|
||||||
transactions: transactions,
|
transactions: transactions,
|
||||||
coinbase_value: coinbase_value,
|
coinbase_value: miner_reward,
|
||||||
size_limit: self.max_block_size,
|
size_limit: self.max_block_size,
|
||||||
sigop_limit: self.max_block_sigops,
|
sigop_limit: self.max_block_sigops,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.0"
|
lazy_static = "1.0"
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
|
keys = { path = "../keys" }
|
||||||
primitives = { path = "../primitives" }
|
primitives = { path = "../primitives" }
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
bitcrypto = { path = "../crypto" }
|
bitcrypto = { path = "../crypto" }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use keys::Address;
|
||||||
use {Network, Magic, Deployment, crypto};
|
use {Network, Magic, Deployment, crypto};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -46,6 +47,25 @@ pub struct ConsensusParams {
|
||||||
pub pow_max_adjust_up: u32,
|
pub pow_max_adjust_up: u32,
|
||||||
/// Optimal blocks interval (in seconds).
|
/// Optimal blocks interval (in seconds).
|
||||||
pub pow_target_spacing: u32,
|
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.
|
/// Equihash (N, K) parameters.
|
||||||
pub equihash_params: Option<(u32, u32)>,
|
pub equihash_params: Option<(u32, u32)>,
|
||||||
|
@ -143,6 +163,60 @@ impl ConsensusParams {
|
||||||
pow_max_adjust_down: 32,
|
pow_max_adjust_down: 32,
|
||||||
pow_max_adjust_up: 16,
|
pow_max_adjust_up: 16,
|
||||||
pow_target_spacing: (2.5 * 60.0) as u32,
|
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)),
|
equihash_params: Some((200, 9)),
|
||||||
|
|
||||||
|
@ -168,6 +242,60 @@ impl ConsensusParams {
|
||||||
pow_max_adjust_down: 32,
|
pow_max_adjust_down: 32,
|
||||||
pow_max_adjust_up: 16,
|
pow_max_adjust_up: 16,
|
||||||
pow_target_spacing: (2.5 * 60.0) as u32,
|
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)),
|
equihash_params: Some((200, 9)),
|
||||||
|
|
||||||
|
@ -193,6 +321,13 @@ impl ConsensusParams {
|
||||||
pow_max_adjust_down: 0,
|
pow_max_adjust_down: 0,
|
||||||
pow_max_adjust_up: 0,
|
pow_max_adjust_up: 0,
|
||||||
pow_target_spacing: (2.5 * 60.0) as u32,
|
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)),
|
equihash_params: Some((200, 9)),
|
||||||
|
|
||||||
|
@ -218,6 +353,13 @@ impl ConsensusParams {
|
||||||
pow_max_adjust_down: 0,
|
pow_max_adjust_down: 0,
|
||||||
pow_max_adjust_up: 0,
|
pow_max_adjust_up: 0,
|
||||||
pow_target_spacing: (2.5 * 60.0) as u32,
|
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,
|
equihash_params: None,
|
||||||
|
|
||||||
|
@ -285,6 +427,45 @@ impl ConsensusParams {
|
||||||
height >= self.sapling_height
|
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 {
|
pub fn consensus_branch_id(&self, height: u32) -> u32 {
|
||||||
// sapling upgrade
|
// sapling upgrade
|
||||||
if height >= self.sapling_height {
|
if height >= self.sapling_height {
|
||||||
|
@ -300,3 +481,22 @@ impl ConsensusParams {
|
||||||
0
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ extern crate chain;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization;
|
extern crate serialization;
|
||||||
extern crate bitcrypto as crypto;
|
extern crate bitcrypto as crypto;
|
||||||
|
extern crate keys;
|
||||||
extern crate rustc_hex as hex;
|
extern crate rustc_hex as hex;
|
||||||
|
|
||||||
mod consensus;
|
mod consensus;
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl Network {
|
||||||
pub fn genesis_block(&self) -> Block {
|
pub fn genesis_block(&self) -> Block {
|
||||||
match *self {
|
match *self {
|
||||||
Network::Mainnet | Network::Other(_) => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac4000000000000000000000000000000000000000000000000000000000000000090041358ffff071f5712000000000000000000000000000000000000000000000000000000000000fd4005000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce1570101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
|
Network::Mainnet | Network::Other(_) => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac4000000000000000000000000000000000000000000000000000000000000000090041358ffff071f5712000000000000000000000000000000000000000000000000000000000000fd4005000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce1570101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
|
||||||
Network::Testnet => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac4000000000000000000000000000000000000000000000000000000000000000090041358ffff071f5712000000000000000000000000000000000000000000000000000000000000fd4005000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce1570101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
|
Network::Testnet => "040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac40000000000000000000000000000000000000000000000000000000000000000a11e1358ffff07200600000000000000000000000000000000000000000000000000000000000000fd400500a6a51259c3f6732481e2d035197218b7a69504461d04335503cd69759b2d02bd2b53a9653f42cb33c608511c953673fa9da76170958115fe92157ad3bb5720d927f18e09459bf5c6072973e143e20f9bdf0584058c96b7c2234c7565f100d5eea083ba5d3dbaff9f0681799a113e7beff4a611d2b49590563109962baa149b628aae869af791f2f70bb041bd7ebfa658570917f6654a142b05e7ec0289a4f46470be7be5f693b90173eaaa6e84907170f32602204f1f4e1c04b1830116ffd0c54f0b1caa9a5698357bd8aa1f5ac8fc93b405265d824ba0e49f69dab5446653927298e6b7bdc61ee86ff31c07bde86331b4e500d42e4e50417e285502684b7966184505b885b42819a88469d1e9cf55072d7f3510f85580db689302eab377e4e11b14a91fdd0df7627efc048934f0aff8e7eb77eb17b3a95de13678004f2512293891d8baf8dde0ef69be520a58bbd6038ce899c9594cf3e30b8c3d9c7ecc832d4c19a6212747b50724e6f70f6451f78fd27b58ce43ca33b1641304a916186cfbe7dbca224f55d08530ba851e4df22baf7ab7078e9cbea46c0798b35a750f54103b0cdd08c81a6505c4932f6bfbd492a9fced31d54e98b6370d4c96600552fcf5b37780ed18c8787d03200963600db297a8f05dfa551321d17b9917edadcda51e274830749d133ad226f8bb6b94f13b4f77e67b35b71f52112ce9ba5da706ad9573584a2570a4ff25d29ab9761a06bdcf2c33638bf9baf2054825037881c14adf3816ba0cbd0fca689aad3ce16f2fe362c98f48134a9221765d939f0b49677d1c2447e56b46859f1810e2cf23e82a53e0d44f34dae932581b3b7f49eaec59af872cf9de757a964f7b33d143a36c270189508fcafe19398e4d2966948164d40556b05b7ff532f66f5d1edc41334ef742f78221dfe0c7ae2275bb3f24c89ae35f00afeea4e6ed187b866b209dc6e83b660593fce7c40e143beb07ac86c56f39e895385924667efe3a3f031938753c7764a2dbeb0a643fd359c46e614873fd0424e435fa7fac083b9a41a9d6bf7e284eee537ea7c50dd239f359941a43dc982745184bf3ee31a8dc850316aa9c6b66d6985acee814373be3458550659e1a06287c3b3b76a185c5cb93e38c1eebcf34ff072894b6430aed8d34122dafd925c46a515cca79b0269c92b301890ca6b0dc8b679cdac0f23318c105de73d7a46d16d2dad988d49c22e9963c117960bdc70ef0db6b091cf09445a516176b7f6d58ec29539166cc8a38bbff387acefffab2ea5faad0e8bb70625716ef0edf61940733c25993ea3de9f0be23d36e7cb8da10505f9dc426cd0e6e5b173ab4fff8c37e1f1fb56d1ea372013d075e0934c6919393cfc21395eea20718fad03542a4162a9ded66c814ad8320b2d7c2da3ecaf206da34c502db2096d1c46699a91dd1c432f019ad434e2c1ce507f91104f66f491fed37b225b8e0b2888c37276cfa0468fc13b8d593fd9a2675f0f5b20b8a15f8fa7558176a530d6865738ddb25d3426dab905221681cf9da0e0200eea5b2eba3ad3a5237d2a391f9074bf1779a2005cee43eec2b058511532635e0fea61664f531ac2b356f40db5c5d275a4cf5c82d468976455af4e3362cc8f71aa95e71d394aff3ead6f7101279f95bcd8a0fedce1d21cb3c9f6dd3b182fce0db5d6712981b651f29178a24119968b14783cafa713bc5f2a65205a42e4ce9dc7ba462bdb1f3e4553afc15f5f39998fdb53e7e231e3e520a46943734a007c2daa1eda9f495791657eefcac5c32833936e568d06187857ed04d7b97167ae207c5c5ae54e528c36016a984235e9c5b2f0718d7b3aa93c7822ccc772580b6599671b3c02ece8a21399abd33cfd3028790133167d0a97e7de53dc8ff0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
|
||||||
Network::Regtest | Network::Unitest => "TODO".into(),
|
Network::Regtest | Network::Unitest => "TODO".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,7 +310,7 @@ pub mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
script_type: ScriptType::Multisig,
|
||||||
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
|
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
|
||||||
},
|
},
|
||||||
version: 33,
|
version: 33,
|
||||||
coinbase: false,
|
coinbase: false,
|
||||||
|
@ -615,7 +615,7 @@ pub mod tests {
|
||||||
"id": 1
|
"id": 1
|
||||||
}"#)).unwrap();
|
}"#)).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]
|
#[test]
|
||||||
|
|
|
@ -222,7 +222,7 @@ pub mod tests {
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "createrawtransaction",
|
"method": "createrawtransaction",
|
||||||
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}],
|
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}],
|
||||||
"id": 1
|
"id": 1
|
||||||
}"#)
|
}"#)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -240,7 +240,7 @@ pub mod tests {
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "createrawtransaction",
|
"method": "createrawtransaction",
|
||||||
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}],
|
"params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}],
|
||||||
"id": 1
|
"id": 1
|
||||||
}"#)
|
}"#)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
|
@ -66,13 +66,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn address_serialize() {
|
fn address_serialize() {
|
||||||
let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into());
|
let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into());
|
||||||
assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#);
|
assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn address_deserialize() {
|
fn address_deserialize() {
|
||||||
let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into());
|
let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into());
|
||||||
assert_eq!(serde_json::from_str::<TestStruct>(r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#).unwrap(), test);
|
assert_eq!(serde_json::from_str::<TestStruct>(r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#).unwrap(), test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,12 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
script_type: ScriptType::Multisig,
|
||||||
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
|
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
|
||||||
},
|
},
|
||||||
version: 33,
|
version: 33,
|
||||||
coinbase: false,
|
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]
|
#[test]
|
||||||
|
@ -59,13 +59,13 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
script_type: ScriptType::Multisig,
|
||||||
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
|
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
|
||||||
},
|
},
|
||||||
version: 33,
|
version: 33,
|
||||||
coinbase: false,
|
coinbase: false,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
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);
|
txout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,11 +253,11 @@ mod tests {
|
||||||
let txout = TransactionOutputs {
|
let txout = TransactionOutputs {
|
||||||
outputs: vec![
|
outputs: vec![
|
||||||
TransactionOutput::Address(TransactionOutputWithAddress {
|
TransactionOutput::Address(TransactionOutputWithAddress {
|
||||||
address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(),
|
address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(),
|
||||||
amount: 123.45,
|
amount: 123.45,
|
||||||
}),
|
}),
|
||||||
TransactionOutput::Address(TransactionOutputWithAddress {
|
TransactionOutput::Address(TransactionOutputWithAddress {
|
||||||
address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(),
|
address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(),
|
||||||
amount: 67.89,
|
amount: 67.89,
|
||||||
}),
|
}),
|
||||||
TransactionOutput::ScriptData(TransactionOutputWithScriptData {
|
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]
|
#[test]
|
||||||
|
@ -276,11 +276,11 @@ mod tests {
|
||||||
let txout = TransactionOutputs {
|
let txout = TransactionOutputs {
|
||||||
outputs: vec![
|
outputs: vec![
|
||||||
TransactionOutput::Address(TransactionOutputWithAddress {
|
TransactionOutput::Address(TransactionOutputWithAddress {
|
||||||
address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(),
|
address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(),
|
||||||
amount: 123.45,
|
amount: 123.45,
|
||||||
}),
|
}),
|
||||||
TransactionOutput::Address(TransactionOutputWithAddress {
|
TransactionOutput::Address(TransactionOutputWithAddress {
|
||||||
address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(),
|
address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(),
|
||||||
amount: 67.89,
|
amount: 67.89,
|
||||||
}),
|
}),
|
||||||
TransactionOutput::ScriptData(TransactionOutputWithScriptData {
|
TransactionOutput::ScriptData(TransactionOutputWithScriptData {
|
||||||
|
@ -292,7 +292,7 @@ mod tests {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
assert_eq!(
|
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);
|
txout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,9 +323,9 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
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]
|
#[test]
|
||||||
|
@ -335,11 +335,11 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
script_type: ScriptType::Multisig,
|
||||||
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
|
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
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);
|
txout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,10 +383,10 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
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]
|
#[test]
|
||||||
|
@ -399,11 +399,11 @@ mod tests {
|
||||||
hex: Bytes::new(vec![1, 2, 3, 4]),
|
hex: Bytes::new(vec![1, 2, 3, 4]),
|
||||||
req_sigs: 777,
|
req_sigs: 777,
|
||||||
script_type: ScriptType::Multisig,
|
script_type: ScriptType::Multisig,
|
||||||
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
|
addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert_eq!(
|
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);
|
txout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -617,7 +617,7 @@ OP_ADD
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_destinations_pub_key_hash() {
|
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);
|
let script = Builder::build_p2pkh(&address);
|
||||||
assert_eq!(script.script_type(), ScriptType::PubKeyHash);
|
assert_eq!(script.script_type(), ScriptType::PubKeyHash);
|
||||||
assert_eq!(script.extract_destinations(), Ok(vec![
|
assert_eq!(script.extract_destinations(), Ok(vec![
|
||||||
|
@ -627,7 +627,7 @@ OP_ADD
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_destinations_script_hash() {
|
fn test_extract_destinations_script_hash() {
|
||||||
let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash;
|
let address = Address::from("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi").hash;
|
||||||
let script = Builder::build_p2sh(&address);
|
let script = Builder::build_p2sh(&address);
|
||||||
assert_eq!(script.script_type(), ScriptType::ScriptHash);
|
assert_eq!(script.script_type(), ScriptType::ScriptHash);
|
||||||
assert_eq!(script.extract_destinations(), Ok(vec![
|
assert_eq!(script.extract_destinations(), Ok(vec![
|
||||||
|
|
|
@ -461,20 +461,17 @@ mod tests {
|
||||||
use ser::deserialize;
|
use ser::deserialize;
|
||||||
use super::{Sighash, UnsignedTransactionInput, TransactionInputSigner, SighashBase};
|
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]
|
#[test]
|
||||||
fn test_signature_hash_simple() {
|
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_tx_hash = H256::from_reversed_str("81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48");
|
||||||
let previous_output_index = 0;
|
let previous_output_index = 0;
|
||||||
let from: Address = "1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5".into();
|
let from: Address = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into();
|
||||||
let to: Address = "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa".into();
|
let to: Address = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst".into();
|
||||||
let previous_output = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into();
|
let previous_output = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into();
|
||||||
let current_output: Bytes = "76a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac".into();
|
let current_output: Bytes = "76a9149a823b698f778ece90b094dc3f12a81f5e3c334588ac".into();
|
||||||
let value = 91234;
|
let value = 91234;
|
||||||
let expected_signature_hash = "5fda68729a6312e17e641e9a49fac2a4a6a680126610af573caab270d232f850".into();
|
let expected_signature_hash = "f6d326b3b48fd8f6d6e29b590d76507aebe647043b1588a35605e9405234e391".into();
|
||||||
|
|
||||||
// this is irrelevant
|
// this is irrelevant
|
||||||
let kp = KeyPair::from_private(private).unwrap();
|
let kp = KeyPair::from_private(private).unwrap();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use EpochTag;
|
use EpochTag;
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use crypto::{Sha256, Digest};
|
|
||||||
|
|
||||||
/// Trait to query sequence of blockchain commitments;
|
/// Trait to query sequence of blockchain commitments;
|
||||||
pub trait CommitmentProvider : Sync {
|
pub trait CommitmentProvider : Sync {
|
||||||
|
|
|
@ -8,7 +8,6 @@ extern crate display_derive;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization as ser;
|
extern crate serialization as ser;
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
extern crate bitcrypto as crypto;
|
|
||||||
|
|
||||||
mod best_block;
|
mod best_block;
|
||||||
mod block_ancestors;
|
mod block_ancestors;
|
||||||
|
|
|
@ -2230,6 +2230,8 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn when_transaction_double_spends_during_reorg() {
|
fn when_transaction_double_spends_during_reorg() {
|
||||||
|
let consensus = ConsensusParams::new(Network::Unitest);
|
||||||
|
|
||||||
let b0 = test_data::block_builder().header().build()
|
let b0 = test_data::block_builder().header().build()
|
||||||
.transaction().coinbase()
|
.transaction().coinbase()
|
||||||
.output().value(10).build()
|
.output().value(10).build()
|
||||||
|
@ -2250,7 +2252,7 @@ pub mod tests {
|
||||||
|
|
||||||
// in-storage spends b0[1] && b0[2]
|
// in-storage spends b0[1] && b0[2]
|
||||||
let b1 = test_data::block_builder()
|
let b1 = test_data::block_builder()
|
||||||
.transaction().coinbase()
|
.transaction().coinbase().founder_reward(&consensus, 1)
|
||||||
.output().value(50).build()
|
.output().value(50).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction().version(10)
|
.transaction().version(10)
|
||||||
|
@ -2280,7 +2282,7 @@ pub mod tests {
|
||||||
|
|
||||||
// in-storage [side] spends b0[3]
|
// in-storage [side] spends b0[3]
|
||||||
let b2 = test_data::block_builder().header().parent(b0.hash()).build()
|
let b2 = test_data::block_builder().header().parent(b0.hash()).build()
|
||||||
.transaction().coinbase()
|
.transaction().coinbase().founder_reward(&consensus, 1)
|
||||||
.output().value(5555).build()
|
.output().value(5555).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction().version(20)
|
.transaction().version(20)
|
||||||
|
@ -2290,7 +2292,8 @@ pub mod tests {
|
||||||
.build();
|
.build();
|
||||||
// in-storage [causes reorg to b2 + b3] spends b0[1]
|
// in-storage [causes reorg to b2 + b3] spends b0[1]
|
||||||
let b3 = test_data::block_builder()
|
let b3 = test_data::block_builder()
|
||||||
.transaction().coinbase().version(40)
|
.transaction().coinbase().founder_reward(&consensus, 2)
|
||||||
|
.version(40)
|
||||||
.output().value(50).build()
|
.output().value(50).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction().version(30)
|
.transaction().version(30)
|
||||||
|
|
|
@ -264,7 +264,8 @@ pub mod tests {
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
use db::BlockChainDatabase;
|
use db::BlockChainDatabase;
|
||||||
use network::{Network, ConsensusParams};
|
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_client_core::CoreVerificationSink;
|
||||||
use synchronization_executor::tests::DummyTaskExecutor;
|
use synchronization_executor::tests::DummyTaskExecutor;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
|
@ -371,18 +372,21 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verification_level_header_accept_incorrect_transaction() {
|
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 blocks: Vec<IndexedBlock> = vec![test_data::genesis().into()];
|
||||||
let mut rolling_hash = blocks[0].hash().clone();
|
let mut rolling_hash = blocks[0].hash().clone();
|
||||||
for i in 1..101 {
|
for i in 1..101 {
|
||||||
let next_block = test_data::block_builder()
|
let next_block = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
.version(i)
|
.founder_reward(&consensus, i)
|
||||||
|
.version(i as i32)
|
||||||
.output().value(5000000000).build()
|
.output().value(5000000000).build()
|
||||||
.build()
|
.build()
|
||||||
.merkled_header()
|
.merkled_header()
|
||||||
.parent(rolling_hash.clone())
|
.parent(rolling_hash.clone())
|
||||||
.bits(Network::Unitest.max_bits().into())
|
.bits(Network::Unitest.max_bits().into())
|
||||||
|
.time(consensus.pow_target_spacing * 7 * i)
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
rolling_hash = next_block.hash();
|
rolling_hash = next_block.hash();
|
||||||
|
@ -394,7 +398,10 @@ pub mod tests {
|
||||||
let storage: StorageRef = Arc::new(BlockChainDatabase::init_test_chain(blocks));
|
let storage: StorageRef = Arc::new(BlockChainDatabase::init_test_chain(blocks));
|
||||||
let verifier = Arc::new(ChainVerifier::new(storage.clone(), ConsensusParams::new(Network::Unitest)));
|
let verifier = Arc::new(ChainVerifier::new(storage.clone(), ConsensusParams::new(Network::Unitest)));
|
||||||
let bad_transaction_block: IndexedBlock = test_data::block_builder()
|
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()
|
.transaction()
|
||||||
.input().hash(coinbase_transaction_hash).build()
|
.input().hash(coinbase_transaction_hash).build()
|
||||||
.output().value(1000).build()
|
.output().value(1000).build()
|
||||||
|
@ -402,6 +409,7 @@ pub mod tests {
|
||||||
.merkled_header()
|
.merkled_header()
|
||||||
.parent(last_block_hash)
|
.parent(last_block_hash)
|
||||||
.bits(Network::Unitest.max_bits().into())
|
.bits(Network::Unitest.max_bits().into())
|
||||||
|
.time(consensus.pow_target_spacing * 7 * 102)
|
||||||
.build()
|
.build()
|
||||||
.build().into();
|
.build().into();
|
||||||
|
|
||||||
|
@ -414,12 +422,11 @@ pub mod tests {
|
||||||
assert_eq!(wrapper.verify_block(&bad_transaction_block), Ok(()));
|
assert_eq!(wrapper.verify_block(&bad_transaction_block), Ok(()));
|
||||||
|
|
||||||
// Error when tx script is checked
|
// Error when tx script is checked
|
||||||
/* TODO: fixme
|
|
||||||
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
|
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
|
||||||
verification_level: VerificationLevel::Full,
|
verification_level: VerificationLevel::Full,
|
||||||
verification_edge: 1.into(),
|
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]
|
#[test]
|
||||||
|
|
|
@ -6,7 +6,8 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
|
network = { path = "../network" }
|
||||||
primitives = { path = "../primitives" }
|
primitives = { path = "../primitives" }
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
script = { path = "../script" }
|
script = { path = "../script" }
|
||||||
|
|
|
@ -6,6 +6,7 @@ use primitives::bytes::Bytes;
|
||||||
use primitives::compact::Compact;
|
use primitives::compact::Compact;
|
||||||
use ser::{Serializable, serialized_list_size};
|
use ser::{Serializable, serialized_list_size};
|
||||||
use chain;
|
use chain;
|
||||||
|
use network::ConsensusParams;
|
||||||
use script::{Builder as ScriptBuilder, Opcode};
|
use script::{Builder as ScriptBuilder, Opcode};
|
||||||
use invoke::{Invoke, Identity};
|
use invoke::{Invoke, Identity};
|
||||||
use super::genesis;
|
use super::genesis;
|
||||||
|
@ -331,6 +332,10 @@ impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
|
||||||
self.input().coinbase().build()
|
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> {
|
pub fn output(self) -> TransactionOutputBuilder<Self> {
|
||||||
TransactionOutputBuilder::with_callback(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 {
|
pub fn value(mut self, value: u64) -> Self {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
self
|
self
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
|
extern crate network;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization as ser;
|
extern crate serialization as ser;
|
||||||
extern crate script;
|
extern crate script;
|
||||||
|
|
|
@ -10,6 +10,7 @@ rayon = "1.0"
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
lazy_static = "1.2.0"
|
lazy_static = "1.2.0"
|
||||||
|
keys = { path = "../keys" }
|
||||||
primitives = { path = "../primitives" }
|
primitives = { path = "../primitives" }
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use keys::Address;
|
||||||
use network::{ConsensusParams};
|
use network::{ConsensusParams};
|
||||||
use storage::{TransactionOutputProvider, BlockHeaderProvider};
|
use storage::{TransactionOutputProvider, BlockHeaderProvider};
|
||||||
use script;
|
use script::{self, Builder};
|
||||||
use sigops::{transaction_sigops};
|
use sigops::{transaction_sigops};
|
||||||
use work::block_reward_satoshi;
|
|
||||||
use duplex_store::DuplexTransactionOutputProvider;
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
use deployments::BlockDeployments;
|
use deployments::BlockDeployments;
|
||||||
use canon::CanonBlock;
|
use canon::CanonBlock;
|
||||||
|
@ -14,7 +14,8 @@ pub struct BlockAcceptor<'a> {
|
||||||
pub finality: BlockFinality<'a>,
|
pub finality: BlockFinality<'a>,
|
||||||
pub serialized_size: BlockSerializedSize<'a>,
|
pub serialized_size: BlockSerializedSize<'a>,
|
||||||
pub sigops: BlockSigops<'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>,
|
pub coinbase_script: BlockCoinbaseScript<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +32,8 @@ impl<'a> BlockAcceptor<'a> {
|
||||||
finality: BlockFinality::new(block, height, deployments, headers),
|
finality: BlockFinality::new(block, height, deployments, headers),
|
||||||
serialized_size: BlockSerializedSize::new(block, consensus),
|
serialized_size: BlockSerializedSize::new(block, consensus),
|
||||||
coinbase_script: BlockCoinbaseScript::new(block, consensus, height),
|
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),
|
sigops: BlockSigops::new(block, store, consensus),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +42,8 @@ impl<'a> BlockAcceptor<'a> {
|
||||||
self.finality.check()?;
|
self.finality.check()?;
|
||||||
self.sigops.check()?;
|
self.sigops.check()?;
|
||||||
self.serialized_size.check()?;
|
self.serialized_size.check()?;
|
||||||
self.coinbase_claim.check()?;
|
self.miner_reward.check()?;
|
||||||
|
self.founder_reward.check()?;
|
||||||
self.coinbase_script.check()?;
|
self.coinbase_script.check()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -144,22 +147,23 @@ impl<'a> BlockSigops<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockCoinbaseClaim<'a> {
|
pub struct BlockCoinbaseMinerReward<'a> {
|
||||||
block: CanonBlock<'a>,
|
block: CanonBlock<'a>,
|
||||||
store: &'a TransactionOutputProvider,
|
store: &'a TransactionOutputProvider,
|
||||||
height: u32,
|
max_reward: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockCoinbaseClaim<'a> {
|
impl<'a> BlockCoinbaseMinerReward<'a> {
|
||||||
fn new(
|
fn new(
|
||||||
block: CanonBlock<'a>,
|
block: CanonBlock<'a>,
|
||||||
store: &'a TransactionOutputProvider,
|
store: &'a TransactionOutputProvider,
|
||||||
|
consensus: &ConsensusParams,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
BlockCoinbaseClaim {
|
BlockCoinbaseMinerReward {
|
||||||
block: block,
|
block: block,
|
||||||
store: store,
|
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 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 {
|
if overflow {
|
||||||
return Err(Error::TransactionFeeAndRewardOverflow);
|
return Err(Error::TransactionFeeAndRewardOverflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if claim > reward {
|
if claim > max_reward {
|
||||||
Err(Error::CoinbaseOverspend { expected_max: reward, actual: claim })
|
Err(Error::CoinbaseOverspend { expected_max: max_reward, actual: claim })
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate test_data;
|
extern crate test_data;
|
||||||
|
|
|
@ -17,14 +17,14 @@ pub struct ChainAcceptor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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());
|
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
|
||||||
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
|
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
|
||||||
let headers = store.as_block_header_provider();
|
let headers = store.as_block_header_provider();
|
||||||
|
|
||||||
ChainAcceptor {
|
ChainAcceptor {
|
||||||
block: BlockAcceptor::new(store.as_transaction_output_provider(), consensus, block, height, deployments, headers),
|
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()
|
transactions: block.transactions()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
|
@ -18,11 +18,12 @@ impl<'a> HeaderAcceptor<'a> {
|
||||||
consensus: &'a ConsensusParams,
|
consensus: &'a ConsensusParams,
|
||||||
header: CanonHeader<'a>,
|
header: CanonHeader<'a>,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
time: u32,
|
||||||
deployments: D,
|
deployments: D,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let csv_active = deployments.as_ref().csv(height, store, consensus);
|
let csv_active = deployments.as_ref().csv(height, store, consensus);
|
||||||
HeaderAcceptor {
|
HeaderAcceptor {
|
||||||
work: HeaderWork::new(header, store, height, consensus),
|
work: HeaderWork::new(header, store, height, time, consensus),
|
||||||
median_timestamp: HeaderMedianTimestamp::new(header, store, csv_active),
|
median_timestamp: HeaderMedianTimestamp::new(header, store, csv_active),
|
||||||
version: HeaderVersion::new(header, height, consensus),
|
version: HeaderVersion::new(header, height, consensus),
|
||||||
}
|
}
|
||||||
|
@ -66,22 +67,24 @@ pub struct HeaderWork<'a> {
|
||||||
header: CanonHeader<'a>,
|
header: CanonHeader<'a>,
|
||||||
store: &'a BlockHeaderProvider,
|
store: &'a BlockHeaderProvider,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
time: u32,
|
||||||
consensus: &'a ConsensusParams,
|
consensus: &'a ConsensusParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HeaderWork<'a> {
|
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 {
|
HeaderWork {
|
||||||
header: header,
|
header: header,
|
||||||
store: store,
|
store: store,
|
||||||
height: height,
|
height: height,
|
||||||
|
time: time,
|
||||||
consensus: consensus,
|
consensus: consensus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let previous_header_hash = self.header.raw.previous_header_hash.clone();
|
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 {
|
if work == self.header.raw.bits {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl BackwardsCompatibleChainVerifier {
|
||||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||||
let canon_block = CanonBlock::new(block);
|
let canon_block = CanonBlock::new(block);
|
||||||
let chain_acceptor = ChainAcceptor::new(self.store.as_store(), &self.consensus, verification_level,
|
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()?;
|
chain_acceptor.check()?;
|
||||||
},
|
},
|
||||||
BlockOrigin::SideChain(origin) => {
|
BlockOrigin::SideChain(origin) => {
|
||||||
|
@ -64,7 +64,7 @@ impl BackwardsCompatibleChainVerifier {
|
||||||
let fork = self.store.fork(origin)?;
|
let fork = self.store.fork(origin)?;
|
||||||
let canon_block = CanonBlock::new(block);
|
let canon_block = CanonBlock::new(block);
|
||||||
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_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()?;
|
chain_acceptor.check()?;
|
||||||
},
|
},
|
||||||
BlockOrigin::SideChainBecomingCanonChain(origin) => {
|
BlockOrigin::SideChainBecomingCanonChain(origin) => {
|
||||||
|
@ -74,7 +74,7 @@ impl BackwardsCompatibleChainVerifier {
|
||||||
let fork = self.store.fork(origin)?;
|
let fork = self.store.fork(origin)?;
|
||||||
let canon_block = CanonBlock::new(block);
|
let canon_block = CanonBlock::new(block);
|
||||||
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_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()?;
|
chain_acceptor.check()?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coinbase_maturity() {
|
fn coinbase_maturity() {
|
||||||
|
let consensus = ConsensusParams::new(Network::Unitest);
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
@ -199,6 +200,7 @@ mod tests {
|
||||||
let block = test_data::block_builder()
|
let block = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.founder_reward(&consensus, 1)
|
||||||
.output().value(1).build()
|
.output().value(1).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
|
@ -208,7 +210,7 @@ mod tests {
|
||||||
.merkled_header().parent(genesis.hash()).build()
|
.merkled_header().parent(genesis.hash()).build()
|
||||||
.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(
|
let expected = Err(Error::Transaction(
|
||||||
1,
|
1,
|
||||||
|
@ -220,6 +222,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn non_coinbase_happy() {
|
fn non_coinbase_happy() {
|
||||||
|
let consensus = ConsensusParams::new(Network::Unitest);
|
||||||
|
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
@ -237,6 +241,7 @@ mod tests {
|
||||||
let block = test_data::block_builder()
|
let block = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.founder_reward(&consensus, 1)
|
||||||
.output().value(2).build()
|
.output().value(2).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
|
@ -246,12 +251,14 @@ mod tests {
|
||||||
.merkled_header().parent(genesis.hash()).build()
|
.merkled_header().parent(genesis.hash()).build()
|
||||||
.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(()));
|
assert_eq!(verifier.verify(VerificationLevel::Full, &block.into()), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_references_same_block_happy() {
|
fn transaction_references_same_block_happy() {
|
||||||
|
let consensus = ConsensusParams::new(Network::Unitest);
|
||||||
|
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
@ -269,6 +276,7 @@ mod tests {
|
||||||
let block = test_data::block_builder()
|
let block = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.founder_reward(&consensus, 1)
|
||||||
.output().value(2).build()
|
.output().value(2).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
|
@ -282,7 +290,7 @@ mod tests {
|
||||||
.merkled_header().parent(genesis.hash()).build()
|
.merkled_header().parent(genesis.hash()).build()
|
||||||
.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());
|
assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +434,7 @@ mod tests {
|
||||||
let block: IndexedBlock = test_data::block_builder()
|
let block: IndexedBlock = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
.output().value(5000000001).build()
|
.output().value(1250000001).build()
|
||||||
.build()
|
.build()
|
||||||
.merkled_header().parent(genesis.hash()).build()
|
.merkled_header().parent(genesis.hash()).build()
|
||||||
.build()
|
.build()
|
||||||
|
@ -435,8 +443,8 @@ mod tests {
|
||||||
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
|
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
|
||||||
|
|
||||||
let expected = Err(Error::CoinbaseOverspend {
|
let expected = Err(Error::CoinbaseOverspend {
|
||||||
expected_max: 5000000000,
|
expected_max: 1250000000,
|
||||||
actual: 5000000001
|
actual: 1250000001,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
|
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
|
||||||
|
|
|
@ -59,6 +59,8 @@ pub enum Error {
|
||||||
InvalidEquihashSolution,
|
InvalidEquihashSolution,
|
||||||
/// Invalid block version
|
/// Invalid block version
|
||||||
InvalidVersion,
|
InvalidVersion,
|
||||||
|
/// Block' coinbase is missing founders reward output.
|
||||||
|
MissingFoundersReward,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DBError> for Error {
|
impl From<DBError> for Error {
|
||||||
|
|
|
@ -65,6 +65,7 @@ extern crate rustc_hex as hex;
|
||||||
|
|
||||||
extern crate storage;
|
extern crate storage;
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
|
extern crate keys;
|
||||||
extern crate network;
|
extern crate network;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization as ser;
|
extern crate serialization as ser;
|
||||||
|
@ -87,7 +88,6 @@ mod sapling;
|
||||||
mod sigops;
|
mod sigops;
|
||||||
mod timestamp;
|
mod timestamp;
|
||||||
mod work;
|
mod work;
|
||||||
mod work_zcash;
|
|
||||||
|
|
||||||
// pre-verification
|
// pre-verification
|
||||||
mod verify_block;
|
mod verify_block;
|
||||||
|
@ -121,7 +121,7 @@ pub use chain_verifier::BackwardsCompatibleChainVerifier;
|
||||||
pub use error::{Error, TransactionError};
|
pub use error::{Error, TransactionError};
|
||||||
pub use sigops::transaction_sigops;
|
pub use sigops::transaction_sigops;
|
||||||
pub use timestamp::{median_timestamp, median_timestamp_inclusive};
|
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;
|
pub use deployments::Deployments;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use primitives::compact::Compact;
|
use primitives::compact::Compact;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use primitives::bigint::U256;
|
use primitives::bigint::{Uint, U256};
|
||||||
use chain::IndexedBlockHeader;
|
|
||||||
use network::ConsensusParams;
|
use network::ConsensusParams;
|
||||||
use storage::BlockHeaderProvider;
|
use storage::{BlockHeaderProvider, BlockAncestors};
|
||||||
use work_zcash::work_required_zcash;
|
use timestamp::median_timestamp_inclusive;
|
||||||
|
|
||||||
/// Returns true if hash is lower or equal than target represented by compact bits
|
/// 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 {
|
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
|
/// 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();
|
let max_bits = consensus.network.max_bits().into();
|
||||||
|
|
||||||
|
// chain starts with has minimal difficulty
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
return max_bits;
|
return max_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent_header = store.block_header(parent_hash.clone().into()).expect("self.height != 0; qed");
|
let parent_header = store.block_header(parent_hash.clone().into()).expect("self.height != 0; qed");
|
||||||
|
|
||||||
work_required_zcash(IndexedBlockHeader {
|
// Special difficulty rule for testnet:
|
||||||
hash: parent_hash,
|
// If the new block's timestamp is more than 6 * 2.5 minutes
|
||||||
raw: parent_header
|
// then allow mining of a min-difficulty block.
|
||||||
}, store, consensus, max_bits)
|
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 {
|
fn calculate_work_required(bits_avg: U256, parent_mtp: u32, oldest_mtp: u32, consensus: &ConsensusParams, max_bits: Compact) -> Compact {
|
||||||
let mut res = 50 * 100 * 1000 * 1000;
|
// Limit adjustment step
|
||||||
for _ in 0..block_height / 210000 { res /= 2 }
|
// Use medians to prevent time-warp attacks
|
||||||
res
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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]
|
#[test]
|
||||||
fn reward() {
|
fn main_chain_required_work_works() {
|
||||||
assert_eq!(block_reward_satoshi(0), 5000000000);
|
let consensus = ConsensusParams::new(Network::Mainnet);
|
||||||
assert_eq!(block_reward_satoshi(209999), 5000000000);
|
|
||||||
assert_eq!(block_reward_satoshi(210000), 2500000000);
|
// insert genesis block
|
||||||
assert_eq!(block_reward_satoshi(420000), 1250000000);
|
let mut header_provider = MemoryBlockHeaderProvider::default();
|
||||||
assert_eq!(block_reward_satoshi(420001), 1250000000);
|
let genesis = test_data::genesis().block_header;
|
||||||
assert_eq!(block_reward_satoshi(629999), 1250000000);
|
header_provider.insert(genesis.clone());
|
||||||
assert_eq!(block_reward_satoshi(630000), 625000000);
|
|
||||||
assert_eq!(block_reward_satoshi(630001), 625000000);
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue