add txids param to listspendtxs
This commit is contained in:
parent
3fcbb0b67d
commit
da1ebce5b6
10
doc/API.md
10
doc/API.md
@ -218,12 +218,14 @@ This command does not return anything for now.
|
||||
|
||||
List stored Spend transactions.
|
||||
|
||||
|
||||
If `txids` is specified, only list transactions whose `txid` is in `txids`(empty list of `txids` is not allowed).
|
||||
|
||||
#### Request
|
||||
|
||||
This command does not take any parameter for now.
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | ----------------- | ----------------------------------------------------------- |
|
||||
| Field | Type | Description |
|
||||
| ------------- | -------------------------- | ------------------------------------ |
|
||||
| `txids` | array of string (optional) | Ids of the transactions to retrieve |
|
||||
|
||||
#### Response
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ pub enum CommandError {
|
||||
/// Overflowing or unhardened derivation index.
|
||||
InvalidDerivationIndex,
|
||||
RbfError(RbfErrorInfo),
|
||||
EmptyFilterList,
|
||||
}
|
||||
|
||||
impl fmt::Display for CommandError {
|
||||
@ -114,6 +115,7 @@ impl fmt::Display for CommandError {
|
||||
write!(f, "Unhardened or overflowing BIP32 derivation index.")
|
||||
}
|
||||
Self::RbfError(e) => write!(f, "RBF error: '{}'.", e),
|
||||
Self::EmptyFilterList => write!(f, "Filter list is empty, should supply None instead."),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -589,14 +591,32 @@ impl DaemonControl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_spend(&self) -> ListSpendResult {
|
||||
pub fn list_spend(
|
||||
&self,
|
||||
txids: Option<Vec<bitcoin::Txid>>,
|
||||
) -> Result<ListSpendResult, CommandError> {
|
||||
if let Some(ids) = &txids {
|
||||
if ids.is_empty() {
|
||||
return Err(CommandError::EmptyFilterList);
|
||||
}
|
||||
}
|
||||
|
||||
let mut db_conn = self.db.connection();
|
||||
let spend_txs = db_conn
|
||||
.list_spend()
|
||||
let spend_psbts = db_conn.list_spend();
|
||||
|
||||
let txids_set: Option<HashSet<_>> = txids.as_ref().map(|list| list.iter().collect());
|
||||
let spend_txs = spend_psbts
|
||||
.into_iter()
|
||||
.map(|(psbt, updated_at)| ListSpendEntry { psbt, updated_at })
|
||||
.filter_map(|(psbt, updated_at)| {
|
||||
if let Some(set) = &txids_set {
|
||||
if !set.contains(&psbt.unsigned_tx.txid()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(ListSpendEntry { psbt, updated_at })
|
||||
})
|
||||
.collect();
|
||||
ListSpendResult { spend_txs }
|
||||
Ok(ListSpendResult { spend_txs })
|
||||
}
|
||||
|
||||
pub fn delete_spend(&self, txid: &bitcoin::Txid) {
|
||||
|
||||
@ -10,7 +10,7 @@ use std::{
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use miniscript::bitcoin::{self, psbt::Psbt};
|
||||
use miniscript::bitcoin::{self, psbt::Psbt, Txid};
|
||||
|
||||
fn create_spend(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let destinations = params
|
||||
@ -226,6 +226,32 @@ fn list_confirmed(control: &DaemonControl, params: Params) -> Result<serde_json:
|
||||
))
|
||||
}
|
||||
|
||||
fn list_spendtxs(
|
||||
control: &DaemonControl,
|
||||
params: Option<Params>,
|
||||
) -> Result<serde_json::Value, Error> {
|
||||
let txids: Option<Vec<bitcoin::Txid>> = if let Some(p) = params {
|
||||
let tx_ids = p.get(0, "txids");
|
||||
if let Some(ids) = tx_ids {
|
||||
let ids: Vec<Txid> = ids
|
||||
.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."))?;
|
||||
Some(ids)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(serde_json::json!(&control.list_spend(txids)?))
|
||||
}
|
||||
|
||||
fn list_transactions(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let txids: Vec<bitcoin::Txid> = params
|
||||
.get(0, "txids")
|
||||
@ -391,7 +417,7 @@ pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response,
|
||||
})?;
|
||||
list_confirmed(control, params)?
|
||||
}
|
||||
"listspendtxs" => serde_json::json!(&control.list_spend()),
|
||||
"listspendtxs" => list_spendtxs(control, req.params)?,
|
||||
"listtransactions" => {
|
||||
let params = req.params.ok_or_else(|| {
|
||||
Error::invalid_params(
|
||||
|
||||
@ -165,6 +165,7 @@ impl From<commands::CommandError> for Error {
|
||||
| commands::CommandError::AlreadyRescanning
|
||||
| commands::CommandError::InvalidDerivationIndex
|
||||
| commands::CommandError::RbfError(..)
|
||||
| commands::CommandError::EmptyFilterList
|
||||
| commands::CommandError::RecoveryNotAvailable => {
|
||||
Error::new(ErrorCode::InvalidParams, e.to_string())
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ from test_framework.utils import (
|
||||
|
||||
def test_getinfo(lianad):
|
||||
res = lianad.rpc.getinfo()
|
||||
assert 'timestamp' in res.keys()
|
||||
assert "timestamp" in res.keys()
|
||||
assert res["version"] == "4.0.0-dev"
|
||||
assert res["network"] == "regtest"
|
||||
wait_for(lambda: lianad.rpc.getinfo()["block_height"] == 101)
|
||||
@ -458,6 +458,35 @@ def test_list_spend(lianad, bitcoind):
|
||||
lianad.rpc.updatespend(res["psbt"])
|
||||
lianad.rpc.updatespend(res_b["psbt"])
|
||||
|
||||
# Check 'txids' parameter
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
|
||||
txid = PSBT.from_base64(list_res[0]["psbt"]).tx.txid().hex()
|
||||
|
||||
filtered_res = lianad.rpc.listspendtxs(txids=[txid])
|
||||
assert filtered_res["spend_txs"][0]["psbt"] == list_res[0]["psbt"]
|
||||
assert len(filtered_res) == 1
|
||||
|
||||
with pytest.raises(
|
||||
RpcError, match="Filter list is empty, should supply None instead."
|
||||
):
|
||||
lianad.rpc.listspendtxs(txids=[])
|
||||
|
||||
with pytest.raises(RpcError, match="Invalid params: Invalid 'txids' parameter."):
|
||||
lianad.rpc.listspendtxs(txids=[txid, 123])
|
||||
|
||||
with pytest.raises(RpcError, match="Invalid params: Invalid 'txids' parameter."):
|
||||
lianad.rpc.listspendtxs(txids=[0])
|
||||
|
||||
with pytest.raises(RpcError, match="Invalid params: Invalid 'txids' parameter."):
|
||||
lianad.rpc.listspendtxs(txids=[123])
|
||||
|
||||
with pytest.raises(RpcError, match="Invalid params: Invalid 'txids' parameter."):
|
||||
lianad.rpc.listspendtxs(txids=["abc"])
|
||||
|
||||
with pytest.raises(RpcError, match="Invalid params: Invalid 'txids' parameter."):
|
||||
lianad.rpc.listspendtxs(txids=["123"])
|
||||
|
||||
# Listing all Spend transactions will list them both. It'll tell us which one has
|
||||
# change and which one doesn't.
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
@ -1232,9 +1261,7 @@ def test_rbfpsbt_cancel(lianad, bitcoind):
|
||||
# But we can't set the feerate explicitly.
|
||||
with pytest.raises(
|
||||
RpcError,
|
||||
match=re.escape(
|
||||
"A feerate must not be provided if creating a cancel."
|
||||
),
|
||||
match=re.escape("A feerate must not be provided if creating a cancel."),
|
||||
):
|
||||
rbf_1_res = lianad.rpc.rbfpsbt(first_txid, True, 2)
|
||||
rbf_1_psbt = PSBT.from_base64(rbf_1_res["psbt"])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user