From da5fb2d0d2aad568f9d10029d58a1ed99e304545 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 18:16:48 +0300 Subject: [PATCH 1/3] flush --- rpc/src/v1/impls/raw.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index 5c2eacfe..8dc7f070 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -6,7 +6,8 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use chain::Transaction as GlobalTransaction; use sync; -use ser::{Reader, deserialize}; +use ser::{Reader, serialize, deserialize}; +use primitives::bytes::Bytes as GlobalBytes; use primitives::hash::H256 as GlobalH256; pub struct RawClient { @@ -52,8 +53,31 @@ impl Raw for RawClient where T: RawClientCoreApi { .map_err(|e| execution(e)) } - fn create_raw_transaction(&self, _inputs: Vec, _outputs: TransactionOutputs, _lock_time: Trailing) -> Result { - rpc_unimplemented!() + fn create_raw_transaction(&self, inputs: Vec, outputs: TransactionOutputs, lock_time: Trailing) -> Result { + use chain; + + let lock_time = lock_time.unwrap_or(0); + let is_locked = lock_time != 0; + let inputs: Vec<_> = inputs.into_iter() + .map(|input| chain::TransactionInput { + previous_output: chain::OutPoint { + hash: input.txid, + index: input.vout, + }, + script_sig: GlobalBytes::new(), + sequence: if is_locked { chain::SEQUENCE_FINAL - 1 } else { chain::SEQUENCE_FINAL }, + }); + let outputs: Vec<_> = outputs.into_iter() + .map(|output| chain::TransactionOutput { + + }) + let transaction = GlobalTransaction { + version: 1, + inputs: inuputs, + outputs: outputs, + lock_time: lock_time.unwrap_or(0), + }; + Ok(serialize(transaction)) } fn decode_raw_transaction(&self, _transaction: RawTransaction) -> Result { From 424d582f8568f7580d889325bd7b8d7827cc229f Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 14 Dec 2016 18:00:22 +0300 Subject: [PATCH 2/3] createrawtransaction RPC completed --- chain/src/constants.rs | 3 + rpc/src/v1/impls/raw.rs | 177 ++++++++++++++++++++++++++------ rpc/src/v1/types/address.rs | 19 +++- rpc/src/v1/types/bytes.rs | 9 ++ rpc/src/v1/types/mod.rs.in | 3 +- rpc/src/v1/types/transaction.rs | 105 +++++++++++-------- script/src/builder.rs | 25 +++++ script/src/script.rs | 14 +-- 8 files changed, 270 insertions(+), 85 deletions(-) diff --git a/chain/src/constants.rs b/chain/src/constants.rs index 72ecc5f5..de23d5d3 100644 --- a/chain/src/constants.rs +++ b/chain/src/constants.rs @@ -20,3 +20,6 @@ pub const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff; /// Threshold for `nLockTime`: below this value it is interpreted as block number, /// otherwise as UNIX timestamp. pub const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC + +/// Number of Satoshis in single coin +pub const SATOSHIS_IN_COIN: u64 = 100_000_000; diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index 8dc7f070..b52b5489 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -1,14 +1,14 @@ -use v1::traits::Raw; -use v1::types::{RawTransaction, TransactionInput, TransactionOutputs, Transaction, GetRawTransactionResponse}; -use v1::types::H256; -use v1::helpers::errors::{execution, invalid_params}; use jsonrpc_core::Error; use jsonrpc_macros::Trailing; -use chain::Transaction as GlobalTransaction; -use sync; use ser::{Reader, serialize, deserialize}; +use v1::traits::Raw; +use v1::types::{RawTransaction, TransactionInput, TransactionOutput, TransactionOutputs, Transaction, GetRawTransactionResponse}; +use v1::types::H256; +use v1::helpers::errors::{execution, invalid_params}; +use chain::Transaction as GlobalTransaction; use primitives::bytes::Bytes as GlobalBytes; use primitives::hash::H256 as GlobalH256; +use sync; pub struct RawClient { core: T, @@ -16,6 +16,7 @@ pub struct RawClient { pub trait RawClientCoreApi: Send + Sync + 'static { fn accept_transaction(&self, transaction: GlobalTransaction) -> Result; + fn create_raw_transaction(&self, inputs: Vec, outputs: TransactionOutputs, lock_time: Trailing) -> Result; } pub struct RawClientCore { @@ -28,12 +29,74 @@ impl RawClientCore { local_sync_node: local_sync_node, } } + + pub fn do_create_raw_transaction(inputs: Vec, outputs: TransactionOutputs, lock_time: Trailing) -> Result { + use chain; + use keys; + use global_script::Builder as ScriptBuilder; + + // to make lock_time work at least one input must have sequnce < SEQUENCE_FINAL + let lock_time = lock_time.0; + let default_sequence = if lock_time != 0 { chain::constants::SEQUENCE_FINAL - 1 } else { chain::constants::SEQUENCE_FINAL }; + + // prepare inputs + let inputs: Vec<_> = inputs.into_iter() + .map(|input| chain::TransactionInput { + previous_output: chain::OutPoint { + hash: Into::::into(input.txid).reversed(), + index: input.vout, + }, + script_sig: GlobalBytes::new(), // default script + sequence: input.sequence.unwrap_or(default_sequence), + }).collect(); + + // prepare outputs + let outputs: Vec<_> = outputs.outputs.into_iter() + .map(|output| match output { + TransactionOutput::Address(with_address) => { + let amount_in_satoshis = (with_address.amount * (chain::constants::SATOSHIS_IN_COIN as f64)) as u64; + let script = match (*with_address.address).kind { + keys::Type::P2PKH => ScriptBuilder::build_p2pkh(&(*with_address.address).hash), + keys::Type::P2SH => ScriptBuilder::build_p2sh(&(*with_address.address).hash), + }; + + chain::TransactionOutput { + value: amount_in_satoshis, + script_pubkey: script.to_bytes(), + } + }, + TransactionOutput::ScriptData(with_script_data) => { + let script = ScriptBuilder::default() + .return_bytes(&*with_script_data.script_data) + .into_script(); + + chain::TransactionOutput { + value: 0, + script_pubkey: script.to_bytes(), + } + }, + }).collect(); + + // now construct && serialize transaction + let transaction = GlobalTransaction { + version: 1, + inputs: inputs, + outputs: outputs, + lock_time: lock_time, + }; + + Ok(transaction) + } } impl RawClientCoreApi for RawClientCore { fn accept_transaction(&self, transaction: GlobalTransaction) -> Result { self.local_sync_node.accept_transaction(transaction) } + + fn create_raw_transaction(&self, inputs: Vec, outputs: TransactionOutputs, lock_time: Trailing) -> Result { + RawClientCore::do_create_raw_transaction(inputs, outputs, lock_time) + } } impl RawClient where T: RawClientCoreApi { @@ -54,30 +117,16 @@ impl Raw for RawClient where T: RawClientCoreApi { } fn create_raw_transaction(&self, inputs: Vec, outputs: TransactionOutputs, lock_time: Trailing) -> Result { - use chain; - - let lock_time = lock_time.unwrap_or(0); - let is_locked = lock_time != 0; + // reverse hashes of inputs let inputs: Vec<_> = inputs.into_iter() - .map(|input| chain::TransactionInput { - previous_output: chain::OutPoint { - hash: input.txid, - index: input.vout, - }, - script_sig: GlobalBytes::new(), - sequence: if is_locked { chain::SEQUENCE_FINAL - 1 } else { chain::SEQUENCE_FINAL }, - }); - let outputs: Vec<_> = outputs.into_iter() - .map(|output| chain::TransactionOutput { - - }) - let transaction = GlobalTransaction { - version: 1, - inputs: inuputs, - outputs: outputs, - lock_time: lock_time.unwrap_or(0), - }; - Ok(serialize(transaction)) + .map(|mut input| { + input.txid = input.txid.reversed(); + input + }).collect(); + + let transaction = try!(self.core.create_raw_transaction(inputs, outputs, lock_time).map_err(|e| execution(e))); + let transaction = serialize(&transaction); + Ok(transaction.into()) } fn decode_raw_transaction(&self, _transaction: RawTransaction) -> Result { @@ -106,12 +155,20 @@ pub mod tests { fn accept_transaction(&self, transaction: Transaction) -> Result { Ok(transaction.hash()) } + + fn create_raw_transaction(&self, _inputs: Vec, _outputs: TransactionOutputs, _lock_time: Trailing) -> Result { + Ok("0100000001ad9d38823d95f31dc6c0cb0724c11a3cf5a466ca4147254a10cd94aade6eb5b3230000006b483045022100b7683165c3ecd57b0c44bf6a0fb258dc08c328458321c8fadc2b9348d4e66bd502204fd164c58d1a949a4d39bb380f8f05c9f6b3e9417f06bf72e5c068428ca3578601210391c35ac5ee7cf82c5015229dcff89507f83f9b8c952b8fecfa469066c1cb44ccffffffff0170f30500000000001976a914801da3cb2ed9e44540f4b982bde07cd3fbae264288ac00000000".into()) + } } impl RawClientCoreApi for ErrorRawClientCore { fn accept_transaction(&self, _transaction: Transaction) -> Result { Err("error".to_owned()) } + + fn create_raw_transaction(&self, _inputs: Vec, _outputs: TransactionOutputs, _lock_time: Trailing) -> Result { + Err("error".to_owned()) + } } #[test] @@ -151,4 +208,66 @@ pub mod tests { assert_eq!(r#"{"jsonrpc":"2.0","error":{"code":-32015,"message":"Execution error.","data":"\"error\""},"id":1}"#, &sample); } + + #[test] + fn createrawtransaction_contents() { + use chain; + use primitives::bytes::Bytes as GlobalBytes; + use v1::types::{TransactionInput, TransactionOutput, TransactionOutputs, TransactionOutputWithAddress}; + + // https://webbtc.com/tx/4dbbc65cf8eff9a04752bf493232e0b82488308f72f2afb497f36bbddada500c + let mut original_transaction: chain::Transaction = "0100000001ad9d38823d95f31dc6c0cb0724c11a3cf5a466ca4147254a10cd94aade6eb5b3230000006b483045022100b7683165c3ecd57b0c44bf6a0fb258dc08c328458321c8fadc2b9348d4e66bd502204fd164c58d1a949a4d39bb380f8f05c9f6b3e9417f06bf72e5c068428ca3578601210391c35ac5ee7cf82c5015229dcff89507f83f9b8c952b8fecfa469066c1cb44ccffffffff0170f30500000000001976a914801da3cb2ed9e44540f4b982bde07cd3fbae264288ac00000000".into(); + // since createrawtransction creates unsigned transaction: + original_transaction.inputs[0].script_sig = GlobalBytes::new(); + + let inputs = vec![TransactionInput { + txid: "b3b56edeaa94cd104a254741ca66a4f53c1ac12407cbc0c61df3953d82389dad".into(), + vout: 35, + sequence: None, + }]; + let outputs = TransactionOutputs { + outputs: vec![TransactionOutput::Address(TransactionOutputWithAddress { + address: "1CgQzxyMMrtoDEBJPtFUkZ5zHcZiDSFtC8".into(), + amount: 0.00390000, + })] + }; + let raw_transaction = RawClientCore::do_create_raw_transaction(inputs, outputs, Trailing(0)).unwrap(); + assert_eq!(raw_transaction, original_transaction); + } + + #[test] + fn createrawtransaction_success() { + let client = RawClient::new(SuccessRawClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "createrawtransaction", + "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], + "id": 1 + }"#) + ).unwrap(); + + assert_eq!(r#"{"jsonrpc":"2.0","result":"0100000001ad9d38823d95f31dc6c0cb0724c11a3cf5a466ca4147254a10cd94aade6eb5b3230000006b483045022100b7683165c3ecd57b0c44bf6a0fb258dc08c328458321c8fadc2b9348d4e66bd502204fd164c58d1a949a4d39bb380f8f05c9f6b3e9417f06bf72e5c068428ca3578601210391c35ac5ee7cf82c5015229dcff89507f83f9b8c952b8fecfa469066c1cb44ccffffffff0170f30500000000001976a914801da3cb2ed9e44540f4b982bde07cd3fbae264288ac00000000","id":1}"#, &sample); + } + + #[test] + fn createrawtransaction_error() { + let client = RawClient::new(ErrorRawClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "createrawtransaction", + "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], + "id": 1 + }"#) + ).unwrap(); + + assert_eq!(r#"{"jsonrpc":"2.0","error":{"code":-32015,"message":"Execution error.","data":"\"error\""},"id":1}"#, &sample); + } } diff --git a/rpc/src/v1/types/address.rs b/rpc/src/v1/types/address.rs index f5e5101b..7a89e4d7 100644 --- a/rpc/src/v1/types/address.rs +++ b/rpc/src/v1/types/address.rs @@ -1,3 +1,4 @@ +use std::ops; use std::str::FromStr; use serde::{Serialize, Deserialize, Serializer, Deserializer}; use global_script::ScriptAddress; @@ -22,6 +23,12 @@ impl Address { kind: address.kind, }) } + + pub fn deserialize_from_string(value: &str) -> Result where E: ::serde::de::Error { + GlobalAddress::from_str(value) + .map_err(|err| E::invalid_value(&format!("error {} parsing address {}", err, value))) + .map(|address| Address(address)) + } } impl Serialize for Address { @@ -40,9 +47,7 @@ impl Deserialize for Address { type Value = Address; fn visit_str(&mut self, value: &str) -> Result where E: ::serde::de::Error { - GlobalAddress::from_str(value) - .map_err(|err| E::invalid_value(&format!("error {} parsing address {}", err, value))) - .map(|address| Address(address)) + Address::deserialize_from_string(value) } } @@ -56,6 +61,14 @@ impl From for Address where GlobalAddress: From { } } +impl ops::Deref for Address { + type Target = GlobalAddress; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[cfg(test)] mod tests { use serde_json; diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index f0bd94b5..f8191c89 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -1,4 +1,5 @@ ///! Serializable wrapper around vector of bytes +use std::ops; use rustc_serialize::hex::{ToHex, FromHex}; use serde::{Serialize, Serializer, Deserialize, Deserializer, Error}; use serde::de::Visitor; @@ -66,6 +67,14 @@ impl Visitor for BytesVisitor { } } +impl ops::Deref for Bytes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 5d1fe7b6..90f64e85 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -23,7 +23,8 @@ pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::{H160, H256}; pub use self::script::ScriptType; pub use self::transaction::{RawTransaction, Transaction, TransactionInput, TransactionOutput, - TransactionInputScript, TransactionOutputScript, SignedTransactionInput, GetRawTransactionResponse, + TransactionOutputWithAddress, TransactionOutputWithScriptData, TransactionInputScript, + TransactionOutputScript, SignedTransactionInput, GetRawTransactionResponse, SignedTransactionOutput, TransactionOutputs}; pub use self::uint::U256; pub use self::nodes::{AddNodeOperation, NodeInfo}; diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 0305c9fe..30a1ebe9 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,15 +19,31 @@ pub struct TransactionInput { pub sequence: Option, } -/// Transaction output -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub struct TransactionOutput { +/// Transaction output of form "address": amount +#[derive(Debug, PartialEq)] +pub struct TransactionOutputWithAddress { /// Receiver' address pub address: Address, /// Amount in BTC pub amount: f64, } +/// Trasaction output of form "data": serialized(output script data) +#[derive(Debug, PartialEq)] +pub struct TransactionOutputWithScriptData { + /// Serialized script data + pub script_data: Bytes, +} + +/// Transaction output +#[derive(Debug, PartialEq)] +pub enum TransactionOutput { + /// Of form address: amount + Address(TransactionOutputWithAddress), + /// Of form data: script_data_bytes + ScriptData(TransactionOutputWithScriptData), +} + /// Transaction outputs, which serializes/deserializes as KV-map #[derive(Debug, PartialEq)] pub struct TransactionOutputs { @@ -147,8 +163,16 @@ impl Serialize for TransactionOutputs { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { let mut state = try!(serializer.serialize_map(Some(self.len()))); for output in &self.outputs { - try!(serializer.serialize_map_key(&mut state, &output.address)); - try!(serializer.serialize_map_value(&mut state, &output.amount)); + match output { + &TransactionOutput::Address(ref address_output) => { + try!(serializer.serialize_map_key(&mut state, &address_output.address)); + try!(serializer.serialize_map_value(&mut state, &address_output.amount)); + }, + &TransactionOutput::ScriptData(ref script_output) => { + try!(serializer.serialize_map_key(&mut state, "data")); + try!(serializer.serialize_map_value(&mut state, &script_output.script_data)); + }, + } } serializer.serialize_map_end(state) } @@ -166,11 +190,20 @@ impl Deserialize for TransactionOutputs { fn visit_map(&mut self, mut visitor: V) -> Result where V: MapVisitor { let mut outputs: Vec = Vec::with_capacity(visitor.size_hint().0); - while let Some((address, amount)) = try!(visitor.visit()) { - outputs.push(TransactionOutput { - address: address, - amount: amount, - }); + while let Some(key) = try!(visitor.visit_key::()) { + if &key == "data" { + let value: Bytes = try!(visitor.visit_value()); + outputs.push(TransactionOutput::ScriptData(TransactionOutputWithScriptData { + script_data: value, + })); + } else { + let address = try!(Address::deserialize_from_string(&key)); + let amount: f64 = try!(visitor.visit_value()); + outputs.push(TransactionOutput::Address(TransactionOutputWithAddress { + address: address, + amount: amount, + })); + } } try!(visitor.end()); @@ -215,59 +248,51 @@ mod tests { txinput); } - #[test] - fn transaction_output_serialize() { - let txout = TransactionOutput { - address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), - amount: 123.45, - }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","amount":123.45}"#); - } - - #[test] - fn transaction_output_deserialize() { - let txout = TransactionOutput { - address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), - amount: 123.45, - }; - assert_eq!( - serde_json::from_str::(r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","amount":123.45}"#).unwrap(), - txout); - } - #[test] fn transaction_outputs_serialize() { let txout = TransactionOutputs { outputs: vec![ - TransactionOutput { + TransactionOutput::Address(TransactionOutputWithAddress { address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), amount: 123.45, - }, - TransactionOutput { + }), + TransactionOutput::Address(TransactionOutputWithAddress { address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), amount: 67.89, - }, + }), + TransactionOutput::ScriptData(TransactionOutputWithScriptData { + script_data: Bytes::new(vec![1, 2, 3, 4]), + }), + TransactionOutput::ScriptData(TransactionOutputWithScriptData { + script_data: Bytes::new(vec![5, 6, 7, 8]), + }), ] }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89}"#); + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#); } #[test] fn transaction_outputs_deserialize() { let txout = TransactionOutputs { outputs: vec![ - TransactionOutput { + TransactionOutput::Address(TransactionOutputWithAddress { address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), amount: 123.45, - }, - TransactionOutput { + }), + TransactionOutput::Address(TransactionOutputWithAddress { address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), amount: 67.89, - }, + }), + TransactionOutput::ScriptData(TransactionOutputWithScriptData { + script_data: Bytes::new(vec![1, 2, 3, 4]), + }), + TransactionOutput::ScriptData(TransactionOutputWithScriptData { + script_data: Bytes::new(vec![5, 6, 7, 8]), + }), ] }; assert_eq!( - serde_json::from_str::(r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89}"#).unwrap(), + serde_json::from_str::(r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89,"data":"01020304","data":"05060708"}"#).unwrap(), txout); } diff --git a/script/src/builder.rs b/script/src/builder.rs index 820568e1..7b5e36b6 100644 --- a/script/src/builder.rs +++ b/script/src/builder.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use {Opcode, Script, Num}; +use keys::AddressHash; #[derive(Default)] pub struct Builder { @@ -7,6 +8,24 @@ pub struct Builder { } impl Builder { + pub fn build_p2pkh(address: &AddressHash) -> Script { + Builder::default() + .push_opcode(Opcode::OP_DUP) + .push_opcode(Opcode::OP_HASH160) + .push_bytes(&**address) + .push_opcode(Opcode::OP_EQUALVERIFY) + .push_opcode(Opcode::OP_CHECKSIG) + .into_script() + } + + pub fn build_p2sh(address: &AddressHash) -> Script { + Builder::default() + .push_opcode(Opcode::OP_HASH160) + .push_bytes(&**address) + .push_opcode(Opcode::OP_EQUAL) + .into_script() + } + pub fn push_opcode(mut self, opcode: Opcode) -> Self { self.data.push(opcode as u8); self @@ -63,6 +82,12 @@ impl Builder { self } + pub fn return_bytes(mut self, bytes: &[u8]) -> Self { + self.data.push(Opcode::OP_RETURN as u8); + self.data.extend_from_slice(bytes); + self + } + pub fn push_invalid_opcode(mut self) -> Self { self.data.push(0xff); self diff --git a/script/src/script.rs b/script/src/script.rs index e9933128..3e2f4b4b 100644 --- a/script/src/script.rs +++ b/script/src/script.rs @@ -699,13 +699,7 @@ OP_ADD #[test] fn test_extract_destinations_pub_key_hash() { let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; - let script = Builder::default() - .push_opcode(Opcode::OP_DUP) - .push_opcode(Opcode::OP_HASH160) - .push_bytes(&*address) - .push_opcode(Opcode::OP_EQUALVERIFY) - .push_opcode(Opcode::OP_CHECKSIG) - .into_script(); + let script = Builder::build_p2pkh(&address); assert_eq!(script.script_type(), ScriptType::PubKeyHash); assert_eq!(script.extract_destinations(), Ok(vec![ ScriptAddress::new_p2pkh(address), @@ -715,11 +709,7 @@ OP_ADD #[test] fn test_extract_destinations_script_hash() { let address = Address::from("13NMTpfNVVJQTNH4spP4UeqBGqLdqDo27S").hash; - let script = Builder::default() - .push_opcode(Opcode::OP_HASH160) - .push_bytes(&*address) - .push_opcode(Opcode::OP_EQUAL) - .into_script(); + let script = Builder::build_p2sh(&address); assert_eq!(script.script_type(), ScriptType::ScriptHash); assert_eq!(script.extract_destinations(), Ok(vec![ ScriptAddress::new_p2sh(address), From d1a28b067609ddc3643bcc8e32c9232137918417 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 14 Dec 2016 18:20:08 +0300 Subject: [PATCH 3/3] fixed compilation --- rpc/src/v1/impls/raw.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index b52b5489..4df7ff3e 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -140,10 +140,12 @@ impl Raw for RawClient where T: RawClientCoreApi { #[cfg(test)] pub mod tests { + use jsonrpc_macros::Trailing; use jsonrpc_core::{IoHandler, GenericIoHandler}; use chain::Transaction; use primitives::hash::H256 as GlobalH256; use v1::traits::Raw; + use v1::types::{TransactionInput, TransactionOutputs}; use super::*; #[derive(Default)]