Add listtransaction command
This commit is contained in:
parent
3cd709697b
commit
774f969594
17
doc/API.md
17
doc/API.md
@ -16,6 +16,7 @@ Commands must be sent as valid JSONRPC 2.0 requests, ending with a `\n`.
|
||||
| [`broadcastspend`](#broadcastspend) | Finalize a stored Spend PSBT, and broadcast it |
|
||||
| [`startrescan`](#startrescan) | Start rescanning the block chain from a given date |
|
||||
| [`listconfirmed`](#listconfirmed) | List of confirmed transactions of incoming and outgoing funds |
|
||||
| [`listtransactions`](#listtransactions) | List of transactions with the given txids |
|
||||
|
||||
# Reference
|
||||
|
||||
@ -244,3 +245,19 @@ Confirmation time is based on the timestamp of blocks.
|
||||
| `height` | int or `null` | Block height of the transaction, `null` if the transaction is unconfirmed |
|
||||
| `time` | int or `null` | Block time of the transaction, `null` if the transaction is unconfirmed |
|
||||
| `tx` | string | hex encoded bitcoin transaction |
|
||||
|
||||
### `listtransactions`
|
||||
|
||||
`listtransactions` retrieves the transactions with the given txids.
|
||||
|
||||
#### Request
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | --------------- | ------------------------------------- |
|
||||
| `txids` | array of string | Ids of the transactions to retrieve |
|
||||
|
||||
#### Response
|
||||
|
||||
| Field | Type | Description |
|
||||
| -------------- | ------ | ------------------------------------------------------ |
|
||||
| `transactions` | array | Array of [Transaction resource](#transaction-resource) |
|
||||
|
||||
@ -584,6 +584,25 @@ impl DaemonControl {
|
||||
.collect();
|
||||
ListTransactionsResult { transactions }
|
||||
}
|
||||
|
||||
/// list_transactions retrieves the transactions with the given txids.
|
||||
pub fn list_transactions(&self, txids: &[bitcoin::Txid]) -> ListTransactionsResult {
|
||||
let transactions = txids
|
||||
.iter()
|
||||
.filter_map(|txid| {
|
||||
// TODO: batch batch those calls to the Bitcoin backend
|
||||
// so it can in turn optimize its queries.
|
||||
self.bitcoin
|
||||
.wallet_transaction(txid)
|
||||
.map(|(tx, block)| TransactionInfo {
|
||||
tx,
|
||||
height: block.map(|b| b.height),
|
||||
time: block.map(|b| b.time),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
ListTransactionsResult { transactions }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -668,6 +687,7 @@ pub struct TransactionInfo {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{bitcoin::Block, database::SpendBlock, testutils::*};
|
||||
|
||||
use bitcoin::{
|
||||
blockdata::transaction::{TxIn, TxOut},
|
||||
util::bip32::ChildNumber,
|
||||
@ -1162,4 +1182,126 @@ mod tests {
|
||||
|
||||
ms.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_transactions() {
|
||||
let outpoint = OutPoint::new(
|
||||
Txid::from_str("617eab1fc0b03ee7f82ba70166725291783461f1a0e7975eaf8b5f8f674234f3")
|
||||
.unwrap(),
|
||||
0,
|
||||
);
|
||||
|
||||
let tx1: Transaction = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(1),
|
||||
input: vec![TxIn {
|
||||
witness: Witness::new(),
|
||||
previous_output: outpoint,
|
||||
script_sig: Script::new(),
|
||||
sequence: Sequence(0),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
script_pubkey: Script::new(),
|
||||
value: 100_000_000,
|
||||
}],
|
||||
};
|
||||
|
||||
let tx2: Transaction = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(1),
|
||||
input: vec![TxIn {
|
||||
witness: Witness::new(),
|
||||
previous_output: outpoint,
|
||||
script_sig: Script::new(),
|
||||
sequence: Sequence(0),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
script_pubkey: Script::new(),
|
||||
value: 2000,
|
||||
}],
|
||||
};
|
||||
|
||||
let tx3: Transaction = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(1),
|
||||
input: vec![TxIn {
|
||||
witness: Witness::new(),
|
||||
previous_output: outpoint,
|
||||
script_sig: Script::new(),
|
||||
sequence: Sequence(0),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
script_pubkey: Script::new(),
|
||||
value: 3000,
|
||||
}],
|
||||
};
|
||||
|
||||
let mut btc = DummyBitcoind::new();
|
||||
btc.txs.insert(
|
||||
tx1.txid(),
|
||||
(
|
||||
tx1.clone(),
|
||||
Some(Block {
|
||||
hash: bitcoin::BlockHash::from_str(
|
||||
"0000000000000000000326b8fca8d3f820647c97ea33ef722096b3c7b2c8ee94",
|
||||
)
|
||||
.unwrap(),
|
||||
time: 1,
|
||||
height: 1,
|
||||
}),
|
||||
),
|
||||
);
|
||||
btc.txs.insert(
|
||||
tx2.txid(),
|
||||
(
|
||||
tx2.clone(),
|
||||
Some(Block {
|
||||
hash: bitcoin::BlockHash::from_str(
|
||||
"0000000000000000000326b8fca8d3f820647c97ea33ef722096b3c7b2c8ee94",
|
||||
)
|
||||
.unwrap(),
|
||||
time: 2,
|
||||
height: 2,
|
||||
}),
|
||||
),
|
||||
);
|
||||
btc.txs.insert(
|
||||
tx3.txid(),
|
||||
(
|
||||
tx3.clone(),
|
||||
Some(Block {
|
||||
hash: bitcoin::BlockHash::from_str(
|
||||
"0000000000000000000326b8fca8d3f820647c97ea33ef722096b3c7b2c8ee94",
|
||||
)
|
||||
.unwrap(),
|
||||
time: 4,
|
||||
height: 4,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
let ms = DummyLiana::new(btc, DummyDatabase::new());
|
||||
|
||||
let control = &ms.handle.control;
|
||||
|
||||
let transactions = control.list_transactions(&[tx1.txid()]).transactions;
|
||||
assert_eq!(transactions.len(), 1);
|
||||
assert_eq!(transactions[0].tx, tx1);
|
||||
|
||||
let transactions = control
|
||||
.list_transactions(&[tx1.txid(), tx2.txid(), tx3.txid()])
|
||||
.transactions;
|
||||
assert_eq!(transactions.len(), 3);
|
||||
|
||||
let txs: Vec<Transaction> = transactions
|
||||
.iter()
|
||||
.map(|transaction| transaction.tx.clone())
|
||||
.collect();
|
||||
|
||||
assert!(txs.contains(&tx1));
|
||||
assert!(txs.contains(&tx2));
|
||||
assert!(txs.contains(&tx3));
|
||||
|
||||
ms.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +113,20 @@ fn list_confirmed(control: &DaemonControl, params: Params) -> Result<serde_json:
|
||||
)))
|
||||
}
|
||||
|
||||
fn list_transactions(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let txids: Vec<bitcoin::Txid> = params
|
||||
.get(0, "txids")
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'txids' parameter."))?
|
||||
.as_array()
|
||||
.and_then(|arr| {
|
||||
arr.iter()
|
||||
.map(|entry| entry.as_str().and_then(|e| bitcoin::Txid::from_str(e).ok()))
|
||||
.collect()
|
||||
})
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'txids' parameter."))?;
|
||||
Ok(serde_json::json!(&control.list_transactions(&txids)))
|
||||
}
|
||||
|
||||
fn start_rescan(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let timestamp: u32 = params
|
||||
.get(0, "timestamp")
|
||||
@ -173,6 +187,14 @@ pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response,
|
||||
})?;
|
||||
list_confirmed(control, params)?
|
||||
}
|
||||
"listtransactions" => {
|
||||
let params = req.params.ok_or_else(|| {
|
||||
Error::invalid_params(
|
||||
"The 'listtransactions' command requires 1 parameter: 'txids'",
|
||||
)
|
||||
})?;
|
||||
list_transactions(control, params)?
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::method_not_found());
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user