Skip to content

Commit

Permalink
feat: execute up to 1920000 blocks of state for state bridge (#1280)
Browse files Browse the repository at this point in the history
  • Loading branch information
KolbyML authored May 10, 2024
1 parent 5fd1d3d commit d96324a
Show file tree
Hide file tree
Showing 11 changed files with 1,521 additions and 52 deletions.
414 changes: 392 additions & 22 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ethportal-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ rand = "0.8.5"
reth-rpc-types = { rev = "8d1d13ef89cf19459adc37ba0c45e7aac6270dc1", git = "https://github.com/paradigmxyz/reth.git"}
rlp = "0.5.0"
rs_merkle = "1.4.2"
secp256k1 = { version = "0.29.0", features = ["global-context", "recovery", "rand"] }
serde = { version = "1.0.150", features = ["derive", "rc"] }
serde_json = "1.0.89"
serde-this-or-that = "0.4.2"
Expand Down
192 changes: 192 additions & 0 deletions ethportal-api/src/types/execution/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use alloy_rlp::{
RlpEncodable, EMPTY_STRING_CODE,
};
use bytes::{Buf, Bytes};
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message, SECP256K1,
};
use serde::{Deserialize, Deserializer};
use serde_json::{json, Value};

Expand All @@ -24,6 +28,47 @@ impl Transaction {
keccak256(alloy_rlp::encode(self))
}

pub fn get_transaction_sender_address(&self, encode_chain_id: bool) -> anyhow::Result<Address> {
let signature_hash = self.signature_hash(encode_chain_id);

let (r, s, odd_y_parity) = match self {
Transaction::Legacy(tx) => {
let odd_y_parity = if encode_chain_id {
tx.v.byte(0) - 37
} else {
tx.v.byte(0) - 27
};
(tx.r, tx.s, odd_y_parity)
}
Transaction::AccessList(tx) => (tx.r, tx.s, tx.y_parity.byte(0)),
Transaction::EIP1559(tx) => (tx.r, tx.s, tx.y_parity.byte(0)),
Transaction::Blob(tx) => (tx.r, tx.s, tx.y_parity.byte(0)),
};

let mut sig: [u8; 65] = [0; 65];
sig[0..32].copy_from_slice(&r.to_be_bytes::<32>());
sig[32..64].copy_from_slice(&s.to_be_bytes::<32>());
sig[64] = odd_y_parity;
let sig =
RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?;

let public = SECP256K1.recover_ecdsa(
&Message::from_digest_slice(signature_hash.as_slice())?,
&sig,
)?;
let hash = keccak256(&public.serialize_uncompressed()[1..]);
Ok(Address::from_slice(&hash[12..]))
}

pub fn signature_hash(&self, encode_chain_id: bool) -> B256 {
match self {
Transaction::Legacy(tx) => tx.signature_hash(encode_chain_id),
Transaction::AccessList(tx) => tx.signature_hash(),
Transaction::EIP1559(tx) => tx.signature_hash(),
Transaction::Blob(tx) => tx.signature_hash(),
}
}

pub fn encode_with_envelope(&self, out: &mut dyn bytes::BufMut, with_header: bool) {
match self {
Self::Legacy(tx) => tx.encode(out),
Expand Down Expand Up @@ -190,6 +235,29 @@ impl LegacyTransaction {
+ self.r.length()
+ self.s.length()
}

pub fn signature_hash(&self, encode_chain_id: bool) -> B256 {
let mut buf = Vec::<u8>::new();
let mut list = Vec::<u8>::new();
self.nonce.encode(&mut list);
self.gas_price.encode(&mut list);
self.gas.encode(&mut list);
self.to.encode(&mut list);
self.value.encode(&mut list);
self.data.encode(&mut list);
if encode_chain_id {
1u64.encode(&mut list);
0x00u8.encode(&mut list);
0x00u8.encode(&mut list);
}
let header = RlpHeader {
list: true,
payload_length: list.len(),
};
header.encode(&mut buf);
buf.extend_from_slice(&list);
keccak256(&buf)
}
}

#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize)]
Expand Down Expand Up @@ -253,6 +321,28 @@ impl AccessListTransaction {
+ self.r.length()
+ self.s.length()
}

pub fn signature_hash(&self) -> B256 {
let mut buf = Vec::<u8>::new();
let mut list = Vec::<u8>::new();
self.nonce.encode(&mut list);
self.gas_price.encode(&mut list);
self.gas_limit.encode(&mut list);
self.to.encode(&mut list);
self.value.encode(&mut list);
self.data.encode(&mut list);
self.access_list.encode(&mut list);
1u64.encode(&mut list);
0x00u8.encode(&mut list);
0x00u8.encode(&mut list);
let header = RlpHeader {
list: true,
payload_length: list.len(),
};
header.encode(&mut buf);
buf.extend_from_slice(&list);
keccak256(&buf)
}
}

