@bennyblader/ddk-rn 0.3.31 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ddk_ffi"
3
- version = "0.3.31"
3
+ version = "0.3.33"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  description = "rust-dlc language bindings"
@@ -205,6 +205,31 @@ namespace ddk_ffi {
205
205
  sequence<OracleInfo> oracle_info,
206
206
  sequence<sequence<sequence<sequence<u8>>>> msgs
207
207
  );
208
+
209
+ [Throws=DLCError]
210
+ sequence<u8> extract_ecdsa_signature_from_oracle_signatures(
211
+ sequence<sequence<u8>> oracle_signatures,
212
+ sequence<u8> adaptor_signature
213
+ );
214
+
215
+ // Debug function to get all inputs for adaptor signature creation
216
+ // Use this to compare values with Fordefi
217
+ [Throws=DLCError]
218
+ CetAdaptorSignatureDebugInfo get_cet_adaptor_signature_inputs(
219
+ Transaction cet,
220
+ sequence<OracleInfo> oracle_info,
221
+ sequence<u8> funding_script_pubkey,
222
+ u64 fund_output_value,
223
+ sequence<sequence<sequence<u8>>> msgs
224
+ );
225
+
226
+ // Get sighash for a CET - the actual message that gets signed
227
+ [Throws=DLCError]
228
+ sequence<u8> get_cet_sighash(
229
+ Transaction cet,
230
+ sequence<u8> funding_script_pubkey,
231
+ u64 fund_output_value
232
+ );
208
233
  };
209
234
 
210
235
  // Transaction representation
@@ -297,6 +322,25 @@ dictionary OracleInfo {
297
322
  sequence<sequence<u8>> nonces;
298
323
  };
299
324
 
325
+ // Debug info for CET adaptor signature inputs
326
+ // All the values that go into creating an adaptor signature
327
+ dictionary CetAdaptorSignatureDebugInfo {
328
+ // The sighash (32 bytes) - this is the message that gets signed
329
+ sequence<u8> sighash;
330
+ // The adaptor point (33 bytes compressed public key)
331
+ sequence<u8> adaptor_point;
332
+ // Input index (always 0 for CETs)
333
+ u32 input_index;
334
+ // The funding script pubkey used for sighash
335
+ sequence<u8> script_pubkey;
336
+ // The fund output value used for sighash
337
+ u64 value;
338
+ // The CET txid
339
+ string cet_txid;
340
+ // Raw CET bytes for verification
341
+ sequence<u8> cet_raw;
342
+ };
343
+
300
344
  // Error types for DLC operations
301
345
  [Error]
