2019-03-06 06:14:54 +00:00
|
|
|
use super::errors::{TransferInvalid as Invalid, TransferValidationError as Error};
|
2019-03-07 05:15:38 +00:00
|
|
|
use bls::get_withdrawal_credentials;
|
|
|
|
use ssz::SignedRoot;
|
2019-03-06 04:22:45 +00:00
|
|
|
use types::*;
|
|
|
|
|
2019-03-06 05:24:56 +00:00
|
|
|
/// Indicates if a `Transfer` is valid to be included in a block in the current epoch of the given
|
|
|
|
/// state.
|
2019-03-06 04:22:45 +00:00
|
|
|
///
|
2019-03-06 05:24:56 +00:00
|
|
|
/// Returns `Ok(())` if the `Transfer` is valid, otherwise indicates the reason for invalidity.
|
|
|
|
///
|
|
|
|
/// Note: this function is incomplete.
|
2019-03-06 04:22:45 +00:00
|
|
|
///
|
2019-03-17 01:25:37 +00:00
|
|
|
/// Spec v0.5.0
|
2019-03-06 04:22:45 +00:00
|
|
|
pub fn verify_transfer(
|
2019-03-07 05:15:38 +00:00
|
|
|
state: &BeaconState,
|
|
|
|
transfer: &Transfer,
|
|
|
|
spec: &ChainSpec,
|
2019-03-06 03:46:12 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
verify_transfer_partial(state, transfer, spec, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parametric version of `verify_transfer` that allows some checks to be skipped.
|
|
|
|
///
|
|
|
|
/// In everywhere except the operation pool, `verify_transfer` should be preferred over this
|
|
|
|
/// function.
|
|
|
|
pub fn verify_transfer_partial(
|
|
|
|
state: &BeaconState,
|
|
|
|
transfer: &Transfer,
|
|
|
|
spec: &ChainSpec,
|
|
|
|
for_op_pool: bool,
|
2019-03-06 04:22:45 +00:00
|
|
|
) -> Result<(), Error> {
|
2019-03-17 01:25:37 +00:00
|
|
|
let sender_balance = *state
|
2019-03-07 05:15:38 +00:00
|
|
|
.validator_balances
|
2019-03-17 01:25:37 +00:00
|
|
|
.get(transfer.sender as usize)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
2019-03-07 05:15:38 +00:00
|
|
|
|
|
|
|
let total_amount = transfer
|
|
|
|
.amount
|
|
|
|
.checked_add(transfer.fee)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
|
|
|
|
|
|
|
|
verify!(
|
2019-03-06 03:46:12 +00:00
|
|
|
for_op_pool || sender_balance >= transfer.amount,
|
2019-03-17 01:25:37 +00:00
|
|
|
Invalid::FromBalanceInsufficient(transfer.amount, sender_balance)
|
2019-03-07 05:15:38 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
verify!(
|
2019-03-06 03:46:12 +00:00
|
|
|
for_op_pool || sender_balance >= transfer.fee,
|
2019-03-17 01:25:37 +00:00
|
|
|
Invalid::FromBalanceInsufficient(transfer.fee, sender_balance)
|
2019-03-07 05:15:38 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
verify!(
|
2019-03-06 03:46:12 +00:00
|
|
|
for_op_pool
|
|
|
|
|| (sender_balance == total_amount)
|
2019-03-17 01:25:37 +00:00
|
|
|
|| (sender_balance >= (total_amount + spec.min_deposit_amount)),
|
|
|
|
Invalid::InvalidResultingFromBalance(
|
|
|
|
sender_balance - total_amount,
|
|
|
|
spec.min_deposit_amount
|
|
|
|
)
|
2019-03-07 05:15:38 +00:00
|
|
|
);
|
|
|
|
|
2019-03-06 03:46:12 +00:00
|
|
|
if for_op_pool {
|
|
|
|
verify!(
|
|
|
|
state.slot <= transfer.slot,
|
|
|
|
Invalid::TransferSlotInPast(state.slot, transfer.slot)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
verify!(
|
|
|
|
state.slot == transfer.slot,
|
|
|
|
Invalid::StateSlotMismatch(state.slot, transfer.slot)
|
|
|
|
);
|
|
|
|
}
|
2019-03-07 05:15:38 +00:00
|
|
|
|
2019-03-17 01:25:37 +00:00
|
|
|
let sender_validator = state
|
2019-03-07 05:15:38 +00:00
|
|
|
.validator_registry
|
2019-03-17 01:25:37 +00:00
|
|
|
.get(transfer.sender as usize)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
2019-03-07 05:15:38 +00:00
|
|
|
let epoch = state.slot.epoch(spec.slots_per_epoch);
|
|
|
|
|
|
|
|
verify!(
|
2019-03-06 03:46:12 +00:00
|
|
|
for_op_pool
|
|
|
|
|| sender_validator.is_withdrawable_at(epoch)
|
2019-03-17 01:25:37 +00:00
|
|
|
|| sender_validator.activation_epoch == spec.far_future_epoch,
|
|
|
|
Invalid::FromValidatorIneligableForTransfer(transfer.sender)
|
2019-03-07 05:15:38 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let transfer_withdrawal_credentials = Hash256::from_slice(
|
|
|
|
&get_withdrawal_credentials(&transfer.pubkey, spec.bls_withdrawal_prefix_byte)[..],
|
|
|
|
);
|
|
|
|
verify!(
|
2019-03-17 01:25:37 +00:00
|
|
|
sender_validator.withdrawal_credentials == transfer_withdrawal_credentials,
|
2019-03-07 06:23:11 +00:00
|
|
|
Invalid::WithdrawalCredentialsMismatch(
|
2019-03-17 01:25:37 +00:00
|
|
|
sender_validator.withdrawal_credentials,
|
2019-03-07 06:23:11 +00:00
|
|
|
transfer_withdrawal_credentials
|
|
|
|
)
|
2019-03-07 05:15:38 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let message = transfer.signed_root();
|
|
|
|
let domain = spec.get_domain(
|
|
|
|
transfer.slot.epoch(spec.slots_per_epoch),
|
|
|
|
Domain::Transfer,
|
|
|
|
&state.fork,
|
|
|
|
);
|
|
|
|
|
|
|
|
verify!(
|
|
|
|
transfer
|
|
|
|
.signature
|
|
|
|
.verify(&message[..], domain, &transfer.pubkey),
|
|
|
|
Invalid::BadSignature
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Executes a transfer on the state.
|
|
|
|
///
|
|
|
|
/// Does not check that the transfer is valid, however checks for overflow in all actions.
|
|
|
|
///
|
2019-03-17 10:07:19 +00:00
|
|
|
/// Spec v0.5.0
|
2019-03-07 05:15:38 +00:00
|
|
|
pub fn execute_transfer(
|
|
|
|
state: &mut BeaconState,
|
|
|
|
transfer: &Transfer,
|
|
|
|
spec: &ChainSpec,
|
|
|
|
) -> Result<(), Error> {
|
2019-03-17 01:25:37 +00:00
|
|
|
let sender_balance = *state
|
2019-03-07 05:15:38 +00:00
|
|
|
.validator_balances
|
2019-03-17 01:25:37 +00:00
|
|
|
.get(transfer.sender as usize)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
|
|
|
let recipient_balance = *state
|
2019-03-07 05:15:38 +00:00
|
|
|
.validator_balances
|
2019-03-17 01:25:37 +00:00
|
|
|
.get(transfer.recipient as usize)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::ToValidatorUnknown(transfer.recipient)))?;
|
2019-03-07 05:15:38 +00:00
|
|
|
|
2019-03-17 01:25:37 +00:00
|
|
|
let proposer_index =
|
|
|
|
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)?;
|
2019-03-07 05:15:38 +00:00
|
|
|
let proposer_balance = state.validator_balances[proposer_index];
|
|
|
|
|
|
|
|
let total_amount = transfer
|
|
|
|
.amount
|
|
|
|
.checked_add(transfer.fee)
|
|
|
|
.ok_or_else(|| Error::Invalid(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
|
|
|
|
|
2019-03-17 01:25:37 +00:00
|
|
|
state.validator_balances[transfer.sender as usize] =
|
|
|
|
sender_balance.checked_sub(total_amount).ok_or_else(|| {
|
|
|
|
Error::Invalid(Invalid::FromBalanceInsufficient(
|
|
|
|
total_amount,
|
|
|
|
sender_balance,
|
|
|
|
))
|
2019-03-07 05:15:38 +00:00
|
|
|
})?;
|
|
|
|
|
2019-03-17 01:25:37 +00:00
|
|
|
state.validator_balances[transfer.recipient as usize] = recipient_balance
|
2019-03-07 05:15:38 +00:00
|
|
|
.checked_add(transfer.amount)
|
2019-03-17 01:25:37 +00:00
|
|
|
.ok_or_else(|| {
|
|
|
|
Error::Invalid(Invalid::ToBalanceOverflow(
|
|
|
|
recipient_balance,
|
|
|
|
transfer.amount,
|
|
|
|
))
|
|
|
|
})?;
|
2019-03-07 05:15:38 +00:00
|
|
|
|
|
|
|
state.validator_balances[proposer_index] =
|
|
|
|
proposer_balance.checked_add(transfer.fee).ok_or_else(|| {
|
|
|
|
Error::Invalid(Invalid::ProposerBalanceOverflow(
|
|
|
|
proposer_balance,
|
|
|
|
transfer.fee,
|
|
|
|
))
|
|
|
|
})?;
|
2019-03-06 04:22:45 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|