TransactionBuilder can build transaction with desired size and number of sigops
This commit is contained in:
parent
c060612a65
commit
76453c956e
|
@ -1080,6 +1080,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"primitives 0.1.0",
|
"primitives 0.1.0",
|
||||||
|
"script 0.1.0",
|
||||||
"serialization 0.1.0",
|
"serialization 0.1.0",
|
||||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,9 @@ version = "0.1.0"
|
||||||
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
time = "0.1"
|
||||||
|
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
primitives = { path = "../primitives" }
|
primitives = { path = "../primitives" }
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
time = "0.1"
|
script = { path = "../script" }
|
||||||
|
|
|
@ -4,7 +4,9 @@ use std::cell::Cell;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use primitives::bytes::Bytes;
|
use primitives::bytes::Bytes;
|
||||||
use primitives::compact::Compact;
|
use primitives::compact::Compact;
|
||||||
|
use ser::{Serializable, serialized_list_size};
|
||||||
use chain;
|
use chain;
|
||||||
|
use script::{Builder as ScriptBuilder, Opcode};
|
||||||
use invoke::{Invoke, Identity};
|
use invoke::{Invoke, Identity};
|
||||||
use super::genesis;
|
use super::genesis;
|
||||||
|
|
||||||
|
@ -118,6 +120,20 @@ impl<F> BlockBuilder<F> where F: Invoke<chain::Block> {
|
||||||
TransactionBuilder::with_callback(self)
|
TransactionBuilder::with_callback(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transaction_with_sigops(self, sigops: usize) -> TransactionBuilder<Self> {
|
||||||
|
// calling `index` creates previous output
|
||||||
|
TransactionBuilder::with_callback(self).input().index(0).signature_with_sigops(sigops).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transaction_with_size(self, size: usize) -> TransactionBuilder<Self> {
|
||||||
|
let builder = TransactionBuilder::with_callback(self);
|
||||||
|
let current_size = builder.size();
|
||||||
|
assert!(size > current_size, "desired transaction size is too low");
|
||||||
|
// calling `index` creates previous output
|
||||||
|
// let's remove current size and 1 (size of 0 script len)
|
||||||
|
builder.input_with_size(size - current_size - 1).index(0).build()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn derived_transaction(self, tx_idx: usize, output_idx: u32) -> TransactionBuilder<Self> {
|
pub fn derived_transaction(self, tx_idx: usize, output_idx: u32) -> TransactionBuilder<Self> {
|
||||||
let tx = self.transactions.get(tx_idx).expect(&format!("using derive_transaction with the wrong index ({})", tx_idx)).clone();
|
let tx = self.transactions.get(tx_idx).expect(&format!("using derive_transaction with the wrong index ({})", tx_idx)).clone();
|
||||||
TransactionBuilder::with_callback(self).input().hash(tx.hash()).index(output_idx).build()
|
TransactionBuilder::with_callback(self).input().hash(tx.hash()).index(output_idx).build()
|
||||||
|
@ -265,6 +281,13 @@ impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.version.serialized_size() +
|
||||||
|
self.lock_time.serialized_size() +
|
||||||
|
serialized_list_size(&self.inputs) +
|
||||||
|
serialized_list_size(&self.outputs)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lock_time(mut self, time: u32) -> Self {
|
pub fn lock_time(mut self, time: u32) -> Self {
|
||||||
self.lock_time = time;
|
self.lock_time = time;
|
||||||
self
|
self
|
||||||
|
@ -274,6 +297,23 @@ impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
|
||||||
TransactionInputBuilder::with_callback(self)
|
TransactionInputBuilder::with_callback(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn input_with_size(self, size: usize) -> TransactionInputBuilder<Self> {
|
||||||
|
// `OutPoint` and sequence size
|
||||||
|
let raw_input_size = 40;
|
||||||
|
let script_len_size = match size {
|
||||||
|
//0...(0xfc + 1) => 1,
|
||||||
|
0...0xfd => 1,
|
||||||
|
//0xfd...(0xffff + 3) => 3,
|
||||||
|
0xfd...0x10002 => 3,
|
||||||
|
//0x10000...(0xffff_ffff + 5) => 5,
|
||||||
|
0x10000...0x1_0000_0004 => 5,
|
||||||
|
_ => 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(size >= raw_input_size + script_len_size, "Desired input size is too small");
|
||||||
|
TransactionInputBuilder::with_callback(self).signature_with_size(size - raw_input_size - script_len_size)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn coinbase(self) -> Self {
|
pub fn coinbase(self) -> Self {
|
||||||
self.input().coinbase().build()
|
self.input().coinbase().build()
|
||||||
}
|
}
|
||||||
|
@ -342,6 +382,32 @@ impl<F> TransactionInputBuilder<F> where F: Invoke<chain::TransactionInput> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signature_with_sigops(mut self, sigops: usize) -> Self {
|
||||||
|
let mut builder = ScriptBuilder::default();
|
||||||
|
for _ in 0..sigops {
|
||||||
|
builder = builder
|
||||||
|
.push_data(&[])
|
||||||
|
.push_data(&[])
|
||||||
|
.push_opcode(Opcode::OP_CHECKSIG);
|
||||||
|
}
|
||||||
|
self.signature = builder.into_script().into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_with_size(mut self, size: usize) -> Self {
|
||||||
|
assert!(size >= 4, "Only signatures with size > 4 are supported");
|
||||||
|
let data_size = size - 4;
|
||||||
|
let mut data = Bytes::new();
|
||||||
|
data.push(Opcode::OP_PUSHDATA4 as u8);
|
||||||
|
data.push(data_size as u8);
|
||||||
|
data.push((data_size >> 8) as u8);
|
||||||
|
data.push((data_size >> 16) as u8);
|
||||||
|
data.push((data_size >> 24) as u8);
|
||||||
|
data.extend(vec![0u8; data_size]);
|
||||||
|
self.signature = data;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hash(mut self, hash: H256) -> Self {
|
pub fn hash(mut self, hash: H256) -> Self {
|
||||||
let mut output = self.output.unwrap_or(chain::OutPoint { hash: hash.clone(), index: 0 });
|
let mut output = self.output.unwrap_or(chain::OutPoint { hash: hash.clone(), index: 0 });
|
||||||
output.hash = hash;
|
output.hash = hash;
|
||||||
|
@ -365,7 +431,7 @@ impl<F> TransactionInputBuilder<F> where F: Invoke<chain::TransactionInput> {
|
||||||
pub fn build(self) -> F::Result {
|
pub fn build(self) -> F::Result {
|
||||||
self.callback.invoke(
|
self.callback.invoke(
|
||||||
chain::TransactionInput {
|
chain::TransactionInput {
|
||||||
previous_output: self.output.unwrap_or_else(|| panic!("Building input without previous output")),
|
previous_output: self.output.expect("Building input without previous output"),
|
||||||
script_sig: self.signature,
|
script_sig: self.signature,
|
||||||
sequence: self.sequence,
|
sequence: self.sequence,
|
||||||
}
|
}
|
||||||
|
@ -384,9 +450,8 @@ impl<F> TransactionOutputBuilder<F> where F: Invoke<chain::TransactionOutput> {
|
||||||
fn with_callback(callback: F) -> Self {
|
fn with_callback(callback: F) -> Self {
|
||||||
TransactionOutputBuilder {
|
TransactionOutputBuilder {
|
||||||
callback: callback,
|
callback: callback,
|
||||||
// 0x51 is OP_1 opcode
|
// always spendable by default
|
||||||
// so the evaluation is always true
|
script_pubkey: ScriptBuilder::default().push_opcode(Opcode::OP_1).into_script().into(),
|
||||||
script_pubkey: vec![0x51].into(),
|
|
||||||
value: 0,
|
value: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,6 +471,19 @@ impl<F> TransactionOutputBuilder<F> where F: Invoke<chain::TransactionOutput> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn script_pubkey_with_sigops(mut self, sigops: usize) -> Self {
|
||||||
|
let mut builder = ScriptBuilder::default();
|
||||||
|
for _ in 0..sigops {
|
||||||
|
builder = builder
|
||||||
|
.push_data(&[])
|
||||||
|
.push_data(&[])
|
||||||
|
.push_opcode(Opcode::OP_CHECKSIG);
|
||||||
|
}
|
||||||
|
builder = builder.push_opcode(Opcode::OP_1);
|
||||||
|
self.script_pubkey = builder.into_script().into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> F::Result {
|
pub fn build(self) -> F::Result {
|
||||||
self.callback.invoke(
|
self.callback.invoke(
|
||||||
chain::TransactionOutput {
|
chain::TransactionOutput {
|
||||||
|
@ -492,6 +570,25 @@ fn example5() {
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assert_eq!(hash, "9f54dbfe94217c473e9acd5f52303d85ce1ef5e563a7e55b378ad555089fdd4d".into());
|
assert_eq!(hash, "842cee5f58ad1b1caf1896902fc62a0542188a1462372b166ca550acd7fccf1a".into());
|
||||||
assert_eq!(block.header().previous_header_hash, "0000000000000000000000000000000000000000000000000000000000000000".into());
|
assert_eq!(block.header().previous_header_hash, "0000000000000000000000000000000000000000000000000000000000000000".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_with_size() {
|
||||||
|
let block = block_builder().header().build()
|
||||||
|
.transaction().coinbase()
|
||||||
|
.output().value(10).build()
|
||||||
|
.build()
|
||||||
|
.transaction_with_size(100)
|
||||||
|
.build()
|
||||||
|
.transaction_with_size(2000)
|
||||||
|
.build()
|
||||||
|
.transaction_with_size(50000)
|
||||||
|
.build()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_eq!(block.transactions[1].serialized_size(), 100);
|
||||||
|
assert_eq!(block.transactions[2].serialized_size(), 2000);
|
||||||
|
assert_eq!(block.transactions[3].serialized_size(), 50000);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
//! Various chain-specific test dummies
|
//! Various chain-specific test dummies
|
||||||
|
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
extern crate primitives;
|
extern crate primitives;
|
||||||
extern crate serialization as ser;
|
extern crate serialization as ser;
|
||||||
extern crate time;
|
extern crate script;
|
||||||
|
|
||||||
use chain::Block;
|
use chain::Block;
|
||||||
|
|
||||||
|
|
|
@ -29,5 +29,6 @@ cargo clippy -p rpc
|
||||||
cargo clippy -p script
|
cargo clippy -p script
|
||||||
cargo clippy -p serialization
|
cargo clippy -p serialization
|
||||||
cargo clippy -p sync
|
cargo clippy -p sync
|
||||||
|
cargo clippy -p test-data
|
||||||
cargo clippy -p verification
|
cargo clippy -p verification
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,5 @@ cargo doc --no-deps\
|
||||||
-p script\
|
-p script\
|
||||||
-p serialization\
|
-p serialization\
|
||||||
-p sync\
|
-p sync\
|
||||||
|
-p test-data\
|
||||||
-p verification
|
-p verification
|
||||||
|
|
|
@ -17,4 +17,5 @@ cargo test\
|
||||||
-p script\
|
-p script\
|
||||||
-p serialization\
|
-p serialization\
|
||||||
-p sync\
|
-p sync\
|
||||||
|
-p test-data\
|
||||||
-p verification
|
-p verification
|
||||||
|
|
Loading…
Reference in New Issue