302
346
  enum DLCError {
@@ -9,13 +9,15 @@ use bitcoin::{
9
9
  TxOut as BtcTxOut, Txid, Witness,
10
10
  };
11
11
  use bitcoin::{Script, WPubkeyHash};
12
+ use ddk_dlc::secp_utils;
12
13
  use ddk_dlc::{
13
14
  self, dlc_input::DlcInputInfo as RustDlcInputInfo, DlcTransactions as RustDlcTransactions,
14
15
  OracleInfo as DlcOracleInfo, PartyParams as DlcPartyParams, Payout as DlcPayout,
15
16
  TxInputInfo as DlcTxInputInfo,
16
17
  };
17
18
  use secp256k1_zkp::{
18
- ecdsa::Signature as EcdsaSignature, Message, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey,
19
+ ecdsa::Signature as EcdsaSignature, Message, PublicKey, Scalar, Secp256k1, SecretKey,
20
+ XOnlyPublicKey,
19
21
  };
20
22
  use secp256k1_zkp::{schnorr::Signature as SchnorrSignature, All, EcdsaAdaptorSignature};
21
23
  use std::str::FromStr;
@@ -191,6 +193,31 @@ pub struct OracleInfo {
191
193
  pub nonces: Vec<Vec<u8>>,
192
194
  }
193
195
 
196
+ /// Debug info for CET adaptor signature inputs.
197
+ ///
198
+ /// Contains all the values that go into creating an adaptor signature,
199
+ /// useful for comparing with external signers during debugging.
200
+ ///
201
+ /// This struct is intentionally always available (not feature-gated)
202
+ /// to support production debugging scenarios.
203
+ #[derive(Clone)]
204
+ pub struct CetAdaptorSignatureDebugInfo {
205
+ /// The sighash (32 bytes) - this is the message that gets signed
206
+ pub sighash: Vec<u8>,
207
+ /// The adaptor point (33 bytes compressed public key)
208
+ pub adaptor_point: Vec<u8>,
209
+ /// Input index (always 0 for CETs)
210
+ pub input_index: u32,
211
+ /// The funding script pubkey used for sighash
212
+ pub script_pubkey: Vec<u8>,
213
+ /// The fund output value used for sighash
214
+ pub value: u64,
215
+ /// The CET txid
216
+ pub cet_txid: String,
217
+ /// Raw CET bytes for verification
218
+ pub cet_raw: Vec<u8>,
219
+ }
220
+
194
221
  // Conversion helpers
195
222
  pub fn btc_tx_to_transaction(tx: &BtcTransaction) -> Transaction {
196
223
  use bitcoin::consensus::Encodable;
@@ -828,6 +855,33 @@ fn vec_to_ecdsa_adaptor_signature(signature: Vec<u8>) -> Result<EcdsaAdaptorSign
828
855
  EcdsaAdaptorSignature::from_slice(&signature).map_err(|_| DLCError::InvalidSignature)
829
856
  }
830
857
 
858
+ fn signatures_to_secret(signatures: &[Vec<SchnorrSignature>]) -> Result<SecretKey, DLCError> {
859
+ let s_values = signatures
860
+ .iter()
861
+ .flatten()
862
+ .map(|x| match secp_utils::schnorrsig_decompose(x) {
863
+ Ok(v) => Ok(v.1),
864
+ Err(err) => Err(DLCError::Secp256k1Error(err.to_string())),
865
+ })
866
+ .collect::<Result<Vec<&[u8]>, DLCError>>()?;
867
+
868
+ if s_values.is_empty() {
869
+ return Err(DLCError::InvalidArgument(
870
+ "No signatures provided".to_string(),
871
+ ));
872
+ }
873
+
874
+ let secret = SecretKey::from_slice(s_values[0])
875
+ .map_err(|_| DLCError::InvalidArgument("Invalid signature".to_string()))?;
876
+
877
+ let result = s_values.iter().skip(1).fold(secret, |accum, s| {
878
+ let sec = SecretKey::from_slice(s).unwrap();
879
+ accum.add_tweak(&Scalar::from(sec)).unwrap()
880
+ });
881
+
882
+ Ok(result)
883
+ }
884
+
831
885
  pub fn create_cet_adaptor_sigs_from_oracle_info(
832
886
  cets: Vec<Transaction>,
833
887
  oracle_info: Vec<OracleInfo>,
@@ -1100,6 +1154,141 @@ pub fn create_cet_adaptor_points_from_oracle_info(
1100
1154
  Ok(adaptor_points)
1101
1155
  }
1102
1156
 
1157
+ pub fn extract_ecdsa_signature_from_oracle_signatures(
1158
+ oracle_signatures: Vec<Vec<u8>>,
1159
+ adaptor_signature: Vec<u8>,
1160
+ ) -> Result<Vec<u8>, DLCError> {
1161
+ // Convert oracle signatures to Schnorr signatures
1162
+ let oracle_sigs = oracle_signatures
1163
+ .iter()
1164
+ .map(|sig| vec_to_schnorr_signature(sig.as_slice()))
1165
+ .collect::<Result<Vec<_>, _>>()?;
1166
+
1167
+ // Extract the secret key from oracle signatures
1168
+ let adaptor_secret = signatures_to_secret(&[oracle_sigs])?;
1169
+
1170
+ // Convert adaptor signature to EcdsaAdaptorSignature
1171
+ let adaptor_sig = vec_to_ecdsa_adaptor_signature(adaptor_signature)?;
1172
+
1173
+ // Decrypt the adaptor signature to get the final ECDSA signature
1174
+ let ecdsa_sig = adaptor_sig
1175
+ .decrypt(&adaptor_secret)
1176
+ .map_err(|e| DLCError::Secp256k1Error(e.to_string()))?;
1177
+
1178
+ // Return the DER-encoded signature
1179
+ Ok(ecdsa_sig.serialize_der().to_vec())
1180
+ }
1181
+
1182
+ /// Get all the inputs that go into creating a CET adaptor signature.
1183
+ ///
1184
+ /// This debug function is intentionally always available (not feature-gated)
1185
+ /// to enable debugging signature mismatches in production environments where
1186
+ /// rebuilding with debug features may not be feasible.
1187
+ ///
1188
+ /// Use this to compare values with external signers (e.g., Fordefi) when
1189
+ /// debugging adaptor signature verification failures.
1190
+ ///
1191
+ /// Returns:
1192
+ /// - `sighash`: The 32-byte BIP143 sighash message that gets signed
1193
+ /// - `adaptor_point`: The 33-byte compressed adaptor public key
1194
+ /// - `input_index`: Always 0 for CETs
1195
+ /// - `script_pubkey`: The funding script used for sighash calculation
1196
+ /// - `value`: The fund output value used for sighash calculation
1197
+ /// - `cet_txid`: The CET transaction ID
1198
+ /// - `cet_raw`: Raw serialized CET bytes
1199
+ pub fn get_cet_adaptor_signature_inputs(
1200
+ cet: Transaction,
1201
+ oracle_info: Vec<OracleInfo>,
1202
+ funding_script_pubkey: Vec<u8>,
1203
+ fund_output_value: u64,
1204
+ msgs: Vec<Vec<Vec<u8>>>,
1205
+ ) -> Result<CetAdaptorSignatureDebugInfo, DLCError> {
1206
+ let btc_tx = transaction_to_btc_tx(&cet)?;
1207
+ let funding_script = Script::from_bytes(&funding_script_pubkey);
1208
+
1209
+ // Convert oracle info
1210
+ let oracle_infos: Vec<DlcOracleInfo> = oracle_info
1211
+ .iter()
1212
+ .map(|info| {
1213
+ let public_key = XOnlyPublicKey::from_slice(&info.public_key)
1214
+ .map_err(|_| DLCError::InvalidPublicKey)?;
1215
+ let nonces = info
1216
+ .nonces
1217
+ .iter()
1218
+ .map(|nonce| XOnlyPublicKey::from_slice(nonce))
1219
+ .collect::<Result<Vec<_>, _>>()
1220
+ .map_err(|_| DLCError::InvalidArgument("Invalid nonce pubkey".to_string()))?;
1221
+ Ok(DlcOracleInfo { public_key, nonces })
1222
+ })
1223
+ .collect::<Result<Vec<_>, DLCError>>()?;
1224
+
1225
+ // Convert messages
1226
+ let cet_msgs: Vec<Vec<Message>> = msgs
1227
+ .into_iter()
1228
+ .map(|outcome_msgs| {
1229
+ outcome_msgs
1230
+ .iter()
1231
+ .map(|m| {
1232
+ Message::from_digest_slice(m)
1233
+ .map_err(|_| DLCError::InvalidArgument("Invalid message".to_string()))
1234
+ })
1235
+ .collect::<Result<Vec<_>, _>>()
1236
+ })
1237
+ .collect::<Result<Vec<_>, _>>()?;
1238
+
1239
+ let secp = get_secp_context();
1240
+
1241
+ // Get the adaptor point
1242
+ let adaptor_point = ddk_dlc::get_adaptor_point_from_oracle_info(secp, &oracle_infos, &cet_msgs)
1243
+ .map_err(|e| DLCError::Secp256k1Error(e.to_string()))?;
1244
+
1245
+ // Get the sighash - this is the actual message being signed
1246
+ let sig_hash = ddk_dlc::util::get_sig_hash_msg(
1247
+ &btc_tx,
1248
+ 0, // input_index is always 0 for CETs
1249
+ funding_script,
1250
+ Amount::from_sat(fund_output_value),
1251
+ )
1252
+ .map_err(DLCError::from)?;
1253
+
1254
+ Ok(CetAdaptorSignatureDebugInfo {
1255
+ sighash: sig_hash.as_ref().to_vec(),
1256
+ adaptor_point: adaptor_point.serialize().to_vec(),
1257
+ input_index: 0,
1258
+ script_pubkey: funding_script_pubkey,
1259
+ value: fund_output_value,
1260
+ cet_txid: btc_tx.compute_txid().to_string(),
1261
+ cet_raw: cet.raw_bytes,
1262
+ })
1263
+ }
1264
+
1265
+ /// Get the sighash for a CET - the actual 32-byte message that gets signed.
1266
+ ///
1267
+ /// This debug function is intentionally always available (not feature-gated)
1268
+ /// to enable debugging sighash mismatches in production environments where
1269
+ /// rebuilding with debug features may not be feasible.
1270
+ ///
1271
+ /// Use this to compare sighash values with external signers (e.g., Fordefi)
1272
+ /// when debugging signature verification failures.
1273
+ pub fn get_cet_sighash(
1274
+ cet: Transaction,
1275
+ funding_script_pubkey: Vec<u8>,
1276
+ fund_output_value: u64,
1277
+ ) -> Result<Vec<u8>, DLCError> {
1278
+ let btc_tx = transaction_to_btc_tx(&cet)?;
1279
+ let funding_script = Script::from_bytes(&funding_script_pubkey);
1280
+
1281
+ let sig_hash = ddk_dlc::util::get_sig_hash_msg(
1282
+ &btc_tx,
1283
+ 0, // input_index is always 0 for CETs
1284
+ funding_script,
1285
+ Amount::from_sat(fund_output_value),
1286
+ )
1287
+ .map_err(DLCError::from)?;
1288
+
1289
+ Ok(sig_hash.as_ref().to_vec())
1290
+ }
1291
+
1103
1292
  pub fn convert_mnemonic_to_seed(
1104
1293
  mnemonic: String,
1105
1294
  passphrase: Option<String>,
@@ -1735,7 +1924,7 @@ mod tests {
1735
1924
  let mut rng = secp256k1_zkp::rand::thread_rng();
1736
1925
  let (offer_party_params, offer_fund_sk) =
1737
1926
  get_party_params(1_000_000_000, 100_000_000, None);
1738
- let (accept_party_params, accept_fund_sk) =
1927
+ let (accept_party_params, _accept_fund_sk) =
1739
1928
  get_party_params(1_000_000_000, 100_000_000, None);
1740
1929
 
1741
1930
  let dlc_txs = create_dlc_transactions(
@@ -1837,7 +2026,7 @@ mod tests {
1837
2026
  cets[0].clone(),
1838
2027
  cet_sigs[0].signature.clone(),
1839
2028
  oracle_signatures[0].clone(),
1840
- accept_fund_sk.secret_bytes().to_vec(),
2029
+ _accept_fund_sk.secret_bytes().to_vec(),
1841
2030
  offer_party_params.fund_pubkey.clone(),
1842
2031
  accept_party_params.fund_pubkey.clone(),
1843
2032
  fund_output_value,
@@ -1886,4 +2075,368 @@ mod tests {
1886
2075
  )
1887
2076
  .expect("Invalid decrypted adaptor signature");
1888
2077
  }
2078
+
2079
+ #[test]
2080
+ fn test_extract_ecdsa_signature_from_oracle_signatures() {
2081
+ // Setup test data (similar to the main test)
2082
+ let secp = Secp256k1::new();
2083
+ let mut rng = secp256k1_zkp::rand::thread_rng();
2084
+ let (offer_party_params, offer_fund_sk) =
2085
+ get_party_params(1_000_000_000, 100_000_000, None);
2086
+ let (accept_party_params, _accept_fund_sk) =
2087
+ get_party_params(1_000_000_000, 100_000_000, None);
2088
+
2089
+ let dlc_txs = create_dlc_transactions(
2090
+ payouts_test(),
2091
+ offer_party_params.clone(),
2092
+ accept_party_params.clone(),
2093
+ 100,
2094
+ 4,
2095
+ 10,
2096
+ 10,
2097
+ 0, // Add missing fund_output_serial_id parameter
2098
+ )
2099
+ .unwrap();
2100
+
2101
+ let cets = dlc_txs.cets;
2102
+ const NB_ORACLES: usize = 1; // 1 oracle
2103
+ const NB_OUTCOMES: usize = 3; // 3 outcomes (enumeration)
2104
+ const NB_DIGITS: usize = 1; // 1 nonce for enumeration contract
2105
+
2106
+ let mut oracle_infos: Vec<OracleInfo> = Vec::with_capacity(NB_ORACLES);
2107
+ let mut oracle_sks: Vec<Keypair> = Vec::with_capacity(NB_ORACLES);
2108
+ let mut oracle_sk_nonce: Vec<Vec<[u8; 32]>> = Vec::with_capacity(NB_ORACLES);
2109
+ let mut oracle_sigs: Vec<Vec<SchnorrSignature>> = Vec::with_capacity(NB_ORACLES);
2110
+
2111
+ // Messages: 3 outcomes × 1 oracle × 1 message per outcome
2112
+ let messages: Vec<Vec<Vec<_>>> = (0..NB_OUTCOMES)
2113
+ .map(|outcome_idx| {
2114
+ vec![
2115
+ // Single oracle
2116
+ vec![
2117
+ // Single message for this outcome
2118
+ {
2119
+ let message = &[outcome_idx as u8]; // Different message per outcome
2120
+ let hash = sha256::Hash::hash(message).to_byte_array();
2121
+ hash.to_vec()
2122
+ },
2123
+ ],
2124
+ ]
2125
+ })
2126
+ .collect();
2127
+
2128
+ // Setup single oracle with single nonce
2129
+ for i in 0..NB_ORACLES {
2130
+ // Runs once
2131
+ let oracle_kp = Keypair::new(&secp, &mut rng);
2132
+ let oracle_pubkey = oracle_kp.x_only_public_key().0;
2133
+ let mut nonces: Vec<XOnlyPublicKey> = Vec::with_capacity(NB_DIGITS);
2134
+ let mut sk_nonces: Vec<[u8; 32]> = Vec::with_capacity(NB_DIGITS);
2135
+ oracle_sigs.push(Vec::with_capacity(NB_DIGITS));
2136
+
2137
+ // Single nonce for enumeration
2138
+ let mut sk_nonce = [0u8; 32];
2139
+ rng.fill_bytes(&mut sk_nonce);
2140
+ let oracle_r_kp = Keypair::from_seckey_slice(&secp, &sk_nonce).unwrap();
2141
+ let nonce = XOnlyPublicKey::from_keypair(&oracle_r_kp).0;
2142
+
2143
+ // Sign the first outcome's message with the single nonce
2144
+ let sig = secp_utils::schnorrsig_sign_with_nonce(
2145
+ &secp,
2146
+ &Message::from_digest_slice(&messages[0][0][0]).unwrap(), // First outcome, first oracle, first message
2147
+ &oracle_kp,
2148
+ &sk_nonce,
2149
+ );
2150
+
2151
+ oracle_sigs[i].push(sig);
2152
+ nonces.push(nonce);
2153
+ sk_nonces.push(sk_nonce);
2154
+
2155
+ oracle_infos.push(OracleInfo {
2156
+ public_key: oracle_pubkey.serialize().to_vec(),
2157
+ nonces: nonces.iter().map(|n| n.serialize().to_vec()).collect(), // Just 1 nonce
2158
+ });
2159
+ oracle_sk_nonce.push(sk_nonces);
2160
+ oracle_sks.push(oracle_kp);
2161
+ }
2162
+
2163
+ let funding_script_pubkey = ddk_dlc::make_funding_redeemscript(
2164
+ &PublicKey::from_slice(&offer_party_params.fund_pubkey.clone()).unwrap(),
2165
+ &PublicKey::from_slice(&accept_party_params.fund_pubkey.clone()).unwrap(),
2166
+ );
2167
+ let fund_output_value = dlc_txs.fund.outputs[0].value;
2168
+
2169
+ // Create adaptor signatures
2170
+ let cet_sigs = create_cet_adaptor_sigs_from_oracle_info(
2171
+ cets.clone(),
2172
+ oracle_infos.clone(),
2173
+ offer_fund_sk.secret_bytes().to_vec(),
2174
+ funding_script_pubkey.clone().into_bytes(),
2175
+ fund_output_value,
2176
+ messages.clone(),
2177
+ )
2178
+ .unwrap();
2179
+
2180
+ // Convert oracle signatures to the format expected by our function
2181
+ let oracle_signatures = oracle_sigs
2182
+ .iter()
2183
+ .map(|s| s.iter().map(|s| s.serialize().to_vec()).collect::<Vec<_>>())
2184
+ .collect::<Vec<_>>();
2185
+
2186
+ // Test our new function
2187
+ let result = extract_ecdsa_signature_from_oracle_signatures(
2188
+ oracle_signatures[0].clone(),
2189
+ cet_sigs[0].signature.clone(),
2190
+ );
2191
+
2192
+ assert!(result.is_ok(), "Function should succeed");
2193
+
2194
+ let ecdsa_sig_bytes = result.unwrap();
2195
+ assert!(
2196
+ !ecdsa_sig_bytes.is_empty(),
2197
+ "Should return non-empty signature"
2198
+ );
2199
+
2200
+ // Verify the signature is valid DER format
2201
+ let ecdsa_sig = EcdsaSignature::from_der(&ecdsa_sig_bytes);
2202
+ assert!(ecdsa_sig.is_ok(), "Should be valid DER signature");
2203
+ }
2204
+
2205
+ #[test]
2206
+ fn test_get_cet_sighash() {
2207
+ // Setup: Create DLC transactions to get a valid CET
2208
+ let (offer_party_params, _offer_fund_sk) =
2209
+ get_party_params(1_000_000_000, 100_000_000, None);
2210
+ let (accept_party_params, _accept_fund_sk) =
2211
+ get_party_params(1_000_000_000, 100_000_000, Some(2));
2212
+
2213
+ let dlc_txs = create_dlc_transactions(
2214
+ payouts_test(),
2215
+ offer_party_params.clone(),
2216
+ accept_party_params.clone(),
2217
+ 100,
2218
+ 4,
2219
+ 10,
2220
+ 10,
2221
+ 0,
2222
+ )
2223
+ .unwrap();
2224
+
2225
+ let cet = dlc_txs.cets[0].clone();
2226
+ let funding_script_pubkey = ddk_dlc::make_funding_redeemscript(
2227
+ &PublicKey::from_slice(&offer_party_params.fund_pubkey).unwrap(),
2228
+ &PublicKey::from_slice(&accept_party_params.fund_pubkey).unwrap(),
2229
+ );
2230
+ let fund_output_value = dlc_txs.fund.outputs[0].value;
2231
+
2232
+ // Act: Get the sighash
2233
+ let result = get_cet_sighash(
2234
+ cet.clone(),
2235
+ funding_script_pubkey.clone().into_bytes(),
2236
+ fund_output_value,
2237
+ );
2238
+
2239
+ // Assert
2240
+ assert!(result.is_ok(), "get_cet_sighash should succeed");
2241
+ let sighash = result.unwrap();
2242
+ assert_eq!(sighash.len(), 32, "Sighash should be 32 bytes");
2243
+
2244
+ // Verify against direct ddk-dlc call
2245
+ let btc_tx = transaction_to_btc_tx(&cet).unwrap();
2246
+ let direct_sighash = ddk_dlc::util::get_sig_hash_msg(
2247
+ &btc_tx,
2248
+ 0,
2249
+ Script::from_bytes(&funding_script_pubkey.clone().into_bytes()),
2250
+ Amount::from_sat(fund_output_value),
2251
+ )
2252
+ .unwrap();
2253
+
2254
+ assert_eq!(
2255
+ sighash,
2256
+ direct_sighash.as_ref().to_vec(),
2257
+ "Sighash should match direct ddk-dlc calculation"
2258
+ );
2259
+ }
2260
+
2261
+ #[test]
2262
+ fn test_get_cet_adaptor_signature_inputs() {
2263
+ // Setup: Create DLC transactions and oracle info
2264
+ let secp = Secp256k1::new();
2265
+ let mut rng = secp256k1_zkp::rand::thread_rng();
2266
+ let (offer_party_params, _offer_fund_sk) =
2267
+ get_party_params(1_000_000_000, 100_000_000, None);
2268
+ let (accept_party_params, _accept_fund_sk) =
2269
+ get_party_params(1_000_000_000, 100_000_000, Some(2));
2270
+
2271
+ let dlc_txs = create_dlc_transactions(
2272
+ payouts_test(),
2273
+ offer_party_params.clone(),
2274
+ accept_party_params.clone(),
2275
+ 100,
2276
+ 4,
2277
+ 10,
2278
+ 10,
2279
+ 0,
2280
+ )
2281
+ .unwrap();
2282
+
2283
+ let cet = dlc_txs.cets[0].clone();
2284
+ let funding_script_pubkey = ddk_dlc::make_funding_redeemscript(
2285
+ &PublicKey::from_slice(&offer_party_params.fund_pubkey).unwrap(),
2286
+ &PublicKey::from_slice(&accept_party_params.fund_pubkey).unwrap(),
2287
+ );
2288
+ let fund_output_value = dlc_txs.fund.outputs[0].value;
2289
+
2290
+ // Create oracle info (single oracle, single nonce for enumeration)
2291
+ let oracle_kp = Keypair::new(&secp, &mut rng);
2292
+ let oracle_pubkey = oracle_kp.x_only_public_key().0;
2293
+ let mut sk_nonce = [0u8; 32];
2294
+ rng.fill_bytes(&mut sk_nonce);
2295
+ let oracle_r_kp = Keypair::from_seckey_slice(&secp, &sk_nonce).unwrap();
2296
+ let nonce = XOnlyPublicKey::from_keypair(&oracle_r_kp).0;
2297
+
2298
+ let oracle_info = vec![OracleInfo {
2299
+ public_key: oracle_pubkey.serialize().to_vec(),
2300
+ nonces: vec![nonce.serialize().to_vec()],
2301
+ }];
2302
+
2303
+ // Create message (first outcome)
2304
+ let message = &[0u8];
2305
+ let hash = sha256::Hash::hash(message).to_byte_array();
2306
+ let msgs = vec![vec![hash.to_vec()]]; // Single oracle, single message
2307
+
2308
+ // Act: Get debug info
2309
+ let result = get_cet_adaptor_signature_inputs(
2310
+ cet.clone(),
2311
+ oracle_info.clone(),
2312
+ funding_script_pubkey.clone().into_bytes(),
2313
+ fund_output_value,
2314
+ msgs.clone(),
2315
+ );
2316
+
2317
+ // Assert
2318
+ assert!(
2319
+ result.is_ok(),
2320
+ "get_cet_adaptor_signature_inputs should succeed"
2321
+ );
2322
+ let debug_info = result.unwrap();
2323
+
2324
+ // Verify sighash
2325
+ assert_eq!(debug_info.sighash.len(), 32, "Sighash should be 32 bytes");
2326
+ let expected_sighash = get_cet_sighash(
2327
+ cet.clone(),
2328
+ funding_script_pubkey.clone().into_bytes(),
2329
+ fund_output_value,
2330
+ )
2331
+ .unwrap();
2332
+ assert_eq!(
2333
+ debug_info.sighash, expected_sighash,
2334
+ "Sighash should match get_cet_sighash result"
2335
+ );
2336
+
2337
+ // Verify adaptor point
2338
+ assert_eq!(
2339
+ debug_info.adaptor_point.len(),
2340
+ 33,
2341
+ "Adaptor point should be 33 bytes (compressed pubkey)"
2342
+ );
2343
+
2344
+ // Verify input index is always 0 for CETs
2345
+ assert_eq!(
2346
+ debug_info.input_index, 0,
2347
+ "Input index should always be 0 for CETs"
2348
+ );
2349
+
2350
+ // Verify script_pubkey matches what we passed in
2351
+ assert_eq!(
2352
+ debug_info.script_pubkey,
2353
+ funding_script_pubkey.clone().into_bytes(),
2354
+ "Script pubkey should match input"
2355
+ );
2356
+
2357
+ // Verify value matches
2358
+ assert_eq!(
2359
+ debug_info.value, fund_output_value,
2360
+ "Value should match input"
2361
+ );
2362
+
2363
+ // Verify cet_txid is valid
2364
+ let btc_tx = transaction_to_btc_tx(&cet).unwrap();
2365
+ assert_eq!(
2366
+ debug_info.cet_txid,
2367
+ btc_tx.compute_txid().to_string(),
2368
+ "CET txid should match"
2369
+ );
2370
+
2371
+ // Verify cet_raw matches input
2372
+ assert_eq!(
2373
+ debug_info.cet_raw, cet.raw_bytes,
2374
+ "CET raw bytes should match input"
2375
+ );
2376
+ }
2377
+
2378
+ #[test]
2379
+ fn test_get_cet_sighash_invalid_transaction() {
2380
+ // Create an invalid transaction (empty raw_bytes)
2381
+ let invalid_tx = Transaction {
2382
+ version: 2,
2383
+ lock_time: 0,
2384
+ inputs: vec![],
2385
+ outputs: vec![],
2386
+ raw_bytes: vec![0x00], // Invalid serialization
2387
+ };
2388
+
2389
+ let result = get_cet_sighash(invalid_tx, vec![0x00, 0x14], 100_000);
2390
+
2391
+ assert!(
2392
+ result.is_err(),
2393
+ "Should fail with invalid transaction bytes"
2394
+ );
2395
+ }
2396
+
2397
+ #[test]
2398
+ fn test_get_cet_adaptor_signature_inputs_invalid_oracle_pubkey() {
2399
+ // Setup valid CET
2400
+ let (offer_party_params, _) = get_party_params(1_000_000_000, 100_000_000, None);
2401
+ let (accept_party_params, _) = get_party_params(1_000_000_000, 100_000_000, Some(2));
2402
+
2403
+ let dlc_txs = create_dlc_transactions(
2404
+ payouts_test(),
2405
+ offer_party_params.clone(),
2406
+ accept_party_params.clone(),
2407
+ 100,
2408
+ 4,
2409
+ 10,
2410
+ 10,
2411
+ 0,
2412
+ )
2413
+ .unwrap();
2414
+
2415
+ let cet = dlc_txs.cets[0].clone();
2416
+ let funding_script_pubkey = ddk_dlc::make_funding_redeemscript(
2417
+ &PublicKey::from_slice(&offer_party_params.fund_pubkey).unwrap(),
2418
+ &PublicKey::from_slice(&accept_party_params.fund_pubkey).unwrap(),
2419
+ );
2420
+
2421
+ // Invalid oracle info (wrong pubkey length)
2422
+ let invalid_oracle_info = vec![OracleInfo {
2423
+ public_key: vec![0x00; 20], // Invalid: should be 32 bytes for x-only
2424
+ nonces: vec![vec![0x00; 32]],
2425
+ }];
2426
+
2427
+ let msgs = vec![vec![vec![0u8; 32]]];
2428
+
2429
+ let result = get_cet_adaptor_signature_inputs(
2430
+ cet,
2431
+ invalid_oracle_info,
2432
+ funding_script_pubkey.into_bytes(),
2433
+ 100_000,
2434
+ msgs,
2435
+ );
2436
+
2437
+ assert!(
2438
+ result.is_err(),
2439
+ "Should fail with invalid oracle public key"
2440
+ );
2441
+ }
1889
2442
  }
@@ -1 +1 @@
1
- {"version":3,"names":["getter","globalThis","NativeDdkFfi","_default","exports","default","isRustFutureContinuationCallbackTypeCompatible","isUniffiForeignFutureTypeCompatible"],"sourceRoot":"../../src","sources":["ddk_ffi-ffi.ts"],"mappings":";;;;;;AAAA;AACA;;AA4PA;AACA;AACA;AACA;AACA;AACA,MAAMA,MAAmC,GAAGA,CAAA,KACzCC,UAAU,CAASC,YAAY;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GACpBL,MAAM,EAErB;AAmHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMM,8CAGL,GAAG,IAAI;AACR,MAAMC,mCAGL,GAAG,IAAI","ignoreList":[]}
1
+ {"version":3,"names":["getter","globalThis","NativeDdkFfi","_default","exports","default","isRustFutureContinuationCallbackTypeCompatible","isUniffiForeignFutureTypeCompatible"],"sourceRoot":"../../src","sources":["ddk_ffi-ffi.ts"],"mappings":";;;;;;AAAA;AACA;;AAkRA;AACA;AACA;AACA;AACA;AACA,MAAMA,MAAmC,GAAGA,CAAA,KACzCC,UAAU,CAASC,YAAY;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GACpBL,MAAM,EAErB;AAmHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMM,8CAGL,GAAG,IAAI;AACR,MAAMC,mCAGL,GAAG,IAAI","ignoreList":[]}