Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Add getProof to provider #459

Merged
merged 6 commits into from
Sep 18, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions ethers-core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ pub use txpool::*;

mod trace;
pub use trace::*;

mod proof;
pub use proof::*;
19 changes: 19 additions & 0 deletions ethers-core/src/types/proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::types::{Bytes, H256, U256};
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct StorageProof {
pub key: H256,
pub proof: Vec<Bytes>,
pub value: U256,
}

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct EIP1186ProofResponse {
balance: U256,
code_hash: H256,
nonce: U256,
storage_hash: H256,
account_proof: Vec<Bytes>,
storage_proof: Vec<StorageProof>,
}
87 changes: 87 additions & 0 deletions ethers-middleware/tests/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,90 @@ async fn ds_proxy_code() {
assert_eq!(last_sender, wallet_addr.into());
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
}

#[tokio::test]
#[cfg(not(feature = "celo"))]
async fn ds_proxy_get_proof() {
// randomness
let mut rng = rand::thread_rng();

// spawn ganache and instantiate a signer middleware.
let ganache = Ganache::new().spawn();
let wallet: LocalWallet = ganache.keys()[1].clone().into();
let provider = Provider::<Http>::try_from(ganache.endpoint())
.unwrap()
.interval(Duration::from_millis(10u64));
let chain_id = provider.get_chainid().await.unwrap().as_u64();
let wallet = wallet.with_chain_id(chain_id);
let signer_middleware = SignerMiddleware::new(provider.clone(), wallet);
let wallet_addr = signer_middleware.address();
let provider = Arc::new(signer_middleware.clone());

// deploy DsProxyFactory which we'll use to deploy a new DsProxy contract.
let compiled = Solc::new("./tests/solidity-contracts/DSProxy.sol")
.build()
.expect("could not compile DSProxyFactory");
let contract = compiled
.get("DSProxyFactory")
.expect("could not find DSProxyFactory");
let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
Arc::clone(&provider),
);
let ds_proxy_factory = factory.deploy(()).unwrap().legacy();
let ds_proxy_factory = ds_proxy_factory.send().await.unwrap();

// deploy a new DsProxy contract.
let ds_proxy = DsProxy::build::<HttpWallet, Arc<HttpWallet>>(
Arc::clone(&provider),
Some(ds_proxy_factory.address()),
provider.address(),
)
.await
.unwrap();
let ds_proxy_addr = ds_proxy.address();

// compile the SimpleStorage contract which we will use to interact via DsProxy.
let compiled = Solc::new("./tests/solidity-contracts/SimpleStorage.sol")
.build()
.expect("could not compile SimpleStorage");
let ss = compiled
.get("SimpleStorage")
.expect("could not find SimpleStorage");
let ss_base_contract: BaseContract = ss.abi.clone().into();
let expected_value: u64 = rng.gen();
let calldata = ss_base_contract
.encode("setValue", U256::from(expected_value))
.expect("could not get ABI encoded data");

// execute code via the deployed DsProxy contract.
ds_proxy
.execute::<HttpWallet, Arc<HttpWallet>, Bytes>(
Arc::clone(&provider),
ss.bytecode.clone(),
calldata,
)
.expect("could not construct DSProxy contract call")
.legacy()
.send()
.await
.unwrap();

// verify that DsProxy's state was updated and get proof
let last_sender = provider
.get_storage_at(ds_proxy_addr, H256::zero(), None)
.await
.unwrap();
let last_value = provider
.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None)
.await
.unwrap();
let proof = provider
.get_proof(ds_proxy_addr, vec![H256::from_low_u64_be(1u64)], None)
.await
.unwrap();
assert!(proof);
assert_eq!(last_sender, wallet_addr.into());
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
}
13 changes: 13 additions & 0 deletions ethers-providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pub use pubsub::{PubsubClient, SubscriptionStream};
use async_trait::async_trait;
use auto_impl::auto_impl;
use ethers_core::types::transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed};
use ethers_core::types::StorageProof;
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
use std::{error::Error, fmt::Debug, future::Future, pin::Pin, str::FromStr};

Expand Down Expand Up @@ -548,6 +549,18 @@ pub trait Middleware: Sync + Send + Debug {
.map_err(FromErr::from)
}

async fn get_proof<T: Into<NameOrAddress> + Send + Sync>(
&self,
from: T,
locations: Vec<H256>,
block: Option<BlockId>,
) -> Result<EIP1186ProofResponse, Self::Error> {
self.inner()
.get_proof(from, locations, block)
.await
.map_err(FromErr::from)
}

// Mempool inspection for Geth's API