#[derive(Eq, Debug, Clone, PartialEq, Deserialize)]
Expand Down Expand Up @@ -326,6 +416,29 @@ impl EIP1559Transaction {
+ self.r.length()
+ self.s.length()
}

pub fn signature_hash(&self) -> B256 {
let mut buf = Vec::<u8>::new();
let mut list = Vec::<u8>::new();
self.nonce.encode(&mut list);
self.max_priority_fee_per_gas.encode(&mut list);
self.max_fee_per_gas.encode(&mut list);
self.gas_limit.encode(&mut list);
self.to.encode(&mut list);
self.value.encode(&mut list);
self.data.encode(&mut list);
self.access_list.encode(&mut list);
1u64.encode(&mut list);
0x00u8.encode(&mut list);
0x00u8.encode(&mut list);
let header = RlpHeader {
list: true,
payload_length: list.len(),
};
header.encode(&mut buf);
buf.extend_from_slice(&list);
keccak256(&buf)
}
}

#[derive(Eq, Debug, Clone, PartialEq, Deserialize)]
Expand Down Expand Up @@ -405,6 +518,31 @@ impl BlobTransaction {
+ self.r.length()
+ self.s.length()
}

pub fn signature_hash(&self) -> B256 {
let mut buf = Vec::<u8>::new();
let mut list = Vec::<u8>::new();
self.nonce.encode(&mut list);
self.max_priority_fee_per_gas.encode(&mut list);
self.max_fee_per_gas.encode(&mut list);
self.gas_limit.encode(&mut list);
self.to.encode(&mut list);
self.value.encode(&mut list);
self.data.encode(&mut list);
self.access_list.encode(&mut list);
self.max_fee_per_blob_gas.encode(&mut list);
self.blob_versioned_hashes.encode(&mut list);
1u64.encode(&mut list);
0x00u8.encode(&mut list);
0x00u8.encode(&mut list);
let header = RlpHeader {
list: true,
payload_length: list.len(),
};
header.encode(&mut buf);
buf.extend_from_slice(&list);
keccak256(&buf)
}
}

