diff --git a/.travis.yml b/.travis.yml
index 6a6263f1..9efcb3f0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,6 +17,7 @@ env:
- TARGET=x86_64-unknown-linux-gnu FEATURES=cli-utils,esplora NO_DEFAULT_FEATURES=1
- TARGET=x86_64-unknown-linux-gnu FEATURES=compiler NO_DEFAULT_FEATURES=1 RUN_TESTS=1 # Test the `miniscriptc` example
- TARGET=x86_64-unknown-linux-gnu FEATURES=test-electrum NO_DEFAULT_FEATURES=1 RUN_TESTS=1 RUN_CORE=1
+ - TARGET=x86_64-unknown-linux-gnu FEATURES=test-md-docs NO_DEFAULT_FEATURES=1 RUN_TESTS=1 NIGHTLY=1
- TARGET=wasm32-unknown-unknown FEATURES=cli-utils,esplora NO_DEFAULT_FEATURES=1
before_script:
- |
@@ -33,6 +34,11 @@ before_script:
if [[ $CHECK_FMT -eq 1 ]]; then
rustup component add rustfmt
fi
+ - |
+ if [[ $NIGHTLY -eq 1 ]]; then
+ rustup toolchain install nightly
+ rustup default nightly
+ fi
- rustup target add "$TARGET"
script:
- |
diff --git a/Cargo.toml b/Cargo.toml
index bb24548f..18ebba9f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -51,6 +51,7 @@ async-interface = ["async-trait"]
# Debug/Test features
debug-proc-macros = ["magical-macros/debug", "testutils-macros/debug"]
test-electrum = ["electrum"]
+test-md-docs = ["base64", "electrum"]
[dev-dependencies]
testutils = { path = "./testutils" }
diff --git a/README.md b/README.md
index 103e8d68..188628ae 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,145 @@
-# Magical Bitcoin Wallet
+
+
Magical Bitcoin Library
-A modern, lightweight, descriptor-based wallet written in Rust!
+

-## Getting Started
+
+ A modern, lightweight, descriptor-based wallet library written in Rust!
+
-See the documentation at [magicalbitcoin.org](https://magicalbitcoin.org)
+
+
+
+
+
+
+
+
+
+## About
+
+The `magical` library aims to be the core building block for Bitcoin wallets of any kind.
+
+* It uses [Miniscript](https://github.com/rust-bitcoin/rust-miniscript) to support descriptors with generalized conditions. This exact same library can be used to build
+ single-sig wallets, multisigs, timelocked contracts and more.
+* It supports multiple blockchain backends and databases, allowing developers to choose exactly what's right for their projects.
+* It's built to be cross-platform: the core logic works on desktop, mobile, and even WebAssembly.
+* It's very easy to extend: developers can implement customized logic for blockchain backends, databases, signers, coin selection, and more, without having to fork and modify this library.
+
+## Examples
+
+### Sync the balance of a descriptor
+
+```no_run
+use magical::Wallet;
+use magical::database::MemoryDatabase;
+use magical::blockchain::{noop_progress, ElectrumBlockchain};
+
+use magical::electrum_client::Client;
+
+fn main() -> Result<(), magical::Error> {
+ let client = Client::new("ssl://electrum.blockstream.info:60002", None)?;
+ let wallet = Wallet::new(
+ "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
+ Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
+ bitcoin::Network::Testnet,
+ MemoryDatabase::default(),
+ ElectrumBlockchain::from(client)
+ )?;
+
+ wallet.sync(noop_progress(), None)?;
+
+ println!("Descriptor balance: {} SAT", wallet.get_balance()?);
+
+ Ok(())
+}
+```
+
+### Generate a few addresses
+
+```
+use magical::{Wallet, OfflineWallet};
+use magical::database::MemoryDatabase;
+
+fn main() -> Result<(), magical::Error> {
+ let wallet: OfflineWallet<_> = Wallet::new_offline(
+ "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
+ Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
+ bitcoin::Network::Testnet,
+ MemoryDatabase::default(),
+ )?;
+
+ println!("Address #0: {}", wallet.get_new_address()?);
+ println!("Address #1: {}", wallet.get_new_address()?);
+ println!("Address #2: {}", wallet.get_new_address()?);
+
+ Ok(())
+}
+```
+
+### Create a transaction
+
+```no_run
+use magical::{FeeRate, TxBuilder, Wallet};
+use magical::database::MemoryDatabase;
+use magical::blockchain::{noop_progress, ElectrumBlockchain};
+
+use magical::electrum_client::Client;
+
+use bitcoin::consensus::serialize;
+
+fn main() -> Result<(), magical::Error> {
+ let client = Client::new("ssl://electrum.blockstream.info:60002", None)?;
+ let wallet = Wallet::new(
+ "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
+ Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
+ bitcoin::Network::Testnet,
+ MemoryDatabase::default(),
+ ElectrumBlockchain::from(client)
+ )?;
+
+ wallet.sync(noop_progress(), None)?;
+
+ let send_to = wallet.get_new_address()?;
+ let (psbt, details) = wallet.create_tx(
+ TxBuilder::with_recipients(vec![(send_to.script_pubkey(), 50_000)])
+ .enable_rbf()
+ .do_not_spend_change()
+ .fee_rate(FeeRate::from_sat_per_vb(5.0))
+ )?;
+
+ println!("Transaction details: {:#?}", details);
+ println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
+
+ Ok(())
+}
+```
+
+### Sign a transaction
+
+```no_run
+use magical::{Wallet, OfflineWallet};
+use magical::database::MemoryDatabase;
+
+use bitcoin::consensus::deserialize;
+
+fn main() -> Result<(), magical::Error> {
+ let wallet: OfflineWallet<_> = Wallet::new_offline(
+ "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
+ Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
+ bitcoin::Network::Testnet,
+ MemoryDatabase::default(),
+ )?;
+
+ let psbt = "...";
+ let psbt = deserialize(&base64::decode(psbt).unwrap())?;
+
+ let (signed_psbt, finalized) = wallet.sign(psbt, None)?;
+
+ Ok(())
+}
+```
diff --git a/src/doctest.rs b/src/doctest.rs
new file mode 100644
index 00000000..1c59362f
--- /dev/null
+++ b/src/doctest.rs
@@ -0,0 +1,3 @@
+#[doc(include = "../README.md")]
+#[cfg(doctest)]
+pub struct ReadmeDoctests;
diff --git a/src/lib.rs b/src/lib.rs
index d215df13..9f2e05ad 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,6 +25,9 @@
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
+// only enables the nightly `external_doc` feature when
+// `test-md-docs` is enabled
+#![cfg_attr(feature = "test-md-docs", feature(external_doc))]
pub extern crate bitcoin;
extern crate log;
@@ -70,6 +73,8 @@ pub(crate) mod error;
pub mod blockchain;
pub mod database;
pub mod descriptor;
+#[cfg(feature = "test-md-docs")]
+mod doctest;
pub(crate) mod psbt;
pub(crate) mod types;
pub mod wallet;
diff --git a/static/wizard.svg b/static/wizard.svg
new file mode 100644
index 00000000..5fc405bf
--- /dev/null
+++ b/static/wizard.svg
@@ -0,0 +1,327 @@
+
+