Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: odyssey_sendTransaction #56

Merged
merged 9 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,44 @@

<!-- [![Crates.io][crates-badge]][crates-io] -->
<!-- [![Downloads][downloads-badge]][crates-io] -->

[![MIT License][mit-badge]][mit-url]
[![Apache-2.0 License][apache-badge]][apache-url]
[![CI Status][actions-badge]][actions-url]

## What is Odyssey?

Odyssey is a testnet OP Stack rollup aimed at enabling experimentation of bleeding edge Ethereum Research.
Odyssey is __not__ a fork of reth.
Odyssey is **not** a fork of reth.
Odyssey implements traits provided by the [reth node builder API](https://paradigmxyz.github.io/reth/docs/reth_node_builder/index.html), allowing implementation of precompiles and instructions of experimental EIPs without forking the node.

Specifically, Odyssey currently implements the following EIPs:
- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Set EOA account code.
- [RIP-7212](https://ethereum-magicians.org/t/eip-7212-precompiled-for-secp256r1-curve-support/14789): Precompile for secp256r1 curve support.
- [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537): Precompiles for BLS12-381 curve operations.

- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Set EOA account code.
- [RIP-7212](https://ethereum-magicians.org/t/eip-7212-precompiled-for-secp256r1-curve-support/14789): Precompile for secp256r1 curve support.
- [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537): Precompiles for BLS12-381 curve operations.

Odyssey also implements the EIPs for EOF, or [The EVM Object Format](https://evmobjectformat.org/).

### Why Odyssey?

Odyssey has 2 goals:

1. Showcase Reth's performance at the extremes. We intend to launch a hosted version of Odyssey on [Conduit](https://conduit.xyz/), targeting 50mgas/s, and eventually ramping up to 1ggas/s and beyond. In the process we hope to hit the state growth performance bottleneck, and discover ways to solve it. If our hosted chains end up getting too big, we may possibly restart the experiment from zero, and try again.
2. Showcase how Reth's modular architecture can serve as a distribution channel for research ideas. Specifically,
Odyssey's node extensions were chosen for their ability to enable applications that enhance the onchain user experience, and
drastically reduce cost for existing applications that improve UX.
Odyssey's node extensions were chosen for their ability to enable applications that enhance the onchain user experience, and
drastically reduce cost for existing applications that improve UX.

### Odyssey Testnet

> [!TIP]
> [The Odyssey Testnet](https://www.ithaca.xyz/updates/odyssey#odyssey-chapter-1-is-live-on-testnet) is now live on Sepolia and is built with Reth, the OP Stack, and [deployed on Conduit](https://app.conduit.xyz/published/view/odyssey).
> [!TIP] > [The Odyssey Testnet](https://www.ithaca.xyz/updates/odyssey#odyssey-chapter-1-is-live-on-testnet) is now live on Sepolia and is built with Reth, the OP Stack, and [deployed on Conduit](https://app.conduit.xyz/published/view/odyssey).

### Odyssey Local Development

Odyssey can be run locally for development and testing purposes. To do this, the binary can be run with the `--dev` flag, which will start the node with a development configuration.

First, odyssey should be built locally:

```bash
git clone https://github.com/ithacaxyz/odyssey
cd odyssey
Expand Down Expand Up @@ -130,12 +133,12 @@ Consult the [Kurtosis OP package](https://github.com/ethpandaops/optimism-packag

Odyssey has a custom `wallet_` namespace, that allows users to delegate their EOAs to a contract using EIP-7702, and perform transactions on those accounts, all funded by the sequencer.

To enable this namespace, set the environment variable `EXP1_SK` to a private key that will sign the transactions, and `EXP1_WHITELIST` to a comma-delimited list of checksummed addresses accounts are allowed to delegate to. The new RPC method, `wallet_sendTransaction`, will only sign transactions that either:
To enable this namespace, set the environment variable `EXP1_SK` to a private key that will sign the transactions, and `EXP1_WHITELIST` to a comma-delimited list of checksummed addresses accounts are allowed to delegate to. The new RPC method, `odyssey_sendTransaction`, will only sign transactions that either:

1. Delegate accounts to one of the whitelisted addresses using EIP-7702, or
1. Send transactions to an EIP-7702 EOA that is already delegated to a whitelisted address

The `wallet_sendTransaction` endpoint accepts the same fields as `eth_sendTransaction`, with these notable exceptions:
The `odyssey_sendTransaction` endpoint accepts the same fields as `eth_sendTransaction`, with these notable exceptions:

1. `nonce` must not be set, as this is managed by the node
1. `value` must be unset or 0
Expand All @@ -147,7 +150,7 @@ The following fields are ignored, as they are overwritten internally:
1. `gasLimit`
1. `chainId`

To get the list of contracts that are whitelisted for `wallet_sendTransaction`, you can query `wallet_getCapabilities`.
To get the list of contracts that are whitelisted for `odyssey_sendTransaction`, you can query `wallet_getCapabilities`.

### Security

Expand All @@ -171,6 +174,7 @@ shall be dual licensed as above, without any additional terms or conditions.
<!-- [crates-badge]: https://img.shields.io/crates/v/odyssey.svg -->
<!-- [crates-io]: https://crates.io/crates/odyssey -->
<!-- [downloads-badge]: https://img.shields.io/crates/d/odyssey -->

[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[apache-badge]: https://img.shields.io/badge/license-Apache--2.0-blue.svg
[mit-url]: LICENSE-MIT
Expand Down
44 changes: 44 additions & 0 deletions crates/e2e-tests/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,47 @@ async fn test_wallet_api() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

// This is new endpoint `odyssey_sendTransaction`, upper test will be deprecate in the future.
#[tokio::test]
async fn test_new_wallet_api() -> Result<(), Box<dyn std::error::Error>> {
if !ci_info::is_ci() {
return Ok(());
}

let provider = ProviderBuilder::new().on_http(REPLICA_RPC.clone());
let signer = PrivateKeySigner::from_bytes(&b256!(
"59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
))?;

let capabilities: BTreeMap<U256, BTreeMap<String, BTreeMap<String, Vec<Address>>>> =
provider.client().request_noparams("wallet_getCapabilities").await?;

let chain_id = U256::from(provider.get_chain_id().await?);

let delegation_address =
capabilities.get(&chain_id).unwrap().get("delegation").unwrap().get("addresses").unwrap()
[0];

let auth = Authorization {
chain_id: provider.get_chain_id().await?,
address: delegation_address,
nonce: provider.get_transaction_count(signer.address()).await?,
};

let signature = signer.sign_hash_sync(&auth.signature_hash())?;
let auth = auth.into_signed(signature);

let tx =
TransactionRequest::default().with_authorization_list(vec![auth]).with_to(signer.address());

let tx_hash: B256 = provider.client().request("odyssey_sendTransaction", vec![tx]).await?;
jenpaff marked this conversation as resolved.
Show resolved Hide resolved

let receipt = PendingTransactionBuilder::new(provider.clone(), tx_hash).get_receipt().await?;

assert!(receipt.status());

assert!(!provider.get_code_at(signer.address()).await?.is_empty());

Ok(())
}
18 changes: 10 additions & 8 deletions crates/wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
//!
//! - `wallet_getCapabilities` based on [EIP-5792][eip-5792], with the only capability being
jenpaff marked this conversation as resolved.
Show resolved Hide resolved
//! `delegation`.
//! - `wallet_sendTransaction` that can perform sequencer-sponsored [EIP-7702][eip-7702] delegations
//! and send other sequencer-sponsored transactions on behalf of EOAs with delegated code.
//! - `odyssey_sendTransaction` that can perform sequencer-sponsored [EIP-7702][eip-7702]
//! delegations and send other sequencer-sponsored transactions on behalf of EOAs with delegated
//! code.
//!
//! # Restrictions
//!
//! `wallet_sendTransaction` has additional verifications in place to prevent some rudimentary abuse
//! of the sequencer's funds. For example, transactions cannot contain any `value`.
//! `odyssey_sendTransaction` has additional verifications in place to prevent some
//! rudimentary abuse of the sequencer's funds. For example, transactions cannot contain any
//! `value`.
//!
//! [eip-5792]: https://eips.ethereum.org/EIPS/eip-5792
//! [eip-7702]: https://eips.ethereum.org/EIPS/eip-7702
Expand Down Expand Up @@ -98,7 +100,7 @@ pub trait OdysseyWalletApi {
///
/// [eip-7702]: https://eips.ethereum.org/EIPS/eip-7702
/// [eip-1559]: https://eips.ethereum.org/EIPS/eip-1559
#[method(name = "sendTransaction")]
#[method(name = "sendTransaction", aliases = ["odyssey_sendTransaction"])]
async fn send_transaction(&self, request: TransactionRequest) -> RpcResult<TxHash>;
}

Expand Down Expand Up @@ -211,7 +213,7 @@ where
}

async fn send_transaction(&self, mut request: TransactionRequest) -> RpcResult<TxHash> {
trace!(target: "rpc::wallet", ?request, "Serving wallet_sendTransaction");
trace!(target: "rpc::wallet", ?request, "Serving odyssey_sendTransaction");

// validate fields common to eip-7702 and eip-1559
if let Err(err) = validate_tx_request(&request) {
Expand Down Expand Up @@ -379,9 +381,9 @@ fn validate_tx_request(request: &TransactionRequest) -> Result<(), OdysseyWallet
#[derive(Metrics)]
#[metrics(scope = "wallet")]
struct WalletMetrics {
/// Number of invalid calls to `wallet_sendTransaction`
/// Number of invalid calls to `odyssey_sendTransaction`
invalid_send_transaction_calls: Counter,
/// Number of valid calls to `wallet_sendTransaction`
/// Number of valid calls to `odyssey_sendTransaction`
valid_send_transaction_calls: Counter,
}

Expand Down