use std::collections::HashMap; use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; use ethereum_types::U256; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::AccountRlp; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::BlockMetadata; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; /// Test a simple token transfer to a new address. #[test] fn test_simple_transfer() -> anyhow::Result<()> { init_logger(); let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); let sender_state_key = keccak(sender); let to_state_key = keccak(to); let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); let value = U256::from(100u32); let sender_account_before = AccountRlp { nonce: 5.into(), balance: eth_to_wei(100_000.into()), storage_root: PartialTrie::Empty.calc_hash(), code_hash: keccak([]), }; let state_trie_before = PartialTrie::Leaf { nibbles: sender_nibbles, value: rlp::encode(&sender_account_before).to_vec(), }; let tries_before = TrieInputs { state_trie: state_trie_before, transactions_trie: PartialTrie::Empty, receipts_trie: PartialTrie::Empty, storage_tries: vec![], }; // Generated using a little py-evm script. let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); let block_metadata = BlockMetadata::default(); let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], tries: tries_before, contract_code: HashMap::new(), block_metadata, }; let mut timing = TimingTree::new("prove", log::Level::Debug); let proof = prove::(&all_stark, &config, inputs, &mut timing)?; timing.filter(Duration::from_millis(100)).print(); let expected_state_trie_after = { let sender_account_after = AccountRlp { // TODO: Should be 21k; 1k gas should be refunded. balance: sender_account_before.balance - value - 22_000 * 10, nonce: sender_account_before.nonce + 1, ..sender_account_before }; let to_account_after = AccountRlp { balance: value, ..AccountRlp::default() }; let mut children = core::array::from_fn(|_| PartialTrie::Empty.into()); children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(), } .into(); children[to_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf { nibbles: to_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&to_account_after).to_vec(), } .into(); PartialTrie::Branch { children, value: vec![], } }; assert_eq!( proof.public_values.trie_roots_after.state_root, expected_state_trie_after.calc_hash() ); verify_proof(&all_stark, proof, &config) } fn eth_to_wei(eth: U256) -> U256 { // 1 ether = 10^18 wei. eth * U256::from(10).pow(18.into()) } fn init_logger() { let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); }