From 8561a95ee9f9c2220624f19b5f1f708fcbe0cf9f Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 18 Nov 2019 16:08:19 +1100 Subject: [PATCH] Add `deposit_contract` crate --- Cargo.toml | 1 + eth2/utils/deposit_contract/.gitignore | 1 + eth2/utils/deposit_contract/Cargo.toml | 17 ++++++ .../utils/deposit_contract}/build.rs | 0 eth2/utils/deposit_contract/src/lib.rs | 56 +++++++++++++++++++ tests/eth1_test_rig/Cargo.toml | 7 +-- tests/eth1_test_rig/src/lib.rs | 39 +++++++------ 7 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 eth2/utils/deposit_contract/.gitignore create mode 100644 eth2/utils/deposit_contract/Cargo.toml rename {tests/eth1_test_rig => eth2/utils/deposit_contract}/build.rs (100%) create mode 100644 eth2/utils/deposit_contract/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index f7abd8ae2b..6fc81d267f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "eth2/utils/bls", "eth2/utils/compare_fields", "eth2/utils/compare_fields_derive", + "eth2/utils/deposit_contract", "eth2/utils/eth2_config", "eth2/utils/eth2_interop_keypairs", "eth2/utils/logging", diff --git a/eth2/utils/deposit_contract/.gitignore b/eth2/utils/deposit_contract/.gitignore new file mode 100644 index 0000000000..81b46ff033 --- /dev/null +++ b/eth2/utils/deposit_contract/.gitignore @@ -0,0 +1 @@ +contract/ diff --git a/eth2/utils/deposit_contract/Cargo.toml b/eth2/utils/deposit_contract/Cargo.toml new file mode 100644 index 0000000000..1d28546dab --- /dev/null +++ b/eth2/utils/deposit_contract/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "deposit_contract" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +build = "build.rs" + +[build-dependencies] +reqwest = "0.9.20" +serde_json = "1.0" + +[dependencies] +types = { path = "../../types"} +eth2_ssz = { path = "../ssz"} +tree_hash = { path = "../tree_hash"} +ethabi = "9.0" diff --git a/tests/eth1_test_rig/build.rs b/eth2/utils/deposit_contract/build.rs similarity index 100% rename from tests/eth1_test_rig/build.rs rename to eth2/utils/deposit_contract/build.rs diff --git a/eth2/utils/deposit_contract/src/lib.rs b/eth2/utils/deposit_contract/src/lib.rs new file mode 100644 index 0000000000..f9f0a3b203 --- /dev/null +++ b/eth2/utils/deposit_contract/src/lib.rs @@ -0,0 +1,56 @@ +use ethabi::{Contract, Token}; +use ssz::Encode; +use types::{ChainSpec, DepositData, SecretKey}; + +pub use ethabi::Error; + +pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000; +pub const DEPOSIT_GAS: usize = 4_000_000; +pub const ABI: &[u8] = include_bytes!("../contract/v0.8.3_validator_registration.json"); +pub const BYTECODE: &[u8] = include_bytes!("../contract/v0.8.3_validator_registration.bytecode"); + +pub fn eth1_tx_data(deposit_data: &DepositData) -> Result, Error> { + let params = vec![ + Token::Bytes(deposit_data.pubkey.as_ssz_bytes()), + Token::Bytes(deposit_data.withdrawal_credentials.as_ssz_bytes()), + Token::Bytes(deposit_data.signature.as_ssz_bytes()), + ]; + + let abi = Contract::load(ABI)?; + let function = abi.function("deposit")?; + function.encode_input(¶ms) +} + +#[cfg(test)] +mod tests { + use super::*; + use types::{ + test_utils::generate_deterministic_keypair, EthSpec, Hash256, Keypair, MinimalEthSpec, + Signature, + }; + + type E = MinimalEthSpec; + + fn get_deposit(keypair: Keypair, spec: &ChainSpec) -> DepositData { + let mut deposit_data = DepositData { + pubkey: keypair.pk.into(), + withdrawal_credentials: Hash256::from_slice(&[42; 32]), + amount: u64::max_value(), + signature: Signature::empty_signature().into(), + }; + deposit_data.signature = deposit_data.create_signature(&keypair.sk, spec); + deposit_data + } + + #[test] + fn basic() { + let spec = &E::default_spec(); + + let keypair = generate_deterministic_keypair(42); + let deposit = get_deposit(keypair.clone(), spec); + + let data = eth1_tx_data(&deposit).expect("should produce tx data"); + + assert_eq!(data.len(), 388, "bytes should be correct length"); + } +} diff --git a/tests/eth1_test_rig/Cargo.toml b/tests/eth1_test_rig/Cargo.toml index e2815db984..725a8402d5 100644 --- a/tests/eth1_test_rig/Cargo.toml +++ b/tests/eth1_test_rig/Cargo.toml @@ -4,12 +4,6 @@ version = "0.1.0" authors = ["Paul Hauner "] edition = "2018" -build = "build.rs" - -[build-dependencies] -reqwest = "0.9.20" -serde_json = "1.0" - [dependencies] web3 = "0.8.0" tokio = "0.1.17" @@ -17,3 +11,4 @@ futures = "0.1.25" types = { path = "../../eth2/types"} eth2_ssz = { path = "../../eth2/utils/ssz"} serde_json = "1.0" +deposit_contract = { path = "../../eth2/utils/deposit_contract"} diff --git a/tests/eth1_test_rig/src/lib.rs b/tests/eth1_test_rig/src/lib.rs index f137468b40..c424c0fe43 100644 --- a/tests/eth1_test_rig/src/lib.rs +++ b/tests/eth1_test_rig/src/lib.rs @@ -7,6 +7,7 @@ //! some initial issues. mod ganache; +use deposit_contract::{eth1_tx_data, ABI, BYTECODE, CONTRACT_DEPLOY_GAS, DEPOSIT_GAS}; use futures::{stream, Future, IntoFuture, Stream}; use ganache::GanacheInstance; use ssz::Encode; @@ -16,19 +17,12 @@ use types::DepositData; use types::{EthSpec, Hash256, Keypair, Signature}; use web3::contract::{Contract, Options}; use web3::transports::Http; -use web3::types::{Address, U256}; +use web3::types::{Address, TransactionRequest, U256}; use web3::{Transport, Web3}; pub const DEPLOYER_ACCOUNTS_INDEX: usize = 0; pub const DEPOSIT_ACCOUNTS_INDEX: usize = 0; -const CONTRACT_DEPLOY_GAS: usize = 4_000_000; -const DEPOSIT_GAS: usize = 4_000_000; - -// Deposit contract -pub const ABI: &[u8] = include_bytes!("../contract/v0.8.3_validator_registration.json"); -pub const BYTECODE: &[u8] = include_bytes!("../contract/v0.8.3_validator_registration.bytecode"); - /// Provides a dedicated ganache-cli instance with the deposit contract already deployed. pub struct GanacheEth1Instance { pub ganache: GanacheInstance, @@ -138,6 +132,7 @@ impl DepositContract { deposit_data: DepositData, ) -> impl Future { let contract = self.contract.clone(); + let web3_1 = self.web3.clone(); self.web3 .eth() @@ -149,19 +144,27 @@ impl DepositContract { .cloned() .ok_or_else(|| "Insufficient accounts for deposit".to_string()) }) - .and_then(move |from_address| { - let params = ( - deposit_data.pubkey.as_ssz_bytes(), - deposit_data.withdrawal_credentials.as_ssz_bytes(), - deposit_data.signature.as_ssz_bytes(), - ); - let options = Options { + .and_then(move |from| { + let tx_request = TransactionRequest { + from, + to: Some(contract.address()), gas: Some(U256::from(DEPOSIT_GAS)), + gas_price: None, value: Some(from_gwei(deposit_data.amount)), - ..Options::default() + // Note: the reason we use this `TransactionRequest` instead of just using the + // function in `self.contract` is so that the `eth1_tx_data` function gets used + // during testing. + // + // It's important that `eth1_tx_data` stays correct and does not suffer from + // code-rot. + data: eth1_tx_data(&deposit_data).map(Into::into).ok(), + nonce: None, + condition: None, }; - contract - .call("deposit", params, from_address, options) + + web3_1 + .eth() + .send_transaction(tx_request) .map_err(|e| format!("Failed to call deposit fn: {:?}", e)) }) .map(|_| ())