lighthouse/common/deposit_contract/src/lib.rs
Michael Sproul 4d0122444b Update and consolidate dependencies (#3136)
## Proposed Changes

I did some gardening 🌳 in our dependency tree:

- Remove duplicate versions of `warp` (git vs patch)
- Remove duplicate versions of lots of small deps: `cpufeatures`, `ethabi`, `ethereum-types`, `bitvec`, `nix`, `libsecp256k1`.
- Update MDBX (should resolve #3028). I tested and Lighthouse compiles on Windows 11 now.
- Restore `psutil` back to upstream
- Make some progress updating everything to rand 0.8. There are a few crates stuck on 0.7.

Hopefully this puts us on a better footing for future `cargo audit` issues, and improves compile times slightly.

## Additional Info

Some crates are held back by issues with `zeroize`. libp2p-noise depends on [`chacha20poly1305`](https://crates.io/crates/chacha20poly1305) which depends on zeroize < v1.5, and we can only have one version of zeroize because it's post 1.0 (see https://github.com/rust-lang/cargo/issues/6584). The latest version of `zeroize` is v1.5.4, which is used by the new versions of many other crates (e.g. `num-bigint-dig`). Once a new version of chacha20poly1305 is released we can update libp2p-noise and upgrade everything to the latest `zeroize` version.

I've also opened a PR to `blst` related to zeroize: https://github.com/supranational/blst/pull/111
2022-04-04 00:26:16 +00:00

131 lines
4.1 KiB
Rust

use ethabi::{Contract, Token};
use ssz::{Decode, DecodeError as SszDecodeError, Encode};
use tree_hash::TreeHash;
use types::{DepositData, Hash256, PublicKeyBytes, SignatureBytes};
pub use ethabi::Error;
#[derive(Debug)]
pub enum DecodeError {
EthabiError(ethabi::Error),
SszDecodeError(SszDecodeError),
MissingField,
UnableToGetBytes,
MissingToken,
InadequateBytes,
}
impl From<ethabi::Error> for DecodeError {
fn from(e: ethabi::Error) -> DecodeError {
DecodeError::EthabiError(e)
}
}
pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000;
pub const DEPOSIT_GAS: usize = 400_000;
pub const ABI: &[u8] = include_bytes!("../contracts/v0.12.1_validator_registration.json");
pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.12.1_validator_registration.bytecode");
pub const DEPOSIT_DATA_LEN: usize = 420; // lol
pub mod testnet {
pub const ABI: &[u8] =
include_bytes!("../contracts/v0.12.1_testnet_validator_registration.json");
pub const BYTECODE: &[u8] =
include_bytes!("../contracts/v0.12.1_testnet_validator_registration.bytecode");
}
pub fn encode_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()),
Token::FixedBytes(deposit_data.tree_hash_root().as_ssz_bytes()),
];
// Here we make an assumption that the `crate::testnet::ABI` has a superset of the features of
// the crate::ABI`.
let abi = Contract::load(ABI)?;
let function = abi.function("deposit")?;
function.encode_input(&params)
}
pub fn decode_eth1_tx_data(
bytes: &[u8],
amount: u64,
) -> Result<(DepositData, Hash256), DecodeError> {
let abi = Contract::load(ABI)?;
let function = abi.function("deposit")?;
let mut tokens = function.decode_input(bytes.get(4..).ok_or(DecodeError::InadequateBytes)?)?;
macro_rules! decode_token {
($type: ty, $to_fn: ident) => {
<$type>::from_ssz_bytes(
&tokens
.pop()
.ok_or_else(|| DecodeError::MissingToken)?
.$to_fn()
.ok_or_else(|| DecodeError::UnableToGetBytes)?,
)
.map_err(DecodeError::SszDecodeError)?
};
}
let root = decode_token!(Hash256, into_fixed_bytes);
let deposit_data = DepositData {
amount,
signature: decode_token!(SignatureBytes, into_bytes),
withdrawal_credentials: decode_token!(Hash256, into_bytes),
pubkey: decode_token!(PublicKeyBytes, into_bytes),
};
Ok((deposit_data, root))
}
#[cfg(test)]
mod tests {
use super::*;
use types::{
test_utils::generate_deterministic_keypair, ChainSpec, 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().into(),
};
deposit_data.signature = deposit_data.create_signature(&keypair.sk, spec);
deposit_data
}
#[test]
fn round_trip() {
let spec = &E::default_spec();
let keypair = generate_deterministic_keypair(42);
let original = get_deposit(keypair, spec);
let data = encode_eth1_tx_data(&original).expect("should produce tx data");
assert_eq!(
data.len(),
DEPOSIT_DATA_LEN,
"bytes should be correct length"
);
let (decoded, root) = decode_eth1_tx_data(&data, original.amount).expect("should decode");
assert_eq!(decoded, original, "decoded should match original");
assert_eq!(
root,
original.tree_hash_root(),
"decode root should match original root"
);
}
}