Add deposit_contract crate

This commit is contained in:
Paul Hauner
2019-11-18 16:08:19 +11:00
parent 5a9298d567
commit 8561a95ee9
7 changed files with 97 additions and 24 deletions

View File

@@ -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",

View File

@@ -0,0 +1 @@
contract/

View File

@@ -0,0 +1,17 @@
[package]
name = "deposit_contract"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
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"

View File

@@ -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<Vec<u8>, 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(&params)
}
#[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");
}
}

View File

@@ -4,12 +4,6 @@ version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
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"}

View File

@@ -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<Item = (), Error = String> {
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(|_| ())