Merge bitcoindevkit/bdk-ffi#125: Add RpcConfig, BlockchainConfig::Rpc, and Auth

4ed6e364e68648a3b5cf6773e8d74b74d922b315 Add API docs for RPC blockchain config and auth (thunderbiscuit)
d0cd3b0f389c10610ad58a999d42b4b7ca39ba6c Add Auth, RpcSyncParams, and RpcConfig (Steve Myers)

Pull request description:

  Fixes #117. This adds the RPC blockchain config but I don't currently have a way to test it as part of the `bdk-kotlin` or `bdk-swift` automated testing, which would need to be able to spin up a local regtest bitcoind for the tests.

  For now this will only be manually tested.

ACKs for top commit:
  thunderbiscuit:
    ACK [4ed6e36](4ed6e364e6).

Tree-SHA512: 2f114753a683c32ec957b26e23918eb3c1de07073fd0293e06fb960f8226f09d4d9ebf89f8d04f9da5cd459619f224d732b81e21c2173afeccb8ce56cc558582
This commit is contained in:
Steve Myers 2022-12-16 10:09:51 -06:00
commit 41fdadb09c
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
5 changed files with 209 additions and 4 deletions

46
Cargo.lock generated
View File

@ -107,6 +107,15 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64-compat"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "bdk" name = "bdk"
version = "0.25.0" version = "0.25.0"
@ -118,6 +127,7 @@ dependencies = [
"bdk-macros", "bdk-macros",
"bip39", "bip39",
"bitcoin", "bitcoin",
"bitcoincore-rpc",
"electrum-client", "electrum-client",
"esplora-client", "esplora-client",
"getrandom", "getrandom",
@ -218,6 +228,30 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitcoincore-rpc"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0261b2bb7617e0c91b452a837bbd1291fd34ad6990cb8e3ffc28239cc045b5ca"
dependencies = [
"bitcoincore-rpc-json",
"jsonrpc",
"log",
"serde",
"serde_json",
]
[[package]]
name = "bitcoincore-rpc-json"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c231bea28e314879c5aef240f6052e8a72a369e3c9f9b20d9bfbb33ad18029b2"
dependencies = [
"bitcoin",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -567,6 +601,18 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "jsonrpc"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f8423b78fc94d12ef1a4a9d13c348c9a78766dda0cc18817adf0faf77e670c8"
dependencies = [
"base64-compat",
"serde",
"serde_derive",
"serde_json",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"

View File

@ -123,7 +123,7 @@ data class SledDbConfiguration(
* *
* @sample org.bitcoindevkit.electrumBlockchainConfigSample * @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/ */
data class ElectrumConfig ( data class ElectrumConfig(
var url: String, var url: String,
var socks5: String?, var socks5: String?,
var retry: UByte, var retry: UByte,
@ -142,7 +142,7 @@ data class ElectrumConfig (
* *
* @sample org.bitcoindevkit.esploraBlockchainConfigSample * @sample org.bitcoindevkit.esploraBlockchainConfigSample
*/ */
data class EsploraConfig ( data class EsploraConfig(
var baseUrl: String, var baseUrl: String,
var proxy: String?, var proxy: String?,
var concurrency: UByte?, var concurrency: UByte?,
@ -150,6 +150,56 @@ data class EsploraConfig (
var timeout: ULong? var timeout: ULong?
) )
/**
* Authentication mechanism for RPC connection to full node
*/
sealed class Auth {
/** No authentication */
object None: Auth()
/** Authentication with username and password, usually [Auth.Cookie] should be preferred */
data class UserPass(val username: String, val password: String): Auth()
/** Authentication with a cookie file */
data class Cookie(val file: String): Auth()
}
/**
* Sync parameters for Bitcoin Core RPC.
*
* In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
* `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
* how the `importdescriptors` RPC calls are to be made.
*
* @property startScriptCount The minimum number of scripts to scan for on initial sync.
* @property startTime Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
* @property forceStartTime Forces every sync to use `start_time` as import timestamp.
* @property pollRateSec RPC poll rate (in seconds) to get state updates.
*/
data class RcpSyncParams(
val startScriptCount: ULong,
val startTime: Ulong,
val forceStartTime: Boolean,
val pollRateSec: ULong,
)
/**
* RpcBlockchain configuration options
*
* @property url The bitcoin node url.
* @property auth The bicoin node authentication mechanism.
* @property network The network we are using (it will be checked the bitcoin node network matches this).
* @property walletName The wallet name in the bitcoin node.
* @property syncParams Sync parameters.
*/
data class RpcConfig(
val url: String,
val auth: Auth,
val network: Network,
val walletName: String,
val syncParams: RcpSyncParams?,
)
/** /**
* Type that can contain any of the blockchain configurations defined by the library. * Type that can contain any of the blockchain configurations defined by the library.
* *
@ -161,6 +211,9 @@ sealed class BlockchainConfig {
/** Esplora client. */ /** Esplora client. */
data class Esplora(val config: EsploraConfig) : BlockchainConfig() data class Esplora(val config: EsploraConfig) : BlockchainConfig()
/** Bitcoin Core RPC client. */
data class Rpc(val config: RpcConfig) : BlockchainConfig()
} }
/** /**

View File

@ -10,7 +10,7 @@ crate-type = ["staticlib", "cdylib"]
name = "bdkffi" name = "bdkffi"
[dependencies] [dependencies]
bdk = { version = "0.25", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] } bdk = { version = "0.25", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] } uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] } uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }

View File

@ -45,6 +45,7 @@ enum BdkError {
"Esplora", "Esplora",
"Sled", "Sled",
"Rusqlite", "Rusqlite",
"Rpc",
}; };
dictionary AddressInfo { dictionary AddressInfo {
@ -126,10 +127,33 @@ dictionary EsploraConfig {
u64? timeout; u64? timeout;
}; };
[Enum]
interface Auth {
None();
UserPass(string username, string password);
Cookie(string file);
};
dictionary RpcSyncParams {
u64 start_script_count;
u64 start_time;
boolean force_start_time;
u64 poll_rate_sec;
};
dictionary RpcConfig {
string url;
Auth auth;
Network network;
string wallet_name;
RpcSyncParams? sync_params;
};
[Enum] [Enum]
interface BlockchainConfig { interface BlockchainConfig {
Electrum(ElectrumConfig config); Electrum(ElectrumConfig config);
Esplora(EsploraConfig config); Esplora(EsploraConfig config);
Rpc(RpcConfig config);
}; };
interface Blockchain { interface Blockchain {

View File

@ -7,10 +7,12 @@ use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTr
use bdk::bitcoin::Sequence; use bdk::bitcoin::Sequence;
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid}; use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::rpc::Auth as BdkAuth;
use bdk::blockchain::GetBlockHash; use bdk::blockchain::GetBlockHash;
use bdk::blockchain::GetHeight; use bdk::blockchain::GetHeight;
use bdk::blockchain::{ use bdk::blockchain::{
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig, ConfigurableBlockchain, electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig,
rpc::RpcConfig as BdkRpcConfig, rpc::RpcSyncParams as BdkRpcSyncParams, ConfigurableBlockchain,
}; };
use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress}; use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress};
use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration}; use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
@ -34,6 +36,7 @@ use std::collections::HashSet;
use std::convert::{From, TryFrom}; use std::convert::{From, TryFrom};
use std::fmt; use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
@ -134,12 +137,84 @@ pub struct EsploraConfig {
pub timeout: Option<u64>, pub timeout: Option<u64>,
} }
pub enum Auth {
/// No authentication
None,
/// Authentication with username and password, usually [Auth::Cookie] should be preferred
UserPass {
/// Username
username: String,
/// Password
password: String,
},
/// Authentication with a cookie file
Cookie {
/// Cookie file
file: String,
},
}
impl From<Auth> for BdkAuth {
fn from(auth: Auth) -> Self {
match auth {
Auth::None => BdkAuth::None,
Auth::UserPass { username, password } => BdkAuth::UserPass { username, password },
Auth::Cookie { file } => BdkAuth::Cookie {
file: PathBuf::from(file),
},
}
}
}
/// Sync parameters for Bitcoin Core RPC.
///
/// In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
/// `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
/// how the `importdescriptors` RPC calls are to be made.
pub struct RpcSyncParams {
/// The minimum number of scripts to scan for on initial sync.
pub start_script_count: u64,
/// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
pub start_time: u64,
/// Forces every sync to use `start_time` as import timestamp.
pub force_start_time: bool,
/// RPC poll rate (in seconds) to get state updates.
pub poll_rate_sec: u64,
}
impl From<RpcSyncParams> for BdkRpcSyncParams {
fn from(params: RpcSyncParams) -> Self {
BdkRpcSyncParams {
start_script_count: params.start_script_count as usize,
start_time: params.start_time,
force_start_time: params.force_start_time,
poll_rate_sec: params.poll_rate_sec,
}
}
}
/// RpcBlockchain configuration options
pub struct RpcConfig {
/// The bitcoin node url
pub url: String,
/// The bitcoin node authentication mechanism
pub auth: Auth,
/// The network we are using (it will be checked the bitcoin node network matches this)
pub network: Network,
/// The wallet name in the bitcoin node, consider using [crate::wallet::wallet_name_from_descriptor] for this
pub wallet_name: String,
/// Sync parameters
pub sync_params: Option<RpcSyncParams>,
}
/// Type that can contain any of the blockchain configurations defined by the library. /// Type that can contain any of the blockchain configurations defined by the library.
pub enum BlockchainConfig { pub enum BlockchainConfig {
/// Electrum client /// Electrum client
Electrum { config: ElectrumConfig }, Electrum { config: ElectrumConfig },
/// Esplora client /// Esplora client
Esplora { config: EsploraConfig }, Esplora { config: EsploraConfig },
/// Bitcoin Core RPC client
Rpc { config: RpcConfig },
} }
/// A wallet transaction /// A wallet transaction
@ -200,6 +275,13 @@ impl Blockchain {
timeout: config.timeout, timeout: config.timeout,
}) })
} }
BlockchainConfig::Rpc { config } => AnyBlockchainConfig::Rpc(BdkRpcConfig {
url: config.url,
auth: config.auth.into(),
network: config.network,
wallet_name: config.wallet_name,
sync_params: config.sync_params.map(|p| p.into()),
}),
}; };
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?; let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
Ok(Self { Ok(Self {