Merge pull request #24 from paritytech/block_founders_reward

Block founders reward
This commit is contained in:
Svyatoslav Nikolsky 2018-12-25 13:13:04 +03:00 committed by GitHub
commit 8231949588
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 366 additions and 127 deletions

3
Cargo.lock generated
View File

@ -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",

View File

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

View File

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

View File

@ -5,7 +5,7 @@ use chain::{OutPoint, TransactionOutput, IndexedTransaction};
use storage::{SharedStore, TransactionOutputProvider};
use network::ConsensusParams;
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
use verification::{work_required, block_reward_satoshi, transaction_sigops};
use verification::{work_required, transaction_sigops};
const BLOCK_VERSION: u32 = 0x20000000;
const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4;
@ -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,
}

View File

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

View File

@ -1,3 +1,4 @@
use keys::Address;
use {Network, Magic, Deployment, crypto};
lazy_static! {
@ -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<Address>,
/// 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<Address> {
let last_founder_reward_block_height = self.subsidy_halving_interval + self.subsidy_slow_start_interval / 2 - 1;
if height == 0 || height > last_founder_reward_block_height {
return None;
}
let founders_len = self.founders_addresses.len() as u32;
let address_change_interval = (last_founder_reward_block_height + founders_len) / founders_len;
let address_index = height / address_change_interval;
Some(self.founders_addresses[address_index as usize].clone())
}
pub fn consensus_branch_id(&self, height: u32) -> u32 {
// sapling upgrade
if height >= self.sapling_height {
@ -300,3 +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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -264,7 +264,8 @@ pub mod tests {
use std::collections::{HashSet, HashMap};
use db::BlockChainDatabase;
use network::{Network, ConsensusParams};
use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError};
use verification::{VerificationLevel, BackwardsCompatibleChainVerifier as ChainVerifier, Error as VerificationError, TransactionError};
use script::Error as ScriptError;
use synchronization_client_core::CoreVerificationSink;
use synchronization_executor::tests::DummyTaskExecutor;
use primitives::hash::H256;
@ -371,13 +372,15 @@ pub mod tests {
#[test]
fn verification_level_header_accept_incorrect_transaction() {
let consensus = ConsensusParams::new(Network::Unitest);
let mut blocks: Vec<IndexedBlock> = vec![test_data::genesis().into()];
let mut rolling_hash = blocks[0].hash().clone();
for i in 1..101 {
let next_block = test_data::block_builder()
.transaction()
.coinbase()
.version(i)
.founder_reward(&consensus, i)
.version(i as i32)
.output().value(5000000000).build()
.build()
.merkled_header()
@ -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]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -185,6 +185,7 @@ mod tests {
#[test]
fn coinbase_maturity() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -199,6 +200,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(1).build()
.build()
.transaction()
@ -208,7 +210,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
let expected = Err(Error::Transaction(
1,
@ -220,6 +222,8 @@ mod tests {
#[test]
fn non_coinbase_happy() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -237,6 +241,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(2).build()
.build()
.transaction()
@ -246,12 +251,14 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert_eq!(verifier.verify(VerificationLevel::Full, &block.into()), Ok(()));
}
#[test]
fn transaction_references_same_block_happy() {
let consensus = ConsensusParams::new(Network::Unitest);
let genesis = test_data::block_builder()
.transaction()
.coinbase()
@ -269,6 +276,7 @@ mod tests {
let block = test_data::block_builder()
.transaction()
.coinbase()
.founder_reward(&consensus, 1)
.output().value(2).build()
.build()
.transaction()
@ -282,7 +290,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build()
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok());
}
@ -426,7 +434,7 @@ mod tests {
let block: IndexedBlock = test_data::block_builder()
.transaction()
.coinbase()
.output().value(5000000001).build()
.output().value(1250000001).build()
.build()
.merkled_header().parent(genesis.hash()).build()
.build()
@ -435,8 +443,8 @@ mod tests {
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let expected = Err(Error::CoinbaseOverspend {
expected_max: 5000000000,
actual: 5000000001
expected_max: 1250000000,
actual: 1250000001,
});
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));

View File

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

View File

@ -65,6 +65,7 @@ extern crate rustc_hex as hex;
extern crate storage;
extern crate chain;
extern crate keys;
extern crate network;
extern crate primitives;
extern crate serialization as ser;
@ -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)]

View File

@ -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);
}
}