Example #1
0
        public async Task OnTickAsync(BeaconChainInformation beaconChainInformation, ulong time,
                                      CancellationToken cancellationToken)
        {
            // update time
            await beaconChainInformation.SetTimeAsync(time).ConfigureAwait(false);

            Slot currentSlot = GetCurrentSlot(beaconChainInformation);

            // Once at start of each slot, confirm responsibilities (could have been a reorg), then do proposal
            bool shouldCheckStartSlot = currentSlot > beaconChainInformation.LastStartSlotChecked;

            if (shouldCheckStartSlot)
            {
                await UpdateForkVersionActivityAsync(cancellationToken).ConfigureAwait(false);

                // TODO: For beacon nodes that don't report SyncingStatus (which is nullable/optional),
                // then need a different strategy to determine the current head known by the beacon node.
                // The current code (2020-03-15) will simply start from slot 0 and process 1/second until
                // caught up with clock slot; at 6 seconds/slot, this is 6x faster, i.e. 1 day still takes 4 hours.
                // (okay for testing).
                // Alternative 1: see if the node supports /beacon/head, and use that slot
                // Alternative 2: try UpdateDuties, and if you get 406 DutiesNotAvailableForRequestedEpoch then use a
                // divide & conquer algorithm to determine block to check. (Could be a back off x1, x2, x4, x8, etc if 406)

                await UpdateSyncStatusActivityAsync(cancellationToken).ConfigureAwait(false);

                // Need to have an anchor block (will provide the genesis time) before can do anything
                // Absolutely no point in generating blocks < anchor slot
                // Irrespective of clock time, can't check duties >= 2 epochs ahead of current (head), as don't have data
                //  - actually, validator only cares about what they need to sign this slot
                //  - the beacon node takes care of subscribing to topics an epoch in advance, etc
                // While signing a block < highest (seen) slot may be a waste, there is no penalty for doing so

                // Generally no point in generating blocks <= current (head) slot
                Slot slotToCheck =
                    Slot.Max(beaconChainInformation.LastStartSlotChecked,
                             beaconChainInformation.SyncStatus.CurrentSlot) +
                    Slot.One;

                LogDebug.ProcessingSlotStart(_logger, slotToCheck, currentSlot,
                                             beaconChainInformation.SyncStatus.CurrentSlot,
                                             null);

                // Slot is set before processing; if there is an error (in process; update duties has a try/catch), it will skip to the next slot
                // (maybe the error will be resolved; trade off of whether error can be fixed by retrying, e.g. network error,
                // but potentially getting stuck in a slot, vs missing a slot)
                // TODO: Maybe add a retry policy/retry count when to advance last slot checked regardless
                await beaconChainInformation.SetLastStartSlotChecked(slotToCheck);

                // TODO: Attestations should run checks one epoch ahead, for topic subscriptions, although this is more a beacon node thing to do.

                // Note that UpdateDuties will continue even if there is an error/issue (i.e. assume no change and process what we have)
                Epoch epochToCheck = ComputeEpochAtSlot(slotToCheck);
                // Check duties each slot, in case there has been a reorg
                await UpdateDutiesActivityAsync(epochToCheck, cancellationToken).ConfigureAwait(false);

                await ProcessProposalDutiesAsync(slotToCheck, cancellationToken).ConfigureAwait(false);

                // If upcoming attester, join (or change) topics
                // Subscribe to topics
            }

            // Attestation is done 1/3 way through slot
            Slot  nextAttestationSlot = GetNextAttestationSlotToCheck(beaconChainInformation);
            ulong nextAttestationTime = GetAttestationTime(beaconChainInformation, nextAttestationSlot);

            if (beaconChainInformation.Time > nextAttestationTime)
            {
                // In theory, there could be a reorg between start of slot and attestation, changing
                // the attestation requirements, but currently (2020-05-24) only checking at start
                // of slot (above).

                LogDebug.ProcessingSlotAttestations(_logger, nextAttestationSlot, beaconChainInformation.Time, null);
                await beaconChainInformation.SetLastAttestationSlotChecked(nextAttestationSlot);
                await ProcessAttestationDutiesAsync(nextAttestationSlot, cancellationToken).ConfigureAwait(false);
            }

            // TODO: Aggregation is done 2/3 way through slot
        }