1use crate::{Cheatcode, Cheatcodes, Result, Vm::*};
4use alloy_primitives::{Address, B256, U256, keccak256};
5use alloy_signer::{Signer, SignerSync};
6use alloy_signer_local::{
7 LocalSigner, MnemonicBuilder, PrivateKeySigner,
8 coins_bip39::{
9 ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean,
10 Portuguese, Spanish, Wordlist,
11 },
12};
13use alloy_sol_types::SolValue;
14use k256::{
15 FieldBytes, Scalar,
16 ecdsa::{SigningKey, hazmat},
17 elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint},
18};
19
20use p256::ecdsa::{
21 Signature as P256Signature, SigningKey as P256SigningKey, signature::hazmat::PrehashSigner,
22};
23
24use ed25519_consensus::{
25 Signature as Ed25519Signature, SigningKey as Ed25519SigningKey,
26 VerificationKey as Ed25519VerificationKey,
27};
28
29const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/";
31
32impl Cheatcode for createWallet_0Call {
33 fn apply(&self, state: &mut Cheatcodes) -> Result {
34 let Self { walletLabel } = self;
35 create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state)
36 }
37}
38
39impl Cheatcode for createWallet_1Call {
40 fn apply(&self, state: &mut Cheatcodes) -> Result {
41 let Self { privateKey } = self;
42 create_wallet(privateKey, None, state)
43 }
44}
45
46impl Cheatcode for createWallet_2Call {
47 fn apply(&self, state: &mut Cheatcodes) -> Result {
48 let Self { privateKey, walletLabel } = self;
49 create_wallet(privateKey, Some(walletLabel), state)
50 }
51}
52
53impl Cheatcode for sign_0Call {
54 fn apply(&self, _state: &mut Cheatcodes) -> Result {
55 let Self { wallet, digest } = self;
56 let sig = sign(&wallet.privateKey, digest)?;
57 Ok(encode_full_sig(sig))
58 }
59}
60
61impl Cheatcode for signWithNonceUnsafeCall {
62 fn apply(&self, _state: &mut Cheatcodes) -> Result {
63 let pk: U256 = self.privateKey;
64 let digest: B256 = self.digest;
65 let nonce: U256 = self.nonce;
66 let sig: alloy_primitives::Signature = sign_with_nonce(&pk, &digest, &nonce)?;
67 Ok(encode_full_sig(sig))
68 }
69}
70
71impl Cheatcode for signCompact_0Call {
72 fn apply(&self, _state: &mut Cheatcodes) -> Result {
73 let Self { wallet, digest } = self;
74 let sig = sign(&wallet.privateKey, digest)?;
75 Ok(encode_compact_sig(sig))
76 }
77}
78
79impl Cheatcode for deriveKey_0Call {
80 fn apply(&self, _state: &mut Cheatcodes) -> Result {
81 let Self { mnemonic, index } = self;
82 derive_key::<English>(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index)
83 }
84}
85
86impl Cheatcode for deriveKey_1Call {
87 fn apply(&self, _state: &mut Cheatcodes) -> Result {
88 let Self { mnemonic, derivationPath, index } = self;
89 derive_key::<English>(mnemonic, derivationPath, *index)
90 }
91}
92
93impl Cheatcode for deriveKey_2Call {
94 fn apply(&self, _state: &mut Cheatcodes) -> Result {
95 let Self { mnemonic, index, language } = self;
96 derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language)
97 }
98}
99
100impl Cheatcode for deriveKey_3Call {
101 fn apply(&self, _state: &mut Cheatcodes) -> Result {
102 let Self { mnemonic, derivationPath, index, language } = self;
103 derive_key_str(mnemonic, derivationPath, *index, language)
104 }
105}
106
107impl Cheatcode for rememberKeyCall {
108 fn apply(&self, state: &mut Cheatcodes) -> Result {
109 let Self { privateKey } = self;
110 let wallet = parse_wallet(privateKey)?;
111 let address = inject_wallet(state, wallet);
112 Ok(address.abi_encode())
113 }
114}
115
116impl Cheatcode for rememberKeys_0Call {
117 fn apply(&self, state: &mut Cheatcodes) -> Result {
118 let Self { mnemonic, derivationPath, count } = self;
119 let wallets = derive_wallets::<English>(mnemonic, derivationPath, *count)?;
120 let mut addresses = Vec::<Address>::with_capacity(wallets.len());
121 for wallet in wallets {
122 let addr = inject_wallet(state, wallet);
123 addresses.push(addr);
124 }
125
126 Ok(addresses.abi_encode())
127 }
128}
129
130impl Cheatcode for rememberKeys_1Call {
131 fn apply(&self, state: &mut Cheatcodes) -> Result {
132 let Self { mnemonic, derivationPath, language, count } = self;
133 let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?;
134 let mut addresses = Vec::<Address>::with_capacity(wallets.len());
135 for wallet in wallets {
136 let addr = inject_wallet(state, wallet);
137 addresses.push(addr);
138 }
139
140 Ok(addresses.abi_encode())
141 }
142}
143
144fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner<SigningKey>) -> Address {
145 let address = wallet.address();
146 state.wallets().add_local_signer(wallet);
147 address
148}
149
150impl Cheatcode for sign_1Call {
151 fn apply(&self, _state: &mut Cheatcodes) -> Result {
152 let Self { privateKey, digest } = self;
153 let sig = sign(privateKey, digest)?;
154 Ok(encode_full_sig(sig))
155 }
156}
157
158impl Cheatcode for signCompact_1Call {
159 fn apply(&self, _state: &mut Cheatcodes) -> Result {
160 let Self { privateKey, digest } = self;
161 let sig = sign(privateKey, digest)?;
162 Ok(encode_compact_sig(sig))
163 }
164}
165
166impl Cheatcode for sign_2Call {
167 fn apply(&self, state: &mut Cheatcodes) -> Result {
168 let Self { digest } = self;
169 let sig = sign_with_wallet(state, None, digest)?;
170 Ok(encode_full_sig(sig))
171 }
172}
173
174impl Cheatcode for signCompact_2Call {
175 fn apply(&self, state: &mut Cheatcodes) -> Result {
176 let Self { digest } = self;
177 let sig = sign_with_wallet(state, None, digest)?;
178 Ok(encode_compact_sig(sig))
179 }
180}
181
182impl Cheatcode for sign_3Call {
183 fn apply(&self, state: &mut Cheatcodes) -> Result {
184 let Self { signer, digest } = self;
185 let sig = sign_with_wallet(state, Some(*signer), digest)?;
186 Ok(encode_full_sig(sig))
187 }
188}
189
190impl Cheatcode for signCompact_3Call {
191 fn apply(&self, state: &mut Cheatcodes) -> Result {
192 let Self { signer, digest } = self;
193 let sig = sign_with_wallet(state, Some(*signer), digest)?;
194 Ok(encode_compact_sig(sig))
195 }
196}
197
198impl Cheatcode for signP256Call {
199 fn apply(&self, _state: &mut Cheatcodes) -> Result {
200 let Self { privateKey, digest } = self;
201 sign_p256(privateKey, digest)
202 }
203}
204
205impl Cheatcode for publicKeyP256Call {
206 fn apply(&self, _state: &mut Cheatcodes) -> Result {
207 let Self { privateKey } = self;
208 let pub_key =
209 parse_private_key_p256(privateKey)?.verifying_key().as_affine().to_encoded_point(false);
210 let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
211 let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
212
213 Ok((pub_key_x, pub_key_y).abi_encode())
214 }
215}
216
217impl Cheatcode for createEd25519KeyCall {
218 fn apply(&self, _state: &mut Cheatcodes) -> Result {
219 let Self { salt } = self;
220 create_ed25519_key(salt)
221 }
222}
223
224impl Cheatcode for publicKeyEd25519Call {
225 fn apply(&self, _state: &mut Cheatcodes) -> Result {
226 let Self { privateKey } = self;
227 public_key_ed25519(privateKey)
228 }
229}
230
231impl Cheatcode for signEd25519Call {
232 fn apply(&self, _state: &mut Cheatcodes) -> Result {
233 let Self { namespace, message, privateKey } = self;
234 sign_ed25519(namespace, message, privateKey)
235 }
236}
237
238impl Cheatcode for verifyEd25519Call {
239 fn apply(&self, _state: &mut Cheatcodes) -> Result {
240 let Self { signature, namespace, message, publicKey } = self;
241 verify_ed25519(signature, namespace, message, publicKey)
242 }
243}
244
245fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes) -> Result {
250 let key = parse_private_key(private_key)?;
251 let addr = alloy_signer::utils::secret_key_to_address(&key);
252
253 let pub_key = key.verifying_key().as_affine().to_encoded_point(false);
254 let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
255 let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
256
257 if let Some(label) = label {
258 state.labels.insert(addr, label.into());
259 }
260
261 Ok(Wallet { addr, publicKeyX: pub_key_x, publicKeyY: pub_key_y, privateKey: *private_key }
262 .abi_encode())
263}
264
265fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
266 let v = U256::from(sig.v() as u64 + 27);
268 let r = B256::from(sig.r());
269 let s = B256::from(sig.s());
270 (v, r, s).abi_encode()
271}
272
273fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
274 let r = B256::from(sig.r());
276 let mut vs = sig.s();
277 vs.set_bit(255, sig.v());
278 (r, vs).abi_encode()
279}
280
281fn sign(private_key: &U256, digest: &B256) -> Result<alloy_primitives::Signature> {
282 let wallet = parse_wallet(private_key)?;
284 let sig = wallet.sign_hash_sync(digest)?;
285 debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address());
286 Ok(sig)
287}
288
289fn sign_with_nonce(
300 private_key: &U256,
301 digest: &B256,
302 nonce: &U256,
303) -> Result<alloy_primitives::Signature> {
304 let d_scalar: Scalar =
305 <Scalar as k256::elliptic_curve::PrimeField>::from_repr(private_key.to_be_bytes().into())
306 .into_option()
307 .ok_or_else(|| fmt_err!("invalid private key scalar"))?;
308 if bool::from(d_scalar.is_zero()) {
309 return Err(fmt_err!("private key cannot be 0"));
310 }
311
312 let k_scalar: Scalar =
313 <Scalar as k256::elliptic_curve::PrimeField>::from_repr(nonce.to_be_bytes().into())
314 .into_option()
315 .ok_or_else(|| fmt_err!("invalid nonce scalar"))?;
316 if bool::from(k_scalar.is_zero()) {
317 return Err(fmt_err!("nonce cannot be 0"));
318 }
319
320 let mut z = [0u8; 32];
321 z.copy_from_slice(digest.as_slice());
322 let z_fb: FieldBytes = FieldBytes::from(z);
323
324 let (sig_raw, recid_opt) =
327 <Scalar as hazmat::SignPrimitive<k256::Secp256k1>>::try_sign_prehashed(
328 &d_scalar, k_scalar, &z_fb,
329 )
330 .map_err(|e| fmt_err!("sign_prehashed failed: {e}"))?;
331
332 let (sig_low, flipped) =
334 if let Some(norm) = sig_raw.normalize_s() { (norm, true) } else { (sig_raw, false) };
335
336 let r_u256 = U256::from_be_bytes(sig_low.r().to_bytes().into());
337 let s_u256 = U256::from_be_bytes(sig_low.s().to_bytes().into());
338
339 let v_parity = if let Some(id) = recid_opt {
341 let mut v = id.to_byte() & 1;
342 if flipped {
343 v ^= 1;
344 }
345 v
346 } else {
347 let expected_addr = {
349 let sk: SigningKey = parse_private_key(private_key)?;
350 alloy_signer::utils::secret_key_to_address(&sk)
351 };
352 let cand0 = alloy_primitives::Signature::new(r_u256, s_u256, false);
354 if cand0.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
355 return Ok(cand0);
356 }
357 let cand1 = alloy_primitives::Signature::new(r_u256, s_u256, true);
359 if cand1.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
360 return Ok(cand1);
361 }
362 return Err(fmt_err!("failed to determine recovery id for signature"));
363 };
364
365 let y_parity = v_parity != 0;
366 Ok(alloy_primitives::Signature::new(r_u256, s_u256, y_parity))
367}
368
369fn sign_with_wallet(
370 state: &mut Cheatcodes,
371 signer: Option<Address>,
372 digest: &B256,
373) -> Result<alloy_primitives::Signature> {
374 if state.wallets().is_empty() {
375 bail!("no wallets available");
376 }
377
378 let mut wallets = state.wallets().inner.lock();
379 let maybe_provided_sender = wallets.provided_sender;
380 let signers = wallets.multi_wallet.signers()?;
381
382 let signer = if let Some(signer) = signer {
383 signer
384 } else if let Some(provided_sender) = maybe_provided_sender {
385 provided_sender
386 } else if signers.len() == 1 {
387 *signers.keys().next().unwrap()
388 } else {
389 bail!(
390 "could not determine signer, there are multiple signers available use vm.sign(signer, digest) to specify one"
391 );
392 };
393
394 let wallet = signers
395 .get(&signer)
396 .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?;
397
398 let sig = foundry_common::block_on(wallet.sign_hash(digest))?;
399 debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer);
400 Ok(sig)
401}
402
403fn sign_p256(private_key: &U256, digest: &B256) -> Result {
404 let signing_key = parse_private_key_p256(private_key)?;
405 let signature: P256Signature = signing_key.sign_prehash(digest.as_slice())?;
406 let signature = signature.normalize_s().unwrap_or(signature);
407 let r_bytes: [u8; 32] = signature.r().to_bytes().into();
408 let s_bytes: [u8; 32] = signature.s().to_bytes().into();
409
410 Ok((r_bytes, s_bytes).abi_encode())
411}
412
413fn validate_private_key<C: ecdsa::PrimeCurve>(private_key: &U256) -> Result<()> {
414 ensure!(*private_key != U256::ZERO, "private key cannot be 0");
415 let order = U256::from_be_slice(&C::ORDER.to_be_byte_array());
416 ensure!(
417 *private_key < order,
418 "private key must be less than the {curve:?} curve order ({order})",
419 curve = C::default(),
420 );
421
422 Ok(())
423}
424
425fn parse_private_key(private_key: &U256) -> Result<SigningKey> {
426 validate_private_key::<k256::Secp256k1>(private_key)?;
427 Ok(SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
428}
429
430fn parse_private_key_p256(private_key: &U256) -> Result<P256SigningKey> {
431 validate_private_key::<p256::NistP256>(private_key)?;
432 Ok(P256SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
433}
434
435fn parse_signing_key_ed25519(private_key: &B256) -> Result<Ed25519SigningKey> {
436 Ed25519SigningKey::try_from(private_key.as_slice())
437 .map_err(|e| fmt_err!("invalid Ed25519 private key: {e}"))
438}
439
440fn create_ed25519_key(salt: &B256) -> Result {
441 let signing_key = parse_signing_key_ed25519(salt)?;
442 let public_key = B256::from_slice(signing_key.verification_key().as_ref());
443 Ok((public_key, *salt).abi_encode())
444}
445
446fn public_key_ed25519(private_key: &B256) -> Result {
447 let signing_key = parse_signing_key_ed25519(private_key)?;
448 Ok(B256::from_slice(signing_key.verification_key().as_ref()).abi_encode())
449}
450
451fn sign_ed25519(namespace: &[u8], message: &[u8], private_key: &B256) -> Result {
452 let signing_key = parse_signing_key_ed25519(private_key)?;
453 let combined = [namespace, message].concat();
454 let signature: [u8; 64] = signing_key.sign(&combined).into();
455 Ok(signature.to_vec().abi_encode())
456}
457
458fn verify_ed25519(signature: &[u8], namespace: &[u8], message: &[u8], public_key: &B256) -> Result {
459 if signature.len() != 64 {
460 return Ok(false.abi_encode());
461 }
462
463 let Ok(verification_key) = Ed25519VerificationKey::try_from(public_key.as_slice()) else {
464 return Ok(false.abi_encode());
465 };
466
467 let Ok(sig_bytes): Result<[u8; 64], _> = signature.try_into() else {
468 return Ok(false.abi_encode());
469 };
470
471 let combined = [namespace, message].concat();
472 let valid = verification_key.verify(&Ed25519Signature::from(sig_bytes), &combined).is_ok();
473 Ok(valid.abi_encode())
474}
475
476pub(super) fn parse_wallet(private_key: &U256) -> Result<PrivateKeySigner> {
477 parse_private_key(private_key).map(PrivateKeySigner::from)
478}
479
480fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result {
481 match language {
482 "chinese_simplified" => derive_key::<ChineseSimplified>(mnemonic, path, index),
483 "chinese_traditional" => derive_key::<ChineseTraditional>(mnemonic, path, index),
484 "czech" => derive_key::<Czech>(mnemonic, path, index),
485 "english" => derive_key::<English>(mnemonic, path, index),
486 "french" => derive_key::<French>(mnemonic, path, index),
487 "italian" => derive_key::<Italian>(mnemonic, path, index),
488 "japanese" => derive_key::<Japanese>(mnemonic, path, index),
489 "korean" => derive_key::<Korean>(mnemonic, path, index),
490 "portuguese" => derive_key::<Portuguese>(mnemonic, path, index),
491 "spanish" => derive_key::<Spanish>(mnemonic, path, index),
492 _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")),
493 }
494}
495
496fn derive_key<W: Wordlist>(mnemonic: &str, path: &str, index: u32) -> Result {
497 fn derive_key_path(path: &str, index: u32) -> String {
498 let mut out = path.to_string();
499 if !out.ends_with('/') {
500 out.push('/');
501 }
502 out.push_str(&index.to_string());
503 out
504 }
505
506 let wallet = MnemonicBuilder::<W>::default()
507 .phrase(mnemonic)
508 .derivation_path(derive_key_path(path, index))?
509 .build()?;
510 let private_key = U256::from_be_bytes(wallet.credential().to_bytes().into());
511 Ok(private_key.abi_encode())
512}
513
514fn derive_wallets_str(
515 mnemonic: &str,
516 path: &str,
517 language: &str,
518 count: u32,
519) -> Result<Vec<LocalSigner<SigningKey>>> {
520 match language {
521 "chinese_simplified" => derive_wallets::<ChineseSimplified>(mnemonic, path, count),
522 "chinese_traditional" => derive_wallets::<ChineseTraditional>(mnemonic, path, count),
523 "czech" => derive_wallets::<Czech>(mnemonic, path, count),
524 "english" => derive_wallets::<English>(mnemonic, path, count),
525 "french" => derive_wallets::<French>(mnemonic, path, count),
526 "italian" => derive_wallets::<Italian>(mnemonic, path, count),
527 "japanese" => derive_wallets::<Japanese>(mnemonic, path, count),
528 "korean" => derive_wallets::<Korean>(mnemonic, path, count),
529 "portuguese" => derive_wallets::<Portuguese>(mnemonic, path, count),
530 "spanish" => derive_wallets::<Spanish>(mnemonic, path, count),
531 _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")),
532 }
533}
534
535fn derive_wallets<W: Wordlist>(
536 mnemonic: &str,
537 path: &str,
538 count: u32,
539) -> Result<Vec<LocalSigner<SigningKey>>> {
540 let mut out = path.to_string();
541
542 if !out.ends_with('/') {
543 out.push('/');
544 }
545
546 let mut wallets = Vec::with_capacity(count as usize);
547 for idx in 0..count {
548 let wallet = MnemonicBuilder::<W>::default()
549 .phrase(mnemonic)
550 .derivation_path(format!("{out}{idx}"))?
551 .build()?;
552 wallets.push(wallet);
553 }
554
555 Ok(wallets)
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use alloy_primitives::{FixedBytes, hex::FromHex};
562 use k256::elliptic_curve::Curve;
563 use p256::ecdsa::signature::hazmat::PrehashVerifier;
564
565 #[test]
566 fn test_sign_p256() {
567 use p256::ecdsa::VerifyingKey;
568
569 let pk_u256: U256 = "1".parse().unwrap();
570 let signing_key = P256SigningKey::from_bytes(&pk_u256.to_be_bytes().into()).unwrap();
571 let digest = FixedBytes::from_hex(
572 "0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56",
573 )
574 .unwrap();
575
576 let result = sign_p256(&pk_u256, &digest).unwrap();
577 let result_bytes: [u8; 64] = result.try_into().unwrap();
578 let signature = P256Signature::from_bytes(&result_bytes.into()).unwrap();
579 let verifying_key = VerifyingKey::from(&signing_key);
580 assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok());
581 }
582
583 #[test]
584 fn test_sign_p256_pk_too_large() {
585 let pk =
587 "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551".parse().unwrap();
588 let digest = FixedBytes::from_hex(
589 "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
590 )
591 .unwrap();
592 let result = sign_p256(&pk, &digest);
593 assert_eq!(
594 result.err().unwrap().to_string(),
595 "private key must be less than the NistP256 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"
596 );
597 }
598
599 #[test]
600 fn test_sign_p256_pk_0() {
601 let digest = FixedBytes::from_hex(
602 "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
603 )
604 .unwrap();
605 let result = sign_p256(&U256::ZERO, &digest);
606 assert_eq!(result.err().unwrap().to_string(), "private key cannot be 0");
607 }
608
609 #[test]
610 fn test_sign_with_nonce_varies_and_recovers() {
611 let pk_u256: U256 = U256::from(1u64);
613 let digest = FixedBytes::from_hex(
614 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
615 )
616 .unwrap();
617
618 let n1: U256 = U256::from(123u64);
620 let n2: U256 = U256::from(456u64);
621
622 let sig1 = sign_with_nonce(&pk_u256, &digest, &n1).expect("sig1");
624 let sig2 = sign_with_nonce(&pk_u256, &digest, &n2).expect("sig2");
625
626 assert!(
628 sig1.r() != sig2.r() || sig1.s() != sig2.s(),
629 "signatures should differ with different nonces"
630 );
631
632 let sk = parse_private_key(&pk_u256).unwrap();
634 let expected = alloy_signer::utils::secret_key_to_address(&sk);
635
636 assert_eq!(sig1.recover_address_from_prehash(&digest).unwrap(), expected);
637 assert_eq!(sig2.recover_address_from_prehash(&digest).unwrap(), expected);
638 }
639
640 #[test]
641 fn test_sign_with_nonce_zero_nonce_errors() {
642 let pk_u256: U256 = U256::from(1u64);
644 let digest = FixedBytes::from_hex(
645 "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
646 )
647 .unwrap();
648 let n0: U256 = U256::ZERO;
649
650 let err = sign_with_nonce(&pk_u256, &digest, &n0).unwrap_err();
651 let msg = err.to_string();
652 assert!(msg.contains("nonce cannot be 0"), "unexpected error: {msg}");
653 }
654
655 #[test]
656 fn test_sign_with_nonce_nonce_ge_order_errors() {
657 use k256::Secp256k1;
659 let n_u256 = U256::from_be_slice(&Secp256k1::ORDER.to_be_byte_array());
661
662 let pk_u256: U256 = U256::from(1u64);
663 let digest = FixedBytes::from_hex(
664 "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
665 )
666 .unwrap();
667
668 let err = sign_with_nonce(&pk_u256, &digest, &n_u256).unwrap_err();
670 let msg = err.to_string();
671 assert!(msg.contains("invalid nonce scalar"), "unexpected error: {msg}");
672 }
673
674 #[test]
675 fn test_create_ed25519_key_determinism() {
676 let salt = B256::from([1u8; 32]);
677 let result1 = create_ed25519_key(&salt).unwrap();
678 let result2 = create_ed25519_key(&salt).unwrap();
679 assert_eq!(result1, result2, "same salt should produce same keys");
680 }
681
682 #[test]
683 fn test_create_ed25519_key_different_salts() {
684 let salt1 = B256::from([1u8; 32]);
685 let salt2 = B256::from([2u8; 32]);
686 let result1 = create_ed25519_key(&salt1).unwrap();
687 let result2 = create_ed25519_key(&salt2).unwrap();
688 assert_ne!(result1, result2, "different salts should produce different keys");
689 }
690
691 #[test]
692 fn test_public_key_ed25519_consistency() {
693 let salt = B256::from([42u8; 32]);
694 let create_result = create_ed25519_key(&salt).unwrap();
695 let (expected_public, private): (B256, B256) =
696 <(B256, B256)>::abi_decode(&create_result).unwrap();
697
698 let derived_public_result = public_key_ed25519(&private).unwrap();
699 let derived_public = B256::abi_decode(&derived_public_result).unwrap();
700
701 assert_eq!(expected_public, derived_public, "derived public key should match");
702 }
703
704 #[test]
705 fn test_sign_and_verify_ed25519_valid() {
706 let salt = B256::from([123u8; 32]);
707 let create_result = create_ed25519_key(&salt).unwrap();
708 let (public_key, private_key): (B256, B256) =
709 <(B256, B256)>::abi_decode(&create_result).unwrap();
710
711 let namespace = b"test.namespace";
712 let message = b"hello world";
713 let sig_result = sign_ed25519(namespace, message, &private_key).unwrap();
714 let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
715
716 let verify_result = verify_ed25519(&sig_bytes, namespace, message, &public_key).unwrap();
717 let valid = bool::abi_decode(&verify_result).unwrap();
718
719 assert!(valid, "signature should be valid");
720 }
721
722 #[test]
723 fn test_verify_ed25519_invalid_signature() {
724 let salt = B256::from([123u8; 32]);
725 let create_result = create_ed25519_key(&salt).unwrap();
726 let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
727
728 let invalid_sig = [0u8; 64];
729 let namespace = b"test.namespace";
730 let message = b"hello world";
731
732 let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
733 let valid = bool::abi_decode(&verify_result).unwrap();
734
735 assert!(!valid, "invalid signature should not verify");
736 }
737
738 #[test]
739 fn test_verify_ed25519_namespace_separation() {
740 let salt = B256::from([123u8; 32]);
741 let create_result = create_ed25519_key(&salt).unwrap();
742 let (public_key, private_key): (B256, B256) =
743 <(B256, B256)>::abi_decode(&create_result).unwrap();
744
745 let namespace_a = b"namespace.a";
746 let message = b"message";
747 let sig_result = sign_ed25519(namespace_a, message, &private_key).unwrap();
748 let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
749
750 let namespace_b = b"namespace.b";
751 let verify_result = verify_ed25519(&sig_bytes, namespace_b, message, &public_key).unwrap();
752 let valid = bool::abi_decode(&verify_result).unwrap();
753 assert!(!valid, "signature with namespace A should not verify with namespace B");
754
755 let verify_result = verify_ed25519(&sig_bytes, namespace_a, message, &public_key).unwrap();
756 let valid = bool::abi_decode(&verify_result).unwrap();
757 assert!(valid, "signature should verify with correct namespace");
758 }
759
760 #[test]
761 fn test_verify_ed25519_invalid_signature_length() {
762 let salt = B256::from([123u8; 32]);
763 let create_result = create_ed25519_key(&salt).unwrap();
764 let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
765
766 let invalid_sig = [0u8; 32];
767 let namespace = b"test";
768 let message = b"message";
769
770 let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
771 let valid = bool::abi_decode(&verify_result).unwrap();
772 assert!(!valid, "signature with wrong length should not verify");
773 }
774}