Add transfer processing to BeaconChain
This commit is contained in:
parent
15e4aabd8a
commit
1ef2652cac
@ -56,6 +56,7 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock, F: ForkChoice> {
|
|||||||
pub attestation_aggregator: RwLock<AttestationAggregator>,
|
pub attestation_aggregator: RwLock<AttestationAggregator>,
|
||||||
pub deposits_for_inclusion: RwLock<Vec<Deposit>>,
|
pub deposits_for_inclusion: RwLock<Vec<Deposit>>,
|
||||||
pub exits_for_inclusion: RwLock<Vec<VoluntaryExit>>,
|
pub exits_for_inclusion: RwLock<Vec<VoluntaryExit>>,
|
||||||
|
pub transfers_for_inclusion: RwLock<Vec<Transfer>>,
|
||||||
pub proposer_slashings_for_inclusion: RwLock<Vec<ProposerSlashing>>,
|
pub proposer_slashings_for_inclusion: RwLock<Vec<ProposerSlashing>>,
|
||||||
pub attester_slashings_for_inclusion: RwLock<Vec<AttesterSlashing>>,
|
pub attester_slashings_for_inclusion: RwLock<Vec<AttesterSlashing>>,
|
||||||
canonical_head: RwLock<CheckPoint>,
|
canonical_head: RwLock<CheckPoint>,
|
||||||
@ -126,6 +127,7 @@ where
|
|||||||
attestation_aggregator,
|
attestation_aggregator,
|
||||||
deposits_for_inclusion: RwLock::new(vec![]),
|
deposits_for_inclusion: RwLock::new(vec![]),
|
||||||
exits_for_inclusion: RwLock::new(vec![]),
|
exits_for_inclusion: RwLock::new(vec![]),
|
||||||
|
transfers_for_inclusion: RwLock::new(vec![]),
|
||||||
proposer_slashings_for_inclusion: RwLock::new(vec![]),
|
proposer_slashings_for_inclusion: RwLock::new(vec![]),
|
||||||
attester_slashings_for_inclusion: RwLock::new(vec![]),
|
attester_slashings_for_inclusion: RwLock::new(vec![]),
|
||||||
state: RwLock::new(genesis_state),
|
state: RwLock::new(genesis_state),
|
||||||
@ -436,6 +438,44 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accept some transfer and queue it for inclusion in an appropriate block.
|
||||||
|
pub fn receive_transfer_for_inclusion(&self, transfer: Transfer) {
|
||||||
|
// TODO: transfers are not checked for validity; check them.
|
||||||
|
//
|
||||||
|
// https://github.com/sigp/lighthouse/issues/276
|
||||||
|
self.transfers_for_inclusion.write().push(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a vec of transfers suitable for inclusion in some block.
|
||||||
|
pub fn get_transfers_for_block(&self) -> Vec<Transfer> {
|
||||||
|
// TODO: transfers are indiscriminately included; check them for validity.
|
||||||
|
//
|
||||||
|
// https://github.com/sigp/lighthouse/issues/275
|
||||||
|
self.transfers_for_inclusion.read().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of `Deposits` that were included in recent blocks and removes them from the
|
||||||
|
/// inclusion queue.
|
||||||
|
///
|
||||||
|
/// This ensures that `Deposits` are not included twice in successive blocks.
|
||||||
|
pub fn set_transfers_as_included(&self, included_transfers: &[Transfer]) {
|
||||||
|
// TODO: method does not take forks into account; consider this.
|
||||||
|
let mut indices_to_delete = vec![];
|
||||||
|
|
||||||
|
for included in included_transfers {
|
||||||
|
for (i, for_inclusion) in self.transfers_for_inclusion.read().iter().enumerate() {
|
||||||
|
if included == for_inclusion {
|
||||||
|
indices_to_delete.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let transfers_for_inclusion = &mut self.transfers_for_inclusion.write();
|
||||||
|
for i in indices_to_delete {
|
||||||
|
transfers_for_inclusion.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Accept some proposer slashing and queue it for inclusion in an appropriate block.
|
/// Accept some proposer slashing and queue it for inclusion in an appropriate block.
|
||||||
pub fn receive_proposer_slashing_for_inclusion(&self, proposer_slashing: ProposerSlashing) {
|
pub fn receive_proposer_slashing_for_inclusion(&self, proposer_slashing: ProposerSlashing) {
|
||||||
// TODO: proposer_slashings are not checked for validity; check them.
|
// TODO: proposer_slashings are not checked for validity; check them.
|
||||||
@ -664,6 +704,8 @@ where
|
|||||||
|
|
||||||
// Update the inclusion queues so they aren't re-submitted.
|
// Update the inclusion queues so they aren't re-submitted.
|
||||||
self.set_deposits_as_included(&block.body.deposits[..]);
|
self.set_deposits_as_included(&block.body.deposits[..]);
|
||||||
|
self.set_transfers_as_included(&block.body.transfers[..]);
|
||||||
|
self.set_exits_as_included(&block.body.voluntary_exits[..]);
|
||||||
self.set_proposer_slashings_as_included(&block.body.proposer_slashings[..]);
|
self.set_proposer_slashings_as_included(&block.body.proposer_slashings[..]);
|
||||||
self.set_attester_slashings_as_included(&block.body.attester_slashings[..]);
|
self.set_attester_slashings_as_included(&block.body.attester_slashings[..]);
|
||||||
|
|
||||||
@ -730,7 +772,7 @@ where
|
|||||||
attestations,
|
attestations,
|
||||||
deposits: self.get_deposits_for_block(),
|
deposits: self.get_deposits_for_block(),
|
||||||
voluntary_exits: self.get_exits_for_block(),
|
voluntary_exits: self.get_exits_for_block(),
|
||||||
transfers: vec![],
|
transfers: self.get_transfers_for_block(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,18 +10,23 @@ test_cases:
|
|||||||
num_slots: 64
|
num_slots: 64
|
||||||
skip_slots: [2, 3]
|
skip_slots: [2, 3]
|
||||||
deposits:
|
deposits:
|
||||||
# At slot 1, create a new validator deposit of 32 ETH.
|
# At slot 1, create a new validator deposit of 5 ETH.
|
||||||
- slot: 1
|
- slot: 1
|
||||||
amount: 32
|
amount: 5000000000
|
||||||
# Trigger more deposits...
|
# Trigger more deposits...
|
||||||
- slot: 3
|
- slot: 3
|
||||||
amount: 32
|
amount: 5000000000
|
||||||
- slot: 5
|
- slot: 5
|
||||||
amount: 32
|
amount: 32000000000
|
||||||
exits:
|
exits:
|
||||||
# At slot 10, submit an exit for validator #50.
|
# At slot 10, submit an exit for validator #50.
|
||||||
- slot: 10
|
- slot: 10
|
||||||
validator_index: 50
|
validator_index: 50
|
||||||
|
transfers:
|
||||||
|
- slot: 6
|
||||||
|
from: 1000
|
||||||
|
to: 1001
|
||||||
|
amount: 5000000000
|
||||||
proposer_slashings:
|
proposer_slashings:
|
||||||
# At slot 2, trigger a proposer slashing for validator #42.
|
# At slot 2, trigger a proposer slashing for validator #42.
|
||||||
- slot: 2
|
- slot: 2
|
||||||
@ -44,4 +49,11 @@ test_cases:
|
|||||||
slashed_validators: [11, 12, 13, 14, 42]
|
slashed_validators: [11, 12, 13, 14, 42]
|
||||||
exited_validators: []
|
exited_validators: []
|
||||||
exit_initiated_validators: [50]
|
exit_initiated_validators: [50]
|
||||||
|
balances:
|
||||||
|
- validator_index: 1000
|
||||||
|
comparison: "eq"
|
||||||
|
balance: 0
|
||||||
|
- validator_index: 1001
|
||||||
|
comparison: "eq"
|
||||||
|
balance: 10000000000
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::ValidatorHarness;
|
use super::ValidatorHarness;
|
||||||
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
|
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
|
||||||
pub use beacon_chain::{BeaconChainError, CheckPoint};
|
pub use beacon_chain::{BeaconChainError, CheckPoint};
|
||||||
use bls::create_proof_of_possession;
|
use bls::{create_proof_of_possession, get_withdrawal_credentials};
|
||||||
use db::{
|
use db::{
|
||||||
stores::{BeaconBlockStore, BeaconStateStore},
|
stores::{BeaconBlockStore, BeaconStateStore},
|
||||||
MemoryDB,
|
MemoryDB,
|
||||||
@ -67,7 +67,13 @@ impl BeaconChainHarness {
|
|||||||
timestamp: genesis_time - 1,
|
timestamp: genesis_time - 1,
|
||||||
deposit_input: DepositInput {
|
deposit_input: DepositInput {
|
||||||
pubkey: keypair.pk.clone(),
|
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),
|
proof_of_possession: create_proof_of_possession(&keypair),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -286,6 +292,11 @@ impl BeaconChainHarness {
|
|||||||
self.beacon_chain.receive_exit_for_inclusion(exit);
|
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.
|
/// Submit a proposer slashing to the `BeaconChain` for inclusion in some block.
|
||||||
pub fn add_proposer_slashing(&mut self, proposer_slashing: ProposerSlashing) {
|
pub fn add_proposer_slashing(&mut self, proposer_slashing: ProposerSlashing) {
|
||||||
self.beacon_chain
|
self.beacon_chain
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::beacon_chain_harness::BeaconChainHarness;
|
use crate::beacon_chain_harness::BeaconChainHarness;
|
||||||
use beacon_chain::CheckPoint;
|
use beacon_chain::CheckPoint;
|
||||||
use bls::create_proof_of_possession;
|
use bls::{create_proof_of_possession, get_withdrawal_credentials};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use ssz::SignedRoot;
|
use ssz::SignedRoot;
|
||||||
use types::*;
|
use types::*;
|
||||||
@ -83,8 +83,8 @@ impl TestCase {
|
|||||||
|
|
||||||
info!("Starting simulation across {} slots...", slots);
|
info!("Starting simulation across {} slots...", slots);
|
||||||
|
|
||||||
// -1 slots because genesis counts as a slot.
|
// Start at 1 because genesis counts as a slot.
|
||||||
for slot_height in 0..slots - 1 {
|
for slot_height in 1..slots {
|
||||||
// Used to ensure that deposits in the same slot have incremental deposit indices.
|
// Used to ensure that deposits in the same slot have incremental deposit indices.
|
||||||
let mut deposit_index_offset = 0;
|
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.
|
// Build a block or skip a slot.
|
||||||
match self.config.skip_slots {
|
match self.config.skip_slots {
|
||||||
Some(ref skip_slots) if skip_slots.contains(&slot_height) => {
|
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`.
|
/// Builds a `Deposit` this is valid for the given `BeaconChainHarness`.
|
||||||
///
|
///
|
||||||
/// `index_offset` is used to ensure that `deposit.index == state.index` when adding multiple
|
/// `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 keypair = Keypair::random();
|
||||||
let proof_of_possession = create_proof_of_possession(&keypair);
|
let proof_of_possession = create_proof_of_possession(&keypair);
|
||||||
let index = harness.beacon_chain.state.read().deposit_index + index_offset;
|
let index = harness.beacon_chain.state.read().deposit_index + index_offset;
|
||||||
|
let withdrawal_credentials = Hash256::from_slice(
|
||||||
info!("index: {}, index_offset: {}", index, index_offset);
|
&get_withdrawal_credentials(&keypair.pk, harness.spec.bls_withdrawal_prefix_byte)[..],
|
||||||
|
);
|
||||||
|
|
||||||
let deposit = Deposit {
|
let deposit = Deposit {
|
||||||
// Note: `branch` and `index` will need to be updated once the spec defines their
|
// Note: `branch` and `index` will need to be updated once the spec defines their
|
||||||
@ -233,7 +272,7 @@ fn build_deposit(
|
|||||||
timestamp: 1,
|
timestamp: 1,
|
||||||
deposit_input: DepositInput {
|
deposit_input: DepositInput {
|
||||||
pubkey: keypair.pk.clone(),
|
pubkey: keypair.pk.clone(),
|
||||||
withdrawal_credentials: Hash256::zero(),
|
withdrawal_credentials,
|
||||||
proof_of_possession,
|
proof_of_possession,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,8 @@ pub type DepositTuple = (SlotHeight, GweiAmount);
|
|||||||
pub type ExitTuple = (SlotHeight, ValidatorIndex);
|
pub type ExitTuple = (SlotHeight, ValidatorIndex);
|
||||||
pub type ProposerSlashingTuple = (SlotHeight, ValidatorIndex);
|
pub type ProposerSlashingTuple = (SlotHeight, ValidatorIndex);
|
||||||
pub type AttesterSlashingTuple = (SlotHeight, ValidatorIndices);
|
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.
|
/// Defines the execution of a `BeaconStateHarness` across a series of slots.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -30,6 +32,8 @@ pub struct Config {
|
|||||||
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
|
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
|
||||||
/// Exits to be including during execution.
|
/// Exits to be including during execution.
|
||||||
pub exits: Option<Vec<ExitTuple>>,
|
pub exits: Option<Vec<ExitTuple>>,
|
||||||
|
/// Transfers to be including during execution.
|
||||||
|
pub transfers: Option<Vec<TransferTuple>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -47,10 +51,27 @@ impl Config {
|
|||||||
proposer_slashings: parse_proposer_slashings(&yaml),
|
proposer_slashings: parse_proposer_slashings(&yaml),
|
||||||
attester_slashings: parse_attester_slashings(&yaml),
|
attester_slashings: parse_attester_slashings(&yaml),
|
||||||
exits: parse_exits(&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.
|
/// Parse the `attester_slashings` section of the YAML document.
|
||||||
fn parse_exits(yaml: &Yaml) -> Option<Vec<ExitTuple>> {
|
fn parse_exits(yaml: &Yaml) -> Option<Vec<ExitTuple>> {
|
||||||
let mut tuples = vec![];
|
let mut tuples = vec![];
|
||||||
@ -102,8 +123,7 @@ fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
|
|||||||
|
|
||||||
for deposit in yaml["deposits"].as_vec()? {
|
for deposit in yaml["deposits"].as_vec()? {
|
||||||
let slot = as_u64(deposit, "slot").expect("Incomplete deposit (slot)");
|
let slot = as_u64(deposit, "slot").expect("Incomplete deposit (slot)");
|
||||||
let amount =
|
let amount = as_u64(deposit, "amount").expect("Incomplete deposit (amount)");
|
||||||
as_u64(deposit, "amount").expect("Incomplete deposit (amount)") * 1_000_000_000;
|
|
||||||
|
|
||||||
deposits.push((SlotHeight::from(slot), amount))
|
deposits.push((SlotHeight::from(slot), amount))
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,11 @@ use log::info;
|
|||||||
use types::*;
|
use types::*;
|
||||||
use yaml_rust::Yaml;
|
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
|
/// Tests to be conducted upon a `BeaconState` object generated during the execution of a
|
||||||
/// `TestCase`.
|
/// `TestCase`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -17,6 +22,8 @@ pub struct StateCheck {
|
|||||||
pub exited_validators: Option<Vec<u64>>,
|
pub exited_validators: Option<Vec<u64>>,
|
||||||
/// A list of validator indices which have had an exit initiated. Must be in ascending order.
|
/// A list of validator indices which have had an exit initiated. Must be in ascending order.
|
||||||
pub exit_initiated_validators: Option<Vec<u64>>,
|
pub exit_initiated_validators: Option<Vec<u64>>,
|
||||||
|
/// A list of balances to check.
|
||||||
|
pub balances: Option<Vec<BalanceCheckTuple>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateCheck {
|
impl StateCheck {
|
||||||
@ -30,6 +37,7 @@ impl StateCheck {
|
|||||||
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
|
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
|
||||||
exited_validators: as_vec_u64(&yaml, "exited_validators"),
|
exited_validators: as_vec_u64(&yaml, "exited_validators"),
|
||||||
exit_initiated_validators: as_vec_u64(&yaml, "exit_initiated_validators"),
|
exit_initiated_validators: as_vec_u64(&yaml, "exit_initiated_validators"),
|
||||||
|
balances: parse_balances(&yaml),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,5 +132,47 @@ impl StateCheck {
|
|||||||
exit_initiated_validators
|
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)
|
||||||
|
}
|
||||||
|
@ -365,7 +365,9 @@ pub enum TransferInvalid {
|
|||||||
/// (from_validator)
|
/// (from_validator)
|
||||||
FromValidatorIneligableForTransfer(u64),
|
FromValidatorIneligableForTransfer(u64),
|
||||||
/// The validators withdrawal credentials do not match `transfer.pubkey`.
|
/// The validators withdrawal credentials do not match `transfer.pubkey`.
|
||||||
WithdrawalCredentialsMismatch,
|
///
|
||||||
|
/// (state_credentials, transfer_pubkey_credentials)
|
||||||
|
WithdrawalCredentialsMismatch(Hash256, Hash256),
|
||||||
/// The deposit was not signed by `deposit.pubkey`.
|
/// The deposit was not signed by `deposit.pubkey`.
|
||||||
BadSignature,
|
BadSignature,
|
||||||
/// Overflow when adding to `transfer.to` balance.
|
/// Overflow when adding to `transfer.to` balance.
|
||||||
|
@ -64,7 +64,10 @@ pub fn verify_transfer(
|
|||||||
);
|
);
|
||||||
verify!(
|
verify!(
|
||||||
from_validator.withdrawal_credentials == transfer_withdrawal_credentials,
|
from_validator.withdrawal_credentials == transfer_withdrawal_credentials,
|
||||||
Invalid::WithdrawalCredentialsMismatch
|
Invalid::WithdrawalCredentialsMismatch(
|
||||||
|
from_validator.withdrawal_credentials,
|
||||||
|
transfer_withdrawal_credentials
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = transfer.signed_root();
|
let message = transfer.signed_root();
|
||||||
|
Loading…
Reference in New Issue
Block a user