Skip to content

Commit

Permalink
Merge pull request #91 from worldcoin/osiris/tests
Browse files Browse the repository at this point in the history
test: fix e2e tests
  • Loading branch information
0xOsiris authored Dec 23, 2024
2 parents 253612f + b81a942 commit 5268032
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 120 deletions.
1 change: 1 addition & 0 deletions world-chain-builder/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions world-chain-builder/crates/world/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ fn main() {
.sequencer_http
.map(SequencerClient::new);
let eth_api_ext = WorldChainEthApiExt::new(pool, provider, sequencer_client);
// Remove the `eth_sendRawTransaction` method from the configured modules
ctx.modules
.remove_method_from_configured("eth_sendRawTransaction");
// Merge the `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` RPC methods
ctx.modules.merge_configured(eth_api_ext.into_rpc())?;
ctx.modules.replace_configured(eth_api_ext.into_rpc())?;
Ok(())
})
.launch()
Expand Down
1 change: 1 addition & 0 deletions world-chain-builder/crates/world/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ reth-trie-db.workspace = true
alloy-eips.workspace = true
alloy-primitives.workspace = true
alloy-rpc-types.workspace = true
alloy-sol-types.workspace = true

tokio.workspace = true
eyre.workspace = true
Expand Down
172 changes: 60 additions & 112 deletions world-chain-builder/crates/world/node/tests/e2e.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
//! Utilities for running world chain builder end-to-end tests.
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;

use alloy_eips::eip2718::Decodable2718;
use alloy_genesis::{Genesis, GenesisAccount};
use alloy_network::eip2718::Encodable2718;
use alloy_network::{Ethereum, EthereumWallet, TransactionBuilder};
use alloy_rpc_types::{TransactionInput, TransactionRequest, Withdrawals};
use alloy_signer_local::PrivateKeySigner;
use chrono::Utc;
use alloy_sol_types::SolCall;
use chrono::Datelike;
use reth::api::{FullNodeTypesAdapter, NodeTypesWithDBAdapter};
use reth::builder::components::Components;
use reth::builder::{NodeAdapter, NodeBuilder, NodeConfig, NodeHandle};
Expand All @@ -20,31 +16,28 @@ use reth_db::test_utils::TempDatabase;
use reth_db::DatabaseEnv;
use reth_e2e_test_utils::node::NodeTestContext;
use reth_e2e_test_utils::transaction::TransactionTestContext;
use reth_e2e_test_utils::wallet::Wallet;
use reth_evm::execute::BasicBlockExecutorProvider;
use reth_node_core::args::RpcServerArgs;
use reth_optimism_chainspec::{OpChainSpec, OpChainSpecBuilder};
use reth_optimism_consensus::OpBeaconConsensus;
use reth_optimism_evm::{OpEvmConfig, OpExecutionStrategyFactory};
use reth_optimism_node::node::OpAddOns;
use reth_optimism_node::OpPayloadBuilderAttributes;
use reth_primitives::PooledTransactionsElement;
use reth_provider::providers::BlockchainProvider;
use revm_primitives::{Address, Bytes, FixedBytes, TxKind, B256, U256};
use semaphore::identity::Identity;
use semaphore::poseidon_tree::LazyPoseidonTree;
use semaphore::protocol::{generate_nullifier_hash, generate_proof};
use semaphore::{hash_to_field, Field};
use std::collections::BTreeMap;
use std::ops::Range;
use std::sync::Arc;
use world_chain_builder_node::args::{ExtArgs, WorldChainBuilderArgs};
use world_chain_builder_node::node::WorldChainBuilder;
use world_chain_builder_pbh::date_marker::DateMarker;
use world_chain_builder_pbh::external_nullifier::ExternalNullifier;
use world_chain_builder_pbh::payload::{PbhPayload, Proof};
use world_chain_builder_pool::ordering::WorldChainOrdering;
use world_chain_builder_pool::root::{LATEST_ROOT_SLOT, OP_WORLD_ID};
use world_chain_builder_pool::test_utils::{
pbh_bundle, signer, tree_root, user_op, PBH_TEST_SIGNATURE_AGGREGATOR, PBH_TEST_VALIDATOR,
};
use world_chain_builder_pool::tx::WorldChainPooledTransaction;
use world_chain_builder_pool::validator::WorldChainTransactionValidator;
use world_chain_builder_primitives::transaction::WorldChainPooledTransactionsElement;
use world_chain_builder_rpc::{EthTransactionsExtServer, WorldChainEthApiExt};

