diff --git a/Cargo.lock b/Cargo.lock index 621c656a..967f31e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -807,6 +807,7 @@ version = "0.1.0" dependencies = [ "bitcrypto 0.1.0", "chain 0.1.0", + "keys 0.1.0", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1446,6 +1447,7 @@ name = "test-data" version = "0.1.0" dependencies = [ "chain 0.1.0", + "network 0.1.0", "primitives 0.1.0", "script 0.1.0", "serialization 0.1.0", @@ -1585,6 +1587,7 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "db 0.1.0", + "keys 0.1.0", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "network 0.1.0", diff --git a/keys/src/address.rs b/keys/src/address.rs index 40987c40..37e21525 100644 --- a/keys/src/address.rs +++ b/keys/src/address.rs @@ -13,7 +13,7 @@ use crypto::checksum; use network::Network; use {DisplayLayout, Error, AddressHash}; -/// There are two address formats currently in use. +/// There are two transparent address formats currently in use. /// https://bitcoin.org/en/developer-reference#address-conversion #[derive(Debug, PartialEq, Clone, Copy)] pub enum Type { @@ -38,7 +38,7 @@ pub struct Address { pub hash: AddressHash, } -pub struct AddressDisplayLayout([u8; 25]); +pub struct AddressDisplayLayout([u8; 26]); impl Deref for AddressDisplayLayout { type Target = [u8]; @@ -52,41 +52,41 @@ impl DisplayLayout for Address { type Target = AddressDisplayLayout; fn layout(&self) -> Self::Target { - let mut result = [0u8; 25]; + let mut result = [0u8; 26]; - result[0] = match (self.network, self.kind) { - (Network::Mainnet, Type::P2PKH) => 0, - (Network::Mainnet, Type::P2SH) => 5, - (Network::Testnet, Type::P2PKH) => 111, - (Network::Testnet, Type::P2SH) => 196, - }; + result[..2].copy_from_slice(&match (self.network, self.kind) { + (Network::Mainnet, Type::P2PKH) => [0x1C, 0xB8], + (Network::Testnet, Type::P2PKH) => [0x1D, 0x25], + (Network::Mainnet, Type::P2SH) => [0x1C, 0xBD], + (Network::Testnet, Type::P2SH) => [0x1C, 0xBA], + }); - result[1..21].copy_from_slice(&*self.hash); - let cs = checksum(&result[0..21]); - result[21..25].copy_from_slice(&*cs); + result[2..22].copy_from_slice(&*self.hash); + let cs = checksum(&result[0..22]); + result[22..].copy_from_slice(&*cs); AddressDisplayLayout(result) } fn from_layout(data: &[u8]) -> Result where Self: Sized { - if data.len() != 25 { + if data.len() != 26 { return Err(Error::InvalidAddress); } - let cs = checksum(&data[0..21]); - if &data[21..] != &*cs { + let cs = checksum(&data[..22]); + if &data[22..] != &*cs { return Err(Error::InvalidChecksum); } - let (network, kind) = match data[0] { - 0 => (Network::Mainnet, Type::P2PKH), - 5 => (Network::Mainnet, Type::P2SH), - 111 => (Network::Testnet, Type::P2PKH), - 196 => (Network::Testnet, Type::P2SH), + let (network, kind) = match (data[0], data[1]) { + (0x1C, 0xB8) => (Network::Mainnet, Type::P2PKH), + (0x1C, 0xBD) => (Network::Mainnet, Type::P2SH), + (0x1D, 0x25) => (Network::Testnet, Type::P2PKH), + (0x1C, 0xBA) => (Network::Testnet, Type::P2SH), _ => return Err(Error::InvalidAddress), }; let mut hash = AddressHash::default(); - hash.copy_from_slice(&data[1..21]); + hash.copy_from_slice(&data[2..22]); let address = Address { kind: kind, @@ -129,10 +129,10 @@ mod tests { let address = Address { kind: Type::P2PKH, network: Network::Mainnet, - hash: "3f4aa1fedf1f54eeb03b759deadb36676b184911".into(), + hash: "ff197b14e502ab41f3bc8ccb48c4abac9eab35bc".into(), }; - assert_eq!("16meyfSoQV6twkAAxPe51RtMVz7PGRmWna".to_owned(), address.to_string()); + assert_eq!("t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".to_owned(), address.to_string()); } #[test] @@ -140,9 +140,9 @@ mod tests { let address = Address { kind: Type::P2PKH, network: Network::Mainnet, - hash: "3f4aa1fedf1f54eeb03b759deadb36676b184911".into(), + hash: "ff197b14e502ab41f3bc8ccb48c4abac9eab35bc".into(), }; - assert_eq!(address, "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna".into()); + assert_eq!(address, "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into()); } } diff --git a/keys/src/keypair.rs b/keys/src/keypair.rs index f0dc93ac..13856558 100644 --- a/keys/src/keypair.rs +++ b/keys/src/keypair.rs @@ -92,17 +92,15 @@ mod tests { use super::KeyPair; /// Tests from: - /// https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/test/key_tests.cpp - const SECRET_0: &'static str = "5KSCKP8NUyBZPCCQusxRwgmz9sfvJQEgbGukmmHepWw5Bzp95mu"; + /// https://github.com/zcash/zcash/blob/66e39a0dd6ffd77bcbede1943195dd456f859cd6/src/test/key_tests.cpp#L25 const SECRET_1: &'static str = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; const SECRET_2: &'static str = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; const SECRET_1C: &'static str = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; const SECRET_2C: &'static str = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; - const ADDRESS_0: &'static str = "16meyfSoQV6twkAAxPe51RtMVz7PGRmWna"; - const ADDRESS_1: &'static str = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; - const ADDRESS_2: &'static str = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; - const ADDRESS_1C: &'static str = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; - const ADDRESS_2C: &'static str = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; + const ADDRESS_1: &'static str = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe"; + const ADDRESS_2: &'static str = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst"; + const ADDRESS_1C: &'static str = "t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC"; + const ADDRESS_2C: &'static str = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6"; const SIGN_1: &'static str = "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"; const SIGN_2: &'static str = "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"; const SIGN_COMPACT_1: &'static str = "1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"; @@ -148,7 +146,6 @@ mod tests { #[test] fn test_keypair_address() { - assert!(check_addresses(SECRET_0, ADDRESS_0)); assert!(check_addresses(SECRET_1, ADDRESS_1)); assert!(check_addresses(SECRET_2, ADDRESS_2)); assert!(check_addresses(SECRET_1C, ADDRESS_1C)); @@ -157,7 +154,6 @@ mod tests { #[test] fn test_keypair_is_compressed() { - assert!(check_compressed(SECRET_0, false)); assert!(check_compressed(SECRET_1, false)); assert!(check_compressed(SECRET_2, false)); assert!(check_compressed(SECRET_1C, true)); @@ -197,7 +193,6 @@ mod tests { #[test] fn test_recover_compact() { let message = b"Very deterministic message"; - assert!(check_recover_compact(SECRET_0, message)); assert!(check_recover_compact(SECRET_1, message)); assert!(check_recover_compact(SECRET_1C, message)); assert!(check_recover_compact(SECRET_2, message)); diff --git a/miner/src/block_assembler.rs b/miner/src/block_assembler.rs index 5d690b22..07ceb6c2 100644 --- a/miner/src/block_assembler.rs +++ b/miner/src/block_assembler.rs @@ -5,7 +5,7 @@ use chain::{OutPoint, TransactionOutput, IndexedTransaction}; use storage::{SharedStore, TransactionOutputProvider}; use network::ConsensusParams; use memory_pool::{MemoryPool, OrderingStrategy, Entry}; -use verification::{work_required, block_reward_satoshi, transaction_sigops}; +use verification::{work_required, transaction_sigops}; const BLOCK_VERSION: u32 = 0x20000000; const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4; @@ -255,7 +255,8 @@ impl BlockAssembler { let bits = work_required(previous_header_hash.clone(), height, store.as_block_header_provider(), consensus); let version = BLOCK_VERSION; - let mut coinbase_value = block_reward_satoshi(height); + // TODO: sync with ZCash RPC - need to return founder reward? + let mut miner_reward = consensus.miner_reward(height); let mut transactions = Vec::new(); let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore); @@ -270,7 +271,7 @@ impl BlockAssembler { for entry in tx_iter { // miner_fee is i64, but we can safely cast it to u64 // memory pool should restrict miner fee to be positive - coinbase_value += entry.miner_fee as u64; + miner_reward += entry.miner_fee as u64; let tx = IndexedTransaction::new(entry.hash.clone(), entry.transaction.clone()); transactions.push(tx); } @@ -282,7 +283,7 @@ impl BlockAssembler { bits: bits, height: height, transactions: transactions, - coinbase_value: coinbase_value, + coinbase_value: miner_reward, size_limit: self.max_block_size, sigop_limit: self.max_block_sigops, } diff --git a/network/Cargo.toml b/network/Cargo.toml index c1570c81..915f2b1c 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -6,6 +6,7 @@ authors = ["debris "] [dependencies] lazy_static = "1.0" chain = { path = "../chain" } +keys = { path = "../keys" } primitives = { path = "../primitives" } serialization = { path = "../serialization" } bitcrypto = { path = "../crypto" } diff --git a/network/src/consensus.rs b/network/src/consensus.rs index c37c46f6..7b475fb1 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -1,3 +1,4 @@ +use keys::Address; use {Network, Magic, Deployment, crypto}; lazy_static! { @@ -47,6 +48,23 @@ pub struct ConsensusParams { /// Optimal blocks interval (in seconds). pub pow_target_spacing: 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
, + /// Equihash (N, K) parameters. pub equihash_params: Option<(u32, u32)>, @@ -144,6 +162,59 @@ impl ConsensusParams { pow_max_adjust_up: 16, pow_target_spacing: (2.5 * 60.0) as u32, + 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)), joinsplit_verification_key: mainnet_pghr_verification_key(), @@ -169,6 +240,59 @@ impl ConsensusParams { pow_max_adjust_up: 16, pow_target_spacing: (2.5 * 60.0) as u32, + 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)), joinsplit_verification_key: testnet_pghr_verification_key(), @@ -194,6 +318,12 @@ impl ConsensusParams { pow_max_adjust_up: 0, pow_target_spacing: (2.5 * 60.0) as u32, + subsidy_slow_start_interval: 0, + subsidy_halving_interval: 150, + founders_addresses: vec![ + "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg".into(), + ], + equihash_params: Some((200, 9)), joinsplit_verification_key: regtest_pghr_verification_key(), @@ -219,6 +349,12 @@ impl ConsensusParams { pow_max_adjust_up: 0, pow_target_spacing: (2.5 * 60.0) as u32, + subsidy_slow_start_interval: 0, + subsidy_halving_interval: 150, + founders_addresses: vec![ + "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg".into(), + ], + equihash_params: None, joinsplit_verification_key: unitest_pghr_verification_key(), @@ -285,6 +421,45 @@ impl ConsensusParams { height >= self.sapling_height } + /// Block reward (goes to miner) at given height. + pub fn miner_reward(&self, height: u32) -> u64 { + let mut reward = 1_250_000_000u64; + if height < self.subsidy_slow_start_interval / 2 { + reward /= self.subsidy_slow_start_interval as u64; + reward *= height as u64; + } else if height < self.subsidy_slow_start_interval { + reward /= self.subsidy_slow_start_interval as u64; + reward *= height as u64 + 1; + } else { + let halvings = (height - self.subsidy_slow_start_interval / 2) / self.subsidy_halving_interval; + if halvings >= 64 { + return 0; + } + + reward >>= halvings as u64; + } + + reward + } + + /// Founders reward (goes to founders) at given height. + pub fn founder_reward(&self, height: u32) -> u64 { + self.miner_reward(height) / 5 + } + + /// Address (transparent) where founders reward goes at given height. + pub fn founder_address(&self, height: u32) -> Option
{ + let last_founder_reward_block_height = self.subsidy_halving_interval + self.subsidy_slow_start_interval / 2 - 1; + if height == 0 || height > last_founder_reward_block_height { + return None; + } + + let founders_len = self.founders_addresses.len() as u32; + let address_change_interval = (last_founder_reward_block_height + founders_len) / founders_len; + let address_index = height / address_change_interval; + Some(self.founders_addresses[address_index as usize].clone()) + } + pub fn consensus_branch_id(&self, height: u32) -> u32 { // sapling upgrade if height >= self.sapling_height { @@ -300,3 +475,22 @@ impl ConsensusParams { 0 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn miner_reward() { + let consensus = ConsensusParams::new(Network::Mainnet); + assert_eq!(consensus.miner_reward(1), 62_500); + assert_eq!(consensus.miner_reward(10_000), 625_062_500); + assert_eq!(consensus.miner_reward(20_000), 1_250_000_000); + assert_eq!(consensus.miner_reward(1_000_000), 625_000_000); + assert_eq!(consensus.miner_reward(2_000_000), 312_500_000); + assert_eq!(consensus.miner_reward(3_000_000), 156_250_000); + assert_eq!(consensus.miner_reward(4_000_000), 78_125_000); + assert_eq!(consensus.miner_reward(20_000_000), 149); + assert_eq!(consensus.miner_reward(30_000_000), 0); + } +} diff --git a/network/src/lib.rs b/network/src/lib.rs index 9b8a3d15..00b48b0c 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -5,6 +5,7 @@ extern crate chain; extern crate primitives; extern crate serialization; extern crate bitcrypto as crypto; +extern crate keys; extern crate rustc_hex as hex; mod consensus; diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index e72f35c9..24d21d62 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -310,7 +310,7 @@ pub mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }, version: 33, coinbase: false, @@ -615,7 +615,7 @@ pub mod tests { "id": 1 }"#)).unwrap(); - assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bestblock":"0000000000000000000000000000000000000000000000000000000000000056","coinbase":false,"confirmations":777,"scriptPubKey":{"addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"],"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig"},"value":100000.56,"version":33},"id":1}"#); + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bestblock":"0000000000000000000000000000000000000000000000000000000000000056","coinbase":false,"confirmations":777,"scriptPubKey":{"addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"],"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig"},"value":100000.56,"version":33},"id":1}"#); } #[test] diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index 19edb81c..0929034d 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -222,7 +222,7 @@ pub mod tests { { "jsonrpc": "2.0", "method": "createrawtransaction", - "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], + "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}], "id": 1 }"#) ).unwrap(); @@ -240,7 +240,7 @@ pub mod tests { { "jsonrpc": "2.0", "method": "createrawtransaction", - "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], + "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":0.01}], "id": 1 }"#) ).unwrap(); diff --git a/rpc/src/v1/types/address.rs b/rpc/src/v1/types/address.rs index c8b611b1..63bec88f 100644 --- a/rpc/src/v1/types/address.rs +++ b/rpc/src/v1/types/address.rs @@ -66,13 +66,13 @@ mod tests { #[test] fn address_serialize() { - let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into()); - assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#); + let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into()); + assert_eq!(serde_json::to_string(&test).unwrap(), r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#); } #[test] fn address_deserialize() { - let test = TestStruct::new("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into()); - assert_eq!(serde_json::from_str::(r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"}"#).unwrap(), test); + let test = TestStruct::new("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into()); + assert_eq!(serde_json::from_str::(r#"{"address":"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"}"#).unwrap(), test); } } diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index 76dcbded..e404e0f3 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -40,12 +40,12 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }, version: 33, coinbase: false, }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#); + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]},"version":33,"coinbase":false}"#); } #[test] @@ -59,13 +59,13 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }, version: 33, coinbase: false, }; assert_eq!( - serde_json::from_str::(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::(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); } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 9e891be9..853226ba 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -253,11 +253,11 @@ mod tests { let txout = TransactionOutputs { outputs: vec![ TransactionOutput::Address(TransactionOutputWithAddress { - address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), amount: 123.45, }), TransactionOutput::Address(TransactionOutputWithAddress { - address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), + address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), amount: 67.89, }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { @@ -268,7 +268,7 @@ mod tests { }), ] }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#); + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#); } #[test] @@ -276,11 +276,11 @@ mod tests { let txout = TransactionOutputs { outputs: vec![ TransactionOutput::Address(TransactionOutputWithAddress { - address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), amount: 123.45, }), TransactionOutput::Address(TransactionOutputWithAddress { - address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), + address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), amount: 67.89, }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { @@ -292,7 +292,7 @@ mod tests { ] }; assert_eq!( - serde_json::from_str::(r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#).unwrap(), + serde_json::from_str::(r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#).unwrap(), txout); } @@ -323,9 +323,9 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#); + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#); } #[test] @@ -335,11 +335,11 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }; assert_eq!( - serde_json::from_str::(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#).unwrap(), + serde_json::from_str::(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#).unwrap(), txout); } @@ -383,10 +383,10 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }, }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#); + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#); } #[test] @@ -399,11 +399,11 @@ mod tests { hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + addresses: vec!["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into()], }, }; assert_eq!( - serde_json::from_str::(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#).unwrap(), + serde_json::from_str::(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#).unwrap(), txout); } diff --git a/script/src/script.rs b/script/src/script.rs index 3d3cd5df..0d287693 100644 --- a/script/src/script.rs +++ b/script/src/script.rs @@ -617,7 +617,7 @@ OP_ADD #[test] fn test_extract_destinations_pub_key_hash() { - let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; + let address = Address::from("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi").hash; let script = Builder::build_p2pkh(&address); assert_eq!(script.script_type(), ScriptType::PubKeyHash); assert_eq!(script.extract_destinations(), Ok(vec![ @@ -627,7 +627,7 @@ OP_ADD #[test] fn test_extract_destinations_script_hash() { - let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; + let address = Address::from("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi").hash; let script = Builder::build_p2sh(&address); assert_eq!(script.script_type(), ScriptType::ScriptHash); assert_eq!(script.extract_destinations(), Ok(vec![ diff --git a/script/src/sign.rs b/script/src/sign.rs index 3fc1b87a..8920ae38 100644 --- a/script/src/sign.rs +++ b/script/src/sign.rs @@ -461,20 +461,17 @@ mod tests { use ser::deserialize; use super::{Sighash, UnsignedTransactionInput, TransactionInputSigner, SighashBase}; - // http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html - // https://blockchain.info/rawtx/81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48 - // https://blockchain.info/rawtx/3f285f083de7c0acabd9f106a43ec42687ab0bebe2e6f0d529db696794540fea #[test] fn test_signature_hash_simple() { - let private: Private = "5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD".into(); + let private: Private = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj".into(); let previous_tx_hash = H256::from_reversed_str("81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48"); let previous_output_index = 0; - let from: Address = "1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5".into(); - let to: Address = "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa".into(); + let from: Address = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into(); + let to: Address = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst".into(); let previous_output = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into(); - let current_output: Bytes = "76a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac".into(); + let current_output: Bytes = "76a9149a823b698f778ece90b094dc3f12a81f5e3c334588ac".into(); let value = 91234; - let expected_signature_hash = "5fda68729a6312e17e641e9a49fac2a4a6a680126610af573caab270d232f850".into(); + let expected_signature_hash = "f6d326b3b48fd8f6d6e29b590d76507aebe647043b1588a35605e9405234e391".into(); // this is irrelevant let kp = KeyPair::from_private(private).unwrap(); diff --git a/sync/src/synchronization_client_core.rs b/sync/src/synchronization_client_core.rs index 9a0d1fed..7c303fcf 100644 --- a/sync/src/synchronization_client_core.rs +++ b/sync/src/synchronization_client_core.rs @@ -2230,6 +2230,8 @@ pub mod tests { #[test] fn when_transaction_double_spends_during_reorg() { + let consensus = ConsensusParams::new(Network::Unitest); + let b0 = test_data::block_builder().header().build() .transaction().coinbase() .output().value(10).build() @@ -2250,7 +2252,7 @@ pub mod tests { // in-storage spends b0[1] && b0[2] let b1 = test_data::block_builder() - .transaction().coinbase() + .transaction().coinbase().founder_reward(&consensus, 1) .output().value(50).build() .build() .transaction().version(10) @@ -2280,7 +2282,7 @@ pub mod tests { // in-storage [side] spends b0[3] let b2 = test_data::block_builder().header().parent(b0.hash()).build() - .transaction().coinbase() + .transaction().coinbase().founder_reward(&consensus, 1) .output().value(5555).build() .build() .transaction().version(20) @@ -2290,7 +2292,8 @@ pub mod tests { .build(); // in-storage [causes reorg to b2 + b3] spends b0[1] let b3 = test_data::block_builder() - .transaction().coinbase().version(40) + .transaction().coinbase().founder_reward(&consensus, 2) + .version(40) .output().value(50).build() .build() .transaction().version(30) diff --git a/sync/src/synchronization_verifier.rs b/sync/src/synchronization_verifier.rs index 4f279fdf..318226e3 100644 --- a/sync/src/synchronization_verifier.rs +++ b/sync/src/synchronization_verifier.rs @@ -264,7 +264,8 @@ pub mod tests { use std::collections::{HashSet, HashMap}; use db::BlockChainDatabase; use network::{Network, ConsensusParams}; - use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError}; + use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError, TransactionError}; + use script::Error as ScriptError; use synchronization_client_core::CoreVerificationSink; use synchronization_executor::tests::DummyTaskExecutor; use primitives::hash::H256; @@ -371,13 +372,15 @@ pub mod tests { #[test] fn verification_level_header_accept_incorrect_transaction() { + let consensus = ConsensusParams::new(Network::Unitest); let mut blocks: Vec = vec![test_data::genesis().into()]; let mut rolling_hash = blocks[0].hash().clone(); for i in 1..101 { let next_block = test_data::block_builder() .transaction() .coinbase() - .version(i) + .founder_reward(&consensus, i) + .version(i as i32) .output().value(5000000000).build() .build() .merkled_header() @@ -394,7 +397,10 @@ pub mod tests { let storage: StorageRef = Arc::new(BlockChainDatabase::init_test_chain(blocks)); let verifier = Arc::new(ChainVerifier::new(storage.clone(), ConsensusParams::new(Network::Unitest))); let bad_transaction_block: IndexedBlock = test_data::block_builder() - .transaction().coinbase().output().value(50).build().build() + .transaction().coinbase() + .founder_reward(&consensus, 101) + .output().value(50).build() + .build() .transaction() .input().hash(coinbase_transaction_hash).build() .output().value(1000).build() @@ -414,12 +420,11 @@ pub mod tests { assert_eq!(wrapper.verify_block(&bad_transaction_block), Ok(())); // Error when tx script is checked - /* TODO: fixme let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters { verification_level: VerificationLevel::Full, verification_edge: 1.into(), }); - assert_eq!(wrapper.verify_block(&bad_transaction_block), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation))));*/ + assert_eq!(wrapper.verify_block(&bad_transaction_block), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation)))); } #[test] diff --git a/test-data/Cargo.toml b/test-data/Cargo.toml index 7df8b60c..f47f9eb7 100644 --- a/test-data/Cargo.toml +++ b/test-data/Cargo.toml @@ -6,7 +6,8 @@ authors = ["Nikolay Volf "] [dependencies] time = "0.1" -chain = { path = "../chain" } +chain = { path = "../chain" } +network = { path = "../network" } primitives = { path = "../primitives" } serialization = { path = "../serialization" } script = { path = "../script" } diff --git a/test-data/src/block.rs b/test-data/src/block.rs index 3e935c8c..6b44f3da 100644 --- a/test-data/src/block.rs +++ b/test-data/src/block.rs @@ -6,6 +6,7 @@ use primitives::bytes::Bytes; use primitives::compact::Compact; use ser::{Serializable, serialized_list_size}; use chain; +use network::ConsensusParams; use script::{Builder as ScriptBuilder, Opcode}; use invoke::{Invoke, Identity}; use super::genesis; @@ -331,6 +332,10 @@ impl TransactionBuilder where F: Invoke { 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 { TransactionOutputBuilder::with_callback(self) } @@ -470,6 +475,12 @@ impl TransactionOutputBuilder where F: Invoke { } } + pub fn founder_reward(mut self, consensus: &ConsensusParams, height: u32) -> Self { + self.script_pubkey = ScriptBuilder::build_p2sh(&consensus.founder_address(height).unwrap().hash).into(); + self.value = consensus.founder_reward(height); + self + } + pub fn value(mut self, value: u64) -> Self { self.value = value; self diff --git a/test-data/src/lib.rs b/test-data/src/lib.rs index b7154e78..51916d2b 100644 --- a/test-data/src/lib.rs +++ b/test-data/src/lib.rs @@ -3,6 +3,7 @@ extern crate time; extern crate chain; +extern crate network; extern crate primitives; extern crate serialization as ser; extern crate script; diff --git a/verification/Cargo.toml b/verification/Cargo.toml index 5adb1b7c..233e0ac7 100644 --- a/verification/Cargo.toml +++ b/verification/Cargo.toml @@ -10,6 +10,7 @@ rayon = "1.0" parking_lot = "0.4" byteorder = "1.2" lazy_static = "1.2.0" +keys = { path = "../keys" } primitives = { path = "../primitives" } chain = { path = "../chain" } serialization = { path = "../serialization" } diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 3d7630a6..1fc73f05 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -1,8 +1,8 @@ +use keys::Address; use network::{ConsensusParams}; use storage::{TransactionOutputProvider, BlockHeaderProvider}; -use script; +use script::{self, Builder}; use sigops::{transaction_sigops}; -use work::block_reward_satoshi; use duplex_store::DuplexTransactionOutputProvider; use deployments::BlockDeployments; use canon::CanonBlock; @@ -14,7 +14,8 @@ pub struct BlockAcceptor<'a> { pub finality: BlockFinality<'a>, pub serialized_size: BlockSerializedSize<'a>, pub sigops: BlockSigops<'a>, - pub coinbase_claim: BlockCoinbaseClaim<'a>, + pub miner_reward: BlockCoinbaseMinerReward<'a>, + pub founder_reward: BlockFounderReward<'a>, pub coinbase_script: BlockCoinbaseScript<'a>, } @@ -31,7 +32,8 @@ impl<'a> BlockAcceptor<'a> { finality: BlockFinality::new(block, height, deployments, headers), serialized_size: BlockSerializedSize::new(block, consensus), coinbase_script: BlockCoinbaseScript::new(block, consensus, height), - coinbase_claim: BlockCoinbaseClaim::new(block, store, height), + miner_reward: BlockCoinbaseMinerReward::new(block, store, consensus, height), + founder_reward: BlockFounderReward::new(block, consensus, height), sigops: BlockSigops::new(block, store, consensus), } } @@ -40,7 +42,8 @@ impl<'a> BlockAcceptor<'a> { self.finality.check()?; self.sigops.check()?; self.serialized_size.check()?; - self.coinbase_claim.check()?; + self.miner_reward.check()?; + self.founder_reward.check()?; self.coinbase_script.check()?; Ok(()) } @@ -144,22 +147,23 @@ impl<'a> BlockSigops<'a> { } } -pub struct BlockCoinbaseClaim<'a> { +pub struct BlockCoinbaseMinerReward<'a> { block: CanonBlock<'a>, store: &'a TransactionOutputProvider, - height: u32, + max_reward: u64, } -impl<'a> BlockCoinbaseClaim<'a> { +impl<'a> BlockCoinbaseMinerReward<'a> { fn new( block: CanonBlock<'a>, store: &'a TransactionOutputProvider, + consensus: &ConsensusParams, height: u32, ) -> Self { - BlockCoinbaseClaim { + BlockCoinbaseMinerReward { block: block, store: store, - height: height, + max_reward: consensus.miner_reward(height), } } @@ -210,13 +214,13 @@ impl<'a> BlockCoinbaseClaim<'a> { let claim = self.block.transactions[0].raw.total_spends(); - let (reward, overflow) = fees.overflowing_add(block_reward_satoshi(self.height)); + let (max_reward, overflow) = fees.overflowing_add(self.max_reward); if overflow { return Err(Error::TransactionFeeAndRewardOverflow); } - if claim > reward { - Err(Error::CoinbaseOverspend { expected_max: reward, actual: claim }) + if claim > max_reward { + Err(Error::CoinbaseOverspend { expected_max: max_reward, actual: claim }) } else { Ok(()) } @@ -260,6 +264,39 @@ impl<'a> BlockCoinbaseScript<'a> { } } +pub struct BlockFounderReward<'a> { + block: CanonBlock<'a>, + founder_address: Option
, + founder_reward: u64, +} + +impl<'a> BlockFounderReward<'a> { + fn new(block: CanonBlock<'a>, consensus_params: &ConsensusParams, height: u32) -> Self { + BlockFounderReward { + block: block, + founder_address: consensus_params.founder_address(height), + founder_reward: consensus_params.founder_reward(height), + } + } + + fn check(&self) -> Result<(), Error> { + if let Some(ref founder_address) = self.founder_address { + let script = Builder::build_p2sh(&founder_address.hash); + let has_founder_reward = self.block.transactions.first() + .map(|tx| tx.raw.outputs.iter().any(|output| + **output.script_pubkey == *script && + output.value == self.founder_reward)) + .unwrap_or(false); + + if !has_founder_reward { + return Err(Error::MissingFoundersReward); + } + } + + Ok(()) + } +} + #[cfg(test)] mod tests { extern crate test_data; diff --git a/verification/src/chain_verifier.rs b/verification/src/chain_verifier.rs index ac33f3e2..da224f09 100644 --- a/verification/src/chain_verifier.rs +++ b/verification/src/chain_verifier.rs @@ -185,6 +185,7 @@ mod tests { #[test] fn coinbase_maturity() { + let consensus = ConsensusParams::new(Network::Unitest); let genesis = test_data::block_builder() .transaction() .coinbase() @@ -199,6 +200,7 @@ mod tests { let block = test_data::block_builder() .transaction() .coinbase() + .founder_reward(&consensus, 1) .output().value(1).build() .build() .transaction() @@ -208,7 +210,7 @@ mod tests { .merkled_header().parent(genesis.hash()).build() .build(); - let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest)); + let verifier = ChainVerifier::new(Arc::new(storage), consensus); let expected = Err(Error::Transaction( 1, @@ -220,6 +222,8 @@ mod tests { #[test] fn non_coinbase_happy() { + let consensus = ConsensusParams::new(Network::Unitest); + let genesis = test_data::block_builder() .transaction() .coinbase() @@ -237,6 +241,7 @@ mod tests { let block = test_data::block_builder() .transaction() .coinbase() + .founder_reward(&consensus, 1) .output().value(2).build() .build() .transaction() @@ -246,12 +251,14 @@ mod tests { .merkled_header().parent(genesis.hash()).build() .build(); - let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest)); + let verifier = ChainVerifier::new(Arc::new(storage), consensus); assert_eq!(verifier.verify(VerificationLevel::Full, &block.into()), Ok(())); } #[test] fn transaction_references_same_block_happy() { + let consensus = ConsensusParams::new(Network::Unitest); + let genesis = test_data::block_builder() .transaction() .coinbase() @@ -269,6 +276,7 @@ mod tests { let block = test_data::block_builder() .transaction() .coinbase() + .founder_reward(&consensus, 1) .output().value(2).build() .build() .transaction() @@ -282,7 +290,7 @@ mod tests { .merkled_header().parent(genesis.hash()).build() .build(); - let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest)); + let verifier = ChainVerifier::new(Arc::new(storage), consensus); assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok()); } @@ -426,7 +434,7 @@ mod tests { let block: IndexedBlock = test_data::block_builder() .transaction() .coinbase() - .output().value(5000000001).build() + .output().value(1250000001).build() .build() .merkled_header().parent(genesis.hash()).build() .build() @@ -435,8 +443,8 @@ mod tests { let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest)); let expected = Err(Error::CoinbaseOverspend { - expected_max: 5000000000, - actual: 5000000001 + expected_max: 1250000000, + actual: 1250000001, }); assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into())); diff --git a/verification/src/error.rs b/verification/src/error.rs index 77046e41..326f5d37 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -59,6 +59,8 @@ pub enum Error { InvalidEquihashSolution, /// Invalid block version InvalidVersion, + /// Block' coinbase is missing founders reward output. + MissingFoundersReward, } impl From for Error { diff --git a/verification/src/lib.rs b/verification/src/lib.rs index b65dd0d6..b19cc9b8 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -65,6 +65,7 @@ extern crate rustc_hex as hex; extern crate storage; extern crate chain; +extern crate keys; extern crate network; extern crate primitives; extern crate serialization as ser; @@ -121,7 +122,7 @@ pub use chain_verifier::BackwardsCompatibleChainVerifier; pub use error::{Error, TransactionError}; pub use sigops::transaction_sigops; pub use timestamp::{median_timestamp, median_timestamp_inclusive}; -pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash, block_reward_satoshi}; +pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash}; pub use deployments::Deployments; #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/verification/src/work.rs b/verification/src/work.rs index 9df5770c..ba014865 100644 --- a/verification/src/work.rs +++ b/verification/src/work.rs @@ -48,26 +48,3 @@ pub fn work_required(parent_hash: H256, height: u32, store: &BlockHeaderProvider raw: parent_header }, store, consensus, max_bits) } - -pub fn block_reward_satoshi(block_height: u32) -> u64 { - let mut res = 50 * 100 * 1000 * 1000; - for _ in 0..block_height / 210000 { res /= 2 } - res -} - -#[cfg(test)] -mod tests { - use super::{block_reward_satoshi}; - - #[test] - fn reward() { - assert_eq!(block_reward_satoshi(0), 5000000000); - assert_eq!(block_reward_satoshi(209999), 5000000000); - assert_eq!(block_reward_satoshi(210000), 2500000000); - assert_eq!(block_reward_satoshi(420000), 1250000000); - assert_eq!(block_reward_satoshi(420001), 1250000000); - assert_eq!(block_reward_satoshi(629999), 1250000000); - assert_eq!(block_reward_satoshi(630000), 625000000); - assert_eq!(block_reward_satoshi(630001), 625000000); - } -}