github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/blocks/deposit.go (about)

     1  package blocks
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
     9  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    10  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    11  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    12  	"github.com/prysmaticlabs/prysm/shared/bls"
    13  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    14  	"github.com/prysmaticlabs/prysm/shared/depositutil"
    15  	"github.com/prysmaticlabs/prysm/shared/mathutil"
    16  	"github.com/prysmaticlabs/prysm/shared/params"
    17  	"github.com/prysmaticlabs/prysm/shared/trieutil"
    18  )
    19  
    20  // ProcessPreGenesisDeposits processes a deposit for the beacon state before chainstart.
    21  func ProcessPreGenesisDeposits(
    22  	ctx context.Context,
    23  	beaconState iface.BeaconState,
    24  	deposits []*ethpb.Deposit,
    25  ) (iface.BeaconState, error) {
    26  	var err error
    27  	beaconState, err = ProcessDeposits(ctx, beaconState, deposits)
    28  	if err != nil {
    29  		return nil, errors.Wrap(err, "could not process deposit")
    30  	}
    31  	beaconState, err = ActivateValidatorWithEffectiveBalance(beaconState, deposits)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	return beaconState, nil
    36  }
    37  
    38  // ActivateValidatorWithEffectiveBalance updates validator's effective balance, and if it's above MaxEffectiveBalance, validator becomes active in genesis.
    39  func ActivateValidatorWithEffectiveBalance(beaconState iface.BeaconState, deposits []*ethpb.Deposit) (iface.BeaconState, error) {
    40  	for _, deposit := range deposits {
    41  		pubkey := deposit.Data.PublicKey
    42  		index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubkey))
    43  		// In the event of the pubkey not existing, we continue processing the other
    44  		// deposits.
    45  		if !ok {
    46  			continue
    47  		}
    48  		balance, err := beaconState.BalanceAtIndex(index)
    49  		if err != nil {
    50  			return nil, err
    51  		}
    52  		validator, err := beaconState.ValidatorAtIndex(index)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		validator.EffectiveBalance = mathutil.Min(balance-balance%params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalance)
    57  		if validator.EffectiveBalance ==
    58  			params.BeaconConfig().MaxEffectiveBalance {
    59  			validator.ActivationEligibilityEpoch = 0
    60  			validator.ActivationEpoch = 0
    61  		}
    62  		if err := beaconState.UpdateValidatorAtIndex(index, validator); err != nil {
    63  			return nil, err
    64  		}
    65  	}
    66  	return beaconState, nil
    67  }
    68  
    69  // ProcessDeposits is one of the operations performed on each processed
    70  // beacon block to verify queued validators from the Ethereum 1.0 Deposit Contract
    71  // into the beacon chain.
    72  //
    73  // Spec pseudocode definition:
    74  //   For each deposit in block.body.deposits:
    75  //     process_deposit(state, deposit)
    76  func ProcessDeposits(
    77  	ctx context.Context,
    78  	beaconState iface.BeaconState,
    79  	deposits []*ethpb.Deposit,
    80  ) (iface.BeaconState, error) {
    81  	// Attempt to verify all deposit signatures at once, if this fails then fall back to processing
    82  	// individual deposits with signature verification enabled.
    83  	batchVerified, err := BatchVerifyDepositsSignatures(ctx, deposits)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	for _, deposit := range deposits {
    89  		if deposit == nil || deposit.Data == nil {
    90  			return nil, errors.New("got a nil deposit in block")
    91  		}
    92  		beaconState, err = ProcessDeposit(beaconState, deposit, batchVerified)
    93  		if err != nil {
    94  			return nil, errors.Wrapf(err, "could not process deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey))
    95  		}
    96  	}
    97  	return beaconState, nil
    98  }
    99  
   100  // BatchVerifyDepositsSignatures batch verifies deposit signatures.
   101  func BatchVerifyDepositsSignatures(ctx context.Context, deposits []*ethpb.Deposit) (bool, error) {
   102  	var err error
   103  	domain, err := helpers.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil)
   104  	if err != nil {
   105  		return false, err
   106  	}
   107  
   108  	verified := false
   109  	if err := verifyDepositDataWithDomain(ctx, deposits, domain); err != nil {
   110  		log.WithError(err).Debug("Failed to batch verify deposits signatures, will try individual verify")
   111  		verified = true
   112  	}
   113  	return verified, nil
   114  }
   115  
   116  // ProcessDeposit takes in a deposit object and inserts it
   117  // into the registry as a new validator or balance change.
   118  //
   119  // Spec pseudocode definition:
   120  // def process_deposit(state: BeaconState, deposit: Deposit) -> None:
   121  //    # Verify the Merkle branch
   122  //    assert is_valid_merkle_branch(
   123  //        leaf=hash_tree_root(deposit.data),
   124  //        branch=deposit.proof,
   125  //        depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1,  # Add 1 for the List length mix-in
   126  //        index=state.eth1_deposit_index,
   127  //        root=state.eth1_data.deposit_root,
   128  //    )
   129  //
   130  //    # Deposits must be processed in order
   131  //    state.eth1_deposit_index += 1
   132  //
   133  //    pubkey = deposit.data.pubkey
   134  //    amount = deposit.data.amount
   135  //    validator_pubkeys = [v.pubkey for v in state.validators]
   136  //    if pubkey not in validator_pubkeys:
   137  //        # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
   138  //        deposit_message = DepositMessage(
   139  //            pubkey=deposit.data.pubkey,
   140  //            withdrawal_credentials=deposit.data.withdrawal_credentials,
   141  //            amount=deposit.data.amount,
   142  //        )
   143  //        domain = compute_domain(DOMAIN_DEPOSIT)  # Fork-agnostic domain since deposits are valid across forks
   144  //        signing_root = compute_signing_root(deposit_message, domain)
   145  //        if not bls.Verify(pubkey, signing_root, deposit.data.signature):
   146  //            return
   147  //
   148  //        # Add validator and balance entries
   149  //        state.validators.append(get_validator_from_deposit(state, deposit))
   150  //        state.balances.append(amount)
   151  //    else:
   152  //        # Increase balance by deposit amount
   153  //        index = ValidatorIndex(validator_pubkeys.index(pubkey))
   154  //        increase_balance(state, index, amount)
   155  func ProcessDeposit(beaconState iface.BeaconState, deposit *ethpb.Deposit, verifySignature bool) (iface.BeaconState, error) {
   156  	if err := verifyDeposit(beaconState, deposit); err != nil {
   157  		if deposit == nil || deposit.Data == nil {
   158  			return nil, err
   159  		}
   160  		return nil, errors.Wrapf(err, "could not verify deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey))
   161  	}
   162  	if err := beaconState.SetEth1DepositIndex(beaconState.Eth1DepositIndex() + 1); err != nil {
   163  		return nil, err
   164  	}
   165  	pubKey := deposit.Data.PublicKey
   166  	amount := deposit.Data.Amount
   167  	index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey))
   168  	if !ok {
   169  		if verifySignature {
   170  			domain, err := helpers.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil)
   171  			if err != nil {
   172  				return nil, err
   173  			}
   174  			if err := verifyDepositDataSigningRoot(deposit.Data, domain); err != nil {
   175  				// Ignore this error as in the spec pseudo code.
   176  				log.Debugf("Skipping deposit: could not verify deposit data signature: %v", err)
   177  				return beaconState, nil
   178  			}
   179  		}
   180  
   181  		effectiveBalance := amount - (amount % params.BeaconConfig().EffectiveBalanceIncrement)
   182  		if params.BeaconConfig().MaxEffectiveBalance < effectiveBalance {
   183  			effectiveBalance = params.BeaconConfig().MaxEffectiveBalance
   184  		}
   185  		if err := beaconState.AppendValidator(&ethpb.Validator{
   186  			PublicKey:                  pubKey,
   187  			WithdrawalCredentials:      deposit.Data.WithdrawalCredentials,
   188  			ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
   189  			ActivationEpoch:            params.BeaconConfig().FarFutureEpoch,
   190  			ExitEpoch:                  params.BeaconConfig().FarFutureEpoch,
   191  			WithdrawableEpoch:          params.BeaconConfig().FarFutureEpoch,
   192  			EffectiveBalance:           effectiveBalance,
   193  		}); err != nil {
   194  			return nil, err
   195  		}
   196  		if err := beaconState.AppendBalance(amount); err != nil {
   197  			return nil, err
   198  		}
   199  	} else if err := helpers.IncreaseBalance(beaconState, index, amount); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	return beaconState, nil
   204  }
   205  
   206  func verifyDeposit(beaconState iface.ReadOnlyBeaconState, deposit *ethpb.Deposit) error {
   207  	// Verify Merkle proof of deposit and deposit trie root.
   208  	if deposit == nil || deposit.Data == nil {
   209  		return errors.New("received nil deposit or nil deposit data")
   210  	}
   211  	eth1Data := beaconState.Eth1Data()
   212  	if eth1Data == nil {
   213  		return errors.New("received nil eth1data in the beacon state")
   214  	}
   215  
   216  	receiptRoot := eth1Data.DepositRoot
   217  	leaf, err := deposit.Data.HashTreeRoot()
   218  	if err != nil {
   219  		return errors.Wrap(err, "could not tree hash deposit data")
   220  	}
   221  	if ok := trieutil.VerifyMerkleBranch(
   222  		receiptRoot,
   223  		leaf[:],
   224  		int(beaconState.Eth1DepositIndex()),
   225  		deposit.Proof,
   226  		params.BeaconConfig().DepositContractTreeDepth,
   227  	); !ok {
   228  		return fmt.Errorf(
   229  			"deposit merkle branch of deposit root did not verify for root: %#x",
   230  			receiptRoot,
   231  		)
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, domain []byte) error {
   238  	return depositutil.VerifyDepositSignature(obj, domain)
   239  }
   240  
   241  func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, domain []byte) error {
   242  	if len(deps) == 0 {
   243  		return nil
   244  	}
   245  	pks := make([]bls.PublicKey, len(deps))
   246  	sigs := make([][]byte, len(deps))
   247  	msgs := make([][32]byte, len(deps))
   248  	for i, dep := range deps {
   249  		if ctx.Err() != nil {
   250  			return ctx.Err()
   251  		}
   252  		if dep == nil || dep.Data == nil {
   253  			return errors.New("nil deposit")
   254  		}
   255  		dpk, err := bls.PublicKeyFromBytes(dep.Data.PublicKey)
   256  		if err != nil {
   257  			return err
   258  		}
   259  		pks[i] = dpk
   260  		sigs[i] = dep.Data.Signature
   261  		depositMessage := &pb.DepositMessage{
   262  			PublicKey:             dep.Data.PublicKey,
   263  			WithdrawalCredentials: dep.Data.WithdrawalCredentials,
   264  			Amount:                dep.Data.Amount,
   265  		}
   266  		sr, err := helpers.ComputeSigningRoot(depositMessage, domain)
   267  		if err != nil {
   268  			return err
   269  		}
   270  		msgs[i] = sr
   271  	}
   272  	verify, err := bls.VerifyMultipleSignatures(sigs, msgs, pks)
   273  	if err != nil {
   274  		return errors.Errorf("could not verify multiple signatures: %v", err)
   275  	}
   276  	if !verify {
   277  		return errors.New("one or more deposit signatures did not verify")
   278  	}
   279  	return nil
   280  }