Skip to content

Commit

Permalink
Allow for BlockNumber based deployment information (#482)
Browse files Browse the repository at this point in the history
[Geth v1.10.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.0) changed their data retention policy to not include transaction inclusion records that are older than one year:

> Geth 1.10.0 contains some changes which remove unnecessary data in the blockchain database. In particular, Geth no longer keeps transaction inclusion info for all transactions, and instead limits the storage of inclusion records to one year. 

This is a problem for the https://github.com/gnosis/dex-services codebase, as it depends on contracts that have been deployed more [than a year ago](https://rinkeby.etherscan.io/tx/0xc5bf4f48747093a79b623c2a2392ccbdb73a3d788e7d1f0f53f640ea18496d90).

The problem in particular happens in `AllEventsBuilder::query_paginated` when we try to map the deployment transaction hash into a block number to decide the earliest point from which to query events.

This PR suggests a fix that changes the deployment information from being purely tx hash based to an enum that can be either a tx hash or a block number. This will allow us to override the deployment hash with the corresponding block number and avoid a `transaction_receipt` RPC call for old contracts.

### Test Plan
Unit tests and manually tested that the override works in `past_events` example.
  • Loading branch information
fleupold authored Mar 22, 2021
1 parent 60c207c commit 5bcebce
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ This example retrieves the entire event history of token OWL contract and prints
the total number of events since deployment.

Note the special handling of the `tokenOWLProxy` contract and how it is cast into
a `tokenOWL` instance using Contract's `with_transaction` feature.
a `tokenOWL` instance using Contract's `with_deployment_info` feature.

```sh
export INFURA_PROJECT_ID="Infura project ID"
Expand Down
23 changes: 23 additions & 0 deletions ethcontract-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,28 @@ pub use crate::abiext::FunctionExt;
pub use crate::bytecode::Bytecode;
pub use crate::truffle::Artifact;
pub use ethabi::{self as abi, Contract as Abi};
use serde::Deserialize;
pub use web3::types::Address;
pub use web3::types::H256 as TransactionHash;

/// Information about when a contract instance was deployed
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(untagged)]
pub enum DeploymentInformation {
/// The block at which the contract was deployed
BlockNumber(u64),
/// The transaction hash at which the contract was deployed
TransactionHash(TransactionHash),
}

impl From<u64> for DeploymentInformation {
fn from(block: u64) -> Self {
Self::BlockNumber(block)
}
}

impl From<TransactionHash> for DeploymentInformation {
fn from(hash: TransactionHash) -> Self {
Self::TransactionHash(hash)
}
}
6 changes: 3 additions & 3 deletions ethcontract-common/src/truffle.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! Module for reading and examining data produced by truffle.
use crate::bytecode::Bytecode;
use crate::errors::ArtifactError;
use crate::{bytecode::Bytecode, DeploymentInformation};
use ethabi::Contract as Abi;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;
use web3::types::{Address, H256};
use web3::types::Address;

/// Represents a truffle artifact.
#[derive(Clone, Debug, Deserialize)]
Expand Down Expand Up @@ -74,7 +74,7 @@ pub struct Network {
pub address: Address,
/// The hash of the transaction that deployed the contract on this network.
#[serde(rename = "transactionHash")]
pub transaction_hash: Option<H256>,
pub deployment_information: Option<DeploymentInformation>,
}

/// A contract's documentation.
Expand Down
4 changes: 2 additions & 2 deletions ethcontract-generate/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod types;
use crate::util;
use crate::Args;
use anyhow::{anyhow, Context as _, Result};
use ethcontract_common::{Address, Artifact, TransactionHash};
use ethcontract_common::{Address, Artifact, DeploymentInformation};
use inflector::Inflector;
use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote;
Expand All @@ -22,7 +22,7 @@ use syn::{Path, Visibility};

pub(crate) struct Deployment {
pub address: Address,
pub transaction_hash: Option<TransactionHash>,
pub deployment_information: Option<DeploymentInformation>,
}

/// Internal shared context for generating smart contract bindings.
Expand Down
59 changes: 36 additions & 23 deletions ethcontract-generate/src/contract/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::contract::Context;
use crate::util::expand_doc;
use ethcontract_common::{Address, TransactionHash};
use ethcontract_common::{Address, DeploymentInformation};
use proc_macro2::{Literal, TokenStream};
use quote::quote;

Expand All @@ -19,14 +19,15 @@ pub(crate) fn expand(cx: &Context) -> TokenStream {
let deployments = cx.deployments.iter().map(|(network_id, deployment)| {
let network_id = Literal::string(&network_id.to_string());
let address = expand_address(deployment.address);
let transaction_hash = expand_transaction_hash(deployment.transaction_hash);
let deployment_information =
expand_deployment_information(deployment.deployment_information);

quote! {
artifact.networks.insert(
#network_id.to_owned(),
self::ethcontract::common::truffle::Network {
address: #address,
transaction_hash: #transaction_hash,
deployment_information: #deployment_information,
},
);
}
Expand Down Expand Up @@ -74,7 +75,7 @@ pub(crate) fn expand(cx: &Context) -> TokenStream {
> + Send + Unpin + 'static,
T: self::ethcontract::web3::Transport<Out = F> + Send + Sync + 'static,
{
Contract::with_transaction(web3, address, None)
Contract::with_deployment_info(web3, address, None)
}


Expand All @@ -86,10 +87,10 @@ pub(crate) fn expand(cx: &Context) -> TokenStream {
/// Note that this does not verify that a contract with a matching `Abi` is
/// actually deployed at the given address nor that the transaction hash,
/// when provided, is actually for this contract deployment.
pub fn with_transaction<F, T>(
pub fn with_deployment_info<F, T>(
web3: &self::ethcontract::web3::api::Web3<T>,
address: self::ethcontract::Address,
transaction_hash: Option<self::ethcontract::TransactionHash>,
deployment_information: Option<ethcontract::common::DeploymentInformation>,
) -> Self
where
F: std::future::Future<
Expand All @@ -104,7 +105,7 @@ pub(crate) fn expand(cx: &Context) -> TokenStream {
let transport = DynTransport::new(web3.transport().clone());
let web3 = Web3::new(transport);
let abi = Self::artifact().abi.clone();
let instance = Instance::with_transaction(web3, abi, address, transaction_hash);
let instance = Instance::with_deployment_info(web3, abi, address, deployment_information);

Contract::from_raw(instance)
}
Expand All @@ -120,10 +121,10 @@ pub(crate) fn expand(cx: &Context) -> TokenStream {
self.raw_instance().address()
}

/// Returns the hash for the transaction that deployed the contract
/// Returns the deployment information of the contract
/// if it is known, `None` otherwise.
pub fn transaction_hash(&self) -> Option<self::ethcontract::TransactionHash> {
self.raw_instance().transaction_hash()
pub fn deployment_information(&self) -> Option<ethcontract::common::DeploymentInformation> {
self.raw_instance().deployment_information()
}

/// Returns a reference to the default method options used by this
Expand Down Expand Up @@ -175,17 +176,20 @@ fn expand_address(address: Address) -> TokenStream {
}
}

/// Expands a transaction hash into a literal representation that can be used
/// Expands a deployment info into a literal representation that can be used
/// with quasi-quoting for code generation.
fn expand_transaction_hash(hash: Option<TransactionHash>) -> TokenStream {
let hash = match hash {
Some(hash) => hash,
fn expand_deployment_information(deployment: Option<DeploymentInformation>) -> TokenStream {
match deployment {
Some(DeploymentInformation::BlockNumber(block)) => quote! {
Some(ethcontract::common::DeploymentInformation::BlockNumber(#block))
},
Some(DeploymentInformation::TransactionHash(hash)) => {
let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed);
quote! {
Some(ethcontract::common::DeploymentInformation::TransactionHash([#( #bytes ),*].into()))
}
}
None => return quote! { None },
};

let bytes = hash.as_bytes().iter().copied().map(Literal::u8_unsuffixed);
quote! {
Some(self::ethcontract::TransactionHash([#( #bytes ),*]))
}
}

Expand Down Expand Up @@ -213,14 +217,23 @@ mod tests {

#[test]
#[rustfmt::skip]
fn expand_transaction_hash_value() {
assert_quote!(expand_transaction_hash(None), { None });
fn expand_deployment_information_value() {
assert_quote!(expand_deployment_information(None), { None });

assert_quote!(
expand_transaction_hash(Some("000102030405060708090a0b0c0d0e0f10111213000000000000000000000000".parse().unwrap())),
expand_deployment_information(Some(DeploymentInformation::TransactionHash("000102030405060708090a0b0c0d0e0f10111213000000000000000000000000".parse().unwrap()))),
{
Some(self::ethcontract::TransactionHash([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
Some(ethcontract::common::DeploymentInformation::TransactionHash([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into()))
},
);

assert_quote!(
expand_deployment_information(Some(DeploymentInformation::BlockNumber(42))),
{
Some(ethcontract::common::DeploymentInformation::BlockNumber(42u64))
},
);


}
}
2 changes: 1 addition & 1 deletion ethcontract-generate/src/contract/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ fn expand_deploy(cx: &Context) -> Result<TokenStream> {
transaction_hash: self::ethcontract::H256,
_: Self::Context,
) -> Self {
Self::with_transaction(&web3, address, Some(transaction_hash))
Self::with_deployment_info(&web3, address, Some(transaction_hash.into()))
}
}
})
Expand Down
2 changes: 1 addition & 1 deletion ethcontract-generate/src/contract/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ fn expand_all_events(cx: &Context) -> TokenStream {
self::ethcontract::dyns::DynAllEventsBuilder::new(
self.raw_instance().web3(),
self.address(),
self.transaction_hash(),
self.deployment_information(),
)
}
}
Expand Down
7 changes: 4 additions & 3 deletions ethcontract-generate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub use crate::source::Source;
pub use crate::util::parse_address;
use anyhow::Result;
use contract::Deployment;
use ethcontract_common::DeploymentInformation;
pub use ethcontract_common::{Address, TransactionHash};
use proc_macro2::TokenStream;
use std::collections::HashMap;
Expand Down Expand Up @@ -156,7 +157,7 @@ impl Builder {
}

/// Manually adds specifies the deployed address and deployment transaction
/// hash of a contract for a given network. Note that manually specified
/// hash or block of a contract for a given network. Note that manually specified
/// deployments take precedence over deployments in the Truffle artifact (in
/// the `networks` property of the artifact).
///
Expand All @@ -168,11 +169,11 @@ impl Builder {
mut self,
network_id: u32,
address: Address,
transaction_hash: Option<TransactionHash>,
deployment_information: Option<DeploymentInformation>,
) -> Self {
let deployment = Deployment {
address,
transaction_hash,
deployment_information,
};
self.args.deployments.insert(network_id, deployment);
self
Expand Down
39 changes: 24 additions & 15 deletions ethcontract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::errors::{DeployError, LinkError};
use ethcontract_common::abi::{Error as AbiError, Result as AbiResult};
use ethcontract_common::abiext::FunctionExt;
use ethcontract_common::hash::H32;
use ethcontract_common::{Abi, Artifact, Bytecode};
use ethcontract_common::{Abi, Artifact, Bytecode, DeploymentInformation};
use std::collections::HashMap;
use std::hash::Hash;
use web3::api::Web3;
Expand All @@ -32,7 +32,7 @@ pub struct Instance<T: Transport> {
web3: Web3<T>,
abi: Abi,
address: Address,
transaction_hash: Option<H256>,
deployment_information: Option<DeploymentInformation>,
/// Default method parameters to use when sending method transactions or
/// querying method calls.
pub defaults: MethodDefaults,
Expand All @@ -52,7 +52,7 @@ impl<T: Transport> Instance<T> {
/// Note that this does not verify that a contract with a matching `Abi` is
/// actually deployed at the given address.
pub fn at(web3: Web3<T>, abi: Abi, address: Address) -> Self {
Instance::with_transaction(web3, abi, address, None)
Instance::with_deployment_info(web3, abi, address, None)
}

/// Creates a new contract instance with the specified `web3` provider with
Expand All @@ -63,11 +63,11 @@ impl<T: Transport> Instance<T> {
/// Note that this does not verify that a contract with a matching `Abi` is
/// actually deployed at the given address nor that the transaction hash,
/// when provided, is actually for this contract deployment.
pub fn with_transaction(
pub fn with_deployment_info(
web3: Web3<T>,
abi: Abi,
address: Address,
transaction_hash: Option<H256>,
deployment_information: Option<DeploymentInformation>,
) -> Self {
let methods = create_mapping(&abi.functions, |function| function.selector());
let events = create_mapping(&abi.events, |event| event.signature());
Expand All @@ -76,7 +76,7 @@ impl<T: Transport> Instance<T> {
web3,
abi,
address,
transaction_hash,
deployment_information,
defaults: MethodDefaults::default(),
methods,
events,
Expand All @@ -95,11 +95,11 @@ impl<T: Transport> Instance<T> {
.get(&network_id)
.ok_or(DeployError::NotFound(network_id))?;

Ok(Instance::with_transaction(
Ok(Instance::with_deployment_info(
web3,
artifact.abi,
network.address,
network.transaction_hash,
network.deployment_information,
))
}

Expand Down Expand Up @@ -154,8 +154,8 @@ impl<T: Transport> Instance<T> {

/// Returns the hash for the transaction that deployed the contract if it is
/// known, `None` otherwise.
pub fn transaction_hash(&self) -> Option<H256> {
self.transaction_hash
pub fn deployment_information(&self) -> Option<DeploymentInformation> {
self.deployment_information
}

/// Returns a method builder to setup a call or transaction on a smart
Expand Down Expand Up @@ -239,7 +239,7 @@ impl<T: Transport> Instance<T> {
/// Returns a log stream that emits a log for every new event emitted after
/// the stream was created for this contract instance.
pub fn all_events(&self) -> AllEventsBuilder<T, RawLog> {
AllEventsBuilder::new(self.web3(), self.address(), self.transaction_hash())
AllEventsBuilder::new(self.web3(), self.address(), self.deployment_information())
}
}

Expand Down Expand Up @@ -309,7 +309,12 @@ impl<T: Transport> Deploy<T> for Instance<T> {
transaction_hash: H256,
cx: Self::Context,
) -> Self {
Instance::with_transaction(web3, cx.abi, address, Some(transaction_hash))
Instance::with_deployment_info(
web3,
cx.abi,
address,
Some(DeploymentInformation::TransactionHash(transaction_hash)),
)
}
}

Expand Down Expand Up @@ -350,14 +355,13 @@ mod tests {

let network_id = "42";
let address = addr!("0x0102030405060708091011121314151617181920");
let transaction_hash = Some(H256::repeat_byte(0x42));
let artifact = {
let mut artifact = Artifact::empty();
artifact.networks.insert(
network_id.to_string(),
Network {
address,
transaction_hash,
deployment_information: Some(H256::repeat_byte(0x42).into()),
},
);
artifact
Expand All @@ -372,7 +376,12 @@ mod tests {
transport.assert_no_more_requests();

assert_eq!(instance.address(), address);
assert_eq!(instance.transaction_hash(), transaction_hash);
assert_eq!(
instance.deployment_information(),
Some(DeploymentInformation::TransactionHash(H256::repeat_byte(
0x42
)))
);
}

#[test]
Expand Down
Loading

0 comments on commit 5bcebce

Please sign in to comment.