Add feature flag to blacklist blocks (#15030)

* Add feature flag to blacklist blocks

* review and add tests

* add test

* review

* Kasey's review

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
Potuz
2025-03-21 19:20:39 -03:00
committed by GitHub
parent bfa24606c3
commit 21e1f7883b
12 changed files with 105 additions and 2 deletions

View File

@@ -32,6 +32,8 @@ var (
ErrNilHead = errors.New("nil head")
// errNotGenesisRoot is returned when the root is not the genesis block root.
errNotGenesisRoot = errors.New("root is not the genesis block root")
// errBlacklistedBlock is returned when a block is blacklisted as invalid.
errBlacklistedRoot = errors.New("block root is blacklisted")
)
var errMaxBlobsExceeded = errors.New("Expected commitments in block exceeds MAX_BLOBS_PER_BLOCK")

View File

@@ -175,6 +175,9 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
var set *bls.SignatureBatch
boundaries := make(map[[32]byte]state.BeaconState)
for i, b := range blks {
if features.BlacklistedBlock(b.Root()) {
return errBlacklistedRoot
}
v, h, err := getStateVersionAndPayload(preState)
if err != nil {
return err

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -64,6 +65,10 @@ type SlashingReceiver interface {
func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, avs das.AvailabilityStore) error {
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlock")
defer span.End()
// Return early if the block is blacklisted
if features.BlacklistedBlock(blockRoot) {
return errBlacklistedRoot
}
// Return early if the block has been synced
if s.InForkchoice(blockRoot) {
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already synced block")

View File

@@ -12,6 +12,7 @@ import (
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
@@ -41,6 +42,16 @@ func TestService_ReceiveBlock(t *testing.T) {
bc.ShardCommitteePeriod = 0 // Required for voluntary exits test in reasonable time.
params.OverrideBeaconConfig(bc)
badBlock := genFullBlock(t, util.DefaultBlockGenConfig(), 101)
badRoot, err := badBlock.Block.HashTreeRoot()
require.NoError(t, err)
badRoots := make(map[[32]byte]struct{})
badRoots[badRoot] = struct{}{}
resetCfg := features.InitWithReset(&features.Flags{
BlacklistedRoots: badRoots,
})
defer resetCfg()
type args struct {
block *ethpb.SignedBeaconBlock
}
@@ -124,8 +135,14 @@ func TestService_ReceiveBlock(t *testing.T) {
}
},
},
{
name: "The block is blacklisted",
args: args{
block: badBlock,
},
wantedErr: errBlacklistedRoot.Error(),
},
}
wg := new(sync.WaitGroup)
for _, tt := range tests {
wg.Add(1)

View File

@@ -32,6 +32,7 @@ go_library(
"//beacon-chain/sync/verify:go_default_library",
"//beacon-chain/verification:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",

View File

@@ -18,6 +18,7 @@ import (
prysmsync "github.com/prysmaticlabs/prysm/v5/beacon-chain/sync"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/sync/verify"
"github.com/prysmaticlabs/prysm/v5/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
@@ -357,6 +358,13 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
log.WithField("peer", p).WithError(err).Debug("invalid BeaconBlocksByRange response")
continue
}
if len(features.Get().BlacklistedRoots) > 0 {
for _, b := range robs {
if features.BlacklistedBlock(b.Block.Root()) {
return nil, p, prysmsync.ErrInvalidFetchedData
}
}
}
return robs, p, err
}
return nil, "", errNoPeersAvailable

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
@@ -409,6 +410,9 @@ func (s *Service) setSeenBlockIndexSlot(slot primitives.Slot, proposerIdx primit
// Returns true if the block is marked as a bad block.
func (s *Service) hasBadBlock(root [32]byte) bool {
if features.BlacklistedBlock(root) {
return true
}
s.badBlockLock.RLock()
defer s.badBlockLock.RUnlock()
_, seen := s.badBlockCache.Get(string(root[:]))

View File

@@ -0,0 +1,3 @@
### Added
- Add a feature flag `--blacklist-roots` to allow the node to specify blocks that will be treated as invalid.

View File

@@ -14,6 +14,7 @@ go_library(
"//cmd:go_default_library",
"//cmd/beacon-chain/sync/backfill/flags:go_default_library",
"//config/params:go_default_library",
"//encoding/bytesutil:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
@@ -30,6 +31,7 @@ go_test(
deps = [
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)

View File

@@ -27,6 +27,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/cmd"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
@@ -88,7 +89,8 @@ type Flags struct {
AggregateIntervals [3]time.Duration
// Feature related flags (alignment forced in the end)
ForceHead string // ForceHead forces the head block to be a specific block root, the last head block, or the last finalized block.
ForceHead string // ForceHead forces the head block to be a specific block root, the last head block, or the last finalized block.
BlacklistedRoots map[[32]byte]struct{} // BlacklistedRoots is a list of roots that are blacklisted from processing.
}
var featureConfig *Flags
@@ -282,11 +284,29 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
cfg.ForceHead = ctx.String(forceHeadFlag.Name)
}
if ctx.IsSet(blacklistRoots.Name) {
logEnabled(blacklistRoots)
cfg.BlacklistedRoots = parseBlacklistedRoots(ctx.StringSlice(blacklistRoots.Name))
}
cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
Init(cfg)
return nil
}
func parseBlacklistedRoots(blacklistedRoots []string) map[[32]byte]struct{} {
roots := make(map[[32]byte]struct{})
for _, root := range blacklistedRoots {
r, err := bytesutil.DecodeHexWithLength(root, 32)
if err != nil {
log.WithError(err).WithField("root", root).Warn("Failed to parse blacklisted root")
continue
}
roots[[32]byte(r)] = struct{}{}
}
return roots
}
// ConfigureValidator sets the global config based
// on what flags are enabled for the validator client.
func ConfigureValidator(ctx *cli.Context) error {
@@ -398,3 +418,10 @@ func ValidateNetworkFlags(ctx *cli.Context) error {
}
return nil
}
// BlacklistedBlock returns weather the given block root belongs to the list of blacklisted roots.
func BlacklistedBlock(r [32]byte) bool {
blacklisted := Get().BlacklistedRoots
_, ok := blacklisted[r]
return ok
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/sirupsen/logrus/hooks/test"
"github.com/urfave/cli/v2"
)
@@ -93,3 +94,26 @@ func TestValidateNetworkFlags(t *testing.T) {
})
}
}
func Test_parseBlacklistedRoots(t *testing.T) {
strings := []string{"0xf98e27558ac9ba27ab7d0d3b97d5742a4a68b5e3d7f33c520eda14c39df6368c",
"0x31604da144e1047b250ee87c5049774870e4a140e8eb0087f38ebc4584c2e7",
"0x4fcf04e7e4075962360a91be0c54c1d3f67237aa2ce07d83b0479971af3f7e78",
}
hook := test.NewGlobal()
resCfg := InitWithReset(&Flags{
BlacklistedRoots: parseBlacklistedRoots(strings),
})
defer resCfg()
expected := [][32]byte{
{0xf9, 0x8e, 0x27, 0x55, 0x8a, 0xc9, 0xba, 0x27, 0xab, 0x7d, 0x0d, 0x3b, 0x97, 0xd5, 0x74, 0x2a, 0x4a, 0x68, 0xb5, 0xe3, 0xd7, 0xf3, 0x3c, 0x52, 0x0e, 0xda, 0x14, 0xc3, 0x9d, 0xf6, 0x36, 0x8c},
{0x4f, 0xcf, 0x04, 0xe7, 0xe4, 0x07, 0x59, 0x62, 0x36, 0x0a, 0x91, 0xbe, 0x0c, 0x54, 0xc1, 0xd3, 0xf6, 0x72, 0x37, 0xaa, 0x2c, 0xe0, 0x7d, 0x83, 0xb0, 0x47, 0x99, 0x71, 0xaf, 0x3f, 0x7e, 0x78},
}
require.LogsContain(t, hook, "Failed to parse blacklisted root")
require.LogsContain(t, hook, strings[1])
require.Equal(t, len(expected), len(Get().BlacklistedRoots))
for _, root := range expected {
require.Equal(t, true, BlacklistedBlock(root))
}
}

View File

@@ -185,6 +185,12 @@ var (
Usage: "Forces the head of the beacon chain to a specific block root. Values can be 'head' or a block root." +
" The block root has to be known to the beacon node and correspond to a block newer than the current finalized checkpoint.",
}
// blacklistRoots is a flag for blacklisting block roots from gossip and
// downscore peers that send them.
blacklistRoots = &cli.StringSliceFlag{
Name: "blacklist-roots",
Usage: "A comma-separatted list of 0x-prefixed hexstrings. Declares blocks with the given blockroots to be invalid. It downscores peers that send these blocks.",
}
)
// devModeFlags holds list of flags that are set when development mode is on.
@@ -244,6 +250,7 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
EnableDiscoveryReboot,
enableExperimentalAttestationPool,
forceHeadFlag,
blacklistRoots,
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {