plonky2/evm/tests/basic_smart_contract.rs

174 lines
6.2 KiB
Rust
Raw Normal View History

2023-01-14 21:18:58 -08:00
use std::collections::HashMap;
use std::time::Duration;
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
2023-03-27 17:30:11 -06:00
use eth_trie_utils::nibbles::Nibbles;
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
2023-03-12 21:35:04 -07:00
use ethereum_types::{Address, U256};
2023-01-14 21:18:58 -08:00
use hex_literal::hex;
use keccak_hash::keccak;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::plonk::config::PoseidonGoldilocksConfig;
2023-01-14 21:18:58 -08:00
use plonky2::util::timing::TimingTree;
use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::cpu::kernel::opcodes::{get_opcode, get_push_opcode};
use plonky2_evm::generation::mpt::AccountRlp;
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::{BlockMetadata, TrieRoots};
2023-01-14 21:18:58 -08:00
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
use plonky2_evm::Node;
2023-01-14 21:18:58 -08:00
type F = GoldilocksField;
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
/// Test a simple token transfer to a new address.
#[test]
#[ignore] // Too slow to run on CI.
2023-01-14 21:18:58 -08:00
fn test_basic_smart_contract() -> anyhow::Result<()> {
init_logger();
let all_stark = AllStark::<F, D>::default();
let config = StarkConfig::standard_fast_config();
2023-03-12 21:35:04 -07:00
let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
2023-01-14 21:18:58 -08:00
let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23");
let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0");
2023-03-12 21:35:04 -07:00
let beneficiary_state_key = keccak(beneficiary);
2023-01-14 21:18:58 -08:00
let sender_state_key = keccak(sender);
let to_state_key = keccak(to);
2023-03-12 21:35:04 -07:00
let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap();
2023-01-14 21:18:58 -08:00
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 push1 = get_push_opcode(1);
let add = get_opcode("ADD");
let stop = get_opcode("STOP");
let code = [push1, 3, push1, 4, add, stop];
2023-03-10 08:54:26 -08:00
let code_gas = 3 + 3 + 3;
2023-01-14 21:18:58 -08:00
let code_hash = keccak(code);
2023-03-12 21:35:04 -07:00
let beneficiary_account_before = AccountRlp::default();
2023-01-14 21:18:58 -08:00
let sender_account_before = AccountRlp {
nonce: 5.into(),
balance: eth_to_wei(100_000.into()),
..AccountRlp::default()
};
let to_account_before = AccountRlp {
code_hash,
..AccountRlp::default()
};
let state_trie_before = {
2023-03-27 17:30:11 -06:00
let mut children = core::array::from_fn(|_| Node::Empty.into());
children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf {
2023-01-14 21:18:58 -08:00
nibbles: sender_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&sender_account_before).to_vec(),
}
.into();
2023-03-27 17:30:11 -06:00
children[to_nibbles.get_nibble(0) as usize] = Node::Leaf {
2023-01-14 21:18:58 -08:00
nibbles: to_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&to_account_before).to_vec(),
}
.into();
2023-03-27 17:30:11 -06:00
Node::Branch {
2023-01-14 21:18:58 -08:00
children,
value: vec![],
}
2023-03-27 17:30:11 -06:00
}
.into();
2023-01-14 21:18:58 -08:00
let tries_before = TrieInputs {
state_trie: state_trie_before,
2023-03-27 17:30:11 -06:00
transactions_trie: Node::Empty.into(),
receipts_trie: Node::Empty.into(),
2023-01-14 21:18:58 -08:00
storage_tries: vec![],
};
// Generated using a little py-evm script.
let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd");
2023-03-12 21:35:04 -07:00
let value = U256::from(100u32);
2023-01-14 21:18:58 -08:00
2023-03-12 21:35:04 -07:00
let block_metadata = BlockMetadata {
block_beneficiary: Address::from(beneficiary),
..BlockMetadata::default()
};
2023-01-14 21:18:58 -08:00
let mut contract_code = HashMap::new();
2023-03-16 14:11:40 -07:00
contract_code.insert(keccak(vec![]), vec![]);
2023-01-14 21:18:58 -08:00
contract_code.insert(code_hash, code.to_vec());
2023-03-16 14:11:40 -07:00
let expected_state_trie_after: HashedPartialTrie = {
2023-03-10 08:54:26 -08:00
let txdata_gas = 2 * 16;
let gas_used = 21_000 + code_gas + txdata_gas;
2023-03-12 21:35:04 -07:00
let beneficiary_account_after = AccountRlp {
balance: beneficiary_account_before.balance + gas_used * 10,
..beneficiary_account_before
};
2023-01-14 21:18:58 -08:00
let sender_account_after = AccountRlp {
2023-03-10 08:54:26 -08:00
balance: sender_account_before.balance - value - gas_used * 10,
2023-01-14 22:08:07 -08:00
nonce: sender_account_before.nonce + 1,
2023-01-14 21:18:58 -08:00
..sender_account_before
};
let to_account_after = AccountRlp {
balance: to_account_before.balance + value,
..to_account_before
};
2023-03-27 17:30:11 -06:00
let mut children = core::array::from_fn(|_| Node::Empty.into());
children[beneficiary_nibbles.get_nibble(0) as usize] = Node::Leaf {
2023-03-12 21:35:04 -07:00
nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&beneficiary_account_after).to_vec(),
}
.into();
2023-03-27 17:30:11 -06:00
children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf {
2023-01-14 21:18:58 -08:00
nibbles: sender_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&sender_account_after).to_vec(),
}
.into();
2023-03-27 17:30:11 -06:00
children[to_nibbles.get_nibble(0) as usize] = Node::Leaf {
2023-01-14 21:18:58 -08:00
nibbles: to_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&to_account_after).to_vec(),
}
.into();
2023-03-27 17:30:11 -06:00
Node::Branch {
2023-01-14 21:18:58 -08:00
children,
value: vec![],
}
2023-03-27 17:30:11 -06:00
}
.into();
let trie_roots_after = TrieRoots {
state_root: expected_state_trie_after.hash(),
transactions_root: tries_before.transactions_trie.hash(), // TODO: Fix this when we have transactions trie.
receipts_root: tries_before.receipts_trie.hash(), // TODO: Fix this when we have receipts trie.
};
let inputs = GenerationInputs {
signed_txns: vec![txn.to_vec()],
tries: tries_before,
trie_roots_after,
contract_code,
block_metadata,
addresses: vec![],
};
2023-01-14 21:18:58 -08:00
let mut timing = TimingTree::new("prove", log::Level::Debug);
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
timing.filter(Duration::from_millis(100)).print();
2023-01-14 21:18:58 -08:00
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"));
}