Browse Source

Add scriptpubkey_address to transaction outputs

liquid_e
Nadav Ivgi 6 years ago
committed by Lawrence Nahum
parent
commit
48e0013e58
  1. 2
      Cargo.toml
  2. 2
      src/lib.rs
  3. 31
      src/rest.rs
  4. 58
      src/util.rs

2
Cargo.toml

@ -14,6 +14,7 @@ readme = "README.md"
arrayref = "0.3"
base64 = "0.9"
bincode = "1.0"
bitcoin-bech32 = "0.8.0"
chan = "0.1"
chan-signal = "0.3"
clap = "2.31"
@ -29,6 +30,7 @@ page_size = "0.4"
prometheus = "0.4"
rocksdb = "0.10.1"
rust-crypto = "0.2"
secp256k1 = "0.11"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"

2
src/lib.rs

@ -3,6 +3,7 @@
extern crate base64;
extern crate bincode;
extern crate bitcoin;
extern crate bitcoin_bech32;
extern crate elements;
extern crate chan_signal;
extern crate crypto;
@ -15,6 +16,7 @@ extern crate num_cpus;
extern crate page_size;
extern crate prometheus;
extern crate rocksdb;
extern crate secp256k1;
extern crate serde;
extern crate stderrlog;
extern crate sysconf;

31
src/rest.rs

