Skip to content

Commit

Permalink
provably bad blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
hashcashier committed Jan 24, 2024
1 parent 7069754 commit e96c68f
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 79 deletions.
15 changes: 11 additions & 4 deletions guests/eth-block/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,24 @@ use zeth_lib::{
builder::{BlockBuilderStrategy, EthereumStrategy},
consts::ETH_MAINNET_CHAIN_SPEC,
};
use zeth_lib::output::BlockBuildOutput;

risc0_zkvm::guest::entry!(main);

pub fn main() {
// Read the input previous block and transaction data
let input = env::read();
// Build the resulting block
let (header, state) = EthereumStrategy::build_from(&ETH_MAINNET_CHAIN_SPEC, input)
let mut output = EthereumStrategy::build_from(&ETH_MAINNET_CHAIN_SPEC, input)
.expect("Failed to build the resulting block");
// Output the resulting block's hash to the journal
env::commit(&header.hash());
// Abridge successful construction results
if let BlockBuildOutput::SUCCESS { new_block_hash, new_block_head, new_block_state } = &mut output {
let trie_root = core::mem::replace(new_block_state, new_block_head.state_root.into());
// Leak memory, save cycles
core::mem::forget(trie_root);
}
// Output the construction result
env::commit(&output);
// Leak memory, save cycles
core::mem::forget((header, state));
core::mem::forget(output);
}
15 changes: 11 additions & 4 deletions guests/op-block/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,24 @@ use zeth_lib::{
builder::{BlockBuilderStrategy, OptimismStrategy},
consts::OP_MAINNET_CHAIN_SPEC,
};
use zeth_lib::output::BlockBuildOutput;

risc0_zkvm::guest::entry!(main);

pub fn main() {
// Read the input previous block and transaction data
let input = env::read();
// Build the resulting block
let (header, state) = OptimismStrategy::build_from(&OP_MAINNET_CHAIN_SPEC, input)
let output = OptimismStrategy::build_from(&OP_MAINNET_CHAIN_SPEC, input)
.expect("Failed to build the resulting block");
// Output the resulting block's hash to the journal
env::commit(&header.hash());
// Abridge successful construction results
if let BlockBuildOutput::SUCCESS { new_block_hash, new_block_head, new_block_state } = &mut output {
let trie_root = core::mem::replace(new_block_state, new_block_head.state_root.into());
// Leak memory, save cycles
core::mem::forget(trie_root);
}
// Output the construction result
env::commit(&output);
// Leak memory, save cycles
core::mem::forget((header, state));
core::mem::forget(output);
}
31 changes: 21 additions & 10 deletions host/src/operations/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ use std::fmt::Debug;

use anyhow::Context;
use ethers_core::types::Transaction as EthersTransaction;
use log::info;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use zeth_lib::{
builder::BlockBuilderStrategy,
consts::ChainSpec,
host::{preflight::Preflight, verify::Verifier},
input::Input,
input::BlockBuildInput,
output::BlockBuildOutput,
};

