From 4b3a714da1c1ef100db13ef24347c4210cb4fced Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 16:52:16 +0200 Subject: [PATCH 1/8] Test fix --- flutter_plugin/src/test_lib.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/flutter_plugin/src/test_lib.rs b/flutter_plugin/src/test_lib.rs index 8bb475e..45e8881 100644 --- a/flutter_plugin/src/test_lib.rs +++ b/flutter_plugin/src/test_lib.rs @@ -1,7 +1,7 @@ use crate::adaptor_signature::verify_ecdsa_signature; use crate::{ combine_pubkeys_intern, combine_seckeys_intern, create_deterministic_nonce_intern, - get_public_key_intern, init_with_entropy, init_with_entropy_intern, keypair_from_sec_key_hex, + get_public_key_intern, init_with_entropy_intern, keypair_from_sec_key_hex, sign_schnorr_with_nonce_intern, verify_public_key_intern, Lib, }; use bitcoin::hex::FromHex; @@ -44,16 +44,6 @@ fn test_init_with_entropy_intern() { ); } -#[test] -fn test_init_with_entropy() { - let xpub = - init_with_entropy(DUMMY_ENTROPY_STR.to_string(), DEFAULT_NETWORK.to_string()).unwrap(); - assert_eq!( - xpub, - "tpubDCWivZp6qaqCALCt8MyLqAb3awnWm4hfbBPjdZqirYFXYeZ5YsfbWVaPacULZTGtK1RPBSZ92UWNjnhL4fB9UVrF2FjgW8cgmBjxPBmB4iB" - ); -} - #[test] fn test_init_with_entropy_lib_mainnet() { let mut lib = Lib::new_empty(); From 191c8520f043c7fc9369f98e7de1d228b1cbfe61 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 16:53:19 +0200 Subject: [PATCH 2/8] Formatting --- flutter_plugin/src/lib.rs | 173 ++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 65 deletions(-) diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index d6dd731..01b084a 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -17,20 +17,19 @@ use crate::secret_entropy_storage::parse_entropy_hex; use bitcoin::hex::{DisplayHex, FromHex}; use bitcoin::secp256k1::{PublicKey, SecretKey}; use secp256k1_zkp::schnorr::Signature as SchnorrSignature; -use secp256k1_zkp::EcdsaAdaptorSignature; // Import missing types -use std::str::FromStr; +use secp256k1_zkp::EcdsaAdaptorSignature; // Import missing types use std::ffi::{CStr, CString}; use std::os::raw::c_char; +use std::str::FromStr; // Conditional compilation to exclude PyO3-related code for Android #[cfg(feature = "with-pyo3")] -use pyo3::prelude::*; -#[cfg(feature = "with-pyo3")] use pyo3::exceptions::PyException; #[cfg(feature = "with-pyo3")] +use pyo3::prelude::*; +#[cfg(feature = "with-pyo3")] use pyo3::wrap_pyfunction; - /// Initialize the library, load secret from encrypted file. Return the XPUB. fn init_intern( path_for_secret_file: &str, @@ -452,24 +451,32 @@ fn create_final_cet_sigs_intern( Ok(sigs) } - // ##### Facade functions for C-style-interface invocations /// Initialize the library, provide the secret as parameter. Return the XPUB. #[no_mangle] -pub extern "C" fn init_with_entropy_c(entropy: *const c_char, network: *const c_char) -> *mut c_char { +pub extern "C" fn init_with_entropy_c( + entropy: *const c_char, + network: *const c_char, +) -> *mut c_char { // Convert input parameter from raw pointer to Rust string - let entropy_str = unsafe { CStr::from_ptr(entropy).to_str().unwrap_or("Error in entropy parameter") }; - let network_str = unsafe { CStr::from_ptr(network).to_str().unwrap_or("Error in network parameter") }; + let entropy_str = unsafe { + CStr::from_ptr(entropy) + .to_str() + .unwrap_or("Error in entropy parameter") + }; + let network_str = unsafe { + CStr::from_ptr(network) + .to_str() + .unwrap_or("Error in network parameter") + }; match init_with_entropy_intern(entropy_str, network_str) { Ok(xpub) => { // Return as a C string CString::new(xpub).unwrap().into_raw() } - Err(e) => { - error_as_cstr_prefix(e) - } + Err(e) => error_as_cstr_prefix(e), } } @@ -481,27 +488,35 @@ pub extern "C" fn get_public_key_c(index: u32) -> *mut c_char { // Return as a C string CString::new(pubkey).unwrap().into_raw() } - Err(e) => { - error_as_cstr_prefix(e) - } + Err(e) => error_as_cstr_prefix(e), } } /// Sign a hash with a child private key (specified by its index). #[no_mangle] -pub extern "C" fn sign_hash_ecdsa_c(hash: *const c_char, signer_index: u32, signer_pubkey: *const c_char) -> *mut c_char { +pub extern "C" fn sign_hash_ecdsa_c( + hash: *const c_char, + signer_index: u32, + signer_pubkey: *const c_char, +) -> *mut c_char { // Convert input parameter from raw pointer to Rust string - let hash_str = unsafe { CStr::from_ptr(hash).to_str().unwrap_or("Error in hash parameter") }; - let signer_pubkey_str = unsafe { CStr::from_ptr(signer_pubkey).to_str().unwrap_or("Error in signer_pubkey parameter") }; + let hash_str = unsafe { + CStr::from_ptr(hash) + .to_str() + .unwrap_or("Error in hash parameter") + }; + let signer_pubkey_str = unsafe { + CStr::from_ptr(signer_pubkey) + .to_str() + .unwrap_or("Error in signer_pubkey parameter") + }; match sign_hash_ecdsa_intern(hash_str, signer_index, signer_pubkey_str) { Ok(sig) => { // Return as a C string CString::new(sig).unwrap().into_raw() } - Err(e) => { - error_as_cstr_prefix(e) - } + Err(e) => error_as_cstr_prefix(e), } } @@ -519,41 +534,72 @@ pub extern "C" fn create_cet_adaptor_sigs_c( sighashes: *const c_char, ) -> *mut c_char { // Convert input parameter from raw pointer to Rust string - let digit_string_template_str = unsafe { CStr::from_ptr(digit_string_template).to_str().unwrap_or("Error in digit_string_template parameter") }; - let oracle_pubkey_str = unsafe { CStr::from_ptr(oracle_pubkey).to_str().unwrap_or("Error in oracle_pubkey parameter") }; - let signing_pubkey_str = unsafe { CStr::from_ptr(signing_pubkey).to_str().unwrap_or("Error in signing_pubkey parameter") }; - let nonces_str = unsafe { CStr::from_ptr(nonces).to_str().unwrap_or("Error in nonces parameter") }; - let interval_wildcards_str = unsafe { CStr::from_ptr(interval_wildcards).to_str().unwrap_or("Error in interval_wildcards parameter") }; - let sighashes_str = unsafe { CStr::from_ptr(sighashes).to_str().unwrap_or("Error in sighashes parameter") }; - - match create_cet_adaptor_sigs_intern(num_digits, num_cets as u64, digit_string_template_str, oracle_pubkey_str, signing_key_index, signing_pubkey_str, nonces_str, interval_wildcards_str, sighashes_str) { + let digit_string_template_str = unsafe { + CStr::from_ptr(digit_string_template) + .to_str() + .unwrap_or("Error in digit_string_template parameter") + }; + let oracle_pubkey_str = unsafe { + CStr::from_ptr(oracle_pubkey) + .to_str() + .unwrap_or("Error in oracle_pubkey parameter") + }; + let signing_pubkey_str = unsafe { + CStr::from_ptr(signing_pubkey) + .to_str() + .unwrap_or("Error in signing_pubkey parameter") + }; + let nonces_str = unsafe { + CStr::from_ptr(nonces) + .to_str() + .unwrap_or("Error in nonces parameter") + }; + let interval_wildcards_str = unsafe { + CStr::from_ptr(interval_wildcards) + .to_str() + .unwrap_or("Error in interval_wildcards parameter") + }; + let sighashes_str = unsafe { + CStr::from_ptr(sighashes) + .to_str() + .unwrap_or("Error in sighashes parameter") + }; + + match create_cet_adaptor_sigs_intern( + num_digits, + num_cets as u64, + digit_string_template_str, + oracle_pubkey_str, + signing_key_index, + signing_pubkey_str, + nonces_str, + interval_wildcards_str, + sighashes_str, + ) { Ok(sigs) => { // Return as a C string CString::new(sigs).unwrap().into_raw() } - Err(e) => { - error_as_cstr_prefix(e) - } + Err(e) => error_as_cstr_prefix(e), } } #[no_mangle] -pub extern "C" fn create_deterministic_nonce_c( - event_id: *const c_char, - index: u32, -) -> *mut c_char { +pub extern "C" fn create_deterministic_nonce_c(event_id: *const c_char, index: u32) -> *mut c_char { // Convert the event_id from raw pointer to Rust string - let event_id_str = unsafe { CStr::from_ptr(event_id).to_str().unwrap_or("Error in event ID") }; + let event_id_str = unsafe { + CStr::from_ptr(event_id) + .to_str() + .unwrap_or("Error in event ID") + }; // Call your existing function that creates the nonce (assuming this is what you want) match create_deterministic_nonce_intern(event_id_str, index) { Ok((sk, pk)) => { // Return as a C string CString::new(format!("{} {}", sk, pk)).unwrap().into_raw() - }, - Err(e) => { - error_as_cstr_prefix(e) } + Err(e) => error_as_cstr_prefix(e), } } @@ -561,56 +607,55 @@ pub extern "C" fn create_deterministic_nonce_c( #[no_mangle] pub extern "C" fn get_xpub_c() -> *mut c_char { match get_xpub_intern() { - Ok(xpub) => { - CString::new(xpub).unwrap().into_raw() - } - Err(e) => { - error_as_cstr_prefix(e) - } + Ok(xpub) => CString::new(xpub).unwrap().into_raw(), + Err(e) => error_as_cstr_prefix(e), } } #[no_mangle] pub extern "C" fn get_address_c(index: u32) -> *mut c_char { match get_address_intern(index) { - Ok(address) => { - CString::new(address).unwrap().into_raw() - } - Err(e) => { - error_as_cstr_prefix(e) - } + Ok(address) => CString::new(address).unwrap().into_raw(), + Err(e) => error_as_cstr_prefix(e), } } #[no_mangle] pub extern "C" fn init_from_file_c(path: *const c_char, password: *const c_char) -> *mut c_char { - let path_str = unsafe { CStr::from_ptr(path).to_str().unwrap_or("Error in path parameter") }; - let password_str = unsafe { CStr::from_ptr(password).to_str().unwrap_or("Error in password parameter") }; + let path_str = unsafe { + CStr::from_ptr(path) + .to_str() + .unwrap_or("Error in path parameter") + }; + let password_str = unsafe { + CStr::from_ptr(password) + .to_str() + .unwrap_or("Error in password parameter") + }; match init_intern(path_str, password_str, false) { - Ok(xpub) => { - CString::new(xpub).unwrap().into_raw() - } - Err(e) => { - error_as_cstr_prefix(e) - } + Ok(xpub) => CString::new(xpub).unwrap().into_raw(), + Err(e) => error_as_cstr_prefix(e), } } #[no_mangle] pub extern "C" fn free_cstring(s: *mut c_char) { unsafe { - if s.is_null() { return } + if s.is_null() { + return; + } let _ = CString::from_raw(s); } } // Return error with an "ERROR: " prefix, as a C string fn error_as_cstr_prefix(error: String) -> *mut c_char { - CString::new(format!("ERROR: {}", error)).unwrap().into_raw() + CString::new(format!("ERROR: {}", error)) + .unwrap() + .into_raw() } - // ##### Facade functions for easy Python invocations (pyo3/maturin) /// Initialize the library, load secret from encrypted file. Return the XPUB. @@ -836,5 +881,3 @@ fn dlcplazacryptlib(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(create_final_cet_sigs, m)?)?; Ok(()) } - - From 73ea652266f993a6e6baf24e265aee26e3bba45b Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 16:23:45 +0200 Subject: [PATCH 3/8] Add copyright messages --- flutter_plugin/src/adaptor_signature.rs | 4 ++++ flutter_plugin/src/hd_wallet_storage.rs | 4 ++++ flutter_plugin/src/lib.rs | 4 ++++ flutter_plugin/src/lib_struct.rs | 4 ++++ flutter_plugin/src/network.rs | 4 ++++ flutter_plugin/src/parse.rs | 4 ++++ flutter_plugin/src/secret_entropy_storage.rs | 4 ++++ flutter_plugin/src/test_lib.rs | 4 ++++ 8 files changed, 32 insertions(+) diff --git a/flutter_plugin/src/adaptor_signature.rs b/flutter_plugin/src/adaptor_signature.rs index 792ffcf..37212fc 100644 --- a/flutter_plugin/src/adaptor_signature.rs +++ b/flutter_plugin/src/adaptor_signature.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use bitcoin::hashes::{sha256, sha256t_hash_newtype, Hash}; use bitcoin::hex::DisplayHex; use bitcoin::key::{Keypair, Secp256k1}; diff --git a/flutter_plugin/src/hd_wallet_storage.rs b/flutter_plugin/src/hd_wallet_storage.rs index 5bf628c..ae66d9a 100644 --- a/flutter_plugin/src/hd_wallet_storage.rs +++ b/flutter_plugin/src/hd_wallet_storage.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use crate::secret_entropy_storage::SecretEntropyStore; use bip39::Mnemonic; diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index 01b084a..028f942 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + mod adaptor_signature; mod hd_wallet_storage; mod lib_struct; diff --git a/flutter_plugin/src/lib_struct.rs b/flutter_plugin/src/lib_struct.rs index 9d3b4b9..f8b320b 100644 --- a/flutter_plugin/src/lib_struct.rs +++ b/flutter_plugin/src/lib_struct.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use crate::adaptor_signature::{ create_cet_adaptor_signatures, create_final_cet_signature, create_final_cet_signatures, sign_hash_ecdsa_with_key, sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, diff --git a/flutter_plugin/src/network.rs b/flutter_plugin/src/network.rs index 139ecce..f59faa4 100644 --- a/flutter_plugin/src/network.rs +++ b/flutter_plugin/src/network.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use bitcoin::Network; pub(crate) fn network_from_byte(network_byte: u8) -> Result { diff --git a/flutter_plugin/src/parse.rs b/flutter_plugin/src/parse.rs index 4362200..9a2e6ac 100644 --- a/flutter_plugin/src/parse.rs +++ b/flutter_plugin/src/parse.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use bitcoin::hex::FromHex; use bitcoin::key::{Keypair, Secp256k1}; use bitcoin::secp256k1::PublicKey; diff --git a/flutter_plugin/src/secret_entropy_storage.rs b/flutter_plugin/src/secret_entropy_storage.rs index 840e455..a48cac9 100644 --- a/flutter_plugin/src/secret_entropy_storage.rs +++ b/flutter_plugin/src/secret_entropy_storage.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use crate::network::{network_from_byte, network_from_string}; use bip39::Mnemonic; diff --git a/flutter_plugin/src/test_lib.rs b/flutter_plugin/src/test_lib.rs index 45e8881..4fad6a9 100644 --- a/flutter_plugin/src/test_lib.rs +++ b/flutter_plugin/src/test_lib.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2025-present Cadena Bitcoin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + use crate::adaptor_signature::verify_ecdsa_signature; use crate::{ combine_pubkeys_intern, combine_seckeys_intern, create_deterministic_nonce_intern, From 6251951cb3f85c8eb3387b00ec207f35733fd0d2 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 17:44:12 +0200 Subject: [PATCH 4/8] create_final_cet_sigs, drop create_final_cet_sig --- flutter_plugin/src/adaptor_signature.rs | 58 ++++++------------- flutter_plugin/src/lib.rs | 74 +------------------------ flutter_plugin/src/lib_struct.rs | 27 +-------- 3 files changed, 22 insertions(+), 137 deletions(-) diff --git a/flutter_plugin/src/adaptor_signature.rs b/flutter_plugin/src/adaptor_signature.rs index 37212fc..b2770c5 100644 --- a/flutter_plugin/src/adaptor_signature.rs +++ b/flutter_plugin/src/adaptor_signature.rs @@ -410,16 +410,19 @@ pub(crate) fn verify_ecdsa_signature( } } -/// Decrypt a signature on a CET when outcome signatures are available. -/// Return the decrypted signature. -pub fn create_final_cet_signature( - pubkey: &PublicKey, +/// Create signatures on a CET when outcome signatures are available +pub fn create_final_cet_signatures( + secp: &Secp256k1, + signing_keypair: &Keypair, + other_pubkey: &PublicKey, num_digits: u8, oracle_signatures: &Vec, cet_value_wildcard: &str, cet_sighash: &[u8; 32], - adaptor_signature: &EcdsaAdaptorSignature, -) -> Result, String> { + other_adaptor_signature: &EcdsaAdaptorSignature, +) -> Result<(Vec, Vec), String> { + let signing_key = &signing_keypair.secret_key(); + // Decompose oracle signatures if oracle_signatures.len() != num_digits as usize { return Err(format!( @@ -447,46 +450,21 @@ pub fn create_final_cet_signature( } let adaptor_secret_aggregate = aggregate_secret_values(&adaptor_secret_vec)?; - // Adaptor signature, from the other - let mut adapted_sig = adaptor_signature + // Adaptor signature from the OTHER + let mut adapted_sig = other_adaptor_signature .decrypt(&adaptor_secret_aggregate) .map_err(|e| format!("Error in adaptor signature decryption {}", e.to_string()))? .serialize_der() .to_vec(); adapted_sig.push(EcdsaSighashType::All as u8); // verify signature - let _res = verify_ecdsa_signature(cet_sighash, &adapted_sig, &pubkey, true).map_err(|e| { - format!( - "Adaptor-derived signature verification failed {}", - e.to_string() - ) - })?; - - Ok(adapted_sig) -} - -/// Create the two signatures on a CET when outcome signatures are available. -/// Return the decrypted signature of the other, and my own signature (newly created). -pub fn create_final_cet_signatures( - secp: &Secp256k1, - signing_keypair: &Keypair, - other_pubkey: &PublicKey, - num_digits: u8, - oracle_signatures: &Vec, - cet_value_wildcard: &str, - cet_sighash: &[u8; 32], - other_adaptor_signature: &EcdsaAdaptorSignature, -) -> Result<(Vec, Vec), String> { - let signing_key = &signing_keypair.secret_key(); - - let adapted_sig = create_final_cet_signature( - other_pubkey, - num_digits, - oracle_signatures, - cet_value_wildcard, - cet_sighash, - other_adaptor_signature, - )?; + let _res = + verify_ecdsa_signature(cet_sighash, &adapted_sig, &other_pubkey, true).map_err(|e| { + format!( + "Adaptor-derived signature verification failed {}", + e.to_string() + ) + })?; // Now sign the CET on my own part let my_sig = sign_hash_ecdsa_with_key(&secp, cet_sighash, &signing_key)?; diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index 028f942..a5c4dd9 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -347,55 +347,7 @@ fn verify_cet_adaptor_sigs_intern( Ok(res.is_ok()) } -fn create_final_cet_sig_intern( - pubkey_str: &str, - num_digits: u8, - oracle_signatures_str: &str, - cet_value_wildcard: &str, - cet_sighash_str: &str, - adaptor_signature_str: &str, -) -> Result { - let pubkey = - pubkey_from_hex(pubkey_str).map_err(|e| format!("Failed to parse other pubkey {}", e))?; - - let sigs_split: Vec<_> = oracle_signatures_str.split(" ").collect(); - let mut sigs = Vec::::with_capacity(sigs_split.len()); - for i in 0..sigs_split.len() { - let sig_hex = sigs_split[i].trim(); - if sig_hex.len() > 0 { - let sig = schnorr_sig_from_hex(&sig_hex) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - sigs.push(sig); - } - } - if sigs.len() != num_digits as usize { - return Err(format!( - "Wrong number of signatures {} {}", - sigs.len(), - num_digits - )); - } - - let cet_sighash = - hash_from_hex(cet_sighash_str).map_err(|e| format!("Failed to parse sighash {}", e))?; - - let adaptor_signature_bin = Vec::from_hex(adaptor_signature_str) - .map_err(|e| format!("Failed to parse adaptor sig {}", e))?; - let adaptor_signature = EcdsaAdaptorSignature::from_slice(&adaptor_signature_bin) - .map_err(|e| format!("Failed to parse adaptor sig {}", e))?; - let sig = Lib::create_final_cet_sig( - &pubkey, - num_digits, - &sigs, - cet_value_wildcard, - &cet_sighash, - &adaptor_signature, - )?; - - Ok(sig.to_lower_hex_string()) -} - -fn create_final_cet_sigs_intern( +pub fn create_final_cet_sigs_intern( signing_key_index: u32, signing_pubkey_str: &str, other_pubkey_str: &str, @@ -814,29 +766,6 @@ pub fn verify_cet_adaptor_sigs( .map_err(|e| PyErr::new::(e)) } -/// Perform final signing of a CET, decrypt a signature when outcome signatures are available. -/// Return the decrypted signature. -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn create_final_cet_sig( - pubkey: String, - num_digits: u8, - oracle_signatures: String, - cet_value_wildcard: String, - cet_sighash: String, - adaptor_signature: String, -) -> PyResult { - create_final_cet_sig_intern( - &pubkey, - num_digits, - &oracle_signatures, - &cet_value_wildcard, - &cet_sighash, - &adaptor_signature, - ) - .map_err(|e| PyErr::new::(e)) -} - /// Perform final signing of a CET, create the two signatures when outcome signatures are available. /// Return the decrypted signature of the other, and my own signature (newly created). #[cfg(feature = "with-pyo3")] @@ -881,7 +810,6 @@ fn dlcplazacryptlib(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(combine_seckeys, m)?)?; m.add_function(wrap_pyfunction!(create_cet_adaptor_sigs, m)?)?; m.add_function(wrap_pyfunction!(verify_cet_adaptor_sigs, m)?)?; - m.add_function(wrap_pyfunction!(create_final_cet_sig, m)?)?; m.add_function(wrap_pyfunction!(create_final_cet_sigs, m)?)?; Ok(()) } diff --git a/flutter_plugin/src/lib_struct.rs b/flutter_plugin/src/lib_struct.rs index f8b320b..dce87a3 100644 --- a/flutter_plugin/src/lib_struct.rs +++ b/flutter_plugin/src/lib_struct.rs @@ -3,8 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. use crate::adaptor_signature::{ - create_cet_adaptor_signatures, create_final_cet_signature, create_final_cet_signatures, - sign_hash_ecdsa_with_key, sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, + create_cet_adaptor_signatures, create_final_cet_signatures, sign_hash_ecdsa_with_key, + sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, }; use crate::hd_wallet_storage::HDWalletStorage; @@ -222,28 +222,7 @@ impl Lib { ) } - /// Decrypt a signature on a CET when outcome signatures are available. - /// Return the decrypted signature. - pub fn create_final_cet_sig( - pubkey: &PublicKey, - num_digits: u8, - oracle_signatures: &Vec, - cet_value_wildcard: &str, - cet_sighash: &[u8; 32], - adaptor_signature: &EcdsaAdaptorSignature, - ) -> Result, String> { - create_final_cet_signature( - pubkey, - num_digits, - oracle_signatures, - cet_value_wildcard, - cet_sighash, - adaptor_signature, - ) - } - - /// Create the two signatures on a CET when outcome signatures are available. - /// Return the decrypted signature of the other, and my own signature (newly created). + /// Create signatures on a CET when outcome signatures are available pub fn create_final_cet_sigs( &self, signing_key_index: u32, From fc5b34f22ee3c2561234a096177a35ec83601305 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 21:01:27 +0200 Subject: [PATCH 5/8] Remove init_from_file --- .../ios/Classes/dlc_wallet_bridge.h | 2 +- .../flutter_plugin/lib/dlc_wallet.dart | 136 +++++++++--------- flutter_plugin/src/lib.rs | 36 ++--- 3 files changed, 87 insertions(+), 87 deletions(-) diff --git a/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h b/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h index bd1f68c..c12b234 100644 --- a/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h +++ b/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h @@ -13,7 +13,7 @@ char* create_cet_adaptor_sigs_c(uint8_t num_digits, uint32_t num_cets, const cha char* create_deterministic_nonce_c(const char* event_id, uint32_t index); char* get_xpub_c(void); char* get_address_c(uint32_t index); -char* init_from_file_c(const char* path, const char* password); +// char* init_from_file_c(const char* path, const char* password); void free_cstring(char* s); #ifdef __cplusplus diff --git a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart index 4ec107c..4986042 100644 --- a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart +++ b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart @@ -61,7 +61,7 @@ typedef FreeCStringCDart = void Function(Pointer s); class DlcWallet { static DynamicLibrary? _lib; static late final InitWithEntropyCDart _initWithEntropy; - static late final InitFromFileCDart _initFromFile; + // static late final InitFromFileCDart _initFromFile; static late final GetXpubCDart _getXpub; static late final GetPublicKeyCDart _getPublicKey; static late final GetAddressCDart _getAddress; @@ -100,9 +100,9 @@ class DlcWallet { _initWithEntropy = _lib! .lookup>('init_with_entropy_c') .asFunction(); - _initFromFile = _lib! - .lookup>('init_from_file_c') - .asFunction(); + // _initFromFile = _lib! + // .lookup>('init_from_file_c') + // .asFunction(); _getXpub = _lib!.lookup>('get_xpub_c').asFunction(); _getPublicKey = _lib! @@ -185,20 +185,20 @@ class DlcWallet { /// Initialize wallet from encrypted file /// Used for: Loading existing wallets - static Future initFromFile(String path, String password) async { - _loadLibrary(); + // static Future initFromFile(String path, String password) async { + // _loadLibrary(); - final pathPtr = path.toNativeUtf8(); - final passwordPtr = password.toNativeUtf8(); + // final pathPtr = path.toNativeUtf8(); + // final passwordPtr = password.toNativeUtf8(); - try { - final result = _initFromFile(pathPtr, passwordPtr); - return _convertCStringAndFree(result); - } finally { - malloc.free(pathPtr); - malloc.free(passwordPtr); - } - } + // try { + // final result = _initFromFile(pathPtr, passwordPtr); + // return _convertCStringAndFree(result); + // } finally { + // malloc.free(pathPtr); + // malloc.free(passwordPtr); + // } + // } /// Get extended public key /// Used for: Wallet identification, sharing public wallet info @@ -395,58 +395,58 @@ class DlcWallet { return addresses; } - /// Create a complete DLC setup - /// Used for: Setting up DLC contracts with all required components - static Future> createDlcSetup({ - required String eventId, - required int keyIndex, - required String digitStringTemplate, - required String oraclePublicKey, - required List intervalWildcards, - required List sighashes, - required int numDigits, - }) async { - // Create deterministic nonce - final nonce = await createDeterministicNonce(eventId, keyIndex); - - // Get signing public key - final signingPublicKey = await getPublicKey(keyIndex); - - // Create multiple nonces for multi-digit outcomes - final nonces = []; - for (int i = 0; i < numDigits; i++) { - final n = await createDeterministicNonce('$eventId-$i', keyIndex); - nonces.add(n['public']!); - } - - // Create CET adaptor signatures - final signatures = await createCetAdaptorSigs( - numDigits: numDigits, - numCets: intervalWildcards.length, - digitStringTemplate: digitStringTemplate, - oraclePublicKey: oraclePublicKey, - signingKeyIndex: keyIndex, - signingPublicKey: signingPublicKey, - nonces: nonces, - intervalWildcards: intervalWildcards, - sighashes: sighashes, - ); - - return { - 'eventId': eventId, - 'keyIndex': keyIndex, - 'signingPublicKey': signingPublicKey, - 'nonce': nonce, - 'nonces': nonces, - 'signatures': signatures, - 'setup': { - 'digitStringTemplate': digitStringTemplate, - 'oraclePublicKey': oraclePublicKey, - 'intervalWildcards': intervalWildcards, - 'sighashes': sighashes, - } - }; - } + // /// Create a complete DLC setup + // /// Used for: Setting up DLC contracts with all required components + // static Future> createDlcSetup({ + // required String eventId, + // required int keyIndex, + // required String digitStringTemplate, + // required String oraclePublicKey, + // required List intervalWildcards, + // required List sighashes, + // required int numDigits, + // }) async { + // // Create deterministic nonce + // final nonce = await createDeterministicNonce(eventId, keyIndex); + + // // Get signing public key + // final signingPublicKey = await getPublicKey(keyIndex); + + // // Create multiple nonces for multi-digit outcomes + // final nonces = []; + // for (int i = 0; i < numDigits; i++) { + // final n = await createDeterministicNonce('$eventId-$i', keyIndex); + // nonces.add(n['public']!); + // } + + // // Create CET adaptor signatures + // final signatures = await createCetAdaptorSigs( + // numDigits: numDigits, + // numCets: intervalWildcards.length, + // digitStringTemplate: digitStringTemplate, + // oraclePublicKey: oraclePublicKey, + // signingKeyIndex: keyIndex, + // signingPublicKey: signingPublicKey, + // nonces: nonces, + // intervalWildcards: intervalWildcards, + // sighashes: sighashes, + // ); + + // return { + // 'eventId': eventId, + // 'keyIndex': keyIndex, + // 'signingPublicKey': signingPublicKey, + // 'nonce': nonce, + // 'nonces': nonces, + // 'signatures': signatures, + // 'setup': { + // 'digitStringTemplate': digitStringTemplate, + // 'oraclePublicKey': oraclePublicKey, + // 'intervalWildcards': intervalWildcards, + // 'sighashes': sighashes, + // } + // }; + // } /// Sign a Bitcoin transaction hash /// Used for: Transaction signing in wallet applications diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index a5c4dd9..ff0f1a8 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -576,24 +576,24 @@ pub extern "C" fn get_address_c(index: u32) -> *mut c_char { } } -#[no_mangle] -pub extern "C" fn init_from_file_c(path: *const c_char, password: *const c_char) -> *mut c_char { - let path_str = unsafe { - CStr::from_ptr(path) - .to_str() - .unwrap_or("Error in path parameter") - }; - let password_str = unsafe { - CStr::from_ptr(password) - .to_str() - .unwrap_or("Error in password parameter") - }; - - match init_intern(path_str, password_str, false) { - Ok(xpub) => CString::new(xpub).unwrap().into_raw(), - Err(e) => error_as_cstr_prefix(e), - } -} +// #[no_mangle] +// pub extern "C" fn init_from_file_c(path: *const c_char, password: *const c_char) -> *mut c_char { +// let path_str = unsafe { +// CStr::from_ptr(path) +// .to_str() +// .unwrap_or("Error in path parameter") +// }; +// let password_str = unsafe { +// CStr::from_ptr(password) +// .to_str() +// .unwrap_or("Error in password parameter") +// }; + +// match init_intern(path_str, password_str, false) { +// Ok(xpub) => CString::new(xpub).unwrap().into_raw(), +// Err(e) => error_as_cstr_prefix(e), +// } +// } #[no_mangle] pub extern "C" fn free_cstring(s: *mut c_char) { From 51d71fa15c3a35abb8e87ae782f234e6bd419cbf Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 22:00:29 +0200 Subject: [PATCH 6/8] Switch to use cryptlib from external repo --- flutter_plugin/Cargo.toml | 11 +- flutter_plugin/src/adaptor_signature.rs | 476 ----------- flutter_plugin/src/hd_wallet_storage.rs | 155 ---- flutter_plugin/src/lib.rs | 788 +------------------ flutter_plugin/src/lib_struct.rs | 262 ------ flutter_plugin/src/network.rs | 24 - flutter_plugin/src/parse.rs | 39 - flutter_plugin/src/secret_entropy_storage.rs | 171 ---- flutter_plugin/src/test_lib.rs | 360 +-------- 9 files changed, 50 insertions(+), 2236 deletions(-) delete mode 100644 flutter_plugin/src/adaptor_signature.rs delete mode 100644 flutter_plugin/src/hd_wallet_storage.rs delete mode 100644 flutter_plugin/src/lib_struct.rs delete mode 100644 flutter_plugin/src/network.rs delete mode 100644 flutter_plugin/src/parse.rs delete mode 100644 flutter_plugin/src/secret_entropy_storage.rs diff --git a/flutter_plugin/Cargo.toml b/flutter_plugin/Cargo.toml index a7dc2cc..6533140 100644 --- a/flutter_plugin/Cargo.toml +++ b/flutter_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dlcplazacryptlib" -version = "0.2.0" +version = "1.0.0" edition = "2021" [lib] @@ -8,13 +8,10 @@ name = "dlcplazacryptlib" crate-type = ["cdylib", "staticlib"] [dependencies] -bip39 = "2.1.0" -bitcoin = "0.32.4" +dlccryptlib = "=1.0.0" +dlccryptlib-py = "=1.0.0" pyo3 = { version = "0.23.1", optional = true } -secp256k1-sys = "0.10.1" -secp256k1-zkp = "0.11.0" [features] -default = ["std"] -std = ["bitcoin/std", "secp256k1-zkp/rand-std"] +default = [] with-pyo3 = ["pyo3"] diff --git a/flutter_plugin/src/adaptor_signature.rs b/flutter_plugin/src/adaptor_signature.rs deleted file mode 100644 index b2770c5..0000000 --- a/flutter_plugin/src/adaptor_signature.rs +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use bitcoin::hashes::{sha256, sha256t_hash_newtype, Hash}; -use bitcoin::hex::DisplayHex; -use bitcoin::key::{Keypair, Secp256k1}; -use bitcoin::secp256k1::ecdsa::Signature; -use bitcoin::secp256k1::{Message, PublicKey, SecretKey, XOnlyPublicKey}; -use bitcoin::EcdsaSighashType; -use core::ptr; -use secp256k1_sys::{ - types::{c_int, c_uchar, c_void, size_t}, - CPtr, SchnorrSigExtraParams, -}; -use secp256k1_zkp::schnorr::Signature as SchnorrSignature; -use secp256k1_zkp::{EcdsaAdaptorSignature, Scalar, Signing, Verification}; - -extern "C" fn constant_nonce_fn( - nonce32: *mut c_uchar, - _msg32: *const c_uchar, - _msg_len: size_t, - _key32: *const c_uchar, - _xonly_pk32: *const c_uchar, - _algo16: *const c_uchar, - _algo_len: size_t, - data: *mut c_void, -) -> c_int { - unsafe { - ptr::copy_nonoverlapping(data as *const c_uchar, nonce32, 32); - } - 1 -} - -/// Create a Schnorr signature using the provided nonce -pub(crate) fn schnorrsig_sign_with_nonce( - secp: &Secp256k1, - msg: &Message, - keypair: &Keypair, - nonce: &[u8; 32], -) -> SchnorrSignature { - unsafe { - let mut sig = [0u8; secp256k1_zkp::constants::SCHNORR_SIGNATURE_SIZE]; - let extra_params = - SchnorrSigExtraParams::new(Some(constant_nonce_fn), nonce.as_c_ptr() as *const c_void); - assert_eq!( - 1, - secp256k1_sys::secp256k1_schnorrsig_sign_custom( - secp.ctx().as_ref(), - sig.as_mut_c_ptr(), - msg.as_c_ptr(), - 32_usize, - keypair.as_c_ptr(), - &extra_params, - ) - ); - - SchnorrSignature::from_slice(&sig).unwrap() - } -} - -pub(crate) fn sign_hash_ecdsa_with_key( - secp: &Secp256k1, - hash: &[u8; 32], - signing_key: &SecretKey, -) -> Result, String> { - let m = Message::from_digest_slice(hash).unwrap(); - let mut sig = secp.sign_ecdsa(&m, &signing_key).serialize_der().to_vec(); - sig.push(EcdsaSighashType::All as u8); - Ok(sig) -} - -const BIP340_MIDSTATE: [u8; 32] = [ - 0x9c, 0xec, 0xba, 0x11, 0x23, 0x92, 0x53, 0x81, 0x11, 0x67, 0x91, 0x12, 0xd1, 0x62, 0x7e, 0x0f, - 0x97, 0xc8, 0x75, 0x50, 0x00, 0x3c, 0xc7, 0x65, 0x90, 0xf6, 0x11, 0x64, 0x33, 0xe9, 0xb6, 0x6a, -]; - -sha256t_hash_newtype! { - /// BIP340 Hash Tag - pub struct BIP340HashTag = raw(BIP340_MIDSTATE, 64); - - /// BIP340 Hash - #[hash_newtype(backward)] - pub struct BIP340Hash(_); -} - -pub(crate) fn create_schnorr_hash( - msg: &Message, - nonce: &XOnlyPublicKey, - pubkey: &XOnlyPublicKey, -) -> [u8; 32] { - let mut buf = Vec::::new(); - buf.extend(nonce.serialize()); - buf.extend(pubkey.serialize()); - buf.extend(msg.as_ref().to_vec()); - BIP340Hash::hash(&buf).to_byte_array() -} - -pub(crate) fn schnorr_pubkey_to_pubkey( - schnorr_pubkey: &XOnlyPublicKey, -) -> Result { - let mut buf = Vec::::with_capacity(33); - buf.push(0x02); - buf.extend(schnorr_pubkey.serialize()); - Ok(PublicKey::from_slice(&buf).map_err(|e| e.to_string())?) -} - -/// Sign a message using Schnorr, using a key -pub(crate) fn sign_schnorr_with_nonce_sec( - secp: &Secp256k1, - keypair: &Keypair, - msg: &str, - nonce_sec: &[u8; 32], -) -> Result { - let msg_hash = sha256::Hash::hash(msg.as_bytes()).to_byte_array(); - let msg_msg = Message::from_digest(msg_hash); - let sig = schnorrsig_sign_with_nonce(&secp, &msg_msg, &keypair, &nonce_sec); - Ok(sig) -} - -/// Compute a signature point for the given public key, nonce and message. -fn schnorrsig_compute_sig_point( - secp: &Secp256k1, - pubkey: &XOnlyPublicKey, - nonce: &XOnlyPublicKey, - message: &Message, -) -> Result { - let hash = create_schnorr_hash(message, nonce, pubkey); - let pk = schnorr_pubkey_to_pubkey(pubkey)?; - let scalar = Scalar::from_be_bytes(hash).unwrap(); - let tweaked = pk - .mul_tweak(&secp, &scalar) - .map_err(|e| e.to_string()) - .map_err(|e| e.to_string())?; - let npk = schnorr_pubkey_to_pubkey(nonce)?; - Ok(npk - .combine(&tweaked) - .map_err(|e| e.to_string()) - .map_err(|e| e.to_string())?) -} - -/// Decompose a bip340 signature into a nonce and a secret key (as byte array) -fn schnorrsig_decompose(signature: &SchnorrSignature) -> Result<(XOnlyPublicKey, &[u8]), String> { - let bytes = signature.as_ref(); - Ok(( - XOnlyPublicKey::from_slice(&bytes[0..32]).map_err(|e| e.to_string())?, - &bytes[32..64], - )) -} - -fn message_hash(msg: &str) -> Result { - Message::from_digest_slice(sha256::Hash::hash(msg.as_bytes()).as_byte_array()) - .map_err(|e| e.to_string()) -} - -pub(crate) fn create_digit_adaptor_sig_point( - secp: &Secp256k1, - oracle_pubkey: &PublicKey, - digit_index: u8, - digit_outcome: u8, - string_template: &str, - nonce: &PublicKey, -) -> Result { - // print("Digit", digit_index, digit_outcome) - let string_msg = string_template - .to_string() - .replace("{digit_index}", &digit_index.to_string()) - .replace("{digit_outcome}", &digit_outcome.to_string()); - // println!( - // "Digit outcome: idx {} outcome {} string {} nonce {}", - // digit_index, - // digit_outcome, - // string_msg, - // nonce.to_string() - // ); - let msg_hash = message_hash(&string_msg)?; - // print(msg_hash) - let sig_point = schnorrsig_compute_sig_point( - &secp, - &oracle_pubkey.x_only_public_key().0, - &nonce.x_only_public_key().0, - &msg_hash, - )?; - // print(" sig point", sig_point) - Ok(sig_point) -} - -pub(crate) fn combine_pubkeys_wrapper(keys: &[&PublicKey]) -> Result { - PublicKey::combine_keys(keys).map_err(|err| err.to_string()) -} - -pub(crate) fn create_cet_adaptor_signatures( - secp: &Secp256k1, - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey: &PublicKey, - signing_keypair: &Keypair, - nonces: &Vec, - interval_wildcards: &Vec, - sighashes: &Vec<[u8; 32]>, -) -> Result, String> { - if interval_wildcards.len() != num_cets as usize { - return Err(format!( - "Invalid number of wildcards {} {}", - interval_wildcards.len(), - num_cets - )); - } - if sighashes.len() != num_cets as usize { - return Err(format!( - "Invalid number of sighashes {} {}", - sighashes.len(), - num_cets - )); - } - - // Create adaptor signature points for each digit and outcome (count: digits x 10) - let mut sig_points: Vec> = Vec::new(); - for d in 0..num_digits { - let mut sig_points_inner: Vec = Vec::new(); - for v in 0..10 { - let sig_point = create_digit_adaptor_sig_point( - &secp, - &oracle_pubkey, - d, - v, - &digit_string_template, - &nonces[d as usize], - )?; - // println!("sig point {}", sig_point.to_string()); - sig_points_inner.push(sig_point); - } - sig_points.push(sig_points_inner); - } - - // Loop through CETs - debug_assert!(interval_wildcards.len() == num_cets as usize); - debug_assert!(sighashes.len() == num_cets as usize); - let mut sigs = Vec::::with_capacity(num_cets as usize); - for ceti in 0..num_cets { - let wildcard = &interval_wildcards[ceti as usize].as_bytes(); - let mut keys = Vec::with_capacity(num_digits as usize); - for d in 0..num_digits { - let ch = wildcard[d as usize]; - if ch >= 48 && ch <= 57 { - let digit_val = ch.saturating_sub(48); - keys.push(&sig_points[d as usize][digit_val as usize]); - } - } - // println!("keys len {} {}", interval_wildcards[ceti as usize], keys.len()); - - let aggr_sig_point = combine_pubkeys_wrapper(keys.as_slice())?; - // println!("aggr_sig_point {}", aggr_sig_point.to_string()); - - let cet_tx_sighash = &sighashes[ceti as usize]; - - // Creates adaptor signature - let adaptor_signature = { - #[cfg(feature = "std")] - { - EcdsaAdaptorSignature::encrypt( - &secp, - &Message::from_digest(*cet_tx_sighash), - &signing_keypair.secret_key(), - &aggr_sig_point, - ) - } - - #[cfg(not(feature = "std"))] - { - return Err("EcdsaAdaptorSignature::encrypt requires the 'std' feature".to_string()); - } - }; - - sigs.push(adaptor_signature); - } - - Ok(sigs) -} - -pub(crate) fn verify_cet_adaptor_signatures( - secp: &Secp256k1, - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey: &PublicKey, - signing_pubkey: &PublicKey, - nonces: &Vec, - interval_wildcards: &Vec, - sighashes: &Vec<[u8; 32]>, - signatures: &Vec, -) -> Result<(), String> { - if interval_wildcards.len() != num_cets as usize { - return Err(format!( - "Invalid number of wildcards {} {}", - interval_wildcards.len(), - num_cets - )); - } - if sighashes.len() != num_cets as usize { - return Err(format!( - "Invalid number of sighashes {} {}", - sighashes.len(), - num_cets - )); - } - if signatures.len() != num_cets as usize { - return Err(format!( - "Invalid number of signatures {} {}", - signatures.len(), - num_cets - )); - } - - // Create adaptor signature points for each digit and outcome (count: digits x 10) - let mut sig_points: Vec> = Vec::new(); - for d in 0..num_digits { - let mut sig_points_inner: Vec = Vec::new(); - for v in 0..10 { - let sig_point = create_digit_adaptor_sig_point( - &secp, - &oracle_pubkey, - d, - v, - &digit_string_template, - &nonces[d as usize], - )?; - // println!("sig point {}", sig_point.to_string()); - sig_points_inner.push(sig_point); - } - sig_points.push(sig_points_inner); - } - - // Loop through CETs - debug_assert!(interval_wildcards.len() == num_cets as usize); - debug_assert!(sighashes.len() == num_cets as usize); - debug_assert!(signatures.len() == num_cets as usize); - for ceti in 0..num_cets { - let wildcard = &interval_wildcards[ceti as usize].as_bytes(); - let mut keys = Vec::with_capacity(num_digits as usize); - for d in 0..num_digits { - let ch = wildcard[d as usize]; - if ch >= 48 && ch <= 57 { - let digit_val = ch.saturating_sub(48); - keys.push(&sig_points[d as usize][digit_val as usize]); - } - } - // println!("keys len {} {}", interval_wildcards[ceti as usize], keys.len()); - - let aggr_sig_point = combine_pubkeys_wrapper(keys.as_slice())?; - // println!("aggr_sig_point {}", aggr_sig_point.to_string()); - - let cet_tx_sighash = &sighashes[ceti as usize]; - - // Verify adaptor signature - #[cfg(feature = "std")] - signatures[ceti as usize] - .verify( - &secp, - &Message::from_digest(*cet_tx_sighash), - &signing_pubkey, - &aggr_sig_point, - ) - .map_err(|err| { - format!( - "CET adaptor signature verification failed, cet idx {}, err {:?}", - ceti, err - ) - })?; - } - // All is ok - Ok(()) -} - -fn aggregate_secret_values(secrets: &Vec) -> Result { - if secrets.len() == 0 { - return Err(format!("At least one key is required!")); - } - let secret = secrets[0]; - let result = secrets.iter().skip(1).fold(secret, |accum, s| { - accum.add_tweak(&Scalar::from(*s)).unwrap() - }); - Ok(result) -} - -pub(crate) fn verify_ecdsa_signature( - msg: &[u8], - sig: &[u8], - pubkey: &PublicKey, - skip_last_sig_byte: bool, -) -> Result { - let m = Message::from_digest_slice(&msg).map_err(|e| e.to_string())?; - let sig_adj = if !skip_last_sig_byte { - &sig - } else { - &sig[0..sig.len() - 1] - }; - let s = Signature::from_der(&sig_adj).map_err(|e| e.to_string())?; - let ctx = Secp256k1::new(); - match ctx.verify_ecdsa(&m, &s, &pubkey) { - Ok(_) => Ok(true), - Err(e) => Err(format!( - "Signature verification failed! err {} msg {} sig {} pk {}", - e, - &msg.as_hex(), - &sig.as_hex(), - &pubkey.to_string(), - )), - } -} - -/// Create signatures on a CET when outcome signatures are available -pub fn create_final_cet_signatures( - secp: &Secp256k1, - signing_keypair: &Keypair, - other_pubkey: &PublicKey, - num_digits: u8, - oracle_signatures: &Vec, - cet_value_wildcard: &str, - cet_sighash: &[u8; 32], - other_adaptor_signature: &EcdsaAdaptorSignature, -) -> Result<(Vec, Vec), String> { - let signing_key = &signing_keypair.secret_key(); - - // Decompose oracle signatures - if oracle_signatures.len() != num_digits as usize { - return Err(format!( - "Wrong number of oracle signatures {} {}", - oracle_signatures.len(), - num_digits - )); - } - debug_assert_eq!(oracle_signatures.len(), num_digits as usize); - let wildcard = cet_value_wildcard.as_bytes(); - let mut adaptor_secret_vec = Vec::new(); - for d in 0..num_digits { - let ch = wildcard[d as usize]; - if ch >= 48 && ch <= 57 { - let (_nonce, secret_value) = schnorrsig_decompose(&oracle_signatures[d as usize]) - .map_err(|e| format!("Error decomposing Schnorr signature {}", e.to_string()))?; - let adaptor_secret = SecretKey::from_slice(secret_value).map_err(|e| { - format!( - "Error retrieving adaptor secret from signature {}", - e.to_string() - ) - })?; - adaptor_secret_vec.push(adaptor_secret); - } - } - let adaptor_secret_aggregate = aggregate_secret_values(&adaptor_secret_vec)?; - - // Adaptor signature from the OTHER - let mut adapted_sig = other_adaptor_signature - .decrypt(&adaptor_secret_aggregate) - .map_err(|e| format!("Error in adaptor signature decryption {}", e.to_string()))? - .serialize_der() - .to_vec(); - adapted_sig.push(EcdsaSighashType::All as u8); - // verify signature - let _res = - verify_ecdsa_signature(cet_sighash, &adapted_sig, &other_pubkey, true).map_err(|e| { - format!( - "Adaptor-derived signature verification failed {}", - e.to_string() - ) - })?; - - // Now sign the CET on my own part - let my_sig = sign_hash_ecdsa_with_key(&secp, cet_sighash, &signing_key)?; - // verify sig - let _res = verify_ecdsa_signature(cet_sighash, &my_sig, &signing_keypair.public_key(), true) - .map_err(|e| format!("Self signature verification failed {}", e.to_string()))?; - - Ok((adapted_sig, my_sig)) -} diff --git a/flutter_plugin/src/hd_wallet_storage.rs b/flutter_plugin/src/hd_wallet_storage.rs deleted file mode 100644 index ae66d9a..0000000 --- a/flutter_plugin/src/hd_wallet_storage.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use crate::secret_entropy_storage::SecretEntropyStore; - -use bip39::Mnemonic; -use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv, Xpub}; -use bitcoin::key::{Keypair, Secp256k1}; -use bitcoin::secp256k1::{All, PublicKey}; -use bitcoin::{Address, Network}; -use std::str::FromStr; - -/// Store a HD wallet. -/// Built on top of SecretEntropyStore, but adds HD wallet functionality -pub(crate) struct HDWalletStorage { - entropy_store: SecretEntropyStore, - wallet: Option, - secp: Secp256k1, -} - -pub(crate) struct HDWalletInfo { - // The level-3 account XPRIV - xpriv: Xpriv, - // The level-3 account XPUB - pub xpub: Xpub, -} - -impl HDWalletStorage { - pub(crate) fn new_from_secret_file( - path_for_secret_file: &str, - encryption_password: &str, - ) -> Result { - let entropy_store = - SecretEntropyStore::new_from_secret_file(path_for_secret_file, encryption_password)?; - let mut instance = Self { - entropy_store, - wallet: None, - secp: Secp256k1::new(), - }; - // check derivation, cache it - let wallet = instance.get_hdwallet_from_secret()?; - instance.wallet = Some(wallet); - Ok(instance) - } - - // #[cfg(test)] - pub(crate) fn new_with_entropy(entropy: &Vec, network: &str) -> Result { - let entropy_store = SecretEntropyStore::new_with_entropy(entropy, network)?; - // check derivation, don't cache secrets, only the xpub - let mut instance = Self { - entropy_store, - wallet: None, - secp: Secp256k1::new(), - }; - // check derivation, cache it - let wallet = instance.get_hdwallet_from_secret()?; - instance.wallet = Some(wallet); - Ok(instance) - } - - /// Return the XPUB - pub(crate) fn get_xpub(&self) -> Result { - let wallet = self.get_cached_hdwallet_info()?; - Ok(wallet.xpub) - } - - pub(crate) fn network(&self) -> Network { - self.entropy_store.network() - } - - fn get_hdwallet_from_secret(&self) -> Result { - let entropy = self.entropy_store.get_secret_entropy(); - let mnemo = Mnemonic::from_entropy(entropy) - .map_err(|e| format!("Could not process entropy {}", e.to_string()))?; - let seed = mnemo.to_seed_normalized(""); - let xpriv = Xpriv::new_master(self.network(), &seed).expect("Creating XPriv"); - let derivation = self.default_account_derivation_path(); - let derivation_path_3 = - DerivationPath::from_str(&derivation).expect("Creating DerivationPath"); - let xpriv_level_3 = xpriv - .derive_priv(&self.secp, &derivation_path_3) - .expect("Derive level3 xpriv"); - let xpub_level_3 = Xpub::from_priv(&self.secp, &xpriv_level_3); - - Ok(HDWalletInfo { - xpriv: xpriv_level_3, - xpub: xpub_level_3, - }) - } - - fn get_cached_hdwallet_info(&self) -> Result<&HDWalletInfo, String> { - if let Some(wallet) = &self.wallet { - Ok(&wallet) - } else { - Err("HDWalletStorage not initialized!".to_string()) - } - } - - fn default_account_derivation_path(&self) -> String { - match self.network() { - Network::Signet => "m/84'/1'/0'".to_string(), - // Bitcoin mainnet and fallback - _ => "m/84'/0'/0'".to_string(), - } - } - - /// Return a child keypair - pub(crate) fn get_child_keypair(&self, index: u32) -> Result { - let wallet = self.get_cached_hdwallet_info()?; - // derive - let index_4 = ChildNumber::from_normal_idx(0).unwrap(); - let index_5 = ChildNumber::from_normal_idx(index).unwrap(); - let xpriv_5 = wallet - .xpriv - .derive_priv(&self.secp, &vec![index_4, index_5]) - .expect("Derivation error"); - let keypair = xpriv_5.to_keypair(&self.secp); - Ok(keypair) - } - - /// Return a child public key - pub(crate) fn get_child_public_key(&self, index: u32) -> Result { - let keypair = self.get_child_keypair(index)?; - Ok(keypair.public_key()) - } - - /// Return a child address - pub(crate) fn get_address(&self, index: u32) -> Result { - let pubkey = self.get_child_public_key(index)?; - let ck = bitcoin::CompressedPublicKey(pubkey); - let address = Address::p2wpkh(&ck, self.network()); - Ok(address) - } - - pub(crate) fn verify_child_public_key_intern( - &self, - index: u32, - pubkey: &PublicKey, - print_entity: &str, - ) -> Result { - let keypair = self.get_child_keypair(index)?; - // verify pubkey - if &keypair.public_key() != pubkey { - return Err(format!( - "{} mismatch, index {}, {} vs. {}", - print_entity, - index, - pubkey, - keypair.public_key() - )); - } - Ok(true) - } -} diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index ff0f1a8..522c8a8 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -2,567 +2,27 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -mod adaptor_signature; -mod hd_wallet_storage; -mod lib_struct; -mod network; -mod parse; -mod secret_entropy_storage; #[cfg(test)] mod test_lib; -use crate::adaptor_signature::combine_pubkeys_wrapper; -use crate::lib_struct::{global_lib, Lib}; -use crate::parse::{ - hash_from_hex, keypair_from_sec_key_hex, pubkey_from_hex, schnorr_sig_from_hex, -}; -use crate::secret_entropy_storage::parse_entropy_hex; +use dlccryptlib::{get_address, get_xpub}; +#[cfg(feature = "with-pyo3")] +use dlccryptlib_py; -use bitcoin::hex::{DisplayHex, FromHex}; -use bitcoin::secp256k1::{PublicKey, SecretKey}; -use secp256k1_zkp::schnorr::Signature as SchnorrSignature; -use secp256k1_zkp::EcdsaAdaptorSignature; // Import missing types -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::os::raw::c_char; -use std::str::FromStr; // Conditional compilation to exclude PyO3-related code for Android #[cfg(feature = "with-pyo3")] -use pyo3::exceptions::PyException; -#[cfg(feature = "with-pyo3")] use pyo3::prelude::*; #[cfg(feature = "with-pyo3")] use pyo3::wrap_pyfunction; -/// Initialize the library, load secret from encrypted file. Return the XPUB. -fn init_intern( - path_for_secret_file: &str, - encryption_password: &str, - allow_reinit: bool, -) -> Result { - global_lib().write().unwrap().init_from_secret_file( - path_for_secret_file, - encryption_password, - allow_reinit, - )?; - let xpub = global_lib().read().unwrap().get_xpub()?; - Ok(xpub.to_string()) -} - -/// Initialize the library, provide the secret as parameter. Return the XPUB. -// #[cfg(test)] -fn init_with_entropy_intern(entropy: &str, network: &str) -> Result { - let entropy_bin = parse_entropy_hex(entropy)?; - global_lib() - .write() - .unwrap() - .init_with_entropy(&entropy_bin, network)?; - let xpub = global_lib().read().unwrap().get_xpub()?; - Ok(xpub.to_string()) -} - -fn get_xpub_intern() -> Result { - let xpub = global_lib().read().unwrap().get_xpub()?; - Ok(xpub.to_string()) -} - -fn get_public_key_intern(index: u32) -> Result { - let pubkey = global_lib().read().unwrap().get_child_public_key(index)?; - Ok(pubkey.to_string()) -} - -fn get_address_intern(index: u32) -> Result { - let address = global_lib().read().unwrap().get_address(index)?; - Ok(address.to_string()) -} - -fn verify_public_key_intern(index: u32, pubkey_str: &str) -> Result { - let pubkey = - pubkey_from_hex(pubkey_str).map_err(|e| format!("Failed to parse pubkey {}", e))?; - let verify_result = global_lib() - .read() - .unwrap() - .verify_child_public_key(index, &pubkey)?; - Ok(verify_result) -} - -fn sign_hash_ecdsa_intern( - hash_str: &str, - index: u32, - signer_pubkey_str: &str, -) -> Result { - let hash = <[u8; 32]>::from_hex(hash_str) - .map_err(|e| format!("Failed to parse hash hex, {}", e.to_string()))?; - let signer_pubkey = pubkey_from_hex(signer_pubkey_str) - .map_err(|e| format!("Failed to parse signer pubkey {}", e))?; - let sig = global_lib() - .read() - .unwrap() - .sign_hash_ecdsa(&hash, index, &signer_pubkey)?; - Ok(sig.to_lower_hex_string()) -} - -fn create_deterministic_nonce_intern( - event_id: &str, - index: u32, -) -> Result<(String, String), String> { - let (sk, pk) = global_lib() - .read() - .unwrap() - .create_deterministic_nonce(event_id, index)?; - Ok((sk, pk.to_string())) -} - -// Schnorr signing with nonce -fn sign_schnorr_with_nonce_intern( - msg: &str, - nonce_sec_hex: &str, - index: u32, -) -> Result { - let nonce_sec_bin = <[u8; 32]>::from_hex(&nonce_sec_hex) - .map_err(|e| format!("Error in nonce hex string {}", e))?; - let sig = global_lib() - .read() - .unwrap() - .sign_schnorr_with_nonce(msg, &nonce_sec_bin, index)?; - Ok(sig.to_string()) -} - -pub fn combine_pubkeys_intern(keys_hex: &str) -> Result { - let keys_split: Vec<_> = keys_hex.split(" ").collect(); - let mut keys = Vec::::with_capacity(keys_split.len()); - for i in 0..keys_split.len() { - let key_hex = keys_split[i].trim(); - if key_hex.len() > 0 { - let key = pubkey_from_hex(&keys_split[i]) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - keys.push(key); - } - } - let combined_key = combine_pubkeys_wrapper(keys.iter().collect::>().as_slice())?; - Ok(combined_key.to_string()) -} - -pub fn combine_seckeys_intern(keys_hex: &str) -> Result { - let keys_split: Vec<_> = keys_hex.split(" ").collect(); - let mut keys = Vec::::with_capacity(keys_split.len()); - for i in 0..keys_split.len() { - let key_hex = keys_split[i].trim(); - if key_hex.len() > 0 { - let keypair = keypair_from_sec_key_hex(&key_hex) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - keys.push(keypair.secret_key()); - } - } - let combined_key = Lib::combine_seckeys(&keys)?; - Ok(combined_key.display_secret().to_string()) -} - -fn create_cet_adaptor_sigs_intern( - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey_str: &str, - signing_key_index: u32, - signing_pubkey_str: &str, - nonces: &str, - interval_wildcards: &str, - sighashes: &str, -) -> Result { - let nonces_split: Vec<_> = nonces.split(" ").collect(); - let mut nonces = Vec::::with_capacity(nonces_split.len()); - for i in 0..nonces_split.len() { - let key_hex = nonces_split[i].trim(); - if key_hex.len() > 0 { - let pubkey = pubkey_from_hex(&key_hex) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - nonces.push(pubkey); - } - } - if nonces.len() != num_digits as usize { - return Err(format!( - "Wrong number of nonces {} {}", - nonces.len(), - num_digits - )); - } - - let wcs_split: Vec<_> = interval_wildcards.split(" ").collect(); - let mut wcs = Vec::::with_capacity(wcs_split.len()); - for i in 0..wcs_split.len() { - let wc = wcs_split[i].trim(); - if wc.len() > 0 { - wcs.push(wc.to_owned()); - } - } - if wcs.len() != num_cets as usize { - return Err(format!( - "Wrong number of wildcards {} {}", - wcs.len(), - num_cets - )); - } - - let shs_split: Vec<_> = sighashes.split(" ").collect(); - let mut shs = Vec::<[u8; 32]>::with_capacity(shs_split.len()); - for i in 0..shs_split.len() { - let sh = shs_split[i].trim(); - if sh.len() > 0 { - let hash = - hash_from_hex(&sh).map_err(|e| format!("Failed to parse element {} {}", i, e))?; - shs.push(hash); - } - } - if shs.len() != num_cets as usize { - return Err(format!( - "Wrong number of sighashes {} {}", - shs.len(), - num_cets - )); - } - - let oracle_pubkey = pubkey_from_hex(oracle_pubkey_str) - .map_err(|e| format!("Failed to parse oracle pubkey {}", e))?; - let signing_pubkey = pubkey_from_hex(signing_pubkey_str) - .map_err(|e| format!("Failed to parse signing pubkey {}", e))?; - - let sigs = global_lib().read().unwrap().create_cet_adaptor_sigs( - num_digits, - num_cets, - digit_string_template, - &oracle_pubkey, - signing_key_index, - &signing_pubkey, - &nonces, - &wcs, - &shs, - )?; - - let mut sigs_str = String::new(); - for s in sigs.iter() { - sigs_str += &s.as_ref().to_lower_hex_string(); - sigs_str += " "; - } - - Ok(sigs_str) -} - -fn verify_cet_adaptor_sigs_intern( - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey_str: &str, - signing_pubkey_str: &str, - nonces: &str, - interval_wildcards: &str, - sighashes: &str, - signatures: &str, -) -> Result { - let nonces_split: Vec<_> = nonces.split(" ").collect(); - let mut nonces = Vec::::with_capacity(nonces_split.len()); - for i in 0..nonces_split.len() { - let key_hex = nonces_split[i].trim(); - if key_hex.len() > 0 { - let pubkey = pubkey_from_hex(&key_hex) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - nonces.push(pubkey); - } - } - if nonces.len() != num_digits as usize { - return Err(format!( - "Wrong number of nonces {} {}", - nonces.len(), - num_digits - )); - } - - let wcs_split: Vec<_> = interval_wildcards.split(" ").collect(); - let mut wcs = Vec::::with_capacity(wcs_split.len()); - for i in 0..wcs_split.len() { - let wc = wcs_split[i].trim(); - if wc.len() > 0 { - wcs.push(wc.to_owned()); - } - } - if wcs.len() != num_cets as usize { - return Err(format!( - "Wrong number of wildcards {} {}", - wcs.len(), - num_cets - )); - } - - let shs_split: Vec<_> = sighashes.split(" ").collect(); - let mut shs = Vec::<[u8; 32]>::with_capacity(shs_split.len()); - for i in 0..shs_split.len() { - let sh = shs_split[i].trim(); - if sh.len() > 0 { - let hash = - hash_from_hex(&sh).map_err(|e| format!("Failed to parse element {} {}", i, e))?; - shs.push(hash); - } - } - if shs.len() != num_cets as usize { - return Err(format!( - "Wrong number of sighashes {} {}", - shs.len(), - num_cets - )); - } - - let sigs_split: Vec<_> = signatures.split(" ").collect(); - let mut sigs = Vec::::with_capacity(sigs_split.len()); - for i in 0..sigs_split.len() { - let sig = sigs_split[i].trim(); - if sig.len() > 0 { - let s = EcdsaAdaptorSignature::from_str(sig) - .map_err(|e| format!("Could not parse ECDSA adaptor signature {} {:?}", sig, e))?; - sigs.push(s); - } - } - if sigs.len() != num_cets as usize { - return Err(format!( - "Wrong number of signatures {} {}", - sigs.len(), - num_cets - )); - } - - let oracle_pubkey = pubkey_from_hex(oracle_pubkey_str) - .map_err(|e| format!("Failed to parse oracle pubkey {}", e))?; - let signing_pubkey = pubkey_from_hex(signing_pubkey_str) - .map_err(|e| format!("Failed to parse signing pubkey {}", e))?; - - let res = global_lib().read().unwrap().verify_cet_adaptor_sigs( - num_digits, - num_cets, - digit_string_template, - &oracle_pubkey, - &signing_pubkey, - &nonces, - &wcs, - &shs, - &sigs, - ); - Ok(res.is_ok()) -} - -pub fn create_final_cet_sigs_intern( - signing_key_index: u32, - signing_pubkey_str: &str, - other_pubkey_str: &str, - num_digits: u8, - oracle_signatures_str: &str, - cet_value_wildcard: &str, - cet_sighash_str: &str, - other_adaptor_signature_str: &str, -) -> Result { - let signing_pubkey = pubkey_from_hex(signing_pubkey_str) - .map_err(|e| format!("Failed to parse signing pubkey {}", e))?; - let other_pubkey = pubkey_from_hex(other_pubkey_str) - .map_err(|e| format!("Failed to parse other pubkey {}", e))?; - - let sigs_split: Vec<_> = oracle_signatures_str.split(" ").collect(); - let mut sigs = Vec::::with_capacity(sigs_split.len()); - for i in 0..sigs_split.len() { - let sig_hex = sigs_split[i].trim(); - if sig_hex.len() > 0 { - let sig = schnorr_sig_from_hex(&sig_hex) - .map_err(|e| format!("Failed to parse element {} {}", i, e))?; - sigs.push(sig); - } - } - if sigs.len() != num_digits as usize { - return Err(format!( - "Wrong number of signatures {} {}", - sigs.len(), - num_digits - )); - } - - let cet_sighash = - hash_from_hex(cet_sighash_str).map_err(|e| format!("Failed to parse sighash {}", e))?; - - let other_adaptor_signature_bin = Vec::from_hex(other_adaptor_signature_str) - .map_err(|e| format!("Failed to parse other adaptor sig {}", e))?; - let other_adaptor_signature = - EcdsaAdaptorSignature::from_slice(&other_adaptor_signature_bin) - .map_err(|e| format!("Failed to parse other adaptor sig {}", e))?; - let (sig1, sig2) = global_lib().read().unwrap().create_final_cet_sigs( - signing_key_index, - &signing_pubkey, - &other_pubkey, - num_digits, - &sigs, - cet_value_wildcard, - &cet_sighash, - &other_adaptor_signature, - )?; - - let sigs = format!( - "{} {}", - sig1.to_lower_hex_string(), - sig2.to_lower_hex_string() - ); - Ok(sigs) -} - // ##### Facade functions for C-style-interface invocations - -/// Initialize the library, provide the secret as parameter. Return the XPUB. -#[no_mangle] -pub extern "C" fn init_with_entropy_c( - entropy: *const c_char, - network: *const c_char, -) -> *mut c_char { - // Convert input parameter from raw pointer to Rust string - let entropy_str = unsafe { - CStr::from_ptr(entropy) - .to_str() - .unwrap_or("Error in entropy parameter") - }; - let network_str = unsafe { - CStr::from_ptr(network) - .to_str() - .unwrap_or("Error in network parameter") - }; - - match init_with_entropy_intern(entropy_str, network_str) { - Ok(xpub) => { - // Return as a C string - CString::new(xpub).unwrap().into_raw() - } - Err(e) => error_as_cstr_prefix(e), - } -} - -/// Return a child public key (specified by its index). -#[no_mangle] -pub extern "C" fn get_public_key_c(index: u32) -> *mut c_char { - match get_public_key_intern(index) { - Ok(pubkey) => { - // Return as a C string - CString::new(pubkey).unwrap().into_raw() - } - Err(e) => error_as_cstr_prefix(e), - } -} - -/// Sign a hash with a child private key (specified by its index). -#[no_mangle] -pub extern "C" fn sign_hash_ecdsa_c( - hash: *const c_char, - signer_index: u32, - signer_pubkey: *const c_char, -) -> *mut c_char { - // Convert input parameter from raw pointer to Rust string - let hash_str = unsafe { - CStr::from_ptr(hash) - .to_str() - .unwrap_or("Error in hash parameter") - }; - let signer_pubkey_str = unsafe { - CStr::from_ptr(signer_pubkey) - .to_str() - .unwrap_or("Error in signer_pubkey parameter") - }; - - match sign_hash_ecdsa_intern(hash_str, signer_index, signer_pubkey_str) { - Ok(sig) => { - // Return as a C string - CString::new(sig).unwrap().into_raw() - } - Err(e) => error_as_cstr_prefix(e), - } -} - -/// Create adaptor signatures for a number of CETs -#[no_mangle] -pub extern "C" fn create_cet_adaptor_sigs_c( - num_digits: u8, - num_cets: u32, - digit_string_template: *const c_char, - oracle_pubkey: *const c_char, - signing_key_index: u32, - signing_pubkey: *const c_char, - nonces: *const c_char, - interval_wildcards: *const c_char, - sighashes: *const c_char, -) -> *mut c_char { - // Convert input parameter from raw pointer to Rust string - let digit_string_template_str = unsafe { - CStr::from_ptr(digit_string_template) - .to_str() - .unwrap_or("Error in digit_string_template parameter") - }; - let oracle_pubkey_str = unsafe { - CStr::from_ptr(oracle_pubkey) - .to_str() - .unwrap_or("Error in oracle_pubkey parameter") - }; - let signing_pubkey_str = unsafe { - CStr::from_ptr(signing_pubkey) - .to_str() - .unwrap_or("Error in signing_pubkey parameter") - }; - let nonces_str = unsafe { - CStr::from_ptr(nonces) - .to_str() - .unwrap_or("Error in nonces parameter") - }; - let interval_wildcards_str = unsafe { - CStr::from_ptr(interval_wildcards) - .to_str() - .unwrap_or("Error in interval_wildcards parameter") - }; - let sighashes_str = unsafe { - CStr::from_ptr(sighashes) - .to_str() - .unwrap_or("Error in sighashes parameter") - }; - - match create_cet_adaptor_sigs_intern( - num_digits, - num_cets as u64, - digit_string_template_str, - oracle_pubkey_str, - signing_key_index, - signing_pubkey_str, - nonces_str, - interval_wildcards_str, - sighashes_str, - ) { - Ok(sigs) => { - // Return as a C string - CString::new(sigs).unwrap().into_raw() - } - Err(e) => error_as_cstr_prefix(e), - } -} - -#[no_mangle] -pub extern "C" fn create_deterministic_nonce_c(event_id: *const c_char, index: u32) -> *mut c_char { - // Convert the event_id from raw pointer to Rust string - let event_id_str = unsafe { - CStr::from_ptr(event_id) - .to_str() - .unwrap_or("Error in event ID") - }; - - // Call your existing function that creates the nonce (assuming this is what you want) - match create_deterministic_nonce_intern(event_id_str, index) { - Ok((sk, pk)) => { - // Return as a C string - CString::new(format!("{} {}", sk, pk)).unwrap().into_raw() - } - Err(e) => error_as_cstr_prefix(e), - } -} - // Additional C exports for Flutter wallet operations #[no_mangle] pub extern "C" fn get_xpub_c() -> *mut c_char { - match get_xpub_intern() { + match get_xpub() { Ok(xpub) => CString::new(xpub).unwrap().into_raw(), Err(e) => error_as_cstr_prefix(e), } @@ -570,31 +30,12 @@ pub extern "C" fn get_xpub_c() -> *mut c_char { #[no_mangle] pub extern "C" fn get_address_c(index: u32) -> *mut c_char { - match get_address_intern(index) { + match get_address(index) { Ok(address) => CString::new(address).unwrap().into_raw(), Err(e) => error_as_cstr_prefix(e), } } -// #[no_mangle] -// pub extern "C" fn init_from_file_c(path: *const c_char, password: *const c_char) -> *mut c_char { -// let path_str = unsafe { -// CStr::from_ptr(path) -// .to_str() -// .unwrap_or("Error in path parameter") -// }; -// let password_str = unsafe { -// CStr::from_ptr(password) -// .to_str() -// .unwrap_or("Error in password parameter") -// }; - -// match init_intern(path_str, password_str, false) { -// Ok(xpub) => CString::new(xpub).unwrap().into_raw(), -// Err(e) => error_as_cstr_prefix(e), -// } -// } - #[no_mangle] pub extern "C" fn free_cstring(s: *mut c_char) { unsafe { @@ -612,204 +53,29 @@ fn error_as_cstr_prefix(error: String) -> *mut c_char { .into_raw() } -// ##### Facade functions for easy Python invocations (pyo3/maturin) - -/// Initialize the library, load secret from encrypted file. Return the XPUB. -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn init(path_for_secret_file: String, encryption_password: String) -> PyResult { - init_intern(&path_for_secret_file, &encryption_password, false) - .map_err(|e| PyErr::new::(e)) -} - -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn reinit_for_testing( - path_for_secret_file: String, - encryption_password: String, -) -> PyResult { - init_intern(&path_for_secret_file, &encryption_password, true) - .map_err(|e| PyErr::new::(e)) -} - -/// network: "bitcoin", or "signet". -// #[cfg(test)] -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn init_with_entropy(entropy: String, network: String) -> PyResult { - init_with_entropy_intern(&entropy, &network).map_err(|e| PyErr::new::(e)) -} - -/// Return the XPUB -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn get_xpub() -> PyResult { - get_xpub_intern().map_err(|e| PyErr::new::(e)) -} - -/// Return a child public key (specified by its index). -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn get_public_key(index: u32) -> PyResult { - get_public_key_intern(index).map_err(|e| PyErr::new::(e)) -} - -/// Return a child address (specified by index). -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn get_address(index: u32) -> PyResult { - get_address_intern(index).map_err(|e| PyErr::new::(e)) -} - -/// Verify a child public key. -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn verify_public_key(index: u32, pubkey: String) -> PyResult { - verify_public_key_intern(index, &pubkey).map_err(|e| PyErr::new::(e)) -} - -/// Sign a hash with a child private key (specified by index). -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn sign_hash_ecdsa(hash: String, signer_index: u32, signer_pubkey: String) -> PyResult { - sign_hash_ecdsa_intern(&hash, signer_index, &signer_pubkey) - .map_err(|e| PyErr::new::(e)) -} - -/// Create a nonce value deterministically -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn create_deterministic_nonce( - event_id: String, - nonce_index: u32, -) -> PyResult<(String, String)> { - create_deterministic_nonce_intern(&event_id, nonce_index) - .map_err(|e| PyErr::new::(e)) -} - -/// Sign a message using Schnorr, with a nonce, using a child key -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn sign_schnorr_with_nonce(msg: String, nonce_sec_hex: String, index: u32) -> PyResult { - sign_schnorr_with_nonce_intern(&msg, &nonce_sec_hex, index) - .map_err(|e| PyErr::new::(e)) -} - -/// Combine a number of public keys into one -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn combine_pubkeys(keys_hex: String) -> PyResult { - combine_pubkeys_intern(&keys_hex).map_err(|e| PyErr::new::(e)) -} - -/// Combine a number of secret keys into one. -/// Warning: Handle secret keys with caution! -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn combine_seckeys(keys_hex: String) -> PyResult { - combine_seckeys_intern(&keys_hex).map_err(|e| PyErr::new::(e)) -} - -/// Create adaptor signatures for a number of CETs -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn create_cet_adaptor_sigs( - num_digits: u8, - num_cets: u64, - digit_string_template: String, - oracle_pubkey: String, - signing_key_index: u32, - signing_pubkey: String, - nonces: String, - interval_wildcards: String, - sighashes: String, -) -> PyResult { - create_cet_adaptor_sigs_intern( - num_digits, - num_cets, - &digit_string_template, - &oracle_pubkey, - signing_key_index, - &signing_pubkey, - &nonces, - &interval_wildcards, - &sighashes, - ) - .map_err(|e| PyErr::new::(e)) -} - -/// Verify adaptor signatures for a number of CETs -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn verify_cet_adaptor_sigs( - num_digits: u8, - num_cets: u64, - digit_string_template: String, - oracle_pubkey: String, - signing_pubkey: String, - nonces: String, - interval_wildcards: String, - sighashes: String, - signatures: String, -) -> PyResult { - verify_cet_adaptor_sigs_intern( - num_digits, - num_cets, - &digit_string_template, - &oracle_pubkey, - &signing_pubkey, - &nonces, - &interval_wildcards, - &sighashes, - &signatures, - ) - .map_err(|e| PyErr::new::(e)) -} - -/// Perform final signing of a CET, create the two signatures when outcome signatures are available. -/// Return the decrypted signature of the other, and my own signature (newly created). -#[cfg(feature = "with-pyo3")] -#[pyfunction] -pub fn create_final_cet_sigs( - signing_key_index: u32, - signing_pubkey: String, - other_pubkey: String, - num_digits: u8, - oracle_signatures: String, - cet_value_wildcard: String, - cet_sighash: String, - other_adaptor_signature: String, -) -> PyResult { - create_final_cet_sigs_intern( - signing_key_index, - &signing_pubkey, - &other_pubkey, - num_digits, - &oracle_signatures, - &cet_value_wildcard, - &cet_sighash, - &other_adaptor_signature, - ) - .map_err(|e| PyErr::new::(e)) -} - #[cfg(feature = "with-pyo3")] #[pymodule] fn dlcplazacryptlib(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(init, m)?)?; - m.add_function(wrap_pyfunction!(reinit_for_testing, m)?)?; - m.add_function(wrap_pyfunction!(init_with_entropy, m)?)?; - m.add_function(wrap_pyfunction!(get_xpub, m)?)?; - m.add_function(wrap_pyfunction!(get_public_key, m)?)?; - m.add_function(wrap_pyfunction!(get_address, m)?)?; - m.add_function(wrap_pyfunction!(verify_public_key, m)?)?; - m.add_function(wrap_pyfunction!(sign_hash_ecdsa, m)?)?; - m.add_function(wrap_pyfunction!(create_deterministic_nonce, m)?)?; - m.add_function(wrap_pyfunction!(sign_schnorr_with_nonce, m)?)?; - m.add_function(wrap_pyfunction!(combine_pubkeys, m)?)?; - m.add_function(wrap_pyfunction!(combine_seckeys, m)?)?; - m.add_function(wrap_pyfunction!(create_cet_adaptor_sigs, m)?)?; - m.add_function(wrap_pyfunction!(verify_cet_adaptor_sigs, m)?)?; - m.add_function(wrap_pyfunction!(create_final_cet_sigs, m)?)?; + // m.add_function(wrap_pyfunction!(init, m)?)?; + // m.add_function(wrap_pyfunction!(reinit_for_testing, m)?)?; + m.add_function(wrap_pyfunction!(dlccryptlib_py::init_with_entropy, m)?)?; + m.add_function(wrap_pyfunction!(dlccryptlib_py::get_xpub, m)?)?; + m.add_function(wrap_pyfunction!(dlccryptlib_py::get_public_key, m)?)?; + m.add_function(wrap_pyfunction!(dlccryptlib_py::get_address, m)?)?; + // m.add_function(wrap_pyfunction!(verify_public_key, m)?)?; + m.add_function(wrap_pyfunction!(dlccryptlib_py::sign_hash_ecdsa, m)?)?; + m.add_function(wrap_pyfunction!( + dlccryptlib_py::create_deterministic_nonce, + m + )?)?; + // m.add_function(wrap_pyfunction!(sign_schnorr_with_nonce, m)?)?; + // m.add_function(wrap_pyfunction!(combine_pubkeys, m)?)?; + // m.add_function(wrap_pyfunction!(combine_seckeys, m)?)?; + m.add_function(wrap_pyfunction!( + dlccryptlib_py::create_cet_adaptor_sigs, + m + )?)?; + // m.add_function(wrap_pyfunction!(verify_cet_adaptor_sigs, m)?)?; + // m.add_function(wrap_pyfunction!(create_final_cet_sigs, m)?)?; Ok(()) } diff --git a/flutter_plugin/src/lib_struct.rs b/flutter_plugin/src/lib_struct.rs deleted file mode 100644 index dce87a3..0000000 --- a/flutter_plugin/src/lib_struct.rs +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use crate::adaptor_signature::{ - create_cet_adaptor_signatures, create_final_cet_signatures, sign_hash_ecdsa_with_key, - sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, -}; -use crate::hd_wallet_storage::HDWalletStorage; - -use bitcoin::bip32::Xpub; -use bitcoin::hashes::{sha256, Hash}; -use bitcoin::hex::DisplayHex; -use bitcoin::key::{Keypair, Secp256k1}; -use bitcoin::secp256k1::{All, PublicKey, SecretKey}; -use bitcoin::Address; -use secp256k1_zkp::schnorr::Signature as SchnorrSignature; -use secp256k1_zkp::{EcdsaAdaptorSignature, Scalar}; -use std::sync::{OnceLock, RwLock}; - -pub(crate) struct Lib { - hd_wallet_storage: Option, - secp: Secp256k1, -} - -impl Lib { - pub(crate) fn new_empty() -> Self { - Self { - hd_wallet_storage: None, - secp: Secp256k1::new(), - } - } - - pub(crate) fn init_from_secret_file( - &mut self, - path_for_secret_file: &str, - encryption_password: &str, - allow_reinit: bool, - ) -> Result<(), String> { - if !allow_reinit && self.hd_wallet_storage.is_some() { - return Err("Library already initialized!".to_string()); - } - let hd_wallet_storage = - HDWalletStorage::new_from_secret_file(path_for_secret_file, encryption_password)?; - self.hd_wallet_storage = Some(hd_wallet_storage); - Ok(()) - } - - // #[cfg(test)] - pub(crate) fn init_with_entropy( - &mut self, - entropy: &Vec, - network: &str, - ) -> Result<(), String> { - let hd_wallet = HDWalletStorage::new_with_entropy(entropy, network)?; - self.hd_wallet_storage = Some(hd_wallet); - Ok(()) - } - - /// Return the XPUB - pub(crate) fn get_xpub(&self) -> Result { - if let Some(hd_wallet) = &self.hd_wallet_storage { - hd_wallet.get_xpub() - } else { - Err("Library not initialized!".to_string()) - } - } - - fn get_child_keypair(&self, index: u32) -> Result { - if let Some(hd_wallet) = &self.hd_wallet_storage { - hd_wallet.get_child_keypair(index) - } else { - Err("Library not initialized!".to_string()) - } - } - - /// Return a child public key - pub(crate) fn get_child_public_key(&self, index: u32) -> Result { - if let Some(hd_wallet) = &self.hd_wallet_storage { - hd_wallet.get_child_public_key(index) - } else { - Err("Library not initialized!".to_string()) - } - } - - /// Return a child address - pub(crate) fn get_address(&self, index: u32) -> Result { - if let Some(hd_wallet) = &self.hd_wallet_storage { - hd_wallet.get_address(index) - } else { - Err("Library not initialized!".to_string()) - } - } - - fn verify_child_public_key_intern( - &self, - index: u32, - pubkey: &PublicKey, - print_entity: &str, - ) -> Result { - if let Some(hd_wallet) = &self.hd_wallet_storage { - hd_wallet.verify_child_public_key_intern(index, pubkey, print_entity) - } else { - Err("Library not initialized!".to_string()) - } - } - - /// Verify a child public key - pub(crate) fn verify_child_public_key( - &self, - index: u32, - pubkey: &PublicKey, - ) -> Result { - self.verify_child_public_key_intern(index, pubkey, "Pubkey") - } - - pub(crate) fn sign_hash_ecdsa( - &self, - hash: &[u8; 32], - index: u32, - signer_pubkey: &PublicKey, - ) -> Result, String> { - let keypair = self.get_child_keypair(index)?; - // verify pubkey - let _ = self.verify_child_public_key_intern(index, signer_pubkey, "Signer pubkey")?; - - sign_hash_ecdsa_with_key(&self.secp, hash, &keypair.secret_key()) - } - - /// Create a nonce value deterministically - pub(crate) fn create_deterministic_nonce( - &self, - event_id: &str, - index: u32, - ) -> Result<(String, PublicKey), String> { - let msg = format!("This is a message for creating a deterministic nonce for event with ID {} and index {}", event_id, index); - let hash = sha256::Hash::hash(msg.as_bytes()).to_byte_array(); - let secretkey = SecretKey::from_slice(&hash).map_err(|e| e.to_string())?; - let publickey = secretkey.public_key(&self.secp); - Ok((hash.to_lower_hex_string(), publickey)) - } - - /// Sign a message using Schnorr, using a child key - pub(crate) fn sign_schnorr_with_nonce( - &self, - msg: &str, - nonce_sec: &[u8; 32], - index: u32, - ) -> Result { - let kp = self.get_child_keypair(index)?; - sign_schnorr_with_nonce_sec(&self.secp, &kp, msg, nonce_sec) - } - - pub(crate) fn combine_seckeys(secrets: &Vec) -> Result { - if secrets.len() == 0 { - return Err("At least one key is required".to_string()); - } - let secret = secrets[0]; - let result = secrets.iter().skip(1).fold(secret, |accum, s| { - accum.add_tweak(&Scalar::from(*s)).unwrap() - }); - Ok(result) - } - - pub(crate) fn create_cet_adaptor_sigs( - &self, - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey: &PublicKey, - signing_key_index: u32, - signing_pubkey: &PublicKey, - nonces: &Vec, - interval_wildcards: &Vec, - sighashes: &Vec<[u8; 32]>, - ) -> Result, String> { - // Prepare signing key - let sign_keypair = self.get_child_keypair(signing_key_index)?; - // Verify signing pubkey - let _ = self.verify_child_public_key_intern( - signing_key_index, - signing_pubkey, - "Signer pubkey", - )?; - - create_cet_adaptor_signatures( - &self.secp, - num_digits, - num_cets, - digit_string_template, - oracle_pubkey, - &sign_keypair, - nonces, - interval_wildcards, - sighashes, - ) - } - - pub(crate) fn verify_cet_adaptor_sigs( - &self, - num_digits: u8, - num_cets: u64, - digit_string_template: &str, - oracle_pubkey: &PublicKey, - signing_pubkey: &PublicKey, - nonces: &Vec, - interval_wildcards: &Vec, - sighashes: &Vec<[u8; 32]>, - signatures: &Vec, - ) -> Result<(), String> { - verify_cet_adaptor_signatures( - &self.secp, - num_digits, - num_cets, - digit_string_template, - oracle_pubkey, - signing_pubkey, - nonces, - interval_wildcards, - sighashes, - signatures, - ) - } - - /// Create signatures on a CET when outcome signatures are available - pub fn create_final_cet_sigs( - &self, - signing_key_index: u32, - signing_pubkey: &PublicKey, - other_pubkey: &PublicKey, - num_digits: u8, - oracle_signatures: &Vec, - cet_value_wildcard: &str, - cet_sighash: &[u8; 32], - other_adaptor_signature: &EcdsaAdaptorSignature, - ) -> Result<(Vec, Vec), String> { - // Prepare signing key - let sign_keypair = self.get_child_keypair(signing_key_index)?; - // verify signer pubkey - let _ = self.verify_child_public_key_intern( - signing_key_index, - signing_pubkey, - "Signer pubkey", - )?; - - create_final_cet_signatures( - &self.secp, - &sign_keypair, - other_pubkey, - num_digits, - oracle_signatures, - cet_value_wildcard, - cet_sighash, - other_adaptor_signature, - ) - } -} - -pub(crate) fn global_lib() -> &'static RwLock { - static GLOBAL_LIB: OnceLock> = OnceLock::new(); - GLOBAL_LIB.get_or_init(|| RwLock::new(Lib::new_empty())) -} diff --git a/flutter_plugin/src/network.rs b/flutter_plugin/src/network.rs deleted file mode 100644 index f59faa4..0000000 --- a/flutter_plugin/src/network.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use bitcoin::Network; - -pub(crate) fn network_from_byte(network_byte: u8) -> Result { - match network_byte { - 0 => Ok(Network::Bitcoin), - 4 => Ok(Network::Signet), - _ => Err(format!("Invalid network byte {}.", network_byte)), - } -} - -pub(crate) fn network_from_string(network_str: &str) -> Result { - match network_str { - "bitcoin" => Ok(Network::Bitcoin), - "signet" => Ok(Network::Signet), - _ => Err(format!( - "Invalid network {}. Use 'bitcoin' for mainnet.", - network_str - )), - } -} diff --git a/flutter_plugin/src/parse.rs b/flutter_plugin/src/parse.rs deleted file mode 100644 index 9a2e6ac..0000000 --- a/flutter_plugin/src/parse.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use bitcoin::hex::FromHex; -use bitcoin::key::{Keypair, Secp256k1}; -use bitcoin::secp256k1::PublicKey; -use secp256k1_zkp::schnorr::Signature as SchnorrSignature; - -pub(crate) fn keypair_from_sec_key_hex(sec_key_hex: &str) -> Result { - let secbin = - <[u8; 32]>::from_hex(&sec_key_hex).map_err(|e| format!("Error in hex string {}", e))?; - let secp = Secp256k1::new(); - let keypair = Keypair::from_seckey_slice(&secp, &secbin) - .map_err(|e| format!("Error in secret key processing {}", e))?; - Ok(keypair) -} - -pub(crate) fn pubkey_from_hex(pub_key_hex: &str) -> Result { - let pubbin = <[u8; 33]>::from_hex(&pub_key_hex) - .map_err(|e| format!("Error in hex string {} ({})", e, pub_key_hex))?; - let pubkey = PublicKey::from_slice(&pubbin) - .map_err(|e| format!("Error in public key processing {}", e))?; - Ok(pubkey) -} - -pub(crate) fn hash_from_hex(hash_hex: &str) -> Result<[u8; 32], String> { - let hashbin = - <[u8; 32]>::from_hex(&hash_hex).map_err(|e| format!("Error in hex string {}", e))?; - Ok(hashbin) -} - -pub(crate) fn schnorr_sig_from_hex(sig_hex: &str) -> Result { - let sigbin = <[u8; 64]>::from_hex(&sig_hex) - .map_err(|e| format!("Error in signature string {} ({})", e, sig_hex))?; - let sig = SchnorrSignature::from_slice(&sigbin) - .map_err(|e| format!("Error in signature processing {}", e))?; - Ok(sig) -} diff --git a/flutter_plugin/src/secret_entropy_storage.rs b/flutter_plugin/src/secret_entropy_storage.rs deleted file mode 100644 index a48cac9..0000000 --- a/flutter_plugin/src/secret_entropy_storage.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2025-present Cadena Bitcoin -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -use crate::network::{network_from_byte, network_from_string}; - -use bip39::Mnemonic; -use bitcoin::hashes::{sha256, Hash}; -use bitcoin::hex::FromHex; -use bitcoin::Network; -use std::fs; - -const ENCRYPT_KEY_HASH_MESSAGE: &str = "Secret Entropy Storage Genesis "; - -/// Store a secret seed entropy -pub(crate) struct SecretEntropyStore { - entropy: Vec, - network: Network, -} - -impl SecretEntropyStore { - pub(crate) fn new_from_secret_file( - path_for_secret_file: &str, - encryption_password: &str, - ) -> Result { - let entropy_encrypted = read_encrypted_payload_from_file(path_for_secret_file)?; - let (network, entropy_decrypted) = - decrypt_payload(&entropy_encrypted, encryption_password)?; - Ok(Self { - entropy: entropy_decrypted, - network, - }) - } - - // #[cfg(test)] - pub(crate) fn new_with_entropy(entropy: &Vec, network: &str) -> Result { - let entropy = entropy.clone(); - let network = network_from_string(network)?; - Ok(Self { entropy, network }) - } - - pub(crate) fn get_secret_entropy(&self) -> &Vec { - &self.entropy - } - - pub(crate) fn network(&self) -> Network { - self.network - } -} - -fn checksum_of_entropy(entropy: &Vec) -> Result { - let mnemo = Mnemonic::from_entropy(entropy) - .map_err(|e| format!("Could not process entropy {}", e.to_string()))?; - let checksum = mnemo.checksum(); - Ok(checksum) -} - -pub(crate) fn read_encrypted_payload_from_file( - path_for_secret_file: &str, -) -> Result, String> { - let contents = fs::read_to_string(path_for_secret_file).map_err(|e| { - format!( - "Could not read file '{}', {}", - path_for_secret_file, - e.to_string() - ) - })?; - if contents.len() < 4 { - return Err(format!( - "File content is too short ({} {})", - contents.len(), - path_for_secret_file - )); - } - let lines = contents.split("\n").collect::>(); - let first_line = lines.get(0).expect("File content is too short"); - let words = first_line.split(" ").collect::>(); - let hex_data = words.get(0).expect("File content is too short"); - let bin_data = parse_payload_hex(hex_data)?; - Ok(bin_data) -} - -fn parse_payload_hex(entropy_hex: &str) -> Result, String> { - let hex_len = entropy_hex.len(); - if hex_len % 2 != 0 { - return Err(format!("File content length is not even! {}", hex_len)); - } - let bin_data = match hex_len { - 38 => <[u8; 19]>::from_hex(entropy_hex) - .map_err(|e| format!("Could not parse contents as hex {}", e.to_string()))? - .to_vec(), - 70 => <[u8; 35]>::from_hex(entropy_hex) - .map_err(|e| format!("Could not parse contents as hex {}", e.to_string()))? - .to_vec(), - _ => return Err(format!("Invalid hex data length {}", hex_len)), - }; - Ok(bin_data) -} - -pub(crate) fn parse_entropy_hex(entropy_hex: &str) -> Result, String> { - let hex_len = entropy_hex.len(); - if hex_len % 2 != 0 { - return Err(format!("Hex string length is not even! {}", hex_len)); - } - let bin_data = match hex_len { - 32 => <[u8; 16]>::from_hex(entropy_hex) - .map_err(|e| format!("Could not parse entropy as hex {}", e.to_string()))? - .to_vec(), - 64 => <[u8; 32]>::from_hex(entropy_hex) - .map_err(|e| format!("Could not parse entropy as hex {}", e.to_string()))? - .to_vec(), - _ => return Err(format!("Invalid hex entropy length {}", hex_len)), - }; - Ok(bin_data) -} - -fn decrypt_xor(data: &Vec, key: &Vec) -> Result, String> { - let keylen = key.len(); - if keylen == 0 { - return Err(format!("Invalid decryption key length {}", keylen)); - } - let mut output = Vec::with_capacity(data.len()); - for i in 0..data.len() { - output.push(data[i] ^ key[i % keylen]); - } - Ok(output) -} - -pub(crate) fn decrypt_payload( - encrypted: &Vec, - encryption_password: &str, -) -> Result<(Network, Vec), String> { - let message = ENCRYPT_KEY_HASH_MESSAGE.to_string() + encryption_password; - let encrypt_key = sha256::Hash::hash(message.as_bytes()) - .to_byte_array() - .to_vec(); - let decrypted = decrypt_xor(encrypted, &encrypt_key)?; - - let network_byte = decrypted[0]; - let entropy_len = decrypted[1]; - let checksum_read = decrypted[2]; - let entropy = decrypted[3..].to_vec(); - - // check & set network - let network = network_from_byte(network_byte).map_err(|e| { - format!( - "Invalid network. Check the encryption password and the secret file! ({})", - e - ) - })?; - - // check entropy len - if entropy_len as usize != entropy.len() { - return Err(format!( - "Entropy length mismatch, {} vs {}. Check the encryption password and the secret file!", - entropy_len, - entropy.len() - )); - } - - // check checksum - let checksum_computed = checksum_of_entropy(&entropy)?; - if checksum_read != checksum_computed { - return Err(format!( - "Checksum mismatch! {} vs {}. Check the encryption password and the secret file!", - checksum_read, checksum_computed - )); - } - - Ok((network, entropy)) -} diff --git a/flutter_plugin/src/test_lib.rs b/flutter_plugin/src/test_lib.rs index 4fad6a9..700a9fd 100644 --- a/flutter_plugin/src/test_lib.rs +++ b/flutter_plugin/src/test_lib.rs @@ -2,395 +2,73 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -use crate::adaptor_signature::verify_ecdsa_signature; -use crate::{ - combine_pubkeys_intern, combine_seckeys_intern, create_deterministic_nonce_intern, - get_public_key_intern, init_with_entropy_intern, keypair_from_sec_key_hex, - sign_schnorr_with_nonce_intern, verify_public_key_intern, Lib, -}; -use bitcoin::hex::FromHex; -use bitcoin::secp256k1::PublicKey; +use dlccryptlib::{create_deterministic_nonce, get_public_key, init_with_entropy, sign_hash_ecdsa}; const DUMMY_ENTROPY_STR: &str = "0000000000000000000000000000000000000000000000000000000000000000"; -const NETWORK_MAINNET: &str = "bitcoin"; const NETWORK_SIGNET: &str = "signet"; const DEFAULT_NETWORK: &str = NETWORK_SIGNET; -fn dummy_bytes32(last_byte: u8) -> [u8; 32] { - let mut b = [0; 32]; - b[31] = last_byte; - b -} - -fn dummy_entropy() -> Vec { - dummy_bytes32(0).to_vec() +fn dummy_bytes32(last_byte: u8) -> String { + format!( + "00000000000000000000000000000000000000000000000000000000000000{:02x}", + last_byte + ) } -#[test] -fn test_init_with_entropy_lib() { - let mut lib = Lib::new_empty(); - let _ = lib - .init_with_entropy(&dummy_entropy(), DEFAULT_NETWORK) - .unwrap(); - let xpub = lib.get_xpub().unwrap().to_string(); - assert_eq!( - xpub, - "tpubDCWivZp6qaqCALCt8MyLqAb3awnWm4hfbBPjdZqirYFXYeZ5YsfbWVaPacULZTGtK1RPBSZ92UWNjnhL4fB9UVrF2FjgW8cgmBjxPBmB4iB" - ); +fn dummy_entropy() -> String { + dummy_bytes32(0) } #[test] -fn test_init_with_entropy_intern() { - let xpub = init_with_entropy_intern(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); +fn test_init_with_entropy() { + let xpub = init_with_entropy(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); assert_eq!( xpub, "tpubDCWivZp6qaqCALCt8MyLqAb3awnWm4hfbBPjdZqirYFXYeZ5YsfbWVaPacULZTGtK1RPBSZ92UWNjnhL4fB9UVrF2FjgW8cgmBjxPBmB4iB" ); } -#[test] -fn test_init_with_entropy_lib_mainnet() { - let mut lib = Lib::new_empty(); - let _ = lib - .init_with_entropy(&dummy_entropy(), NETWORK_MAINNET) - .unwrap(); - let xpub = lib.get_xpub().unwrap().to_string(); - assert_eq!( - xpub, - "xpub6Bner3L3tdQW367NmmMsWKtMfP7hbu4JxdtbSGdWWjSzLkSUEnT7G9h5GFWUXtifeRhHiUXJuek1qeaTJqnXkveWpiHp8rmt53E8HTMshg9" - ); -} - #[test] fn test_get_public_key() { - let _xpub = init_with_entropy_intern(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); + let _xpub = init_with_entropy(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); - let pubkey0 = get_public_key_intern(0).unwrap(); + let pubkey0 = get_public_key(0).unwrap(); assert_eq!( pubkey0.to_string(), "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32" ); - let pubkey3 = get_public_key_intern(3).unwrap(); + let pubkey3 = get_public_key(3).unwrap(); assert_eq!( pubkey3.to_string(), "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e" ); } -#[test] -fn test_verify_public_key() { - let _xpub = init_with_entropy_intern(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); - - assert!(verify_public_key_intern( - 0, - "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32" - ) - .unwrap()); - assert_eq!(verify_public_key_intern(0, "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e").err().unwrap(), - "Pubkey mismatch, index 0, 03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e vs. 0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32"); - assert!(verify_public_key_intern( - 3, - "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e" - ) - .unwrap()); - assert_eq!(verify_public_key_intern(3, "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32").err().unwrap(), - "Pubkey mismatch, index 3, 0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32 vs. 03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e"); -} - #[test] fn test_sign_hash_ecdsa() { - let mut lib = Lib::new_empty(); - let _xpub = lib - .init_with_entropy(&dummy_entropy(), DEFAULT_NETWORK) - .unwrap(); + let _xpub = init_with_entropy(&dummy_entropy(), DEFAULT_NETWORK).unwrap(); - let pubkey3 = lib.get_child_public_key(3).unwrap(); + let pubkey3 = get_public_key(3).unwrap(); assert_eq!( pubkey3.to_string(), "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e" ); let hash = dummy_bytes32(7); - let sig = lib.sign_hash_ecdsa(&hash, 3, &pubkey3).unwrap(); - - // verify_signature - let verif_res = verify_ecdsa_signature(&hash, &sig, &pubkey3, true).unwrap(); - assert!(verif_res); + let _sig = sign_hash_ecdsa(&hash, 3, &pubkey3).unwrap(); // negative test, wrong index - assert!(lib.sign_hash_ecdsa(&hash, 31, &pubkey3).is_err()); + assert!(sign_hash_ecdsa(&hash, 31, &pubkey3).is_err()); } #[test] fn test_create_deterministic_nonce() { - let (sk1, pk1) = create_deterministic_nonce_intern("event01", 0).unwrap(); + let (sk1, pk1) = create_deterministic_nonce("event01", 0).unwrap(); assert_eq!(sk1.len(), 64); assert_eq!(pk1.len(), 66); assert_ne!(sk1, pk1); - let (sk2, pk2) = create_deterministic_nonce_intern("event01", 1).unwrap(); + let (sk2, pk2) = create_deterministic_nonce("event01", 1).unwrap(); assert_ne!(sk1, sk2); assert_ne!(pk1, pk2); } - -#[test] -fn test_sign_schnorr_with_nonce() { - let _xpub = init_with_entropy_intern(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); - - let msg = "This is a message"; - let nonce = "0123450000000000006897528962743076432965432697856340567500000100"; - let sig = sign_schnorr_with_nonce_intern(msg, nonce, 0).unwrap(); - let expected_sig = "ff4cb99e0a9be8ec7dea1e51904cf22f71717c19fc3e7dcbc8346eb28bebffbb892c4c41e05c2383efda00f5acc9c7f3622d88a90630cd62d49db598c8ce10b9"; - assert_eq!(sig.len(), 128); - assert_eq!(sig.to_string(), expected_sig); - - // sign again - let sig2 = sign_schnorr_with_nonce_intern(msg, nonce, 0).unwrap(); - assert_eq!(sig2.to_string(), expected_sig); - - // sign with different nonce - let nonce2 = "0123450000000000006897528962743076432965432697856340567500000199"; - let sig3 = sign_schnorr_with_nonce_intern(msg, nonce2, 0).unwrap(); - assert_eq!(sig3.to_string(), "4578740620e7a2c56eabea07c835dba35e832115930d023d0a7778652fbbf7d97a9f4a207dcb1456f1b0f57c4856085c32c79f4efce81cd276c272190aab5e3c"); -} - -fn create_dummy_pubkey(index: u8) -> PublicKey { - let sechex = format!( - "012345000000000000689752896274307643296543269785634056750000000{}", - index - ); - assert_eq!(sechex.len(), 64); - let keypair = keypair_from_sec_key_hex(&sechex).unwrap(); - keypair.public_key() -} - -#[test] -fn test_combine_pubkeys() { - let mut input_str = String::new(); - for i in 0..3 { - input_str += &(create_dummy_pubkey(i).to_string() + " "); - } - let combined = combine_pubkeys_intern(&input_str).unwrap(); - assert_eq!( - combined, - "030d7a38fb6eab9933efd3149f7ce0c466e93eb0680442856acb664719b60ae977" - ); -} - -#[test] -fn test_combine_seckeys() { - let input_str = "0123450000000000006897528962743076432965432697856340567500000100 \ - 0123450000000000006897528962743076432965432697856340567500000200 \ - 0123450000000000006897528962743076432965432697856340567500000300"; - let combined = combine_seckeys_intern(input_str).unwrap(); - assert_eq!( - combined, - "0369cf00000000000139c5f79c275c9162c97c2fc973c69029c1035f00000600" - ); -} - -#[test] -fn test_create_cet_adaptor_sigs() { - let mut lib = Lib::new_empty(); - let _xpub = lib.init_with_entropy(&dummy_bytes32(0).to_vec(), DEFAULT_NETWORK); - - let nonces = vec![ - create_dummy_pubkey(0), - create_dummy_pubkey(1), - create_dummy_pubkey(2), - create_dummy_pubkey(3), - create_dummy_pubkey(4), - create_dummy_pubkey(5), - ]; - let interval_wildcards = vec![ - "001****".to_string(), - "002****".to_string(), - "003****".to_string(), - ]; - let sighashes = vec![dummy_bytes32(0), dummy_bytes32(1), dummy_bytes32(2)]; - let oracle_pubkey = create_dummy_pubkey(9); - let my_pubkey = lib.get_child_public_key(0).unwrap(); - assert_eq!( - my_pubkey.to_string(), - "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32" - ); - - let adaptor_sigs_vec = lib - .create_cet_adaptor_sigs( - 6, // num_digits - 3, // num_cets - "Outcome:btcusd1741474920:{digit_index}:{digit_outcome}", - &oracle_pubkey, - 0, - &my_pubkey, - &nonces, - &interval_wildcards, - &sighashes, - ) - .unwrap(); - assert_eq!(adaptor_sigs_vec.len(), 3); - // adaptor sigs are variable, cannot assert -} - -#[test] -fn test_verify_cet_adaptor_sigs() { - let mut lib = Lib::new_empty(); - let _xpub = lib.init_with_entropy(&dummy_bytes32(0).to_vec(), DEFAULT_NETWORK); - - let nonces = vec![ - create_dummy_pubkey(0), - create_dummy_pubkey(1), - create_dummy_pubkey(2), - create_dummy_pubkey(3), - create_dummy_pubkey(4), - create_dummy_pubkey(5), - ]; - let interval_wildcards = vec![ - "001****".to_string(), - "002****".to_string(), - "003****".to_string(), - ]; - let sighashes = vec![dummy_bytes32(0), dummy_bytes32(1), dummy_bytes32(2)]; - let oracle_pubkey = create_dummy_pubkey(9); - let my_pubkey = lib.get_child_public_key(0).unwrap(); - assert_eq!( - my_pubkey.to_string(), - "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32" - ); - - // First create the signatures - let adaptor_sigs_vec = lib - .create_cet_adaptor_sigs( - 6, // num_digits - 3, // num_cets - "Outcome:btcusd1741474920:{digit_index}:{digit_outcome}", - &oracle_pubkey, - 0, - &my_pubkey, - &nonces, - &interval_wildcards, - &sighashes, - ) - .unwrap(); - assert_eq!(adaptor_sigs_vec.len(), 3); - - // Verify the signatures - let _res = lib - .verify_cet_adaptor_sigs( - 6, - 3, - "Outcome:btcusd1741474920:{digit_index}:{digit_outcome}", - &oracle_pubkey, - &my_pubkey, - &nonces, - &interval_wildcards, - &sighashes, - &adaptor_sigs_vec, - ) - .unwrap(); -} - -#[test] -fn test_create_final_cet_sigs() { - let event_id = "btcusd1741474920"; - let digits_template_string = "Outcome:btcusd1741474920:{digit_index}:{digit_outcome}"; - let digits = 4 as u8; - let final_cet_wildcard = "9534"; - let sighash = dummy_bytes32(7); - - // First preparation: create oracle signatures - let mut lib_ora = Lib::new_empty(); - let _xpub = lib_ora.init_with_entropy(&dummy_bytes32(3).to_vec(), DEFAULT_NETWORK); - let oracle_pubkey = lib_ora.get_child_public_key(0).unwrap(); - assert_eq!( - oracle_pubkey.to_string(), - "020a5e571a47cc259d3cc0454a8b7e58bba16e01156bb72d0ce490823f51117cce" - ); - - // Prepare nonces - let mut nonces_sec_vec = Vec::new(); - let mut nonces_pub_vec = Vec::new(); - let mut nonces = String::new(); - for i in 0..(digits as usize) { - let (nsec, npub) = lib_ora - .create_deterministic_nonce(event_id, i as u32) - .unwrap(); - nonces_sec_vec.push(<[u8; 32]>::from_hex(&nsec).unwrap()); - nonces_pub_vec.push(npub.clone()); - nonces += &format!("{} ", npub); - } - assert_eq!(nonces, "03829589a7db8530b521577ce5b9560e31cb29b943927b417c580ec3b6e57317a9 0278d6b6808d5370da62c9304f66415c1f9f408a2ee9d95a9dc836512218a7b04f 027c14675c2bd2e728e5760d5017d4ae2b22a4a33193689654e5eb13111ab7f491 02e7657c7d006d27b248642974875348b41299690a6415bc019ac71e6988434daa "); - - let mut oracle_signatures = Vec::new(); - for i in 0..(digits as usize) { - let digit_value = final_cet_wildcard.chars().collect::>()[i as usize]; - let digit_string = digits_template_string - .replace("{digit_index}", &format!("{}", i)) - .replace("{digit_outcome}", &format!("{}", digit_value)); - let sig = lib_ora - .sign_schnorr_with_nonce(&digit_string, &nonces_sec_vec[i], 0) - .unwrap(); - oracle_signatures.push(sig); - } - - // Second, create adaptor sig of other - let mut lib2 = Lib::new_empty(); - let _xpub = lib2 - .init_with_entropy(&dummy_bytes32(2).to_vec(), DEFAULT_NETWORK) - .unwrap(); - let other_pubkey = lib2.get_child_public_key(0).unwrap(); - assert_eq!( - other_pubkey.to_string(), - "02142c5af97c4afd91bea47ac47e56fad2935dcacc04b3ffa69e5ff7760cbd07ed" - ); - - // only use one CET - let other_adaptor_sigs_vec = lib2 - .create_cet_adaptor_sigs( - digits, - 1, // num_cets - digits_template_string, - &oracle_pubkey, - 0, - &other_pubkey, - &nonces_pub_vec, - &vec![final_cet_wildcard.to_string()], // interval_wildcards - &vec![sighash], - ) - .unwrap(); - let other_adaptor_sig = other_adaptor_sigs_vec[0]; - // adator sig is variable, cannot assert - assert_eq!(other_adaptor_sig.to_string().len(), 324); - - // Now back to ourselves - let mut lib1 = Lib::new_empty(); - let _xpub = lib1 - .init_with_entropy(&dummy_bytes32(1).to_vec(), DEFAULT_NETWORK) - .unwrap(); - let my_pubkey = lib1.get_child_public_key(0).unwrap(); - assert_eq!( - my_pubkey.to_string(), - "035bcac7323e9971268213a188d8268277abcd962cdf096e68e2b58c228216f104" - ); - - let final_sigs = lib1 - .create_final_cet_sigs( - 0, - &my_pubkey, - &other_pubkey, - digits, - &oracle_signatures, - final_cet_wildcard, - &sighash, - &other_adaptor_sig, - ) - .unwrap(); - let sig_of_other = final_sigs.0; - let my_sig = final_sigs.1; - - // verify_signatures - let verif_res1 = verify_ecdsa_signature(&sighash, &sig_of_other, &other_pubkey, true).unwrap(); - assert!(verif_res1); - let verif_res2 = verify_ecdsa_signature(&sighash, &my_sig, &my_pubkey, true).unwrap(); - assert!(verif_res2); -} From 128026d9d23f81e6859616c04752c3651b83fcbf Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Wed, 27 May 2026 22:31:39 +0200 Subject: [PATCH 7/8] Switch to v2.0.1 of cryptlib, includes index4&5 --- flutter_plugin/Cargo.toml | 6 +-- .../ios/Classes/DlcWalletPlugin.m | 4 +- .../ios/Classes/dlc_wallet_bridge.h | 8 ++-- .../flutter_plugin/lib/dlc_wallet.dart | 7 ++-- flutter_plugin/src/lib.rs | 39 ------------------- flutter_plugin/src/test_lib.rs | 10 ++--- flutter_plugin/test_lib_c.py | 20 +++++----- 7 files changed, 28 insertions(+), 66 deletions(-) diff --git a/flutter_plugin/Cargo.toml b/flutter_plugin/Cargo.toml index 6533140..e1baf82 100644 --- a/flutter_plugin/Cargo.toml +++ b/flutter_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dlcplazacryptlib" -version = "1.0.0" +version = "2.0.1" edition = "2021" [lib] @@ -8,8 +8,8 @@ name = "dlcplazacryptlib" crate-type = ["cdylib", "staticlib"] [dependencies] -dlccryptlib = "=1.0.0" -dlccryptlib-py = "=1.0.0" +dlccryptlib = "=2.0.1" +dlccryptlib-py = "=2.0.1" pyo3 = { version = "0.23.1", optional = true } [features] diff --git a/flutter_plugin/flutter_plugin/ios/Classes/DlcWalletPlugin.m b/flutter_plugin/flutter_plugin/ios/Classes/DlcWalletPlugin.m index 45e46f4..3145643 100644 --- a/flutter_plugin/flutter_plugin/ios/Classes/DlcWalletPlugin.m +++ b/flutter_plugin/flutter_plugin/ios/Classes/DlcWalletPlugin.m @@ -46,7 +46,7 @@ - (void)handleSignHashEcdsa:(FlutterMethodCall*)call result:(FlutterResult)resul const char *signerPubkeyCStr = [signerPubkey UTF8String]; uint32_t signerIndexU32 = [signerIndex unsignedIntValue]; - char *signatureCStr = sign_hash_ecdsa_c(hashCStr, signerIndexU32, signerPubkeyCStr); + char *signatureCStr = sign_hash_ecdsa_c(hashCStr, 0, signerIndexU32, signerPubkeyCStr); if (signatureCStr == NULL) { result([FlutterError errorWithCode:@"SIGNATURE_ERROR" @@ -109,7 +109,7 @@ - (void)handleCreateCetAdaptorSigs:(FlutterMethodCall*)call result:(FlutterResul char *signaturesCStr = create_cet_adaptor_sigs_c( numDigitsU8, numCetsU32, digitStringTemplateCStr, oraclePublicKeyCStr, - signingKeyIndexU32, signingPublicKeyCStr, noncesCStr, intervalWildcardsCStr, sighashesCStr + 0, signingKeyIndexU32, signingPublicKeyCStr, noncesCStr, intervalWildcardsCStr, sighashesCStr ); if (signaturesCStr == NULL) { diff --git a/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h b/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h index c12b234..e306222 100644 --- a/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h +++ b/flutter_plugin/flutter_plugin/ios/Classes/dlc_wallet_bridge.h @@ -7,12 +7,12 @@ extern "C" { // Function declarations for the Rust library char* init_with_entropy_c(const char* entropy, const char* network); -char* get_public_key_c(uint32_t index); -char* sign_hash_ecdsa_c(const char* hash, uint32_t signer_index, const char* signer_pubkey); -char* create_cet_adaptor_sigs_c(uint8_t num_digits, uint32_t num_cets, const char* digit_string_template, const char* oracle_public_key, uint32_t signing_key_index, const char* signing_public_key, const char* nonces, const char* interval_wildcards, const char* sighashes); +char* get_public_key_c(uint32_t index4, uint32_t index5); +char* sign_hash_ecdsa_c(const char* hash, uint32_t signer_index4, uint32_t signer_index5, const char* signer_pubkey); +char* create_cet_adaptor_sigs_c(uint8_t num_digits, uint32_t num_cets, const char* digit_string_template, const char* oracle_public_key, uint32_t signing_key_index4, uint32_t signing_key_index5, const char* signing_public_key, const char* nonces, const char* interval_wildcards, const char* sighashes); char* create_deterministic_nonce_c(const char* event_id, uint32_t index); char* get_xpub_c(void); -char* get_address_c(uint32_t index); +char* get_address_c(uint32_t index4, uint32_t index5); // char* init_from_file_c(const char* path, const char* password); void free_cstring(char* s); diff --git a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart index 4986042..6e9a1b4 100644 --- a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart +++ b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart @@ -212,7 +212,7 @@ class DlcWallet { /// Used for: Address generation, verification static Future getPublicKey(int index) async { _loadLibrary(); - final result = _getPublicKey(index); + final result = _getPublicKey(0, index); return _convertCStringAndFree(result); } @@ -220,7 +220,7 @@ class DlcWallet { /// Used for: Receiving Bitcoin payments static Future getAddress(int index) async { _loadLibrary(); - final result = _getAddress(index); + final result = _getAddress(0, index); return _convertCStringAndFree(result); } @@ -254,7 +254,7 @@ class DlcWallet { final signerPubkeyPtr = signerPubkey.toNativeUtf8(); try { - final result = _signHashEcdsa(hashPtr, signerIndex, signerPubkeyPtr); + final result = _signHashEcdsa(hashPtr, 0, signerIndex, signerPubkeyPtr); return _convertCStringAndFree(result); } finally { @@ -353,6 +353,7 @@ class DlcWallet { numCets, digitStringTemplatePtr, oraclePublicKeyPtr, + 0, signingKeyIndex, signingPublicKeyPtr, noncesPtr, diff --git a/flutter_plugin/src/lib.rs b/flutter_plugin/src/lib.rs index 522c8a8..5848398 100644 --- a/flutter_plugin/src/lib.rs +++ b/flutter_plugin/src/lib.rs @@ -5,54 +5,15 @@ #[cfg(test)] mod test_lib; -use dlccryptlib::{get_address, get_xpub}; #[cfg(feature = "with-pyo3")] use dlccryptlib_py; -use std::ffi::CString; -use std::os::raw::c_char; - // Conditional compilation to exclude PyO3-related code for Android #[cfg(feature = "with-pyo3")] use pyo3::prelude::*; #[cfg(feature = "with-pyo3")] use pyo3::wrap_pyfunction; -// ##### Facade functions for C-style-interface invocations -// Additional C exports for Flutter wallet operations -#[no_mangle] -pub extern "C" fn get_xpub_c() -> *mut c_char { - match get_xpub() { - Ok(xpub) => CString::new(xpub).unwrap().into_raw(), - Err(e) => error_as_cstr_prefix(e), - } -} - -#[no_mangle] -pub extern "C" fn get_address_c(index: u32) -> *mut c_char { - match get_address(index) { - Ok(address) => CString::new(address).unwrap().into_raw(), - Err(e) => error_as_cstr_prefix(e), - } -} - -#[no_mangle] -pub extern "C" fn free_cstring(s: *mut c_char) { - unsafe { - if s.is_null() { - return; - } - let _ = CString::from_raw(s); - } -} - -// Return error with an "ERROR: " prefix, as a C string -fn error_as_cstr_prefix(error: String) -> *mut c_char { - CString::new(format!("ERROR: {}", error)) - .unwrap() - .into_raw() -} - #[cfg(feature = "with-pyo3")] #[pymodule] fn dlcplazacryptlib(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { diff --git a/flutter_plugin/src/test_lib.rs b/flutter_plugin/src/test_lib.rs index 700a9fd..76d7b84 100644 --- a/flutter_plugin/src/test_lib.rs +++ b/flutter_plugin/src/test_lib.rs @@ -32,13 +32,13 @@ fn test_init_with_entropy() { fn test_get_public_key() { let _xpub = init_with_entropy(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); - let pubkey0 = get_public_key(0).unwrap(); + let pubkey0 = get_public_key(0, 0).unwrap(); assert_eq!( pubkey0.to_string(), "0298720ece754e377af1b2716256e63c2e2427ff6ebdc66c2071c43ae80132ca32" ); - let pubkey3 = get_public_key(3).unwrap(); + let pubkey3 = get_public_key(0, 3).unwrap(); assert_eq!( pubkey3.to_string(), "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e" @@ -49,17 +49,17 @@ fn test_get_public_key() { fn test_sign_hash_ecdsa() { let _xpub = init_with_entropy(&dummy_entropy(), DEFAULT_NETWORK).unwrap(); - let pubkey3 = get_public_key(3).unwrap(); + let pubkey3 = get_public_key(0, 3).unwrap(); assert_eq!( pubkey3.to_string(), "03b74dc470965932fc976459096526b08a0f939a95e4b72db8f9aadce18a08a72e" ); let hash = dummy_bytes32(7); - let _sig = sign_hash_ecdsa(&hash, 3, &pubkey3).unwrap(); + let _sig = sign_hash_ecdsa(&hash, 0, 3, &pubkey3).unwrap(); // negative test, wrong index - assert!(sign_hash_ecdsa(&hash, 31, &pubkey3).is_err()); + assert!(sign_hash_ecdsa(&hash, 0, 31, &pubkey3).is_err()); } #[test] diff --git a/flutter_plugin/test_lib_c.py b/flutter_plugin/test_lib_c.py index 1ec796d..8b7f22d 100644 --- a/flutter_plugin/test_lib_c.py +++ b/flutter_plugin/test_lib_c.py @@ -24,11 +24,11 @@ def __init__(self): # Define Rust method signatures self.lib.init_with_entropy_c.argtypes = [c_char_p, c_char_p] self.lib.init_with_entropy_c.restype = c_char_p - self.lib.get_public_key_c.argtypes = [c_uint32] + self.lib.get_public_key_c.argtypes = [c_uint32, c_uint32] self.lib.get_public_key_c.restype = c_char_p - self.lib.sign_hash_ecdsa_c.argtypes = [c_char_p, c_uint32, c_char_p] + self.lib.sign_hash_ecdsa_c.argtypes = [c_char_p, c_uint32, c_uint32, c_char_p] self.lib.sign_hash_ecdsa_c.restype = c_char_p - self.lib.create_cet_adaptor_sigs_c.argtypes = [c_uint8, c_uint32, c_char_p, c_char_p, c_uint32, c_char_p, c_char_p, c_char_p, c_char_p] + self.lib.create_cet_adaptor_sigs_c.argtypes = [c_uint8, c_uint32, c_char_p, c_char_p, c_uint32, c_uint32, c_char_p, c_char_p, c_char_p, c_char_p] self.lib.create_cet_adaptor_sigs_c.restype = c_char_p self.lib.create_deterministic_nonce_c.argtypes = [c_char_p, c_char_p] self.lib.create_deterministic_nonce_c.restype = c_char_p @@ -39,19 +39,19 @@ def init_with_entropy(self, entropy, network): return self.lib.init_with_entropy_c(entropy.encode('utf-8'), network.encode('utf-8')).decode("utf-8") # Sign a hash with a child private key (specified by index). - def sign_hash_ecdsa(self, hash, signer_index, signer_pubkey): + def sign_hash_ecdsa(self, hash, signer_index4, signer_index5 , signer_pubkey): # Call the Rust function (sign_hash_ecdsa_c) from the .so library - return self.lib.sign_hash_ecdsa_c(hash.encode('utf-8'), signer_index, signer_pubkey.encode('utf-8')).decode('utf-8') + return self.lib.sign_hash_ecdsa_c(hash.encode('utf-8'), signer_index4, signer_index5, signer_pubkey.encode('utf-8')).decode('utf-8') # Create adaptor signatures for a number of CETs - def create_cet_adaptor_sigs(self, num_digits: int, num_cets: int, digit_string_template: str, oracle_pubkey: str, signing_key_index: int, signing_pubkey: str, nonces: str, interval_wildcards: str, sighashes: str): + def create_cet_adaptor_sigs(self, num_digits: int, num_cets: int, digit_string_template: str, oracle_pubkey: str, signing_key_index4: int, signing_key_index5: int, signing_pubkey: str, nonces: str, interval_wildcards: str, sighashes: str): # Call the Rust function (create_cet_adaptor_sigs_c) from the .so library - return self.lib.create_cet_adaptor_sigs_c(num_digits, num_cets, digit_string_template.encode('utf-8'), oracle_pubkey.encode('utf-8'), signing_key_index, signing_pubkey.encode('utf-8'), nonces.encode('utf-8'), interval_wildcards.encode('utf-8'), sighashes.encode('utf-8')).decode('utf-8') + return self.lib.create_cet_adaptor_sigs_c(num_digits, num_cets, digit_string_template.encode('utf-8'), oracle_pubkey.encode('utf-8'), signing_key_index4, signing_key_index5, signing_pubkey.encode('utf-8'), nonces.encode('utf-8'), interval_wildcards.encode('utf-8'), sighashes.encode('utf-8')).decode('utf-8') # Return a child public key. - def get_public_key(self, index): + def get_public_key(self, index4, index5): # Call the Rust function (get_public_key_c) from the .so library - return self.lib.get_public_key_c(index).decode("utf-8") + return self.lib.get_public_key_c(index4, index5).decode("utf-8") def create_deterministic_nonce(self, event_id, index): # Call the Rust function (create_deterministic_nonce_c) from the .so library @@ -85,7 +85,7 @@ def create_deterministic_nonce(self, event_id, index): nonces = "03bf8272fd77ac83400e8b7f1af5899ab96ce81871ca26d31fa3b80db08bdc412e 03ec14b379b5db0c5305a452ee04d4b82b5a1db90f8eddc55f1f94d5947b341ed4 0325642feb3db37b3ffa88b0754d59ad1c3116e035ee9e5557e107fd3d914fb3fb 02d597f9bd84cb925ade7efa04edf46c33a7d96cc4252647204a6961a34838d00d 03d11c778b1c4f1f7710a4b17816f02d049325220b2fb8007efd84248f08fd75dc 038fb0dbd6eb0e970c75c28ca02b614523fe59b5da000f815fcfbfcf4a4ecdd192 023f0eadc3b9c3337d31e38a9238a3c59505cc8004fa7ca6facdd3c853d824ca0d" interval_wildcards = "0000*** 0001*** 0002***" sighashes = "0001020300000000000000000000000000000000000000000000000000010200 0001020300000000000000000000000000000000000000000000000000010201 0001020300000000000000000000000000000000000000000000000000010202" -sigs = rust_interface.create_cet_adaptor_sigs(7, 3, digit_string_template, oracle_pubkey, 0, pubkey0, nonces, interval_wildcards, sighashes) +sigs = rust_interface.create_cet_adaptor_sigs(7, 3, digit_string_template, oracle_pubkey, 0, 0, pubkey0, nonces, interval_wildcards, sighashes) print("signatures:", sigs) From 1b239ad3b7e67fadb6babfdb7a1c26f34e126a82 Mon Sep 17 00:00:00 2001 From: optout <13562139+optout21@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:50:50 +0200 Subject: [PATCH 8/8] Support optional signer_index4 fields in signing requests. Changes done: - Extend `InputReqs` with `signerIndex4` field (next to `signerIndex`) - Extend `Cets` with `signerIndex4` field (next to `signerIndex`) - Read them from the Json version, if missing, fallback to default 0 - Extend `signHashEcdsa()` with extra `int signerIndex4` parameter - Extend `createCetAdaptorSigs()` with extra `int signingKeyIndex4` parameter - Extend `signTransaction()`with extra `int keyIndex4` parameter - Pass the extra parameters, down to the crytlib library methods. --- flutter_plugin/DLC_FUNCTIONS_GUIDE.md | 9 +++++---- .../flutter_plugin/example/lib/main.dart | 4 ++++ flutter_plugin/flutter_plugin/lib/dlc_wallet.dart | 11 +++++++---- lib/models/sigReqsByDlcIdsModel.dart | 10 ++++++++-- lib/services/auto_signing_service.dart | 12 +++++++----- lib/src/ui/screens/SignTransactionScreen.dart | 14 ++++++++++---- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/flutter_plugin/DLC_FUNCTIONS_GUIDE.md b/flutter_plugin/DLC_FUNCTIONS_GUIDE.md index 998ba58..e39bbad 100644 --- a/flutter_plugin/DLC_FUNCTIONS_GUIDE.md +++ b/flutter_plugin/DLC_FUNCTIONS_GUIDE.md @@ -71,7 +71,7 @@ final address = await DlcWallet.getAddress(0); ```dart final signature = await DlcWallet.signHashEcdsa( "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - 0, + 0, 0, pubkey ); ``` @@ -114,6 +114,7 @@ final signatures = await DlcWallet.createCetAdaptorSigs( digitStringTemplate: 'BTCUSD', oraclePublicKey: '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', signingKeyIndex: 0, + signingKeyIndex4: 0, signingPublicKey: await DlcWallet.getPublicKey(0), nonces: ['nonce1', 'nonce2'], intervalWildcards: ['50000-60000', '60000-70000'], @@ -156,7 +157,7 @@ final xpub = await DlcWallet.initWithEntropy(entropy, "signet"); final addresses = await DlcWallet.generateAddresses(10); // Sign transaction -final signature = await DlcWallet.signTransaction(txHash, keyIndex); +final signature = await DlcWallet.signTransaction(txHash, keyIndex, keyIndex4); ``` ### **2. DLC Contract Creation** @@ -183,13 +184,13 @@ final nonces = dlcSetup['nonces']; // Sign Bitcoin transaction final txSignature = await DlcWallet.signTransaction( transactionHash, - keyIndex + keyIndex, keyIndex4, ); // Sign arbitrary hash final hashSignature = await DlcWallet.signHashEcdsa( messageHash, - keyIndex, + keyIndex, keyIndex4, publicKey ); ``` diff --git a/flutter_plugin/flutter_plugin/example/lib/main.dart b/flutter_plugin/flutter_plugin/example/lib/main.dart index 2e98236..16e893f 100644 --- a/flutter_plugin/flutter_plugin/example/lib/main.dart +++ b/flutter_plugin/flutter_plugin/example/lib/main.dart @@ -121,10 +121,12 @@ class _WalletScreenState extends State with TickerProviderStateMix try { final index = int.parse(_indexController.text); + final index4 = 0; final publicKey = await DlcWallet.getPublicKey(index); final signature = await DlcWallet.signHashEcdsa( _hashController.text, index, + index4, publicKey, ); @@ -162,6 +164,7 @@ class _WalletScreenState extends State with TickerProviderStateMix final signature = await DlcWallet.signTransaction( _hashController.text, int.parse(_indexController.text), + 0, ); _showResult('Transaction Signature', signature); @@ -226,6 +229,7 @@ class _WalletScreenState extends State with TickerProviderStateMix digitStringTemplate: 'BTCUSD', oraclePublicKey: '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', signingKeyIndex: int.parse(_indexController.text), + signingKeyIndex4: 0, signingPublicKey: await DlcWallet.getPublicKey(int.parse(_indexController.text)), nonces: [ '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', diff --git a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart index 6e9a1b4..4a9a668 100644 --- a/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart +++ b/flutter_plugin/flutter_plugin/lib/dlc_wallet.dart @@ -229,13 +229,14 @@ class DlcWallet { /// Sign hash with ECDSA /// Used for: Transaction signing, message authentication static Future signHashEcdsa( - String hash, int signerIndex, String signerPubkey) async { + String hash, int signerIndex, int signerIndex4, String signerPubkey) async { if (Platform.isIOS) { // Use method channel for iOS try { final result = await _channel.invokeMethod('signHashEcdsa', { 'hash': hash, 'signerIndex': signerIndex, + 'signerIndex4': signerIndex4, 'signerPubkey': signerPubkey, }); return result.toString(); @@ -301,6 +302,7 @@ class DlcWallet { required String digitStringTemplate, required String oraclePublicKey, required int signingKeyIndex, + required int signingKeyIndex4, required String signingPublicKey, required List nonces, required List intervalWildcards, @@ -315,6 +317,7 @@ class DlcWallet { 'digitStringTemplate': digitStringTemplate, 'oraclePublicKey': oraclePublicKey, 'signingKeyIndex': signingKeyIndex, + 'signingKeyIndex4': signingKeyIndex4, 'signingPublicKey': signingPublicKey, 'nonces': nonces, 'intervalWildcards': intervalWildcards, @@ -353,7 +356,7 @@ class DlcWallet { numCets, digitStringTemplatePtr, oraclePublicKeyPtr, - 0, + signingKeyIndex4, signingKeyIndex, signingPublicKeyPtr, noncesPtr, @@ -451,9 +454,9 @@ class DlcWallet { /// Sign a Bitcoin transaction hash /// Used for: Transaction signing in wallet applications - static Future signTransaction(String txHash, int keyIndex) async { + static Future signTransaction(String txHash, int keyIndex, int keyIndex4) async { final publicKey = await getPublicKey(keyIndex); - return await signHashEcdsa(txHash, keyIndex, publicKey); + return await signHashEcdsa(txHash, keyIndex, keyIndex4, publicKey); } /// Check if the native library is loaded diff --git a/lib/models/sigReqsByDlcIdsModel.dart b/lib/models/sigReqsByDlcIdsModel.dart index d95a882..3a913c0 100644 --- a/lib/models/sigReqsByDlcIdsModel.dart +++ b/lib/models/sigReqsByDlcIdsModel.dart @@ -87,15 +87,17 @@ class Funding { class InputReqs { int? inputIndex; int? signerIndex; + int? signerIndex4; String? signerPubkey; String? sighash; int? sighashType; - InputReqs({this.inputIndex, this.signerIndex, this.signerPubkey, this.sighash, this.sighashType}); + InputReqs({this.inputIndex, this.signerIndex, this.signerIndex4, this.signerPubkey, this.sighash, this.sighashType}); InputReqs.fromJson(Map json) { inputIndex = json['input_index']; signerIndex = json['signer_index']; + signerIndex4 = json['signer_index4'] != null ? json['signer_index4'] : 0; signerPubkey = json['signer_pubkey']; sighash = json['sighash']; sighashType = json['sighash_type']; @@ -105,6 +107,7 @@ class InputReqs { final Map data = new Map(); data['input_index'] = this.inputIndex; data['signer_index'] = this.signerIndex; + data['signer_index4'] = this.signerIndex4; data['signer_pubkey'] = this.signerPubkey; data['sighash'] = this.sighash; data['sighash_type'] = this.sighashType; @@ -118,12 +121,13 @@ class Cets { String? digitStringTemplate; String? oraclePubkey; int? signerIndex; + int? signerIndex4; String? signerPubkey; String? nonces; String? intervalWildcards; String? sighashes; - Cets({this.numDigits, this.numCets, this.digitStringTemplate, this.oraclePubkey, this.signerIndex, this.signerPubkey, this.nonces, this.intervalWildcards, this.sighashes}); + Cets({this.numDigits, this.numCets, this.digitStringTemplate, this.oraclePubkey, this.signerIndex, this.signerIndex4, this.signerPubkey, this.nonces, this.intervalWildcards, this.sighashes}); Cets.fromJson(Map json) { numDigits = json['num_digits']; @@ -131,6 +135,7 @@ class Cets { digitStringTemplate = json['digit_string_template']; oraclePubkey = json['oracle_pubkey']; signerIndex = json['signer_index']; + signerIndex4 = json['signer_index4'] != null ? json['signer_index4'] : 0; signerPubkey = json['signer_pubkey']; nonces = json['nonces']; intervalWildcards = json['interval_wildcards']; @@ -144,6 +149,7 @@ class Cets { data['digit_string_template'] = this.digitStringTemplate; data['oracle_pubkey'] = this.oraclePubkey; data['signer_index'] = this.signerIndex; + data['signer_index4'] = this.signerIndex4; data['signer_pubkey'] = this.signerPubkey; data['nonces'] = this.nonces; data['interval_wildcards'] = this.intervalWildcards; diff --git a/lib/services/auto_signing_service.dart b/lib/services/auto_signing_service.dart index 7281c34..d146ef7 100644 --- a/lib/services/auto_signing_service.dart +++ b/lib/services/auto_signing_service.dart @@ -817,14 +817,15 @@ class AutoSigningService extends GetxService { for (final req in inputReqs!) { final hash = req.sighash; final signerIndex = req.signerIndex; + final signerIndex4 = req.signerIndex4; final signerPubkey = req.signerPubkey; - if (hash == null || signerIndex == null || signerPubkey == null) { + if (hash == null || signerIndex == null || signerIndex4 == null || signerPubkey == null) { throw Exception('Invalid signature request parameters'); } print('hash: $hash'); - final signature = await DlcWallet.signHashEcdsa(hash, signerIndex, signerPubkey); + final signature = await DlcWallet.signHashEcdsa(hash, signerIndex, signerIndex4, signerPubkey); print('ECDSA Signature: $signature'); fundingSignatures.add(signature); } @@ -834,8 +835,8 @@ class AutoSigningService extends GetxService { final refundReqs = _appController.sigReqsByDlcIdsModelObject.value.payload![0].refund?.inputReqs; if (refundReqs != null && refundReqs.isNotEmpty) { final refundReq = refundReqs[0]; - if (refundReq.sighash != null && refundReq.signerIndex != null && refundReq.signerPubkey != null) { - refundSignature = await DlcWallet.signHashEcdsa(refundReq.sighash!, refundReq.signerIndex!, refundReq.signerPubkey!); + if (refundReq.sighash != null && refundReq.signerIndex != null refundReq.signerIndex4 != null && refundReq.signerPubkey != null) { + refundSignature = await DlcWallet.signHashEcdsa(refundReq.sighash!, refundReq.signerIndex!, refundReq.signerIndex4!, refundReq.signerPubkey!); print('Refund Signature: $refundSignature'); } } @@ -856,7 +857,7 @@ class AutoSigningService extends GetxService { } // Validate that we have the required CET parameters - if (cets.signerIndex == null || cets.signerPubkey == null || cets.oraclePubkey == null) { + if (cets.signerIndex == null || cets.signerIndex4 == null || cets.signerPubkey == null || cets.oraclePubkey == null) { throw Exception('Missing required CET parameters'); } @@ -867,6 +868,7 @@ class AutoSigningService extends GetxService { digitStringTemplate: cets.digitStringTemplate ?? 'BTCUSD', oraclePublicKey: cets.oraclePubkey!, signingKeyIndex: cets.signerIndex!, + signingKeyIndex4: cets.signerIndex4!, signingPublicKey: cets.signerPubkey!, nonces: noncesList, intervalWildcards: intervalWildcardsList, diff --git a/lib/src/ui/screens/SignTransactionScreen.dart b/lib/src/ui/screens/SignTransactionScreen.dart index c17642b..362fe20 100644 --- a/lib/src/ui/screens/SignTransactionScreen.dart +++ b/lib/src/ui/screens/SignTransactionScreen.dart @@ -396,15 +396,16 @@ class _SignTransactionsState extends State for (final req in inputReqs!) { final hash = req.sighash; final signerIndex = req.signerIndex; + final signerIndex4 = req.signerIndex4; final signerPubkey = req.signerPubkey; - if (hash == null || signerIndex == null || signerPubkey == null) { + if (hash == null || signerIndex == null || signerIndex4 == null || signerPubkey == null) { throw Exception('Invalid signature request parameters'); } print('hash: $hash'); final signature = - await DlcWallet.signHashEcdsa(hash, signerIndex, signerPubkey); + await DlcWallet.signHashEcdsa(hash, signerIndex, signerIndex4, signerPubkey); print('ECDSA Signature: $signature'); fundingSignatures.add(signature); } @@ -417,9 +418,10 @@ class _SignTransactionsState extends State final refundReq = refundReqs[0]; if (refundReq.sighash != null && refundReq.signerIndex != null && + refundReq.signerIndex4 != null && refundReq.signerPubkey != null) { refundSignature = await DlcWallet.signHashEcdsa(refundReq.sighash!, - refundReq.signerIndex!, refundReq.signerPubkey!); + refundReq.signerIndex!, refundReq.signerIndex4!, refundReq.signerPubkey!); print('Refund Signature: $refundSignature'); } } @@ -474,10 +476,11 @@ class _SignTransactionsState extends State // Validate that we have the required CET parameters if (cets.signerIndex == null || + cets.signerIndex4 == null || cets.signerPubkey == null || cets.oraclePubkey == null) { print( - 'Missing required CET parameters: signerIndex=${cets.signerIndex}, signerPubkey=${cets.signerPubkey}, oraclePubkey=${cets.oraclePubkey}'); + 'Missing required CET parameters: signerIndex=${cets.signerIndex}, signerIndex4=${cets.signerIndex4}, signerPubkey=${cets.signerPubkey}, oraclePubkey=${cets.oraclePubkey}'); throw Exception('Missing required CET parameters'); } @@ -516,6 +519,7 @@ class _SignTransactionsState extends State digitStringTemplate: cets.digitStringTemplate ?? 'BTCUSD', oraclePublicKey: cets.oraclePubkey!, signingKeyIndex: cets.signerIndex!, + signingKeyIndex4: cets.signerIndex4!, signingPublicKey: cets.signerPubkey!, nonces: repeatedNonces, intervalWildcards: intervalWildcardsList, @@ -539,6 +543,7 @@ class _SignTransactionsState extends State digitStringTemplate: cets.digitStringTemplate ?? 'BTCUSD', oraclePublicKey: cets.oraclePubkey!, signingKeyIndex: cets.signerIndex!, + signingKeyIndex4: cets.signerIndex4!, signingPublicKey: cets.signerPubkey!, nonces: noncesList, // Use original 7 nonces intervalWildcards: intervalWildcardsList, @@ -560,6 +565,7 @@ class _SignTransactionsState extends State digitStringTemplate: cets.digitStringTemplate ?? 'BTCUSD', oraclePublicKey: cets.oraclePubkey!, signingKeyIndex: cets.signerIndex!, + signingKeyIndex4: cets.signerIndex4!, signingPublicKey: cets.signerPubkey!, nonces: noncesList, intervalWildcards: intervalWildcardsList,