async fn txpool_content(&self) -> Result<TxpoolContent, Self::Error> {
Expand Down
112 changes: 109 additions & 3 deletions ethers-providers/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use ethers_core::{
abi::{self, Detokenize, ParamType},
types::{
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, Filter, Log, NameOrAddress,
Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt,
TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, Filter, Log,
NameOrAddress, Selector, Signature, TraceFilter, TraceType, Transaction,
TransactionReceipt, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
},
utils,
};
Expand Down Expand Up @@ -582,6 +582,29 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
self.request("eth_getCode", [at, block]).await
}

/// Returns the EIP-1186 proof response
/// https://github.com/ethereum/EIPs/issues/1186
async fn get_proof<T: Into<NameOrAddress> + Send + Sync>(
&self,
from: T,
locations: Vec<H256>,
block: Option<BlockId>,
) -> Result<EIP1186ProofResponse, ProviderError> {
let from = match from.into() {
NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?,
NameOrAddress::Address(addr) => addr,
};

let from = utils::serialize(&from);
let locations = locations
.iter()
.map(|location| utils::serialize(&location))
.collect();
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));

self.request("eth_getProof", [from, locations, block]).await
}

////// Ethereum Naming Service
// The Ethereum Naming Service (ENS) allows easy to remember and use names to
// be assigned to Ethereum addresses. Any provider operation which takes an address
Expand Down Expand Up @@ -804,6 +827,89 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
)
.await)
}

async fn fill_transaction(
drewstone marked this conversation as resolved.
Show resolved Hide resolved
&self,
tx: &mut TypedTransaction,
block: Option<BlockId>,
) -> Result<(), Self::Error> {
let mut tx_clone = tx.clone();

// TODO: Maybe deduplicate the code in a nice way
match tx {
TypedTransaction::Legacy(ref mut inner) => {
if let Some(NameOrAddress::Name(ref ens_name)) = inner.to {
let addr = self.resolve_name(ens_name).await?;
inner.to = Some(addr.into());
tx_clone.set_to(addr);
};

if inner.from.is_none() {
inner.from = self.default_sender();
}

let (gas_price, gas) = futures_util::try_join!(
maybe(inner.gas_price, self.get_gas_price()),
maybe(inner.gas, self.estimate_gas(&tx_clone)),
)?;
inner.gas = Some(gas);
inner.gas_price = Some(gas_price);
}
TypedTransaction::Eip2930(inner) => {
if let Ok(lst) = self.create_access_list(&tx_clone, block).await {
inner.access_list = lst.access_list;
}

if let Some(NameOrAddress::Name(ref ens_name)) = inner.tx.to {
let addr = self.resolve_name(ens_name).await?;
inner.tx.to = Some(addr.into());
tx_clone.set_to(addr);
};

if inner.tx.from.is_none() {
inner.tx.from = self.default_sender();
}

let (gas_price, gas) = futures_util::try_join!(
maybe(inner.tx.gas_price, self.get_gas_price()),
maybe(inner.tx.gas, self.estimate_gas(&tx_clone)),
)?;
inner.tx.gas = Some(gas);
inner.tx.gas_price = Some(gas_price);
}
TypedTransaction::Eip1559(inner) => {
if let Ok(lst) = self.create_access_list(&tx_clone, block).await {
inner.access_list = lst.access_list;
}

if let Some(NameOrAddress::Name(ref ens_name)) = inner.to {
let addr = self.resolve_name(ens_name).await?;
inner.to = Some(addr.into());
tx_clone.set_to(addr);
};

if inner.from.is_none() {
inner.from = self.default_sender();
}

let gas = crate::maybe(inner.gas, self.estimate_gas(&tx_clone)).await?;
inner.gas = Some(gas);

if inner.max_fee_per_gas.is_none() || inner.max_priority_fee_per_gas.is_none() {
let (max_fee_per_gas, max_priority_fee_per_gas) =
self.estimate_eip1559_fees(None).await?;
if inner.max_fee_per_gas.is_none() {
inner.max_fee_per_gas = Some(max_fee_per_gas);
}
if inner.max_priority_fee_per_gas.is_none() {
inner.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
}
}
}
};

Ok(())
}
}

impl<P: JsonRpcClient> Provider<P> {
Expand Down
1 change: 1 addition & 0 deletions ethers-providers/src/transports/quorum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl<T: JsonRpcClientWrapper> QuorumProvider<T> {
| "eth_createAccessList"
| "eth_getStorageAt"
| "eth_getCode"
| "eth_getProof"
| "trace_call"
| "trace_block" => {
// calls that include the block number in the params at the last index of json array
Expand Down