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};
30use tempo_primitives::transaction::{KeychainSignature, PrimitiveSignature, TempoSignature};
31
32const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/";
34const PRIVATE_KEY_SIGNER_CACHE_LIMIT: usize = 64;
35
36impl Cheatcode for createWallet_0Call {
37 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
38 let Self { walletLabel } = self;
39 create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state)
40 }
41}
42
43impl Cheatcode for createWallet_1Call {
44 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
45 let Self { privateKey } = self;
46 create_wallet(privateKey, None, state)
47 }
48}
49
50impl Cheatcode for createWallet_2Call {
51 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
52 let Self { privateKey, walletLabel } = self;
53 create_wallet(privateKey, Some(walletLabel), state)
54 }
55}
56
57impl Cheatcode for sign_0Call {
58 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
59 let Self { wallet, digest } = self;
60 let sig = sign(&wallet.privateKey, digest)?;
61 Ok(encode_full_sig(sig))
62 }
63}
64
65impl Cheatcode for signWithNonceUnsafeCall {
66 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
67 let pk: U256 = self.privateKey;
68 let digest: B256 = self.digest;
69 let nonce: U256 = self.nonce;
70 let sig: alloy_primitives::Signature = sign_with_nonce(&pk, &digest, &nonce)?;
71 Ok(encode_full_sig(sig))
72 }
73}
74
75impl Cheatcode for signKeychainCall {
76 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
77 let Self { privateKey, account, digest } = self;
78 sign_keychain(state, privateKey, account, digest)
79 }
80}
81
82impl Cheatcode for signKeychainAdminCall {
83 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
84 let Self { privateKey, account, digest } = self;
85 sign_keychain(state, privateKey, account, digest)
86 }
87}
88
89impl Cheatcode for signCompact_0Call {
90 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
91 let Self { wallet, digest } = self;
92 let sig = sign(&wallet.privateKey, digest)?;
93 Ok(encode_compact_sig(sig))
94 }
95}
96
97impl Cheatcode for deriveKey_0Call {
98 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
99 let Self { mnemonic, index } = self;
100 derive_key::<English>(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index)
101 }
102}
103
104impl Cheatcode for deriveKey_1Call {
105 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
106 let Self { mnemonic, derivationPath, index } = self;
107 derive_key::<English>(mnemonic, derivationPath, *index)
108 }
109}
110
111impl Cheatcode for deriveKey_2Call {
112 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
113 let Self { mnemonic, index, language } = self;
114 derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language)
115 }
116}
117
118impl Cheatcode for deriveKey_3Call {
119 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
120 let Self { mnemonic, derivationPath, index, language } = self;
121 derive_key_str(mnemonic, derivationPath, *index, language)
122 }
123}
124
125impl Cheatcode for rememberKeyCall {
126 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
127 let Self { privateKey } = self;
128 let wallet = with_private_key_signer(state, privateKey, |wallet| Ok(wallet.clone()))?;
129 let address = inject_wallet(state, wallet);
130 Ok(address.abi_encode())
131 }
132}
133
134impl Cheatcode for rememberKeys_0Call {
135 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
136 let Self { mnemonic, derivationPath, count } = self;
137 let wallets = derive_wallets::<English>(mnemonic, derivationPath, *count)?;
138 let mut addresses = Vec::<Address>::with_capacity(wallets.len());
139 for wallet in wallets {
140 let addr = inject_wallet(state, wallet);
141 addresses.push(addr);
142 }
143
144 Ok(addresses.abi_encode())
145 }
146}
147
148impl Cheatcode for rememberKeys_1Call {
149 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
150 let Self { mnemonic, derivationPath, language, count } = self;
151 let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?;
152 let mut addresses = Vec::<Address>::with_capacity(wallets.len());
153 for wallet in wallets {
154 let addr = inject_wallet(state, wallet);
155 addresses.push(addr);
156 }
157
158 Ok(addresses.abi_encode())
159 }
160}
161
162fn inject_wallet<FEN: FoundryEvmNetwork>(
163 state: &mut Cheatcodes<FEN>,
164 wallet: LocalSigner<SigningKey>,
165) -> Address {
166 let address = wallet.address();
167 state.wallets().add_local_signer(wallet);
168 address
169}
170
171impl Cheatcode for sign_1Call {
172 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
173 let Self { privateKey, digest } = self;
174 let sig = sign_cached(state, privateKey, digest)?;
175 Ok(encode_full_sig(sig))
176 }
177}
178
179impl Cheatcode for signCompact_1Call {
180 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
181 let Self { privateKey, digest } = self;
182 let sig = sign_cached(state, privateKey, digest)?;
183 Ok(encode_compact_sig(sig))
184 }
185}
186
187impl Cheatcode for sign_2Call {
188 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
189 let Self { digest } = self;
190 let sig = sign_with_wallet(state, None, digest)?;
191 Ok(encode_full_sig(sig))
192 }
193}
194
195impl Cheatcode for signCompact_2Call {
196 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
197 let Self { digest } = self;
198 let sig = sign_with_wallet(state, None, digest)?;
199 Ok(encode_compact_sig(sig))
200 }
201}
202
203impl Cheatcode for sign_3Call {
204 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
205 let Self { signer, digest } = self;
206 let sig = sign_with_wallet(state, Some(*signer), digest)?;
207 Ok(encode_full_sig(sig))
208 }
209}
210
211impl Cheatcode for signCompact_3Call {
212 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
213 let Self { signer, digest } = self;
214 let sig = sign_with_wallet(state, Some(*signer), digest)?;
215 Ok(encode_compact_sig(sig))
216 }
217}
218
219impl Cheatcode for signP256Call {
220 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
221 let Self { privateKey, digest } = self;
222 sign_p256(privateKey, digest)
223 }
224}
225
226impl Cheatcode for publicKeyP256Call {
227 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
228 let Self { privateKey } = self;
229 let pub_key =
230 parse_private_key_p256(privateKey)?.verifying_key().as_affine().to_encoded_point(false);
231 let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
232 let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
233
234 Ok((pub_key_x, pub_key_y).abi_encode())
235 }
236}
237
238impl Cheatcode for createEd25519KeyCall {
239 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
240 let Self { salt } = self;
241 create_ed25519_key(salt)
242 }
243}
244
245impl Cheatcode for publicKeyEd25519Call {
246 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
247 let Self { privateKey } = self;
248 public_key_ed25519(privateKey)
249 }
250}
251
252impl Cheatcode for signEd25519Call {
253 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
254 let Self { namespace, message, privateKey } = self;
255 sign_ed25519(namespace, message, privateKey)
256 }
257}
258
259impl Cheatcode for verifyEd25519Call {
260 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
261 let Self { signature, namespace, message, publicKey } = self;
262 verify_ed25519(signature, namespace, message, publicKey)
263 }
264}
265
266fn create_wallet<FEN: FoundryEvmNetwork>(
271 private_key: &U256,
272 label: Option<&str>,
273 state: &mut Cheatcodes<FEN>,
274) -> Result {
275 let (addr, pub_key_x, pub_key_y) = with_private_key_signer(state, private_key, |wallet| {
276 let addr = wallet.address();
277 let pub_key = wallet.credential().verifying_key().as_affine().to_encoded_point(false);
278 let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
279 let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
280 Ok((addr, pub_key_x, pub_key_y))
281 })?;
282
283 if let Some(label) = label {
284 state.labels.insert(addr, label.into());
285 }
286
287 Ok(Wallet { addr, publicKeyX: pub_key_x, publicKeyY: pub_key_y, privateKey: *private_key }
288 .abi_encode())
289}
290
291fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
292 let v = U256::from(sig.v_byte());
294 let r = B256::from(sig.r());
295 let s = B256::from(sig.s());
296 (v, r, s).abi_encode()
297}
298
299fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
300 let r = B256::from(sig.r());
302 let mut vs = sig.s();
303 vs.set_bit(255, sig.v());
304 (r, vs).abi_encode()
305}
306
307fn sign(private_key: &U256, digest: &B256) -> Result<alloy_primitives::Signature> {
308 let wallet = parse_wallet(private_key)?;
310 let sig = wallet.sign_hash_sync(digest)?;
311 debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address());
312 Ok(sig)
313}
314
315fn sign_cached<FEN: FoundryEvmNetwork>(
316 state: &mut Cheatcodes<FEN>,
317 private_key: &U256,
318 digest: &B256,
319) -> Result<alloy_primitives::Signature> {
320 with_private_key_signer(state, private_key, |wallet| {
321 let sig = wallet.sign_hash_sync(digest)?;
322 debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address());
323 Ok(sig)
324 })
325}
326
327fn sign_keychain<FEN: FoundryEvmNetwork>(
328 state: &mut Cheatcodes<FEN>,
329 private_key: &U256,
330 account: &Address,
331 digest: &B256,
332) -> Result {
333 let signing_hash = KeychainSignature::signing_hash(*digest, *account);
334 let inner = sign_cached(state, private_key, &signing_hash)?;
335 let signature = TempoSignature::Keychain(KeychainSignature::new(
336 *account,
337 PrimitiveSignature::Secp256k1(inner),
338 ));
339 Ok(signature.to_bytes().abi_encode())
340}
341
342fn sign_with_nonce(
353 private_key: &U256,
354 digest: &B256,
355 nonce: &U256,
356) -> Result<alloy_primitives::Signature> {
357 let d_scalar: Scalar =
358 <Scalar as k256::elliptic_curve::PrimeField>::from_repr(private_key.to_be_bytes().into())
359 .into_option()
360 .ok_or_else(|| fmt_err!("invalid private key scalar"))?;
361 if bool::from(d_scalar.is_zero()) {
362 return Err(fmt_err!("private key cannot be 0"));
363 }
364
365 let k_scalar: Scalar =
366 <Scalar as k256::elliptic_curve::PrimeField>::from_repr(nonce.to_be_bytes().into())
367 .into_option()
368 .ok_or_else(|| fmt_err!("invalid nonce scalar"))?;
369 if bool::from(k_scalar.is_zero()) {
370 return Err(fmt_err!("nonce cannot be 0"));
371 }
372
373 let mut z = [0u8; 32];
374 z.copy_from_slice(digest.as_slice());
375 let z_fb: FieldBytes = FieldBytes::from(z);
376
377 let (sig_raw, recid_opt) =
380 <Scalar as hazmat::SignPrimitive<k256::Secp256k1>>::try_sign_prehashed(
381 &d_scalar, k_scalar, &z_fb,
382 )
383 .map_err(|e| fmt_err!("sign_prehashed failed: {e}"))?;
384
385 let (sig_low, flipped) =
387 if let Some(norm) = sig_raw.normalize_s() { (norm, true) } else { (sig_raw, false) };
388
389 let r_u256 = U256::from_be_bytes(sig_low.r().to_bytes().into());
390 let s_u256 = U256::from_be_bytes(sig_low.s().to_bytes().into());
391
392 let v_parity = if let Some(id) = recid_opt {
394 let mut v = id.to_byte() & 1;
395 if flipped {
396 v ^= 1;
397 }
398 v
399 } else {
400 let expected_addr = {
402 let sk: SigningKey = parse_private_key(private_key)?;
403 alloy_signer::utils::secret_key_to_address(&sk)
404 };
405 let cand0 = alloy_primitives::Signature::new(r_u256, s_u256, false);
407 if cand0.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
408 return Ok(cand0);
409 }
410 let cand1 = alloy_primitives::Signature::new(r_u256, s_u256, true);
412 if cand1.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
413 return Ok(cand1);
414 }
415 return Err(fmt_err!("failed to determine recovery id for signature"));
416 };
417
418 let y_parity = v_parity != 0;
419 Ok(alloy_primitives::Signature::new(r_u256, s_u256, y_parity))
420}
421
422fn sign_with_wallet<FEN: FoundryEvmNetwork>(
423 state: &mut Cheatcodes<FEN>,
424 signer: Option<Address>,
425 digest: &B256,
426) -> Result<alloy_primitives::Signature> {
427 if state.wallets().is_empty() {
428 bail!("no wallets available");
429 }
430
431 let mut wallets = state.wallets().inner.lock();
432 let maybe_provided_sender = wallets.provided_sender;
433 let signers = wallets.multi_wallet.signers()?;
434
435 let signer = if let Some(signer) = signer {
436 signer
437 } else if let Some(provided_sender) = maybe_provided_sender {
438 provided_sender
439 } else if signers.len() == 1 {
440 *signers.keys().next().unwrap()
441 } else {
442 bail!(
443 "could not determine signer, there are multiple signers available use vm.sign(signer, digest) to specify one"
444 );
445 };
446
447 let wallet = signers
448 .get(&signer)
449 .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?;
450
451 let sig = foundry_common::block_on(wallet.sign_hash(digest))?;
452 debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer);
453 Ok(sig)
454}
455
456fn sign_p256(private_key: &U256, digest: &B256) -> Result {
457 let signing_key = parse_private_key_p256(private_key)?;
458 let signature: P256Signature = signing_key.sign_prehash(digest.as_slice())?;
459 let signature = signature.normalize_s().unwrap_or(signature);
460 let r_bytes: [u8; 32] = signature.r().to_bytes().into();
461 let s_bytes: [u8; 32] = signature.s().to_bytes().into();
462
463 Ok((r_bytes, s_bytes).abi_encode())
464}
465
466fn validate_private_key<C: ecdsa::PrimeCurve>(private_key: &U256) -> Result<()> {
467 ensure!(*private_key != U256::ZERO, "private key cannot be 0");
468 let order = U256::from_be_slice(&C::ORDER.to_be_byte_array());
469 ensure!(
470 *private_key < order,
471 "private key must be less than the {curve:?} curve order ({order})",
472 curve = C::default(),
473 );
474
475 Ok(())
476}
477
478fn parse_private_key(private_key: &U256) -> Result<SigningKey> {
479 validate_private_key::<k256::Secp256k1>(private_key)?;
480 Ok(SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
481}
482
483fn parse_private_key_p256(private_key: &U256) -> Result<P256SigningKey> {
484 validate_private_key::<p256::NistP256>(private_key)?;
485 Ok(P256SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
486}
487
488fn parse_signing_key_ed25519(private_key: &B256) -> Result<Ed25519SigningKey> {
489 Ed25519SigningKey::try_from(private_key.as_slice())
490 .map_err(|e| fmt_err!("invalid Ed25519 private key: {e}"))
491}
492
493fn create_ed25519_key(salt: &B256) -> Result {
494 let signing_key = parse_signing_key_ed25519(salt)?;
495 let public_key = B256::from_slice(signing_key.verification_key().as_ref());
496 Ok((public_key, *salt).abi_encode())
497}
498
499fn public_key_ed25519(private_key: &B256) -> Result {
500 let signing_key = parse_signing_key_ed25519(private_key)?;
501 Ok(B256::from_slice(signing_key.verification_key().as_ref()).abi_encode())
502}
503
504fn sign_ed25519(namespace: &[u8], message: &[u8], private_key: &B256) -> Result {
505 let signing_key = parse_signing_key_ed25519(private_key)?;
506 let combined = [namespace, message].concat();
507 let signature: [u8; 64] = signing_key.sign(&combined).into();
508 Ok(signature.to_vec().abi_encode())
509}
510
511fn verify_ed25519(signature: &[u8], namespace: &[u8], message: &[u8], public_key: &B256) -> Result {
512 if signature.len() != 64 {
513 return Ok(false.abi_encode());
514 }
515
516 let Ok(verification_key) = Ed25519VerificationKey::try_from(public_key.as_slice()) else {
517 return Ok(false.abi_encode());
518 };
519
520 let Ok(sig_bytes): Result<[u8; 64], _> = signature.try_into() else {
521 return Ok(false.abi_encode());
522 };
523
524 let combined = [namespace, message].concat();
525 let valid = verification_key.verify(&Ed25519Signature::from(sig_bytes), &combined).is_ok();
526 Ok(valid.abi_encode())
527}
528
529pub(super) fn parse_wallet(private_key: &U256) -> Result<PrivateKeySigner> {
530 parse_private_key(private_key).map(PrivateKeySigner::from)
531}
532
533pub(super) fn with_private_key_signer<FEN: FoundryEvmNetwork, R>(
534 state: &mut Cheatcodes<FEN>,
535 private_key: &U256,
536 f: impl FnOnce(&PrivateKeySigner) -> Result<R>,
537) -> Result<R> {
538 if !state.private_key_signers.contains_key(private_key)
539 && state.private_key_signers.len() < PRIVATE_KEY_SIGNER_CACHE_LIMIT
540 {
541 let wallet = parse_wallet(private_key)?;
542 state.private_key_signers.insert(*private_key, wallet);
543 }
544
545 if let Some(wallet) = state.private_key_signers.get(private_key) {
546 f(wallet)
547 } else {
548 let wallet = parse_wallet(private_key)?;
549 f(&wallet)
550 }
551}
552
553fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result {
554 let private_key = derive_private_key_with_language(mnemonic, path, index, language)
555 .map_err(|e| fmt_err!("{e}"))?;
556 Ok(private_key.abi_encode())
557}
558
559fn derive_key<W: Wordlist>(mnemonic: &str, path: &str, index: u32) -> Result {
560 let private_key =
561 derive_private_key::<W>(mnemonic, path, index).map_err(|e| fmt_err!("{e}"))?;
562 Ok(private_key.abi_encode())
563}
564
565fn derive_wallets_str(
566 mnemonic: &str,
567 path: &str,
568 language: &str,
569 count: u32,
570) -> Result<Vec<LocalSigner<SigningKey>>> {
571 match language {
572 "chinese_simplified" => derive_wallets::<ChineseSimplified>(mnemonic, path, count),
573 "chinese_traditional" => derive_wallets::<ChineseTraditional>(mnemonic, path, count),
574 "czech" => derive_wallets::<Czech>(mnemonic, path, count),
575 "english" => derive_wallets::<English>(mnemonic, path, count),
576 "french" => derive_wallets::<French>(mnemonic, path, count),
577 "italian" => derive_wallets::<Italian>(mnemonic, path, count),
578 "japanese" => derive_wallets::<Japanese>(mnemonic, path, count),
579 "korean" => derive_wallets::<Korean>(mnemonic, path, count),
580 "portuguese" => derive_wallets::<Portuguese>(mnemonic, path, count),
581 "spanish" => derive_wallets::<Spanish>(mnemonic, path, count),
582 _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")),
583 }
584}
585
586fn derive_wallets<W: Wordlist>(
587 mnemonic: &str,
588 path: &str,
589 count: u32,
590) -> Result<Vec<LocalSigner<SigningKey>>> {
591 let mut out = path.to_string();
592
593 if !out.ends_with('/') {
594 out.push('/');
595 }
596
597 let mut wallets = Vec::with_capacity(count as usize);
598 for idx in 0..count {
599 let wallet = MnemonicBuilder::<W>::default()
600 .phrase(mnemonic)
601 .derivation_path(format!("{out}{idx}"))?
602 .build()?;
603 wallets.push(wallet);
604 }
605
606 Ok(wallets)
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612 use alloy_primitives::{FixedBytes, hex::FromHex};
613 use alloy_sol_types::SolCall;
614 use k256::elliptic_curve::Curve;
615 use p256::ecdsa::signature::hazmat::PrehashVerifier;
616 use tempo_contracts::precompiles::{IAccountKeychain, ISignatureVerifier};
617 use tempo_hardfork::TempoHardfork;
618 use tempo_precompiles::{
619 Precompile,
620 account_keychain::{AccountKeychain, KeyRestrictions, SignatureType},
621 signature_verifier::SignatureVerifier,
622 storage::{StorageCtx, hashmap::HashMapStorageProvider},
623 };
624
625 #[test]
626 fn test_sign_p256() {
627 use p256::ecdsa::VerifyingKey;
628
629 let pk_u256: U256 = "1".parse().unwrap();
630 let signing_key = P256SigningKey::from_bytes(&pk_u256.to_be_bytes().into()).unwrap();
631 let digest = FixedBytes::from_hex(
632 "0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56",
633 )
634 .unwrap();
635
636 let result = sign_p256(&pk_u256, &digest).unwrap();
637 let result_bytes: [u8; 64] = result.try_into().unwrap();
638 let signature = P256Signature::from_bytes(&result_bytes.into()).unwrap();
639 let verifying_key = VerifyingKey::from(&signing_key);
640 assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok());
641 }
642
643 #[test]
644 fn test_sign_p256_pk_too_large() {
645 let pk =
647 "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551".parse().unwrap();
648 let digest = FixedBytes::from_hex(
649 "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
650 )
651 .unwrap();
652 let result = sign_p256(&pk, &digest);
653 assert_eq!(
654 result.err().unwrap().to_string(),
655 "private key must be less than the NistP256 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"
656 );
657 }
658
659 #[test]
660 fn test_sign_p256_pk_0() {
661 let digest = FixedBytes::from_hex(
662 "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
663 )
664 .unwrap();
665 let result = sign_p256(&U256::ZERO, &digest);
666 assert_eq!(result.err().unwrap().to_string(), "private key cannot be 0");
667 }
668
669 #[test]
670 fn test_sign_with_nonce_varies_and_recovers() {
671 let pk_u256: U256 = U256::from(1u64);
673 let digest = FixedBytes::from_hex(
674 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
675 )
676 .unwrap();
677
678 let n1: U256 = U256::from(123u64);
680 let n2: U256 = U256::from(456u64);
681
682 let sig1 = sign_with_nonce(&pk_u256, &digest, &n1).expect("sig1");
684 let sig2 = sign_with_nonce(&pk_u256, &digest, &n2).expect("sig2");
685
686 assert!(
688 sig1.r() != sig2.r() || sig1.s() != sig2.s(),
689 "signatures should differ with different nonces"
690 );
691
692 let sk = parse_private_key(&pk_u256).unwrap();
694 let expected = alloy_signer::utils::secret_key_to_address(&sk);
695
696 assert_eq!(sig1.recover_address_from_prehash(&digest).unwrap(), expected);
697 assert_eq!(sig2.recover_address_from_prehash(&digest).unwrap(), expected);
698 }
699
700 #[test]
701 fn test_sign_with_nonce_zero_nonce_errors() {
702 let pk_u256: U256 = U256::from(1u64);
704 let digest = FixedBytes::from_hex(
705 "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
706 )
707 .unwrap();
708 let n0: U256 = U256::ZERO;
709
710 let err = sign_with_nonce(&pk_u256, &digest, &n0).unwrap_err();
711 let msg = err.to_string();
712 assert!(msg.contains("nonce cannot be 0"), "unexpected error: {msg}");
713 }
714
715 #[test]
716 fn test_sign_with_nonce_nonce_ge_order_errors() {
717 use k256::Secp256k1;
719 let n_u256 = U256::from_be_slice(&Secp256k1::ORDER.to_be_byte_array());
721
722 let pk_u256: U256 = U256::from(1u64);
723 let digest = FixedBytes::from_hex(
724 "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
725 )
726 .unwrap();
727
728 let err = sign_with_nonce(&pk_u256, &digest, &n_u256).unwrap_err();
730 let msg = err.to_string();
731 assert!(msg.contains("invalid nonce scalar"), "unexpected error: {msg}");
732 }
733
734 #[test]
735 fn test_sign_keychain_encodes_v2_signature_for_account() {
736 let private_key = U256::from(0xB0Bu64);
737 let account = Address::repeat_byte(0x11);
738 let digest = B256::from([0x22; 32]);
739 let mut state = Cheatcodes::default();
740
741 let result = sign_keychain(&mut state, &private_key, &account, &digest).unwrap();
742 let signature = Vec::<u8>::abi_decode(&result).unwrap();
743
744 assert_eq!(signature.len(), 86);
745 assert_eq!(signature[0], 0x04);
746 assert_eq!(Address::from_slice(&signature[1..21]), account);
747
748 let parsed = TempoSignature::from_bytes(&signature).unwrap();
749 assert!(parsed.is_v2_keychain());
750
751 let keychain = parsed.as_keychain().unwrap();
752 let expected_key = parse_wallet(&private_key).unwrap().address();
753 assert_eq!(keychain.user_address, account);
754 assert_eq!(keychain.key_id(&digest).unwrap(), expected_key);
755 }
756
757 #[test]
758 fn private_key_signers_are_cached_for_repeated_lookup() {
759 let private_key = U256::from(0xB0Bu64);
760 let mut state = Cheatcodes::default();
761
762 let first =
763 with_private_key_signer(&mut state, &private_key, |wallet| Ok(wallet.address()))
764 .unwrap();
765 assert_eq!(state.private_key_signers.len(), 1);
766
767 let second =
768 with_private_key_signer(&mut state, &private_key, |wallet| Ok(wallet.address()))
769 .unwrap();
770 assert_eq!(state.private_key_signers.len(), 1);
771 assert_eq!(first, second);
772 }
773
774 #[test]
775 fn test_sign_keychain_matches_t6_signature_verifier_state() {
776 let root_pk = U256::from(0xA11CEu64);
777 let access_pk = U256::from(0xB0Bu64);
778 let admin_pk = U256::from(0xC0FFEEu64);
779 let revoked_pk = U256::from(0xBADu64);
780 let expired_pk = U256::from(0xE441u64);
781 let unknown_pk = U256::from(0xFACEu64);
782
783 let root = parse_wallet(&root_pk).unwrap().address();
784 let access_key = parse_wallet(&access_pk).unwrap().address();
785 let admin_key = parse_wallet(&admin_pk).unwrap().address();
786 let revoked_key = parse_wallet(&revoked_pk).unwrap().address();
787 let expired_key = parse_wallet(&expired_pk).unwrap().address();
788
789 let hash = B256::from([0x44; 32]);
790 let admin_hash = B256::from([0x66; 32]);
791
792 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
793 storage.set_timestamp(U256::from(1_000u64));
794 StorageCtx::enter(&mut storage, || {
795 let mut keychain = AccountKeychain::new();
796 keychain.initialize()?;
797 keychain.set_tx_origin(root)?;
798
799 authorize_t6_access_key(&mut keychain, root, access_key, u64::MAX)?;
800 authorize_t6_access_key(&mut keychain, root, revoked_key, u64::MAX)?;
801 authorize_t6_access_key(&mut keychain, root, expired_key, 1_005)?;
802 keychain.authorize_admin_key(root, admin_key, SignatureType::Secp256k1, None)?;
803 keychain.revoke_key(root, IAccountKeychain::revokeKeyCall { keyId: revoked_key })?;
804
805 assert!(verify_keychain(root, hash, keychain_signature(&access_pk, root, hash)));
806 assert!(!verify_keychain(root, hash, keychain_signature(&revoked_pk, root, hash)));
807 assert!(!verify_keychain(root, hash, keychain_signature(&unknown_pk, root, hash)));
808 assert!(!verify_keychain(
809 Address::repeat_byte(0x99),
810 hash,
811 keychain_signature(&access_pk, root, hash)
812 ));
813 assert!(verify_keychain_admin(
814 root,
815 admin_hash,
816 keychain_signature(&admin_pk, root, admin_hash)
817 ));
818 assert!(verify_keychain_admin(
819 root,
820 admin_hash,
821 keychain_signature(&root_pk, root, admin_hash)
822 ));
823 assert!(!verify_keychain_admin(
824 root,
825 admin_hash,
826 keychain_signature(&access_pk, root, admin_hash)
827 ));
828 assert!(!verify_keychain_admin(
829 Address::repeat_byte(0x88),
830 admin_hash,
831 keychain_signature(&admin_pk, root, admin_hash)
832 ));
833 assert_keychain_signature_reverts(root, hash, vec![0x04]);
834
835 Ok::<_, eyre::Report>(())
836 })
837 .unwrap();
838
839 storage.set_timestamp(U256::from(1_006u64));
840 StorageCtx::enter(&mut storage, || {
841 assert!(!verify_keychain(root, hash, keychain_signature(&expired_pk, root, hash)));
842 Ok::<_, eyre::Report>(())
843 })
844 .unwrap();
845 }
846
847 fn authorize_t6_access_key(
848 keychain: &mut AccountKeychain,
849 account: Address,
850 key_id: Address,
851 expiry: u64,
852 ) -> eyre::Result<()> {
853 keychain.authorize_key(
854 account,
855 key_id,
856 SignatureType::Secp256k1,
857 KeyRestrictions {
858 expiry,
859 enforceLimits: false,
860 limits: vec![],
861 allowAnyCalls: true,
862 allowedCalls: vec![],
863 },
864 None,
865 )?;
866 Ok(())
867 }
868
869 fn keychain_signature(private_key: &U256, account: Address, hash: B256) -> Vec<u8> {
870 let mut state = Cheatcodes::default();
871 Vec::<u8>::abi_decode(&sign_keychain(&mut state, private_key, &account, &hash).unwrap())
872 .unwrap()
873 }
874
875 fn verify_keychain(account: Address, hash: B256, signature: Vec<u8>) -> bool {
876 let calldata =
877 ISignatureVerifier::verifyKeychainCall { account, hash, signature: signature.into() }
878 .abi_encode();
879
880 let output = SignatureVerifier::new().call(&calldata, Address::ZERO).unwrap();
881 assert!(!output.is_revert(), "verifyKeychain reverted: {:?}", output.bytes);
882 ISignatureVerifier::verifyKeychainCall::abi_decode_returns(&output.bytes).unwrap()
883 }
884
885 fn verify_keychain_admin(account: Address, hash: B256, signature: Vec<u8>) -> bool {
886 let calldata = ISignatureVerifier::verifyKeychainAdminCall {
887 account,
888 hash,
889 signature: signature.into(),
890 }
891 .abi_encode();
892
893 let output = SignatureVerifier::new().call(&calldata, Address::ZERO).unwrap();
894 assert!(!output.is_revert(), "verifyKeychainAdmin reverted: {:?}", output.bytes);
895 ISignatureVerifier::verifyKeychainAdminCall::abi_decode_returns(&output.bytes).unwrap()
896 }
897
898 fn assert_keychain_signature_reverts(account: Address, hash: B256, signature: Vec<u8>) {
899 let calldata =
900 ISignatureVerifier::verifyKeychainCall { account, hash, signature: signature.into() }
901 .abi_encode();
902
903 let output = SignatureVerifier::new().call(&calldata, Address::ZERO).unwrap();
904 assert!(output.is_revert(), "malformed keychain signature should revert");
905 }
906
907 #[test]
908 fn test_create_ed25519_key_determinism() {
909 let salt = B256::from([1u8; 32]);
910 let result1 = create_ed25519_key(&salt).unwrap();
911 let result2 = create_ed25519_key(&salt).unwrap();
912 assert_eq!(result1, result2, "same salt should produce same keys");
913 }
914
915 #[test]
916 fn test_create_ed25519_key_different_salts() {
917 let salt1 = B256::from([1u8; 32]);
918 let salt2 = B256::from([2u8; 32]);
919 let result1 = create_ed25519_key(&salt1).unwrap();
920 let result2 = create_ed25519_key(&salt2).unwrap();
921 assert_ne!(result1, result2, "different salts should produce different keys");
922 }
923
924 #[test]
925 fn test_public_key_ed25519_consistency() {
926 let salt = B256::from([42u8; 32]);
927 let create_result = create_ed25519_key(&salt).unwrap();
928 let (expected_public, private): (B256, B256) =
929 <(B256, B256)>::abi_decode(&create_result).unwrap();
930
931 let derived_public_result = public_key_ed25519(&private).unwrap();
932 let derived_public = B256::abi_decode(&derived_public_result).unwrap();
933
934 assert_eq!(expected_public, derived_public, "derived public key should match");
935 }
936
937 #[test]
938 fn test_sign_and_verify_ed25519_valid() {
939 let salt = B256::from([123u8; 32]);
940 let create_result = create_ed25519_key(&salt).unwrap();
941 let (public_key, private_key): (B256, B256) =
942 <(B256, B256)>::abi_decode(&create_result).unwrap();
943
944 let namespace = b"test.namespace";
945 let message = b"hello world";
946 let sig_result = sign_ed25519(namespace, message, &private_key).unwrap();
947 let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
948
949 let verify_result = verify_ed25519(&sig_bytes, namespace, message, &public_key).unwrap();
950 let valid = bool::abi_decode(&verify_result).unwrap();
951
952 assert!(valid, "signature should be valid");
953 }
954
955 #[test]
956 fn test_verify_ed25519_invalid_signature() {
957 let salt = B256::from([123u8; 32]);
958 let create_result = create_ed25519_key(&salt).unwrap();
959 let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
960
961 let invalid_sig = [0u8; 64];
962 let namespace = b"test.namespace";
963 let message = b"hello world";
964
965 let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
966 let valid = bool::abi_decode(&verify_result).unwrap();
967
968 assert!(!valid, "invalid signature should not verify");
969 }
970
971 #[test]
972 fn test_verify_ed25519_namespace_separation() {
973 let salt = B256::from([123u8; 32]);
974 let create_result = create_ed25519_key(&salt).unwrap();
975 let (public_key, private_key): (B256, B256) =
976 <(B256, B256)>::abi_decode(&create_result).unwrap();
977
978 let namespace_a = b"namespace.a";
979 let message = b"message";
980 let sig_result = sign_ed25519(namespace_a, message, &private_key).unwrap();
981 let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
982
983 let namespace_b = b"namespace.b";
984 let verify_result = verify_ed25519(&sig_bytes, namespace_b, message, &public_key).unwrap();
985 let valid = bool::abi_decode(&verify_result).unwrap();
986 assert!(!valid, "signature with namespace A should not verify with namespace B");
987
988 let verify_result = verify_ed25519(&sig_bytes, namespace_a, message, &public_key).unwrap();
989 let valid = bool::abi_decode(&verify_result).unwrap();
990 assert!(valid, "signature should verify with correct namespace");
991 }
992
993 #[test]
994 fn test_verify_ed25519_invalid_signature_length() {
995 let salt = B256::from([123u8; 32]);
996 let create_result = create_ed25519_key(&salt).unwrap();
997 let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
998
999 let invalid_sig = [0u8; 32];
1000 let namespace = b"test";
1001 let message = b"message";
1002
1003 let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
1004 let valid = bool::abi_decode(&verify_result).unwrap();
1005 assert!(!valid, "signature with wrong length should not verify");
1006 }
1007}