Improve the README, add examples
This commit is contained in:
parent
6a2d0db674
commit
c4f4f20d8b
@ -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=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=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-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
|
- TARGET=wasm32-unknown-unknown FEATURES=cli-utils,esplora NO_DEFAULT_FEATURES=1
|
||||||
before_script:
|
before_script:
|
||||||
- |
|
- |
|
||||||
@ -33,6 +34,11 @@ before_script:
|
|||||||
if [[ $CHECK_FMT -eq 1 ]]; then
|
if [[ $CHECK_FMT -eq 1 ]]; then
|
||||||
rustup component add rustfmt
|
rustup component add rustfmt
|
||||||
fi
|
fi
|
||||||
|
- |
|
||||||
|
if [[ $NIGHTLY -eq 1 ]]; then
|
||||||
|
rustup toolchain install nightly
|
||||||
|
rustup default nightly
|
||||||
|
fi
|
||||||
- rustup target add "$TARGET"
|
- rustup target add "$TARGET"
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
|
@ -51,6 +51,7 @@ async-interface = ["async-trait"]
|
|||||||
# Debug/Test features
|
# Debug/Test features
|
||||||
debug-proc-macros = ["magical-macros/debug", "testutils-macros/debug"]
|
debug-proc-macros = ["magical-macros/debug", "testutils-macros/debug"]
|
||||||
test-electrum = ["electrum"]
|
test-electrum = ["electrum"]
|
||||||
|
test-md-docs = ["base64", "electrum"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
testutils = { path = "./testutils" }
|
testutils = { path = "./testutils" }
|
||||||
|
146
README.md
146
README.md
@ -1,7 +1,145 @@
|
|||||||
# Magical Bitcoin Wallet
|
<div align="center">
|
||||||
|
<h1>Magical Bitcoin Library</h1>
|
||||||
|
|
||||||
A modern, lightweight, descriptor-based wallet written in Rust!
|
<img src="./static/wizard.svg" width="220" />
|
||||||
|
|
||||||
## Getting Started
|
<p>
|
||||||
|
<strong>A modern, lightweight, descriptor-based wallet library written in Rust!</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
See the documentation at [magicalbitcoin.org](https://magicalbitcoin.org)
|
<p>
|
||||||
|
<a href="https://crates.io/crates/magical"><img alt="Crate Info" src="https://img.shields.io/crates/v/magical.svg"/></a>
|
||||||
|
<a href="https://docs.rs/magical/"><img alt="API Docs" src="https://img.shields.io/badge/docs.rs-magical-green"/></a>
|
||||||
|
<a href="https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html"><img alt="Rustc Version 1.45+" src="https://img.shields.io/badge/rustc-1.45%2B-lightgrey.svg"/></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>
|
||||||
|
<a href="https://magicalbitcoin.org">Project Homepage</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://docs.rs/magical">Documentation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 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(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3
src/doctest.rs
Normal file
3
src/doctest.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#[doc(include = "../README.md")]
|
||||||
|
#[cfg(doctest)]
|
||||||
|
pub struct ReadmeDoctests;
|
@ -25,6 +25,9 @@
|
|||||||
// only enables the `doc_cfg` feature when
|
// only enables the `doc_cfg` feature when
|
||||||
// the `docsrs` configuration attribute is defined
|
// the `docsrs` configuration attribute is defined
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![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;
|
pub extern crate bitcoin;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
@ -70,6 +73,8 @@ pub(crate) mod error;
|
|||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod descriptor;
|
pub mod descriptor;
|
||||||
|
#[cfg(feature = "test-md-docs")]
|
||||||
|
mod doctest;
|
||||||
pub(crate) mod psbt;
|
pub(crate) mod psbt;
|
||||||
pub(crate) mod types;
|
pub(crate) mod types;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
327
static/wizard.svg
Normal file
327
static/wizard.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 125 KiB |
Loading…
x
Reference in New Issue
Block a user