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"
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]]
name = "bdk"
version = "0.25.0"
@ -118,6 +127,7 @@ dependencies = [
"bdk-macros",
"bip39",
"bitcoin",
"bitcoincore-rpc",
"electrum-client",
"esplora-client",
"getrandom",
@ -218,6 +228,30 @@ dependencies = [
"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]]
name = "bitflags"
version = "1.3.2"
@ -567,6 +601,18 @@ dependencies = [
"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]]
name = "lazy_static"
version = "1.4.0"

View File

@ -123,7 +123,7 @@ data class SledDbConfiguration(
*
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/
data class ElectrumConfig (
data class ElectrumConfig(
var url: String,
var socks5: String?,
var retry: UByte,
@ -142,7 +142,7 @@ data class ElectrumConfig (
*
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
*/
data class EsploraConfig (
data class EsploraConfig(
var baseUrl: String,
var proxy: String?,
var concurrency: UByte?,
@ -150,6 +150,56 @@ data class EsploraConfig (
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.
*
@ -161,6 +211,9 @@ sealed class BlockchainConfig {
/** Esplora client. */
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"
[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 = { version = "0.21.0", features = ["builtin-bindgen"] }

View File

@ -45,6 +45,7 @@ enum BdkError {
"Esplora",
"Sled",
"Rusqlite",
"Rpc",
};
dictionary AddressInfo {
@ -126,10 +127,33 @@ dictionary EsploraConfig {
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]
interface BlockchainConfig {
Electrum(ElectrumConfig config);
Esplora(EsploraConfig config);
Rpc(RpcConfig config);
};
interface Blockchain {

View File

@ -7,10 +7,12 @@ use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTr
use bdk::bitcoin::Sequence;
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::rpc::Auth as BdkAuth;
use bdk::blockchain::GetBlockHash;
use bdk::blockchain::GetHeight;
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::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration};
@ -34,6 +36,7 @@ use std::collections::HashSet;
use std::convert::{From, TryFrom};
use std::fmt;
use std::ops::Deref;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard};
@ -134,12 +137,84 @@ pub struct EsploraConfig {
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.
pub enum BlockchainConfig {
/// Electrum client
Electrum { config: ElectrumConfig },
/// Esplora client
Esplora { config: EsploraConfig },
/// Bitcoin Core RPC client
Rpc { config: RpcConfig },
}
/// A wallet transaction
@ -200,6 +275,13 @@ impl Blockchain {
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)?;
Ok(Self {