Skip to content

Commit

Permalink
Remove custody (#3986)
Browse files Browse the repository at this point in the history
* Update proto fields

* Updated block operations

* Fixed all block operation tests

* Fixed tests part 1

* Fixed tests part 1

* All tests pass

* Clean up

* Skip spec test

* Fixed ssz test

* Skip ssz test

* Skip mainnet tests

* Update beacon-chain/operations/attestation.go

* Update beacon-chain/operations/attestation.go
  • Loading branch information
terencechain authored and rauljordan committed Nov 12, 2019
1 parent 76e9111 commit 63e7173
Show file tree
Hide file tree
Showing 50 changed files with 373 additions and 1,113 deletions.
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

0 comments on commit 63e7173

Please sign in to comment.