plonky2/evm/tests/transfer_to_new_addr.rs
Daniel Lubarov 7557f320d4 Fix to add_eth
It was creating a new account with the hash of an empty storage trie, when really it should be a pointer to an empty storage trie. We can use 0 as this pointer since `@SEGMENT_TRIE_DATA[0] = 0 = @MPT_NODE_EMPTY`.

Also a couple tweaks that helped me debug, like moving the memory value range checks from the interpreter into `MemoryState`, so they're done in actual witness generation as well as interpreter tests.
2022-12-09 18:58:48 -08:00

117 lines
3.9 KiB
Rust

use std::collections::HashMap;
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::<F, D>::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(sender_state_key);
let to_nibbles = Nibbles::from(to_state_key);
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::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
timing.print();
let expected_state_trie_after = {
let sender_account_after = AccountRlp {
balance: sender_account_before.balance - value, // TODO: Also subtract gas_used * price.
// nonce: sender_account_before.nonce + 1, // TODO
..sender_account_before
};
let to_account_after = AccountRlp {
balance: value,
..AccountRlp::default()
};
let mut children = std::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(10u8) << 18)
}
fn init_logger() {
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
}