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