commands: add a new 'startrescan' command

This commit is contained in:
Antoine Poinsot 2022-11-09 18:21:09 +01:00
parent 7e83bfad55
commit 7866ff46cf
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
4 changed files with 68 additions and 1 deletions

View File

@ -13,6 +13,7 @@ Commands must be sent as valid JSONRPC 2.0 requests, ending with a `\n`.
| [`listspendtxs`](#listspendtxs) | List all stored Spend transactions |
| [`delspendtx`](#delspendtx) | Delete a stored Spend transaction |
| [`broadcastspend`](#broadcastspend) | Finalize a stored Spend PSBT, and broadcast it |
| [`startrescan`](#startrescan) | Start rescanning the block chain from a given date |
# Reference
@ -199,3 +200,19 @@ This command does not return anything for now.
| Field | Type | Description |
| -------------- | --------- | ---------------------------------------------------- |
### `startrescan`
#### Request
| Field | Type | Description |
| ------------ | ------ | ------------------------------------------------------ |
| `timestamp` | int | Date to start rescanning from, as a UNIX timestamp |
#### Response
This command does not return anything for now.
| Field | Type | Description |
| -------------- | --------- | ---------------------------------------------------- |

View File

@ -38,6 +38,9 @@ const MAX_FEE: u64 = bitcoin::blockdata::constants::COIN_VALUE;
// Assume that paying more than 1000sat/vb in feerate is a bug.
const MAX_FEERATE: u64 = bitcoin::blockdata::constants::COIN_VALUE;
// Timestamp in the header of the genesis block. Used for sanity checks.
const MAINNET_GENESIS_TIME: u32 = 1231006505;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CommandError {
NoOutpoint,
@ -56,6 +59,8 @@ pub enum CommandError {
// FIXME: when upgrading Miniscript put the actual error there
SpendFinalization(String),
TxBroadcast(String),
AlreadyRescanning,
InsaneRescanTimestamp(u32),
}
impl fmt::Display for CommandError {
@ -82,6 +87,11 @@ impl fmt::Display for CommandError {
write!(f, "Failed to finalize the spend transaction PSBT: '{}'.", e)
}
Self::TxBroadcast(e) => write!(f, "Failed to broadcast transaction: '{}'.", e),
Self::AlreadyRescanning => write!(
f,
"There is already a rescan ongoing. Please wait for it to complete first."
),
Self::InsaneRescanTimestamp(t) => write!(f, "Insane timestamp '{}'.", t),
}
}
}
@ -490,6 +500,26 @@ impl DaemonControl {
Err(BitcoinError::Broadcast(e)) => Err(CommandError::TxBroadcast(e)),
}
}
/// Trigger a rescan of the block chain for transactions involving our main descriptor between
/// the given date and the current tip.
/// The date must be after the genesis block time and before the current tip blocktime.
pub fn start_rescan(&self, timestamp: u32) -> Result<(), CommandError> {
let mut db_conn = self.db.connection();
if db_conn.rescan_timestamp().is_some() {
return Err(CommandError::AlreadyRescanning);
}
if timestamp < MAINNET_GENESIS_TIME || timestamp >= self.bitcoin.tip_time() {
return Err(CommandError::InsaneRescanTimestamp(timestamp));
}
self.bitcoin
.start_rescan(&self.config.main_descriptor, timestamp);
db_conn.set_rescan(timestamp);
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -84,6 +84,18 @@ fn broadcast_spend(control: &DaemonControl, params: Params) -> Result<serde_json
Ok(serde_json::json!({}))
}
fn start_rescan(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
let timestamp: u32 = params
.get(0, "timestamp")
.ok_or_else(|| Error::invalid_params("Missing 'timestamp' parameter."))?
.as_u64()
.and_then(|t| t.try_into().ok())
.ok_or_else(|| Error::invalid_params("Invalid 'timestamp' parameter."))?;
control.start_rescan(timestamp)?;
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() {
@ -111,6 +123,12 @@ pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response,
"getnewaddress" => serde_json::json!(&control.get_new_address()),
"listcoins" => serde_json::json!(&control.list_coins()),
"listspendtxs" => serde_json::json!(&control.list_spend()),
"startrescan" => {
let params = req
.params
.ok_or_else(|| Error::invalid_params("Missing 'timestamp' parameter."))?;
start_rescan(control, params)?
}
"stop" => serde_json::json!({}),
"updatespend" => {
let params = req

View File

@ -159,7 +159,9 @@ impl From<commands::CommandError> for Error {
| commands::CommandError::InvalidOutputValue(..)
| commands::CommandError::InsufficientFunds(..)
| commands::CommandError::UnknownSpend(..)
| commands::CommandError::SpendFinalization(..) => {
| commands::CommandError::SpendFinalization(..)
| commands::CommandError::InsaneRescanTimestamp(..)
| commands::CommandError::AlreadyRescanning => {
Error::new(ErrorCode::InvalidParams, e.to_string())
}
commands::CommandError::SanityCheckFailure(_) => {