2020-07-06 22:27:42 +00:00
package blocks
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
2021-03-08 22:37:33 +00:00
iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
2020-07-06 22:27:42 +00:00
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
2021-07-21 21:34:07 +00:00
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
2020-07-06 22:27:42 +00:00
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
2020-07-29 04:19:07 +00:00
"github.com/prysmaticlabs/prysm/shared/depositutil"
2020-07-06 22:27:42 +00:00
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/trieutil"
)
// ProcessPreGenesisDeposits processes a deposit for the beacon state before chainstart.
func ProcessPreGenesisDeposits (
ctx context . Context ,
2021-03-08 22:37:33 +00:00
beaconState iface . BeaconState ,
2020-07-06 22:27:42 +00:00
deposits [ ] * ethpb . Deposit ,
2021-03-08 22:37:33 +00:00
) ( iface . BeaconState , error ) {
2020-07-06 22:27:42 +00:00
var err error
2021-04-02 14:48:41 +00:00
beaconState , err = ProcessDeposits ( ctx , beaconState , deposits )
2020-07-06 22:27:42 +00:00
if err != nil {
return nil , errors . Wrap ( err , "could not process deposit" )
}
2021-06-11 19:29:50 +00:00
beaconState , err = ActivateValidatorWithEffectiveBalance ( beaconState , deposits )
2021-04-05 16:49:17 +00:00
if err != nil {
return nil , err
}
return beaconState , nil
}
2021-06-11 19:29:50 +00:00
// ActivateValidatorWithEffectiveBalance updates validator's effective balance, and if it's above MaxEffectiveBalance, validator becomes active in genesis.
func ActivateValidatorWithEffectiveBalance ( beaconState iface . BeaconState , deposits [ ] * ethpb . Deposit ) ( iface . BeaconState , error ) {
2020-07-06 22:27:42 +00:00
for _ , deposit := range deposits {
pubkey := deposit . Data . PublicKey
index , ok := beaconState . ValidatorIndexByPubkey ( bytesutil . ToBytes48 ( pubkey ) )
2020-11-02 16:34:37 +00:00
// In the event of the pubkey not existing, we continue processing the other
// deposits.
2020-07-06 22:27:42 +00:00
if ! ok {
2020-11-02 16:34:37 +00:00
continue
2020-07-06 22:27:42 +00:00
}
balance , err := beaconState . BalanceAtIndex ( index )
if err != nil {
return nil , err
}
validator , err := beaconState . ValidatorAtIndex ( index )
if err != nil {
return nil , err
}
validator . EffectiveBalance = mathutil . Min ( balance - balance % params . BeaconConfig ( ) . EffectiveBalanceIncrement , params . BeaconConfig ( ) . MaxEffectiveBalance )
if validator . EffectiveBalance ==
params . BeaconConfig ( ) . MaxEffectiveBalance {
validator . ActivationEligibilityEpoch = 0
validator . ActivationEpoch = 0
}
if err := beaconState . UpdateValidatorAtIndex ( index , validator ) ; err != nil {
return nil , err
}
}
return beaconState , nil
}
// ProcessDeposits is one of the operations performed on each processed
// beacon block to verify queued validators from the Ethereum 1.0 Deposit Contract
// into the beacon chain.
//
// Spec pseudocode definition:
// For each deposit in block.body.deposits:
// process_deposit(state, deposit)
func ProcessDeposits (
ctx context . Context ,
2021-03-08 22:37:33 +00:00
beaconState iface . BeaconState ,
2021-04-02 14:48:41 +00:00
deposits [ ] * ethpb . Deposit ,
2021-03-08 22:37:33 +00:00
) ( iface . BeaconState , error ) {
2020-07-06 22:27:42 +00:00
// Attempt to verify all deposit signatures at once, if this fails then fall back to processing
// individual deposits with signature verification enabled.
2021-06-11 19:29:50 +00:00
batchVerified , err := BatchVerifyDepositsSignatures ( ctx , deposits )
2021-04-05 16:49:17 +00:00
if err != nil {
return nil , err
2020-07-06 22:27:42 +00:00
}
for _ , deposit := range deposits {
if deposit == nil || deposit . Data == nil {
return nil , errors . New ( "got a nil deposit in block" )
}
2021-04-05 16:49:17 +00:00
beaconState , err = ProcessDeposit ( beaconState , deposit , batchVerified )
2020-07-06 22:27:42 +00:00
if err != nil {
return nil , errors . Wrapf ( err , "could not process deposit from %#x" , bytesutil . Trunc ( deposit . Data . PublicKey ) )
}
}
return beaconState , nil
}
2021-06-11 19:29:50 +00:00
// BatchVerifyDepositsSignatures batch verifies deposit signatures.
func BatchVerifyDepositsSignatures ( ctx context . Context , deposits [ ] * ethpb . Deposit ) ( bool , error ) {
2021-04-05 16:49:17 +00:00
var err error
domain , err := helpers . ComputeDomain ( params . BeaconConfig ( ) . DomainDeposit , nil , nil )
if err != nil {
return false , err
}
verified := false
if err := verifyDepositDataWithDomain ( ctx , deposits , domain ) ; err != nil {
log . WithError ( err ) . Debug ( "Failed to batch verify deposits signatures, will try individual verify" )
verified = true
}
return verified , nil
}
2020-07-06 22:27:42 +00:00
// ProcessDeposit takes in a deposit object and inserts it
// into the registry as a new validator or balance change.
//
// Spec pseudocode definition:
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
// # Verify the Merkle branch
// assert is_valid_merkle_branch(
// leaf=hash_tree_root(deposit.data),
// branch=deposit.proof,
// depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
// index=state.eth1_deposit_index,
// root=state.eth1_data.deposit_root,
// )
//
// # Deposits must be processed in order
// state.eth1_deposit_index += 1
//
// pubkey = deposit.data.pubkey
// amount = deposit.data.amount
// validator_pubkeys = [v.pubkey for v in state.validators]
// if pubkey not in validator_pubkeys:
// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
// deposit_message = DepositMessage(
// pubkey=deposit.data.pubkey,
// withdrawal_credentials=deposit.data.withdrawal_credentials,
// amount=deposit.data.amount,
// )
// domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
// signing_root = compute_signing_root(deposit_message, domain)
// if not bls.Verify(pubkey, signing_root, deposit.data.signature):
// return
//
// # Add validator and balance entries
// state.validators.append(get_validator_from_deposit(state, deposit))
// state.balances.append(amount)
// else:
// # Increase balance by deposit amount
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
// increase_balance(state, index, amount)
2021-03-08 22:37:33 +00:00
func ProcessDeposit ( beaconState iface . BeaconState , deposit * ethpb . Deposit , verifySignature bool ) ( iface . BeaconState , error ) {
2020-07-06 22:27:42 +00:00
if err := verifyDeposit ( beaconState , deposit ) ; err != nil {
if deposit == nil || deposit . Data == nil {
return nil , err
}
return nil , errors . Wrapf ( err , "could not verify deposit from %#x" , bytesutil . Trunc ( deposit . Data . PublicKey ) )
}
if err := beaconState . SetEth1DepositIndex ( beaconState . Eth1DepositIndex ( ) + 1 ) ; err != nil {
return nil , err
}
pubKey := deposit . Data . PublicKey
amount := deposit . Data . Amount
index , ok := beaconState . ValidatorIndexByPubkey ( bytesutil . ToBytes48 ( pubKey ) )
2020-07-24 05:43:15 +00:00
if ! ok {
2020-07-06 22:27:42 +00:00
if verifySignature {
domain , err := helpers . ComputeDomain ( params . BeaconConfig ( ) . DomainDeposit , nil , nil )
if err != nil {
return nil , err
}
2020-10-12 08:11:05 +00:00
if err := verifyDepositDataSigningRoot ( deposit . Data , domain ) ; err != nil {
2020-07-06 22:27:42 +00:00
// Ignore this error as in the spec pseudo code.
log . Debugf ( "Skipping deposit: could not verify deposit data signature: %v" , err )
return beaconState , nil
}
}
effectiveBalance := amount - ( amount % params . BeaconConfig ( ) . EffectiveBalanceIncrement )
if params . BeaconConfig ( ) . MaxEffectiveBalance < effectiveBalance {
effectiveBalance = params . BeaconConfig ( ) . MaxEffectiveBalance
}
if err := beaconState . AppendValidator ( & ethpb . Validator {
PublicKey : pubKey ,
WithdrawalCredentials : deposit . Data . WithdrawalCredentials ,
ActivationEligibilityEpoch : params . BeaconConfig ( ) . FarFutureEpoch ,
ActivationEpoch : params . BeaconConfig ( ) . FarFutureEpoch ,
ExitEpoch : params . BeaconConfig ( ) . FarFutureEpoch ,
WithdrawableEpoch : params . BeaconConfig ( ) . FarFutureEpoch ,
EffectiveBalance : effectiveBalance ,
} ) ; err != nil {
return nil , err
}
if err := beaconState . AppendBalance ( amount ) ; err != nil {
return nil , err
}
2020-08-10 16:16:45 +00:00
} else if err := helpers . IncreaseBalance ( beaconState , index , amount ) ; err != nil {
return nil , err
2020-07-06 22:27:42 +00:00
}
return beaconState , nil
}
2021-03-08 22:37:33 +00:00
func verifyDeposit ( beaconState iface . ReadOnlyBeaconState , deposit * ethpb . Deposit ) error {
2020-07-06 22:27:42 +00:00
// Verify Merkle proof of deposit and deposit trie root.
if deposit == nil || deposit . Data == nil {
return errors . New ( "received nil deposit or nil deposit data" )
}
eth1Data := beaconState . Eth1Data ( )
if eth1Data == nil {
return errors . New ( "received nil eth1data in the beacon state" )
}
receiptRoot := eth1Data . DepositRoot
2020-08-27 18:13:32 +00:00
leaf , err := deposit . Data . HashTreeRoot ( )
2020-07-06 22:27:42 +00:00
if err != nil {
return errors . Wrap ( err , "could not tree hash deposit data" )
}
if ok := trieutil . VerifyMerkleBranch (
receiptRoot ,
leaf [ : ] ,
int ( beaconState . Eth1DepositIndex ( ) ) ,
deposit . Proof ,
2020-08-25 20:57:41 +00:00
params . BeaconConfig ( ) . DepositContractTreeDepth ,
2020-07-06 22:27:42 +00:00
) ; ! ok {
return fmt . Errorf (
"deposit merkle branch of deposit root did not verify for root: %#x" ,
receiptRoot ,
)
}
2020-08-25 20:57:41 +00:00
2020-07-06 22:27:42 +00:00
return nil
}
2020-10-12 08:11:05 +00:00
func verifyDepositDataSigningRoot ( obj * ethpb . Deposit_Data , domain [ ] byte ) error {
2020-07-29 04:19:07 +00:00
return depositutil . VerifyDepositSignature ( obj , domain )
2020-07-06 22:27:42 +00:00
}
func verifyDepositDataWithDomain ( ctx context . Context , deps [ ] * ethpb . Deposit , domain [ ] byte ) error {
if len ( deps ) == 0 {
return nil
}
pks := make ( [ ] bls . PublicKey , len ( deps ) )
2020-09-16 13:28:28 +00:00
sigs := make ( [ ] [ ] byte , len ( deps ) )
2020-07-06 22:27:42 +00:00
msgs := make ( [ ] [ 32 ] byte , len ( deps ) )
for i , dep := range deps {
if ctx . Err ( ) != nil {
return ctx . Err ( )
}
if dep == nil || dep . Data == nil {
return errors . New ( "nil deposit" )
}
dpk , err := bls . PublicKeyFromBytes ( dep . Data . PublicKey )
if err != nil {
return err
}
pks [ i ] = dpk
2020-09-16 13:28:28 +00:00
sigs [ i ] = dep . Data . Signature
2021-01-12 00:45:11 +00:00
depositMessage := & pb . DepositMessage {
PublicKey : dep . Data . PublicKey ,
WithdrawalCredentials : dep . Data . WithdrawalCredentials ,
Amount : dep . Data . Amount ,
}
2021-01-20 20:30:58 +00:00
sr , err := helpers . ComputeSigningRoot ( depositMessage , domain )
2020-07-06 22:27:42 +00:00
if err != nil {
2021-01-20 20:30:58 +00:00
return err
2020-07-06 22:27:42 +00:00
}
2021-01-20 20:30:58 +00:00
msgs [ i ] = sr
2020-07-06 22:27:42 +00:00
}
verify , err := bls . VerifyMultipleSignatures ( sigs , msgs , pks )
if err != nil {
return errors . Errorf ( "could not verify multiple signatures: %v" , err )
}
if ! verify {
return errors . New ( "one or more deposit signatures did not verify" )
}
return nil
}