diff --git a/.gitignore b/.gitignore index c67fac1..f2880e6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .vscode/ .cache .env +Simplex.toml config.toml # Debugging data diff --git a/Cargo.lock b/Cargo.lock index 72f6600..4fe6324 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1818,6 +1818,15 @@ dependencies = [ "trybuild", ] +[[package]] +name = "simplex-build" +version = "0.1.0" +dependencies = [ + "simplex-sdk", + "simplicityhl", + "thiserror", +] + [[package]] name = "simplex-cli" version = "0.1.0" @@ -1827,11 +1836,13 @@ dependencies = [ "ctrlc", "dotenvy", "electrsd", + "serde", "simplex-config", "simplex-test", "simplicityhl", "thiserror", "tokio", + "toml 0.9.12+spec-1.1.0", "tracing", "tracing-subscriber", ] diff --git a/crates/artifacts/Cargo.toml b/crates/artifacts/Cargo.toml new file mode 100644 index 0000000..c50113f --- /dev/null +++ b/crates/artifacts/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "simplex-build" +version = "0.1.0" +edition = "2024" +description = "Simplex Build" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lints] +workspace = true + +[dependencies] +thiserror = { workspace = true } + +simplex-sdk = { workspace = true } +simplicityhl = { workspace = true } diff --git a/crates/artifacts/src/asset_auth.rs b/crates/artifacts/src/asset_auth.rs new file mode 100644 index 0000000..fea1b84 --- /dev/null +++ b/crates/artifacts/src/asset_auth.rs @@ -0,0 +1,89 @@ +use simplex_sdk::arguments::ArgumentsTrait; +use simplex_sdk::program::Program; + +use simplicityhl::simplicity::bitcoin::XOnlyPublicKey; + +// use simplex_macros::simplex_build; + +// #[derive(SimplexBuild)] +// #[simplex("../../contracts/src/asset_auth/source_simf/asset_auth.simf")] +pub struct AssetAuth<'a> { + program: Program<'a>, +} + +impl<'a> AssetAuth<'a> { + // the path is autogenerated + pub const SOURCE: &'static str = ""; + // include_str!("../../contracts/src/asset_auth/source_simf/asset_auth.simf"); + + pub fn new(public_key: &'a XOnlyPublicKey, arguments: &'a impl ArgumentsTrait) -> Self { + Self { + program: Program::new(Self::SOURCE, public_key, arguments), + } + } + + pub fn get_program(&self) -> &Program<'a> { + &self.program + } +} + +// Expanded by macro + +pub mod asset_auth_build { + use simplex_sdk::arguments::ArgumentsTrait; + use simplex_sdk::witness::WitnessTrait; + use simplicityhl::value::UIntValue; + use simplicityhl::value::ValueConstructible; + use simplicityhl::{Value, WitnessValues}; + use std::collections::HashMap; + + pub struct AssetAuthWitness { + pub path: (bool, u64, u64), + } + + pub struct AssetAuthArguments { + pub first: u64, + pub second: bool, + } + + impl WitnessTrait for AssetAuthWitness { + fn build_witness(&self) -> WitnessValues { + WitnessValues::from(HashMap::from([( + simplicityhl::str::WitnessName::from_str_unchecked("PATH"), + Value::tuple([ + Value::from(self.path.0), + Value::from(UIntValue::U64(self.path.1)), + Value::from(UIntValue::U64(self.path.1)), + ]), + )])) + } + + // fn from_witness(_witness: &::simplicityhl::WitnessValues) -> Self { + // Self { + // path: (false, 0, 0), + // } + // } + } + + impl ArgumentsTrait for AssetAuthArguments { + fn build_arguments(&self) -> simplicityhl::Arguments { + simplicityhl::Arguments::from(HashMap::from([ + ( + simplicityhl::str::WitnessName::from_str_unchecked("FIRST"), + Value::from(UIntValue::U64(self.first)), + ), + ( + simplicityhl::str::WitnessName::from_str_unchecked("SECOND"), + Value::from(self.second), + ), + ])) + } + + // fn from_arguments(_args: &simplicityhl::Arguments) -> Self { + // Self { + // first: 0, + // second: false, + // } + // } + } +} diff --git a/crates/artifacts/src/lib.rs b/crates/artifacts/src/lib.rs new file mode 100644 index 0000000..c37f1ff --- /dev/null +++ b/crates/artifacts/src/lib.rs @@ -0,0 +1 @@ +pub mod asset_auth; diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a5401d6..3a10be4 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -18,6 +18,9 @@ workspace = true simplex-test = { workspace = true } simplex-config = { workspace = true } +serde = { version = "1.0.228" } +toml = { version = "0.9.8" } + anyhow = "1" dotenvy = "0.15" clap = { version = "4", features = ["derive", "env"] } @@ -27,4 +30,4 @@ tracing = { version = "0.1.44" } thiserror = { workspace = true } tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } ctrlc = { version = "3.5.2", features = ["termination"] } -electrsd = { workspace = true } \ No newline at end of file +electrsd = { workspace = true } diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs new file mode 100644 index 0000000..ea5b6c6 --- /dev/null +++ b/crates/cli/src/config.rs @@ -0,0 +1,157 @@ +use serde::{Deserialize, Serialize}; +use simplex_core::SimplicityNetwork; +use std::fmt::Display; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +const MANIFEST_DIR: &str = "CARGO_MANIFEST_DIR"; +const CONFIG_FILENAME: &str = "Simplex.toml"; + +#[derive(thiserror::Error, Debug)] +pub enum ConfigError { + /// Standard I/O errors. + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + /// Errors when parsing TOML configuration files. + #[error("TOML parse error: {0}")] + TomlParse(#[from] toml::de::Error), + + /// Errors when parsing TOML configuration files. + #[error("Unable to deserialize config: {0}")] + UnableToDeserialize(toml::de::Error), + + /// Errors when parsing env variable. + #[error("Unable to get env variable: {0}")] + UnableToGetEnv(#[from] std::env::VarError), + + /// Errors when getting a path to config. + #[error("Path doesn't a file: '{0}'")] + PathIsNotFile(PathBuf), + + /// Errors when getting a path to config. + #[error("Path doesn't exist: '{0}'")] + PathIsNotEsixt(PathBuf), +} + +#[derive(Debug, Default, Clone)] +pub struct Config { + pub provider_config: ProviderConfig, + pub test_config: TestConfig, +} + +#[derive(Debug, Clone)] +pub struct ProviderConfig { + simplicity_network: SimplicityNetwork, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct TestConfig { + pub rpc_creds: RpcCreds, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub enum RpcCreds { + Auth { + rpc_username: String, + rpc_password: String, + }, + #[default] + None, +} + +#[derive(Debug, Default, Clone)] +pub struct ConfigOverride { + pub rpc_creds: Option, + pub network: Option, +} + +impl Default for ProviderConfig { + fn default() -> Self { + ProviderConfig { + simplicity_network: SimplicityNetwork::LiquidTestnet, + } + } +} + +impl Config { + pub fn discover(cfg_override: &ConfigOverride) -> Result, ConfigError> { + Config::_discover().map(|opt| { + opt.map(|mut cfg| { + if let Some(test_conf) = cfg_override.rpc_creds.clone() { + cfg.test_config = test_conf; + } + if let Some(network) = cfg_override.network { + cfg.provider_config.simplicity_network = network; + } + cfg + }) + }) + } + + pub fn load_or_default(path_buf: impl AsRef) -> Self { + Self::from_path(path_buf).unwrap_or_else(|_| { + if let Ok(Some(conf)) = Self::_discover() { + conf + } else { + Self::default() + } + }) + } + + fn _discover() -> Result, ConfigError> { + let cwd = std::env::current_dir()?; + let path = cwd.join(CONFIG_FILENAME); + dbg!(&path); + if !path.is_file() { + return Err(ConfigError::PathIsNotFile(path)); + } + if !path.exists() { + return Err(ConfigError::PathIsNotEsixt(path)); + } + Ok(Some(Config::from_path(&path)?)) + } + + fn from_path(p: impl AsRef) -> Result { + std::fs::read_to_string(p.as_ref())?.parse() + } +} + +impl FromStr for Config { + type Err = ConfigError; + + fn from_str(s: &str) -> Result { + let cfg: _Config = toml::from_str(s).map_err(ConfigError::UnableToDeserialize)?; + Ok(Config { + provider_config: ProviderConfig { + simplicity_network: cfg.network.unwrap_or_default().into(), + }, + test_config: cfg.test.unwrap_or_default(), + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct _Config { + network: Option<_NetworkName>, + test: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "lowercase")] +enum _NetworkName { + #[default] + Liquid, + LiquidTestnet, + ElementsRegtest, +} + +impl Into for _NetworkName { + fn into(self) -> SimplicityNetwork { + match self { + _NetworkName::Liquid => SimplicityNetwork::Liquid, + _NetworkName::LiquidTestnet => SimplicityNetwork::LiquidTestnet, + _NetworkName::ElementsRegtest => SimplicityNetwork::default_regtest(), + } + } +} diff --git a/crates/simplex/tests/simplex_test.rs b/crates/simplex/tests/simplex_test.rs index 475fdc4..7bb673f 100644 --- a/crates/simplex/tests/simplex_test.rs +++ b/crates/simplex/tests/simplex_test.rs @@ -35,7 +35,7 @@ fn test_invocation_tx_tracking() -> anyhow::Result<()> { // broadcast, fetch fee transaction - ElementsRpcClient::sendtoaddress( + let result = ElementsRpcClient::sendtoaddress( rpc.as_ref(), &p2pk, DEFAULT_SAT_AMOUNT_FAUCET,