pub const DEV_CHAIN_ID: u64 = 8453;
Expand Down Expand Up @@ -84,26 +77,14 @@ type NodeAdapterType = NodeAdapter<
type Adapter = NodeTestContext<NodeAdapterType, OpAddOns<NodeAdapterType>>;

pub struct WorldChainBuilderTestContext {
pub pbh_wallets: Vec<PrivateKeySigner>,
pub tree: LazyPoseidonTree,
pub signers: Range<u32>,
pub tasks: TaskManager,
pub node: Adapter,
pub identities: HashMap<Address, usize>,
}

impl WorldChainBuilderTestContext {
pub async fn setup() -> eyre::Result<Self> {
let wallets = Wallet::new(20).with_chain_id(DEV_CHAIN_ID).gen();
let mut tree = LazyPoseidonTree::new(30, Field::from(0)).derived();
let mut identities = HashMap::new();
for (i, signer) in wallets.iter().enumerate() {
let address = signer.address();
identities.insert(address, i);
let identity = Identity::from_secret(signer.address().as_mut_slice(), None);
tree = tree.update(i, &identity.commitment());
}

let op_chain_spec = Arc::new(get_chain_spec(tree.root()));
let op_chain_spec = Arc::new(get_chain_spec());

let tasks = TaskManager::current();
let exec = tasks.executor();
Expand All @@ -130,7 +111,8 @@ impl WorldChainBuilderTestContext {
builder_args: WorldChainBuilderArgs {
num_pbh_txs: 30,
verified_blockspace_capacity: 70,
..Default::default()
pbh_validator: PBH_TEST_VALIDATOR,
signature_aggregator: PBH_TEST_SIGNATURE_AGGREGATOR,
},
..Default::default()
})?)
Expand All @@ -149,94 +131,54 @@ impl WorldChainBuilderTestContext {
} = builder.launch().await?;

let test_ctx = NodeTestContext::new(node, optimism_payload_attributes).await?;

Ok(Self {
pbh_wallets: wallets,
tree,
signers: (0..5),
tasks,
node: test_ctx,
identities,
})
}

