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