2019-02-06 16:20:38 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2019-02-15 16:58:06 +00:00
|
|
|
"time"
|
2019-02-06 16:20:38 +00:00
|
|
|
|
|
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
2019-03-12 20:05:08 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bitutil"
|
2019-02-23 06:06:20 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-02-28 03:55:47 +00:00
|
|
|
"go.opencensus.io/trace"
|
2019-02-06 16:20:38 +00:00
|
|
|
)
|
|
|
|
|
2019-02-18 16:52:16 +00:00
|
|
|
var delay = params.BeaconConfig().SecondsPerSlot / 2
|
2019-02-15 16:58:06 +00:00
|
|
|
|
2019-02-06 16:20:38 +00:00
|
|
|
// AttestToBlockHead completes the validator client's attester responsibility at a given slot.
|
|
|
|
// It fetches the latest beacon block head along with the latest canonical beacon state
|
|
|
|
// information in order to sign the block and include information about the validator's
|
|
|
|
// participation in voting on the block.
|
|
|
|
func (v *validator) AttestToBlockHead(ctx context.Context, slot uint64) {
|
2019-02-28 03:55:47 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "validator.AttestToBlockHead")
|
|
|
|
defer span.End()
|
2019-02-25 02:09:45 +00:00
|
|
|
log.Info("Attesting...")
|
2019-02-06 16:20:38 +00:00
|
|
|
// First the validator should construct attestation_data, an AttestationData
|
|
|
|
// object based upon the state at the assigned slot.
|
|
|
|
attData := &pbp2p.AttestationData{
|
2019-02-27 23:07:39 +00:00
|
|
|
Slot: slot,
|
|
|
|
CrosslinkDataRootHash32: params.BeaconConfig().ZeroHash[:], // Stub for Phase 0.
|
2019-02-06 16:20:38 +00:00
|
|
|
}
|
2019-02-11 16:15:25 +00:00
|
|
|
// We fetch the validator index as it is necessary to generate the aggregation
|
|
|
|
// bitfield of the attestation itself.
|
2019-03-03 22:55:12 +00:00
|
|
|
pubKey := v.key.PublicKey.Marshal()
|
2019-02-11 16:15:25 +00:00
|
|
|
idxReq := &pb.ValidatorIndexRequest{
|
2019-03-03 22:55:12 +00:00
|
|
|
PublicKey: pubKey,
|
2019-02-06 16:20:38 +00:00
|
|
|
}
|
2019-02-11 16:15:25 +00:00
|
|
|
validatorIndexRes, err := v.validatorClient.ValidatorIndex(ctx, idxReq)
|
2019-02-06 16:20:38 +00:00
|
|
|
if err != nil {
|
2019-02-11 16:15:25 +00:00
|
|
|
log.Errorf("Could not fetch validator index: %v", err)
|
2019-02-06 16:20:38 +00:00
|
|
|
return
|
|
|
|
}
|
2019-03-03 22:55:12 +00:00
|
|
|
req := &pb.ValidatorEpochAssignmentsRequest{
|
|
|
|
EpochStart: slot,
|
|
|
|
PublicKey: pubKey,
|
2019-02-11 16:15:25 +00:00
|
|
|
}
|
2019-03-03 22:55:12 +00:00
|
|
|
resp, err := v.validatorClient.CommitteeAssignment(ctx, req)
|
2019-02-11 16:15:25 +00:00
|
|
|
if err != nil {
|
2019-02-25 02:09:45 +00:00
|
|
|
log.Errorf("Could not fetch crosslink committees at slot %d: %v",
|
|
|
|
slot-params.BeaconConfig().GenesisSlot, err)
|
2019-02-06 16:20:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// Set the attestation data's shard as the shard associated with the validator's
|
|
|
|
// committee as retrieved by CrosslinkCommitteesAtSlot.
|
|
|
|
attData.Shard = resp.Shard
|
|
|
|
|
|
|
|
// Fetch other necessary information from the beacon node in order to attest
|
|
|
|
// including the justified epoch, epoch boundary information, and more.
|
2019-02-27 20:21:15 +00:00
|
|
|
infoReq := &pb.AttestationDataRequest{
|
2019-03-02 00:33:55 +00:00
|
|
|
Slot: slot,
|
2019-02-06 16:20:38 +00:00
|
|
|
Shard: resp.Shard,
|
|
|
|
}
|
2019-02-27 20:21:15 +00:00
|
|
|
infoRes, err := v.attesterClient.AttestationDataAtSlot(ctx, infoReq)
|
2019-02-06 16:20:38 +00:00
|
|
|
if err != nil {
|
2019-02-25 02:09:45 +00:00
|
|
|
log.Errorf("Could not fetch necessary info to produce attestation at slot %d: %v",
|
|
|
|
slot-params.BeaconConfig().GenesisSlot, err)
|
2019-02-06 16:20:38 +00:00
|
|
|
return
|
|
|
|
}
|
2019-02-25 02:09:45 +00:00
|
|
|
log.Infof("Attestation info response: %v", infoRes)
|
2019-02-06 16:20:38 +00:00
|
|
|
// Set the attestation data's beacon block root = hash_tree_root(head) where head
|
|
|
|
// is the validator's view of the head block of the beacon chain during the slot.
|
|
|
|
attData.BeaconBlockRootHash32 = infoRes.BeaconBlockRootHash32
|
|
|
|
// Set the attestation data's epoch boundary root = hash_tree_root(epoch_boundary)
|
|
|
|
// where epoch_boundary is the block at the most recent epoch boundary in the
|
2019-02-27 20:21:15 +00:00
|
|
|
// chain defined by head -- i.e. the BeaconBlock where block.slot == get_epoch_start_slot(slot_to_epoch(head.slot)).
|
2019-02-06 16:20:38 +00:00
|
|
|
attData.EpochBoundaryRootHash32 = infoRes.EpochBoundaryRootHash32
|
|
|
|
// Set the attestation data's latest crosslink root = state.latest_crosslinks[shard].shard_block_root
|
|
|
|
// where state is the beacon state at head and shard is the validator's assigned shard.
|
2019-02-10 17:59:17 +00:00
|
|
|
attData.LatestCrosslink = infoRes.LatestCrosslink
|
2019-02-06 16:20:38 +00:00
|
|
|
// Set the attestation data's justified epoch = state.justified_epoch where state
|
|
|
|
// is the beacon state at the head.
|
|
|
|
attData.JustifiedEpoch = infoRes.JustifiedEpoch
|
|
|
|
// Set the attestation data's justified block root = hash_tree_root(justified_block) where
|
|
|
|
// justified_block is the block at state.justified_epoch in the chain defined by head.
|
|
|
|
// On the server side, this is fetched by calling get_block_root(state, justified_epoch).
|
|
|
|
attData.JustifiedBlockRootHash32 = infoRes.JustifiedBlockRootHash32
|
|
|
|
|
|
|
|
// The validator now creates an Attestation object using the AttestationData as
|
|
|
|
// set in the code above after all properties have been set.
|
|
|
|
attestation := &pbp2p.Attestation{
|
|
|
|
Data: attData,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We set the custody bitfield to an slice of zero values as a stub for phase 0
|
|
|
|
// of length len(committee)+7 // 8.
|
|
|
|
attestation.CustodyBitfield = make([]byte, (len(resp.Committee)+7)/8)
|
|
|
|
|
|
|
|
// Note: calling get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
|
|
|
|
// should return a list of length equal to 1, containing validator_index.
|
2019-03-12 20:05:08 +00:00
|
|
|
|
2019-03-14 16:29:44 +00:00
|
|
|
// Find the index in committee to be used for
|
|
|
|
// the aggregation bitfield
|
|
|
|
var indexInCommittee int
|
|
|
|
for i, vIndex := range resp.Committee {
|
|
|
|
if vIndex == validatorIndexRes.Index {
|
|
|
|
indexInCommittee = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aggregationBitfield := bitutil.SetBitfield(indexInCommittee)
|
2019-02-06 16:20:38 +00:00
|
|
|
attestation.AggregationBitfield = aggregationBitfield
|
|
|
|
|
2019-03-11 00:02:47 +00:00
|
|
|
// TODO(#1366): Use BLS to generate an aggregate signature.
|
|
|
|
attestation.AggregateSignature = []byte("signed")
|
2019-02-06 16:20:38 +00:00
|
|
|
|
2019-02-18 16:52:16 +00:00
|
|
|
duration := time.Duration(slot*params.BeaconConfig().SecondsPerSlot+delay) * time.Second
|
2019-02-15 16:58:06 +00:00
|
|
|
timeToBroadcast := time.Unix(int64(v.genesisTime), 0).Add(duration)
|
2019-02-28 03:55:47 +00:00
|
|
|
_, sleepSpan := trace.StartSpan(ctx, "validator.AttestToBlockHead_sleepUntilTimeToBroadcast")
|
2019-02-15 16:58:06 +00:00
|
|
|
time.Sleep(time.Until(timeToBroadcast))
|
2019-02-28 03:55:47 +00:00
|
|
|
sleepSpan.End()
|
2019-02-25 02:09:45 +00:00
|
|
|
log.Infof("Produced attestation: %v", attestation)
|
2019-02-06 16:20:38 +00:00
|
|
|
attestRes, err := v.attesterClient.AttestHead(ctx, attestation)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Could not submit attestation to beacon node: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.WithField(
|
|
|
|
"hash", fmt.Sprintf("%#x", attestRes.AttestationHash),
|
2019-02-15 16:58:06 +00:00
|
|
|
).Infof("Submitted attestation successfully with hash %#x", attestRes.AttestationHash)
|
2019-02-06 16:20:38 +00:00
|
|
|
}
|