Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ᚬrc/v0.14.0] fix: cellset consistency #1076

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ impl<CS: ChainStore + 'static> ChainService<CS> {
let mut new_best_block = false;
let mut total_difficulty = U256::zero();

let mut cell_set_diff = CellSetDiff::default();
let mut fork = ForkChanges::default();
let mut chain_state = self.shared.lock_chain_state();
let mut txs_verify_cache = self.shared.lock_txs_verify_cache();
Expand Down Expand Up @@ -317,14 +316,15 @@ impl<CS: ChainStore + 'static> ChainService<CS> {
fork.attached_blocks.iter(),
)?;
// MUST update index before reconcile_main_chain
cell_set_diff = self.reconcile_main_chain(
let cell_set_diff = self.reconcile_main_chain(
&mut batch,
&mut fork,
&mut chain_state,
&mut txs_verify_cache,
need_verify,
)?;
self.update_proposal_ids(&mut chain_state, &fork);
chain_state.update_cell_set(cell_set_diff, &mut batch)?;
batch.insert_tip_header(&block.header())?;
if new_epoch || fork.has_detached() {
batch.insert_current_epoch_ext(&epoch)?;
Expand Down Expand Up @@ -353,7 +353,7 @@ impl<CS: ChainStore + 'static> ChainService<CS> {
if new_epoch || fork.has_detached() {
chain_state.update_current_epoch_ext(epoch);
}
chain_state.update_tip(tip_header, total_difficulty, cell_set_diff)?;
chain_state.update_tip(tip_header, total_difficulty)?;
chain_state.update_tx_pool_for_reorg(
fork.detached_blocks().iter(),
fork.attached_blocks().iter(),
Expand Down
37 changes: 22 additions & 15 deletions chain/src/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::tests::util::{
create_transaction, create_transaction_with_out_point, gen_block, start_chain,
create_multi_outputs_transaction, create_transaction, create_transaction_with_out_point,
gen_block, start_chain,
};
use ckb_chain_spec::consensus::Consensus;
use ckb_core::block::Block;
Expand Down Expand Up @@ -94,15 +95,15 @@ fn test_transaction_spend_in_same_block() {

let last_cell_base = &chain.last().unwrap().transactions()[0];
let last_cell_base_hash = last_cell_base.hash().to_owned();
let tx1 = create_transaction(&last_cell_base_hash, 1);
let tx1 = create_multi_outputs_transaction(&last_cell_base, vec![0], 2, vec![1]);
let tx1_hash = tx1.hash().to_owned();
let tx2 = create_transaction(&tx1_hash, 2);
let tx2 = create_multi_outputs_transaction(&tx1, vec![0], 2, vec![2]);
let tx2_hash = tx2.hash().to_owned();
let tx2_output = tx2.outputs()[0].clone();

let txs = vec![tx1, tx2];

for hash in [&last_cell_base_hash, &tx1_hash, &tx2_hash].iter() {
for hash in &[&last_cell_base_hash, &tx1_hash, &tx2_hash] {
assert_eq!(
shared
.lock_chain_state()
Expand Down Expand Up @@ -154,14 +155,20 @@ fn test_transaction_spend_in_same_block() {
.expect("process block ok");
}

for hash in [&last_cell_base_hash, &tx1_hash].iter() {
assert_eq!(
shared
.lock_chain_state()
.cell(&OutPoint::new_cell(hash.to_owned().to_owned(), 0)),
CellStatus::Dead
);
}
// assert last_cell_base_hash is full dead
assert_eq!(
shared
.lock_chain_state()
.cell(&OutPoint::new_cell(last_cell_base_hash.to_owned(), 0)),
CellStatus::Unknown
);

assert_eq!(
shared
.lock_chain_state()
.cell(&OutPoint::new_cell(tx1_hash.to_owned(), 0)),
CellStatus::Dead
);

assert_eq!(
shared
Expand Down Expand Up @@ -280,10 +287,10 @@ fn test_transaction_conflict_in_different_blocks() {
}

let last_cell_base = &chain.last().unwrap().transactions()[0];
let tx1 = create_transaction(last_cell_base.hash(), 1);
let tx1 = create_multi_outputs_transaction(&last_cell_base, vec![0], 2, vec![1]);
let tx1_hash = tx1.hash();
let tx2 = create_transaction(tx1_hash, 2);
let tx3 = create_transaction(tx1_hash, 3);
let tx2 = create_multi_outputs_transaction(&tx1, vec![0], 2, vec![1]);
let tx3 = create_multi_outputs_transaction(&tx1, vec![0], 2, vec![2]);
// proposal txs
{
let difficulty = parent.difficulty().to_owned();
Expand Down
146 changes: 142 additions & 4 deletions chain/src/tests/delay_verify.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::tests::util::{
create_transaction, create_transaction_with_out_point, gen_block, start_chain,
create_multi_outputs_transaction, create_transaction, create_transaction_with_out_point,
gen_block, start_chain,
};
use ckb_core::block::Block;
use ckb_core::cell::UnresolvableError;
Expand Down Expand Up @@ -145,10 +146,11 @@ fn test_dead_cell_in_different_block() {
}

let last_cell_base = &chain2.last().unwrap().transactions()[0];
let tx1 = create_transaction(last_cell_base.hash(), 1);
let tx1 = create_multi_outputs_transaction(&last_cell_base, vec![0], 2, vec![1]);
let tx1_hash = tx1.hash();
let tx2 = create_transaction(tx1_hash, 2);
let tx3 = create_transaction(tx1_hash, 3);
let tx2 = create_multi_outputs_transaction(&tx1, vec![0], 2, vec![2]);
let tx3 = create_multi_outputs_transaction(&tx1, vec![0], 2, vec![3]);

for i in switch_fork_number..final_number {
let difficulty = parent.difficulty().to_owned();
let new_block = if i == switch_fork_number {
Expand Down Expand Up @@ -418,3 +420,139 @@ fn test_invalid_out_point_index_in_different_blocks() {
.unwrap()
);
}

#[test]
fn test_full_dead_transaction() {
let (chain_controller, shared) = start_chain(None);
let final_number = 20;
let switch_fork_number = 10;
let pin = 3;

let mut chain1: Vec<Block> = Vec::new();
let mut chain2: Vec<Block> = Vec::new();

let mut parent = shared.block_header(&shared.block_hash(0).unwrap()).unwrap();
let difficulty = parent.difficulty().to_owned();
let block = gen_block(
&parent,
difficulty + U256::from(100u64),
vec![],
vec![],
vec![],
);
chain1.push(block.clone());
chain2.push(block.clone());
let root_tx = &block.transactions()[0];
let tx1 = create_multi_outputs_transaction(&root_tx, vec![0], 1, vec![1]);

parent = block.header().to_owned();
for i in 2..switch_fork_number {
let difficulty = parent.difficulty().to_owned();
let new_block = if i == pin {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![],
vec![tx1.clone()],
vec![],
)
} else if i == pin + 2 {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![tx1.clone()],
vec![],
vec![],
)
} else {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![],
vec![],
vec![],
)
};
chain1.push(new_block.clone());
chain2.push(new_block.clone());
parent = new_block.header().to_owned();
}

let tx2 = create_multi_outputs_transaction(&tx1, vec![0], 1, vec![2]);
let tx3 = create_multi_outputs_transaction(&tx2, vec![0], 1, vec![3]);

for i in switch_fork_number..final_number {
let difficulty = parent.difficulty().to_owned();
let new_block = if i == final_number - 3 {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![],
vec![tx2.clone(), tx3.clone()],
vec![],
)
} else if i == final_number - 1 {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![tx2.clone(), tx3.clone()],
vec![],
vec![],
)
} else {
gen_block(
&parent,
difficulty + U256::from(100u64),
vec![],
vec![],
vec![],
)
};
chain1.push(new_block.clone());
parent = new_block.header().to_owned();
}

parent = chain2.last().unwrap().header().clone();
for i in switch_fork_number..final_number {
let difficulty = parent.difficulty().to_owned();
let new_block = if i == final_number - 3 {
gen_block(
&parent,
difficulty + U256::from(101u64),
vec![],
vec![tx2.clone(), tx3.clone()],
vec![],
)
} else if i == final_number - 1 {
gen_block(
&parent,
difficulty + U256::from(101u64),
vec![tx2.clone(), tx3.clone()],
vec![],
vec![],
)
} else {
gen_block(
&parent,
difficulty + U256::from(101u64),
vec![],
vec![],
vec![],
)
};
chain2.push(new_block.clone());
parent = new_block.header().to_owned();
}

for block in &chain1 {
chain_controller
.process_block(Arc::new(block.clone()), true)
.expect("process block ok");
}

for block in &chain2 {
chain_controller
.process_block(Arc::new(block.clone()), true)
.expect("process block ok");
}
}
52 changes: 46 additions & 6 deletions chain/src/tests/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use numext_fixed_hash::H256;
use numext_fixed_uint::U256;
use test_chain_utils::create_always_success_cell;

const MIN_CAP: Capacity = capacity_bytes!(60);

fn create_always_success_tx() -> Transaction {
let (always_success_cell, _) = create_always_success_cell();
TransactionBuilder::default()
Expand Down Expand Up @@ -83,16 +85,54 @@ pub(crate) fn gen_block(
.transaction(cellbase)
.transactions(transactions)
.uncles(uncles)
.proposals(
proposals
.iter()
.map(Transaction::proposal_short_id)
.collect(),
)
.proposals(proposals.iter().map(Transaction::proposal_short_id))
.header_builder(header_builder)
.build()
}

// more flexible mock function for make non-full-dead-cell test case
pub(crate) fn create_multi_outputs_transaction(
parent: &Transaction,
indices: Vec<usize>,
output_len: usize,
data: Vec<u8>,
) -> Transaction {
let (_, always_success_script) = create_always_success_cell();
let always_success_out_point = create_always_success_out_point();

let parent_outputs = parent.outputs();
let total_capacity = indices
.iter()
.map(|i| parent_outputs[*i].capacity)
.try_fold(Capacity::zero(), Capacity::safe_add)
.unwrap();

let output_capacity = Capacity::shannons(total_capacity.as_u64() / output_len as u64);

assert!(output_capacity > MIN_CAP);
let data = Bytes::from(data);

let outputs = (0..output_len).map(|_| {
CellOutput::new(
output_capacity,
data.clone(),
always_success_script.clone(),
None,
)
});

let parent_pts = parent.output_pts();
let inputs = indices
.iter()
.map(|i| CellInput::new(parent_pts[*i].clone(), 0));

TransactionBuilder::default()
.outputs(outputs)
.inputs(inputs)
.dep(always_success_out_point)
.build()
}

pub(crate) fn create_transaction(parent: &H256, unique_data: u8) -> Transaction {
create_transaction_with_out_point(OutPoint::new_cell(parent.to_owned(), 0), unique_data)
}
Expand Down
9 changes: 6 additions & 3 deletions core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl BlockBuilder {
self
}

pub fn uncles(mut self, uncles: Vec<UncleBlock>) -> Self {
pub fn uncles(mut self, uncles: impl IntoIterator<Item = UncleBlock>) -> Self {
self.uncles.extend(uncles);
self
}
Expand All @@ -213,7 +213,7 @@ impl BlockBuilder {
self
}

pub fn transactions(mut self, transactions: Vec<Transaction>) -> Self {
pub fn transactions(mut self, transactions: impl IntoIterator<Item = Transaction>) -> Self {
self.transactions.extend(transactions);
self
}
Expand All @@ -223,7 +223,10 @@ impl BlockBuilder {
self
}

pub fn proposals(mut self, proposal_short_ids: Vec<ProposalShortId>) -> Self {
pub fn proposals(
mut self,
proposal_short_ids: impl IntoIterator<Item = ProposalShortId>,
) -> Self {
self.proposals.extend(proposal_short_ids);
self
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ impl TransactionBuilder {
self
}

pub fn inputs(mut self, inputs: Vec<CellInput>) -> Self {
pub fn inputs(mut self, inputs: impl IntoIterator<Item = CellInput>) -> Self {
self.inputs.extend(inputs);
self
}
Expand All @@ -654,7 +654,7 @@ impl TransactionBuilder {
self
}

pub fn outputs(mut self, outputs: Vec<CellOutput>) -> Self {
pub fn outputs(mut self, outputs: impl IntoIterator<Item = CellOutput>) -> Self {
self.outputs.extend(outputs);
self
}
Expand All @@ -669,7 +669,7 @@ impl TransactionBuilder {
self
}

pub fn witnesses(mut self, witness: Vec<Witness>) -> Self {
pub fn witnesses(mut self, witness: impl IntoIterator<Item = Witness>) -> Self {
self.witnesses.extend(witness);
self
}
Expand Down
Loading