Merge #97: daemon: rename Minisafe to Liana
968a7d17a02bc776c38274fc9e61235163c22442 daemon: rename Minisafe to Liana (Antoine Poinsot) Pull request description: We decided on a new name for this project. Farewell Minisafe, "not a Bitcoin vault". To avoid conflicts with ongoing work in the GUI i have only made the change on the daemon for now. Fixes #91 ACKs for top commit: edouardparis: utACK 968a7d17a02bc776c38274fc9e61235163c22442 Tree-SHA512: e0d9324423b650bf4c8ec3084fb0002b2eb9b29e028f6f7c7be2062c10cc8ea3a2c3186689700495a0bfbd5c7cb75b549fffa06604bdd182c589aad3bbf61e0d
This commit is contained in:
commit
9f4d6ba064
@ -31,7 +31,7 @@ task:
|
||||
fingerprint_script:
|
||||
- rustc --version
|
||||
- cat Cargo.lock
|
||||
minisafed_build_script: cargo build --release
|
||||
lianad_build_script: cargo build --release
|
||||
|
||||
deps_script: apt update && apt install -y python3 python3-pip
|
||||
|
||||
@ -50,6 +50,6 @@ task:
|
||||
export BITCOIND_PATH=bitcoin-24.0rc1/bin/bitcoind
|
||||
|
||||
# Run the functional tests
|
||||
MINISAFED_PATH=$PWD/target/release/minisafed pytest $TEST_GROUP -vvv -n 2
|
||||
LIANAD_PATH=$PWD/target/release/lianad pytest $TEST_GROUP -vvv -n 2
|
||||
|
||||
before_cache_script: rm -rf $CARGO_HOME/registry/index
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
- nightly
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# See issue https://github.com/revault/minisafe/issues/69
|
||||
# See issue https://github.com/revault/liana/issues/69
|
||||
#- macOS-latest
|
||||
- windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -203,6 +203,24 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liana"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"base64",
|
||||
"dirs",
|
||||
"fern",
|
||||
"jsonrpc",
|
||||
"libc",
|
||||
"log",
|
||||
"miniscript",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.135"
|
||||
@ -235,24 +253,6 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "minisafe"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"base64",
|
||||
"dirs",
|
||||
"fern",
|
||||
"jsonrpc",
|
||||
"libc",
|
||||
"log",
|
||||
"miniscript",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniscript"
|
||||
version = "8.0.0"
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@ -1,21 +1,21 @@
|
||||
[package]
|
||||
name = "minisafe"
|
||||
name = "liana"
|
||||
version = "0.0.1"
|
||||
authors = ["Antoine Poinsot <darosior@protonmail.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/revault/minisafed"
|
||||
repository = "https://github.com/revault/liana"
|
||||
license-file = "LICENCE"
|
||||
keywords = ["bitcoin", "wallet", "safe", "script", "miniscript", "inheritance", "recovery"]
|
||||
description = "Minisafe wallet daemon"
|
||||
description = "Liana wallet daemon"
|
||||
exclude = [".github/", ".cirrus.yml", "tests/", "test_data/", "contrib/", "pyproject.toml"]
|
||||
|
||||
[[bin]]
|
||||
name = "minisafed"
|
||||
name = "lianad"
|
||||
path = "src/bin/daemon.rs"
|
||||
required-features = ["jsonrpc_server"]
|
||||
|
||||
[[bin]]
|
||||
name = "minisafe-cli"
|
||||
name = "liana-cli"
|
||||
path = "src/bin/cli.rs"
|
||||
required-features = ["jsonrpc_server"]
|
||||
|
||||
|
||||
2
LICENCE
2
LICENCE
@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022, The Minisafe developers
|
||||
Copyright (c) 2022, The Liana developers
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use minisafe::config::{config_folder_path, Config};
|
||||
use liana::config::{config_folder_path, Config};
|
||||
|
||||
use std::{
|
||||
env,
|
||||
@ -14,7 +14,7 @@ use std::os::unix::net::UnixStream;
|
||||
// Exits with error
|
||||
fn show_usage() {
|
||||
eprintln!("Usage:");
|
||||
eprintln!(" minisafe-cli [--conf conf_path] [--raw] <command> [<param 1> <param 2> ...]");
|
||||
eprintln!(" liana-cli [--conf conf_path] [--raw] <command> [<param 1> <param 2> ...]");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ fn rpc_request(method: String, params: Vec<String>) -> Json {
|
||||
object.insert("jsonrpc".to_string(), Json::String("2.0".to_string()));
|
||||
object.insert(
|
||||
"id".to_string(),
|
||||
Json::String(format!("minisafe-cli-{}", process::id())),
|
||||
Json::String(format!("liana-cli-{}", process::id())),
|
||||
);
|
||||
object.insert("method".to_string(), method);
|
||||
object.insert("params".to_string(), params);
|
||||
@ -94,7 +94,7 @@ fn socket_file(conf_file: Option<PathBuf>) -> PathBuf {
|
||||
[
|
||||
data_dir,
|
||||
config.bitcoin_config.network.to_string().as_str(),
|
||||
"minisafed_rpc",
|
||||
"lianad_rpc",
|
||||
]
|
||||
.iter()
|
||||
.collect()
|
||||
@ -160,7 +160,7 @@ fn main() {
|
||||
println!("{:#}", serde_json::json!({ "error": e }));
|
||||
} else {
|
||||
log::warn!(
|
||||
"minisafed response doesn't contain result or error: '{}'",
|
||||
"lianad response doesn't contain result or error: '{}'",
|
||||
response
|
||||
);
|
||||
println!("{:#}", response);
|
||||
|
||||
@ -5,7 +5,7 @@ use std::{
|
||||
process, thread, time,
|
||||
};
|
||||
|
||||
use minisafe::{config::Config, DaemonHandle};
|
||||
use liana::{config::Config, DaemonHandle};
|
||||
|
||||
fn parse_args(args: Vec<String>) -> Option<PathBuf> {
|
||||
if args.len() == 1 {
|
||||
@ -61,7 +61,7 @@ fn main() {
|
||||
|
||||
let daemon = DaemonHandle::start_default(config).unwrap_or_else(|e| {
|
||||
// The panic hook will log::error
|
||||
panic!("Starting Minisafe daemon: {}", e);
|
||||
panic!("Starting Liana daemon: {}", e);
|
||||
});
|
||||
daemon
|
||||
.rpc_server()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! # Minisafe commands
|
||||
//! # Liana commands
|
||||
//!
|
||||
//! External interface to the Minisafe daemon.
|
||||
//! External interface to the Liana daemon.
|
||||
|
||||
mod utils;
|
||||
|
||||
@ -632,7 +632,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn getinfo() {
|
||||
let ms = DummyMinisafe::new();
|
||||
let ms = DummyLiana::new();
|
||||
// We can query getinfo
|
||||
ms.handle.control.get_info();
|
||||
ms.shutdown();
|
||||
@ -640,7 +640,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn getnewaddress() {
|
||||
let ms = DummyMinisafe::new();
|
||||
let ms = DummyLiana::new();
|
||||
|
||||
let control = &ms.handle.control;
|
||||
// We can get an address
|
||||
@ -661,7 +661,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn create_spend() {
|
||||
let ms = DummyMinisafe::new();
|
||||
let ms = DummyLiana::new();
|
||||
let control = &ms.handle.control;
|
||||
|
||||
// Arguments sanity checking
|
||||
@ -777,7 +777,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_spend() {
|
||||
let ms = DummyMinisafe::new();
|
||||
let ms = DummyLiana::new();
|
||||
let control = &ms.handle.control;
|
||||
let mut db_conn = control.db().lock().unwrap().connection();
|
||||
|
||||
|
||||
@ -144,11 +144,11 @@ impl From<std::io::Error> for ConfigError {
|
||||
|
||||
impl std::error::Error for ConfigError {}
|
||||
|
||||
/// Get the absolute path to the minisafe configuration folder.
|
||||
/// Get the absolute path to the liana configuration folder.
|
||||
///
|
||||
/// It's a "minisafe/<network>/" directory in the XDG standard configuration directory for
|
||||
/// all OSes but Linux-based ones, for which it's `~/.minisafe/<network>/`.
|
||||
/// There is only one config file at `minisafe/config.toml`, which specifies the network.
|
||||
/// It's a "liana/<network>/" directory in the XDG standard configuration directory for
|
||||
/// all OSes but Linux-based ones, for which it's `~/.liana/<network>/`.
|
||||
/// There is only one config file at `liana/config.toml`, which specifies the network.
|
||||
/// Rationale: we want to have the database, RPC socket, etc.. in the same folder as the
|
||||
/// configuration file but for Linux the XDG specifoes a data directory (`~/.local/share/`)
|
||||
/// different from the configuration one (`~/.config/`).
|
||||
@ -161,10 +161,10 @@ pub fn config_folder_path() -> Option<PathBuf> {
|
||||
|
||||
if let Some(mut path) = configs_dir {
|
||||
#[cfg(target_os = "linux")]
|
||||
path.push(".minisafe");
|
||||
path.push(".liana");
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
path.push("Minisafe");
|
||||
path.push("Liana");
|
||||
|
||||
return Some(path);
|
||||
}
|
||||
@ -174,7 +174,7 @@ pub fn config_folder_path() -> Option<PathBuf> {
|
||||
|
||||
fn config_file_path() -> Option<PathBuf> {
|
||||
config_folder_path().map(|mut path| {
|
||||
path.push("minisafe.toml");
|
||||
path.push("liana.toml");
|
||||
path
|
||||
})
|
||||
}
|
||||
@ -306,17 +306,17 @@ mod tests {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
assert!(filepath.as_path().starts_with("/home/"));
|
||||
assert!(filepath.as_path().ends_with(".minisafe/minisafe.toml"));
|
||||
assert!(filepath.as_path().ends_with(".liana/liana.toml"));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
assert!(filepath
|
||||
.as_path()
|
||||
.ends_with("Library/Application Support/Minisafe/minisafe.toml"));
|
||||
.ends_with("Library/Application Support/Liana/liana.toml"));
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
assert!(filepath
|
||||
.as_path()
|
||||
.ends_with(r#"AppData\Roaming\Minisafe\minisafe.toml"#));
|
||||
.ends_with(r#"AppData\Roaming\Liana\liana.toml"#));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
///! Database interface for Minisafe.
|
||||
///! Database interface for Liana.
|
||||
///!
|
||||
///! Record wallet metadata, spent and unspent coins, ongoing transactions.
|
||||
pub mod sqlite;
|
||||
|
||||
@ -563,7 +563,7 @@ mod tests {
|
||||
fs::create_dir_all(&tmp_dir).unwrap();
|
||||
let secp = secp256k1::Secp256k1::verification_only();
|
||||
|
||||
let db_path: path::PathBuf = [tmp_dir.as_path(), path::Path::new("minisafed.sqlite3")]
|
||||
let db_path: path::PathBuf = [tmp_dir.as_path(), path::Path::new("lianad.sqlite3")]
|
||||
.iter()
|
||||
.collect();
|
||||
let options = dummy_options();
|
||||
@ -578,7 +578,7 @@ mod tests {
|
||||
fs::create_dir_all(&tmp_dir).unwrap();
|
||||
let secp = secp256k1::Secp256k1::verification_only();
|
||||
|
||||
let db_path: path::PathBuf = [tmp_dir.as_path(), path::Path::new("minisafed.sqlite3")]
|
||||
let db_path: path::PathBuf = [tmp_dir.as_path(), path::Path::new("lianad.sqlite3")]
|
||||
.iter()
|
||||
.collect();
|
||||
assert!(SqliteDb::new(db_path.clone(), None, &secp)
|
||||
|
||||
@ -101,7 +101,7 @@ fn connection_handler(
|
||||
let req_id = req.id.clone();
|
||||
if &req.method == "stop" {
|
||||
shutdown.store(true, atomic::Ordering::Relaxed);
|
||||
log::info!("Stopping the minisafe daemon.");
|
||||
log::info!("Stopping the liana daemon.");
|
||||
}
|
||||
|
||||
log::trace!("JSONRPC request: {:?}", serde_json::to_string(&req));
|
||||
@ -146,7 +146,7 @@ pub fn rpcserver_loop(
|
||||
|
||||
let handler_id = connections_counter.load(atomic::Ordering::Relaxed);
|
||||
thread::Builder::new()
|
||||
.name(format!("minisafe-jsonrpc-{}", handler_id))
|
||||
.name(format!("liana-jsonrpc-{}", handler_id))
|
||||
.spawn({
|
||||
let control = daemon_control.clone();
|
||||
let counter = connections_counter.clone();
|
||||
@ -260,7 +260,7 @@ mod tests {
|
||||
#[test]
|
||||
fn command_read_single() {
|
||||
let socket_path = env::temp_dir().join(format!(
|
||||
"minisafed-jsonrpc-socket-{}-{:?}",
|
||||
"lianad-jsonrpc-socket-{}-{:?}",
|
||||
process::id(),
|
||||
thread::current().id()
|
||||
));
|
||||
@ -287,7 +287,7 @@ mod tests {
|
||||
#[test]
|
||||
fn command_read_parts() {
|
||||
let socket_path = env::temp_dir().join(format!(
|
||||
"minisafed-jsonrpc-socket-{}-{:?}",
|
||||
"lianad-jsonrpc-socket-{}-{:?}",
|
||||
process::id(),
|
||||
thread::current().id()
|
||||
));
|
||||
@ -320,7 +320,7 @@ mod tests {
|
||||
#[test]
|
||||
fn command_read_multiple() {
|
||||
let socket_path = env::temp_dir().join(format!(
|
||||
"minisafed-jsonrpc-socket-{}-{:?}",
|
||||
"lianad-jsonrpc-socket-{}-{:?}",
|
||||
process::id(),
|
||||
thread::current().id()
|
||||
));
|
||||
@ -367,7 +367,7 @@ mod tests {
|
||||
#[test]
|
||||
fn command_read_linebreak() {
|
||||
let socket_path = env::temp_dir().join(format!(
|
||||
"minisafed-jsonrpc-socket-{}-{:?}",
|
||||
"lianad-jsonrpc-socket-{}-{:?}",
|
||||
process::id(),
|
||||
thread::current().id()
|
||||
));
|
||||
@ -400,12 +400,12 @@ mod tests {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[test]
|
||||
fn server_sanity_check() {
|
||||
let ms = DummyMinisafe::new();
|
||||
let ms = DummyLiana::new();
|
||||
let socket_path: path::PathBuf = [
|
||||
ms.tmp_dir.as_path(),
|
||||
path::Path::new("d"),
|
||||
path::Path::new("bitcoin"),
|
||||
path::Path::new("minisafed_rpc"),
|
||||
path::Path::new("lianad_rpc"),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
18
src/lib.rs
18
src/lib.rs
@ -163,7 +163,7 @@ fn setup_sqlite(
|
||||
fresh_data_dir: bool,
|
||||
secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>,
|
||||
) -> Result<SqliteDb, StartupError> {
|
||||
let db_path: path::PathBuf = [data_dir, path::Path::new("minisafed.sqlite3")]
|
||||
let db_path: path::PathBuf = [data_dir, path::Path::new("lianad.sqlite3")]
|
||||
.iter()
|
||||
.collect();
|
||||
let options = if fresh_data_dir {
|
||||
@ -189,7 +189,7 @@ fn setup_bitcoind(
|
||||
fresh_data_dir: bool,
|
||||
) -> Result<BitcoinD, StartupError> {
|
||||
// Now set up the bitcoind interface
|
||||
let wo_path: path::PathBuf = [data_dir, path::Path::new("minisafed_watchonly_wallet")]
|
||||
let wo_path: path::PathBuf = [data_dir, path::Path::new("lianad_watchonly_wallet")]
|
||||
.iter()
|
||||
.collect();
|
||||
let bitcoind = BitcoinD::new(
|
||||
@ -247,7 +247,7 @@ pub struct DaemonHandle {
|
||||
}
|
||||
|
||||
impl DaemonHandle {
|
||||
/// This starts the Minisafe daemon. Call `shutdown` to shut it down.
|
||||
/// This starts the Liana daemon. Call `shutdown` to shut it down.
|
||||
///
|
||||
/// You may specify a custom Bitcoin interface through the `bitcoin` parameter. If `None`, the
|
||||
/// default Bitcoin interface (`bitcoind` JSONRPC) will be used.
|
||||
@ -329,7 +329,7 @@ impl DaemonHandle {
|
||||
})
|
||||
}
|
||||
|
||||
/// Start the Minisafe daemon with the default Bitcoin and database interfaces (`bitcoind` RPC
|
||||
/// Start the Liana daemon with the default Bitcoin and database interfaces (`bitcoind` RPC
|
||||
/// and SQLite).
|
||||
pub fn start_default(config: Config) -> Result<DaemonHandle, StartupError> {
|
||||
DaemonHandle::start(config, Option::<BitcoinD>::None, Option::<SqliteDb>::None)
|
||||
@ -351,7 +351,7 @@ impl DaemonHandle {
|
||||
.expect("Didn't fail at startup, must not now")
|
||||
.as_path(),
|
||||
path::Path::new(&control.config.bitcoin_config.network.to_string()),
|
||||
path::Path::new("minisafed_rpc"),
|
||||
path::Path::new("lianad_rpc"),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
@ -367,12 +367,12 @@ impl DaemonHandle {
|
||||
}
|
||||
|
||||
// NOTE: this moves out the data as it should not be reused after shutdown
|
||||
/// Shut down the Minisafe daemon.
|
||||
/// Shut down the Liana daemon.
|
||||
pub fn shutdown(self) {
|
||||
self.bitcoin_poller.stop();
|
||||
}
|
||||
|
||||
// We need a shutdown utility that does not move for implementing Drop for the DummyMinisafe
|
||||
// We need a shutdown utility that does not move for implementing Drop for the DummyLiana
|
||||
#[cfg(test)]
|
||||
pub fn test_shutdown(&mut self) {
|
||||
self.bitcoin_poller.test_stop();
|
||||
@ -553,7 +553,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// TODO: we could move the dummy bitcoind thread stuff to the bitcoind module to test the
|
||||
// bitcoind interface, and use the DummyMinisafe from testutils to sanity check the startup.
|
||||
// bitcoind interface, and use the DummyLiana from testutils to sanity check the startup.
|
||||
// Note that startup as checked by this unit test is also tested in the functional test
|
||||
// framework.
|
||||
#[test]
|
||||
@ -566,7 +566,7 @@ mod tests {
|
||||
let wo_path: path::PathBuf = [
|
||||
data_dir.as_path(),
|
||||
path::Path::new("bitcoin"),
|
||||
path::Path::new("minisafed_watchonly_wallet"),
|
||||
path::Path::new("lianad_watchonly_wallet"),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
@ -286,7 +286,7 @@ impl DatabaseConnection for DummyDbConn {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyMinisafe {
|
||||
pub struct DummyLiana {
|
||||
pub tmp_dir: path::PathBuf,
|
||||
pub handle: DaemonHandle,
|
||||
}
|
||||
@ -302,15 +302,15 @@ fn uid() -> usize {
|
||||
|
||||
pub fn tmp_dir() -> path::PathBuf {
|
||||
env::temp_dir().join(format!(
|
||||
"minisafed-{}-{:?}-{}",
|
||||
"lianad-{}-{:?}-{}",
|
||||
process::id(),
|
||||
thread::current().id(),
|
||||
uid(),
|
||||
))
|
||||
}
|
||||
|
||||
impl DummyMinisafe {
|
||||
pub fn new() -> DummyMinisafe {
|
||||
impl DummyLiana {
|
||||
pub fn new() -> DummyLiana {
|
||||
let tmp_dir = tmp_dir();
|
||||
fs::create_dir_all(&tmp_dir).unwrap();
|
||||
// Use a shorthand for 'datadir', to avoid overflowing SUN_LEN on MacOS.
|
||||
@ -338,7 +338,7 @@ impl DummyMinisafe {
|
||||
|
||||
let db = sync::Arc::from(sync::RwLock::from(DummyDb::new()));
|
||||
let handle = DaemonHandle::start(config, Some(DummyBitcoind {}), Some(db)).unwrap();
|
||||
DummyMinisafe { tmp_dir, handle }
|
||||
DummyLiana { tmp_dir, handle }
|
||||
}
|
||||
|
||||
#[cfg(feature = "jsonrpc_server")]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Minisafed blackbox tests
|
||||
## Lianad blackbox tests
|
||||
|
||||
Here we test `minisafed` by starting it on a regression testing Bitcoin network,
|
||||
Here we test `lianad` by starting it on a regression testing Bitcoin network,
|
||||
and by then talking to it as an user would, from the outside.
|
||||
|
||||
Python scripts are used for the automation, and specifically the [`pytest` framework](https://docs.pytest.org/en/stable/index.html).
|
||||
|
||||
@ -3,7 +3,7 @@ from bip380.descriptors import Descriptor
|
||||
from concurrent import futures
|
||||
from ephemeral_port_reserve import reserve
|
||||
from test_framework.bitcoind import Bitcoind
|
||||
from test_framework.minisafed import Minisafed
|
||||
from test_framework.lianad import Lianad
|
||||
from test_framework.utils import (
|
||||
EXECUTOR_WORKERS,
|
||||
)
|
||||
@ -24,7 +24,7 @@ ATTEMPTS = {}
|
||||
def test_base_dir():
|
||||
d = os.getenv("TEST_DIR", "/tmp")
|
||||
|
||||
directory = tempfile.mkdtemp(prefix="minisafed-tests-", dir=d)
|
||||
directory = tempfile.mkdtemp(prefix="lianad-tests-", dir=d)
|
||||
print("Running tests in {}".format(directory))
|
||||
|
||||
yield directory
|
||||
@ -114,8 +114,8 @@ def bitcoind(directory):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def minisafed(bitcoind, directory):
|
||||
datadir = os.path.join(directory, "minisafed")
|
||||
def lianad(bitcoind, directory):
|
||||
datadir = os.path.join(directory, "lianad")
|
||||
os.makedirs(datadir, exist_ok=True)
|
||||
bitcoind_cookie = os.path.join(bitcoind.bitcoin_dir, "regtest", ".cookie")
|
||||
|
||||
@ -123,7 +123,7 @@ def minisafed(bitcoind, directory):
|
||||
owner_xpub = owner_hd.get_xpub()
|
||||
main_desc = Descriptor.from_str(f"wsh(or_d(pk({owner_xpub}/<0;1>/*),and_v(v:pkh(tpubD9vQiBdDxYzU4cVFtApWj4devZrvcfWaPXX1zHdDc7GPfUsDKqGnbhraccfm7BAXgRgUbVQUV2v2o4NitjGEk7hpbuP85kvBrD4ahFDtNBJ/<0;1>/*),older(65000))))")
|
||||
|
||||
minisafed = Minisafed(
|
||||
lianad = Lianad(
|
||||
datadir,
|
||||
owner_hd,
|
||||
main_desc,
|
||||
@ -132,10 +132,10 @@ def minisafed(bitcoind, directory):
|
||||
)
|
||||
|
||||
try:
|
||||
minisafed.start()
|
||||
yield minisafed
|
||||
lianad.start()
|
||||
yield lianad
|
||||
except Exception:
|
||||
minisafed.cleanup()
|
||||
lianad.cleanup()
|
||||
raise
|
||||
|
||||
minisafed.cleanup()
|
||||
lianad.cleanup()
|
||||
|
||||
@ -4,88 +4,88 @@ from fixtures import *
|
||||
from test_framework.utils import wait_for, get_txid, spend_coins
|
||||
|
||||
|
||||
def get_coin(minisafed, outpoint_or_txid):
|
||||
def get_coin(lianad, outpoint_or_txid):
|
||||
return next(
|
||||
c
|
||||
for c in minisafed.rpc.listcoins()["coins"]
|
||||
for c in lianad.rpc.listcoins()["coins"]
|
||||
if outpoint_or_txid in c["outpoint"]
|
||||
)
|
||||
|
||||
|
||||
def test_reorg_detection(minisafed, bitcoind):
|
||||
def test_reorg_detection(lianad, bitcoind):
|
||||
"""Test we detect block chain reorganization under various conditions."""
|
||||
initial_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# Re-mine the last block. We should detect it as a reorg.
|
||||
bitcoind.invalidate_remine(initial_height)
|
||||
minisafed.wait_for_logs(
|
||||
lianad.wait_for_logs(
|
||||
["Block chain reorganization detected.", "Tip was rolled back."]
|
||||
)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# Same if we re-mine the next-to-last block.
|
||||
bitcoind.invalidate_remine(initial_height - 1)
|
||||
minisafed.wait_for_logs(
|
||||
lianad.wait_for_logs(
|
||||
["Block chain reorganization detected.", "Tip was rolled back."]
|
||||
)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# Same if we re-mine a deep block.
|
||||
bitcoind.invalidate_remine(initial_height - 50)
|
||||
minisafed.wait_for_logs(
|
||||
lianad.wait_for_logs(
|
||||
["Block chain reorganization detected.", "Tip was rolled back."]
|
||||
)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# Same if the new chain is longer.
|
||||
bitcoind.simple_reorg(initial_height - 10, shift=20)
|
||||
minisafed.wait_for_logs(
|
||||
lianad.wait_for_logs(
|
||||
["Block chain reorganization detected.", "Tip was rolled back."]
|
||||
)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height + 10)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height + 10)
|
||||
|
||||
|
||||
def test_reorg_exclusion(minisafed, bitcoind):
|
||||
def test_reorg_exclusion(lianad, bitcoind):
|
||||
"""Test the unconfirmation by a reorg of a coin in various states."""
|
||||
initial_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# A confirmed received coin
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 1)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 1)
|
||||
coin_a = minisafed.rpc.listcoins()["coins"][0]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1)
|
||||
coin_a = lianad.rpc.listcoins()["coins"][0]
|
||||
|
||||
# A confirmed and 'spending' (unconfirmed spend) coin
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 2)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 2)
|
||||
coin_b = get_coin(minisafed, txid)
|
||||
b_spend_tx = spend_coins(minisafed, bitcoind, [coin_b])
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 2)
|
||||
coin_b = get_coin(lianad, txid)
|
||||
b_spend_tx = spend_coins(lianad, bitcoind, [coin_b])
|
||||
|
||||
# A confirmed and spent coin
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 3)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 3)
|
||||
coin_c = get_coin(minisafed, txid)
|
||||
c_spend_tx = spend_coins(minisafed, bitcoind, [coin_c])
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 3)
|
||||
coin_c = get_coin(lianad, txid)
|
||||
c_spend_tx = spend_coins(lianad, bitcoind, [coin_c])
|
||||
bitcoind.generate_block(1, wait_for_mempool=1)
|
||||
|
||||
# Reorg the chain down to the initial height, excluding all transactions.
|
||||
current_height = bitcoind.rpc.getblockcount()
|
||||
bitcoind.simple_reorg(initial_height, shift=-1)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == current_height + 1)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == current_height + 1)
|
||||
|
||||
# They must all be marked as unconfirmed.
|
||||
new_coin_a = get_coin(minisafed, coin_a["outpoint"])
|
||||
new_coin_a = get_coin(lianad, coin_a["outpoint"])
|
||||
assert new_coin_a["block_height"] is None
|
||||
new_coin_b = get_coin(minisafed, coin_b["outpoint"])
|
||||
new_coin_b = get_coin(lianad, coin_b["outpoint"])
|
||||
assert new_coin_b["block_height"] is None
|
||||
new_coin_c = get_coin(minisafed, coin_c["outpoint"])
|
||||
new_coin_c = get_coin(lianad, coin_c["outpoint"])
|
||||
assert new_coin_c["block_height"] is None
|
||||
|
||||
# And if we now confirm everything, they'll be marked as such. The one that was 'spending'
|
||||
@ -99,13 +99,13 @@ def test_reorg_exclusion(minisafed, bitcoind):
|
||||
bitcoind.rpc.sendrawtransaction(c_spend_tx)
|
||||
bitcoind.generate_block(1, wait_for_mempool=5)
|
||||
new_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == new_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == new_height)
|
||||
assert all(
|
||||
c["block_height"] == new_height for c in minisafed.rpc.listcoins()["coins"]
|
||||
), (minisafed.rpc.listcoins()["coins"], new_height)
|
||||
c["block_height"] == new_height for c in lianad.rpc.listcoins()["coins"]
|
||||
), (lianad.rpc.listcoins()["coins"], new_height)
|
||||
new_coin_b = next(
|
||||
c
|
||||
for c in minisafed.rpc.listcoins()["coins"]
|
||||
for c in lianad.rpc.listcoins()["coins"]
|
||||
if coin_b["outpoint"] == c["outpoint"]
|
||||
)
|
||||
b_spend_txid = get_txid(b_spend_tx)
|
||||
@ -113,7 +113,7 @@ def test_reorg_exclusion(minisafed, bitcoind):
|
||||
assert new_coin_b["spend_info"]["height"] == new_height
|
||||
new_coin_c = next(
|
||||
c
|
||||
for c in minisafed.rpc.listcoins()["coins"]
|
||||
for c in lianad.rpc.listcoins()["coins"]
|
||||
if coin_c["outpoint"] == c["outpoint"]
|
||||
)
|
||||
c_spend_txid = get_txid(c_spend_tx)
|
||||
@ -123,8 +123,8 @@ def test_reorg_exclusion(minisafed, bitcoind):
|
||||
# TODO: maybe test with some malleation for the deposit and spending txs?
|
||||
|
||||
|
||||
def spend_confirmed_noticed(minisafed, outpoint):
|
||||
c = get_coin(minisafed, outpoint)
|
||||
def spend_confirmed_noticed(lianad, outpoint):
|
||||
c = get_coin(lianad, outpoint)
|
||||
if c["spend_info"] is None:
|
||||
return False
|
||||
if c["spend_info"]["height"] is None:
|
||||
@ -132,53 +132,53 @@ def spend_confirmed_noticed(minisafed, outpoint):
|
||||
return True
|
||||
|
||||
|
||||
def test_reorg_status_recovery(minisafed, bitcoind):
|
||||
def test_reorg_status_recovery(lianad, bitcoind):
|
||||
"""
|
||||
Test the coins that were not unconfirmed recover their initial state after a reorg.
|
||||
"""
|
||||
list_coins = lambda: minisafed.rpc.listcoins()["coins"]
|
||||
list_coins = lambda: lianad.rpc.listcoins()["coins"]
|
||||
|
||||
# Create two confirmed coins. Note how we take the initial_height after having
|
||||
# mined them, as we'll reorg back to this height and due to anti fee-sniping
|
||||
# these deposit transactions might not be valid anymore!
|
||||
addresses = (minisafed.rpc.getnewaddress()["address"] for _ in range(2))
|
||||
addresses = (lianad.rpc.getnewaddress()["address"] for _ in range(2))
|
||||
txids = [bitcoind.rpc.sendtoaddress(addr, 0.5670) for addr in addresses]
|
||||
bitcoind.generate_block(1, wait_for_mempool=txids)
|
||||
initial_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == initial_height)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == initial_height)
|
||||
|
||||
# Both coins are confirmed. Spend the second one then get their infos.
|
||||
wait_for(lambda: len(list_coins()) == 2)
|
||||
wait_for(lambda: all(c["block_height"] is not None for c in list_coins()))
|
||||
coin_b = get_coin(minisafed, txids[1])
|
||||
spend_coins(minisafed, bitcoind, [coin_b])
|
||||
coin_b = get_coin(lianad, txids[1])
|
||||
spend_coins(lianad, bitcoind, [coin_b])
|
||||
bitcoind.generate_block(1, wait_for_mempool=1)
|
||||
wait_for(lambda: spend_confirmed_noticed(minisafed, coin_b["outpoint"]))
|
||||
coin_a = get_coin(minisafed, txids[0])
|
||||
coin_b = get_coin(minisafed, txids[1])
|
||||
wait_for(lambda: spend_confirmed_noticed(lianad, coin_b["outpoint"]))
|
||||
coin_a = get_coin(lianad, txids[0])
|
||||
coin_b = get_coin(lianad, txids[1])
|
||||
|
||||
# Reorg the chain down to the initial height without shifting nor malleating
|
||||
# any transaction. The coin info should be identical (except the transaction
|
||||
# spending the second coin will be mined at the height the reorg happened).
|
||||
bitcoind.simple_reorg(initial_height, shift=0)
|
||||
new_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == new_height)
|
||||
new_coin_a = get_coin(minisafed, coin_a["outpoint"])
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == new_height)
|
||||
new_coin_a = get_coin(lianad, coin_a["outpoint"])
|
||||
assert coin_a == new_coin_a
|
||||
new_coin_b = get_coin(minisafed, coin_b["outpoint"])
|
||||
new_coin_b = get_coin(lianad, coin_b["outpoint"])
|
||||
coin_b["spend_info"]["height"] = initial_height
|
||||
assert new_coin_b == coin_b
|
||||
|
||||
|
||||
def test_rescan_edge_cases(minisafed, bitcoind):
|
||||
def test_rescan_edge_cases(lianad, bitcoind):
|
||||
"""Test some specific cases that could arise when rescanning the chain."""
|
||||
initial_tip = bitcoind.rpc.getblockheader(bitcoind.rpc.getbestblockhash())
|
||||
|
||||
# Some helpers
|
||||
list_coins = lambda: minisafed.rpc.listcoins()["coins"]
|
||||
list_coins = lambda: lianad.rpc.listcoins()["coins"]
|
||||
sorted_coins = lambda: sorted(list_coins(), key=lambda c: c["outpoint"])
|
||||
wait_synced = lambda: wait_for(
|
||||
lambda: minisafed.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
lambda: lianad.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
)
|
||||
|
||||
def reorg_shift(height, txs):
|
||||
@ -196,12 +196,12 @@ def test_rescan_edge_cases(minisafed, bitcoind):
|
||||
# rebroadcast them on reorgs.
|
||||
txs = []
|
||||
for _ in range(3):
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
amount = 0.356
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, amount)
|
||||
txs.append(bitcoind.rpc.gettransaction(txid)["hex"])
|
||||
wait_for(lambda: len(list_coins()) == 3)
|
||||
txs.append(spend_coins(minisafed, bitcoind, list_coins()[:2]))
|
||||
txs.append(spend_coins(lianad, bitcoind, list_coins()[:2]))
|
||||
bitcoind.generate_block(1, wait_for_mempool=4)
|
||||
wait_synced()
|
||||
|
||||
@ -214,27 +214,27 @@ def test_rescan_edge_cases(minisafed, bitcoind):
|
||||
coins_before = sorted_coins()
|
||||
outpoints_before = set(c["outpoint"] for c in coins_before)
|
||||
bitcoind.generate_block(1)
|
||||
minisafed.restart_fresh(bitcoind)
|
||||
lianad.restart_fresh(bitcoind)
|
||||
assert len(list_coins()) == 0
|
||||
|
||||
# We can be stopped while we are rescanning
|
||||
minisafed.rpc.startrescan(initial_tip["time"])
|
||||
minisafed.stop()
|
||||
minisafed.start()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
lianad.rpc.startrescan(initial_tip["time"])
|
||||
lianad.stop()
|
||||
lianad.start()
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
assert coins_before == sorted_coins()
|
||||
|
||||
# Lose our state again
|
||||
bitcoind.generate_block(1)
|
||||
minisafed.restart_fresh(bitcoind)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
lianad.restart_fresh(bitcoind)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
assert len(list_coins()) == 0
|
||||
|
||||
# There can be a reorg when we start rescanning
|
||||
reorg_shift(initial_tip["height"], txs)
|
||||
minisafed.rpc.startrescan(initial_tip["time"])
|
||||
lianad.rpc.startrescan(initial_tip["time"])
|
||||
wait_synced()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
assert len(sorted_coins()) == len(coins_before)
|
||||
assert all(c["outpoint"] in outpoints_before for c in list_coins())
|
||||
|
||||
@ -244,15 +244,15 @@ def test_rescan_edge_cases(minisafed, bitcoind):
|
||||
|
||||
# Lose our state again
|
||||
bitcoind.generate_block(1)
|
||||
minisafed.restart_fresh(bitcoind)
|
||||
lianad.restart_fresh(bitcoind)
|
||||
wait_synced()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
assert len(list_coins()) == 0
|
||||
|
||||
# We can be rescanning when a reorg happens
|
||||
minisafed.rpc.startrescan(initial_tip["time"])
|
||||
lianad.rpc.startrescan(initial_tip["time"])
|
||||
reorg_shift(initial_tip["height"] + 1, txs)
|
||||
wait_synced()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
assert len(sorted_coins()) == len(coins_before)
|
||||
assert all(c["outpoint"] in outpoints_before for c in list_coins())
|
||||
|
||||
@ -71,7 +71,7 @@ class Bitcoind(TailableProc):
|
||||
# An RPC interface with our internal wallet, and an RPC interface with no
|
||||
# wallet to be able to call 'unloadwallet' on any wallet.
|
||||
self.rpc = BitcoindRpcInterface(
|
||||
bitcoin_dir, "regtest", rpcport, wallet="minisafed-tests"
|
||||
bitcoin_dir, "regtest", rpcport, wallet="lianad-tests"
|
||||
)
|
||||
self.node_rpc = BitcoindRpcInterface(bitcoin_dir, "regtest", rpcport)
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ from test_framework.utils import (
|
||||
TailableProc,
|
||||
VERBOSE,
|
||||
LOG_LEVEL,
|
||||
MINISAFED_PATH,
|
||||
LIANAD_PATH,
|
||||
wait_for,
|
||||
)
|
||||
from test_framework.serializations import (
|
||||
@ -25,7 +25,7 @@ from test_framework.serializations import (
|
||||
)
|
||||
|
||||
|
||||
class Minisafed(TailableProc):
|
||||
class Lianad(TailableProc):
|
||||
def __init__(
|
||||
self,
|
||||
datadir,
|
||||
@ -44,8 +44,8 @@ class Minisafed(TailableProc):
|
||||
self.receive_desc, self.change_desc = multi_desc.singlepath_descriptors()
|
||||
|
||||
self.conf_file = os.path.join(datadir, "config.toml")
|
||||
self.cmd_line = [MINISAFED_PATH, "--conf", f"{self.conf_file}"]
|
||||
socket_path = os.path.join(os.path.join(datadir, "regtest"), "minisafed_rpc")
|
||||
self.cmd_line = [LIANAD_PATH, "--conf", f"{self.conf_file}"]
|
||||
socket_path = os.path.join(os.path.join(datadir, "regtest"), "lianad_rpc")
|
||||
self.rpc = UnixDomainSocketRpc(socket_path)
|
||||
|
||||
with open(self.conf_file, "w") as f:
|
||||
@ -153,7 +153,7 @@ class Minisafed(TailableProc):
|
||||
self.stop()
|
||||
dir_path = os.path.join(self.datadir, "regtest")
|
||||
shutil.rmtree(dir_path)
|
||||
wallet_path = os.path.join(dir_path, "minisafed_watchonly_wallet")
|
||||
wallet_path = os.path.join(dir_path, "lianad_watchonly_wallet")
|
||||
bitcoind.node_rpc.unloadwallet(wallet_path)
|
||||
self.start()
|
||||
wait_for(
|
||||
@ -174,7 +174,7 @@ class Minisafed(TailableProc):
|
||||
try:
|
||||
self.rpc.stop()
|
||||
self.wait_for_log(
|
||||
"Stopping the minisafe daemon.",
|
||||
"Stopping the liana daemon.",
|
||||
)
|
||||
self.proc.wait(timeout)
|
||||
except Exception as e:
|
||||
@ -17,9 +17,9 @@ VERBOSE = os.getenv("VERBOSE", "0") == "1"
|
||||
LOG_LEVEL = os.getenv("LOG_LEVEL", "debug")
|
||||
assert LOG_LEVEL in ["trace", "debug", "info", "warn", "error"]
|
||||
DEFAULT_MS_PATH = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "target/debug/minisafed"
|
||||
os.path.dirname(__file__), "..", "..", "target/debug/lianad"
|
||||
)
|
||||
MINISAFED_PATH = os.getenv("MINISAFED_PATH", DEFAULT_MS_PATH)
|
||||
LIANAD_PATH = os.getenv("LIANAD_PATH", DEFAULT_MS_PATH)
|
||||
DEFAULT_BITCOIND_PATH = "bitcoind"
|
||||
BITCOIND_PATH = os.getenv("BITCOIND_PATH", DEFAULT_BITCOIND_PATH)
|
||||
|
||||
@ -53,7 +53,7 @@ def get_txid(hex_tx):
|
||||
return tx.txid().hex()
|
||||
|
||||
|
||||
def spend_coins(minisafed, bitcoind, coins):
|
||||
def spend_coins(lianad, bitcoind, coins):
|
||||
"""Spend these coins, no matter how.
|
||||
This will create a single transaction spending them all at once at the minimum
|
||||
feerate. This will broadcast but not confirm the transaction.
|
||||
@ -65,10 +65,10 @@ def spend_coins(minisafed, bitcoind, coins):
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): total_value - 11 - 31 - 300 * len(coins)
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, [c["outpoint"] for c in coins], 1)
|
||||
res = lianad.rpc.createspend(destinations, [c["outpoint"] for c in coins], 1)
|
||||
|
||||
signed_psbt = minisafed.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
finalized_psbt = minisafed.finalize_psbt(signed_psbt)
|
||||
signed_psbt = lianad.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
finalized_psbt = lianad.finalize_psbt(signed_psbt)
|
||||
tx = finalized_psbt.tx.serialize_with_witness().hex()
|
||||
bitcoind.rpc.sendrawtransaction(tx)
|
||||
|
||||
@ -175,7 +175,7 @@ class UnixDomainSocketRpc(object):
|
||||
except json.JSONDecodeError:
|
||||
# There is more to read, continue
|
||||
# FIXME: this is a workaround for large reads taken from revaultd.
|
||||
# We should use the '\n' marker instead since minisafed uses that.
|
||||
# We should use the '\n' marker instead since lianad uses that.
|
||||
continue
|
||||
|
||||
def __getattr__(self, name):
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from fixtures import *
|
||||
|
||||
|
||||
def test_startup(minisafed):
|
||||
def test_startup(lianad):
|
||||
pass
|
||||
|
||||
@ -9,36 +9,36 @@ from test_framework.serializations import PSBT, PSBT_IN_PARTIAL_SIG
|
||||
from test_framework.utils import wait_for, COIN, RpcError, get_txid, spend_coins
|
||||
|
||||
|
||||
def test_getinfo(minisafed):
|
||||
res = minisafed.rpc.getinfo()
|
||||
def test_getinfo(lianad):
|
||||
res = lianad.rpc.getinfo()
|
||||
assert res["version"] == "0.1"
|
||||
assert res["network"] == "regtest"
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == 101)
|
||||
res = minisafed.rpc.getinfo()
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == 101)
|
||||
res = lianad.rpc.getinfo()
|
||||
assert res["sync"] == 1.0
|
||||
assert "main" in res["descriptors"]
|
||||
assert res["rescan_progress"] is None
|
||||
|
||||
|
||||
def test_getaddress(minisafed):
|
||||
res = minisafed.rpc.getnewaddress()
|
||||
def test_getaddress(lianad):
|
||||
res = lianad.rpc.getnewaddress()
|
||||
assert "address" in res
|
||||
# We'll get a new one at every call
|
||||
assert res["address"] != minisafed.rpc.getnewaddress()["address"]
|
||||
assert res["address"] != lianad.rpc.getnewaddress()["address"]
|
||||
|
||||
|
||||
def test_listcoins(minisafed, bitcoind):
|
||||
def test_listcoins(lianad, bitcoind):
|
||||
# Initially empty
|
||||
res = minisafed.rpc.listcoins()
|
||||
res = lianad.rpc.listcoins()
|
||||
assert "coins" in res
|
||||
assert len(res["coins"]) == 0
|
||||
|
||||
# If we send a coin, we'll get a new entry. Note we monitor for unconfirmed
|
||||
# funds as well.
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 1)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 1)
|
||||
res = minisafed.rpc.listcoins()["coins"]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1)
|
||||
res = lianad.rpc.listcoins()["coins"]
|
||||
assert txid == res[0]["outpoint"][:64]
|
||||
assert res[0]["amount"] == 1 * COIN
|
||||
assert res[0]["block_height"] is None
|
||||
@ -48,66 +48,66 @@ def test_listcoins(minisafed, bitcoind):
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
block_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(
|
||||
lambda: minisafed.rpc.listcoins()["coins"][0]["block_height"] == block_height
|
||||
lambda: lianad.rpc.listcoins()["coins"][0]["block_height"] == block_height
|
||||
)
|
||||
|
||||
# Same if the coin gets spent.
|
||||
spend_tx = spend_coins(minisafed, bitcoind, (res[0],))
|
||||
spend_tx = spend_coins(lianad, bitcoind, (res[0],))
|
||||
spend_txid = get_txid(spend_tx)
|
||||
wait_for(lambda: minisafed.rpc.listcoins()["coins"][0]["spend_info"] is not None)
|
||||
spend_info = minisafed.rpc.listcoins()["coins"][0]["spend_info"]
|
||||
wait_for(lambda: lianad.rpc.listcoins()["coins"][0]["spend_info"] is not None)
|
||||
spend_info = lianad.rpc.listcoins()["coins"][0]["spend_info"]
|
||||
assert spend_info["txid"] == spend_txid
|
||||
assert spend_info["height"] is None
|
||||
|
||||
# And if this spending tx gets confirmed.
|
||||
bitcoind.generate_block(1, wait_for_mempool=spend_txid)
|
||||
curr_height = bitcoind.rpc.getblockcount()
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["blockheight"] == curr_height)
|
||||
spend_info = minisafed.rpc.listcoins()["coins"][0]["spend_info"]
|
||||
wait_for(lambda: lianad.rpc.getinfo()["blockheight"] == curr_height)
|
||||
spend_info = lianad.rpc.listcoins()["coins"][0]["spend_info"]
|
||||
assert spend_info["txid"] == spend_txid
|
||||
assert spend_info["height"] == curr_height
|
||||
|
||||
|
||||
def test_jsonrpc_server(minisafed, bitcoind):
|
||||
def test_jsonrpc_server(lianad, bitcoind):
|
||||
"""Test passing parameters as a list or a mapping."""
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
bitcoind.rpc.sendtoaddress(addr, 1)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 1)
|
||||
outpoints = [minisafed.rpc.listcoins()["coins"][0]["outpoint"]]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1)
|
||||
outpoints = [lianad.rpc.listcoins()["coins"][0]["outpoint"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 20_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 18)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 18)
|
||||
assert "psbt" in res
|
||||
res = minisafed.rpc.createspend(
|
||||
res = lianad.rpc.createspend(
|
||||
outpoints=outpoints, destinations=destinations, feerate=18
|
||||
)
|
||||
assert "psbt" in res
|
||||
|
||||
|
||||
def test_create_spend(minisafed, bitcoind):
|
||||
def test_create_spend(lianad, bitcoind):
|
||||
# Receive a number of coins in different blocks on different addresses, and
|
||||
# one more on the same address.
|
||||
for _ in range(15):
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 0.01)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 0.3556)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 16)
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 16)
|
||||
|
||||
# Stop the daemon, should be a no-op
|
||||
minisafed.stop()
|
||||
minisafed.start()
|
||||
lianad.stop()
|
||||
lianad.start()
|
||||
|
||||
# Now create a transaction spending all those coins to a few addresses
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 200_000,
|
||||
bitcoind.rpc.getnewaddress(): 400_000,
|
||||
bitcoind.rpc.getnewaddress(): 1_000_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 18)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 18)
|
||||
assert "psbt" in res
|
||||
|
||||
# The transaction must contain a change output.
|
||||
@ -116,45 +116,45 @@ def test_create_spend(minisafed, bitcoind):
|
||||
assert len(spend_psbt.tx.vout) == 4
|
||||
|
||||
# We can sign it and broadcast it.
|
||||
signed_psbt = minisafed.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
finalized_psbt = minisafed.finalize_psbt(signed_psbt)
|
||||
signed_psbt = lianad.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
finalized_psbt = lianad.finalize_psbt(signed_psbt)
|
||||
tx = finalized_psbt.tx.serialize_with_witness().hex()
|
||||
bitcoind.rpc.sendrawtransaction(tx)
|
||||
|
||||
|
||||
def test_list_spend(minisafed, bitcoind):
|
||||
def test_list_spend(lianad, bitcoind):
|
||||
# Start by creating two conflicting Spend PSBTs. The first one will have a change
|
||||
# output but not the second one.
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
value_a = 0.2567
|
||||
bitcoind.rpc.sendtoaddress(addr, value_a)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 1)
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1)
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): int(value_a * COIN // 2),
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 6)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 6)
|
||||
assert "psbt" in res
|
||||
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
value_b = 0.0987
|
||||
bitcoind.rpc.sendtoaddress(addr, value_b)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 2)
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 2)
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): int((value_a + value_b) * COIN - 1_000),
|
||||
}
|
||||
res_b = minisafed.rpc.createspend(destinations, outpoints, 2)
|
||||
res_b = lianad.rpc.createspend(destinations, outpoints, 2)
|
||||
assert "psbt" in res_b
|
||||
|
||||
# Store them both in DB.
|
||||
assert len(minisafed.rpc.listspendtxs()["spend_txs"]) == 0
|
||||
minisafed.rpc.updatespend(res["psbt"])
|
||||
minisafed.rpc.updatespend(res_b["psbt"])
|
||||
assert len(lianad.rpc.listspendtxs()["spend_txs"]) == 0
|
||||
lianad.rpc.updatespend(res["psbt"])
|
||||
lianad.rpc.updatespend(res_b["psbt"])
|
||||
|
||||
# Listing all Spend transactions will list them both. It'll tell us which one has
|
||||
# change and which one doesn't.
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 2
|
||||
first_psbt = next(entry for entry in list_res if entry["psbt"] == res["psbt"])
|
||||
assert first_psbt["change_index"] == 1
|
||||
@ -163,34 +163,34 @@ def test_list_spend(minisafed, bitcoind):
|
||||
|
||||
# If we delete the first one, we'll get only the second one.
|
||||
first_psbt = PSBT.from_base64(res["psbt"])
|
||||
minisafed.rpc.delspendtx(first_psbt.tx.txid().hex())
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
lianad.rpc.delspendtx(first_psbt.tx.txid().hex())
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 1
|
||||
assert list_res[0]["psbt"] == res_b["psbt"]
|
||||
|
||||
# If we delete the second one, result will be empty.
|
||||
second_psbt = PSBT.from_base64(res_b["psbt"])
|
||||
minisafed.rpc.delspendtx(second_psbt.tx.txid().hex())
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
lianad.rpc.delspendtx(second_psbt.tx.txid().hex())
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 0
|
||||
|
||||
|
||||
def test_update_spend(minisafed, bitcoind):
|
||||
def test_update_spend(lianad, bitcoind):
|
||||
# Start by creating a Spend PSBT
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
bitcoind.rpc.sendtoaddress(addr, 0.2567)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) > 0)
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) > 0)
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 200_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 6)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 6)
|
||||
assert "psbt" in res
|
||||
|
||||
# Now update it
|
||||
assert len(minisafed.rpc.listspendtxs()["spend_txs"]) == 0
|
||||
minisafed.rpc.updatespend(res["psbt"])
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(lianad.rpc.listspendtxs()["spend_txs"]) == 0
|
||||
lianad.rpc.updatespend(res["psbt"])
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 1
|
||||
assert list_res[0]["psbt"] == res["psbt"]
|
||||
|
||||
@ -204,10 +204,10 @@ def test_update_spend(minisafed, bitcoind):
|
||||
)
|
||||
psbt_sig_a.i[0].map[PSBT_IN_PARTIAL_SIG] = {dummy_pk_a: dummy_sig_a}
|
||||
psbt_sig_a_ser = psbt_sig_a.to_base64()
|
||||
minisafed.rpc.updatespend(psbt_sig_a_ser)
|
||||
lianad.rpc.updatespend(psbt_sig_a_ser)
|
||||
|
||||
# We'll get it when querying
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 1
|
||||
assert list_res[0]["psbt"] == psbt_sig_a_ser
|
||||
|
||||
@ -221,10 +221,10 @@ def test_update_spend(minisafed, bitcoind):
|
||||
)
|
||||
psbt_sig_b.i[0].map[PSBT_IN_PARTIAL_SIG] = {dummy_pk_b: dummy_sig_b}
|
||||
psbt_sig_b_ser = psbt_sig_b.to_base64()
|
||||
minisafed.rpc.updatespend(psbt_sig_b_ser)
|
||||
lianad.rpc.updatespend(psbt_sig_b_ser)
|
||||
|
||||
# It will have merged both.
|
||||
list_res = minisafed.rpc.listspendtxs()["spend_txs"]
|
||||
list_res = lianad.rpc.listspendtxs()["spend_txs"]
|
||||
assert len(list_res) == 1
|
||||
psbt_merged = PSBT.from_base64(list_res[0]["psbt"])
|
||||
assert len(psbt_merged.i[0].map[PSBT_IN_PARTIAL_SIG]) == 2
|
||||
@ -232,45 +232,45 @@ def test_update_spend(minisafed, bitcoind):
|
||||
assert psbt_merged.i[0].map[PSBT_IN_PARTIAL_SIG][dummy_pk_b] == dummy_sig_b
|
||||
|
||||
|
||||
def test_broadcast_spend(minisafed, bitcoind):
|
||||
def test_broadcast_spend(lianad, bitcoind):
|
||||
# Create a new coin and a spending tx for it.
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
bitcoind.rpc.sendtoaddress(addr, 0.2567)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) > 0)
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) > 0)
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 200_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 6)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 6)
|
||||
psbt = PSBT.from_base64(res["psbt"])
|
||||
txid = psbt.tx.txid().hex()
|
||||
|
||||
# We can't broadcast an unknown Spend
|
||||
with pytest.raises(RpcError, match="Unknown spend transaction.*"):
|
||||
minisafed.rpc.broadcastspend(txid)
|
||||
minisafed.rpc.updatespend(res["psbt"])
|
||||
lianad.rpc.broadcastspend(txid)
|
||||
lianad.rpc.updatespend(res["psbt"])
|
||||
|
||||
# We can't broadcast an unsigned transaction
|
||||
with pytest.raises(RpcError, match="Failed to finalize the spend transaction.*"):
|
||||
minisafed.rpc.broadcastspend(txid)
|
||||
signed_psbt = minisafed.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
minisafed.rpc.updatespend(signed_psbt.to_base64())
|
||||
lianad.rpc.broadcastspend(txid)
|
||||
signed_psbt = lianad.sign_psbt(PSBT.from_base64(res["psbt"]))
|
||||
lianad.rpc.updatespend(signed_psbt.to_base64())
|
||||
|
||||
# Now we've signed and stored it, the daemon will take care of finalizing
|
||||
# the PSBT before broadcasting the transaction.
|
||||
minisafed.rpc.broadcastspend(txid)
|
||||
lianad.rpc.broadcastspend(txid)
|
||||
|
||||
|
||||
def test_start_rescan(minisafed, bitcoind):
|
||||
def test_start_rescan(lianad, bitcoind):
|
||||
"""Test we successfully retrieve all our transactions after losing state by rescanning."""
|
||||
initial_timestamp = int(time.time())
|
||||
first_address = minisafed.rpc.getnewaddress()
|
||||
second_address = minisafed.rpc.getnewaddress()
|
||||
first_address = lianad.rpc.getnewaddress()
|
||||
second_address = lianad.rpc.getnewaddress()
|
||||
|
||||
# Some utility functions to DRY
|
||||
list_coins = lambda: minisafed.rpc.listcoins()["coins"]
|
||||
list_coins = lambda: lianad.rpc.listcoins()["coins"]
|
||||
unspent_coins = lambda: (
|
||||
c for c in minisafed.rpc.listcoins()["coins"] if c["spend_info"] is None
|
||||
c for c in lianad.rpc.listcoins()["coins"] if c["spend_info"] is None
|
||||
)
|
||||
sorted_coins = lambda: sorted(list_coins(), key=lambda c: c["outpoint"])
|
||||
|
||||
@ -283,24 +283,24 @@ def test_start_rescan(minisafed, bitcoind):
|
||||
|
||||
# We can rescan from one second before the tip timestamp, that's almost a no-op.
|
||||
tip_timestamp = bitcoind.rpc.getblockheader(bitcoind.rpc.getbestblockhash())["time"]
|
||||
minisafed.rpc.startrescan(tip_timestamp - 1)
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
lianad.rpc.startrescan(tip_timestamp - 1)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
# We can't rescan from an insane timestamp though.
|
||||
with pytest.raises(RpcError, match="Insane timestamp.*"):
|
||||
minisafed.rpc.startrescan(tip_timestamp)
|
||||
assert minisafed.rpc.getinfo()["rescan_progress"] is None
|
||||
lianad.rpc.startrescan(tip_timestamp)
|
||||
assert lianad.rpc.getinfo()["rescan_progress"] is None
|
||||
future_timestamp = tip_timestamp + 60 * 60
|
||||
with pytest.raises(RpcError, match="Insane timestamp.*"):
|
||||
minisafed.rpc.startrescan(future_timestamp)
|
||||
assert minisafed.rpc.getinfo()["rescan_progress"] is None
|
||||
lianad.rpc.startrescan(future_timestamp)
|
||||
assert lianad.rpc.getinfo()["rescan_progress"] is None
|
||||
prebitcoin_timestamp = 1231006505 - 1
|
||||
with pytest.raises(RpcError, match="Insane timestamp."):
|
||||
minisafed.rpc.startrescan(prebitcoin_timestamp)
|
||||
assert minisafed.rpc.getinfo()["rescan_progress"] is None
|
||||
lianad.rpc.startrescan(prebitcoin_timestamp)
|
||||
assert lianad.rpc.getinfo()["rescan_progress"] is None
|
||||
|
||||
# First, get some coins
|
||||
for _ in range(10):
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
amount = random.randint(1, COIN * 10) / COIN
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, amount)
|
||||
bitcoind.generate_block(random.randint(1, 10), wait_for_mempool=txid)
|
||||
@ -310,20 +310,20 @@ def test_start_rescan(minisafed, bitcoind):
|
||||
# TODO: instead of having randomness we should lay down all different cases (with or
|
||||
# without change, single or multiple inputs, sending externally or to self).
|
||||
for _ in range(5):
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
amount = random.randint(1, COIN * 10) / COIN
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, amount)
|
||||
avail = list(unspent_coins())
|
||||
to_spend = random.sample(avail, random.randint(1, len(avail)))
|
||||
spend_coins(minisafed, bitcoind, to_spend)
|
||||
spend_coins(lianad, bitcoind, to_spend)
|
||||
bitcoind.generate_block(random.randint(1, 5), wait_for_mempool=2)
|
||||
wait_for(lambda: all_spent(to_spend))
|
||||
wait_for(
|
||||
lambda: minisafed.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
lambda: lianad.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
)
|
||||
|
||||
# Receiving addresses are derived at much higher indexes now.
|
||||
assert minisafed.rpc.getnewaddress() not in (first_address, second_address)
|
||||
assert lianad.rpc.getnewaddress() not in (first_address, second_address)
|
||||
|
||||
# Move time forward one day as bitcoind will rescan the last 2 hours of block upon
|
||||
# importing a descriptor.
|
||||
@ -336,23 +336,23 @@ def test_start_rescan(minisafed, bitcoind):
|
||||
# and watchonly wallet. Those won't be aware of past coins for the configured
|
||||
# descriptor.
|
||||
coins_before = sorted_coins()
|
||||
minisafed.restart_fresh(bitcoind)
|
||||
lianad.restart_fresh(bitcoind)
|
||||
assert len(list_coins()) == 0
|
||||
|
||||
# The wallet isn't aware what derivation indexes were used. Necessarily it'll start
|
||||
# from 0.
|
||||
assert minisafed.rpc.getnewaddress() == first_address
|
||||
assert lianad.rpc.getnewaddress() == first_address
|
||||
|
||||
# Once the rescan is done, we must have detected all previous transactions.
|
||||
minisafed.rpc.startrescan(initial_timestamp)
|
||||
rescan_progress = minisafed.rpc.getinfo()["rescan_progress"]
|
||||
lianad.rpc.startrescan(initial_timestamp)
|
||||
rescan_progress = lianad.rpc.getinfo()["rescan_progress"]
|
||||
assert rescan_progress is None or 0 <= rescan_progress <= 1
|
||||
wait_for(lambda: minisafed.rpc.getinfo()["rescan_progress"] is None)
|
||||
wait_for(lambda: lianad.rpc.getinfo()["rescan_progress"] is None)
|
||||
wait_for(
|
||||
lambda: minisafed.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
lambda: lianad.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
|
||||
)
|
||||
assert coins_before == sorted_coins()
|
||||
|
||||
# Now that it caught up it noticed which one were used onchain, so it won't reuse
|
||||
# this derivation indexes anymore.
|
||||
assert minisafed.rpc.getnewaddress() not in (first_address, second_address)
|
||||
assert lianad.rpc.getnewaddress() not in (first_address, second_address)
|
||||
|
||||
@ -3,22 +3,22 @@ from test_framework.serializations import PSBT
|
||||
from test_framework.utils import wait_for
|
||||
|
||||
|
||||
def test_spend_change(minisafed, bitcoind):
|
||||
def test_spend_change(lianad, bitcoind):
|
||||
"""We can spend a coin that was received on a change address."""
|
||||
# Receive a coin on a receive address
|
||||
addr = minisafed.rpc.getnewaddress()["address"]
|
||||
addr = lianad.rpc.getnewaddress()["address"]
|
||||
txid = bitcoind.rpc.sendtoaddress(addr, 0.01)
|
||||
bitcoind.generate_block(1, wait_for_mempool=txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 1)
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 1)
|
||||
|
||||
# Create a transaction that will spend this coin to 1) one of our receive
|
||||
# addresses 2) an external address 3) one of our change addresses.
|
||||
outpoints = [c["outpoint"] for c in minisafed.rpc.listcoins()["coins"]]
|
||||
outpoints = [c["outpoint"] for c in lianad.rpc.listcoins()["coins"]]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 100_000,
|
||||
minisafed.rpc.getnewaddress()["address"]: 100_000,
|
||||
lianad.rpc.getnewaddress()["address"]: 100_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 2)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 2)
|
||||
assert "psbt" in res
|
||||
|
||||
# The transaction must contain a change output.
|
||||
@ -27,29 +27,29 @@ def test_spend_change(minisafed, bitcoind):
|
||||
assert len(spend_psbt.tx.vout) == 3
|
||||
|
||||
# Sign and broadcast this first Spend transaction.
|
||||
signed_psbt = minisafed.sign_psbt(spend_psbt)
|
||||
minisafed.rpc.updatespend(signed_psbt.to_base64())
|
||||
signed_psbt = lianad.sign_psbt(spend_psbt)
|
||||
lianad.rpc.updatespend(signed_psbt.to_base64())
|
||||
spend_txid = signed_psbt.tx.txid().hex()
|
||||
minisafed.rpc.broadcastspend(spend_txid)
|
||||
lianad.rpc.broadcastspend(spend_txid)
|
||||
bitcoind.generate_block(1, wait_for_mempool=spend_txid)
|
||||
wait_for(lambda: len(minisafed.rpc.listcoins()["coins"]) == 3)
|
||||
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 3)
|
||||
|
||||
# Now create a new transaction that spends the change output as well as
|
||||
# the output sent to the receive address.
|
||||
outpoints = [
|
||||
c["outpoint"]
|
||||
for c in minisafed.rpc.listcoins()["coins"]
|
||||
for c in lianad.rpc.listcoins()["coins"]
|
||||
if c["spend_info"] is None
|
||||
]
|
||||
destinations = {
|
||||
bitcoind.rpc.getnewaddress(): 100_000,
|
||||
}
|
||||
res = minisafed.rpc.createspend(destinations, outpoints, 2)
|
||||
res = lianad.rpc.createspend(destinations, outpoints, 2)
|
||||
spend_psbt = PSBT.from_base64(res["psbt"])
|
||||
|
||||
# We can sign and broadcast it.
|
||||
signed_psbt = minisafed.sign_psbt(spend_psbt)
|
||||
minisafed.rpc.updatespend(signed_psbt.to_base64())
|
||||
signed_psbt = lianad.sign_psbt(spend_psbt)
|
||||
lianad.rpc.updatespend(signed_psbt.to_base64())
|
||||
spend_txid = signed_psbt.tx.txid().hex()
|
||||
minisafed.rpc.broadcastspend(spend_txid)
|
||||
lianad.rpc.broadcastspend(spend_txid)
|
||||
bitcoind.generate_block(1, wait_for_mempool=spend_txid)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user