pub async fn raw_pbh_tx_bytes(
&self,
signer: PrivateKeySigner,
acc: u32,
pbh_nonce: u8,
tx_nonce: u64,
user_op_nonce: U256,
) -> Bytes {
let tx = tx(DEV_CHAIN_ID, None, tx_nonce);
let envelope = TransactionTestContext::sign_tx(signer.clone(), tx).await;
let raw_tx = envelope.encoded_2718();
let mut data = raw_tx.as_ref();
let recovered = PooledTransactionsElement::decode_2718(&mut data).unwrap();
let pbh_payload = self.valid_proof(
signer.address(),
recovered.hash().as_slice(),
chrono::Utc::now(),
pbh_nonce,
let dt = chrono::Utc::now();
let dt = dt.naive_local();

let month = dt.month() as u8;
let year = dt.year() as u16;

let ext_nullifier = ExternalNullifier::v1(month, year, pbh_nonce);
let (uo, proof) = user_op()
.acc(acc)
.nonce(user_op_nonce)
.external_nullifier(ext_nullifier)
.call();

let data = pbh_bundle(vec![uo], vec![proof]);
let encoded = data.abi_encode();
let tx = tx(
DEV_CHAIN_ID,
Some(Bytes::from(encoded)),
tx_nonce,
PBH_TEST_VALIDATOR,
);

let world_chain_pooled_tx_element = WorldChainPooledTransactionsElement {
inner: recovered,
pbh_payload: Some(pbh_payload.clone()),
};

let mut buff = Vec::<u8>::new();
world_chain_pooled_tx_element.encode_2718(&mut buff);
buff.into()
}

fn valid_proof(
&self,
identity: Address,
tx_hash: &[u8],
time: chrono::DateTime<Utc>,
pbh_nonce: u8,
) -> PbhPayload {
let external_nullifier =
ExternalNullifier::with_date_marker(DateMarker::from(time), pbh_nonce).to_string();

self.create_proof(identity, external_nullifier, tx_hash)
}

fn create_proof(
&self,
mut identity: Address,
external_nullifier: String,
signal: &[u8],
) -> PbhPayload {
let external_nullifier: ExternalNullifier = external_nullifier.parse().unwrap();

let idx = self.identities.get(&identity).unwrap();
let secret = identity.as_mut_slice();
// generate identity
let id = Identity::from_secret(secret, None);
let merkle_proof = self.tree.proof(*idx);

let signal_hash = hash_to_field(signal);
let external_nullifier_hash = external_nullifier.to_word();
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);

let proof = Proof(
generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap(),
);

PbhPayload {
root: self.tree.root(),
nullifier_hash,
external_nullifier,
proof,
}
let envelope = TransactionTestContext::sign_tx(signer(acc), tx).await;
let raw_tx = envelope.encoded_2718();
raw_tx.into()
}
}

#[tokio::test]
async fn test_can_build_pbh_payload() -> eyre::Result<()> {
let mut ctx = WorldChainBuilderTestContext::setup().await?;
let mut pbh_tx_hashes = vec![];
for signer in ctx.pbh_wallets.iter() {
let raw_tx = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0).await;
let signers = ctx.signers.clone();
for signer in signers.into_iter() {
let raw_tx = ctx.raw_pbh_tx_bytes(signer, 0, 0, U256::ZERO).await;
let pbh_hash = ctx.node.rpc.inject_tx(raw_tx.clone()).await?;
pbh_tx_hashes.push(pbh_hash);
}
Expand All @@ -258,16 +200,22 @@ async fn test_can_build_pbh_payload() -> eyre::Result<()> {
#[tokio::test]
async fn test_transaction_pool_ordering() -> eyre::Result<()> {
let mut ctx = WorldChainBuilderTestContext::setup().await?;
let non_pbh_tx = tx(ctx.node.inner.chain_spec().chain.id(), None, 0);
let wallet = ctx.pbh_wallets[0].clone();
let non_pbh_tx = tx(
ctx.node.inner.chain_spec().chain.id(),
None,
0,
Address::default(),
);
let wallet = signer(0);
let signer = EthereumWallet::from(wallet);
let signed = <TransactionRequest as TransactionBuilder<Ethereum>>::build(non_pbh_tx, &signer)
.await
.unwrap();
let non_pbh_hash = ctx.node.rpc.inject_tx(signed.encoded_2718().into()).await?;
let mut pbh_tx_hashes = vec![];
for signer in ctx.pbh_wallets.iter().skip(1) {
let raw_tx = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0).await;
let signers = ctx.signers.clone();
for signer in signers.into_iter().skip(1) {
let raw_tx = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0, U256::ZERO).await;
let pbh_hash = ctx.node.rpc.inject_tx(raw_tx.clone()).await?;
pbh_tx_hashes.push(pbh_hash);
}
Expand Down Expand Up @@ -297,8 +245,8 @@ async fn test_transaction_pool_ordering() -> eyre::Result<()> {
#[tokio::test]
async fn test_invalidate_dup_tx_and_nullifier() -> eyre::Result<()> {
let ctx = WorldChainBuilderTestContext::setup().await?;
let signer = ctx.pbh_wallets[0].clone();
let raw_tx = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0).await;
let signer = 0;
let raw_tx = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0, U256::ZERO).await;
ctx.node.rpc.inject_tx(raw_tx.clone()).await?;
let dup_pbh_hash_res = ctx.node.rpc.inject_tx(raw_tx.clone()).await;
assert!(dup_pbh_hash_res.is_err());
Expand All @@ -308,11 +256,11 @@ async fn test_invalidate_dup_tx_and_nullifier() -> eyre::Result<()> {
#[tokio::test]
async fn test_dup_pbh_nonce() -> eyre::Result<()> {
let mut ctx = WorldChainBuilderTestContext::setup().await?;
let signer = ctx.pbh_wallets[0].clone();
let signer = 0;

let raw_tx_0 = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0).await;
let raw_tx_0 = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0, U256::ZERO).await;
ctx.node.rpc.inject_tx(raw_tx_0.clone()).await?;
let raw_tx_1 = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0).await;
let raw_tx_1 = ctx.raw_pbh_tx_bytes(signer.clone(), 0, 0, U256::ZERO).await;

// Now that the nullifier has successfully been stored in
// the `ExecutedPbhNullifierTable`, inserting a new tx with the
Expand Down Expand Up @@ -349,11 +297,11 @@ pub fn optimism_payload_attributes(timestamp: u64) -> OpPayloadBuilderAttributes
}
}

