diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3ac315..5da4f75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: true env: - SIMPLEX_COMMIT: c1e9d8b06231973d81fbaa677cf3f03787104b81 + SIMPLEX_VERSION: 0.0.11 jobs: ci: @@ -28,21 +28,9 @@ jobs: rustup component add rustfmt clippy - name: Install pinned Simplex CLI - shell: bash - run: | - set -euo pipefail - SIMPLEX_DIR="${SIMPLEX_DIR:-${XDG_CONFIG_HOME:-$HOME}/.simplex}" - mkdir -p "$SIMPLEX_DIR/bin" - curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 \ - "https://raw.githubusercontent.com/BlockstreamResearch/smplx/$SIMPLEX_COMMIT/simplexup/simplexup" \ - -o "$SIMPLEX_DIR/bin/simplexup" - grep -Fq 'curl -#o "$2" -L "$1"' "$SIMPLEX_DIR/bin/simplexup" - sed -i 's/curl -#o "$2" -L "$1"/curl -#fL --retry 5 --retry-all-errors --retry-delay 2 -o "$2" "$1"/' \ - "$SIMPLEX_DIR/bin/simplexup" - chmod +x "$SIMPLEX_DIR/bin/simplexup" - echo "$SIMPLEX_DIR/bin" >> "$GITHUB_PATH" - "$SIMPLEX_DIR/bin/simplexup" -C "$SIMPLEX_COMMIT" - "$SIMPLEX_DIR/bin/simplex" --version + uses: BlockstreamResearch/simplicityhl-toolchain@v0.0.2 + with: + components: simplex@${{ env.SIMPLEX_VERSION }} - name: Generate contract artifacts working-directory: contracts diff --git a/.github/workflows/contracts-tests.yml b/.github/workflows/contracts-tests.yml index 2ea012a..67c270c 100644 --- a/.github/workflows/contracts-tests.yml +++ b/.github/workflows/contracts-tests.yml @@ -15,7 +15,7 @@ concurrency: env: CARGO_TERM_COLOR: always - SIMPLEX_COMMIT: c1e9d8b06231973d81fbaa677cf3f03787104b81 + SIMPLEX_VERSION: 0.0.11 jobs: test: @@ -40,24 +40,9 @@ jobs: cache-on-failure: true - name: Install simplex-cli via simplexup - shell: bash - run: | - set -euo pipefail - BASE_DIR="${XDG_CONFIG_HOME:-$HOME}" - SIMPLEX_DIR="${SIMPLEX_DIR:-$BASE_DIR/.simplex}" - SIMPLEX_BIN_DIR="$SIMPLEX_DIR/bin" - mkdir -p "$SIMPLEX_BIN_DIR" - curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 \ - "https://raw.githubusercontent.com/BlockstreamResearch/smplx/$SIMPLEX_COMMIT/simplexup/simplexup" \ - -o "$SIMPLEX_BIN_DIR/simplexup" - grep -Fq 'curl -#o "$2" -L "$1"' "$SIMPLEX_BIN_DIR/simplexup" - sed -i 's/curl -#o "$2" -L "$1"/curl -#fL --retry 5 --retry-all-errors --retry-delay 2 -o "$2" "$1"/' \ - "$SIMPLEX_BIN_DIR/simplexup" - chmod +x "$SIMPLEX_BIN_DIR/simplexup" - - "$SIMPLEX_BIN_DIR/simplexup" -C "$SIMPLEX_COMMIT" - echo "$SIMPLEX_BIN_DIR" >> "$GITHUB_PATH" - "$SIMPLEX_BIN_DIR/simplex" --version + uses: BlockstreamResearch/simplicityhl-toolchain@v0.0.2 + with: + components: simplex@${{ env.SIMPLEX_VERSION }} - name: Generate contract artifacts shell: bash diff --git a/Cargo.lock b/Cargo.lock index 4da6cc1..89a4361 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2975,8 +2975,9 @@ dependencies = [ [[package]] name = "smplx-build" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1538c2de418c94bfec9824b140a0f8bc2f2542cde0f18ef1b2e3a23e5c9eb1" dependencies = [ "glob", "globwalk", @@ -2995,8 +2996,9 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb08baf344083ccad07886156cbd92feb2d66b1886b5b73e61a9e03db2ef8f7" dependencies = [ "smplx-build", "smplx-test", @@ -3005,8 +3007,9 @@ dependencies = [ [[package]] name = "smplx-regtest" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d419c9a387fcd2944ac487dff8ced762fc1d52e21186366ab70e7c899931a6" dependencies = [ "electrsd", "hex", @@ -3020,8 +3023,9 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ac052541ca5611bcdd23d46317970b2065c73ebc7864e53ff5607b30c96dfa9" dependencies = [ "bip39", "bitcoin_hashes", @@ -3039,8 +3043,9 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6a9e1bdf1d8139fbdf2be662fdcd11761ae61604aeb78628ced8506f7c6a7b9" dependencies = [ "either", "serde", @@ -3052,8 +3057,9 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.10" -source = "git+https://github.com/BlockstreamResearch/smplx?rev=c1e9d8b06231973d81fbaa677cf3f03787104b81#c1e9d8b06231973d81fbaa677cf3f03787104b81" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3bd10a1047d731372fc1589ebf2a76444ad8c65d352f84115718e6345f3aa0c" dependencies = [ "electrsd", "proc-macro2", @@ -3479,7 +3485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index 95752f3..d82b6b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ edition = "2024" [workspace.dependencies] axum = "0.8.9" +simplex = { package = "smplx-std", version = "0.0.11" } bip322 = "0.0.11" bitcoin = "0.32.5" bitcoincore-rpc = "0.19.0" diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 132571f..9eb8fee 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -smplx-std = { git = "https://github.com/BlockstreamResearch/smplx", rev = "c1e9d8b06231973d81fbaa677cf3f03787104b81" } +simplex.workspace = true thiserror.workspace = true storm-tree = { path = "../storm-tree" } diff --git a/contracts/Simplex.toml b/contracts/Simplex.toml index 706329f..fb31f8f 100644 --- a/contracts/Simplex.toml +++ b/contracts/Simplex.toml @@ -6,7 +6,7 @@ # out_dir = "./src/artifacts" [dependencies] -std = { git = "https://github.com/BlockstreamResearch/simplicityhl-std.git", tag = "v0.1.0" } +std = { git = "https://github.com/BlockstreamResearch/simplicityhl-std.git", tag = "v0.1.0" } # [regtest] # mnemonic = "exist carry drive collect lend cereal occur much tiger just involve mean" diff --git a/contracts/simf/auth_helpers/network_auth_test.simf b/contracts/simf/auth_helpers/network_auth_test.simf index 3c6d73a..3c6300f 100644 --- a/contracts/simf/auth_helpers/network_auth_test.simf +++ b/contracts/simf/auth_helpers/network_auth_test.simf @@ -1,4 +1,4 @@ -use crate::network::{StormTreeBloom, assert_network_authorization}; +use crate::auth_helpers::network::{StormTreeBloom, assert_network_authorization}; fn main() { let merkle_root: u256 = witness::MERKLE_ROOT; diff --git a/contracts/simf/tick_asset.simf b/contracts/simf/voucher.simf similarity index 74% rename from contracts/simf/tick_asset.simf rename to contracts/simf/voucher.simf index 892a19b..8e5846c 100644 --- a/contracts/simf/tick_asset.simf +++ b/contracts/simf/voucher.simf @@ -3,11 +3,11 @@ use std::lib::op_return::assert_output_is_op_return; use crate::helper::{get_input_asset, get_input_script_hash, assert_output_preserves_utxo}; -/// 1. asset_auth_utxo_input_index, tick_utxo_output_index +/// 1. asset_auth_utxo_input_index, voucher_utxo_output_index type AssetAuthWit = (u32, u32); -/// 2. script_auth_utxo_input_index, tick_utxo_output_index +/// 2. script_auth_utxo_input_index, voucher_utxo_output_index type ScriptAuthWit = (u32, u32); -/// 3. auth_signature, tick_utxo_output_index +/// 3. auth_signature, voucher_utxo_output_index type SignAuthWit = (Signature, u32); /// 4. storm_eye_input_index type NetworkAuthWit = u32; @@ -15,33 +15,33 @@ type NetworkAuthWit = u32; type AuthKindLeft = Either; type AuthKindRight = Either; -fn assert_tick_utxo_burned(tick_utxo_output_index: u32) { - assert_output_preserves_utxo(tick_utxo_output_index); - assert_output_is_op_return(tick_utxo_output_index); +fn assert_voucher_utxo_burned(voucher_utxo_output_index: u32) { + assert_output_preserves_utxo(voucher_utxo_output_index); + assert_output_is_op_return(voucher_utxo_output_index); } /// 1. User spending via the Asset authorization method. -fn user_spend_via_asset_auth(asset_auth_utxo_input_index: u32, tick_utxo_output_index: u32) { +fn user_spend_via_asset_auth(asset_auth_utxo_input_index: u32, voucher_utxo_output_index: u32) { assert_eq_32(param::AUTH_METHOD, 0); assert_eq_256(get_input_asset(asset_auth_utxo_input_index), param::AUTH_ASSET_ID); - assert_tick_utxo_burned(tick_utxo_output_index); + assert_voucher_utxo_burned(voucher_utxo_output_index); } /// 2. User spending via the Script authorization method. -fn user_spend_via_script_auth(script_auth_utxo_input_index: u32, tick_utxo_output_index: u32) { +fn user_spend_via_script_auth(script_auth_utxo_input_index: u32, voucher_utxo_output_index: u32) { assert_eq_32(param::AUTH_METHOD, 1); assert_eq_256(get_input_script_hash(script_auth_utxo_input_index), param::AUTH_SCRIPT_HASH); - assert_tick_utxo_burned(tick_utxo_output_index); + assert_voucher_utxo_burned(voucher_utxo_output_index); } /// 3. User spending via the Signature authorization method. -fn user_spend_via_sign_auth(auth_signature: Signature, tick_utxo_output_index: u32) { +fn user_spend_via_sign_auth(auth_signature: Signature, voucher_utxo_output_index: u32) { assert_eq_32(param::AUTH_METHOD, 2); jet::bip_0340_verify((param::AUTH_PUBKEY, jet::sig_all_hash()), auth_signature); - assert_tick_utxo_burned(tick_utxo_output_index); + assert_voucher_utxo_burned(voucher_utxo_output_index); } /// 4. Network authorized spending. diff --git a/contracts/src/account/core.rs b/contracts/src/account/core.rs new file mode 100644 index 0000000..8ae881f --- /dev/null +++ b/contracts/src/account/core.rs @@ -0,0 +1,70 @@ +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::{AssetId, Script}; +use simplex::transaction::utxo::UTXO; +use simplex::transaction::{ + FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, +}; + +use crate::artifacts::account::AccountProgram; +use crate::artifacts::account::derived_account::{AccountArguments, AccountWitness}; + +/// The Account covenant: spendable alongside a Storm Eye input. +pub struct Account { + program: AccountProgram, + params: AccountParameters, +} + +pub struct AccountParameters { + pub storm_eye_asset_id: AssetId, + pub account_owner_pubkey: [u8; 32], + pub network: SimplicityNetwork, +} + +impl Account { + #[must_use] + pub fn new(params: AccountParameters) -> Self { + let program = AccountProgram::new(&AccountArguments { + storm_eye_asset_id: params.storm_eye_asset_id.into_inner().to_byte_array(), + account_owner_pubkey: params.account_owner_pubkey, + }); + + Self { program, params } + } + + #[must_use] + pub fn get_script_pubkey(&self) -> Script { + self.program.get_script_pubkey(&self.params.network) + } + + #[must_use] + pub fn get_parameters(&self) -> &AccountParameters { + &self.params + } + + pub fn attach_spend( + &self, + ft: &mut FinalTransaction, + account_utxo: &UTXO, + storm_eye_input_index: u32, + ) { + ft.add_program_input( + PartialInput::new(account_utxo.clone()), + ProgramInput::new( + Box::new(self.program.as_ref().clone()), + Box::new(AccountWitness { + storm_eye_input_index, + }), + ), + RequiredSignature::None, + ); + } + + /// Adds an output that pays `amount` of `asset_id` to this Account. + pub fn attach_account_output(&self, ft: &mut FinalTransaction, amount: u64, asset_id: AssetId) { + ft.add_output(PartialOutput::new( + self.get_script_pubkey(), + amount, + asset_id, + )); + } +} diff --git a/contracts/src/account/mod.rs b/contracts/src/account/mod.rs new file mode 100644 index 0000000..58168e6 --- /dev/null +++ b/contracts/src/account/mod.rs @@ -0,0 +1,3 @@ +mod core; + +pub use core::{Account, AccountParameters}; diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index e88ada3..31921bb 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -1,4 +1,6 @@ pub mod artifacts; +pub mod account; pub mod auth; pub mod treasury; +pub mod voucher; diff --git a/contracts/src/voucher/core.rs b/contracts/src/voucher/core.rs new file mode 100644 index 0000000..9dd4c64 --- /dev/null +++ b/contracts/src/voucher/core.rs @@ -0,0 +1,69 @@ +use simplex::simplicityhl::elements::schnorr::XOnlyPublicKey; +use simplex::simplicityhl::elements::{AssetId, Script}; +use simplex::transaction::utxo::UTXO; +use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, ProgramInput}; + +use crate::artifacts::voucher::VoucherProgram; +use crate::voucher::{VoucherParameters, VoucherSpendPath}; + +/// The Voucher covenant: a burn-after-use, network-issued proof token. +pub struct Voucher { + program: VoucherProgram, + params: VoucherParameters, +} + +impl Voucher { + #[must_use] + pub fn new(params: VoucherParameters) -> Self { + Self { + program: VoucherProgram::new(¶ms.build_arguments()), + params, + } + } + + #[must_use] + pub fn new_with_taproot_pubkey(params: VoucherParameters, pub_key: XOnlyPublicKey) -> Self { + Self { + program: VoucherProgram::new(¶ms.build_arguments()).with_taproot_pubkey(pub_key), + params, + } + } + + #[must_use] + pub fn get_script_pubkey(&self) -> Script { + self.program.get_script_pubkey(&self.params.network) + } + + #[must_use] + pub fn get_parameters(&self) -> &VoucherParameters { + &self.params + } + + /// Attaches a spend of `voucher_utxo` along the given spending path. + pub fn attach_spend( + &self, + ft: &mut FinalTransaction, + voucher_utxo: &UTXO, + path: VoucherSpendPath, + ) { + let required_signature = path.required_signature(); + + ft.add_program_input( + PartialInput::new(voucher_utxo.clone()), + ProgramInput::new( + Box::new(self.program.as_ref().clone()), + Box::new(path.build_witness()), + ), + required_signature, + ); + } + + /// Adds an output that pays `amount` of `asset_id` to this Voucher. + pub fn attach_voucher_output(&self, ft: &mut FinalTransaction, amount: u64, asset_id: AssetId) { + ft.add_output(PartialOutput::new( + self.get_script_pubkey(), + amount, + asset_id, + )); + } +} diff --git a/contracts/src/voucher/mod.rs b/contracts/src/voucher/mod.rs new file mode 100644 index 0000000..5a1144d --- /dev/null +++ b/contracts/src/voucher/mod.rs @@ -0,0 +1,7 @@ +mod core; +mod params; +mod witness; + +pub use core::Voucher; +pub use params::{VoucherAuthMethod, VoucherParameters}; +pub use witness::VoucherSpendPath; diff --git a/contracts/src/voucher/params.rs b/contracts/src/voucher/params.rs new file mode 100644 index 0000000..846ecc1 --- /dev/null +++ b/contracts/src/voucher/params.rs @@ -0,0 +1,51 @@ +use simplex::provider::SimplicityNetwork; +use simplex::simplicityhl::elements::AssetId; + +use crate::artifacts::voucher::derived_voucher::VoucherArguments; + +/// Compilation parameters for the Voucher covenant. +pub struct VoucherParameters { + pub storm_eye_asset_id: AssetId, + pub auth_method: VoucherAuthMethod, + pub network: SimplicityNetwork, +} + +#[derive(Debug, Clone, Copy)] +pub enum VoucherAuthMethod { + /// An input carrying this asset id authorizes the burn. + Asset { auth_asset_id: AssetId }, + /// An input matching this script hash authorizes the burn. + Script { auth_script_hash: [u8; 32] }, + /// A BIP-340 signature under this pubkey authorizes the burn. + Signature { auth_pubkey: [u8; 32] }, +} + +impl VoucherParameters { + #[must_use] + pub fn build_arguments(&self) -> VoucherArguments { + let mut arguments = VoucherArguments { + storm_eye_asset_id: self.storm_eye_asset_id.into_inner().to_byte_array(), + auth_method: 0, + auth_asset_id: [0u8; 32], + auth_script_hash: [0u8; 32], + auth_pubkey: [0u8; 32], + }; + + match self.auth_method { + VoucherAuthMethod::Asset { auth_asset_id } => { + arguments.auth_method = 0; + arguments.auth_asset_id = auth_asset_id.into_inner().to_byte_array(); + } + VoucherAuthMethod::Script { auth_script_hash } => { + arguments.auth_method = 1; + arguments.auth_script_hash = auth_script_hash; + } + VoucherAuthMethod::Signature { auth_pubkey } => { + arguments.auth_method = 2; + arguments.auth_pubkey = auth_pubkey; + } + } + + arguments + } +} diff --git a/contracts/src/voucher/witness.rs b/contracts/src/voucher/witness.rs new file mode 100644 index 0000000..38f9507 --- /dev/null +++ b/contracts/src/voucher/witness.rs @@ -0,0 +1,57 @@ +use simplex::constants::DUMMY_SIGNATURE; +use simplex::either::Either; +use simplex::transaction::RequiredSignature; + +use crate::artifacts::voucher::derived_voucher::VoucherWitness; + +#[derive(Debug, Clone, Copy)] +pub enum VoucherSpendPath { + /// An input carrying `AUTH_ASSET_ID` authorizes the burn. + AssetAuth { + auth_input_index: u32, + voucher_output_index: u32, + }, + /// An input matching `AUTH_SCRIPT_HASH` authorizes the burn. + ScriptAuth { + auth_input_index: u32, + voucher_output_index: u32, + }, + /// A BIP-340 signature over `sig_all_hash` authorizes the burn. + SignatureAuth { voucher_output_index: u32 }, + /// The Storm Eye asset at this input authorizes the burn. + NetworkAuth { storm_eye_input_index: u32 }, +} + +impl VoucherSpendPath { + #[must_use] + pub fn required_signature(&self) -> RequiredSignature { + match self { + Self::SignatureAuth { .. } => { + RequiredSignature::witness_with_path("PATH", ["Right", "Left", "0"]) + } + _ => RequiredSignature::None, + } + } + + #[must_use] + pub fn build_witness(self) -> VoucherWitness { + let path = match self { + Self::AssetAuth { + auth_input_index, + voucher_output_index, + } => Either::Left(Either::Left((auth_input_index, voucher_output_index))), + Self::ScriptAuth { + auth_input_index, + voucher_output_index, + } => Either::Left(Either::Right((auth_input_index, voucher_output_index))), + Self::SignatureAuth { + voucher_output_index, + } => Either::Right(Either::Left((DUMMY_SIGNATURE, voucher_output_index))), + Self::NetworkAuth { + storm_eye_input_index, + } => Either::Right(Either::Right(storm_eye_input_index)), + }; + + VoucherWitness { path } + } +} diff --git a/contracts/tests/account.rs b/contracts/tests/account.rs index 6222c6c..169eb9a 100644 --- a/contracts/tests/account.rs +++ b/contracts/tests/account.rs @@ -1,51 +1,66 @@ #[path = "common/mod.rs"] mod common; -use common::{assert_covenant_rejects, fund_script, issue_asset}; +use common::{assert_covenant_rejects, issue_asset}; use simplex::transaction::utxo::UTXO; -use simplex::transaction::{ - FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, -}; +use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature}; -use contracts::artifacts::account::AccountProgram; -use contracts::artifacts::account::derived_account::{AccountArguments, AccountWitness}; +use contracts::account::{Account, AccountParameters}; const STORM_EYE_SUPPLY: u64 = 10_000; const ACCOUNT_AMOUNT: u64 = 1_000; const ACCOUNT_OWNER_PUBKEY: [u8; 32] = [0u8; 32]; +/// Funds `account` with `amount` of the policy asset and returns the resulting UTXO. +fn fund_account( + context: &simplex::TestContext, + account: &Account, + amount: u64, +) -> anyhow::Result { + let signer = context.get_default_signer(); + let funding_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); + + let mut ft = FinalTransaction::new(); + ft.add_input( + PartialInput::new(funding_utxo), + RequiredSignature::NativeEcdsa, + ); + + account.attach_account_output(&mut ft, amount, context.get_network().policy_asset()); + + signer.broadcast(&ft)?.wait()?; + + context + .get_default_provider() + .fetch_scripthash_utxos(&account.get_script_pubkey())? + .first() + .cloned() + .ok_or_else(|| anyhow::anyhow!("funding transaction produced no UTXO")) +} + /// Spends the Account UTXO while claiming input 1 is the Storm Eye. fn spend_transaction( context: &simplex::TestContext, - account: &AccountProgram, + account: &Account, account_utxo: &UTXO, auth_utxo: &UTXO, ) -> FinalTransaction { let signer = context.get_default_signer(); let mut ft = FinalTransaction::new(); - ft.add_program_input( - PartialInput::new(account_utxo.clone()), - ProgramInput::new( - Box::new(account.as_ref().clone()), - Box::new(AccountWitness { - storm_eye_input_index: 1, - }), - ), - RequiredSignature::None, - ); + account.attach_spend(&mut ft, account_utxo, 1); ft.add_input( PartialInput::new(auth_utxo.clone()), RequiredSignature::NativeEcdsa, ); - ft.add_output(PartialOutput::new( - account.get_script_pubkey(context.get_network()), + account.attach_account_output( + &mut ft, account_utxo.explicit_amount(), account_utxo.explicit_asset(), - )); + ); ft.add_output(PartialOutput::new( signer.get_address().script_pubkey(), auth_utxo.explicit_amount(), @@ -60,13 +75,13 @@ fn spends_account_when_storm_eye_is_present(context: simplex::TestContext) -> an let signer = context.get_default_signer(); let storm_eye_asset = issue_asset(&context, STORM_EYE_SUPPLY)?; - let account = AccountProgram::new(&AccountArguments { - storm_eye_asset_id: storm_eye_asset.into_inner().to_byte_array(), + let account = Account::new(AccountParameters { + storm_eye_asset_id: storm_eye_asset, account_owner_pubkey: ACCOUNT_OWNER_PUBKEY, + network: *context.get_network(), }); - let account_script_pubkey = account.get_script_pubkey(context.get_network()); - let account_utxo = fund_script(&context, &account_script_pubkey, ACCOUNT_AMOUNT)?; + let account_utxo = fund_account(&context, &account, ACCOUNT_AMOUNT)?; let storm_eye_utxo = signer.get_utxos_asset(storm_eye_asset)?[0].clone(); let ft = spend_transaction(&context, &account, &account_utxo, &storm_eye_utxo); @@ -83,13 +98,13 @@ fn rejects_account_spend_without_storm_eye(context: simplex::TestContext) -> any let decoy_asset = issue_asset(&context, STORM_EYE_SUPPLY)?; assert_ne!(storm_eye_asset, decoy_asset); - let account = AccountProgram::new(&AccountArguments { - storm_eye_asset_id: storm_eye_asset.into_inner().to_byte_array(), + let account = Account::new(AccountParameters { + storm_eye_asset_id: storm_eye_asset, account_owner_pubkey: ACCOUNT_OWNER_PUBKEY, + network: *context.get_network(), }); - let account_script_pubkey = account.get_script_pubkey(context.get_network()); - let account_utxo = fund_script(&context, &account_script_pubkey, ACCOUNT_AMOUNT)?; + let account_utxo = fund_account(&context, &account, ACCOUNT_AMOUNT)?; // Asset that simply is not the Storm Eye let decoy_utxo = signer.get_utxos_asset(decoy_asset)?[0].clone(); diff --git a/contracts/tests/common/mod.rs b/contracts/tests/common/mod.rs index e22c1d6..48e0085 100644 --- a/contracts/tests/common/mod.rs +++ b/contracts/tests/common/mod.rs @@ -1,8 +1,7 @@ #![allow(dead_code)] use simplex::signer::SignerError; -use simplex::simplicityhl::elements::{AssetId, Script}; +use simplex::simplicityhl::elements::AssetId; use simplex::transaction::partial_input::IssuanceInput; -use simplex::transaction::utxo::UTXO; use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature}; pub fn issue_asset(context: &simplex::TestContext, amount: u64) -> anyhow::Result { @@ -27,26 +26,6 @@ pub fn issue_asset(context: &simplex::TestContext, amount: u64) -> anyhow::Resul Ok(issuance.asset_id) } -/// Sends `amount` of the policy asset to `script_pubkey` and returns the resulting UTXO, -/// which is how a covenant under test gets something to guard. -pub fn fund_script( - context: &simplex::TestContext, - script_pubkey: &Script, - amount: u64, -) -> anyhow::Result { - context - .get_default_signer() - .send(script_pubkey.clone(), amount)? - .wait()?; - - context - .get_default_provider() - .fetch_scripthash_utxos(script_pubkey)? - .first() - .cloned() - .ok_or_else(|| anyhow::anyhow!("funding transaction produced no UTXO")) -} - /// Asserts the transaction is rejected by the covenant #[track_caller] pub fn assert_covenant_rejects(context: &simplex::TestContext, tx: &FinalTransaction) { diff --git a/contracts/tests/tick_asset.rs b/contracts/tests/voucher.rs similarity index 51% rename from contracts/tests/tick_asset.rs rename to contracts/tests/voucher.rs index fe8069e..b8789d4 100644 --- a/contracts/tests/tick_asset.rs +++ b/contracts/tests/voucher.rs @@ -1,88 +1,74 @@ -//! 3. Tick asset contract (spec §3.4). +//! 3. Voucher contract (spec §3.4). #[path = "common/mod.rs"] mod common; use common::{assert_covenant_rejects, issue_asset}; -use simplex::either::Either; use simplex::simplicityhl::elements::{AssetId, Script}; use simplex::transaction::partial_input::IssuanceInput; use simplex::transaction::utxo::UTXO; -use simplex::transaction::{ - FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, -}; +use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature}; use simplex::utils::hash_script; -use contracts::artifacts::tick_asset::TickAssetProgram; -use contracts::artifacts::tick_asset::derived_tick_asset::{TickAssetArguments, TickAssetWitness}; +use contracts::voucher::{Voucher, VoucherAuthMethod, VoucherParameters, VoucherSpendPath}; const STORM_EYE_SUPPLY: u64 = 10_000; const AUTH_ASSET_SUPPLY: u64 = 10_000; /// Timestamp encoded in amount. -const TICK_TIMESTAMP: u64 = 1_700_000_000; - -const AUTH_METHOD_ASSET: u32 = 0; -const AUTH_METHOD_SCRIPT: u32 = 1; -const AUTH_METHOD_SIGNATURE: u32 = 2; - -type TickPath = Either, Either<([u8; 64], u32), u32>>; - -/// One constructor per spending path, in the order `tick_asset.simf` declares them. -mod path { - use super::{Either, TickPath}; - - pub fn asset_auth(input_index: u32, output_index: u32) -> TickPath { - Either::Left(Either::Left((input_index, output_index))) - } - - pub fn script_auth(input_index: u32, output_index: u32) -> TickPath { - Either::Left(Either::Right((input_index, output_index))) - } - - pub fn sign_auth(output_index: u32) -> TickPath { - Either::Right(Either::Left(([0u8; 64], output_index))) - } - - pub fn network_auth(input_index: u32) -> TickPath { - Either::Right(Either::Right(input_index)) - } -} +const VOUCHER_TIMESTAMP: u64 = 1_700_000_000; fn op_return_output(amount: u64, asset: AssetId) -> PartialOutput { PartialOutput::new(Script::new_op_return(&[]), amount, asset) } -struct TickFixture { - program: TickAssetProgram, +struct VoucherFixture { + voucher: Voucher, storm_eye_asset: AssetId, - tick_asset: AssetId, + voucher_asset: AssetId, /// Carries `AUTH_ASSET_ID`, and sits at the signer's address. auth_asset: AssetId, } -impl TickFixture { - fn new(context: &simplex::TestContext, auth_method: u32) -> anyhow::Result { +/// Which auth method to configure a `VoucherFixture` for. +enum AuthMethodKind { + Asset, + Script, + Signature, +} + +impl VoucherFixture { + fn new(context: &simplex::TestContext, kind: AuthMethodKind) -> anyhow::Result { let signer = context.get_default_signer(); let storm_eye_asset = issue_asset(context, STORM_EYE_SUPPLY)?; let auth_asset = issue_asset(context, AUTH_ASSET_SUPPLY)?; - let program = TickAssetProgram::new(&TickAssetArguments { - storm_eye_asset_id: storm_eye_asset.into_inner().to_byte_array(), + let auth_method = match kind { + AuthMethodKind::Asset => VoucherAuthMethod::Asset { + auth_asset_id: auth_asset, + }, + AuthMethodKind::Script => VoucherAuthMethod::Script { + auth_script_hash: hash_script(&signer.get_address().script_pubkey()), + }, + AuthMethodKind::Signature => VoucherAuthMethod::Signature { + auth_pubkey: signer.get_schnorr_public_key().serialize(), + }, + }; + + let voucher = Voucher::new(VoucherParameters { + storm_eye_asset_id: storm_eye_asset, auth_method, - auth_asset_id: auth_asset.into_inner().to_byte_array(), - auth_script_hash: hash_script(&signer.get_address().script_pubkey()), - auth_pubkey: signer.get_schnorr_public_key().serialize(), + network: *context.get_network(), }); - let tick_asset = issue_tick_asset(context, &program)?; + let voucher_asset = issue_voucher(context, &voucher)?; Ok(Self { - program, + voucher, storm_eye_asset, - tick_asset, + voucher_asset, auth_asset, }) } @@ -94,19 +80,17 @@ impl TickFixture { .clone()) } - fn tick_utxo(&self, context: &simplex::TestContext) -> anyhow::Result { - self.tick_utxos(context)? + fn voucher_utxo(&self, context: &simplex::TestContext) -> anyhow::Result { + self.voucher_utxos(context)? .into_iter() .next() - .ok_or_else(|| anyhow::anyhow!("the covenant holds no Tick UTXO")) + .ok_or_else(|| anyhow::anyhow!("the covenant holds no Voucher UTXO")) } - fn tick_utxos(&self, context: &simplex::TestContext) -> anyhow::Result> { - let script_pubkey = self.program.get_script_pubkey(context.get_network()); - + fn voucher_utxos(&self, context: &simplex::TestContext) -> anyhow::Result> { Ok(context .get_default_provider() - .fetch_scripthash_utxos(&script_pubkey)?) + .fetch_scripthash_utxos(&self.voucher.get_script_pubkey())?) } fn storm_eye_utxo(&self, context: &simplex::TestContext) -> anyhow::Result { @@ -116,29 +100,21 @@ impl TickFixture { .clone()) } - /// Spends the Tick UTXO with `auth_utxo` at input 1 and `burn_output` at output 0. + /// Spends the Voucher UTXO with `auth_utxo` at input 1 and `burn_output` at output 0. fn burn_transaction( &self, context: &simplex::TestContext, auth_utxo: &UTXO, - path: TickPath, - required_signature: RequiredSignature, + path: VoucherSpendPath, burn_output: PartialOutput, ) -> anyhow::Result { let signer = context.get_default_signer(); - let tick_utxo = self.tick_utxo(context)?; + let voucher_utxo = self.voucher_utxo(context)?; let mut ft = FinalTransaction::new(); - // Input 0: the Tick UTXO under the covenant. - ft.add_program_input( - PartialInput::new(tick_utxo.clone()), - ProgramInput::new( - Box::new(self.program.as_ref().clone()), - Box::new(TickAssetWitness { path }), - ), - required_signature, - ); + // Input 0: the Voucher UTXO under the covenant. + self.voucher.attach_spend(&mut ft, &voucher_utxo, path); // Input 1: whatever is meant to authorise the spend. ft.add_input( PartialInput::new(auth_utxo.clone()), @@ -158,11 +134,8 @@ impl TickFixture { } } -/// Issues the Tick asset directly to the covenant, with the timestamp as its amount. -fn issue_tick_asset( - context: &simplex::TestContext, - program: &TickAssetProgram, -) -> anyhow::Result { +/// Issues the Voucher directly to the covenant, with the timestamp as its amount. +fn issue_voucher(context: &simplex::TestContext, voucher: &Voucher) -> anyhow::Result { let signer = context.get_default_signer(); let funding_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); @@ -170,15 +143,11 @@ fn issue_tick_asset( let issuance = ft.add_issuance_input( PartialInput::new(funding_utxo), - IssuanceInput::new_issuance(TICK_TIMESTAMP * 2, 0, [1u8; 32]), + IssuanceInput::new_issuance(VOUCHER_TIMESTAMP * 2, 0, [1u8; 32]), RequiredSignature::NativeEcdsa, ); for _ in 0..2 { - ft.add_output(PartialOutput::new( - program.get_script_pubkey(context.get_network()), - TICK_TIMESTAMP, - issuance.asset_id, - )); + voucher.attach_voucher_output(&mut ft, VOUCHER_TIMESTAMP, issuance.asset_id); } signer.broadcast(&ft)?.wait()?; @@ -188,7 +157,7 @@ fn issue_tick_asset( #[simplex::test] fn rejects_network_burn_without_storm_eye(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let decoy_asset = issue_asset(&context, STORM_EYE_SUPPLY)?; assert_ne!(decoy_asset, fixture.storm_eye_asset); @@ -197,9 +166,10 @@ fn rejects_network_burn_without_storm_eye(context: simplex::TestContext) -> anyh let ft = fixture.burn_transaction( &context, &decoy_utxo, - path::network_auth(1), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::NetworkAuth { + storm_eye_input_index: 1, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; assert_covenant_rejects(&context, &ft); @@ -209,18 +179,20 @@ fn rejects_network_burn_without_storm_eye(context: simplex::TestContext) -> anyh #[simplex::test] fn rejects_burn_to_a_spendable_output(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let auth_utxo = fixture.auth_utxo(&context)?; let ft = fixture.burn_transaction( &context, &auth_utxo, - path::asset_auth(1, 0), - RequiredSignature::None, + VoucherSpendPath::AssetAuth { + auth_input_index: 1, + voucher_output_index: 0, + }, PartialOutput::new( context.get_default_signer().get_address().script_pubkey(), - TICK_TIMESTAMP, - fixture.tick_asset, + VOUCHER_TIMESTAMP, + fixture.voucher_asset, ), )?; @@ -233,15 +205,17 @@ fn rejects_burn_to_a_spendable_output(context: simplex::TestContext) -> anyhow:: fn rejects_burn_that_does_not_preserve_the_amount( context: simplex::TestContext, ) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let auth_utxo = fixture.auth_utxo(&context)?; let ft = fixture.burn_transaction( &context, &auth_utxo, - path::asset_auth(1, 0), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP - 1, fixture.tick_asset), + VoucherSpendPath::AssetAuth { + auth_input_index: 1, + voucher_output_index: 0, + }, + op_return_output(VOUCHER_TIMESTAMP - 1, fixture.voucher_asset), )?; assert_covenant_rejects(&context, &ft); @@ -250,21 +224,22 @@ fn rejects_burn_that_does_not_preserve_the_amount( } #[simplex::test] -fn network_authorization_does_not_constrain_tick_outputs( +fn network_authorization_does_not_constrain_voucher_outputs( context: simplex::TestContext, ) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let storm_eye_utxo = fixture.storm_eye_utxo(&context)?; let ft = fixture.burn_transaction( &context, &storm_eye_utxo, - path::network_auth(1), - RequiredSignature::None, + VoucherSpendPath::NetworkAuth { + storm_eye_input_index: 1, + }, PartialOutput::new( context.get_default_signer().get_address().script_pubkey(), - TICK_TIMESTAMP, - fixture.tick_asset, + VOUCHER_TIMESTAMP, + fixture.voucher_asset, ), )?; @@ -277,15 +252,17 @@ fn network_authorization_does_not_constrain_tick_outputs( fn rejects_spending_through_another_auth_method( context: simplex::TestContext, ) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let storm_eye_utxo = fixture.storm_eye_utxo(&context)?; let ft = fixture.burn_transaction( &context, &storm_eye_utxo, - path::script_auth(1, 0), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::ScriptAuth { + auth_input_index: 1, + voucher_output_index: 0, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; assert_covenant_rejects(&context, &ft); @@ -293,18 +270,20 @@ fn rejects_spending_through_another_auth_method( Ok(()) } -/// §3.4.1. happy path. +/// 1. happy path. #[simplex::test] -fn burns_tick_utxo_via_asset_auth(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; +fn burns_voucher_utxo_via_asset_auth(context: simplex::TestContext) -> anyhow::Result<()> { + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let auth_utxo = fixture.auth_utxo(&context)?; let ft = fixture.burn_transaction( &context, &auth_utxo, - path::asset_auth(1, 0), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::AssetAuth { + auth_input_index: 1, + voucher_output_index: 0, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; context.get_default_signer().broadcast(&ft)?.wait()?; @@ -314,16 +293,18 @@ fn burns_tick_utxo_via_asset_auth(context: simplex::TestContext) -> anyhow::Resu /// 2 happy path. #[simplex::test] -fn burns_tick_utxo_via_script_auth(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_SCRIPT)?; +fn burns_voucher_utxo_via_script_auth(context: simplex::TestContext) -> anyhow::Result<()> { + let fixture = VoucherFixture::new(&context, AuthMethodKind::Script)?; let auth_utxo = fixture.auth_utxo(&context)?; let ft = fixture.burn_transaction( &context, &auth_utxo, - path::script_auth(1, 0), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::ScriptAuth { + auth_input_index: 1, + voucher_output_index: 0, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; context.get_default_signer().broadcast(&ft)?.wait()?; @@ -333,16 +314,17 @@ fn burns_tick_utxo_via_script_auth(context: simplex::TestContext) -> anyhow::Res /// 3 happy path. #[simplex::test] -fn burns_tick_utxo_via_signature_auth(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_SIGNATURE)?; +fn burns_voucher_utxo_via_signature_auth(context: simplex::TestContext) -> anyhow::Result<()> { + let fixture = VoucherFixture::new(&context, AuthMethodKind::Signature)?; let auth_utxo = fixture.auth_utxo(&context)?; let ft = fixture.burn_transaction( &context, &auth_utxo, - path::sign_auth(0), - RequiredSignature::witness_with_path("PATH", ["Right", "Left", "0"]), - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::SignatureAuth { + voucher_output_index: 0, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; context.get_default_signer().broadcast(&ft)?.wait()?; @@ -352,16 +334,19 @@ fn burns_tick_utxo_via_signature_auth(context: simplex::TestContext) -> anyhow:: /// 4 happy path. #[simplex::test] -fn burns_tick_utxo_when_storm_eye_is_present(context: simplex::TestContext) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; +fn burns_voucher_utxo_when_storm_eye_is_present( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; let storm_eye_utxo = fixture.storm_eye_utxo(&context)?; let ft = fixture.burn_transaction( &context, &storm_eye_utxo, - path::network_auth(1), - RequiredSignature::None, - op_return_output(TICK_TIMESTAMP, fixture.tick_asset), + VoucherSpendPath::NetworkAuth { + storm_eye_input_index: 1, + }, + op_return_output(VOUCHER_TIMESTAMP, fixture.voucher_asset), )?; context.get_default_signer().broadcast(&ft)?.wait()?; @@ -370,32 +355,32 @@ fn burns_tick_utxo_when_storm_eye_is_present(context: simplex::TestContext) -> a } #[simplex::test] -fn burns_multiple_tick_utxos_to_one_empty_op_return( +fn burns_multiple_voucher_utxos_to_one_empty_op_return( context: simplex::TestContext, ) -> anyhow::Result<()> { - let fixture = TickFixture::new(&context, AUTH_METHOD_ASSET)?; - let tick_utxos = fixture.tick_utxos(&context)?; + let fixture = VoucherFixture::new(&context, AuthMethodKind::Asset)?; + let voucher_utxos = fixture.voucher_utxos(&context)?; let storm_eye_utxo = fixture.storm_eye_utxo(&context)?; - assert_eq!(tick_utxos.len(), 2); + assert_eq!(voucher_utxos.len(), 2); let mut transaction = FinalTransaction::new(); - for tick_utxo in tick_utxos { - transaction.add_program_input( - PartialInput::new(tick_utxo), - ProgramInput::new( - Box::new(fixture.program.as_ref().clone()), - Box::new(TickAssetWitness { - path: path::network_auth(2), - }), - ), - RequiredSignature::None, + for voucher_utxo in voucher_utxos { + fixture.voucher.attach_spend( + &mut transaction, + &voucher_utxo, + VoucherSpendPath::NetworkAuth { + storm_eye_input_index: 2, + }, ); } transaction.add_input( PartialInput::new(storm_eye_utxo.clone()), RequiredSignature::NativeEcdsa, ); - transaction.add_output(op_return_output(TICK_TIMESTAMP * 2, fixture.tick_asset)); + transaction.add_output(op_return_output( + VOUCHER_TIMESTAMP * 2, + fixture.voucher_asset, + )); transaction.add_output(PartialOutput::new( context.get_default_signer().get_address().script_pubkey(), storm_eye_utxo.explicit_amount(), diff --git a/high-storm/Cargo.toml b/high-storm/Cargo.toml index 2d7501c..25d467a 100644 --- a/high-storm/Cargo.toml +++ b/high-storm/Cargo.toml @@ -27,7 +27,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } url.workspace = true contracts.workspace = true -simplex = { package = "smplx-std", git = "https://github.com/BlockstreamResearch/smplx", rev = "c1e9d8b06231973d81fbaa677cf3f03787104b81" } +simplex.workspace = true storm.workspace = true storm-tree = { path = "../storm-tree" } diff --git a/high-storm/src/high_storm/burning.rs b/high-storm/src/high_storm/burning.rs index e19ea52..96ba1d6 100644 --- a/high-storm/src/high_storm/burning.rs +++ b/high-storm/src/high_storm/burning.rs @@ -8,9 +8,9 @@ use bitcoincore_rpc::{Auth, Client, RpcApi}; use contracts::artifacts::{ account::{AccountProgram, derived_account::AccountArguments}, auth::derived_auth::AuthWitness, - tick_asset::{ - TickAssetProgram, - derived_tick_asset::{TickAssetArguments, TickAssetWitness}, + voucher::{ + VoucherProgram, + derived_voucher::{VoucherArguments, VoucherWitness}, }, }; use secp256k1_zkp::{Message, Secp256k1, XOnlyPublicKey, schnorr::Signature}; @@ -258,7 +258,7 @@ impl Burning { PartialInput::new(tick_utxo.clone()), ProgramInput::new( Box::new(program.as_ref().clone()), - Box::new(TickAssetWitness { + Box::new(VoucherWitness { path: Either::Right(Either::Right(0)), }), ), @@ -720,8 +720,8 @@ fn select_groups(expired: Vec) -> Result, BurningE fn tick_program( storm_eye_asset_id: [u8; 32], tick: &MonitoredUtxo, -) -> Result { - let mut arguments = TickAssetArguments { +) -> Result { + let mut arguments = VoucherArguments { storm_eye_asset_id, auth_method: 0, auth_asset_id: [0; 32], @@ -744,7 +744,7 @@ fn tick_program( )); } } - Ok(TickAssetProgram::new(&arguments)) + Ok(VoucherProgram::new(&arguments)) } fn fee_share(total: u64, groups: usize, index: usize) -> Result { diff --git a/high-storm/src/high_storm/issuance.rs b/high-storm/src/high_storm/issuance.rs index 10643cb..1027da9 100644 --- a/high-storm/src/high_storm/issuance.rs +++ b/high-storm/src/high_storm/issuance.rs @@ -1,4 +1,4 @@ -use contracts::artifacts::tick_asset::{TickAssetProgram, derived_tick_asset::TickAssetArguments}; +use contracts::artifacts::voucher::{VoucherProgram, derived_voucher::VoucherArguments}; use simplex::{ provider::SimplicityNetwork, simplicityhl::elements::{Script, opcodes, script::Instruction}, @@ -130,8 +130,8 @@ impl IssuedTickDescriptor { } } - pub(crate) fn tick_program(&self, storm_eye_asset_id: [u8; 32]) -> TickAssetProgram { - let mut arguments = TickAssetArguments { + pub(crate) fn tick_program(&self, storm_eye_asset_id: [u8; 32]) -> VoucherProgram { + let mut arguments = VoucherArguments { storm_eye_asset_id, auth_method: self.auth_kind as u32, auth_asset_id: [0; 32], @@ -144,7 +144,7 @@ impl IssuedTickDescriptor { 2 => arguments.auth_pubkey = self.auth_data, _ => unreachable!("descriptor authentication kind is validated"), } - TickAssetProgram::new(&arguments) + VoucherProgram::new(&arguments) } pub(crate) fn matches_tick_script( diff --git a/high-storm/src/high_storm/user_requests.rs b/high-storm/src/high_storm/user_requests.rs index 47a7ec3..03a65aa 100644 --- a/high-storm/src/high_storm/user_requests.rs +++ b/high-storm/src/high_storm/user_requests.rs @@ -7,11 +7,11 @@ use bitcoincore_rpc::{Auth, Client, RpcApi}; use contracts::artifacts::{ account::{AccountProgram, derived_account::AccountArguments}, auth::derived_auth::AuthWitness, - tick_asset::{TickAssetProgram, derived_tick_asset::TickAssetArguments}, treasury::{ TreasuryProgram, derived_treasury::{TreasuryArguments, TreasuryWitness}, }, + voucher::{VoucherProgram, derived_voucher::VoucherArguments}, }; use secp256k1_zkp::{ Message, PublicKey, RangeProof, Secp256k1, SurjectionProof, XOnlyPublicKey, schnorr::Signature, @@ -1310,8 +1310,8 @@ fn validate_execute_request( fn tick_program( storm_eye_asset_id: [u8; 32], details: &TickUtxoRequestDetails, -) -> Result { - let mut arguments = TickAssetArguments { +) -> Result { + let mut arguments = VoucherArguments { storm_eye_asset_id, auth_method: 0, auth_asset_id: [0; 32], @@ -1341,7 +1341,7 @@ fn tick_program( } } - Ok(TickAssetProgram::new(&arguments)) + Ok(VoucherProgram::new(&arguments)) } fn validate_accounting_outputs(