#[derive(Eq, Debug, Clone, PartialEq, Deserialize)]
Expand Down Expand Up @@ -556,3 +694,57 @@ pub struct AccessListItem {
pub address: Address,
pub storage_keys: Vec<B256>,
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use alloy_rlp::Decodable;

use crate::{types::execution::transaction::Transaction, utils::bytes::hex_decode};

#[rstest::rstest]
// Block 46170 https://etherscan.io/tx/0x9e6e19637bb625a8ff3d052b7c2fe57dc78c55a15d258d77c43d5a9c160b0384
#[case(
"0xf86d8085746a52880082520894c93f2250589a6563f5359051c1ea25746549f0d889208686e75e903bc000801ba034b6fdc33ea520e8123cf5ac4a9ff476f639cab68980cd9366ccae7aef437ea0a0e517caa5f50e27ca0d1e9a92c503b4ccb039680c6d9d0c71203ed611ea4feb33",
false,
"0x0aa924d917fd61675076e5456610c166d62aecf4685318febcf25bab0f63b779",
"0x63ac545c991243fa18aec41d4f6f598e555015dc",
)]
// Block 100004 https://etherscan.io/tx/0x6f12399cc2cb42bed5b267899b08a847552e8c42a64f5eb128c1bcbd1974fb0c
#[case(
"0xf86d19850cb5bea61483015f9094d9666150a9da92d9108198a4072970805a8b3428884563918244f40000801ba0b23adc880d3735e4389698dddc953fb02f1fa9b57e84d3510a2a4b3597ac2486a04e856f95c4e2828933246fb4765a5bfd2ca5959840643bef0e80b4e3a243d064",
false,
"0x9e669fcad535566e5b69acbceb660c636886ac655f1afcb5686aebf820f52ca2",
"0xcf00a85f3826941e7a25bfcf9aac575d40410852",
)]
// Block 3000000 https://etherscan.io/tx/0xb95ab9484280074f7b8c6a3cf5ffe2bf0c39168433adcdedc1aacd10d994d95a
#[case(
"0xf8708310aa038504a817c80083015f9094e7268aadb21f48a3b65f0880b6b9480217995979880dfe6c5bd5fa6ff08026a0a186e1a20b3973a29d28d0cddb205ff8b9e670cff1d3e794cd4de1b08b5a8562a0429c2166e893a646cb3b5faf1216ee4c7d99e3957ae145036ca68dec0bcb5f57",
true,
"0xd5f76f3a1f7eebadc04d702334445d261d24831d6bfef61e3974bcdb4f015c68",
"0xea674fdde714fd979de3edf0f56aa9716b898ec8"
)]
fn test_legacy_get_sender_address_from_transaction(
#[case] transaction: &str,
#[case] post_eip155: bool,
#[case] signature: &str,
#[case] sender_address: &str,
) {
let transaction_rlp = hex_decode(transaction).unwrap();
let transaction: Transaction =
Decodable::decode(&mut transaction_rlp.as_slice()).expect("error decoding transaction");
assert_eq!(
format!("{:?}", transaction.signature_hash(post_eip155)),
signature
);
assert_eq!(
format!(
"{:?}",
transaction
.get_transaction_sender_address(post_eip155)
.unwrap()
),
sender_address
);
}
}
6 changes: 6 additions & 0 deletions portal-bridge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,21 @@ jsonrpsee = { version = "0.20.0", features = [
"server",
] }
lazy_static = "1.4.0"
parking_lot = "0.11.2"
portalnet = { path = "../portalnet" }
prometheus_exporter = "0.8.4"
rand = "0.8.4"
revm = { version = "8.0.0", features = ["std", "secp256k1", "serde-json"], default-features = false }
revm-primitives = { version = "3.1.0", features = ["std", "serde"], default-features = false }
revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "848d568" }
scraper = "0.18.1"
serde = { version = "1.0.150", features = ["derive", "rc"] }
serde_json = "1.0.89"
serde_yaml = "0.9"
snap = "1.1.1"
ssz_types = { git = "https://github.com/KolbyML/ssz_types.git", rev = "2a5922de75f00746890bf4ea9ad663c9d5d58efe" }
surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls", "middleware-logger", "encoding"] } # we use rustls because OpenSSL cause issues compiling on aarch64
thiserror = "1.0.57"
tokio = { version = "1.14.0", features = ["full"] }
tracing = "0.1.36"
tracing-subscriber = "0.3.15"
Expand All @@ -54,3 +59,4 @@ url = "2.3.1"
env_logger = "0.9.0"
rstest = "0.18.2"
test-log = { version = "0.2.11", features = ["trace"] }
tracing-test = "0.1"
11 changes: 8 additions & 3 deletions portal-bridge/src/bridge/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{path::PathBuf, sync::Arc};
use super::era1::get_shuffled_era1_files;
use anyhow::anyhow;
use ethportal_api::{jsonrpsee::http_client::HttpClient, StateContentKey, StateContentValue};
use revm_primitives::SpecId;
use surf::{Client, Config};
use tokio::{
sync::{OwnedSemaphorePermit, Semaphore},
Expand All @@ -22,6 +23,7 @@ use crate::{
state::{
content::{create_content_key, create_content_value},
execution::State,
spec_id::get_spec_block_number,
},
},
};
Expand Down Expand Up @@ -67,8 +69,11 @@ impl StateBridge {
info!("Launching state bridge: {:?}", self.mode);
match self.mode.clone() {
BridgeMode::Single(ModeType::Block(last_block)) => {
if last_block > 46146 {
panic!("State bridge only supports blocks up to 46146 for the time being.");
if last_block > get_spec_block_number(SpecId::DAO_FORK) {
panic!(
"State bridge only supports blocks up to {} for the time being.",
get_spec_block_number(SpecId::DAO_FORK)
);
}
self.launch_state(last_block)
.await
Expand Down Expand Up @@ -110,7 +115,7 @@ impl StateBridge {
// process block
let block_tuple = Era1::get_tuple_by_index(&current_raw_era1, block_index % EPOCH_SIZE);
let updated_accounts = match block_index == 0 {
true => state.accounts.clone(),
true => state.database.accounts.keys().copied().collect(),
false => state.process_block(&block_tuple)?,
};

Expand Down
Loading

0 comments on commit d96324a

Please sign in to comment.