From ebf3897017ca1db2bb790de6affac8119b698f50 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 3 Sep 2021 11:26:10 -0700 Subject: [PATCH] Validator: propose Altair block (#9414) * Validator can propose block * Gazelle * Preston's feedback * Rename * Fix deep source * Fix build Co-authored-by: Raul Jordan Co-authored-by: Preston Van Loon --- validator/client/BUILD.bazel | 1 + validator/client/propose.go | 222 ++++++++++++++++++++++++++++--- validator/client/propose_test.go | 192 +++++++++++++++++++++++++- 3 files changed, 393 insertions(+), 22 deletions(-) diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 9f5094a9b4..66e4c7a9e1 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -43,6 +43,7 @@ go_library( "//shared/slotutil:go_default_library", "//shared/timeutils:go_default_library", "//shared/traceutil:go_default_library", + "//shared/version:go_default_library", "//validator/accounts/iface:go_default_library", "//validator/accounts/wallet:go_default_library", "//validator/client/iface:go_default_library", diff --git a/validator/client/propose.go b/validator/client/propose.go index dc14bd3068..dd6e672f3e 100644 --- a/validator/client/propose.go +++ b/validator/client/propose.go @@ -10,14 +10,16 @@ import ( types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" - wrapperv1 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" + wrapper "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/mputil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/rand" "github.com/prysmaticlabs/prysm/shared/timeutils" + "github.com/prysmaticlabs/prysm/shared/version" "github.com/prysmaticlabs/prysm/validator/client/iface" "github.com/sirupsen/logrus" "go.opencensus.io/trace" @@ -36,6 +38,16 @@ const signExitErr = "could not sign voluntary exit proposal" // the state root computation, and finally signed by the validator before being // sent back to the beacon node for broadcasting. func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [48]byte) { + currEpoch := helpers.SlotToEpoch(slot) + switch { + case currEpoch >= params.BeaconConfig().AltairForkEpoch: + v.proposeBlockAltair(ctx, slot, pubKey) + default: + v.proposeBlockPhase0(ctx, slot, pubKey) + } +} + +func (v *validator) proposeBlockPhase0(ctx context.Context, slot types.Slot, pubKey [48]byte) { if slot == 0 { log.Debug("Assigned to genesis slot, skipping proposal") return @@ -43,7 +55,7 @@ func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [4 lock := mputil.NewMultilock(fmt.Sprint(iface.RoleProposer), string(pubKey[:])) lock.Lock() defer lock.Unlock() - ctx, span := trace.StartSpan(ctx, "validator.ProposeBlock") + ctx, span := trace.StartSpan(ctx, "validator.proposeBlockPhase0") defer span.End() fmtKey := fmt.Sprintf("%#x", pubKey[:]) @@ -84,7 +96,7 @@ func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [4 } // Sign returned block from beacon node - sig, domain, err := v.signBlock(ctx, pubKey, epoch, b) + sig, domain, err := v.signBlock(ctx, pubKey, epoch, wrapper.WrappedPhase0BeaconBlock(b)) if err != nil { log.WithError(err).Error("Failed to sign block") if v.emitAccountMetrics { @@ -106,16 +118,16 @@ func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [4 return } - if err := v.preBlockSignValidations(ctx, pubKey, wrapperv1.WrappedPhase0BeaconBlock(b), signingRoot); err != nil { + if err := v.preBlockSignValidations(ctx, pubKey, wrapper.WrappedPhase0BeaconBlock(b), signingRoot); err != nil { log.WithFields( - blockLogFields(pubKey, wrapperv1.WrappedPhase0BeaconBlock(b), nil), + blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), nil), ).WithError(err).Error("Failed block slashing protection check") return } - if err := v.postBlockSignUpdate(ctx, pubKey, wrapperv1.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil { + if err := v.postBlockSignUpdate(ctx, pubKey, wrapper.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil { log.WithFields( - blockLogFields(pubKey, wrapperv1.WrappedPhase0BeaconBlock(b), sig), + blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), sig), ).WithError(err).Error("Failed block slashing protection check") return } @@ -150,6 +162,146 @@ func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [4 } } +// This is a routine to propose altair compatible beacon blocks. +func (v *validator) proposeBlockAltair(ctx context.Context, slot types.Slot, pubKey [48]byte) { + if slot == 0 { + log.Debug("Assigned to genesis slot, skipping proposal") + return + } + ctx, span := trace.StartSpan(ctx, "validator.proposeBlockAltair") + defer span.End() + + lock := mputil.NewMultilock(fmt.Sprint(iface.RoleProposer), string(pubKey[:])) + lock.Lock() + defer lock.Unlock() + + fmtKey := fmt.Sprintf("%#x", pubKey[:]) + span.AddAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey))) + log := log.WithField("pubKey", fmt.Sprintf("%#x", bytesutil.Trunc(pubKey[:]))) + + // Sign randao reveal, it's used to request block from beacon node + epoch := types.Epoch(slot / params.BeaconConfig().SlotsPerEpoch) + randaoReveal, err := v.signRandaoReveal(ctx, pubKey, epoch) + if err != nil { + log.WithError(err).Error("Failed to sign randao reveal") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + + g, err := v.getGraffiti(ctx, pubKey) + if err != nil { + // Graffiti is not a critical enough to fail block production and cause + // validator to miss block reward. When failed, validator should continue + // to produce the block. + log.WithError(err).Warn("Could not get graffiti") + } + + // Request block from beacon node + b, err := v.validatorClient.GetBlockAltair(ctx, ðpb.BlockRequest{ + Slot: slot, + RandaoReveal: randaoReveal, + Graffiti: g, + }) + if err != nil { + log.WithField("blockSlot", slot).WithError(err).Error("Failed to request block from beacon node") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + + // Sign returned block from beacon node + wb, err := wrapper.WrappedAltairBeaconBlock(b) + if err != nil { + log.WithError(err).Error("Failed to wrap block") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + sig, domain, err := v.signBlock(ctx, pubKey, epoch, wb) + if err != nil { + log.WithError(err).Error("Failed to sign block") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + blk := ðpb.SignedBeaconBlockAltair{ + Block: b, + Signature: sig, + } + + signingRoot, err := helpers.ComputeSigningRoot(b, domain.SignatureDomain) + if err != nil { + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + log.WithError(err).Error("Failed to compute signing root for block") + return + } + + if err := v.preBlockSignValidations(ctx, pubKey, wb, signingRoot); err != nil { + log.WithFields( + blockLogFields(pubKey, wb, nil), + ).WithError(err).Error("Failed block slashing protection check") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + + wsb, err := wrapper.WrappedAltairSignedBeaconBlock(blk) + if err != nil { + log.WithError(err).Error("Failed to wrap signed block") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + if err := v.postBlockSignUpdate(ctx, pubKey, wsb, signingRoot); err != nil { + log.WithFields( + blockLogFields(pubKey, wb, sig), + ).WithError(err).Error("Failed block slashing protection check") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + + // Propose and broadcast block via beacon node + blkResp, err := v.validatorClient.ProposeBlockAltair(ctx, blk) + if err != nil { + log.WithError(err).Error("Failed to propose block") + if v.emitAccountMetrics { + ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + + span.AddAttributes( + trace.StringAttribute("blockRoot", fmt.Sprintf("%#x", blkResp.BlockRoot)), + trace.Int64Attribute("numDeposits", int64(len(b.Body.Deposits))), + trace.Int64Attribute("numAttestations", int64(len(b.Body.Attestations))), + ) + + blkRoot := fmt.Sprintf("%#x", bytesutil.Trunc(blkResp.BlockRoot)) + log.WithFields(logrus.Fields{ + "slot": b.Slot, + "blockRoot": blkRoot, + "numAttestations": len(b.Body.Attestations), + "numDeposits": len(b.Body.Deposits), + "graffiti": string(b.Body.Graffiti), + "fork": "altair", + }).Info("Submitted new block") + + if v.emitAccountMetrics { + ValidatorProposeSuccessVec.WithLabelValues(fmtKey).Inc() + } +} + // ProposeExit performs a voluntary exit on a validator. // The exit is signed by the validator before being sent to the beacon node for broadcasting. func ProposeExit( @@ -221,7 +373,7 @@ func (v *validator) signRandaoReveal(ctx context.Context, pubKey [48]byte, epoch } // Sign block with proposer domain and private key. -func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch types.Epoch, b *ethpb.BeaconBlock) ([]byte, *ethpb.DomainResponse, error) { +func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch types.Epoch, b block.BeaconBlock) ([]byte, *ethpb.DomainResponse, error) { domain, err := v.domainData(ctx, epoch, params.BeaconConfig().DomainBeaconProposer[:]) if err != nil { return nil, nil, errors.Wrap(err, domainDataErr) @@ -231,20 +383,48 @@ func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch types. } var sig bls.Signature - blockRoot, err := helpers.ComputeSigningRoot(b, domain.SignatureDomain) - if err != nil { - return nil, nil, errors.Wrap(err, signingRootErr) + switch b.Version() { + case version.Altair: + block, ok := b.Proto().(*ethpb.BeaconBlockAltair) + if !ok { + return nil, nil, errors.New("could not convert obj to beacon block altair") + } + blockRoot, err := helpers.ComputeSigningRoot(block, domain.SignatureDomain) + if err != nil { + return nil, nil, errors.Wrap(err, signingRootErr) + } + sig, err = v.keyManager.Sign(ctx, &validatorpb.SignRequest{ + PublicKey: pubKey[:], + SigningRoot: blockRoot[:], + SignatureDomain: domain.SignatureDomain, + Object: &validatorpb.SignRequest_BlockV2{BlockV2: block}, + }) + if err != nil { + return nil, nil, errors.Wrap(err, "could not sign block proposal") + } + return sig.Marshal(), domain, nil + case version.Phase0: + block, ok := b.Proto().(*ethpb.BeaconBlock) + if !ok { + return nil, nil, errors.New("could not convert obj to beacon block phase 0") + } + blockRoot, err := helpers.ComputeSigningRoot(block, domain.SignatureDomain) + if err != nil { + return nil, nil, errors.Wrap(err, signingRootErr) + } + sig, err = v.keyManager.Sign(ctx, &validatorpb.SignRequest{ + PublicKey: pubKey[:], + SigningRoot: blockRoot[:], + SignatureDomain: domain.SignatureDomain, + Object: &validatorpb.SignRequest_Block{Block: block}, + }) + if err != nil { + return nil, nil, errors.Wrap(err, "could not sign block proposal") + } + return sig.Marshal(), domain, nil + default: + return nil, nil, errors.New("unknown block type") } - sig, err = v.keyManager.Sign(ctx, &validatorpb.SignRequest{ - PublicKey: pubKey[:], - SigningRoot: blockRoot[:], - SignatureDomain: domain.SignatureDomain, - Object: &validatorpb.SignRequest_Block{Block: b}, - }) - if err != nil { - return nil, nil, errors.Wrap(err, "could not sign block proposal") - } - return sig.Marshal(), domain, nil } // Sign voluntary exit with proposer domain and private key. diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 0156f66820..8c39dd2c2d 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -12,6 +12,7 @@ import ( types "github.com/prysmaticlabs/eth2-types" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/bytesutil" lruwrpr "github.com/prysmaticlabs/prysm/shared/lru" @@ -156,6 +157,31 @@ func TestProposeBlock_RequestBlockFailed(t *testing.T) { require.LogsContain(t, hook, "Failed to request block from beacon node") } +func TestProposeBlockAltair_RequestBlockFailed(t *testing.T) { + hook := logTest.NewGlobal() + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 2 + params.OverrideBeaconConfig(cfg) + validator, m, validatorKey, finish := setup(t) + defer finish() + pubKey := [48]byte{} + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT().GetBlockAltair( + gomock.Any(), // ctx + gomock.Any(), // block request + ).Return(nil /*response*/, errors.New("uh oh")) + + validator.ProposeBlock(context.Background(), 2*params.BeaconConfig().SlotsPerEpoch, pubKey) + require.LogsContain(t, hook, "Failed to request block from beacon node") +} + func TestProposeBlock_ProposeBlockFailed(t *testing.T) { hook := logTest.NewGlobal() validator, m, validatorKey, finish := setup(t) @@ -187,6 +213,41 @@ func TestProposeBlock_ProposeBlockFailed(t *testing.T) { require.LogsContain(t, hook, "Failed to propose block") } +func TestProposeBlockAltair_ProposeBlockFailed(t *testing.T) { + hook := logTest.NewGlobal() + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 2 + params.OverrideBeaconConfig(cfg) + validator, m, validatorKey, finish := setup(t) + defer finish() + pubKey := [48]byte{} + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT().GetBlockAltair( + gomock.Any(), // ctx + gomock.Any(), + ).Return(testutil.NewBeaconBlockAltair().Block, nil /*err*/) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT().ProposeBlockAltair( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlockAltair{}), + ).Return(nil /*response*/, errors.New("uh oh")) + + validator.ProposeBlock(context.Background(), 2*params.BeaconConfig().SlotsPerEpoch, pubKey) + require.LogsContain(t, hook, "Failed to propose block") +} + func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { hook := logTest.NewGlobal() validator, m, validatorKey, finish := setup(t) @@ -239,6 +300,62 @@ func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { require.LogsContain(t, hook, failedPreBlockSignLocalErr) } +func TestProposeBlockAltair_BlocksDoubleProposal(t *testing.T) { + hook := logTest.NewGlobal() + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 2 + params.OverrideBeaconConfig(cfg) + validator, m, validatorKey, finish := setup(t) + defer finish() + pubKey := [48]byte{} + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + + dummyRoot := [32]byte{} + // Save a dummy proposal history at slot 0. + err := validator.db.SaveProposalHistoryForSlot(context.Background(), pubKey, 0, dummyRoot[:]) + require.NoError(t, err) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + testBlock := testutil.NewBeaconBlockAltair() + slot := params.BeaconConfig().SlotsPerEpoch*5 + 2 + testBlock.Block.Slot = slot + m.validatorClient.EXPECT().GetBlockAltair( + gomock.Any(), // ctx + gomock.Any(), + ).Return(testBlock.Block, nil /*err*/) + + secondTestBlock := testutil.NewBeaconBlockAltair() + secondTestBlock.Block.Slot = slot + graffiti := [32]byte{} + copy(graffiti[:], "someothergraffiti") + secondTestBlock.Block.Body.Graffiti = graffiti[:] + m.validatorClient.EXPECT().GetBlockAltair( + gomock.Any(), // ctx + gomock.Any(), + ).Return(secondTestBlock.Block, nil /*err*/) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Times(3).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT().ProposeBlockAltair( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlockAltair{}), + ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) + + validator.ProposeBlock(context.Background(), slot, pubKey) + require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + + validator.ProposeBlock(context.Background(), slot, pubKey) + require.LogsContain(t, hook, failedPreBlockSignLocalErr) +} + func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { hook := logTest.NewGlobal() validator, m, validatorKey, finish := setup(t) @@ -455,6 +572,49 @@ func TestProposeBlock_BroadcastsBlock_WithGraffiti(t *testing.T) { assert.Equal(t, string(validator.graffiti), string(sentBlock.Block.Body.Graffiti)) } +func TestProposeBlockAltair_BroadcastsBlock_WithGraffiti(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 2 + params.OverrideBeaconConfig(cfg) + validator, m, validatorKey, finish := setup(t) + defer finish() + pubKey := [48]byte{} + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + + validator.graffiti = []byte("12345678901234567890123456789012") + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + blk := testutil.NewBeaconBlockAltair() + blk.Block.Body.Graffiti = validator.graffiti + m.validatorClient.EXPECT().GetBlockAltair( + gomock.Any(), // ctx + gomock.Any(), + ).Return(blk.Block, nil /*err*/) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + var sentBlock *ethpb.SignedBeaconBlockAltair + + m.validatorClient.EXPECT().ProposeBlockAltair( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlockAltair{}), + ).DoAndReturn(func(ctx context.Context, block *ethpb.SignedBeaconBlockAltair, opts ...grpc.CallOption) (*ethpb.ProposeResponse, error) { + sentBlock = block + return ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil + }) + + validator.ProposeBlock(context.Background(), 2*params.BeaconConfig().SlotsPerEpoch, pubKey) + assert.Equal(t, string(validator.graffiti), string(sentBlock.Block.Body.Graffiti)) +} + func TestProposeExit_ValidatorIndexFailed(t *testing.T) { _, m, validatorKey, finish := setup(t) defer finish() @@ -661,7 +821,7 @@ func TestSignBlock(t *testing.T) { }, } validator.keyManager = km - sig, domain, err := validator.signBlock(ctx, pubKey, 0, blk.Block) + sig, domain, err := validator.signBlock(ctx, pubKey, 0, wrapper.WrappedPhase0BeaconBlock(blk.Block)) require.NoError(t, err, "%x,%x,%v", sig, domain.SignatureDomain, err) require.Equal(t, "a049e1dc723e5a8b5bd14f292973572dffd53785ddb337"+ "82f20bf762cbe10ee7b9b4f5ae1ad6ff2089d352403750bed402b94b58469c072536"+ @@ -671,6 +831,36 @@ func TestSignBlock(t *testing.T) { require.DeepEqual(t, proposerDomain, domain.SignatureDomain) } +func TestSignAltairBlock(t *testing.T) { + validator, m, _, finish := setup(t) + defer finish() + + secretKey, err := bls.SecretKeyFromBytes(bytesutil.PadTo([]byte{1}, 32)) + require.NoError(t, err, "Failed to generate key from bytes") + publicKey := secretKey.PublicKey() + proposerDomain := make([]byte, 32) + m.validatorClient.EXPECT(). + DomainData(gomock.Any(), gomock.Any()). + Return(ðpb.DomainResponse{SignatureDomain: proposerDomain}, nil) + ctx := context.Background() + blk := testutil.NewBeaconBlockAltair() + blk.Block.Slot = 1 + blk.Block.ProposerIndex = 100 + var pubKey [48]byte + copy(pubKey[:], publicKey.Marshal()) + km := &mockKeymanager{ + keysMap: map[[48]byte]bls.SecretKey{ + pubKey: secretKey, + }, + } + validator.keyManager = km + wb, err := wrapper.WrappedAltairBeaconBlock(blk.Block) + require.NoError(t, err) + sig, domain, err := validator.signBlock(ctx, pubKey, 0, wb) + require.NoError(t, err, "%x,%x,%v", sig, domain.SignatureDomain, err) + require.DeepEqual(t, proposerDomain, domain.SignatureDomain) +} + func TestGetGraffiti_Ok(t *testing.T) { ctrl := gomock.NewController(t) m := &mocks{