finished gettxout implementation

This commit is contained in:
Svyatoslav Nikolsky 2016-12-12 21:49:01 +03:00
parent 4aa85a7261
commit 9e1932e7f9
16 changed files with 325 additions and 54 deletions

1
Cargo.lock generated
View File

@ -804,6 +804,7 @@ dependencies = [
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
"keys 0.1.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miner 0.1.0",
"network 0.1.0",

View File

@ -35,6 +35,7 @@ pub use self::error::Error;
pub use self::private::Private;
pub use self::public::Public;
pub use self::signature::{Signature, CompactSignature};
pub use self::network::Network;
use hash::{H160, H256};

View File

@ -34,10 +34,12 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
};
let sync_handle = el.handle();
let local_sync_node = create_local_sync_node(&sync_handle, cfg.magic, db);
let local_sync_node = create_local_sync_node(&sync_handle, cfg.magic, db.clone());
let sync_connection_factory = create_sync_connection_factory(local_sync_node.clone());
let rpc_deps = rpc::Dependencies {
network: cfg.magic,
storage: db,
local_sync_node: local_sync_node,
};
let _rpc_server = try!(rpc::new_http(cfg.rpc_config, rpc_deps));

View File

@ -1,11 +1,15 @@
use std::net::SocketAddr;
use rpc_apis::{self, ApiSet};
use ethcore_rpc::{Server, RpcServer, RpcServerError};
use network::Magic;
use std::io;
use sync;
use db;
pub struct Dependencies {
pub network: Magic,
pub local_sync_node: sync::LocalNodeRef,
pub storage: db::SharedStore,
}
#[derive(Debug, PartialEq)]

View File

@ -5,10 +5,12 @@ use ethcore_rpc::Extendable;
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum Api {
/// Raw
/// Raw methods
Raw,
/// Miner
/// Miner-related methods
Miner,
/// BlockChain-related methods
BlockChain,
}
#[derive(Debug, PartialEq, Eq)]
@ -18,7 +20,7 @@ pub enum ApiSet {
impl Default for ApiSet {
fn default() -> Self {
ApiSet::List(vec![Api::Raw].into_iter().collect())
ApiSet::List(vec![Api::Raw, Api::Miner, Api::BlockChain].into_iter().collect())
}
}
@ -29,6 +31,7 @@ impl FromStr for Api {
match s {
"raw" => Ok(Api::Raw),
"miner" => Ok(Api::Miner),
"blockchain" => Ok(Api::BlockChain),
api => Err(format!("Unknown api: {}", api)),
}
}
@ -49,6 +52,7 @@ pub fn setup_rpc<T: Extendable>(server: T, apis: ApiSet, deps: Dependencies) ->
match api {
Api::Raw => server.add_delegate(RawClient::new(RawClientCore::new(deps.local_sync_node.clone())).to_delegate()),
Api::Miner => server.add_delegate(MinerClient::new(MinerClientCore::new(deps.local_sync_node.clone())).to_delegate()),
Api::BlockChain => server.add_delegate(BlockChainClient::new(BlockChainClientCore::new(deps.network, deps.storage.clone())).to_delegate()),
}
}
server

View File

@ -29,6 +29,7 @@ test-data = { path = "../test-data" }
miner = { path = "../miner" }
verification = { path = "../verification" }
script = { path = "../script" }
keys = { path = "../keys" }
[build-dependencies]
serde_codegen = { version = "0.8.0", optional = true }

View File

@ -23,7 +23,8 @@ extern crate miner;
extern crate verification;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
extern crate script;
extern crate script as global_script;
extern crate keys;
pub mod v1;
pub mod rpc_server;

View File

