Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove custody #3986

Merged
merged 13 commits into from
Nov 12, 2019
4 changes: 2 additions & 2 deletions beacon-chain/blockchain/forkchoice/process_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import (
// assert is_valid_indexed_attestation(target_state, indexed_attestation)
//
// # Update latest messages
// for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices:
// for i in indexed_attestation.attesting_indices:
// if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
// store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root)
func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error) {
Expand Down Expand Up @@ -236,7 +236,7 @@ func (s *Store) updateAttVotes(
tgtRoot []byte,
tgtEpoch uint64) error {

indices := append(indexedAtt.CustodyBit_0Indices, indexedAtt.CustodyBit_1Indices...)
indices := indexedAtt.AttestingIndices
newVoteIndices := make([]uint64, 0, len(indices))
newVotes := make([]*pb.ValidatorLatestVote, 0, len(indices))
for _, i := range indices {
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/blockchain/forkchoice/process_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func (s *Store) updateBlockAttestationVote(ctx context.Context, att *ethpb.Attes
if err != nil {
return errors.Wrap(err, "could not convert attestation to indexed attestation")
}
for _, i := range append(indexedAtt.CustodyBit_0Indices, indexedAtt.CustodyBit_1Indices...) {
for _, i := range indexedAtt.AttestingIndices {
vote, err := s.db.ValidatorLatestVote(ctx, i)
if err != nil {
return errors.Wrapf(err, "could not get latest vote for validator %d", i)
Expand Down
19 changes: 6 additions & 13 deletions beacon-chain/blockchain/forkchoice/process_block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,26 +146,20 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) {
Target: &ethpb.Checkpoint{Epoch: 0, Root: r[:]},
},
AggregationBits: []byte{255},
CustodyBits: []byte{255},
}
if err := store.db.SaveState(ctx, beaconState, r); err != nil {
t.Fatal(err)
}

indices, err := blocks.ConvertToIndexed(ctx, beaconState, att)
indexedAtt, err := blocks.ConvertToIndexed(ctx, beaconState, att)
if err != nil {
t.Fatal(err)
}

var attestedIndices []uint64
for _, k := range append(indices.CustodyBit_0Indices, indices.CustodyBit_1Indices...) {
attestedIndices = append(attestedIndices, k)
}

if err := store.updateBlockAttestationVote(ctx, att); err != nil {
t.Fatal(err)
}
for _, i := range attestedIndices {
for _, i := range indexedAtt.AttestingIndices {
v, err := store.db.ValidatorLatestVote(ctx, i)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -199,7 +193,6 @@ func TestStore_UpdateBlockAttestationsVote(t *testing.T) {
Target: &ethpb.Checkpoint{Epoch: 0, Root: r[:]},
},
AggregationBits: []byte{255},
CustodyBits: []byte{255},
}
h, _ := hashutil.HashProto(atts[i])
hashes[i] = h
Expand All @@ -226,8 +219,8 @@ func TestStore_SavesNewBlockAttestations(t *testing.T) {
defer testDB.TeardownDB(t, db)

store := NewForkChoiceService(ctx, db)
a1 := &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}, CustodyBits: bitfield.NewBitlist(2)}
a2 := &ethpb.Attestation{Data: &ethpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b110}, CustodyBits: bitfield.NewBitlist(2)}
a1 := &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}}
a2 := &ethpb.Attestation{Data: &ethpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b110}}
r1, _ := ssz.HashTreeRoot(a1.Data)
r2, _ := ssz.HashTreeRoot(a2.Data)

Expand All @@ -251,8 +244,8 @@ func TestStore_SavesNewBlockAttestations(t *testing.T) {
t.Error("did not retrieve saved attestation")
}

a1 = &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b111}, CustodyBits: bitfield.NewBitlist(2)}
a2 = &ethpb.Attestation{Data: &ethpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b111}, CustodyBits: bitfield.NewBitlist(2)}
a1 = &ethpb.Attestation{Data: &ethpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b111}}
a2 = &ethpb.Attestation{Data: &ethpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b111}}

if err := store.saveNewBlockAttestations(ctx, []*ethpb.Attestation{a1, a2}); err != nil {
t.Fatal(err)
Expand Down
166 changes: 32 additions & 134 deletions beacon-chain/core/blocks/block_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,19 +384,15 @@ func VerifyProposerSlashing(
//
// Spec pseudocode definition:
// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
// """
// Process ``AttesterSlashing`` operation.
// """
// attestation_1 = attester_slashing.attestation_1
// attestation_2 = attester_slashing.attestation_2
// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
// validate_indexed_attestation(state, attestation_1)
// validate_indexed_attestation(state, attestation_2)
// assert is_valid_indexed_attestation(state, attestation_1)
// assert is_valid_indexed_attestation(state, attestation_2)
//
// slashed_any = False
// attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices
// attesting_indices_2 = attestation_2.custody_bit_0_indices + attestation_2.custody_bit_1_indices
// for index in sorted(set(attesting_indices_1).intersection(attesting_indices_2)):
// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
// for index in sorted(indices):
// if is_slashable_validator(state.validators[index], get_current_epoch(state)):
// slash_validator(state, index)
// slashed_any = True
Expand Down Expand Up @@ -468,10 +464,8 @@ func IsSlashableAttestationData(data1 *ethpb.AttestationData, data2 *ethpb.Attes
}

func slashableAttesterIndices(slashing *ethpb.AttesterSlashing) []uint64 {
att1 := slashing.Attestation_1
att2 := slashing.Attestation_1
indices1 := append(att1.CustodyBit_0Indices, att1.CustodyBit_1Indices...)
indices2 := append(att2.CustodyBit_0Indices, att2.CustodyBit_1Indices...)
indices1 := slashing.Attestation_1.AttestingIndices
indices2 := slashing.Attestation_1.AttestingIndices
return sliceutil.IntersectionUint64(indices1, indices2)
}

Expand Down Expand Up @@ -512,7 +506,7 @@ func ProcessAttestationsNoVerify(ctx context.Context, beaconState *pb.BeaconStat
// assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
//
// committee = get_beacon_committee(state, data.slot, data.index)
// assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee)
// assert len(attestation.aggregation_bits) == len(committee)
//
// pending_attestation = PendingAttestation(
// data=data,
Expand Down Expand Up @@ -624,13 +618,9 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
// Return the indexed attestation corresponding to ``attestation``.
// """
// attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
// custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bits)
// assert custody_bit_1_indices.issubset(attesting_indices)
// custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices)
//
// return IndexedAttestation(
// custody_bit_0_indices=sorted(custody_bit_0_indices),
// custody_bit_1_indices=sorted(custody_bit_1_indices),
// attesting_indices=sorted(attesting_indices),
// data=attestation.data,
// signature=attestation.signature,
// )
Expand All @@ -643,35 +633,13 @@ func ConvertToIndexed(ctx context.Context, state *pb.BeaconState, attestation *e
return nil, errors.Wrap(err, "could not get attesting indices")
}

cb1i, err := helpers.AttestingIndices(state, attestation.Data, attestation.CustodyBits)
if err != nil {
return nil, err
}
if !sliceutil.SubsetUint64(cb1i, attIndices) {
return nil, fmt.Errorf("%v is not a subset of %v", cb1i, attIndices)
}
cb1Map := make(map[uint64]bool)
for _, idx := range cb1i {
cb1Map[idx] = true
}
cb0i := []uint64{}
for _, idx := range attIndices {
if !cb1Map[idx] {
cb0i = append(cb0i, idx)
}
}
sort.Slice(cb0i, func(i, j int) bool {
return cb0i[i] < cb0i[j]
})

sort.Slice(cb1i, func(i, j int) bool {
return cb1i[i] < cb1i[j]
sort.Slice(attIndices, func(i, j int) bool {
return attIndices[i] < attIndices[j]
})
inAtt := &ethpb.IndexedAttestation{
Data: attestation.Data,
Signature: attestation.Signature,
CustodyBit_0Indices: cb0i,
CustodyBit_1Indices: cb1i,
Data: attestation.Data,
Signature: attestation.Signature,
AttestingIndices: attIndices,
}
return inAtt, nil
}
Expand All @@ -683,131 +651,61 @@ func ConvertToIndexed(ctx context.Context, state *pb.BeaconState, attestation *e
// """
// Check if ``indexed_attestation`` has valid indices and signature.
// """
// bit_0_indices = indexed_attestation.custody_bit_0_indices
// bit_1_indices = indexed_attestation.custody_bit_1_indices
// indices = indexed_attestation.attesting_indices
//
// # Verify no index has custody bit equal to 1 [to be removed in phase 1]
// if not len(bit_1_indices) == 0:
// return False
// # Verify max number of indices
// if not len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE:
// return False
// # Verify index sets are disjoint
// if not len(set(bit_0_indices).intersection(bit_1_indices)) == 0:
// if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
// return False
// # Verify indices are sorted
// if not (bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)):
// if not indices == sorted(indices):
// return False
// # Verify aggregate signature
// if not bls_verify_multiple(
// pubkeys=[
// bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]),
// bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]),
// ],
// message_hashes=[
// hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
// hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
// ],
// if not bls_verify(
// pubkey=bls_aggregate_pubkeys([state.validators[i].pubkey for i in indices]),
// message_hash=hash_tree_root(indexed_attestation.data),
// signature=indexed_attestation.signature,
// domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch),
// domain=get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch),
// ):
// return False
// return True
func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState, indexedAtt *ethpb.IndexedAttestation) error {
ctx, span := trace.StartSpan(ctx, "core.VerifyIndexedAttestation")
defer span.End()

custodyBit0Indices := indexedAtt.CustodyBit_0Indices
custodyBit1Indices := indexedAtt.CustodyBit_1Indices
indices := indexedAtt.AttestingIndices

// To be removed in phase 1
if len(custodyBit1Indices) != 0 {
return fmt.Errorf("expected no bit 1 indices, received %v", len(custodyBit1Indices))
}

maxIndices := params.BeaconConfig().MaxValidatorsPerCommittee
totalIndicesLength := uint64(len(custodyBit0Indices) + len(custodyBit1Indices))
if totalIndicesLength > maxIndices {
return fmt.Errorf("over max number of allowed indices per attestation: %d", totalIndicesLength)
}
custodyBitIntersection := sliceutil.IntersectionUint64(custodyBit0Indices, custodyBit1Indices)
if len(custodyBitIntersection) != 0 {
return fmt.Errorf("expected disjoint indices intersection, received %v", custodyBitIntersection)
}

custodyBit0IndicesIsSorted := sort.SliceIsSorted(custodyBit0Indices, func(i, j int) bool {
return custodyBit0Indices[i] < custodyBit0Indices[j]
})

if !custodyBit0IndicesIsSorted {
return fmt.Errorf("custody Bit0 indices are not sorted, got %v", custodyBit0Indices)
}

custodyBit1IndicesIsSorted := sort.SliceIsSorted(custodyBit1Indices, func(i, j int) bool {
return custodyBit1Indices[i] < custodyBit1Indices[j]
})

if !custodyBit1IndicesIsSorted {
return fmt.Errorf("custody Bit1 indices are not sorted, got %v", custodyBit1Indices)
if uint64(len(indices)) > params.BeaconConfig().MaxValidatorsPerCommittee {
return fmt.Errorf("validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE, %d > %d", len(indices), params.BeaconConfig().MaxValidatorsPerCommittee)
}

domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
var pubkeys []*bls.PublicKey
if len(custodyBit0Indices) > 0 {
pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit0Indices[0]].PublicKey)
if err != nil {
return errors.Wrap(err, "could not deserialize validator public key")
}
for _, i := range custodyBit0Indices[1:] {
pk, err := bls.PublicKeyFromBytes(beaconState.Validators[i].PublicKey)
if err != nil {
return errors.Wrap(err, "could not deserialize validator public key")
}
pubkey.Aggregate(pk)
}
pubkeys = append(pubkeys, pubkey)
}
if len(custodyBit1Indices) > 0 {
pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit1Indices[0]].PublicKey)
var pubkey *bls.PublicKey
var err error
if len(indices) > 0 {
pubkey, err = bls.PublicKeyFromBytes(beaconState.Validators[indices[0]].PublicKey)
if err != nil {
return errors.Wrap(err, "could not deserialize validator public key")
}
for _, i := range custodyBit1Indices[1:] {
for _, i := range indices[1:] {
pk, err := bls.PublicKeyFromBytes(beaconState.Validators[i].PublicKey)
if err != nil {
return errors.Wrap(err, "could not deserialize validator public key")
}
pubkey.Aggregate(pk)
}
pubkeys = append(pubkeys, pubkey)
}

var msgs [][32]byte
cus0 := &pb.AttestationDataAndCustodyBit{Data: indexedAtt.Data, CustodyBit: false}
cus1 := &pb.AttestationDataAndCustodyBit{Data: indexedAtt.Data, CustodyBit: true}
if len(custodyBit0Indices) > 0 {
cus0Root, err := ssz.HashTreeRoot(cus0)
if err != nil {
return errors.Wrap(err, "could not tree hash att data and custody bit 0")
}
msgs = append(msgs, cus0Root)
}
if len(custodyBit1Indices) > 0 {
cus1Root, err := ssz.HashTreeRoot(cus1)
if err != nil {
return errors.Wrap(err, "could not tree hash att data and custody bit 1")
}
msgs = append(msgs, cus1Root)
messageHash, err := ssz.HashTreeRoot(indexedAtt.Data)
if err != nil {
return errors.Wrap(err, "could not tree hash att data")
}

sig, err := bls.SignatureFromBytes(indexedAtt.Signature)
if err != nil {
return errors.Wrap(err, "could not convert bytes to signature")
}

hasVotes := len(custodyBit0Indices) > 0 || len(custodyBit1Indices) > 0

if hasVotes && !sig.VerifyAggregate(pubkeys, msgs, domain) {
if !sig.Verify(messageHash[:], pubkey, domain) {
return fmt.Errorf("attestation aggregation signature did not verify")
}
return nil
Expand Down
Loading