mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-24 12:27:18 +00:00
9ea3f58a5e
* first cherry pick * second * new endpoint * fork digest by epoch * fork and sig verify * fix test * fix comment * gaz * remove unused param * remove space * moved to shared attestation_utils.go * raul comment * gaz * goimports * fix import cycle * fix test * shorter helper method * add another helper function * fix comment * param fix * gaz * fix error * Update beacon-chain/core/blocks/block_operations.go Co-authored-by: Victor Farazdagi <simple.square@gmail.com> * update pseudocode * remove teardown Co-authored-by: Raul Jordan <raul@prysmaticlabs.com> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: Victor Farazdagi <simple.square@gmail.com>
170 lines
6.6 KiB
Go
170 lines
6.6 KiB
Go
// Package attestationutil contains useful helpers for converting
|
|
// attestations into indexed form.
|
|
package attestationutil
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"github.com/pkg/errors"
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
// ConvertToIndexed converts attestation to (almost) indexed-verifiable form.
|
|
//
|
|
// Note about spec pseudocode definition. The state was used by get_attesting_indices to determine
|
|
// the attestation committee. Now that we provide this as an argument, we no longer need to provide
|
|
// a state.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation:
|
|
// """
|
|
// Return the indexed attestation corresponding to ``attestation``.
|
|
// """
|
|
// attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
|
|
//
|
|
// return IndexedAttestation(
|
|
// attesting_indices=sorted(attesting_indices),
|
|
// data=attestation.data,
|
|
// signature=attestation.signature,
|
|
// )
|
|
func ConvertToIndexed(ctx context.Context, attestation *ethpb.Attestation, committee []uint64) *ethpb.IndexedAttestation {
|
|
ctx, span := trace.StartSpan(ctx, "attestationutil.ConvertToIndexed")
|
|
defer span.End()
|
|
|
|
attIndices := AttestingIndices(attestation.AggregationBits, committee)
|
|
|
|
sort.Slice(attIndices, func(i, j int) bool {
|
|
return attIndices[i] < attIndices[j]
|
|
})
|
|
inAtt := ðpb.IndexedAttestation{
|
|
Data: attestation.Data,
|
|
Signature: attestation.Signature,
|
|
AttestingIndices: attIndices,
|
|
}
|
|
return inAtt
|
|
}
|
|
|
|
// AttestingIndices returns the attesting participants indices from the attestation data. The
|
|
// committee is provided as an argument rather than a direct implementation from the spec definition.
|
|
// Having the committee as an argument allows for re-use of beacon committees when possible.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def get_attesting_indices(state: BeaconState,
|
|
// data: AttestationData,
|
|
// bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]:
|
|
// """
|
|
// Return the set of attesting indices corresponding to ``data`` and ``bits``.
|
|
// """
|
|
// committee = get_beacon_committee(state, data.slot, data.index)
|
|
// return set(index for i, index in enumerate(committee) if bits[i])
|
|
func AttestingIndices(bf bitfield.Bitfield, committee []uint64) []uint64 {
|
|
indices := make([]uint64, 0, len(committee))
|
|
for _, idx := range bf.BitIndices() {
|
|
if idx < len(committee) {
|
|
indices = append(indices, committee[idx])
|
|
}
|
|
}
|
|
return indices
|
|
}
|
|
|
|
// VerifyIndexedAttestationSig this helper function performs the last part of the
|
|
// spec indexed attestation validation starting at Verify aggregate signature
|
|
// comment.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
|
// """
|
|
// Check if ``indexed_attestation`` has sorted and unique indices and a valid aggregate signature.
|
|
// """
|
|
// # Verify indices are sorted and unique
|
|
// indices = indexed_attestation.attesting_indices
|
|
// if not indices == sorted(set(indices)):
|
|
// return False
|
|
// # Verify aggregate signature
|
|
// pubkeys = [state.validators[i].pubkey for i in indices]
|
|
// domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
|
// signing_root = compute_signing_root(indexed_attestation.data, domain)
|
|
// return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
|
|
func VerifyIndexedAttestationSig(ctx context.Context, indexedAtt *ethpb.IndexedAttestation, pubKeys []*bls.PublicKey, domain []byte) error {
|
|
ctx, span := trace.StartSpan(ctx, "attestationutil.VerifyIndexedAttestationSig")
|
|
defer span.End()
|
|
indices := indexedAtt.AttestingIndices
|
|
messageHash, err := helpers.ComputeSigningRoot(indexedAtt.Data, domain)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not get signing root of object")
|
|
}
|
|
|
|
sig, err := bls.SignatureFromBytes(indexedAtt.Signature)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert bytes to signature")
|
|
}
|
|
|
|
voted := len(indices) > 0
|
|
if voted && !sig.FastAggregateVerify(pubKeys, messageHash) {
|
|
return helpers.ErrSigFailedToVerify
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsValidAttestationIndices this helper function performs the first part of the
|
|
// spec indexed attestation validation starting at Check if ``indexed_attestation``
|
|
// comment and ends at Verify aggregate signature comment.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
|
// """
|
|
// Check if ``indexed_attestation`` has valid indices and signature.
|
|
// """
|
|
// indices = indexed_attestation.attesting_indices
|
|
//
|
|
// # Verify max number of indices
|
|
// if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
|
|
// return False
|
|
// # Verify indices are sorted and unique
|
|
// if not indices == sorted(set(indices)):
|
|
// # Verify aggregate signature
|
|
// 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_BEACON_ATTESTER, indexed_attestation.data.target.epoch),
|
|
// ):
|
|
// return False
|
|
// return True
|
|
func IsValidAttestationIndices(ctx context.Context, indexedAttestation *ethpb.IndexedAttestation) error {
|
|
ctx, span := trace.StartSpan(ctx, "attestationutil.IsValidAttestationIndices")
|
|
defer span.End()
|
|
|
|
if indexedAttestation == nil || indexedAttestation.Data == nil || indexedAttestation.Data.Target == nil {
|
|
return errors.New("nil or missing indexed attestation data")
|
|
}
|
|
indices := indexedAttestation.AttestingIndices
|
|
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)
|
|
}
|
|
set := make(map[uint64]bool)
|
|
setIndices := make([]uint64, 0, len(indices))
|
|
for _, i := range indices {
|
|
if ok := set[i]; ok {
|
|
continue
|
|
}
|
|
setIndices = append(setIndices, i)
|
|
set[i] = true
|
|
}
|
|
sort.SliceStable(setIndices, func(i, j int) bool {
|
|
return setIndices[i] < setIndices[j]
|
|
})
|
|
if !reflect.DeepEqual(setIndices, indices) {
|
|
return errors.New("attesting indices is not uniquely sorted")
|
|
}
|
|
return nil
|
|
}
|