use crate::tests::util::{
calculate_reward, create_always_success_out_point, create_always_success_tx, start_chain,
MockStore,
};
use ckb_chain_spec::consensus::{Consensus, ConsensusBuilder};
use ckb_dao_utils::genesis_dao_data;
use ckb_shared::shared::Shared;
use ckb_store::ChainStore;
use ckb_test_chain_utils::always_success_cell;
use ckb_types::prelude::*;
use ckb_types::{
bytes::Bytes,
core::{
capacity_bytes, BlockBuilder, BlockView, Capacity, EpochNumberWithFraction, HeaderView,
TransactionBuilder, TransactionView,
},
packed::{CellDep, CellInput, CellOutputBuilder, OutPoint},
utilities::DIFF_TWO,
};
use std::sync::Arc;
const TX_FEE: Capacity = capacity_bytes!(10);
#[allow(clippy::int_plus_one)]
pub(crate) fn create_cellbase(
parent: &HeaderView,
store: &MockStore,
consensus: &Consensus,
) -> TransactionView {
let (_, _, always_success_script) = always_success_cell();
let number = parent.number() + 1;
let capacity = calculate_reward(store, consensus, parent);
let builder = TransactionBuilder::default()
.input(CellInput::new_cellbase_input(number))
.witness(always_success_script.clone().into_witness());
if (parent.number() + 1) <= consensus.finalization_delay_length() {
builder.build()
} else {
builder
.output(
CellOutputBuilder::default()
.capacity(capacity.pack())
.lock(always_success_script.clone())
.build(),
)
.output_data(Bytes::new().pack())
.build()
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn gen_block(
parent_header: &HeaderView,
transactions: Vec<TransactionView>,
shared: &Shared,
store: &MockStore,
) -> BlockView {
let number = parent_header.number() + 1;
let consensus = shared.consensus();
let cellbase = create_cellbase(parent_header, store, consensus);
let mut txs = vec![cellbase];
txs.extend_from_slice(&transactions);
let epoch = shared
.consensus()
.next_epoch_ext(parent_header, &shared.store().as_data_provider())
.unwrap()
.epoch();
let block = BlockBuilder::default()
.parent_hash(parent_header.hash())
.timestamp((parent_header.timestamp() + 20_000).pack())
.number(number.pack())
.compact_target(epoch.compact_target().pack())
.epoch(epoch.number_with_fraction(number).pack())
.transactions(txs)
.build();
store.insert_block(&block, consensus.genesis_epoch_ext());
block
}
pub(crate) fn create_transaction(
parent: &TransactionView,
index: u32,
missing_output_data: bool,
) -> TransactionView {
let (_, _, always_success_script) = always_success_cell();
let always_success_out_point = create_always_success_out_point();
let input_cap: Capacity = parent
.outputs()
.get(0)
.expect("get output index 0")
.capacity()
.unpack();
let mut builder = TransactionBuilder::default()
.output(
CellOutputBuilder::default()
.capacity(input_cap.safe_sub(TX_FEE).unwrap().pack())
.lock(always_success_script.clone())
.build(),
)
.input(CellInput::new(OutPoint::new(parent.hash(), index), 0))
.cell_dep(
CellDep::new_builder()
.out_point(always_success_out_point)
.build(),
);
if !missing_output_data {
builder = builder.output_data(Bytes::new().pack())
}
builder.build()
}
#[test]
fn non_contextual_block_txs_verify() {
let (_, _, always_success_script) = always_success_cell();
let always_success_tx = create_always_success_tx();
let issue_tx = TransactionBuilder::default()
.input(CellInput::new(OutPoint::null(), 0))
.output(
CellOutputBuilder::default()
.capacity(capacity_bytes!(5_000).pack())
.lock(always_success_script.clone())
.build(),
)
.output_data(Bytes::new().pack())
.build();
let dao = genesis_dao_data(vec![&always_success_tx, &issue_tx]).unwrap();
let genesis_block = BlockBuilder::default()
.transaction(always_success_tx)
.transaction(issue_tx.clone())
.compact_target(DIFF_TWO.pack())
.dao(dao)
.build();
let consensus = ConsensusBuilder::default()
.cellbase_maturity(EpochNumberWithFraction::new(0, 0, 1))
.genesis_block(genesis_block)
.build();
let (chain_controller, shared, parent) = start_chain(Some(consensus));
let mock_store = MockStore::new(&parent, shared.store());
let tx0 = create_transaction(&issue_tx, 0, true);
let tx1 = create_transaction(&tx0, 0, false);
let block = gen_block(&parent, vec![tx0, tx1], &shared, &mock_store);
let ret = chain_controller.process_block(Arc::new(block));
assert!(ret.is_err());
assert_eq!(
format!("{}", ret.err().unwrap()),
"Transaction(OutputsDataLengthMismatch: expected outputs data length (0) = outputs length (1))"
);
}