fn tx(chain_id: u64, data: Option<Bytes>, nonce: u64) -> TransactionRequest {
fn tx(chain_id: u64, data: Option<Bytes>, nonce: u64, to: Address) -> TransactionRequest {
TransactionRequest {
nonce: Some(nonce),
value: Some(U256::from(100)),
to: Some(TxKind::Call(Address::random())),
to: Some(TxKind::Call(to)),
gas: Some(210000),
max_fee_per_gas: Some(20e10 as u128),
max_priority_fee_per_gas: Some(20e10 as u128),
Expand All @@ -365,14 +313,14 @@ fn tx(chain_id: u64, data: Option<Bytes>, nonce: u64) -> TransactionRequest {

/// Builds an OP Mainnet chain spec with the given merkle root
/// Populated in the OpWorldID contract.
fn get_chain_spec(merkle_root: Field) -> OpChainSpec {
fn get_chain_spec() -> OpChainSpec {
let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap();
OpChainSpecBuilder::base_mainnet()
.genesis(genesis.extend_accounts(vec![(
OP_WORLD_ID,
GenesisAccount::default().with_storage(Some(BTreeMap::from_iter(vec![(
LATEST_ROOT_SLOT.into(),
merkle_root.into(),
tree_root().into(),
)]))),
)]))
.ecotone_activated()
Expand Down
6 changes: 3 additions & 3 deletions world-chain-builder/crates/world/pool/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,13 @@ pub async fn eth_tx(acc: u32, mut tx: TxEip1559) -> EthPooledTransaction {
pub fn user_op(
acc: u32,
#[builder(into, default = U256::ZERO)] nonce: U256,
#[builder(default = ExternalNullifier::v1(1, 2025, 0))] external_nullifier: ExternalNullifier,
#[builder(default = ExternalNullifier::v1(12, 2024, 0))] external_nullifier: ExternalNullifier,
) -> (IEntryPoint::PackedUserOperation, PbhPayload) {
let sender = account(acc);

let user_op = PackedUserOperation {
sender,
nonce: nonce,
nonce,
..Default::default()
};

Expand Down Expand Up @@ -178,7 +178,7 @@ pub fn pbh_bundle(
signature: signature_buff.into(),
aggregator: PBH_TEST_SIGNATURE_AGGREGATOR,
}],
_1: Address::ZERO,
_1: PBH_TEST_VALIDATOR,
}
}

Expand Down

0 comments on commit 5268032

Please sign in to comment.