From b455f5c026eda200df71220dfd3dbcb5ae20bfab Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 24 Jan 2019 10:17:15 +0200 Subject: [PATCH] liquid: Add preliminary conditional support for Elements data structures This makes the code properly compile with the "liquid" feature flag enabled, but it is still broken in all sorts of ways and missing some functionality. --- src/daemon.rs | 2 +- src/new_index/mempool.rs | 11 +++++ src/new_index/schema.rs | 39 ++++++++++++--- src/rest.rs | 104 ++++++++++++++++++++++++++++++--------- src/util.rs | 9 +++- 5 files changed, 133 insertions(+), 32 deletions(-) diff --git a/src/daemon.rs b/src/daemon.rs index 6c375d3..22389d5 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -624,8 +624,8 @@ impl Daemon { let header = self .getblockheader(&blockhash) .chain_err(|| format!("failed to get {} header", blockhash))?; - new_headers.push(header); blockhash = header.prev_blockhash; + new_headers.push(header); } trace!("downloaded {} block headers", new_headers.len()); new_headers.reverse(); // so the tip is the last vector entry diff --git a/src/new_index/mempool.rs b/src/new_index/mempool.rs index 1a0db17..85d11d3 100644 --- a/src/new_index/mempool.rs +++ b/src/new_index/mempool.rs @@ -123,14 +123,25 @@ impl Mempool { } match entry { + #[cfg(not(feature="liquid"))] TxHistoryInfo::Funding(info) => { stats.funded_txo_count += 1; stats.funded_txo_sum += info.value; } + #[cfg(feature="liquid")] + TxHistoryInfo::Funding(_) => { + stats.funded_txo_count += 1; + } + + #[cfg(not(feature="liquid"))] TxHistoryInfo::Spending(info) => { stats.spent_txo_count += 1; stats.spent_txo_sum += info.value; } + #[cfg(feature="liquid")] + TxHistoryInfo::Spending(_) => { + stats.spent_txo_count += 1; + } }; } diff --git a/src/new_index/schema.rs b/src/new_index/schema.rs index 0c0d322..7c56772 100644 --- a/src/new_index/schema.rs +++ b/src/new_index/schema.rs @@ -7,6 +7,9 @@ use crypto::sha2::Sha256; use itertools::Itertools; use rayon::prelude::*; +#[cfg(feature="liquid")] +use elements::confidential::Value; + use std::collections::{BTreeSet, HashMap, HashSet}; use std::path::Path; use std::sync::{Arc, RwLock}; @@ -16,7 +19,7 @@ use crate::daemon::Daemon; use crate::errors::*; use crate::metrics::{HistogramOpts, HistogramTimer, HistogramVec, Metrics}; use crate::util::{ - full_hash, BlockHeaderMeta, BlockMeta, BlockStatus, Bytes, HeaderEntry, HeaderList, + is_coinbase, full_hash, BlockHeaderMeta, BlockMeta, BlockStatus, Bytes, HeaderEntry, HeaderList, }; use crate::new_index::db::{DBFlush, DBRow, ScanIterator, DB}; @@ -77,8 +80,11 @@ impl From<&HeaderEntry> for BlockId { pub struct Utxo { pub txid: Sha256dHash, pub vout: u32, - pub value: u64, pub confirmed: Option, + #[cfg(not(feature="liquid"))] + pub value: u64, + #[cfg(feature="liquid")] + pub value: Value, } impl From<&Utxo> for OutPoint { @@ -98,8 +104,10 @@ pub struct SpendingInput { pub struct ScriptStats { pub tx_count: usize, pub funded_txo_count: usize, - pub funded_txo_sum: u64, pub spent_txo_count: usize, + #[cfg(not(feature="liquid"))] + pub funded_txo_sum: u64, + #[cfg(not(feature="liquid"))] pub spent_txo_sum: u64, } @@ -108,8 +116,10 @@ impl ScriptStats { ScriptStats { tx_count: 0, funded_txo_count: 0, - funded_txo_sum: 0, spent_txo_count: 0, + #[cfg(not(feature="liquid"))] + funded_txo_sum: 0, + #[cfg(not(feature="liquid"))] spent_txo_sum: 0, } } @@ -427,14 +437,25 @@ impl ChainQuery { } match history.key.txinfo { + #[cfg(not(feature="liquid"))] TxHistoryInfo::Funding(ref info) => { stats.funded_txo_count += 1; stats.funded_txo_sum += info.value; } + #[cfg(feature="liquid")] + TxHistoryInfo::Funding(_) => { + stats.funded_txo_count += 1; + } + + #[cfg(not(feature="liquid"))] TxHistoryInfo::Spending(ref info) => { stats.spent_txo_count += 1; stats.spent_txo_sum += info.value; } + #[cfg(feature="liquid")] + TxHistoryInfo::Spending(_) => { + stats.spent_txo_count += 1; + } }; lastblock = Some(blockid.hash); @@ -636,7 +657,7 @@ fn get_previous_txos(block_entries: &[BlockEntry]) -> BTreeSet { .flat_map(|b| { b.block.txdata.iter().flat_map(|tx| { tx.input.iter().filter_map(|txin| { - if txin.previous_output.is_null() { + if is_coinbase(txin) { None } else { Some(txin.previous_output) @@ -718,7 +739,7 @@ fn index_transaction( } } for (txi_index, txi) in tx.input.iter().enumerate() { - if txi.previous_output.is_null() { + if is_coinbase(txi) { continue; } let prev_txo = previous_txos_map @@ -955,7 +976,10 @@ impl BlockRow { pub struct FundingInfo { pub txid: FullHash, // funding transaction pub vout: u16, + #[cfg(not(feature="liquid"))] pub value: u64, + #[cfg(feature="liquid")] + pub value: Value, } #[derive(Serialize, Deserialize)] @@ -964,7 +988,10 @@ pub struct SpendingInfo { pub vin: u16, pub prev_txid: FullHash, // funding transaction pub prev_vout: u16, + #[cfg(not(feature="liquid"))] pub value: u64, + #[cfg(feature="liquid")] + pub value: Value, } #[derive(Serialize, Deserialize)] diff --git a/src/rest.rs b/src/rest.rs index 03a36ef..d7fe115 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -3,7 +3,7 @@ use crate::config::Config; use crate::errors; use crate::new_index::{compute_script_hash, BlockId, Query, SpendingInput, Utxo}; use crate::util::{ - full_hash, get_script_asm, script_to_address, BlockHeaderMeta, FullHash, TransactionStatus, + is_coinbase, full_hash, get_script_asm, script_to_address, BlockHeaderMeta, FullHash, TransactionStatus, }; use bitcoin::consensus::encode::{self, serialize}; @@ -17,6 +17,9 @@ use hyper::rt::{self, Future}; use hyper::service::service_fn_ok; use hyper::{Body, Method, Request, Response, Server, StatusCode}; +#[cfg(feature="liquid")] +use elements::confidential::Value; + use serde::Serialize; use serde_json; use std::collections::BTreeMap; @@ -39,13 +42,15 @@ struct BlockValue { height: u32, version: u32, timestamp: u32, - bits: u32, - nonce: u32, tx_count: u32, size: u32, weight: u32, merkle_root: String, previousblockhash: Option, + #[cfg(not(feature="liquid"))] + nonce: u32, + #[cfg(not(feature="liquid"))] + bits: u32, } impl From for BlockValue { @@ -56,8 +61,6 @@ impl From for BlockValue { height: blockhm.header_entry.height() as u32, version: header.version, timestamp: header.time, - bits: header.bits, - nonce: header.nonce, tx_count: blockhm.meta.tx_count, size: blockhm.meta.size, weight: blockhm.meta.weight, @@ -67,6 +70,11 @@ impl From for BlockValue { } else { None }, + + #[cfg(not(feature="liquid"))] + bits: header.bits, + #[cfg(not(feature="liquid"))] + nonce: header.nonce, } } } @@ -134,12 +142,17 @@ struct TxInValue { impl From for TxInValue { fn from(txin: TxIn) -> Self { - let script = txin.script_sig; + #[cfg(not(feature="liquid"))] let witness = if txin.witness.len() > 0 { Some(txin.witness.iter().map(|w| hex::encode(w)).collect()) } else { None }; + #[cfg(feature="liquid")] + let witness = None; // @TODO + + let is_coinbase = is_coinbase(&txin); + let script = txin.script_sig; TxInValue { txid: txin.previous_output.txid, @@ -147,8 +160,8 @@ impl From for TxInValue { prevout: None, // added later scriptsig_asm: get_script_asm(&script), scriptsig: script, - witness: witness, - is_coinbase: txin.previous_output.is_null(), + witness, + is_coinbase, sequence: txin.sequence, } } @@ -158,14 +171,33 @@ impl From for TxInValue { struct TxOutValue { scriptpubkey: Script, scriptpubkey_asm: String, - value: u64, scriptpubkey_address: Option, scriptpubkey_type: String, + + #[cfg(not(feature="liquid"))] + value: u64, + #[cfg(feature="liquid")] + value: Option, + #[cfg(feature="liquid")] + valuecommitment: Option, } impl From for TxOutValue { fn from(txout: TxOut) -> Self { + #[cfg(not(feature="liquid"))] let value = txout.value; + + #[cfg(feature="liquid")] + let value = match txout.value { + Value::Explicit(value) => Some(value), + _ => None, + }; + #[cfg(feature="liquid")] + let valuecommitment = match txout.value { + Value::Confidential(..) => Some(hex::encode(serialize(&txout.value))), + _ => None, + }; + let script = txout.script_pubkey; let script_asm = get_script_asm(&script); @@ -195,6 +227,8 @@ impl From for TxOutValue { scriptpubkey_address: None, // added later scriptpubkey_type: script_type.to_string(), value, + #[cfg(feature="liquid")] + valuecommitment, } } } @@ -203,17 +237,37 @@ impl From for TxOutValue { struct UtxoValue { txid: Sha256dHash, vout: u32, - value: u64, status: TransactionStatus, + #[cfg(not(feature="liquid"))] + value: u64, + #[cfg(feature="liquid")] + value: Option, + #[cfg(feature="liquid")] + valuecommitment: Option, } impl From for UtxoValue { fn from(utxo: Utxo) -> Self { + #[cfg(not(feature="liquid"))] + let value = utxo.value; + + #[cfg(feature="liquid")] + let value = match utxo.value { + Value::Explicit(value) => Some(value), + _ => None, + }; + #[cfg(feature="liquid")] + let valuecommitment = match utxo.value { + Value::Confidential(..) => Some(hex::encode(serialize(&utxo.value))), + _ => None, + }; + UtxoValue { txid: utxo.txid, vout: utxo.vout, - value: utxo.value, + value, status: TransactionStatus::from(utxo.confirmed), - // utxo.script is also available but is unused here + #[cfg(feature="liquid")] + valuecommitment, } } } @@ -307,19 +361,21 @@ fn attach_txs_data(txs: &mut Vec, config: &Config, query: &Que } // attach tx fee - if config.prevout_enabled { - for mut tx in txs.iter_mut() { - if tx.vin.iter().any(|vin| vin.prevout.is_none()) { - continue; - } + #[cfg(not(feature="liquid"))] { + if config.prevout_enabled { + for mut tx in txs.iter_mut() { + if tx.vin.iter().any(|vin| vin.prevout.is_none()) { + continue; + } - let total_in: u64 = tx - .vin - .iter() - .map(|vin| vin.clone().prevout.unwrap().value) - .sum(); - let total_out: u64 = tx.vout.iter().map(|vout| vout.value).sum(); - tx.fee = Some(total_in - total_out); + let total_in: u64 = tx + .vin + .iter() + .map(|vin| vin.clone().prevout.unwrap().value) + .sum(); + let total_out: u64 = tx.vout.iter().map(|vout| vout.value).sum(); + tx.fee = Some(total_in - total_out); + } } } } diff --git a/src/util.rs b/src/util.rs index 1845f24..7244abc 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -use crate::chain::{Block, BlockHeader}; +use crate::chain::{Block, BlockHeader, TxIn}; use crate::errors::*; use crate::new_index::{BlockEntry, BlockId}; @@ -433,3 +433,10 @@ pub fn get_script_asm(script: &Script) -> String { let asm = format!("{:?}", script); (&asm[7..asm.len() - 1]).to_string() } + +pub fn is_coinbase(txin: &TxIn) -> bool { + #[cfg(not(feature="liquid"))] + return txin.previous_output.is_null(); + #[cfg(feature="liquid")] + return txin.is_coinbase(); +}