1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 01:43:29 +01:00

Implement 'blockchain.scripthash.listunspent' RPC

Following #475
This commit is contained in:
Roman Zeyde 2021-09-15 21:00:17 +03:00
parent e99906ff83
commit 0a04888673
4 changed files with 66 additions and 6 deletions

View File

@ -67,11 +67,19 @@ def main():
client.request('blockchain.scripthash.get_balance', script_hash)
for script_hash in script_hashes
)
for addr, balance in sorted(zip(args.address, balances), key=lambda v: v[0]):
if balance["confirmed"]:
log.info("{}: confirmed {:,.5f} mBTC", addr, balance["confirmed"] / 1e5)
if balance["unconfirmed"]:
log.info("{}: unconfirmed {:,.5f} mBTC", addr, balance["unconfirmed"] / 1e5)
unspents = conn.call(
client.request('blockchain.scripthash.listunspent', script_hash)
for script_hash in script_hashes
)
for addr, balance, unspent in sorted(zip(args.address, balances, unspents), key=lambda v: v[0]):
if unspent:
log.debug("{}: confirmed={:,.5f} mBTC, unconfirmed={:,.5f} mBTC",
addr, balance["confirmed"] / 1e5, balance["unconfirmed"] / 1e5)
for u in unspent:
log.debug("\t{}:{} = {:,.5f} mBTC {}",
u["tx_hash"], u["tx_pos"], u["value"] / 1e5,
f'@ {u["height"]}' if u["height"] else "")
histories = conn.call(
client.request('blockchain.scripthash.get_history', script_hash)

View File

@ -256,6 +256,24 @@ impl Rpc {
Ok(json!(history_entries))
}
fn scripthash_list_unspent(
&self,
client: &Client,
(scripthash,): (ScriptHash,),
) -> Result<Value> {
let unspent_entries = match client.scripthashes.get(&scripthash) {
Some(status) => self.tracker.get_unspent(status),
None => {
warn!(
"blockchain.scripthash.listunspent called for unsubscribed scripthash: {}",
scripthash
);
self.tracker.get_unspent(&self.new_status(scripthash)?)
}
};
Ok(json!(unspent_entries))
}
fn scripthash_subscribe(
&self,
client: &mut Client,
@ -406,6 +424,7 @@ impl Rpc {
Call::RelayFee => self.relayfee(),
Call::ScriptHashGetBalance(args) => self.scripthash_get_balance(client, args),
Call::ScriptHashGetHistory(args) => self.scripthash_get_history(client, args),
Call::ScriptHashListUnspent(args) => self.scripthash_list_unspent(client, args),
Call::ScriptHashSubscribe(args) => self.scripthash_subscribe(client, args),
Call::TransactionBroadcast(args) => self.transaction_broadcast(args),
Call::TransactionGet(args) => self.transaction_get(args),
@ -445,6 +464,7 @@ enum Call {
RelayFee,
ScriptHashGetBalance((ScriptHash,)),
ScriptHashGetHistory((ScriptHash,)),
ScriptHashListUnspent((ScriptHash,)),
ScriptHashSubscribe((ScriptHash,)),
TransactionGet(TxGetArgs),
TransactionGetMerkle((Txid, usize)),
@ -461,6 +481,7 @@ impl Call {
"blockchain.relayfee" => Call::RelayFee,
"blockchain.scripthash.get_balance" => Call::ScriptHashGetBalance(convert(params)?),
"blockchain.scripthash.get_history" => Call::ScriptHashGetHistory(convert(params)?),
"blockchain.scripthash.listunspent" => Call::ScriptHashListUnspent(convert(params)?),
"blockchain.scripthash.subscribe" => Call::ScriptHashSubscribe(convert(params)?),
"blockchain.transaction.broadcast" => Call::TransactionBroadcast(convert(params)?),
"blockchain.transaction.get" => Call::TransactionGet(convert(params)?),

View File

@ -142,6 +142,17 @@ pub(crate) struct Balance {
mempool_delta: SignedAmount,
}
// A single unspent transaction output entry:
// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html#blockchain-scripthash-listunspent
#[derive(Serialize)]
pub(crate) struct UnspentEntry {
height: usize, // 0 = mempool entry
tx_hash: Txid,
tx_pos: u32,
#[serde(with = "bitcoin::util::amount::serde::as_sat")]
value: Amount,
}
#[derive(Default)]
struct Unspent {
// mapping an outpoint to its value & confirmation height
@ -172,6 +183,18 @@ impl Unspent {
unspent
}
fn into_entries(self) -> Vec<UnspentEntry> {
self.outpoints
.into_iter()
.map(|(outpoint, (value, height))| UnspentEntry {
height,
tx_hash: outpoint.txid,
tx_pos: outpoint.vout,
value,
})
.collect()
}
fn balance(&self) -> Amount {
self.outpoints
.values()
@ -237,6 +260,10 @@ impl ScriptHashStatus {
.collect()
}
pub(crate) fn get_unspent(&self, chain: &Chain) -> Vec<UnspentEntry> {
Unspent::build(self, chain).into_entries()
}
pub(crate) fn get_balance(&self, chain: &Chain) -> Balance {
let unspent = Unspent::build(self, chain);
Balance {

View File

@ -12,7 +12,7 @@ use crate::{
index::Index,
mempool::{Histogram, Mempool},
metrics::Metrics,
status::{Balance, HistoryEntry, ScriptHashStatus},
status::{Balance, HistoryEntry, ScriptHashStatus, UnspentEntry},
};
/// Electrum protocol subscriptions' tracker
@ -55,6 +55,10 @@ impl Tracker {
status.get_history(self.index.chain(), &self.mempool)
}
pub(crate) fn get_unspent(&self, status: &ScriptHashStatus) -> Vec<UnspentEntry> {
status.get_unspent(self.index.chain())
}
pub fn sync(&mut self, daemon: &Daemon) -> Result<()> {
self.index.sync(daemon, self.index_batch_size)?;
if !self.ignore_mempool {