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

feat: tx valid since #372

Merged
merged 18 commits into from Apr 12, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions benches/benches/process_block.rs
Expand Up @@ -142,7 +142,7 @@ fn new_chain() -> (
let commit_transactions: Vec<Transaction> = (0..100)
.map(|i| {
TransactionBuilder::default()
.input(CellInput::new(OutPoint::null(), vec![]))
.input(CellInput::new(OutPoint::null(), 0, vec![]))
.output(CellOutput::new(
50000,
vec![i],
Expand Down Expand Up @@ -231,6 +231,6 @@ fn create_transaction(hash: H256) -> Transaction {
Script::always_success(),
None,
))
.input(CellInput::new(OutPoint::new(hash, 0), vec![]))
.input(CellInput::new(OutPoint::new(hash, 0), 0, vec![]))
.build()
}
52 changes: 46 additions & 6 deletions chain/src/chain.rs
@@ -1,19 +1,20 @@
use ckb_chain_spec::consensus::Consensus;
use ckb_core::block::Block;
use ckb_core::cell::{
resolve_transaction, BlockCellProvider, OverlayCellProvider, ResolvedTransaction,
};
use ckb_core::extras::BlockExt;
use ckb_core::service::{Request, DEFAULT_CHANNEL_SIZE, SIGNAL_CHANNEL_SIZE};
use ckb_core::transaction::ProposalShortId;
use ckb_core::BlockNumber;
use ckb_core::{header::Header, BlockNumber};
use ckb_notify::NotifyController;
use ckb_shared::cell_set::CellSetDiff;
use ckb_shared::chain_state::ChainState;
use ckb_shared::error::SharedError;
use ckb_shared::index::ChainIndex;
use ckb_shared::shared::Shared;
use ckb_shared::store::StoreBatch;
use ckb_traits::ChainProvider;
use ckb_traits::{BlockMedianTimeContext, ChainProvider};
use ckb_verification::{BlockVerifier, TransactionsVerifier, Verifier};
use crossbeam_channel::{self, select, Receiver, Sender};
use failure::Error as FailureError;
Expand Down Expand Up @@ -96,6 +97,39 @@ impl GlobalIndex {
}
}

// Verification context for fork
struct ForkContext<'a, CI> {
pub fork_blocks: &'a [Block],
pub store: Arc<CI>,
pub consensus: &'a Consensus,
}

impl<'a, CI: ChainIndex> ForkContext<'a, CI> {
fn get_header(&self, number: BlockNumber) -> Option<Header> {
match self
.fork_blocks
.iter()
.find(|b| b.header().number() == number)
{
Some(block) => Some(block.header().to_owned()),
None => self
.store
.get_block_hash(number)
.and_then(|hash| self.store.get_header(&hash)),
}
}
}

impl<'a, CI: ChainIndex> BlockMedianTimeContext for ForkContext<'a, CI> {
fn median_block_count(&self) -> u64 {
self.consensus.median_time_block_count() as u64
}

fn timestamp(&self, number: BlockNumber) -> Option<u64> {
self.get_header(number).map(|header| header.timestamp())
}
}

pub struct ChainService<CI> {
shared: Shared<CI>,
notify: NotifyController,
Expand Down Expand Up @@ -439,6 +473,12 @@ impl<CI: ChainIndex + 'static> ChainService<CI> {
chain_state.mut_txs_verify_cache(),
&resolved,
self.shared.block_reward(b.header().number()),
ForkContext {
fork_blocks: &fork.attached_blocks,
store: Arc::clone(self.shared.store()),
consensus: self.shared.consensus(),
},
b.header().number(),
) {
Ok(_) => {
cell_set_diff.push_new(b);
Expand Down Expand Up @@ -506,10 +546,10 @@ impl<CI: ChainIndex + 'static> ChainService<CI> {

debug!(target: "chain", "Proposal in Head Block {{");
debug!(target: "chain", " {:?}", self
.shared
.block_hash(tip)
.and_then(|hash| self.shared.store().get_block_proposal_txs_ids(&hash))
.expect("invalid block number"));
.shared
.block_hash(tip)
.and_then(|hash| self.shared.store().get_block_proposal_txs_ids(&hash))
.expect("invalid block number"));

debug!(target: "chain", "}}");

Expand Down
11 changes: 7 additions & 4 deletions chain/src/tests/basic.rs
Expand Up @@ -2,7 +2,7 @@ use crate::tests::util::{create_transaction, gen_block, start_chain};
use ckb_chain_spec::consensus::Consensus;
use ckb_core::block::Block;
use ckb_core::block::BlockBuilder;
use ckb_core::cell::{CellProvider, CellStatus};
use ckb_core::cell::{CellMeta, CellProvider, CellStatus};
use ckb_core::header::HeaderBuilder;
use ckb_core::script::Script;
use ckb_core::transaction::{CellInput, CellOutput, OutPoint, TransactionBuilder};
Expand All @@ -14,7 +14,7 @@ use std::sync::Arc;
#[test]
fn test_genesis_transaction_spend() {
let tx = TransactionBuilder::default()
.input(CellInput::new(OutPoint::null(), Default::default()))
.input(CellInput::new(OutPoint::null(), 0, Default::default()))
.outputs(vec![
CellOutput::new(
100_000_000,
Expand Down Expand Up @@ -173,7 +173,10 @@ fn test_transaction_spend_in_same_block() {
.chain_state()
.lock()
.cell(&OutPoint::new(tx2_hash, 0)),
CellStatus::Live(tx2_output)
CellStatus::Live(CellMeta {
cell_output: tx2_output,
block_number: Some(4)
})
);
}

Expand Down Expand Up @@ -344,7 +347,7 @@ fn test_transaction_conflict_in_different_blocks() {
#[test]
fn test_genesis_transaction_fetch() {
let tx = TransactionBuilder::default()
.input(CellInput::new(OutPoint::null(), Default::default()))
.input(CellInput::new(OutPoint::null(), 0, Default::default()))
.outputs(vec![
CellOutput::new(
100_000_000,
Expand Down
2 changes: 1 addition & 1 deletion chain/src/tests/util.rs
Expand Up @@ -81,6 +81,6 @@ pub(crate) fn create_transaction(parent: H256, unique_data: u8) -> Transaction {
Script::always_success(),
None,
))
.input(CellInput::new(OutPoint::new(parent, 0), vec![]))
.input(CellInput::new(OutPoint::new(parent, 0), 0, vec![]))
.build()
}
38 changes: 25 additions & 13 deletions core/src/cell.rs
Expand Up @@ -6,10 +6,16 @@ use numext_fixed_hash::H256;
use std::iter::Chain;
use std::slice;

#[derive(Clone, PartialEq, Debug)]
pub struct CellMeta {
pub cell_output: CellOutput,
pub block_number: Option<u64>,
}

#[derive(Clone, PartialEq, Debug)]
pub enum CellStatus {
/// Cell exists and has not been spent.
Live(CellOutput),
Live(CellMeta),
/// Cell exists and has been spent.
Dead,
/// Cell does not exist.
Expand All @@ -32,14 +38,14 @@ impl CellStatus {
self == &CellStatus::Unknown
}

pub fn get_live(&self) -> Option<&CellOutput> {
pub fn get_live(&self) -> Option<&CellMeta> {
match *self {
CellStatus::Live(ref output) => Some(output),
_ => None,
}
}

pub fn take_live(self) -> Option<CellOutput> {
pub fn take_live(self) -> Option<CellMeta> {
match self {
CellStatus::Live(output) => Some(output),
_ => None,
Expand Down Expand Up @@ -114,7 +120,10 @@ impl<'a> CellProvider for BlockCellProvider<'a> {
.outputs()
.get(out_point.index as usize)
{
Some(x) => CellStatus::Live(x.clone()),
Some(x) => CellStatus::Live(CellMeta {
cell_output: x.clone(),
block_number: Some(self.block.header().number()),
}),
None => CellStatus::Unknown,
}
} else {
Expand Down Expand Up @@ -191,8 +200,8 @@ impl ResolvedTransaction {
self.input_cells
.iter()
.filter_map(|cell_status| {
if let CellStatus::Live(cell_output) = cell_status {
Some(cell_output.capacity)
if let CellStatus::Live(cell_meta) = cell_status {
Some(cell_meta.cell_output.capacity)
} else {
None
}
Expand All @@ -209,12 +218,12 @@ mod tests {
use std::collections::HashMap;

struct CellMemoryDb {
cells: HashMap<OutPoint, Option<CellOutput>>,
cells: HashMap<OutPoint, Option<CellMeta>>,
}
impl CellProvider for CellMemoryDb {
fn cell(&self, o: &OutPoint) -> CellStatus {
match self.cells.get(o) {
Some(&Some(ref cell_output)) => CellStatus::Live(cell_output.clone()),
Some(&Some(ref cell)) => CellStatus::Live(cell.clone()),
Some(&None) => CellStatus::Dead,
None => CellStatus::Unknown,
}
Expand All @@ -239,11 +248,14 @@ mod tests {
hash: H256::zero(),
index: 3,
};
let o = CellOutput {
capacity: 2,
data: vec![],
lock: Script::default(),
type_: None,
let o = CellMeta {
block_number: Some(1),
cell_output: CellOutput {
capacity: 2,
data: vec![],
lock: Script::default(),
type_: None,
},
};

db.cells.insert(p1.clone(), Some(o.clone()));
Expand Down
22 changes: 12 additions & 10 deletions core/src/transaction.rs
Expand Up @@ -63,32 +63,36 @@ impl OutPoint {
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, OccupiedCapacity)]
pub struct CellInput {
pub previous_output: OutPoint,
pub valid_since: u64,
// Depends on whether the operation is Transform or Destroy, this is the proof to transform
// lock or destroy lock.
pub args: Vec<Vec<u8>>,
}

impl CellInput {
pub fn new(previous_output: OutPoint, args: Vec<Vec<u8>>) -> Self {
pub fn new(previous_output: OutPoint, valid_since: u64, args: Vec<Vec<u8>>) -> Self {
CellInput {
previous_output,
valid_since,
args,
}
}

pub fn new_cellbase_input(block_number: BlockNumber) -> Self {
CellInput {
previous_output: OutPoint::null(),
valid_since: 0,
args: vec![block_number.to_le_bytes().to_vec()],
}
}

pub fn destruct(self) -> (OutPoint, Vec<Vec<u8>>) {
pub fn destruct(self) -> (OutPoint, u64, Vec<Vec<u8>>) {
let CellInput {
previous_output,
valid_since,
args,
} = self;
(previous_output, args)
(previous_output, valid_since, args)
}
}

Expand Down Expand Up @@ -424,19 +428,17 @@ mod test {
Script::default(),
None,
))
.input(CellInput::new(OutPoint::new(H256::zero(), 0), vec![]))
.input(CellInput::new(OutPoint::new(H256::zero(), 0), 0, vec![]))
.witness(vec![vec![7, 8, 9]])
.build();

assert_eq!(
tx.hash(),
H256::from_hex_str("3a4238c3fda565d6e76e76b5b05d3403b37b94d53c1644d5ff58d4e9293ca468")
.unwrap()
format!("{:x}", tx.hash()),
"a896bfe8c8439305f099e1f07b47844ba0a5a27ec1e26ec25b236fa7e4831115"
);
assert_eq!(
tx.witness_hash(),
H256::from_hex_str("997f0627d2c1ef00fc98311357aa097c3fff5ed0a0408e14ea26656f5beae6b3")
.unwrap()
format!("{:x}", tx.witness_hash()),
"3f08580e373cad9a828173efe614ce3a8310957351c77f2859a18fc49d4cd227"
);
}
}
4 changes: 4 additions & 0 deletions core/src/transaction_meta.rs
Expand Up @@ -47,6 +47,10 @@ impl TransactionMeta {
self.dead_cell.len()
}

pub fn block_number(&self) -> u64 {
self.block_number
}

pub fn is_empty(&self) -> bool {
self.dead_cell.is_empty()
}
Expand Down
8 changes: 6 additions & 2 deletions miner/src/block_assembler.rs
Expand Up @@ -538,8 +538,12 @@ mod tests {
.with_header_builder(header_builder);

let resolver = HeaderResolverWrapper::new(block.header(), shared.clone());
let header_verifier = HeaderVerifier::new(shared.clone(), Pow::Dummy.engine());
assert!(header_verifier.verify(&resolver).is_ok());
let header_verify_result = {
let chain_state = shared.chain_state().lock();
let header_verifier = HeaderVerifier::new(&*chain_state, Pow::Dummy.engine());
header_verifier.verify(&resolver)
};
assert!(header_verify_result.is_ok());

let block_verify = BlockVerifier::new(shared.clone());
assert!(block_verify.verify(&block).is_ok());
Expand Down
1 change: 1 addition & 0 deletions protocol/src/builder.rs
Expand Up @@ -156,6 +156,7 @@ impl<'a> FbsCellInput<'a> {
let mut builder = CellInputBuilder::new(fbb);
builder.add_hash(&hash);
builder.add_index(cell_input.previous_output.index);
builder.add_valid_since(cell_input.valid_since);
builder.add_args(args);
builder.finish()
}
Expand Down
1 change: 1 addition & 0 deletions protocol/src/convert.rs
Expand Up @@ -280,6 +280,7 @@ impl<'a> TryFrom<ckb_protocol::CellInput<'a>> for ckb_core::transaction::CellInp
hash: TryInto::try_into(hash)?,
index: cell_input.index(),
},
valid_since: cell_input.valid_since(),
args: cast!(args)?,
})
}
Expand Down
1 change: 1 addition & 0 deletions protocol/src/protocol.fbs
Expand Up @@ -80,6 +80,7 @@ table OutPoint {
table CellInput {
hash: H256;
index: uint32;
valid_since: uint64;
args: [Bytes];
}

Expand Down