From f45505f51de782c51e83efd26f20ca12331b1338 Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 30 Aug 2023 09:13:31 +0900 Subject: [PATCH] feat: notify dummy miner for new work --- docs/hashes.toml | 2 +- resource/specs/dev.toml | 1 + spec/src/consensus.rs | 25 +++ spec/src/lib.rs | 23 ++- spec/src/tests/mod.rs | 31 ++++ tx-pool/src/chunk_process.rs | 2 +- tx-pool/src/process.rs | 2 +- tx-pool/src/util.rs | 4 +- .../src/contextual_block_verifier.rs | 2 +- .../src/tests/transaction_verifier.rs | 159 ++++++++++++++++-- verification/src/transaction_verifier.rs | 31 +++- 11 files changed, 250 insertions(+), 32 deletions(-) diff --git a/docs/hashes.toml b/docs/hashes.toml index 73c4e670b2..15808ef248 100644 --- a/docs/hashes.toml +++ b/docs/hashes.toml @@ -127,7 +127,7 @@ tx_hash = "0xd5780747735fd22c9ba7363bde8afe59061658caa836962867253b03cbda264e" index = 1 [ckb_dev] -spec_hash = "0x6cb679a15a7ff16596cad85f4680ab4335c87bcbf43956c591c23d11841719e4" +spec_hash = "0xab85c78cf9641a7709f9d48ba06415c283050888c9fb927d743bc521aa1ddfec" genesis = "0x823b2ff5785b12da8b1363cac9a5cbe566d8b715a4311441b119c39a0367488c" cellbase = "0xa563884b3686078ec7e7677a5f86449b15cf2693f3c1241766c6996f206cc541" diff --git a/resource/specs/dev.toml b/resource/specs/dev.toml index 8ae8149df2..da6f72eff3 100644 --- a/resource/specs/dev.toml +++ b/resource/specs/dev.toml @@ -94,6 +94,7 @@ genesis_epoch_length = 1000 # For development and testing purposes only. # Keep difficulty be permanent if the pow is Dummy. (default: false) permanent_difficulty_in_dummy = true +starting_block_limiting_dao_withdrawing_lock = 0 [params.hardfork] ckb2023 = 0 diff --git a/spec/src/consensus.rs b/spec/src/consensus.rs index 776447aef0..461225a3d9 100644 --- a/spec/src/consensus.rs +++ b/spec/src/consensus.rs @@ -95,6 +95,10 @@ pub(crate) const SATOSHI_CELL_OCCUPIED_RATIO: Ratio = Ratio::new(6, 10); pub(crate) const LC_MAINNET_ACTIVATION_THRESHOLD: Ratio = Ratio::new(8, 10); pub(crate) const TESTNET_ACTIVATION_THRESHOLD: Ratio = Ratio::new(3, 4); +/// The starting block number from which the lock script size of a DAO withdrawing +/// cell shall be limited +pub(crate) const STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK: u64 = 10_000_000; + /// The struct represent CKB two-step-transaction-confirmation params /// /// [two-step-transaction-confirmation params](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0020-ckb-consensus-protocol/0020-ckb-consensus-protocol.md#two-step-transaction-confirmation) @@ -289,6 +293,8 @@ impl ConsensusBuilder { hardfork_switch: HardForks::new_mirana(), deployments: HashMap::new(), versionbits_caches: VersionbitsCache::default(), + starting_block_limiting_dao_withdrawing_lock: + STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK, }, } } @@ -483,6 +489,17 @@ impl ConsensusBuilder { self.inner.deployments = deployments; self } + + ///The starting block number where Nervos DAO withdrawing cell's lock is + /// size limited. + pub fn starting_block_limiting_dao_withdrawing_lock( + mut self, + starting_block_limiting_dao_withdrawing_lock: u64, + ) -> Self { + self.inner.starting_block_limiting_dao_withdrawing_lock = + starting_block_limiting_dao_withdrawing_lock; + self + } } /// Struct Consensus defines various parameters that influence chain consensus @@ -563,6 +580,8 @@ pub struct Consensus { pub deployments: HashMap, /// Soft fork state cache pub versionbits_caches: VersionbitsCache, + /// Starting block where DAO withdrawing lock is limited in size + pub starting_block_limiting_dao_withdrawing_lock: u64, } // genesis difficulty should not be zero @@ -741,6 +760,12 @@ impl Consensus { self.tx_proposal_window } + /// The starting block number where Nervos DAO withdrawing cell's lock is + /// size limited. + pub fn starting_block_limiting_dao_withdrawing_lock(&self) -> u64 { + self.starting_block_limiting_dao_withdrawing_lock + } + // Apply the dampening filter on hash_rate estimation calculate fn bounding_hash_rate( &self, diff --git a/spec/src/lib.rs b/spec/src/lib.rs index c08eb123e3..627e66c047 100644 --- a/spec/src/lib.rs +++ b/spec/src/lib.rs @@ -90,7 +90,7 @@ pub mod default_params { CELLBASE_MATURITY, DEFAULT_EPOCH_DURATION_TARGET, DEFAULT_ORPHAN_RATE_TARGET, DEFAULT_PRIMARY_EPOCH_REWARD_HALVING_INTERVAL, DEFAULT_SECONDARY_EPOCH_REWARD, GENESIS_EPOCH_LENGTH, INITIAL_PRIMARY_EPOCH_REWARD, MAX_BLOCK_BYTES, MAX_BLOCK_CYCLES, - MAX_BLOCK_PROPOSALS_LIMIT, + MAX_BLOCK_PROPOSALS_LIMIT, STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK, }; use ckb_types::core::{Capacity, Cycle, EpochNumber}; @@ -170,6 +170,13 @@ pub mod default_params { pub fn orphan_rate_target() -> (u32, u32) { DEFAULT_ORPHAN_RATE_TARGET } + + /// The default starting_block_limiting_dao_withdrawing_lock + /// + /// Apply to [`starting_block_limiting_dao_withdrawing_lock`](../consensus/struct.Consensus.html#structfield.starting_block_limiting_dao_withdrawing_lock) + pub fn starting_block_limiting_dao_withdrawing_lock() -> u64 { + STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK + } } /// Parameters for CKB block chain @@ -231,6 +238,11 @@ pub struct Params { /// See [`orphan_rate_target`](consensus/struct.Consensus.html#structfield.orphan_rate_target) #[serde(skip_serializing_if = "Option::is_none")] pub orphan_rate_target: Option<(u32, u32)>, + /// The starting_block_limiting_dao_withdrawing_lock. + /// + /// See [`starting_block_limiting_dao_withdrawing_lock`](consensus/struct.Consensus.html#structfield.starting_block_limiting_dao_withdrawing_lock) + #[serde(skip_serializing_if = "Option::is_none")] + pub starting_block_limiting_dao_withdrawing_lock: Option, /// The parameters for hard fork features. /// /// See [`hardfork_switch`](consensus/struct.Consensus.html#structfield.hardfork_switch) @@ -304,6 +316,12 @@ impl Params { self.orphan_rate_target .unwrap_or_else(default_params::orphan_rate_target) } + + /// Return the `starting_block_limiting_dao_withdrawing_lock`, otherwise if None, returns the default value + pub fn starting_block_limiting_dao_withdrawing_lock(&self) -> u64 { + self.starting_block_limiting_dao_withdrawing_lock + .unwrap_or_else(default_params::starting_block_limiting_dao_withdrawing_lock) + } } /// The genesis information @@ -585,6 +603,9 @@ impl ChainSpec { .permanent_difficulty_in_dummy(self.params.permanent_difficulty_in_dummy()) .max_block_proposals_limit(self.params.max_block_proposals_limit()) .orphan_rate_target(self.params.orphan_rate_target()) + .starting_block_limiting_dao_withdrawing_lock( + self.params.starting_block_limiting_dao_withdrawing_lock(), + ) .hardfork_switch(hardfork_switch); if let Some(deployments) = self.softfork_deployments() { diff --git a/spec/src/tests/mod.rs b/spec/src/tests/mod.rs index 07bfd3362f..613381293b 100644 --- a/spec/src/tests/mod.rs +++ b/spec/src/tests/mod.rs @@ -213,6 +213,18 @@ fn test_default_params() { }; assert_eq!(params, expected); + + let test_params: &str = r#" + starting_block_limiting_dao_withdrawing_lock = 77 + "#; + + let params: Params = toml::from_str(test_params).unwrap(); + let expected = Params { + starting_block_limiting_dao_withdrawing_lock: Some(77), + ..Default::default() + }; + + assert_eq!(params, expected); } #[test] @@ -256,3 +268,22 @@ fn test_default_genesis_epoch_ext() { assert_eq!(genesis_epoch_ext, expected); } + +#[test] +fn test_devnet_limits_dao_withdrawing_lock_from_genesis() { + let chain_spec = load_spec_by_name("ckb_dev"); + let consensus = chain_spec.build_consensus().unwrap(); + + assert_eq!(consensus.starting_block_limiting_dao_withdrawing_lock(), 0); +} + +#[test] +fn test_mainnet_limits_dao_withdrawing_lock_from_10000000() { + let chain_spec = load_spec_by_name("ckb"); + let consensus = chain_spec.build_consensus().unwrap(); + + assert_eq!( + consensus.starting_block_limiting_dao_withdrawing_lock(), + 10000000 + ); +} diff --git a/tx-pool/src/chunk_process.rs b/tx-pool/src/chunk_process.rs index 34c38caf2b..1cae5abbe8 100644 --- a/tx-pool/src/chunk_process.rs +++ b/tx-pool/src/chunk_process.rs @@ -273,7 +273,7 @@ impl ChunkProcess { .and_then(|result| { DaoScriptSizeVerifier::new( Arc::clone(&rtx), - consensus.dao_type_hash(), + Arc::clone(&consensus), data_loader.clone(), ) .verify()?; diff --git a/tx-pool/src/process.rs b/tx-pool/src/process.rs index 5880ae128e..d05498e99f 100644 --- a/tx-pool/src/process.rs +++ b/tx-pool/src/process.rs @@ -656,7 +656,7 @@ impl TxPoolService { ScriptVerifyResult::Completed(cycles) => { if let Err(e) = DaoScriptSizeVerifier::new( Arc::clone(&rtx), - self.consensus.dao_type_hash(), + Arc::clone(&self.consensus), snapshot.as_data_loader(), ) .verify() diff --git a/tx-pool/src/util.rs b/tx-pool/src/util.rs index 28f141e2f1..ea8757e997 100644 --- a/tx-pool/src/util.rs +++ b/tx-pool/src/util.rs @@ -106,7 +106,7 @@ pub(crate) fn verify_rtx( .and_then(|result| { DaoScriptSizeVerifier::new( rtx, - snapshot.cloned_consensus().dao_type_hash(), + snapshot.cloned_consensus(), snapshot.as_data_loader(), ) .verify()?; @@ -122,7 +122,7 @@ pub(crate) fn verify_rtx( .and_then(|result| { DaoScriptSizeVerifier::new( rtx, - snapshot.cloned_consensus().dao_type_hash(), + snapshot.cloned_consensus(), snapshot.as_data_loader(), ) .verify()?; diff --git a/verification/contextual/src/contextual_block_verifier.rs b/verification/contextual/src/contextual_block_verifier.rs index a3ed5c002c..18e848e69e 100644 --- a/verification/contextual/src/contextual_block_verifier.rs +++ b/verification/contextual/src/contextual_block_verifier.rs @@ -472,7 +472,7 @@ impl<'a, 'b, CS: ChainStore + VersionbitsIndexer + 'static> BlockTxsVerifier<'a, if self.context.versionbits_active(DeploymentPos::LightClient, self.parent) { DaoScriptSizeVerifier::new( Arc::clone(tx), - self.context.consensus.dao_type_hash(), + Arc::clone(&self.context.consensus), self.context.store.as_data_loader(), ).verify()?; } diff --git a/verification/src/tests/transaction_verifier.rs b/verification/src/tests/transaction_verifier.rs index 97813a9e30..4dd123e90b 100644 --- a/verification/src/tests/transaction_verifier.rs +++ b/verification/src/tests/transaction_verifier.rs @@ -4,7 +4,11 @@ use super::super::transaction_verifier::{ }; use crate::error::TransactionErrorSource; use crate::{TransactionError, TxVerifyEnv}; -use ckb_chain_spec::{build_genesis_type_id_script, consensus::ConsensusBuilder, OUTPUT_INDEX_DAO}; +use ckb_chain_spec::{ + build_genesis_type_id_script, + consensus::{Consensus, ConsensusBuilder}, + OUTPUT_INDEX_DAO, +}; use ckb_error::{assert_error_eq, Error}; use ckb_test_chain_utils::{MockMedianTime, MOCK_MEDIAN_TIME_COUNT}; use ckb_traits::CellDataProvider; @@ -15,7 +19,7 @@ use ckb_types::{ capacity_bytes, cell::{CellMetaBuilder, ResolvedTransaction}, hardfork::HardForks, - BlockNumber, Capacity, EpochNumber, EpochNumberWithFraction, HeaderView, + BlockNumber, Capacity, EpochNumber, EpochNumberWithFraction, HeaderView, ScriptHashType, TransactionBuilder, TransactionInfo, TransactionView, }, h256, @@ -793,9 +797,31 @@ impl CellDataProvider for EmptyDataProvider { } } +fn build_consensus_with_dao_limiting_block(block_number: u64) -> (Arc, Script) { + let dao_script = build_genesis_type_id_script(OUTPUT_INDEX_DAO); + let mut consensus = ConsensusBuilder::default() + .starting_block_limiting_dao_withdrawing_lock(block_number) + .build(); + + // Default consensus built this way only has one dummy output in the + // cellbase transaction from genesis block, meaning it will be missing + // the dao script. For simplicity, we are hacking consensus here with + // a dao_type_hash value, a proper way should be creating a proper genesis + // block here, but we will leave it till we really need it. + consensus.dao_type_hash = Some(dao_script.calc_script_hash()); + + let dao_type_script = Script::new_builder() + .code_hash(dao_script.calc_script_hash()) + .hash_type(ScriptHashType::Type.into()) + .build(); + + (Arc::new(consensus), dao_type_script) +} + #[test] fn test_dao_disables_different_lock_script_size() { - let dao_type_script = build_genesis_type_id_script(OUTPUT_INDEX_DAO); + let (consensus, dao_type_script) = build_consensus_with_dao_limiting_block(20000); + let transaction = TransactionBuilder::default() .outputs(vec![ CellOutput::new_builder() @@ -824,21 +850,30 @@ fn test_dao_disables_different_lock_script_size() { .build(), Bytes::new(), ) + .transaction_info(mock_transaction_info( + 20010, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), CellMetaBuilder::from_cell_output( CellOutput::new_builder() .capacity(capacity_bytes!(201).pack()) .lock(Script::new_builder().args(Bytes::new().pack()).build()) - .type_(Some(dao_type_script.clone()).pack()) + .type_(Some(dao_type_script).pack()) .build(), Bytes::from(vec![0; 8]), ) + .transaction_info(mock_transaction_info( + 20011, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), ], resolved_dep_groups: vec![], }); - let verifier = - DaoScriptSizeVerifier::new(rtx, Some(dao_type_script.code_hash()), EmptyDataProvider {}); + let verifier = DaoScriptSizeVerifier::new(rtx, consensus, EmptyDataProvider {}); assert_error_eq!( verifier.verify().unwrap_err(), @@ -846,9 +881,70 @@ fn test_dao_disables_different_lock_script_size() { ); } +#[test] +fn test_dao_disables_different_lock_script_size_before_limiting_block() { + let (consensus, dao_type_script) = build_consensus_with_dao_limiting_block(21000); + + let transaction = TransactionBuilder::default() + .outputs(vec![ + CellOutput::new_builder() + .capacity(capacity_bytes!(50).pack()) + .build(), + CellOutput::new_builder() + .capacity(capacity_bytes!(200).pack()) + .lock( + Script::new_builder() + .args(Bytes::from(vec![1; 20]).pack()) + .build(), + ) + .type_(Some(dao_type_script.clone()).pack()) + .build(), + ]) + .outputs_data(vec![Bytes::new().pack(); 2]) + .build(); + + let rtx = Arc::new(ResolvedTransaction { + transaction, + resolved_cell_deps: Vec::new(), + resolved_inputs: vec![ + CellMetaBuilder::from_cell_output( + CellOutput::new_builder() + .capacity(capacity_bytes!(50).pack()) + .build(), + Bytes::new(), + ) + .transaction_info(mock_transaction_info( + 20010, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) + .build(), + CellMetaBuilder::from_cell_output( + CellOutput::new_builder() + .capacity(capacity_bytes!(201).pack()) + .lock(Script::new_builder().args(Bytes::new().pack()).build()) + .type_(Some(dao_type_script).pack()) + .build(), + Bytes::from(vec![0; 8]), + ) + .transaction_info(mock_transaction_info( + 20011, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) + .build(), + ], + resolved_dep_groups: vec![], + }); + let verifier = DaoScriptSizeVerifier::new(rtx, consensus, EmptyDataProvider {}); + + assert!(verifier.verify().is_ok()); +} + #[test] fn test_non_dao_allows_lock_script_size() { - let dao_type_script = build_genesis_type_id_script(OUTPUT_INDEX_DAO); + let (consensus, _dao_type_script) = build_consensus_with_dao_limiting_block(20000); + let transaction = TransactionBuilder::default() .outputs(vec![ CellOutput::new_builder() @@ -876,6 +972,11 @@ fn test_non_dao_allows_lock_script_size() { .build(), Bytes::new(), ) + .transaction_info(mock_transaction_info( + 20010, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), CellMetaBuilder::from_cell_output( CellOutput::new_builder() @@ -884,19 +985,24 @@ fn test_non_dao_allows_lock_script_size() { .build(), Bytes::from(vec![0; 8]), ) + .transaction_info(mock_transaction_info( + 20011, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), ], resolved_dep_groups: vec![], }); - let verifier = - DaoScriptSizeVerifier::new(rtx, Some(dao_type_script.code_hash()), EmptyDataProvider {}); + let verifier = DaoScriptSizeVerifier::new(rtx, consensus, EmptyDataProvider {}); assert!(verifier.verify().is_ok()); } #[test] fn test_dao_allows_different_lock_script_size_in_withdraw_phase_2() { - let dao_type_script = build_genesis_type_id_script(OUTPUT_INDEX_DAO); + let (consensus, dao_type_script) = build_consensus_with_dao_limiting_block(20000); + let transaction = TransactionBuilder::default() .outputs(vec![ CellOutput::new_builder() @@ -925,28 +1031,38 @@ fn test_dao_allows_different_lock_script_size_in_withdraw_phase_2() { .build(), Bytes::new(), ) + .transaction_info(mock_transaction_info( + 20010, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), CellMetaBuilder::from_cell_output( CellOutput::new_builder() .capacity(capacity_bytes!(201).pack()) .lock(Script::new_builder().args(Bytes::new().pack()).build()) - .type_(Some(dao_type_script.clone()).pack()) + .type_(Some(dao_type_script).pack()) .build(), Bytes::from(vec![1; 8]), ) + .transaction_info(mock_transaction_info( + 20011, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), ], resolved_dep_groups: vec![], }); - let verifier = - DaoScriptSizeVerifier::new(rtx, Some(dao_type_script.code_hash()), EmptyDataProvider {}); + let verifier = DaoScriptSizeVerifier::new(rtx, consensus, EmptyDataProvider {}); assert!(verifier.verify().is_ok()); } #[test] fn test_dao_allows_different_lock_script_size_using_normal_cells_in_withdraw_phase_2() { - let dao_type_script = build_genesis_type_id_script(OUTPUT_INDEX_DAO); + let (consensus, dao_type_script) = build_consensus_with_dao_limiting_block(20000); + let transaction = TransactionBuilder::default() .outputs(vec![ CellOutput::new_builder() @@ -959,7 +1075,7 @@ fn test_dao_allows_different_lock_script_size_using_normal_cells_in_withdraw_pha .args(Bytes::from(vec![1; 20]).pack()) .build(), ) - .type_(Some(dao_type_script.clone()).pack()) + .type_(Some(dao_type_script).pack()) .build(), ]) .outputs_data(vec![]) @@ -975,6 +1091,11 @@ fn test_dao_allows_different_lock_script_size_using_normal_cells_in_withdraw_pha .build(), Bytes::new(), ) + .transaction_info(mock_transaction_info( + 20010, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), CellMetaBuilder::from_cell_output( CellOutput::new_builder() @@ -983,12 +1104,16 @@ fn test_dao_allows_different_lock_script_size_using_normal_cells_in_withdraw_pha .build(), Bytes::from(vec![1; 8]), ) + .transaction_info(mock_transaction_info( + 20011, + EpochNumberWithFraction::new(10, 0, 10), + 0, + )) .build(), ], resolved_dep_groups: vec![], }); - let verifier = - DaoScriptSizeVerifier::new(rtx, Some(dao_type_script.code_hash()), EmptyDataProvider {}); + let verifier = DaoScriptSizeVerifier::new(rtx, consensus, EmptyDataProvider {}); assert!(verifier.verify().is_ok()); } diff --git a/verification/src/transaction_verifier.rs b/verification/src/transaction_verifier.rs index 22468e862a..11214f583f 100644 --- a/verification/src/transaction_verifier.rs +++ b/verification/src/transaction_verifier.rs @@ -972,8 +972,7 @@ where /// It provides a temporary solution till Nervos DAO script can be properly upgraded. pub struct DaoScriptSizeVerifier
{ resolved_transaction: Arc, - // It's Option because genesis block might not always have dao system cell - dao_type_hash: Option, + consensus: Arc, data_loader: DL, } @@ -981,23 +980,27 @@ impl DaoScriptSizeVerifier
{ /// Create a new `DaoScriptSizeVerifier` pub fn new( resolved_transaction: Arc, - dao_type_hash: Option, + consensus: Arc, data_loader: DL, ) -> Self { DaoScriptSizeVerifier { resolved_transaction, - dao_type_hash, + consensus, data_loader, } } + fn dao_type_hash(&self) -> Option { + self.consensus.dao_type_hash() + } + /// Verifies that for all Nervos DAO transactions, withdrawing cells must use lock scripts /// of the same size as corresponding deposit cells pub fn verify(&self) -> Result<(), Error> { - if self.dao_type_hash.is_none() { + if self.dao_type_hash().is_none() { return Ok(()); } - let dao_type_hash = self.dao_type_hash.as_ref().unwrap(); + let dao_type_hash = self.dao_type_hash().unwrap(); for (i, (input_meta, cell_output)) in self .resolved_transaction .resolved_inputs @@ -1006,8 +1009,8 @@ impl DaoScriptSizeVerifier
{ .enumerate() { // Both the input and output cell must use Nervos DAO as type script - if !(cell_uses_dao_type_script(&input_meta.cell_output, dao_type_hash) - && cell_uses_dao_type_script(&cell_output, dao_type_hash)) + if !(cell_uses_dao_type_script(&input_meta.cell_output, &dao_type_hash) + && cell_uses_dao_type_script(&cell_output, &dao_type_hash)) { continue; } @@ -1023,6 +1026,18 @@ impl DaoScriptSizeVerifier
{ continue; } + // Only cells committed after the pre-defined block number in consensus is + // applied to this rule + if let Some(info) = &input_meta.transaction_info { + if info.block_number + < self + .consensus + .starting_block_limiting_dao_withdrawing_lock() + { + continue; + } + } + // Now we have a pair of DAO deposit and withdrawing cells, it is expected // they have the lock scripts of the same size. if input_meta.cell_output.lock().total_size() != cell_output.lock().total_size() {