diff --git a/db/src/storage.rs b/db/src/storage.rs index 15cdb71a..3f117e11 100644 --- a/db/src/storage.rs +++ b/db/src/storage.rs @@ -385,34 +385,9 @@ mod tests { use super::{Storage, Store}; use devtools::RandomTempPath; - use chain::{ - Block, BlockHeader, Transaction, RepresentH256, TransactionOutput, - TransactionInput, OutPoint, - }; + use chain::{Block, RepresentH256}; use super::super::BlockRef; use test_data; - use primitives::hash::H256; - use primitives::bytes::Bytes; - - fn dummy_coinbase_tx() -> Transaction { - Transaction { - version: 0, - inputs: vec![ - TransactionInput { - previous_output: OutPoint { hash: H256::from(0), index: 0xffffffff }, - script_sig: Bytes::new_with_len(0), - sequence: 0 - } - ], - outputs: vec![ - TransactionOutput { - value: 0, - script_pubkey: Bytes::new_with_len(0), - } - ], - lock_time: 0, - } - } #[test] fn open_store() { @@ -505,36 +480,15 @@ mod tests { let genesis_meta = store.transaction_meta(&genesis_coinbase).unwrap(); assert!(!genesis_meta.is_spent(0)); - let forged_block = Block::new( - BlockHeader { - version: 0, - previous_header_hash: genesis.hash(), - merkle_root_hash: H256::from(0), - nbits: 0, - time: 0, - nonce: 0, - }, - vec![ - dummy_coinbase_tx(), - Transaction { - version: 0, - inputs: vec![ - TransactionInput { - previous_output: OutPoint { hash: genesis_coinbase.clone(), index: 0 }, - script_sig: Bytes::new_with_len(0), - sequence: 0 - } - ], - outputs: vec![ - TransactionOutput { - value: 0, - script_pubkey: Bytes::new_with_len(0), - } - ], - lock_time: 0, - }, - ] - ); + let forged_block = test_data::block_builder() + .header().parent(genesis.hash()).build() + .transaction().coinbase().build() + .transaction() + .input().hash(genesis_coinbase.clone()).build() + .output().build() + .build() + .build(); + store.insert_block(&forged_block).unwrap(); let genesis_meta = store.transaction_meta(&genesis_coinbase).unwrap(); diff --git a/test-data/src/block.rs b/test-data/src/block.rs new file mode 100644 index 00000000..f3775dd9 --- /dev/null +++ b/test-data/src/block.rs @@ -0,0 +1,350 @@ +//! Block builder + +use chain; +use primitives::hash::H256; +use primitives::bytes::Bytes; +use invoke::{Invoke, Identity}; + +pub struct BlockBuilder { + callback: F, + header: Option, + transactions: Vec, +} + +impl BlockBuilder { + pub fn new() -> Self { + BlockBuilder::with_callback(Identity) + } +} + +impl BlockBuilder where F: Invoke { + pub fn with_callback(callback: F) -> Self { + BlockBuilder { + callback: callback, + header: None, + transactions: Vec::new(), + } + } + + pub fn with_header(mut self, header: chain::BlockHeader) -> Self { + self.header = Some(header); + self + } + + pub fn with_transaction(mut self, transaction: chain::Transaction) -> Self { + self.transactions.push(transaction); + self + } + + pub fn with_transactions(mut self, txs: I) -> Self + where I: IntoIterator + { + self.transactions.extend(txs); + self + } + + pub fn with_raw(mut self, raw: &'static str) -> Self { + let raw_block: chain::Block = raw.into(); + self.transactions = raw_block.transactions.to_vec(); + self.header = Some(raw_block.header().clone()); + self + } + + pub fn header(self) -> BlockHeaderBuilder { + BlockHeaderBuilder::with_callback(self) + } + + pub fn transaction(self) -> TransactionBuilder { + TransactionBuilder::with_callback(self) + } + + pub fn build(self) -> F::Result { + self.callback.invoke( + chain::Block::new( + self.header.unwrap(), + self.transactions, + ) + ) + } +} + +impl Invoke for BlockBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, header: chain::BlockHeader) -> Self { + self.with_header(header) + } +} + +impl Invoke for BlockBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, tx: chain::Transaction) -> Self { + self.with_transaction(tx) + } +} + +pub struct BlockHeaderBuilder { + callback: F, + time: u32, + parent: H256, + nonce: u32, + nbits: u32, + version: u32, + merkle_root: H256, +} + +impl BlockHeaderBuilder where F: Invoke { + pub fn with_callback(callback: F) -> Self { + BlockHeaderBuilder { + callback: callback, + time: 0, + nonce: 0, + merkle_root: H256::from(0), + parent: H256::from(0), + nbits: 0, + version: 1, + } + } + + pub fn parent(mut self, parent: H256) -> Self { + self.parent = parent; + self + } + + pub fn time(mut self, time: u32) -> Self { + self.time = time; + self + } + + pub fn merkle_root(mut self, merkle_root: H256) -> Self { + self.merkle_root = merkle_root; + self + } + + pub fn nbits(mut self, nbits: u32) -> Self { + self.nbits = nbits; + self + } + + pub fn nonce(mut self, nonce: u32) -> Self { + self.nonce = nonce; + self + } + + pub fn build(self) -> F::Result { + self.callback.invoke( + chain::BlockHeader { + time: self.time, + previous_header_hash: self.parent, + nbits: self.nbits, + nonce: self.nonce, + merkle_root_hash: self.merkle_root, + version: self.version, + } + ) + } +} + +pub struct TransactionBuilder { + callback: F, + version: i32, + lock_time: u32, + inputs: Vec, + outputs: Vec, +} + +impl TransactionBuilder where F: Invoke { + fn with_callback(callback: F) -> Self { + TransactionBuilder { + callback: callback, + version: 1, + lock_time: 0, + inputs: Vec::new(), + outputs: Vec::new(), + } + } + + fn with_input(mut self, input: chain::TransactionInput) -> Self { + self.inputs.push(input); + self + } + + fn with_output(mut self, input: chain::TransactionOutput) -> Self { + self.outputs.push(input); + self + } + + pub fn lock_time(mut self, time: u32) -> Self { + self.lock_time = time; + self + } + + pub fn input(self) -> TransactionInputBuilder { + TransactionInputBuilder::with_callback(self) + } + + pub fn coinbase(self) -> Self { + self.input().coinbase().build() + } + + pub fn output(self) -> TransactionOutputBuilder { + TransactionOutputBuilder::with_callback(self) + } + + pub fn build(self) -> F::Result { + self.callback.invoke( + chain::Transaction { + lock_time: self.lock_time, + version: self.version, + inputs: self.inputs, + outputs: self.outputs, + } + ) + } +} + + +impl Invoke for TransactionBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, tx: chain::TransactionInput) -> Self { + self.with_input(tx) + } +} + +impl Invoke for TransactionBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, tx: chain::TransactionOutput) -> Self { + self.with_output(tx) + } +} + +pub struct TransactionInputBuilder { + callback: F, + output: Option, + signature: Bytes, + sequence: u32, +} + +impl TransactionInputBuilder where F: Invoke { + fn with_callback(callback: F) -> Self { + TransactionInputBuilder { + callback: callback, + output: None, + signature: Bytes::new_with_len(0), + sequence: 0, + } + } + + pub fn hash(mut self, hash: H256) -> Self { + let mut output = self.output.unwrap_or(chain::OutPoint { hash: hash.clone(), index: 0 }); + output.hash = hash; + self.output = Some(output); + self + } + + pub fn index(mut self, index: u32) -> Self { + let mut output = self.output.unwrap_or(chain::OutPoint { hash: H256::from(0), index: index }); + output.index = index; + self.output = Some(output); + self + } + + pub fn coinbase(mut self) -> Self { + self.output = Some(chain::OutPoint { hash: H256::from(0), index: 0xffffffff }); + self + } + + pub fn build(self) -> F::Result { + self.callback.invoke( + chain::TransactionInput { + previous_output: self.output.unwrap_or_else(|| panic!("Building input without previous output")), + script_sig: self.signature, + sequence: self.sequence, + } + ) + } +} + + +pub struct TransactionOutputBuilder { + callback: F, + value: u64, + signature: Bytes, +} + +impl TransactionOutputBuilder where F: Invoke { + fn with_callback(callback: F) -> Self { + TransactionOutputBuilder { + callback: callback, + signature: Bytes::new_with_len(0), + value: 0, + } + } + + pub fn value(mut self, value: u64) -> Self { + self.value = value; + self + } + + pub fn build(self) -> F::Result { + self.callback.invoke( + chain::TransactionOutput { + script_pubkey: self.signature, + value: self.value, + } + ) + } +} + +pub fn block_builder() -> BlockBuilder { BlockBuilder::new() } + +#[test] +fn example1() { + let block = BlockBuilder::new().header().time(1000).build().build(); + assert_eq!(block.header().time, 1000); +} + +#[test] +fn example2() { + let block = BlockBuilder::new() + .header().build() + .transaction().lock_time(100500).build() + .build(); + + assert_eq!(block.transactions().len(), 1); +} + +#[test] +fn example3() { + let block = block_builder().header().build() + .transaction().coinbase().build() + .build(); + + assert!(block.transactions()[0].is_coinbase()); +} + +#[test] +fn example4() { + let block = block_builder().header().build() + .transaction().coinbase() + .output().value(10).build() + .build() + .transaction() + .input().hash(H256::from(1)).index(1).build() + .build() + .build(); + + assert_eq!(block.transactions().len(), 2); + assert_eq!(block.transactions()[1].inputs[0].previous_output.hash, H256::from(1)); +} diff --git a/test-data/src/invoke.rs b/test-data/src/invoke.rs new file mode 100644 index 00000000..4223dc58 --- /dev/null +++ b/test-data/src/invoke.rs @@ -0,0 +1,15 @@ +//! invoke helper + +pub trait Invoke { + type Result; + + fn invoke(self, arg: A) -> Self::Result; +} + +pub struct Identity; + +impl Invoke for Identity { + type Result = A; + + fn invoke(self, arg: A) -> A { arg } +} diff --git a/test-data/src/lib.rs b/test-data/src/lib.rs index 3c347d29..a3236e17 100644 --- a/test-data/src/lib.rs +++ b/test-data/src/lib.rs @@ -7,8 +7,11 @@ extern crate serialization as ser; use chain::Block; pub mod chain_builder; +pub mod block; +pub mod invoke; pub use chain_builder::{ChainBuilder, TransactionBuilder}; +pub use block::block_builder; pub fn block1() -> Block { let block: Block = "01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad27b9137190000000000190760b278fe7b8565fda3b968b918d5fd997f993b23674c0af3b6fde300b38f33a5914ce6ed5b1b01e32f570201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704e6ed5b1b014effffffff0100f2052a01000000434104b68a50eaa0287eff855189f949c1c6e5f58b37c88231373d8a59809cbae83059cc6469d65c665ccfd1cfeb75c6e8e19413bba7fbff9bc762419a76d87b16086eac000000000100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".into();