commands: add a new 'delspendtx' command

This commit is contained in:
Antoine Poinsot 2022-10-01 14:04:07 +02:00
parent d5bd10add8
commit c73e8a42dd
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
8 changed files with 82 additions and 0 deletions

View File

@ -11,6 +11,7 @@ Commands must be sent as valid JSONRPC 2.0 requests, ending with a `\n`.
| [`getinfo`](#getinfo) | Get general information about the daemon |
| [`getnewaddress`](#getnewaddress) | Get a new receiving address |
| [`listspendtxs`](#listspendtxs) | List all stored Spend transactions |
| [`delspendtx`](#delspendtx) | Delete a stored Spend transaction |
# Reference
@ -156,3 +157,19 @@ This command does not take any parameter for now.
| -------------- | ----------------- | ----------------------------------------------------------------------- |
| `psbt` | string | Base64-encoded PSBT of the Spend transaction. |
| `change_index` | int or null | Index of the change output in the transaction outputs, if there is one. |
### `delspendtx`
#### Request
| Field | Type | Description |
| -------- | ------ | --------------------------------------------------- |
| `txid` | string | Hex encoded txid of the Spend transaction to delete |
#### Response
This command does not return anything for now.
| Field | Type | Description |
| -------------- | --------- | ---------------------------------------------------- |

View File

@ -431,6 +431,11 @@ impl DaemonControl {
.collect();
ListSpendResult { spend_txs }
}
pub fn delete_spend(&self, txid: &bitcoin::Txid) {
let mut db_conn = self.db.connection();
db_conn.delete_spend(txid);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -79,6 +79,9 @@ pub trait DatabaseConnection {
/// List all existing Spend transactions.
fn list_spend(&mut self) -> Vec<Psbt>;
/// Delete a Spend transaction from database.
fn delete_spend(&mut self, txid: &bitcoin::Txid);
}
// FIXME: if possible, avoid reallocating.
@ -181,6 +184,10 @@ impl DatabaseConnection for SqliteConn {
.map(|db_spend| db_spend.psbt)
.collect()
}
fn delete_spend(&mut self, txid: &bitcoin::Txid) {
self.delete_spend(txid)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]

View File

@ -392,6 +392,17 @@ impl SqliteConn {
)
.expect("Db must not fail")
}
pub fn delete_spend(&mut self, txid: &bitcoin::Txid) {
db_exec(&mut self.conn, |db_tx| {
db_tx.execute(
"DELETE FROM spend_transactions WHERE txid = ?1",
rusqlite::params![txid.to_vec()],
)?;
Ok(())
})
.expect("Db must not fail");
}
}
#[cfg(test)]

View File

@ -60,6 +60,18 @@ fn update_spend(control: &DaemonControl, params: Params) -> Result<serde_json::V
Ok(serde_json::json!({}))
}
fn delete_spend(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
let txid = params
.get(0, "txid")
.ok_or(Error::invalid_params("Missing 'txid' parameter."))?
.as_str()
.and_then(|s| bitcoin::Txid::from_str(&s).ok())
.ok_or(Error::invalid_params("Invalid 'feerate' parameter."))?;
control.delete_spend(&txid);
Ok(serde_json::json!({}))
}
/// Handle an incoming JSONRPC2 request.
pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response, Error> {
let result = match req.method.as_str() {
@ -69,6 +81,12 @@ pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response,
))?;
create_spend(control, params)?
}
"delspendtx" => {
let params = req
.params
.ok_or(Error::invalid_params("Missing 'txid' parameter."))?;
delete_spend(control, params)?
}
"getinfo" => serde_json::json!(&control.get_info()),
"getnewaddress" => serde_json::json!(&control.get_new_address()),
"listcoins" => serde_json::json!(&control.list_coins()),

View File

@ -180,6 +180,10 @@ impl DatabaseConnection for DummyDbConn {
.cloned()
.collect()
}
fn delete_spend(&mut self, txid: &bitcoin::Txid) {
self.db.write().unwrap().spend_txs.remove(txid);
}
}
pub struct DummyMinisafe {

View File

@ -551,6 +551,11 @@ class CTransaction(object):
self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
self.hash = encode(hash256(self.serialize())[::-1], "hex_codec").decode("ascii")
def txid(self):
if self.sha256 is None:
self.calc_sha256()
return ser_uint256(self.sha256)[::-1]
def is_valid(self):
self.calc_sha256()
for tout in self.vout:

View File

@ -134,6 +134,21 @@ def test_list_spend(minisafed, bitcoind):
second_psbt = next(entry for entry in list_res if entry["psbt"] == res_b["psbt"])
assert second_psbt["change_index"] is None
# If we delete the first one, we'll get only the second one.
first_psbt = PSBT()
first_psbt.deserialize(res["psbt"])
minisafed.rpc.delspendtx(first_psbt.tx.txid().hex())
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
assert len(list_res) == 1
assert list_res[0]["psbt"] == res_b["psbt"]
# If we delete the second one, result will be empty.
second_psbt = PSBT()
second_psbt.deserialize(res_b["psbt"])
minisafed.rpc.delspendtx(second_psbt.tx.txid().hex())
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
assert len(list_res) == 0
def test_update_spend(minisafed, bitcoind):
# Start by creating a Spend PSBT