2022-09-06 17:57:47 +00:00
|
|
|
use ethers::types::{Bytes, EIP1186ProofResponse};
|
2022-08-19 00:33:44 +00:00
|
|
|
use ethers::utils::keccak256;
|
2022-08-20 20:33:32 +00:00
|
|
|
use ethers::utils::rlp::{decode_list, RlpStream};
|
2022-08-19 00:33:44 +00:00
|
|
|
|
2022-09-06 17:57:47 +00:00
|
|
|
pub fn verify_proof(proof: &Vec<Bytes>, root: &Vec<u8>, path: &Vec<u8>, value: &Vec<u8>) -> bool {
|
2022-08-19 22:43:58 +00:00
|
|
|
let mut expected_hash = root.clone();
|
2022-08-19 22:02:06 +00:00
|
|
|
let mut path_offset = 0;
|
2022-08-19 00:33:44 +00:00
|
|
|
|
2022-08-19 22:02:06 +00:00
|
|
|
for (i, node) in proof.iter().enumerate() {
|
2022-08-19 22:43:58 +00:00
|
|
|
if expected_hash != keccak256(node).to_vec() {
|
2022-08-19 22:02:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
2022-08-19 00:33:44 +00:00
|
|
|
|
2022-08-19 22:02:06 +00:00
|
|
|
let node_list: Vec<Vec<u8>> = decode_list(node);
|
|
|
|
|
2022-08-20 20:33:32 +00:00
|
|
|
if node_list.len() == 17 {
|
2022-09-01 00:31:02 +00:00
|
|
|
if i == proof.len() - 1 {
|
|
|
|
// exclusion proof
|
|
|
|
let nibble = get_nibble(&path, path_offset);
|
|
|
|
let node = &node_list[nibble as usize];
|
|
|
|
|
2022-09-01 19:58:45 +00:00
|
|
|
if node.len() == 0 && is_empty_value(value) {
|
2022-09-01 00:31:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let nibble = get_nibble(&path, path_offset);
|
|
|
|
expected_hash = node_list[nibble as usize].clone();
|
2022-08-19 22:02:06 +00:00
|
|
|
|
2022-09-01 00:31:02 +00:00
|
|
|
path_offset += 1;
|
|
|
|
}
|
2022-08-19 22:02:06 +00:00
|
|
|
} else if node_list.len() == 2 {
|
|
|
|
if i == proof.len() - 1 {
|
2022-08-26 22:13:35 +00:00
|
|
|
// exclusion proof
|
2022-11-02 00:40:37 +00:00
|
|
|
if !paths_match(
|
|
|
|
&node_list[0],
|
|
|
|
skip_length(&node_list[0]),
|
|
|
|
&path,
|
|
|
|
path_offset,
|
|
|
|
) && is_empty_value(value)
|
2022-08-27 00:05:12 +00:00
|
|
|
{
|
|
|
|
return true;
|
2022-08-26 22:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// inclusion proof
|
2022-09-01 00:31:02 +00:00
|
|
|
if &node_list[1] == value {
|
2022-11-02 00:40:37 +00:00
|
|
|
return paths_match(
|
|
|
|
&node_list[0],
|
|
|
|
skip_length(&node_list[0]),
|
|
|
|
&path,
|
|
|
|
path_offset,
|
|
|
|
);
|
2022-08-19 22:02:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-09-01 00:31:02 +00:00
|
|
|
let node_path = &node_list[0];
|
|
|
|
let prefix_length = shared_prefix_length(path, path_offset, node_path);
|
|
|
|
path_offset += prefix_length;
|
|
|
|
expected_hash = node_list[1].clone();
|
2022-08-19 22:02:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-01 00:31:02 +00:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2022-11-02 00:40:37 +00:00
|
|
|
fn paths_match(p1: &Vec<u8>, s1: usize, p2: &Vec<u8>, s2: usize) -> bool {
|
|
|
|
let len1 = p1.len() * 2 - s1;
|
|
|
|
let len2 = p2.len() * 2 - s2;
|
|
|
|
|
|
|
|
if len1 != len2 {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for offset in 0..len1 {
|
|
|
|
let n1 = get_nibble(p1, s1 + offset);
|
|
|
|
let n2 = get_nibble(p2, s2 + offset);
|
|
|
|
|
|
|
|
if n1 != n2 {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn get_rest_path(p: &Vec<u8>, s: usize) -> String {
|
|
|
|
let mut ret = String::new();
|
|
|
|
for i in s..p.len() * 2 {
|
|
|
|
let n = get_nibble(p, i);
|
|
|
|
ret += &format!("{:01x}", n);
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
2022-09-01 19:58:45 +00:00
|
|
|
fn is_empty_value(value: &Vec<u8>) -> bool {
|
|
|
|
let mut stream = RlpStream::new();
|
|
|
|
stream.begin_list(4);
|
|
|
|
stream.append_empty_data();
|
|
|
|
stream.append_empty_data();
|
|
|
|
let empty_storage_hash = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";
|
|
|
|
stream.append(&hex::decode(empty_storage_hash).unwrap());
|
|
|
|
let empty_code_hash = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
|
|
|
|
stream.append(&hex::decode(empty_code_hash).unwrap());
|
|
|
|
let empty_account = stream.out();
|
|
|
|
|
|
|
|
let is_empty_slot = value.len() == 1 && value[0] == 0x80;
|
|
|
|
let is_empty_account = value == &empty_account;
|
|
|
|
is_empty_slot || is_empty_account
|
|
|
|
}
|
|
|
|
|
2022-09-01 00:31:02 +00:00
|
|
|
fn shared_prefix_length(path: &Vec<u8>, path_offset: usize, node_path: &Vec<u8>) -> usize {
|
|
|
|
let skip_length = skip_length(node_path);
|
|
|
|
let mut node_decoded = vec![];
|
|
|
|
for i in skip_length..node_path.len() * 2 {
|
|
|
|
let decoded_nibble_offset = i - skip_length;
|
|
|
|
if decoded_nibble_offset % 2 == 0 {
|
|
|
|
let shifted = get_nibble(node_path, i) << 4;
|
|
|
|
node_decoded.push(shifted);
|
|
|
|
} else {
|
|
|
|
let byte = &node_decoded.get(decoded_nibble_offset / 2).unwrap().clone();
|
|
|
|
let right = get_nibble(node_path, i);
|
|
|
|
node_decoded.pop();
|
|
|
|
node_decoded.push(byte | right);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let len = node_decoded.len() * 2;
|
|
|
|
let mut prefix_len = 0;
|
|
|
|
|
|
|
|
for i in 0..len {
|
|
|
|
let path_nibble = get_nibble(path, i + path_offset);
|
|
|
|
let node_path_nibble = get_nibble(&node_decoded, i);
|
|
|
|
|
|
|
|
if path_nibble == node_path_nibble {
|
|
|
|
prefix_len += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix_len
|
2022-08-19 22:02:06 +00:00
|
|
|
}
|
|
|
|
|
2022-08-26 22:13:35 +00:00
|
|
|
fn skip_length(node: &Vec<u8>) -> usize {
|
2022-11-02 00:40:37 +00:00
|
|
|
if node.len() == 0 {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-26 22:13:35 +00:00
|
|
|
let nibble = get_nibble(node, 0);
|
|
|
|
match nibble {
|
2022-09-01 00:31:02 +00:00
|
|
|
0 => 2,
|
|
|
|
1 => 1,
|
2022-08-26 22:13:35 +00:00
|
|
|
2 => 2,
|
|
|
|
3 => 1,
|
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-19 22:02:06 +00:00
|
|
|
fn get_nibble(path: &Vec<u8>, offset: usize) -> u8 {
|
|
|
|
let byte = path[offset / 2];
|
|
|
|
if offset % 2 == 0 {
|
|
|
|
byte >> 4
|
|
|
|
} else {
|
|
|
|
byte & 0xF
|
|
|
|
}
|
2022-08-19 00:33:44 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 17:57:47 +00:00
|
|
|
pub fn encode_account(proof: &EIP1186ProofResponse) -> Vec<u8> {
|
2022-08-19 00:33:44 +00:00
|
|
|
let mut stream = RlpStream::new_list(4);
|
|
|
|
stream.append(&proof.nonce);
|
|
|
|
stream.append(&proof.balance);
|
|
|
|
stream.append(&proof.storage_hash);
|
|
|
|
stream.append(&proof.code_hash);
|
|
|
|
let encoded = stream.out();
|
|
|
|
encoded.to_vec()
|
|
|
|
}
|