commands: add a new 'startrescan' command
This commit is contained in:
parent
7e83bfad55
commit
7866ff46cf
17
doc/API.md
17
doc/API.md
@ -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 |
|
||||
| -------------- | --------- | ---------------------------------------------------- |
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(_) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user