Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ src_dir = "./simf"
simf_files = ["*.simf"]
out_dir = "./src/artifacts"

[regtest]
mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean"

[test]
mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean"

[test.esplora]
url = "<esplora url>"
network = "<Liquid, LiquidTestnet, LiquidRegtest>"
network = "<Liquid, LiquidTestnet, ElementsRegtest>"

[test.rpc]
url = "<rpc url>"
Expand All @@ -60,7 +63,7 @@ Where:
- `test` (`simplex test` config)
- `esplora`
- `url` - Esplora API endpoint url
- `network` - Esplora network type (`Liquid`, `LiquidTestnet`, `LiquidRegtest`).
- `network` - Esplora network type (`Liquid`, `LiquidTestnet`, `ElementsRegtest`).
- `rpc`
- `url` - Elements RPC endpoint url
- `username` - Elements RPC username
Expand Down Expand Up @@ -89,8 +92,7 @@ Check out the complete project examples in the `examples` directory to learn mor

## Future work

- [ ] Custom signer setup with `simplex regtest`.
- [ ] SDK support for confidential assets.
- [ ] SDK support for confidential assets, taproot signer, and custom witness signatures.
- [ ] `simplex init` and `simplex clean` tasks.
- [ ] Proper installation scripts.
- [ ] Simplicity dependencies management once the language adds [support for modules](https://github.com/BlockstreamResearch/SimplicityHL/issues/155).
Expand Down
2 changes: 1 addition & 1 deletion crates/build/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum BuildError {
#[error("Glob error: {0}")]
Glob(#[from] GlobError),

#[error("Occurred config deserialization error: '{0}'")]
#[error("Failed to deserialize config: '{0}'")]
ConfigDeserialize(#[from] toml::de::Error),

#[error("Invalid generation path: '{0}'")]
Expand Down
15 changes: 9 additions & 6 deletions crates/cli/Simplex.default.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# TEST CONFIG

# [build]
# src_dir = "./simf"
# simf_files = ["*.simf"]
# out_dir = "./src/artifacts"
# src_dir = "./simf"

# [regtest]
# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean"

# [test]
# mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean"

# [test.esplora]
# url = "https://blockstream.info/liquidtestnet/api"
# network = "LiquidTestnet"
# url = "<esplora url>"
# network = "<Liquid, LiquidTestnet, ElementsRegtest>"

# [test.rpc]
# url = ""
# username = ""
# password = ""
# url = "<rpc url>"
# username = "<rpc username>"
# password = "<rpc password>"
10 changes: 5 additions & 5 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ impl Cli {
let config_path = Config::get_default_path()?;
let loaded_config = Config::load(config_path)?;

let test_config = loaded_config.test.unwrap_or_default();

Ok(Test::run(test_config, command)?)
Ok(Test::run(loaded_config.test, command)?)
}
Command::Regtest => {
// TODO: pass config
Ok(Regtest::run()?)
let config_path = Config::get_default_path()?;
let loaded_config = Config::load(config_path)?;

Ok(Regtest::run(loaded_config.regtest)?)
}
Command::Build => {
let config_path = Config::get_default_path()?;
Expand Down
9 changes: 6 additions & 3 deletions crates/cli/src/commands/error.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#[derive(thiserror::Error, Debug)]
pub enum CommandError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error(transparent)]
Signer(#[from] simplex_sdk::signer::SignerError),

#[error(transparent)]
Client(#[from] simplex_regtest::error::ClientError),
Regtest(#[from] simplex_regtest::error::RegtestError),

#[error(transparent)]
Test(#[from] simplex_test::error::TestError),

#[error(transparent)]
Build(#[from] simplex_build::error::BuildError),

#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
19 changes: 12 additions & 7 deletions crates/cli/src/commands/regtest.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

use simplex_regtest::TestClient;
use simplex_regtest::Regtest as RegtestRunner;
use simplex_regtest::RegtestConfig;

use crate::commands::error::CommandError;

pub struct Regtest {}

impl Regtest {
pub fn run() -> Result<(), CommandError> {
let mut client = TestClient::new();
pub fn run(config: RegtestConfig) -> Result<(), CommandError> {
let (mut client, signer) = RegtestRunner::new(config)?;

let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
Expand All @@ -19,12 +20,16 @@ impl Regtest {
})
.expect("Error setting Ctrl-C handler");

let auth = client.auth().get_user_pass().unwrap();

println!("======================================");
println!("Waiting for Ctrl-C...");
println!("rpc: {}", client.rpc_url());
println!("esplora: {}", client.esplora_url());
let auth = client.auth().get_user_pass().unwrap();
println!("user: {:?}, password: {:?}", auth.0.unwrap(), auth.1.unwrap());
println!();
println!("RPC: {}", client.rpc_url());
println!("Esplora: {}", client.esplora_url());
println!("User: {:?}, Password: {:?}", auth.0.unwrap(), auth.1.unwrap());
println!();
println!("Signer: {:?}", signer.get_wpkh_address()?);
println!("======================================");

while running.load(Ordering::SeqCst) {}
Expand Down
23 changes: 11 additions & 12 deletions crates/cli/src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use serde::Deserialize;
use std::path::{Path, PathBuf};

use simplex_build::BuildConfig;
use simplex_regtest::RegtestConfig;
use simplex_test::TestConfig;

use super::error::ConfigError;
Expand All @@ -12,8 +13,9 @@ pub const CONFIG_FILENAME: &str = "Simplex.toml";
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(default)]
pub struct Config {
pub test: Option<TestConfig>,
pub build: BuildConfig,
pub regtest: RegtestConfig,
pub test: TestConfig,
}

impl Config {
Expand Down Expand Up @@ -43,19 +45,16 @@ impl Config {
}

fn validate(config: &Config) -> Result<(), ConfigError> {
match config.test.clone() {
Some(test_config) => match test_config.esplora {
Some(esplora_config) => {
Self::validate_network(&esplora_config.network)?;
match config.test.esplora.clone() {
Some(esplora_config) => {
Self::validate_network(&esplora_config.network)?;

if test_config.rpc.is_some() && esplora_config.network != "ElementsRegtest" {
return Err(ConfigError::NetworkNameUnmatched(esplora_config.network.clone()));
}

Ok(())
if config.test.rpc.is_some() && esplora_config.network != "ElementsRegtest" {
return Err(ConfigError::NetworkNameUnmatched(esplora_config.network.clone()));
}
None => Ok(()),
},

Ok(())
}
None => Ok(()),
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/regtest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ simplex-sdk = { workspace = true }

thiserror = { workspace = true }
electrsd = { workspace = true }
serde = { workspace = true }
toml = { workspace = true }
10 changes: 5 additions & 5 deletions crates/regtest/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use electrsd::bitcoind;
use electrsd::bitcoind::bitcoincore_rpc::Auth;
use electrsd::bitcoind::{BitcoinD, Conf};

use super::error::ClientError;
use super::error::RegtestError;
use crate::args::{get_electrs_bin_args, get_elementsd_bin_args};

pub struct TestClient {
pub struct RegtestClient {
pub electrs: ElectrsD,
pub elements: BitcoinD,
}

impl TestClient {
impl RegtestClient {
// TODO: pass custom config
pub fn new() -> Self {
let (electrs_path, elementsd_path) = Self::default_bin_paths();
Expand Down Expand Up @@ -57,9 +57,9 @@ impl TestClient {
Auth::UserPass(cookie.user, cookie.password)
}

pub fn kill(&mut self) -> Result<(), ClientError> {
pub fn kill(&mut self) -> Result<(), RegtestError> {
// electrs stops elements automatically
self.electrs.kill().map_err(|_| ClientError::ElectrsTermination())?;
self.electrs.kill().map_err(|_| RegtestError::ElectrsTermination())?;

Ok(())
}
Expand Down
34 changes: 34 additions & 0 deletions crates/regtest/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::fs::OpenOptions;
use std::io::Read;
use std::path::Path;

use serde::Deserialize;

use super::error::RegtestError;

pub const DEFAULT_REGTEST_MNEMONIC: &str = "exist carry drive collect lend cereal occur much tiger just involve mean";

#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct RegtestConfig {
pub mnemonic: String,
}

impl RegtestConfig {
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, RegtestError> {
let mut content = String::new();
let mut file = OpenOptions::new().read(true).open(path)?;

file.read_to_string(&mut content)?;

Ok(toml::from_str(&content)?)
}
}

impl Default for RegtestConfig {
fn default() -> Self {
Self {
mnemonic: DEFAULT_REGTEST_MNEMONIC.to_string(),
}
}
}
23 changes: 22 additions & 1 deletion crates/regtest/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
use std::io;

use simplex_sdk::provider::ProviderError;
use simplex_sdk::provider::RpcError;
use simplex_sdk::signer::SignerError;

#[derive(thiserror::Error, Debug)]
pub enum ClientError {
pub enum RegtestError {

Check warning on line 8 in crates/regtest/src/error.rs

View workflow job for this annotation

GitHub Actions / Format and Clippy

Diff in /home/runner/work/simplex/simplex/crates/regtest/src/error.rs
#[error(transparent)]
Provider(#[from] ProviderError),

#[error(transparent)]
Rpc(#[from] RpcError),

#[error(transparent)]

Check warning on line 15 in crates/regtest/src/error.rs

View workflow job for this annotation

GitHub Actions / Format and Clippy

Diff in /home/runner/work/simplex/simplex/crates/regtest/src/error.rs
Signer(#[from] SignerError),

#[error("Failed to terminate elements")]
ElementsTermination(),

#[error("Failed to terminate electrs")]
ElectrsTermination(),

#[error("Failed to deserialize config: '{0}'")]
ConfigDeserialize(#[from] toml::de::Error),

#[error("io error occurred: '{0}'")]
Io(#[from] io::Error),
}
5 changes: 4 additions & 1 deletion crates/regtest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
mod args;
pub mod client;
pub mod error;
pub mod config;
pub mod regtest;

pub use client::TestClient;
pub use regtest::Regtest;
pub use config::RegtestConfig;
62 changes: 62 additions & 0 deletions crates/regtest/src/regtest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::time::Duration;

use simplex_sdk::provider::ElementsRpc;
use simplex_sdk::provider::SimplexProvider;
use simplex_sdk::provider::SimplicityNetwork;
use simplex_sdk::signer::Signer;

use super::client::RegtestClient;
use super::RegtestConfig;
use super::error::RegtestError;

pub struct Regtest {}

impl Regtest {
pub fn new(config: RegtestConfig) -> Result<(RegtestClient, Signer), RegtestError> {
let client = RegtestClient::new();

let provider = Box::new(SimplexProvider::new(
client.esplora_url(),
client.rpc_url(),
client.auth(),
SimplicityNetwork::default_regtest(),
)?);

let signer = Signer::new(config.mnemonic.as_str(), provider)?;

Self::prepare_signer(&client, &signer)?;

Ok((client, signer))
}

fn prepare_signer(client: &RegtestClient, signer: &Signer) -> Result<(), RegtestError> {
let rpc_provider = ElementsRpc::new(client.rpc_url(), client.auth())?;

rpc_provider.generate_blocks(1)?;
rpc_provider.rescanblockchain(None, None)?;
rpc_provider.sweep_initialfreecoins()?;
rpc_provider.generate_blocks(100)?;

// 20 million BTC
rpc_provider.sendtoaddress(&signer.get_wpkh_address()?, 20_000_000 * u64::pow(10, 8), None)?;

// wait for electrs to index
let mut attempts = 0;

loop {
if !(signer.get_wpkh_utxos()?).is_empty() {
break;
}

attempts += 1;

if attempts > 100 {
panic!("Electrs failed to index the sweep after 10 seconds");
}

std::thread::sleep(Duration::from_millis(100));
}

Ok(())
}
}
Loading
Loading