@ -4,18 +4,18 @@ use v1::types::{GetTxOutResponse, TxOutScriptPubKey};
use v1::types::GetTxOutSetInfoResponse;
use v1::types::H256;
use v1::types::U256;
use v1::types::ScriptType;
use v1::types::Address;
use v1::helpers::errors::{block_not_found, block_at_height_not_found, transaction_not_found,
transaction_output_not_found, transaction_of_side_branch};
use jsonrpc_macros::Trailing;
use jsonrpc_core::Error;
use db;
use script::Script;
use global_script::Script;
use chain::OutPoint;
use verification;
use ser::serialize;
use primitives::hash::H256 as GlobalH256;
use network::Magic;
pub struct BlockChainClient<T: BlockChainClientCoreApi> {
core: T,
@ -31,14 +31,16 @@ pub trait BlockChainClientCoreApi: Send + Sync + 'static {
}
pub struct BlockChainClientCore {
network: Magic,
storage: db::SharedStore,
}
impl BlockChainClientCore {
pub fn new(storage: db::SharedStore) -> Self {
pub fn new(network: Magic, storage: db::SharedStore) -> Self {
assert!(storage.best_block().is_some());
BlockChainClientCore {
network: network,
storage: storage,
}
}
@ -130,6 +132,7 @@ impl BlockChainClientCoreApi for BlockChainClientCore {
let ref script_bytes = transaction.outputs[prev_out.index as usize].script_pubkey;
let script: Script = script_bytes.clone().into();
let script_asm = format!("{}", script);
let script_addresses = script.extract_destinations().unwrap_or(vec![]);
Ok(GetTxOutResponse {
bestblock: block_header.hash().into(),
@ -138,9 +141,9 @@ impl BlockChainClientCoreApi for BlockChainClientCore {
script_pub_key: TxOutScriptPubKey {
asm: script_asm,
hex: script_bytes.clone().into(),
req_sigs: 0, // TODO
script_type: ScriptType::NonStandard, // TODO
addresses: vec![],
req_sigs: script.num_signatures_required() as u32,
script_type: script.script_type().into(),
addresses: script_addresses.into_iter().map(|a| Address::new(self.network, a)).collect(),
},
version: transaction.version,
coinbase: transaction.is_coinbase(),
@ -193,7 +196,9 @@ impl<T> BlockChain for BlockChainClient<T> where T: BlockChainClientCoreApi {
}
fn transaction_out(&self, transaction_hash: H256, out_index: u32, _include_mempool: Trailing<bool>) -> Result<GetTxOutResponse, Error> {
self.core.verbose_transaction_out(OutPoint { hash: transaction_hash.into(), index: out_index })
// TODO: include_mempool
let transaction_hash: GlobalH256 = transaction_hash.into();
self.core.verbose_transaction_out(OutPoint { hash: transaction_hash.reversed(), index: out_index })
.map(|mut response| {
response.bestblock = response.bestblock.reversed();
response
@ -218,8 +223,11 @@ pub mod tests {
use v1::traits::BlockChain;
use v1::types::GetTxOutResponse;
use v1::helpers::errors::block_not_found;
use v1::types::Bytes;
use v1::types::ScriptType;
use chain::OutPoint;
use test_data;
use network::Magic;
use super::*;
#[derive(Default)]
@ -272,7 +280,20 @@ pub mod tests {
}
fn verbose_transaction_out(&self, _prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
Ok(GetTxOutResponse::default()) // TODO: non-default
Ok(GetTxOutResponse {
bestblock: H256::from(0x56),
confirmations: 777,
value: 100000.56,
script_pub_key: TxOutScriptPubKey {
asm: "Hello, world!!!".to_owned(),
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
},
version: 33,
coinbase: false,
})
}
}
@ -382,7 +403,7 @@ pub mod tests {
storage.insert_block(&test_data::block_h1()).expect("no error");
storage.insert_block(&test_data::block_h2()).expect("no error");
let core = BlockChainClientCore::new(storage);
let core = BlockChainClientCore::new(Magic::Mainnet, storage);
// get info on block #1:
// https://blockexplorer.com/block/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048
@ -515,4 +536,65 @@ pub mod tests {
assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block with given hash is not found","data":"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"},"id":1}"#);
}
#[test]
fn verbose_transaction_out_contents() {
let storage = Arc::new(db::TestStorage::with_genesis_block());
let core = BlockChainClientCore::new(Magic::Mainnet, storage);
// get info on tx from genesis block:
// https://blockchain.info/ru/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
let verbose_transaction_out = core.verbose_transaction_out(OutPoint {
hash: "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a".into(),
index: 0,
});
assert_eq!(verbose_transaction_out, Ok(GetTxOutResponse {
bestblock: "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000".into(),
confirmations: 1,
value: 50.0,
script_pub_key: TxOutScriptPubKey {
asm: "OP_PUSHBYTES_65 0x04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f\nOP_CHECKSIG\n".to_owned(),
hex: Bytes::from("4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"),
req_sigs: 1,
script_type: ScriptType::PubKey,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into()]
},
version: 1,
coinbase: true
}));
}
#[test]
fn transaction_out_success() {
let client = BlockChainClient::new(SuccessBlockChainClientCore::default());
let handler = IoHandler::new();
handler.add_delegate(client.to_delegate());
let sample = handler.handle_request_sync(&(r#"
{
"jsonrpc": "2.0",
"method": "gettxout",
"params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", 0],
"id": 1
}"#)).unwrap();
assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bestblock":"0000000000000000000000000000000000000000000000000000000000000056","coinbase":false,"confirmations":777,"scriptPubKey":{"addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"],"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig"},"value":100000.56,"version":33},"id":1}"#);
}
#[test]
fn transaction_out_failure() {
let client = BlockChainClient::new(ErrorBlockChainClientCore::default());
let handler = IoHandler::new();
handler.add_delegate(client.to_delegate());
let sample = handler.handle_request_sync(&(r#"
{
"jsonrpc": "2.0",
"method": "gettxout",
"params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", 0],
"id": 1
}"#)).unwrap();
assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block with given hash is not found","data":"3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"},"id":1}"#);
}
}

View File

@ -6,5 +6,7 @@ pub mod types;
pub use self::traits::Raw;
pub use self::traits::Miner;
pub use self::traits::BlockChain;
pub use self::impls::{RawClient, RawClientCore};
pub use self::impls::{MinerClient, MinerClientCore};
pub use self::impls::{BlockChainClient, BlockChainClientCore};

View File

@ -0,0 +1,76 @@
use std::str::FromStr;
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use global_script::ScriptAddress;
use keys::Address as GlobalAddress;
use keys::Network as KeysNetwork;
use network::Magic;
/// Bitcoin address
#[derive(Debug, PartialEq)]
pub struct Address(GlobalAddress);
impl Address {
pub fn new(network: Magic, address: ScriptAddress) -> Self {
Address(GlobalAddress {
network: match network {
Magic::Mainnet => KeysNetwork::Mainnet,
// there's no correct choices for Regtests && Other networks
// => let's just make Testnet key
_ => KeysNetwork::Testnet,
},
hash: address.hash,
kind: address.kind,
})
}
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
serializer.serialize_str(&self.0.to_string())
}
}
impl Deserialize for Address {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
use serde::de::Visitor;
struct AddressVisitor;
impl Visitor for AddressVisitor {
type Value = Address;
fn visit_str<E>(&mut self, value: &str) -> Result<Address, E> 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))
}
}
deserializer.deserialize(AddressVisitor)
}
}
impl<T> From<T> for Address where GlobalAddress: From<T> {
fn from(o: T) -> Self {
Address(GlobalAddress::from(o))
}
}
#[cfg(test)]
mod tests {
use serde_json;
use super::Address;
#[test]
fn address_serialize() {
let address: Address = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into();
assert_eq!(serde_json::to_string(&address).unwrap(), r#""1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa""#);
}
#[test]
fn address_deserialize() {
let address: Address = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into();
assert_eq!(serde_json::from_str::<Address>(r#""1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa""#).unwrap(), address);
assert!(serde_json::from_str::<Address>(r#""DEADBEEF""#).is_err());
}
}

View File

@ -20,15 +20,9 @@ impl Bytes {
}
}
impl From<GlobalBytes> for Bytes {
fn from(v: GlobalBytes) -> Self {
Bytes(v.take())
}
}
impl From<Vec<u8>> for Bytes {
fn from(bytes: Vec<u8>) -> Bytes {
Bytes(bytes)
impl<T> From<T> for Bytes where GlobalBytes: From<T> {
fn from(other: T) -> Self {
Bytes(GlobalBytes::from(other).take())
}
}

View File

@ -1,9 +1,10 @@
use super::address::Address;
use super::bytes::Bytes;
use super::hash::{H160, H256};
use super::hash::H256;
use super::script::ScriptType;
/// gettxout response
#[derive(Debug, Default, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct GetTxOutResponse {
/// Hash of the block this transaction output is included into.
/// Why it's called 'best'? Who knows
@ -22,7 +23,7 @@ pub struct GetTxOutResponse {
}
/// Script pub key information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct TxOutScriptPubKey {
/// Script code
pub asm: String,
@ -35,18 +36,81 @@ pub struct TxOutScriptPubKey {
#[serde(rename = "type")]
pub script_type: ScriptType,
/// Array of bitcoin addresses
pub addresses: Vec<H160>,
pub addresses: Vec<Address>,
}
// TODO: remove me
impl Default for TxOutScriptPubKey {
fn default() -> Self {
TxOutScriptPubKey {
asm: String::default(),
hex: Bytes::default(),
req_sigs: u32::default(),
script_type: ScriptType::NonStandard,
addresses: vec![],
}
#[cfg(test)]
mod tests {
use serde_json;
use super::super::bytes::Bytes;
use super::super::hash::H256;
use super::super::script::ScriptType;
use super::*;
#[test]
fn tx_out_response_serialize() {
let txout = GetTxOutResponse {
bestblock: H256::from(0x56),
confirmations: 777,
value: 100000.56,
script_pub_key: TxOutScriptPubKey {
asm: "Hello, world!!!".to_owned(),
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
},
version: 33,
coinbase: false,
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#);
}
#[test]
fn tx_out_response_deserialize() {
let txout = GetTxOutResponse {
bestblock: H256::from(0x56),
confirmations: 777,
value: 100000.56,
script_pub_key: TxOutScriptPubKey {
asm: "Hello, world!!!".to_owned(),
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
},
version: 33,
coinbase: false,
};
assert_eq!(
serde_json::from_str::<GetTxOutResponse>(r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#).unwrap(),
txout);
}
#[test]
fn tx_out_script_pubkey_serialize() {
let txout = TxOutScriptPubKey {
asm: "Hello, world!!!".to_owned(),
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
};
assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#);
}
#[test]
fn tx_out_script_pubkey_deserialize() {
let txout = TxOutScriptPubKey {
asm: "Hello, world!!!".to_owned(),
hex: Bytes::new(vec![1, 2, 3, 4]),
req_sigs: 777,
script_type: ScriptType::Multisig,
addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()],
};
assert_eq!(
serde_json::from_str::<TxOutScriptPubKey>(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#).unwrap(),
txout);
}
}

View File

@ -1,3 +1,4 @@
mod address;
mod block_template;
mod block_template_request;
mod bytes;
@ -10,13 +11,14 @@ mod raw_transaction;
mod script;
mod uint;
pub use self::address::Address;
pub use self::block_template::{BlockTemplate, BlockTemplateTransaction};
pub use self::block_template_request::{BlockTemplateRequest, BlockTemplateRequestMode};
pub use self::bytes::Bytes;
pub use self::get_block_response::{GetBlockResponse, VerboseBlock};
pub use self::get_tx_out_response::{GetTxOutResponse, TxOutScriptPubKey};
pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse;
pub use self::hash::H256;
pub use self::hash::{H160, H256};
pub use self::raw_block::RawBlock;
pub use self::raw_transaction::RawTransaction;
pub use self::script::ScriptType;

View File

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use script::ScriptType as GlobalScriptType;
use global_script::ScriptType as GlobalScriptType;
#[derive(Debug, PartialEq)]
pub enum ScriptType {

View File

@ -25,7 +25,7 @@ pub use self::flags::VerificationFlags;
pub use self::interpreter::{eval_script, verify_script};
pub use self::opcode::Opcode;
pub use self::num::Num;
pub use self::script::{Script, ScriptType};
pub use self::script::{Script, ScriptType, ScriptAddress};
pub use self::sign::{
TransactionInputSigner, UnsignedTransactionInput,
Sighash, SighashBase, SignatureVersion

View File

@ -29,6 +29,33 @@ pub enum ScriptType {
WitnessKey,
}
/// Address from Script
#[derive(PartialEq, Debug)]
pub struct ScriptAddress {
/// The type of the address.
pub kind: keys::Type,
/// Public key hash.
pub hash: AddressHash,
}
impl ScriptAddress {
/// Creates P2PKH-type ScriptAddress
pub fn new_p2pkh(hash: AddressHash) -> Self {
ScriptAddress {
kind: keys::Type::P2PKH,
hash: hash,
}
}
/// Creates P2SH-type ScriptAddress
pub fn new_p2sh(hash: AddressHash) -> Self {
ScriptAddress {
kind: keys::Type::P2SH,
hash: hash,
}
}
}
/// Serialized script, used inside transaction inputs and outputs.
#[derive(PartialEq, Debug)]
pub struct Script {
@ -350,7 +377,7 @@ impl Script {
return 1;
}
pub fn extract_destinations(&self) -> Result<Vec<AddressHash>, keys::Error> {
pub fn extract_destinations(&self) -> Result<Vec<ScriptAddress>, keys::Error> {
match self.script_type() {
ScriptType::NonStandard => {
Ok(vec![])
@ -361,26 +388,26 @@ impl Script {
x if x == Opcode::OP_PUSHBYTES_65 as u8 => &self.data[1..66],
_ => unreachable!(), // because we are relying on script_type() checks here
})
.map(|public| vec![public.address_hash()])
.map(|public| vec![ScriptAddress::new_p2pkh(public.address_hash())])
},
ScriptType::PubKeyHash => {
Ok(vec![
self.data[3..23].into()
ScriptAddress::new_p2pkh(self.data[3..23].into()),
])
},
ScriptType::ScriptHash => {
Ok(vec![
self.data[2..22].into()
ScriptAddress::new_p2sh(self.data[2..22].into()),
])
},
ScriptType::Multisig => {
let mut addresses: Vec<AddressHash> = Vec::new();
let mut addresses: Vec<ScriptAddress> = Vec::new();
let mut pc = 1;
while pc < self.len() - 2 {
let instruction = self.get_instruction(pc).expect("this method depends on previous check in script_type()");
let data = instruction.data.expect("this method depends on previous check in script_type()");
let address = try!(Public::from_slice(data)).address_hash();
addresses.push(address);
addresses.push(ScriptAddress::new_p2pkh(address));
pc += instruction.step;
}
Ok(addresses)
@ -516,7 +543,7 @@ impl fmt::Display for Script {
#[cfg(test)]
mod tests {
use {Builder, Opcode};
use super::{Script, ScriptType, MAX_SCRIPT_ELEMENT_SIZE};
use super::{Script, ScriptType, ScriptAddress, MAX_SCRIPT_ELEMENT_SIZE};
use keys::{Address, Public};
#[test]
@ -650,7 +677,9 @@ OP_ADD
.push_opcode(Opcode::OP_CHECKSIG)
.into_script();
assert_eq!(script.script_type(), ScriptType::PubKey);
assert_eq!(script.extract_destinations(), Ok(vec![address]));
assert_eq!(script.extract_destinations(), Ok(vec![
ScriptAddress::new_p2pkh(address),
]));
}
#[test]
@ -662,7 +691,9 @@ OP_ADD
.push_opcode(Opcode::OP_CHECKSIG)
.into_script();
assert_eq!(script.script_type(), ScriptType::PubKey);
assert_eq!(script.extract_destinations(), Ok(vec![address]));
assert_eq!(script.extract_destinations(), Ok(vec![
ScriptAddress::new_p2pkh(address),
]));
}
#[test]
@ -676,7 +707,9 @@ OP_ADD
.push_opcode(Opcode::OP_CHECKSIG)
.into_script();
assert_eq!(script.script_type(), ScriptType::PubKeyHash);
assert_eq!(script.extract_destinations(), Ok(vec![address]));
assert_eq!(script.extract_destinations(), Ok(vec![
ScriptAddress::new_p2pkh(address),
]));
}
#[test]
@ -688,7 +721,9 @@ OP_ADD
.push_opcode(Opcode::OP_EQUAL)
.into_script();
assert_eq!(script.script_type(), ScriptType::ScriptHash);
assert_eq!(script.extract_destinations(), Ok(vec![address]));
assert_eq!(script.extract_destinations(), Ok(vec![
ScriptAddress::new_p2sh(address),
]));
}
#[test]
@ -705,7 +740,10 @@ OP_ADD
.push_opcode(Opcode::OP_CHECKMULTISIG)
.into_script();
assert_eq!(script.script_type(), ScriptType::Multisig);
assert_eq!(script.extract_destinations(), Ok(vec![address1, address2]));
assert_eq!(script.extract_destinations(), Ok(vec![
ScriptAddress::new_p2pkh(address1),
ScriptAddress::new_p2pkh(address2),
]));
}
#[test]
@ -729,6 +767,5 @@ OP_ADD
.into_script();
assert_eq!(script.script_type(), ScriptType::ScriptHash);
assert_eq!(script.num_signatures_required(), 1);
}
}