Add transfer processing to BeaconChain

This commit is contained in:
Paul Hauner
2019-03-07 17:23:11 +11:00
parent 15e4aabd8a
commit 1ef2652cac
8 changed files with 196 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
use super::ValidatorHarness;
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
pub use beacon_chain::{BeaconChainError, CheckPoint};
use bls::create_proof_of_possession;
use bls::{create_proof_of_possession, get_withdrawal_credentials};
use db::{
stores::{BeaconBlockStore, BeaconStateStore},
MemoryDB,
@@ -67,7 +67,13 @@ impl BeaconChainHarness {
timestamp: genesis_time - 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
// Validator can withdraw using their main keypair.
withdrawal_credentials: Hash256::from_slice(
&get_withdrawal_credentials(
&keypair.pk,
spec.bls_withdrawal_prefix_byte,
)[..],
),
proof_of_possession: create_proof_of_possession(&keypair),
},
},
@@ -286,6 +292,11 @@ impl BeaconChainHarness {
self.beacon_chain.receive_exit_for_inclusion(exit);
}
/// Submit an transfer to the `BeaconChain` for inclusion in some block.
pub fn add_transfer(&mut self, transfer: Transfer) {
self.beacon_chain.receive_transfer_for_inclusion(transfer);
}
/// Submit a proposer slashing to the `BeaconChain` for inclusion in some block.
pub fn add_proposer_slashing(&mut self, proposer_slashing: ProposerSlashing) {
self.beacon_chain

View File

@@ -3,7 +3,7 @@
use crate::beacon_chain_harness::BeaconChainHarness;
use beacon_chain::CheckPoint;
use bls::create_proof_of_possession;
use bls::{create_proof_of_possession, get_withdrawal_credentials};
use log::{info, warn};
use ssz::SignedRoot;
use types::*;
@@ -83,8 +83,8 @@ impl TestCase {
info!("Starting simulation across {} slots...", slots);
// -1 slots because genesis counts as a slot.
for slot_height in 0..slots - 1 {
// Start at 1 because genesis counts as a slot.
for slot_height in 1..slots {
// Used to ensure that deposits in the same slot have incremental deposit indices.
let mut deposit_index_offset = 0;
@@ -144,6 +144,20 @@ impl TestCase {
}
}
// Feed transfers to the BeaconChain.
if let Some(ref transfers) = self.config.transfers {
for (slot, from, to, amount) in transfers {
if *slot == slot_height {
info!(
"Including transfer at slot height {} from validator {}.",
slot_height, from
);
let transfer = build_transfer(&harness, *from, *to, *amount);
harness.add_transfer(transfer);
}
}
}
// Build a block or skip a slot.
match self.config.skip_slots {
Some(ref skip_slots) if skip_slots.contains(&slot_height) => {
@@ -208,6 +222,30 @@ impl TestCase {
}
}
/// Builds a `Deposit` this is valid for the given `BeaconChainHarness` at its next slot.
fn build_transfer(harness: &BeaconChainHarness, from: u64, to: u64, amount: u64) -> Transfer {
let slot = harness.beacon_chain.state.read().slot + 1;
let mut transfer = Transfer {
from,
to,
amount,
fee: 0,
slot,
pubkey: harness.validators[from as usize].keypair.pk.clone(),
signature: Signature::empty_signature(),
};
let message = transfer.signed_root();
let epoch = slot.epoch(harness.spec.slots_per_epoch);
transfer.signature = harness
.validator_sign(from as usize, &message[..], epoch, Domain::Transfer)
.expect("Unable to sign Transfer");
transfer
}
/// Builds a `Deposit` this is valid for the given `BeaconChainHarness`.
///
/// `index_offset` is used to ensure that `deposit.index == state.index` when adding multiple
@@ -220,8 +258,9 @@ fn build_deposit(
let keypair = Keypair::random();
let proof_of_possession = create_proof_of_possession(&keypair);
let index = harness.beacon_chain.state.read().deposit_index + index_offset;
info!("index: {}, index_offset: {}", index, index_offset);
let withdrawal_credentials = Hash256::from_slice(
&get_withdrawal_credentials(&keypair.pk, harness.spec.bls_withdrawal_prefix_byte)[..],
);
let deposit = Deposit {
// Note: `branch` and `index` will need to be updated once the spec defines their
@@ -233,7 +272,7 @@ fn build_deposit(
timestamp: 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(),
withdrawal_credentials,
proof_of_possession,
},
},

View File

@@ -10,6 +10,8 @@ pub type DepositTuple = (SlotHeight, GweiAmount);
pub type ExitTuple = (SlotHeight, ValidatorIndex);
pub type ProposerSlashingTuple = (SlotHeight, ValidatorIndex);
pub type AttesterSlashingTuple = (SlotHeight, ValidatorIndices);
/// (slot_height, from, to, amount)
pub type TransferTuple = (SlotHeight, ValidatorIndex, ValidatorIndex, GweiAmount);
/// Defines the execution of a `BeaconStateHarness` across a series of slots.
#[derive(Debug)]
@@ -30,6 +32,8 @@ pub struct Config {
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
/// Exits to be including during execution.
pub exits: Option<Vec<ExitTuple>>,
/// Transfers to be including during execution.
pub transfers: Option<Vec<TransferTuple>>,
}
impl Config {
@@ -47,10 +51,27 @@ impl Config {
proposer_slashings: parse_proposer_slashings(&yaml),
attester_slashings: parse_attester_slashings(&yaml),
exits: parse_exits(&yaml),
transfers: parse_transfers(&yaml),
}
}
}
/// Parse the `transfers` section of the YAML document.
fn parse_transfers(yaml: &Yaml) -> Option<Vec<TransferTuple>> {
let mut tuples = vec![];
for exit in yaml["transfers"].as_vec()? {
let slot = as_u64(exit, "slot").expect("Incomplete transfer (slot)");
let from = as_u64(exit, "from").expect("Incomplete transfer (from)");
let to = as_u64(exit, "to").expect("Incomplete transfer (to)");
let amount = as_u64(exit, "amount").expect("Incomplete transfer (amount)");
tuples.push((SlotHeight::from(slot), from, to, amount));
}
Some(tuples)
}
/// Parse the `attester_slashings` section of the YAML document.
fn parse_exits(yaml: &Yaml) -> Option<Vec<ExitTuple>> {
let mut tuples = vec![];
@@ -102,8 +123,7 @@ fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
for deposit in yaml["deposits"].as_vec()? {
let slot = as_u64(deposit, "slot").expect("Incomplete deposit (slot)");
let amount =
as_u64(deposit, "amount").expect("Incomplete deposit (amount)") * 1_000_000_000;
let amount = as_u64(deposit, "amount").expect("Incomplete deposit (amount)");
deposits.push((SlotHeight::from(slot), amount))
}

View File

@@ -3,6 +3,11 @@ use log::info;
use types::*;
use yaml_rust::Yaml;
type ValidatorIndex = u64;
type BalanceGwei = u64;
type BalanceCheckTuple = (ValidatorIndex, String, BalanceGwei);
/// Tests to be conducted upon a `BeaconState` object generated during the execution of a
/// `TestCase`.
#[derive(Debug)]
@@ -17,6 +22,8 @@ pub struct StateCheck {
pub exited_validators: Option<Vec<u64>>,
/// A list of validator indices which have had an exit initiated. Must be in ascending order.
pub exit_initiated_validators: Option<Vec<u64>>,
/// A list of balances to check.
pub balances: Option<Vec<BalanceCheckTuple>>,
}
impl StateCheck {
@@ -30,6 +37,7 @@ impl StateCheck {
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
exited_validators: as_vec_u64(&yaml, "exited_validators"),
exit_initiated_validators: as_vec_u64(&yaml, "exit_initiated_validators"),
balances: parse_balances(&yaml),
}
}
@@ -124,5 +132,47 @@ impl StateCheck {
exit_initiated_validators
);
}
// Check validator balances.
if let Some(ref balances) = self.balances {
for (index, comparison, expected) in balances {
let actual = *state
.validator_balances
.get(*index as usize)
.expect("Balance check specifies unknown validator");
let result = match comparison.as_ref() {
"eq" => actual == *expected,
_ => panic!("Unknown balance comparison (use `eq`)"),
};
assert!(
result,
format!(
"Validator balance for {}: {} !{} {}.",
index, actual, comparison, expected
)
);
info!("OK: validator balance for {:?}.", index);
}
}
}
}
/// Parse the `transfers` section of the YAML document.
fn parse_balances(yaml: &Yaml) -> Option<Vec<BalanceCheckTuple>> {
let mut tuples = vec![];
for exit in yaml["balances"].as_vec()? {
let from =
as_u64(exit, "validator_index").expect("Incomplete balance check (validator_index)");
let comparison = exit["comparison"]
.clone()
.into_string()
.expect("Incomplete balance check (amount)");
let balance = as_u64(exit, "balance").expect("Incomplete balance check (balance)");
tuples.push((from, comparison, balance));
}
Some(tuples)
}