mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-23 23:04:53 +00:00
Bulk signature verification (#507)
* Add basic block processing benches * Start reviving state processing benches * Fix old block builders * Add optimization for faster pubkey add * Tidy benches, add another * Add extra block processing bench * Start working on faster BLS scheme * Add partially complete sig verify optimization * Add .gitignore to state processing * Add progress on faster signature verification * Fix SignatureSet for fake_crypto * Tidy attester slashings sig set * Tidy bulk signature verifier * Refactor signature sets to be cleaner * Start threading SignatureStrategy through code * Add (empty) test dir * Move BenchingBlockBuilder * Add initial block signature verification tests * Add tests for bulk signature verification * Start threading SignatureStrategy in block proc. * Refactor per_block_processing errors * Use sig set tuples instead of lists of two * Remove dead code * Thread VerifySignatures through per_block_processing * Add bulk signature verification * Introduce parallel bulk signature verification * Expand state processing benches * Fix additional compile errors * Fix issue where par iter chunks is 0 * Update milagro_bls dep * Remove debugs, code fragment in beacon chain * Tidy, add comments to block sig verifier * Fix various PR comments * Add block_root option to per_block_processing * Fix comment in block signature verifier * Fix comments from PR review * Remove old comment * Fix comment
This commit is contained in:
@@ -1,42 +1,87 @@
|
||||
use super::signature_sets::Error as SignatureSetError;
|
||||
use types::*;
|
||||
|
||||
macro_rules! impl_from_beacon_state_error {
|
||||
($type: ident) => {
|
||||
impl From<BeaconStateError> for $type {
|
||||
fn from(e: BeaconStateError) -> $type {
|
||||
$type::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
/// The error returned from the `per_block_processing` function. Indicates that a block is either
|
||||
/// invalid, or we were unable to determine it's validity (we encountered an unexpected error).
|
||||
///
|
||||
/// Any of the `...Error` variants indicate that at some point during block (and block operation)
|
||||
/// verification, there was an error. There is no indication as to _where_ that error happened
|
||||
/// (e.g., when processing attestations instead of when processing deposits).
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockProcessingError {
|
||||
RandaoSignatureInvalid,
|
||||
BulkSignatureVerificationFailed,
|
||||
StateRootMismatch,
|
||||
DepositCountInvalid {
|
||||
expected: usize,
|
||||
found: usize,
|
||||
},
|
||||
DuplicateTransfers {
|
||||
duplicates: usize,
|
||||
},
|
||||
HeaderInvalid {
|
||||
reason: HeaderInvalid,
|
||||
},
|
||||
ProposerSlashingInvalid {
|
||||
index: usize,
|
||||
reason: ProposerSlashingInvalid,
|
||||
},
|
||||
AttesterSlashingInvalid {
|
||||
index: usize,
|
||||
reason: AttesterSlashingInvalid,
|
||||
},
|
||||
IndexedAttestationInvalid {
|
||||
index: usize,
|
||||
reason: IndexedAttestationInvalid,
|
||||
},
|
||||
AttestationInvalid {
|
||||
index: usize,
|
||||
reason: AttestationInvalid,
|
||||
},
|
||||
DepositInvalid {
|
||||
index: usize,
|
||||
reason: DepositInvalid,
|
||||
},
|
||||
ExitInvalid {
|
||||
index: usize,
|
||||
reason: ExitInvalid,
|
||||
},
|
||||
TransferInvalid {
|
||||
index: usize,
|
||||
reason: TransferInvalid,
|
||||
},
|
||||
BeaconStateError(BeaconStateError),
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
}
|
||||
|
||||
macro_rules! impl_into_with_index_with_beacon_error {
|
||||
($error_type: ident, $invalid_type: ident) => {
|
||||
impl IntoWithIndex<BlockProcessingError> for $error_type {
|
||||
fn into_with_index(self, i: usize) -> BlockProcessingError {
|
||||
match self {
|
||||
$error_type::Invalid(e) => {
|
||||
BlockProcessingError::Invalid(BlockInvalid::$invalid_type(i, e))
|
||||
}
|
||||
$error_type::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
impl From<BeaconStateError> for BlockProcessingError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
BlockProcessingError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_into_with_index_without_beacon_error {
|
||||
($error_type: ident, $invalid_type: ident) => {
|
||||
impl IntoWithIndex<BlockProcessingError> for $error_type {
|
||||
fn into_with_index(self, i: usize) -> BlockProcessingError {
|
||||
match self {
|
||||
$error_type::Invalid(e) => {
|
||||
BlockProcessingError::Invalid(BlockInvalid::$invalid_type(i, e))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<SignatureSetError> for BlockProcessingError {
|
||||
fn from(e: SignatureSetError) -> Self {
|
||||
BlockProcessingError::SignatureSetError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for BlockProcessingError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
BlockProcessingError::SszTypesError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||
match e {
|
||||
BlockOperationError::Invalid(reason) => BlockProcessingError::HeaderInvalid { reason },
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// A conversion that consumes `self` and adds an `index` variable to resulting struct.
|
||||
@@ -48,81 +93,117 @@ pub trait IntoWithIndex<T>: Sized {
|
||||
fn into_with_index(self, index: usize) -> T;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block Validation
|
||||
*/
|
||||
macro_rules! impl_into_block_processing_error_with_index {
|
||||
($($type: ident),*) => {
|
||||
$(
|
||||
impl IntoWithIndex<BlockProcessingError> for BlockOperationError<$type> {
|
||||
fn into_with_index(self, index: usize) -> BlockProcessingError {
|
||||
match self {
|
||||
BlockOperationError::Invalid(reason) => BlockProcessingError::$type {
|
||||
index,
|
||||
reason
|
||||
},
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_into_block_processing_error_with_index!(
|
||||
ProposerSlashingInvalid,
|
||||
AttesterSlashingInvalid,
|
||||
IndexedAttestationInvalid,
|
||||
AttestationInvalid,
|
||||
DepositInvalid,
|
||||
ExitInvalid,
|
||||
TransferInvalid
|
||||
);
|
||||
|
||||
pub type HeaderValidationError = BlockOperationError<HeaderInvalid>;
|
||||
pub type AttesterSlashingValidationError = BlockOperationError<AttesterSlashingInvalid>;
|
||||
pub type ProposerSlashingValidationError = BlockOperationError<ProposerSlashingInvalid>;
|
||||
pub type AttestationValidationError = BlockOperationError<AttestationInvalid>;
|
||||
pub type DepositValidationError = BlockOperationError<DepositInvalid>;
|
||||
pub type ExitValidationError = BlockOperationError<ExitInvalid>;
|
||||
pub type TransferValidationError = BlockOperationError<TransferInvalid>;
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockProcessingError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(BlockInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
pub enum BlockOperationError<T> {
|
||||
Invalid(T),
|
||||
BeaconStateError(BeaconStateError),
|
||||
/// Encountered an `ssz_types::Error` whilst attempting to determine validity.
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(BlockProcessingError);
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockInvalid {
|
||||
StateSlotMismatch,
|
||||
ParentBlockRootMismatch {
|
||||
state: Hash256,
|
||||
block: Hash256,
|
||||
},
|
||||
ProposerSlashed(usize),
|
||||
BadSignature,
|
||||
BadRandaoSignature,
|
||||
MaxAttestationsExceeded,
|
||||
MaxAttesterSlashingsExceed,
|
||||
MaxProposerSlashingsExceeded,
|
||||
DepositCountInvalid,
|
||||
DuplicateTransfers,
|
||||
MaxExitsExceeded,
|
||||
MaxTransfersExceed,
|
||||
AttestationInvalid(usize, AttestationInvalid),
|
||||
/// A `IndexedAttestation` inside an `AttesterSlashing` was invalid.
|
||||
///
|
||||
/// To determine the offending `AttesterSlashing` index, divide the error message `usize` by two.
|
||||
IndexedAttestationInvalid(usize, IndexedAttestationInvalid),
|
||||
AttesterSlashingInvalid(usize, AttesterSlashingInvalid),
|
||||
ProposerSlashingInvalid(usize, ProposerSlashingInvalid),
|
||||
DepositInvalid(usize, DepositInvalid),
|
||||
// TODO: merge this into the `DepositInvalid` error.
|
||||
DepositProcessingFailed(usize),
|
||||
ExitInvalid(usize, ExitInvalid),
|
||||
TransferInvalid(usize, TransferInvalid),
|
||||
// NOTE: this is only used in tests, normally a state root mismatch is handled
|
||||
// in the beacon_chain rather than in state_processing
|
||||
StateRootMismatch,
|
||||
impl<T> BlockOperationError<T> {
|
||||
pub fn invalid(reason: T) -> BlockOperationError<T> {
|
||||
BlockOperationError::Invalid(reason)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for BlockProcessingError {
|
||||
impl<T> From<BeaconStateError> for BlockOperationError<T> {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
BlockOperationError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
impl<T> From<SignatureSetError> for BlockOperationError<T> {
|
||||
fn from(e: SignatureSetError) -> Self {
|
||||
BlockOperationError::SignatureSetError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ssz_types::Error> for BlockOperationError<T> {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
BlockProcessingError::SszTypesError(error)
|
||||
BlockOperationError::SszTypesError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BlockProcessingError> for BlockInvalid {
|
||||
fn into(self) -> BlockProcessingError {
|
||||
BlockProcessingError::Invalid(self)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attestation Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(AttestationInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
BeaconStateError(BeaconStateError),
|
||||
pub enum HeaderInvalid {
|
||||
ProposalSignatureInvalid,
|
||||
StateSlotMismatch,
|
||||
ParentBlockRootMismatch { state: Hash256, block: Hash256 },
|
||||
ProposerSlashed(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProposerSlashingInvalid {
|
||||
/// The proposer index is not a known validator.
|
||||
ProposerUnknown(u64),
|
||||
/// The two proposal have different epochs.
|
||||
///
|
||||
/// (proposal_1_slot, proposal_2_slot)
|
||||
ProposalEpochMismatch(Slot, Slot),
|
||||
/// The proposals are identical and therefore not slashable.
|
||||
ProposalsIdentical,
|
||||
/// The specified proposer cannot be slashed because they are already slashed, or not active.
|
||||
ProposerNotSlashable(u64),
|
||||
/// The first proposal signature was invalid.
|
||||
BadProposal1Signature,
|
||||
/// The second proposal signature was invalid.
|
||||
BadProposal2Signature,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterSlashingInvalid {
|
||||
/// The attestation data is identical, an attestation cannot conflict with itself.
|
||||
AttestationDataIdentical,
|
||||
/// The attestations were not in conflict.
|
||||
NotSlashable,
|
||||
/// The first `IndexedAttestation` was invalid.
|
||||
IndexedAttestation1Invalid(BlockOperationError<IndexedAttestationInvalid>),
|
||||
/// The second `IndexedAttestation` was invalid.
|
||||
IndexedAttestation2Invalid(BlockOperationError<IndexedAttestationInvalid>),
|
||||
/// The validator index is unknown. One cannot slash one who does not exist.
|
||||
UnknownValidator(u64),
|
||||
/// The specified validator has already been withdrawn.
|
||||
ValidatorAlreadyWithdrawn(u64),
|
||||
/// There were no indices able to be slashed.
|
||||
NoSlashableIndices,
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
@@ -186,69 +267,21 @@ pub enum AttestationInvalid {
|
||||
BadIndexedAttestation(IndexedAttestationInvalid),
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(AttestationValidationError);
|
||||
impl_into_with_index_with_beacon_error!(AttestationValidationError, AttestationInvalid);
|
||||
|
||||
impl From<IndexedAttestationValidationError> for AttestationValidationError {
|
||||
fn from(err: IndexedAttestationValidationError) -> Self {
|
||||
let IndexedAttestationValidationError::Invalid(e) = err;
|
||||
AttestationValidationError::Invalid(AttestationInvalid::BadIndexedAttestation(e))
|
||||
impl From<BlockOperationError<IndexedAttestationInvalid>>
|
||||
for BlockOperationError<AttestationInvalid>
|
||||
{
|
||||
fn from(e: BlockOperationError<IndexedAttestationInvalid>) -> Self {
|
||||
match e {
|
||||
BlockOperationError::Invalid(e) => {
|
||||
BlockOperationError::invalid(AttestationInvalid::BadIndexedAttestation(e))
|
||||
}
|
||||
BlockOperationError::BeaconStateError(e) => BlockOperationError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockOperationError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockOperationError::SszTypesError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for AttestationValidationError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
Self::from(IndexedAttestationValidationError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* `AttesterSlashing` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterSlashingValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(AttesterSlashingInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttesterSlashingInvalid {
|
||||
/// The attestation data is identical, an attestation cannot conflict with itself.
|
||||
AttestationDataIdentical,
|
||||
/// The attestations were not in conflict.
|
||||
NotSlashable,
|
||||
/// The first `IndexedAttestation` was invalid.
|
||||
IndexedAttestation1Invalid(IndexedAttestationInvalid),
|
||||
/// The second `IndexedAttestation` was invalid.
|
||||
IndexedAttestation2Invalid(IndexedAttestationInvalid),
|
||||
/// The validator index is unknown. One cannot slash one who does not exist.
|
||||
UnknownValidator(u64),
|
||||
/// The specified validator has already been withdrawn.
|
||||
ValidatorAlreadyWithdrawn(u64),
|
||||
/// There were no indices able to be slashed.
|
||||
NoSlashableIndices,
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(AttesterSlashingValidationError);
|
||||
impl_into_with_index_with_beacon_error!(AttesterSlashingValidationError, AttesterSlashingInvalid);
|
||||
|
||||
/*
|
||||
* `IndexedAttestation` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum IndexedAttestationValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(IndexedAttestationInvalid),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum IndexedAttestationInvalid {
|
||||
/// The custody bit 0 validators intersect with the bit 1 validators.
|
||||
@@ -271,106 +304,24 @@ pub enum IndexedAttestationInvalid {
|
||||
UnknownValidator(u64),
|
||||
/// The indexed attestation aggregate signature was not valid.
|
||||
BadSignature,
|
||||
/// There was an error whilst attempting to get a set of signatures. The signatures may have
|
||||
/// been invalid or an internal error occurred.
|
||||
SignatureSetError(SignatureSetError),
|
||||
}
|
||||
|
||||
impl Into<IndexedAttestationInvalid> for IndexedAttestationValidationError {
|
||||
fn into(self) -> IndexedAttestationInvalid {
|
||||
match self {
|
||||
IndexedAttestationValidationError::Invalid(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for IndexedAttestationValidationError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
IndexedAttestationValidationError::Invalid(
|
||||
IndexedAttestationInvalid::CustodyBitfieldBoundsError(error),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_with_index_without_beacon_error!(
|
||||
IndexedAttestationValidationError,
|
||||
IndexedAttestationInvalid
|
||||
);
|
||||
|
||||
/*
|
||||
* `ProposerSlashing` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProposerSlashingValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(ProposerSlashingInvalid),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProposerSlashingInvalid {
|
||||
/// The proposer index is not a known validator.
|
||||
ProposerUnknown(u64),
|
||||
/// The two proposal have different epochs.
|
||||
///
|
||||
/// (proposal_1_slot, proposal_2_slot)
|
||||
ProposalEpochMismatch(Slot, Slot),
|
||||
/// The proposals are identical and therefore not slashable.
|
||||
ProposalsIdentical,
|
||||
/// The specified proposer cannot be slashed because they are already slashed, or not active.
|
||||
ProposerNotSlashable(u64),
|
||||
/// The first proposal signature was invalid.
|
||||
BadProposal1Signature,
|
||||
/// The second proposal signature was invalid.
|
||||
BadProposal2Signature,
|
||||
}
|
||||
|
||||
impl_into_with_index_without_beacon_error!(
|
||||
ProposerSlashingValidationError,
|
||||
ProposerSlashingInvalid
|
||||
);
|
||||
|
||||
/*
|
||||
* `Deposit` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DepositValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(DepositInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DepositInvalid {
|
||||
/// The deposit index does not match the state index.
|
||||
BadIndex { state: u64, deposit: u64 },
|
||||
/// The signature (proof-of-possession) does not match the given pubkey.
|
||||
BadSignature,
|
||||
/// The signature does not represent a valid BLS signature.
|
||||
BadSignatureBytes,
|
||||
/// The signature or pubkey does not represent a valid BLS point.
|
||||
BadBlsBytes,
|
||||
/// The specified `branch` and `index` did not form a valid proof that the deposit is included
|
||||
/// in the eth1 deposit root.
|
||||
BadMerkleProof,
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(DepositValidationError);
|
||||
impl_into_with_index_with_beacon_error!(DepositValidationError, DepositInvalid);
|
||||
|
||||
/*
|
||||
* `Exit` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExitValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(ExitInvalid),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExitInvalid {
|
||||
/// The specified validator is not active.
|
||||
@@ -390,24 +341,11 @@ pub enum ExitInvalid {
|
||||
},
|
||||
/// The exit signature was not signed by the validator.
|
||||
BadSignature,
|
||||
/// There was an error whilst attempting to get a set of signatures. The signatures may have
|
||||
/// been invalid or an internal error occurred.
|
||||
SignatureSetError(SignatureSetError),
|
||||
}
|
||||
|
||||
impl_into_with_index_without_beacon_error!(ExitValidationError, ExitInvalid);
|
||||
|
||||
/*
|
||||
* `Transfer` Validation
|
||||
*/
|
||||
|
||||
/// The object is invalid or validation failed.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TransferValidationError {
|
||||
/// Validation completed successfully and the object is invalid.
|
||||
Invalid(TransferInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TransferInvalid {
|
||||
/// The validator indicated by `transfer.from` is unknown.
|
||||
@@ -460,6 +398,3 @@ pub enum TransferInvalid {
|
||||
/// (proposer_balance, transfer_fee)
|
||||
ProposerBalanceOverflow(u64, u64),
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(TransferValidationError);
|
||||
impl_into_with_index_with_beacon_error!(TransferValidationError, TransferInvalid);
|
||||
|
||||
Reference in New Issue
Block a user