Skip to content

Commit

Permalink
Better block attestation inclusion (#4838)
Browse files Browse the repository at this point in the history
* Fill blocks with unaggregated atts when possible, don't delete from pool until the block is submitted

* Add back invalid atts removal

* Don't delete attestations unless they can be aggregated

* comment and test

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
  • Loading branch information
prestonvanloon and prylabs-bulldozer[bot] committed Feb 12, 2020
1 parent c7698cd commit 090d962
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 15 deletions.
10 changes: 7 additions & 3 deletions beacon-chain/operations/attestations/aggregate.go
Expand Up @@ -46,10 +46,14 @@ func (s *Service) aggregateAttestations(ctx context.Context, attsToBeAggregated
return err
}
attsByRoot[attDataRoot] = append(attsByRoot[attDataRoot], att)
}

if !helpers.IsAggregated(att) {
if err := s.pool.DeleteUnaggregatedAttestation(att); err != nil {
return err
for _, atts := range attsByRoot {
for _, att := range atts {
if !helpers.IsAggregated(att) && len(atts) > 1 {
if err := s.pool.DeleteUnaggregatedAttestation(att); err != nil {
return err
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions beacon-chain/rpc/validator/proposer.go
Expand Up @@ -60,6 +60,16 @@ func (vs *Server) GetBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb
return nil, status.Errorf(codes.Internal, "Could not filter attestations: %v", err)
}

// If there is any room left in the block, consider unaggregated attestations as well.
if len(atts) < int(params.BeaconConfig().MaxAttestations) {
uAtts := vs.AttPool.UnaggregatedAttestations()
uAtts, err = vs.filterAttestationsForBlockInclusion(ctx, req.Slot, uAtts)
if len(uAtts)+len(atts) > int(params.BeaconConfig().MaxAttestations) {
uAtts = uAtts[:int(params.BeaconConfig().MaxAttestations)-len(atts)]
}
atts = append(atts, uAtts...)
}

// Use zero hash as stub for state root to compute later.
stateRoot := params.BeaconConfig().ZeroHash[:]

Expand Down
122 changes: 122 additions & 0 deletions beacon-chain/rpc/validator/proposer_test.go
Expand Up @@ -109,6 +109,128 @@ func TestGetBlock_OK(t *testing.T) {
}
}

func TestGetBlock_AddsUnaggregatedAtts(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
ctx := context.Background()

beaconState, privKeys := testutil.DeterministicGenesisState(t, params.BeaconConfig().MinGenesisActiveValidatorCount)

stateRoot, err := beaconState.HashTreeRoot()
if err != nil {
t.Fatalf("Could not hash genesis state: %v", err)
}

genesis := b.NewGenesisBlock(stateRoot[:])
if err := db.SaveBlock(ctx, genesis); err != nil {
t.Fatalf("Could not save genesis block: %v", err)
}

parentRoot, err := ssz.HashTreeRoot(genesis.Block)
if err != nil {
t.Fatalf("Could not get signing root %v", err)
}
if err := db.SaveState(ctx, beaconState, parentRoot); err != nil {
t.Fatalf("Could not save genesis state: %v", err)
}
if err := db.SaveHeadBlockRoot(ctx, parentRoot); err != nil {
t.Fatalf("Could not save genesis state: %v", err)
}

proposerServer := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{State: beaconState, Root: parentRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
BlockReceiver: &mock.ChainService{},
ChainStartFetcher: &mockPOW.POWChain{},
Eth1InfoFetcher: &mockPOW.POWChain{},
Eth1BlockFetcher: &mockPOW.POWChain{},
MockEth1Votes: true,
AttPool: attestations.NewPool(),
ExitPool: voluntaryexits.NewPool(),
}


// Generate a bunch of random attestations at slot. These would be considered double votes, but
// we don't care for the purpose of this test.
var atts []*ethpb.Attestation
for i := uint64(0); len(atts) < int(params.BeaconConfig().MaxAttestations); i++ {
a, err := testutil.GenerateAttestations(beaconState, privKeys, 2, 1, true)
if err != nil {
t.Fatal(err)
}
atts = append(atts, a...)
}
// Max attestations minus one so we can almost fill the block and then include 1 unaggregated
// att to maximize inclusion.
atts = atts[:params.BeaconConfig().MaxAttestations-1]
if err := proposerServer.AttPool.SaveAggregatedAttestations(atts); err != nil {
t.Fatal(err)
}

// Generate some more random attestations with a larger spread so that we can capture at least
// one unaggregated attestation.
if atts, err := testutil.GenerateAttestations(beaconState, privKeys, 8, 1, true); err != nil {
t.Fatal(err)
} else {
found := false
for _, a := range atts {
if !helpers.IsAggregated(a) {
found = true
if err := proposerServer.AttPool.SaveUnaggregatedAttestation(a); err != nil {
t.Fatal(err)
}
}
}
if !found {
t.Fatal("No unaggregated attestations were generated")
}
}

randaoReveal, err := testutil.RandaoReveal(beaconState, 0, privKeys)
if err != nil {
t.Error(err)
}

graffiti := bytesutil.ToBytes32([]byte("eth2"))
req := &ethpb.BlockRequest{
Slot: 1,
RandaoReveal: randaoReveal,
Graffiti: graffiti[:],
}

block, err := proposerServer.GetBlock(ctx, req)
if err != nil {
t.Fatal(err)
}

if block.Slot != req.Slot {
t.Fatal("Expected block to have slot of 1")
}
if !bytes.Equal(block.ParentRoot, parentRoot[:]) {
t.Fatal("Expected block to have correct parent root")
}
if !bytes.Equal(block.Body.RandaoReveal, randaoReveal) {
t.Fatal("Expected block to have correct randao reveal")
}
if !bytes.Equal(block.Body.Graffiti, req.Graffiti) {
t.Fatal("Expected block to have correct graffiti")
}
if len(block.Body.Attestations) != int(params.BeaconConfig().MaxAttestations) {
t.Fatalf("Expected a full block of attestations, only received %d", len(block.Body.Attestations))
}
hasUnaggregatedAtt := false
for _, a := range block.Body.Attestations {
if !helpers.IsAggregated(a) {
hasUnaggregatedAtt = true
break
}
}
if !hasUnaggregatedAtt {
t.Fatal("Expected block to contain at least one unaggregated attestation")
}
}

func TestProposeBlock_OK(t *testing.T) {
db := dbutil.SetupDB(t)
defer dbutil.TeardownDB(t, db)
Expand Down
28 changes: 17 additions & 11 deletions shared/testutil/block.go
Expand Up @@ -81,7 +81,7 @@ func GenerateFullBlock(
numToGen = conf.NumAttestations
atts := []*ethpb.Attestation{}
if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot)
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen)
}
Expand Down Expand Up @@ -291,19 +291,11 @@ func generateAttesterSlashings(
// for the same data with their aggregation bits split uniformly.
//
// If you request 4 attestations, but there are 8 committees, you will get 4 fully aggregated attestations.
func GenerateAttestations(
bState *stateTrie.BeaconState,
privs []*bls.SecretKey,
numToGen uint64,
slot uint64,
) ([]*ethpb.Attestation, error) {
func GenerateAttestations(bState *stateTrie.BeaconState, privs []*bls.SecretKey, numToGen uint64, slot uint64, randomRoot bool, ) ([]*ethpb.Attestation, error) {
currentEpoch := helpers.SlotToEpoch(slot)
attestations := []*ethpb.Attestation{}
generateHeadState := false
bState, err := stateTrie.InitializeFromProtoUnsafe(bState.CloneInnerState())
if err != nil {
return nil, err
}
bState = bState.Copy()
if slot > bState.Slot() {
// Going back a slot here so there's no inclusion delay issues.
slot--
Expand All @@ -312,6 +304,7 @@ func GenerateAttestations(

targetRoot := make([]byte, 32)
headRoot := make([]byte, 32)
var err error
// Only calculate head state if its an attestation for the current slot or future slot.
if generateHeadState || slot == bState.Slot() {
headState, err := stateTrie.InitializeFromProtoUnsafe(bState.CloneInnerState())
Expand All @@ -336,6 +329,14 @@ func GenerateAttestations(
return nil, err
}
}
if randomRoot {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
return nil, err
}
headRoot = b
}

activeValidatorCount, err := helpers.ActiveValidatorCount(bState, currentEpoch)
if err != nil {
Expand Down Expand Up @@ -401,6 +402,11 @@ func GenerateAttestations(
sigs = append(sigs, privs[committee[b]].Sign(dataRoot[:], domain))
}

// bls.AggregateSignatures will return nil if sigs is 0.
if len(sigs) == 0 {
continue
}

att := &ethpb.Attestation{
Data: attData,
AggregationBits: aggregationBits,
Expand Down
2 changes: 1 addition & 1 deletion tools/benchmark-files-gen/main.go
Expand Up @@ -115,7 +115,7 @@ func generateMarshalledFullStateAndBlock() error {

atts := []*ethpb.Attestation{}
for i := slotOffset + 1; i < slotsPerEpoch+slotOffset; i++ {
attsForSlot, err := testutil.GenerateAttestations(beaconState, privs, attConfig.NumAttestations, i)
attsForSlot, err := testutil.GenerateAttestations(beaconState, privs, attConfig.NumAttestations, i, false)
if err != nil {
return err
}
Expand Down

0 comments on commit 090d962

Please sign in to comment.