use crate::{
Expand Down Expand Up @@ -61,20 +62,30 @@ where
let preflight_data = preflight_result.context("preflight failed")?;

// Create the guest input from [Init]
let input: Input<N::TxEssence> = preflight_data
let input: BlockBuildInput<N::TxEssence> = preflight_data
.clone()
.try_into()
.context("invalid preflight data")?;

// Verify that the transactions run correctly
info!("Running from memory ...");
let (header, state_trie) =
N::build_from(&chain_spec, input.clone()).context("Error while building block")?;
let output = N::build_from(&chain_spec, input.clone()).context("Error while building block")?;

info!("Verifying final state using provider data ...");
preflight_data.verify_block(&header, &state_trie)?;
match &output {
BlockBuildOutput::SUCCESS {
new_block_hash,
new_block_head,
new_block_state,
} => {
info!("Verifying final state using provider data ...");
preflight_data.verify_block(new_block_head, new_block_state)?;

info!("Final block hash derived successfully. {}", header.hash());
info!("Final block hash derived successfully. {}", new_block_hash);
}
BlockBuildOutput::FAILURE { .. } => {
warn!("Proving bad block construction!")
}
}

match &cli {
Cli::Build(..) => {}
Expand All @@ -84,7 +95,7 @@ where
run_args.exec_args.local_exec,
run_args.exec_args.profile,
guest_elf,
&preflight_data.header.hash(),
&output,
file_reference,
);
}
Expand All @@ -93,7 +104,7 @@ where
&cli,
&input,
guest_elf,
&preflight_data.header.hash(),
&output,
vec![],
file_reference,
None,
Expand Down
4 changes: 2 additions & 2 deletions host/src/operations/rollups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use zeth_lib::{
builder::OptimismStrategy,
consts::{Network, OP_MAINNET_CHAIN_SPEC},
host::{preflight::Preflight, rpc_db::RpcDb},
input::Input,
input::BlockBuildInput,
optimism::{
batcher_db::BatcherDb,
composition::{ComposeInput, ComposeInputOperation, ComposeOutputOperation},
Expand All @@ -45,7 +45,7 @@ async fn fetch_op_blocks(
core_args: &CoreArgs,
block_number: u64,
block_count: u64,
) -> anyhow::Result<Vec<Input<OptimismTxEssence>>> {
) -> anyhow::Result<Vec<BlockBuildInput<OptimismTxEssence>>> {
let mut op_blocks = vec![];
for i in 0..block_count {
let block_number = block_number + i;
Expand Down
49 changes: 39 additions & 10 deletions lib/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ use crate::{
prepare::{EthHeaderPrepStrategy, HeaderPrepStrategy},
},
consts::ChainSpec,
input::Input,
input::BlockBuildInput,
mem_db::MemDb,
output::BlockBuildOutput,
};

mod execute;
Expand All @@ -41,7 +42,7 @@ mod prepare;
#[derive(Clone, Debug)]
pub struct BlockBuilder<'a, D, E: TxEssence> {
pub(crate) chain_spec: &'a ChainSpec,
pub(crate) input: Input<E>,
pub(crate) input: BlockBuildInput<E>,
pub(crate) db: Option<D>,
pub(crate) header: Option<Header>,
}
Expand All @@ -53,7 +54,7 @@ where
E: TxEssence,
{
/// Creates a new block builder.
pub fn new(chain_spec: &ChainSpec, input: Input<E>) -> BlockBuilder<'_, D, E> {
pub fn new(chain_spec: &ChainSpec, input: BlockBuildInput<E>) -> BlockBuilder<'_, D, E> {
BlockBuilder {
chain_spec,
db: None,
Expand Down Expand Up @@ -111,13 +112,41 @@ pub trait BlockBuilderStrategy {
/// Builds a block from the given input.
fn build_from(
chain_spec: &ChainSpec,
input: Input<Self::TxEssence>,
) -> Result<(Header, MptNode)> {
BlockBuilder::<MemDb, Self::TxEssence>::new(chain_spec, input)
.initialize_database::<Self::DbInitStrategy>()?
.prepare_header::<Self::HeaderPrepStrategy>()?
.execute_transactions::<Self::TxExecStrategy>()?
.finalize::<Self::BlockFinalizeStrategy>()
input: BlockBuildInput<Self::TxEssence>,
) -> Result<BlockBuildOutput> {
// Database initialization failure does not mean the block is faulty
let input_hash = input.partial_hash();
let initialized = BlockBuilder::<MemDb, Self::TxEssence>::new(chain_spec, input)
.initialize_database::<Self::DbInitStrategy>()?;

// Header validation errors mean a faulty block
let prepared = match initialized.prepare_header::<Self::HeaderPrepStrategy>() {
Ok(builder) => builder,
Err(_) => {
return Ok(BlockBuildOutput::FAILURE {
bad_input_hash: input_hash.into(),
})
}
};

// Transaction execution errors mean a faulty block
let executed = match prepared.execute_transactions::<Self::TxExecStrategy>() {
Ok(builder) => builder,
Err(_) => {
return Ok(BlockBuildOutput::FAILURE {
bad_input_hash: input_hash.into(),
})
}
};

// Finalization does not indicate a faulty block
let (header, state) = executed.finalize::<Self::BlockFinalizeStrategy>()?;

Ok(BlockBuildOutput::SUCCESS {
new_block_hash: header.hash(),
new_block_head: header,
new_block_state: state,
})
}
}

Expand Down
14 changes: 7 additions & 7 deletions lib/src/host/preflight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use crate::{
provider::{new_provider, BlockQuery},
provider_db::ProviderDb,
},
input::{Input, StorageEntry},
input::{BlockBuildInput, StorageEntry},
mem_db::MemDb,
};

Expand Down Expand Up @@ -149,7 +149,7 @@ where
fn new_preflight_input<E>(
block: EthersBlock<EthersTransaction>,
parent_header: Header,
) -> Result<Input<E>>
) -> Result<BlockBuildInput<E>>
where
E: TxEssence + TryFrom<EthersTransaction>,
<E as TryFrom<EthersTransaction>>::Error: Debug,
Expand All @@ -176,7 +176,7 @@ where
})
.collect::<Result<Vec<_>, _>>()?;

let input = Input {
let input = BlockBuildInput {
beneficiary: from_ethers_h160(block.author.context("author missing")?),
gas_limit: from_ethers_u256(block.gas_limit),
timestamp: from_ethers_u256(block.timestamp),
Expand All @@ -193,12 +193,12 @@ where
Ok(input)
}

/// Converts the [Data] returned by the [Preflight] into [Input] required by the
/// Converts the [Data] returned by the [Preflight] into [BlockBuildInput] required by the
/// [BlockBuilder].
impl<E: TxEssence> TryFrom<Data<E>> for Input<E> {
impl<E: TxEssence> TryFrom<Data<E>> for BlockBuildInput<E> {
type Error = anyhow::Error;

fn try_from(data: Data<E>) -> Result<Input<E>> {
fn try_from(data: Data<E>) -> Result<BlockBuildInput<E>> {
// collect the code from each account
let mut contracts = HashSet::new();
for account in data.db.accounts.values() {
Expand All @@ -225,7 +225,7 @@ impl<E: TxEssence> TryFrom<Data<E>> for Input<E> {
);

// Create the block builder input
let input = Input {
let input = BlockBuildInput {
parent_header: data.parent_header,
beneficiary: data.header.beneficiary,
gas_limit: data.header.gas_limit,
Expand Down
28 changes: 24 additions & 4 deletions lib/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use ethers_core::k256::sha2::{Digest, Sha256};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use zeth_primitives::{
block::Header,
transactions::{Transaction, TxEssence},
tree::Hash,
trie::MptNode,
withdrawal::Withdrawal,
Address, Bytes, B256, U256,
Address, Bytes, RlpBytes, B256, U256,
};

/// Represents the state of an account's storage.
Expand All @@ -29,7 +31,7 @@ pub type StorageEntry = (MptNode, Vec<U256>);

/// External block input.
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Input<E: TxEssence> {
pub struct BlockBuildInput<E: TxEssence> {
/// Previous block header
pub parent_header: Header,
/// Address to which all priority fees in this block are transferred.
Expand All @@ -56,6 +58,24 @@ pub struct Input<E: TxEssence> {
pub ancestor_headers: Vec<Header>,
}

impl<E: TxEssence> BlockBuildInput<E> {
pub fn partial_hash(&self) -> Hash {
let mut hasher = Sha256::new();

hasher.update(self.parent_header.to_rlp());
hasher.update(self.beneficiary.0);
hasher.update(self.gas_limit.as_le_slice());
hasher.update(self.timestamp.as_le_slice());
hasher.update(self.extra_data.as_ref());
hasher.update(self.mix_hash.0);
// todo: use precalculated trie root hashes if available
hasher.update(self.transactions.to_rlp());
hasher.update(self.withdrawals.to_rlp());

hasher.finalize().into()
}
}

#[cfg(test)]
mod tests {
use zeth_primitives::transactions::ethereum::EthereumTxEssence;
Expand All @@ -64,7 +84,7 @@ mod tests {

#[test]
fn input_serde_roundtrip() {
let input = Input::<EthereumTxEssence> {
let input = BlockBuildInput::<EthereumTxEssence> {
parent_header: Default::default(),
beneficiary: Default::default(),
gas_limit: Default::default(),
Expand All @@ -78,7 +98,7 @@ mod tests {
contracts: vec![],
ancestor_headers: vec![],
};
let _: Input<EthereumTxEssence> =
let _: BlockBuildInput<EthereumTxEssence> =
bincode::deserialize(&bincode::serialize(&input).unwrap()).unwrap();
}
}
1 change: 1 addition & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod consts;
pub mod input;
pub mod mem_db;
pub mod optimism;
pub mod output;

mod utils;

Expand Down
Loading

0 comments on commit e96c68f

Please sign in to comment.