Merge pull request #52 from ethcore/chain-builder

Handy test helpers
This commit is contained in:
Svyatoslav Nikolsky 2016-10-28 05:59:51 +00:00 committed by GitHub
commit acd9467147
4 changed files with 378 additions and 56 deletions

View File

@ -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();

350
test-data/src/block.rs Normal file
View File

@ -0,0 +1,350 @@
//! Block builder
use chain;
use primitives::hash::H256;
use primitives::bytes::Bytes;
use invoke::{Invoke, Identity};
pub struct BlockBuilder<F=Identity> {
callback: F,
header: Option<chain::BlockHeader>,
transactions: Vec<chain::Transaction>,
}
impl BlockBuilder {
pub fn new() -> Self {
BlockBuilder::with_callback(Identity)
}
}
impl<F> BlockBuilder<F> where F: Invoke<chain::Block> {
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<I>(mut self, txs: I) -> Self
where I: IntoIterator<Item=chain::Transaction>
{
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<Self> {
BlockHeaderBuilder::with_callback(self)
}
pub fn transaction(self) -> TransactionBuilder<Self> {
TransactionBuilder::with_callback(self)
}
pub fn build(self) -> F::Result {
self.callback.invoke(
chain::Block::new(
self.header.unwrap(),
self.transactions,
)
)
}
}
impl<F> Invoke<chain::BlockHeader> for BlockBuilder<F>
where F: Invoke<chain::Block>
{
type Result = Self;
fn invoke(self, header: chain::BlockHeader) -> Self {
self.with_header(header)
}
}
impl<F> Invoke<chain::Transaction> for BlockBuilder<F>
where F: Invoke<chain::Block>
{
type Result = Self;
fn invoke(self, tx: chain::Transaction) -> Self {
self.with_transaction(tx)
}
}
pub struct BlockHeaderBuilder<F=Identity> {
callback: F,
time: u32,
parent: H256,
nonce: u32,
nbits: u32,
version: u32,
merkle_root: H256,
}
impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
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<F=Identity> {
callback: F,
version: i32,
lock_time: u32,
inputs: Vec<chain::TransactionInput>,
outputs: Vec<chain::TransactionOutput>,
}
impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
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<Self> {
TransactionInputBuilder::with_callback(self)
}
pub fn coinbase(self) -> Self {
self.input().coinbase().build()
}
pub fn output(self) -> TransactionOutputBuilder<Self> {
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<F> Invoke<chain::TransactionInput> for TransactionBuilder<F>
where F: Invoke<chain::Transaction>
{
type Result = Self;
fn invoke(self, tx: chain::TransactionInput) -> Self {
self.with_input(tx)
}
}
impl<F> Invoke<chain::TransactionOutput> for TransactionBuilder<F>
where F: Invoke<chain::Transaction>
{
type Result = Self;
fn invoke(self, tx: chain::TransactionOutput) -> Self {
self.with_output(tx)
}
}
pub struct TransactionInputBuilder<F=Identity> {
callback: F,
output: Option<chain::OutPoint>,
signature: Bytes,
sequence: u32,
}
impl<F> TransactionInputBuilder<F> where F: Invoke<chain::TransactionInput> {
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<F=Identity> {
callback: F,
value: u64,
signature: Bytes,
}
impl<F> TransactionOutputBuilder<F> where F: Invoke<chain::TransactionOutput> {
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));
}

15
test-data/src/invoke.rs Normal file
View File

@ -0,0 +1,15 @@
//! invoke helper
pub trait Invoke<A> {
type Result;
fn invoke(self, arg: A) -> Self::Result;
}
pub struct Identity;
impl<A> Invoke<A> for Identity {
type Result = A;
fn invoke(self, arg: A) -> A { arg }
}

View File

@ -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();