@ -19,7 +19,8 @@ use std::num::ParseIntError;
use std::thread;
use std::sync::{Arc,Mutex};
use url::form_urlencoded;
use util::HeaderEntry;
use daemon::Network;
use util::{HeaderEntry,script_to_address};
#[derive(Serialize, Deserialize)]
struct BlockValue {
@ -127,6 +128,7 @@ impl From<TxIn> for TxInValue {
struct TxOutValue {
scriptpubkey_hex: Script,
scriptpubkey_asm: String,
scriptpubkey_address: Option<String>,
asset: Option<String>,
assetcommitment: Option<String>,
value: Option<u64>,
@ -173,6 +175,7 @@ impl From<TxOut> for TxOutValue {
TxOutValue {
scriptpubkey_hex: script,
scriptpubkey_asm: (&script_asm[7..script_asm.len()-1]).to_string(),
scriptpubkey_address: None, // added later
asset,
assetcommitment,
value,
@ -181,19 +184,36 @@ impl From<TxOut> for TxOutValue {
}
}
pub fn run_server(_config: &Config, query: Arc<Query>) {
// @XXX we should ideally set the scriptpubkey_address inside TxOutValue::from(), but it
// cannot easily access the Network, so for now, we attach it later with mutation instead
fn attach_addresses_tx(tx: &mut TransactionValue, network: &Network) {
for mut vout in tx.vout.iter_mut() {
vout.scriptpubkey_address = script_to_address(&vout.scriptpubkey_hex, &network);
}
}
fn attach_addresses_txs(txs: &mut Vec<TransactionValue>, network: &Network) {
for mut tx in txs.iter_mut() {
attach_addresses_tx(&mut tx, &network);
}
}
pub fn run_server(config: &Config, query: Arc<Query>) {
let addr = ([127, 0, 0, 1], 3000).into(); // TODO take from config
info!("REST server running on {}", addr);
let cache = Arc::new(Mutex::new(LruCache::new(100)));
let network = config.network_type;
let new_service = move || {
let query = query.clone();
let cache = cache.clone();
service_fn_ok(move |req: Request<Body>| {
match handle_request(req,&query,&cache) {
match handle_request(req,&query,&cache,&network) {
Ok(response) => response,
Err(e) => {
warn!("{:?}",e);
@ -212,7 +232,7 @@ pub fn run_server(_config: &Config, query: Arc<Query>) {
});
}
fn handle_request(req: Request<Body>, query: &Arc<Query>, cache: &Arc<Mutex<LruCache<Sha256dHash, Block>>>) -> Result<Response<Body>, StringError> {
fn handle_request(req: Request<Body>, query: &Arc<Query>, cache: &Arc<Mutex<LruCache<Sha256dHash, Block>>>, network: &Network) -> Result<Response<Body>, StringError> {
// TODO it looks hyper does not have routing and query parsing :(
let uri = req.uri();
let path: Vec<&str> = uri.path().split('/').skip(1).collect();
@ -264,6 +284,7 @@ fn handle_request(req: Request<Body>, query: &Arc<Query>, cache: &Arc<Mutex<LruC
tx_value.confirmations = value.block_summary.confirmations;
tx_value.block_hash = Some(value.block_summary.id.clone());
}
attach_addresses_txs(&mut value.txs, &network);
json_response(value)
}
}
@ -285,6 +306,7 @@ fn handle_request(req: Request<Body>, query: &Arc<Query>, cache: &Arc<Mutex<LruC
tx_value.confirmations = Some(confirmations);
tx_value.block_hash = Some(block_hash.clone());
}
attach_addresses_txs(&mut value.txs, &network);
json_response(value)
},
(&Method::GET, Some(&"tx"), Some(hash), None) => {
@ -304,6 +326,7 @@ fn handle_request(req: Request<Body>, query: &Arc<Query>, cache: &Arc<Mutex<LruC
value.confirmations = Some(confirmations as u32);
value.hex = Some(tx_hex.to_string());
value.block_hash = Some(blockhash.to_string());
attach_addresses_tx(&mut value, &network);
json_response(value)
},
_ => {

58
src/util.rs

@ -254,3 +254,61 @@ where
.spawn(f)
.unwrap()
}
use secp256k1::{Secp256k1};
use secp256k1::key::PublicKey;
use bitcoin::{Script};
use bitcoin::util::address::{Address,Payload};
use bitcoin::util::hash::Hash160;
use bitcoin_bech32::{WitnessProgram,u5};
use daemon::Network;
use bitcoin::network::constants::Network as BNetwork;
use bitcoin_bech32::constants::Network as B32Network;
// @XXX we can't use any of the Address:p2{...}h utility methods, since they expect the pre-image data, which we don't have.
// we must instead create the Payload manually, which results in code duplication with the p2{...}h methods, especially for witness programs.
// ideally, this should be implemented as part of the rust-bitcoin lib.
pub fn script_to_address(script: &Script, network: &Network) -> Option<String> {
let payload = if script.is_p2pk() {
Some(Payload::Pubkey(PublicKey::from_slice(&Secp256k1::without_caps(), &script[1..66]).unwrap()))
} else if script.is_p2pkh() {
Some(Payload::PubkeyHash(Hash160::from(&script[3..23])))
} else if script.is_p2sh() {
Some(Payload::ScriptHash(Hash160::from(&script[2..22])))
} else if script.is_v0_p2wpkh() {
Some(Payload::WitnessProgram(WitnessProgram::new(u5::try_from_u8(0).expect("0<32"),
script[2..22].to_vec(),
to_bech_network(network)).unwrap()))
} else if script.is_v0_p2wsh() {
Some(Payload::WitnessProgram(WitnessProgram::new(u5::try_from_u8(0).expect("0<32"),
script[2..34].to_vec(),
to_bech_network(network)).unwrap()))
} else { None };
Some(Address { payload: payload?, network: to_bitcoin_network(network) }.to_string())
}
fn to_bitcoin_network (network: &Network) -> BNetwork {
match network {
Network::Bitcoin => BNetwork::Bitcoin,
Network::Testnet => BNetwork::Testnet,
Network::Regtest => BNetwork::Regtest,
Network::Liquid => BNetwork::Bitcoin, // @FIXME
Network::LiquidV1 => BNetwork::Bitcoin, // @FIXME
Network::LiquidRegtest => BNetwork::Regtest, // @FIXME
}
}
fn to_bech_network (network: &Network) -> B32Network {
match network {
Network::Bitcoin => B32Network::Bitcoin,
Network::Testnet => B32Network::Testnet,
Network::Regtest => B32Network::Regtest,
Network::Liquid => B32Network::Bitcoin, // @FIXME
Network::LiquidV1 => B32Network::Bitcoin, // @FIXME
Network::LiquidRegtest => B32Network::Regtest, // @FIXME
}
}

Loading…
Cancel
Save