2022-06-30 00:24:39 +00:00
|
|
|
package evaluators
|
|
|
|
|
|
|
|
import (
|
2022-12-21 04:43:29 +00:00
|
|
|
"bytes"
|
2022-06-30 00:24:39 +00:00
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
|
|
"github.com/pkg/errors"
|
2024-02-15 05:46:47 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/runtime/interop"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/testing/endtoend/components"
|
|
|
|
e2e "github.com/prysmaticlabs/prysm/v5/testing/endtoend/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/testing/endtoend/policies"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/testing/endtoend/types"
|
2022-12-21 04:43:29 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2022-06-30 00:24:39 +00:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
|
|
)
|
|
|
|
|
|
|
|
var FeeRecipientIsPresent = types.Evaluator{
|
2023-12-16 11:37:44 +00:00
|
|
|
Name: "fee_recipient_is_present_%d",
|
|
|
|
Policy: func(e primitives.Epoch) bool {
|
|
|
|
fEpoch := params.BeaconConfig().BellatrixForkEpoch
|
|
|
|
return policies.AfterNthEpoch(fEpoch)(e)
|
|
|
|
},
|
2022-06-30 00:24:39 +00:00
|
|
|
Evaluation: feeRecipientIsPresent,
|
|
|
|
}
|
|
|
|
|
2022-12-21 04:43:29 +00:00
|
|
|
func lhKeyMap() (map[string]bool, error) {
|
|
|
|
if e2e.TestParams.LighthouseBeaconNodeCount == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
pry, lh := e2e.TestParams.BeaconNodeCount, e2e.TestParams.LighthouseBeaconNodeCount
|
|
|
|
valPerNode := int(params.BeaconConfig().MinGenesisActiveValidatorCount) / (pry + lh)
|
|
|
|
lhOff := valPerNode * pry
|
|
|
|
_, keys, err := interop.DeterministicallyGenerateKeys(uint64(lhOff), uint64(valPerNode*lh))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
km := make(map[string]bool)
|
|
|
|
for _, k := range keys {
|
|
|
|
km[hexutil.Encode(k.Marshal())] = true
|
|
|
|
}
|
|
|
|
return km, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func valKeyMap() (map[string]bool, error) {
|
|
|
|
nvals := params.BeaconConfig().MinGenesisActiveValidatorCount
|
|
|
|
// matches validator start in validator component + validators used for deposits
|
|
|
|
_, pubs, err := interop.DeterministicallyGenerateKeys(0, nvals+e2e.DepositCount)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
km := make(map[string]bool)
|
|
|
|
for _, k := range pubs {
|
|
|
|
km[hexutil.Encode(k.Marshal())] = true
|
|
|
|
}
|
|
|
|
return km, nil
|
|
|
|
}
|
|
|
|
|
2023-01-06 07:49:42 +00:00
|
|
|
func feeRecipientIsPresent(_ *types.EvaluationContext, conns ...*grpc.ClientConn) error {
|
2022-06-30 00:24:39 +00:00
|
|
|
conn := conns[0]
|
|
|
|
client := ethpb.NewBeaconChainClient(conn)
|
|
|
|
chainHead, err := client.GetChainHead(context.Background(), &emptypb.Empty{})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to get chain head")
|
|
|
|
}
|
|
|
|
req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch.Sub(1)}}
|
|
|
|
blks, err := client.ListBeaconBlocks(context.Background(), req)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to list blocks")
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcclient, err := rpc.DialHTTP(fmt.Sprintf("http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1RPCPort))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rpcclient.Close()
|
|
|
|
|
2022-12-21 04:43:29 +00:00
|
|
|
valkeys, err := valKeyMap()
|
2022-07-01 14:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
lhkeys, err := lhKeyMap()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-07-01 14:02:01 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 00:24:39 +00:00
|
|
|
for _, ctr := range blks.BlockContainers {
|
2022-12-21 04:43:29 +00:00
|
|
|
if ctr.GetBellatrixBlock() != nil {
|
|
|
|
bb := ctr.GetBellatrixBlock().Block
|
|
|
|
payload := bb.Body.ExecutionPayload
|
|
|
|
// If the beacon chain has transitioned to Bellatrix, but the EL hasn't hit TTD, we could see a few slots
|
|
|
|
// of blocks with empty payloads.
|
|
|
|
if bytes.Equal(payload.BlockHash, make([]byte, 32)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(payload.FeeRecipient) == 0 || hexutil.Encode(payload.FeeRecipient) == params.BeaconConfig().EthBurnAddressHex {
|
2024-02-22 22:40:36 +00:00
|
|
|
log.WithField("proposerIndex", bb.ProposerIndex).WithField("slot", bb.Slot).Error("fee recipient eval bug")
|
2022-06-30 00:24:39 +00:00
|
|
|
return errors.New("fee recipient is not set")
|
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
|
|
|
|
fr := common.BytesToAddress(payload.FeeRecipient)
|
|
|
|
gvr := ðpb.GetValidatorRequest{
|
2022-06-30 00:24:39 +00:00
|
|
|
QueryFilter: ðpb.GetValidatorRequest_Index{
|
|
|
|
Index: ctr.GetBellatrixBlock().Block.ProposerIndex,
|
|
|
|
},
|
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
validator, err := client.GetValidator(context.Background(), gvr)
|
2022-06-30 00:24:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to get validators")
|
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
pk := hexutil.Encode(validator.GetPublicKey())
|
|
|
|
|
|
|
|
if _, ok := lhkeys[pk]; ok {
|
|
|
|
// Don't check lighthouse keys.
|
2022-07-01 14:02:01 +00:00
|
|
|
continue
|
2022-06-30 00:24:39 +00:00
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
|
|
|
|
// In e2e we generate deterministic keys by validator index, and then use a slice of their public key bytes
|
|
|
|
// as the fee recipient, so that this will also be deterministic, so this test can statelessly verify it.
|
|
|
|
// These should be the only keys we see.
|
|
|
|
// Otherwise something has changed in e2e and this test needs to be updated.
|
|
|
|
_, knownKey := valkeys[pk]
|
|
|
|
if !knownKey {
|
|
|
|
log.WithField("pubkey", pk).
|
|
|
|
WithField("slot", bb.Slot).
|
2024-02-22 22:40:36 +00:00
|
|
|
WithField("proposerIndex", bb.ProposerIndex).
|
|
|
|
WithField("feeRecipient", fr.Hex()).
|
2022-12-21 04:43:29 +00:00
|
|
|
Warn("unknown key observed, not a deterministically generated key")
|
|
|
|
return errors.New("unknown key observed, not a deterministically generated key")
|
2022-06-30 00:24:39 +00:00
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
|
|
|
|
if components.FeeRecipientFromPubkey(pk) != fr.Hex() {
|
2022-06-30 00:24:39 +00:00
|
|
|
return fmt.Errorf("publickey %s, fee recipient %s does not match the proposer settings fee recipient %s",
|
2022-12-21 04:43:29 +00:00
|
|
|
pk, fr.Hex(), components.FeeRecipientFromPubkey(pk))
|
2022-06-30 00:24:39 +00:00
|
|
|
}
|
|
|
|
|
2022-12-21 04:43:29 +00:00
|
|
|
if err := checkRecipientBalance(rpcclient, common.BytesToHash(payload.BlockHash), common.BytesToHash(payload.ParentHash), fr); err != nil {
|
2022-06-30 00:24:39 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-12-21 04:43:29 +00:00
|
|
|
|
|
|
|
func checkRecipientBalance(c *rpc.Client, block, parent common.Hash, account common.Address) error {
|
|
|
|
web3 := ethclient.NewClient(c)
|
|
|
|
ctx := context.Background()
|
|
|
|
b, err := web3.BlockByHash(ctx, block)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bal, err := web3.BalanceAt(ctx, account, b.Number())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pBlock, err := web3.BlockByHash(ctx, parent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pBal, err := web3.BalanceAt(ctx, account, pBlock.Number())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if b.GasUsed() > 0 && bal.Uint64() <= pBal.Uint64() {
|
|
|
|
return errors.Errorf("account balance didn't change after applying fee recipient for account: %s", account.Hex())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|