@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.
- package/cpp/ddk_ffi.cpp +124 -0
- package/cpp/ddk_ffi.hpp +6 -0
- package/ddk-ffi/Cargo.lock +131 -162
- package/ddk-ffi/Cargo.toml +1 -1
- package/ddk-ffi/src/ddk_ffi.udl +44 -0
- package/ddk-ffi/src/lib.rs +556 -3
- package/lib/commonjs/ddk_ffi-ffi.js.map +1 -1
- package/lib/commonjs/ddk_ffi.js +91 -1
- package/lib/commonjs/ddk_ffi.js.map +1 -1
- package/lib/module/ddk_ffi-ffi.js.map +1 -1
- package/lib/module/ddk_ffi.js +87 -0
- package/lib/module/ddk_ffi.js.map +1 -1
- package/lib/typescript/commonjs/src/ddk_ffi-ffi.d.ts +6 -0
- package/lib/typescript/commonjs/src/ddk_ffi-ffi.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ddk_ffi.d.ts +38 -0
- package/lib/typescript/commonjs/src/ddk_ffi.d.ts.map +1 -1
- package/lib/typescript/module/src/ddk_ffi-ffi.d.ts +6 -0
- package/lib/typescript/module/src/ddk_ffi-ffi.d.ts.map +1 -1
- package/lib/typescript/module/src/ddk_ffi.d.ts +38 -0
- package/lib/typescript/module/src/ddk_ffi.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ddk_ffi-ffi.ts +22 -0
- package/src/ddk_ffi.ts +187 -0
package/ddk-ffi/Cargo.toml
CHANGED
package/ddk-ffi/src/ddk_ffi.udl
CHANGED
|
@@ -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 {
|
package/ddk-ffi/src/lib.rs
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
-
|
|
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;;
|
|
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":[]}
|