2020-03-25 23:16:17 +00:00
|
|
|
package beacon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2021-04-06 13:04:32 +00:00
|
|
|
logging "github.com/ipfs/go-log/v2"
|
2020-03-25 23:16:17 +00:00
|
|
|
"golang.org/x/xerrors"
|
2020-07-10 14:43:14 +00:00
|
|
|
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
"github.com/filecoin-project/go-state-types/network"
|
|
|
|
|
2020-07-10 14:43:14 +00:00
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-03-25 23:16:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var log = logging.Logger("beacon")
|
|
|
|
|
|
|
|
type Response struct {
|
2020-04-06 12:47:14 +00:00
|
|
|
Entry types.BeaconEntry
|
2020-03-25 23:16:17 +00:00
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
2020-09-08 20:28:06 +00:00
|
|
|
type Schedule []BeaconPoint
|
|
|
|
|
|
|
|
func (bs Schedule) BeaconForEpoch(e abi.ChainEpoch) RandomBeacon {
|
|
|
|
for i := len(bs) - 1; i >= 0; i-- {
|
|
|
|
bp := bs[i]
|
2020-09-09 18:50:04 +00:00
|
|
|
if e >= bp.Start {
|
2020-09-08 20:28:06 +00:00
|
|
|
return bp.Beacon
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bs[0].Beacon
|
|
|
|
}
|
|
|
|
|
|
|
|
type BeaconPoint struct {
|
|
|
|
Start abi.ChainEpoch
|
|
|
|
Beacon RandomBeacon
|
|
|
|
}
|
|
|
|
|
2020-06-23 21:51:25 +00:00
|
|
|
// RandomBeacon represents a system that provides randomness to Lotus.
|
|
|
|
// Other components interrogate the RandomBeacon to acquire randomness that's
|
|
|
|
// valid for a specific chain epoch. Also to verify beacon entries that have
|
|
|
|
// been posted on chain.
|
2020-04-08 22:12:36 +00:00
|
|
|
type RandomBeacon interface {
|
2020-03-25 23:16:17 +00:00
|
|
|
Entry(context.Context, uint64) <-chan Response
|
2024-01-25 00:40:46 +00:00
|
|
|
VerifyEntry(entry types.BeaconEntry, prevEntrySig []byte) error
|
2021-09-27 01:23:41 +00:00
|
|
|
MaxBeaconRoundForEpoch(network.Version, abi.ChainEpoch) uint64
|
2020-03-25 23:16:17 +00:00
|
|
|
}
|
|
|
|
|
2021-09-27 01:23:41 +00:00
|
|
|
func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockHeader, parentEpoch abi.ChainEpoch,
|
2020-09-08 20:28:06 +00:00
|
|
|
prevEntry types.BeaconEntry) error {
|
2024-01-25 00:40:46 +00:00
|
|
|
// Before nv22 we had "chained" beacons, and so required two entries at a fork
|
|
|
|
if nv < network.Version22 {
|
2020-09-09 18:37:12 +00:00
|
|
|
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
|
|
|
|
currBeacon := bSchedule.BeaconForEpoch(h.Height)
|
|
|
|
if parentBeacon != currBeacon {
|
|
|
|
if len(h.BeaconEntries) != 2 {
|
|
|
|
return xerrors.Errorf("expected two beacon entries at beacon fork, got %d", len(h.BeaconEntries))
|
|
|
|
}
|
2024-01-25 00:40:46 +00:00
|
|
|
err := currBeacon.VerifyEntry(h.BeaconEntries[1], h.BeaconEntries[0].Data)
|
2020-09-09 18:37:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("beacon at fork point invalid: (%v, %v): %w",
|
|
|
|
h.BeaconEntries[1], h.BeaconEntries[0], err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2020-09-08 20:28:06 +00:00
|
|
|
|
|
|
|
b := bSchedule.BeaconForEpoch(h.Height)
|
2021-09-27 01:23:41 +00:00
|
|
|
maxRound := b.MaxBeaconRoundForEpoch(nv, h.Height)
|
2024-01-25 00:40:46 +00:00
|
|
|
// We don't expect to ever actually meet this condition
|
2020-04-08 17:49:50 +00:00
|
|
|
if maxRound == prevEntry.Round {
|
|
|
|
if len(h.BeaconEntries) != 0 {
|
|
|
|
return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries))
|
|
|
|
}
|
|
|
|
return nil
|
2020-04-08 15:11:42 +00:00
|
|
|
}
|
2020-04-06 12:47:14 +00:00
|
|
|
|
2020-08-29 00:13:38 +00:00
|
|
|
if len(h.BeaconEntries) == 0 {
|
|
|
|
return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any")
|
|
|
|
}
|
|
|
|
|
2024-01-25 00:40:46 +00:00
|
|
|
if nv < network.Version22 && prevEntry.Round == 0 {
|
|
|
|
// We skip verifying the genesis entry before nv22, since that was "chained" randomness.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:49:50 +00:00
|
|
|
last := h.BeaconEntries[len(h.BeaconEntries)-1]
|
|
|
|
if last.Round != maxRound {
|
|
|
|
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
|
|
|
|
}
|
2020-03-25 23:16:17 +00:00
|
|
|
|
2024-01-24 23:35:46 +00:00
|
|
|
// Verify that all other entries' rounds are as expected for the epochs in between parentEpoch and h.Height
|
|
|
|
for i, e := range h.BeaconEntries {
|
|
|
|
correctRound := b.MaxBeaconRoundForEpoch(nv, parentEpoch+abi.ChainEpoch(i)+1)
|
|
|
|
if e.Round != correctRound {
|
|
|
|
return xerrors.Errorf("unexpected beacon round %d, expected %d for epoch %d", e.Round, correctRound, parentEpoch+abi.ChainEpoch(i))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the beacon entries themselves
|
2020-04-08 17:49:50 +00:00
|
|
|
for i, e := range h.BeaconEntries {
|
2024-01-25 00:40:46 +00:00
|
|
|
if err := b.VerifyEntry(e, prevEntry.Data); err != nil {
|
2020-04-14 03:05:19 +00:00
|
|
|
return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err)
|
2020-04-08 15:11:42 +00:00
|
|
|
}
|
|
|
|
prevEntry = e
|
2020-04-07 18:23:16 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 23:16:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-27 01:23:41 +00:00
|
|
|
func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.Version, epoch abi.ChainEpoch, parentEpoch abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
|
2024-01-25 00:40:46 +00:00
|
|
|
// Before nv22 we had "chained" beacons, and so required two entries at a fork
|
|
|
|
if nv < network.Version22 {
|
2020-09-09 18:37:12 +00:00
|
|
|
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
|
|
|
|
currBeacon := bSchedule.BeaconForEpoch(epoch)
|
|
|
|
if parentBeacon != currBeacon {
|
|
|
|
// Fork logic
|
2021-09-27 01:23:41 +00:00
|
|
|
round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch)
|
2020-09-09 18:37:12 +00:00
|
|
|
out := make([]types.BeaconEntry, 2)
|
|
|
|
rch := currBeacon.Entry(ctx, round-1)
|
|
|
|
res := <-rch
|
|
|
|
if res.Err != nil {
|
|
|
|
return nil, xerrors.Errorf("getting entry %d returned error: %w", round-1, res.Err)
|
|
|
|
}
|
|
|
|
out[0] = res.Entry
|
|
|
|
rch = currBeacon.Entry(ctx, round)
|
|
|
|
res = <-rch
|
|
|
|
if res.Err != nil {
|
|
|
|
return nil, xerrors.Errorf("getting entry %d returned error: %w", round, res.Err)
|
|
|
|
}
|
|
|
|
out[1] = res.Entry
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-08 20:28:06 +00:00
|
|
|
beacon := bSchedule.BeaconForEpoch(epoch)
|
|
|
|
|
2020-07-10 14:43:14 +00:00
|
|
|
start := build.Clock.Now()
|
2020-03-25 23:16:17 +00:00
|
|
|
|
2021-09-27 01:23:41 +00:00
|
|
|
maxRound := beacon.MaxBeaconRoundForEpoch(nv, epoch)
|
2024-01-25 00:40:46 +00:00
|
|
|
// We don't expect this to ever be the case
|
2020-04-08 17:49:50 +00:00
|
|
|
if maxRound == prev.Round {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-04-14 03:05:19 +00:00
|
|
|
// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
|
|
|
|
if prev.Round == 0 {
|
|
|
|
prev.Round = maxRound - 1
|
|
|
|
}
|
|
|
|
|
2020-04-06 12:47:14 +00:00
|
|
|
var out []types.BeaconEntry
|
2024-01-24 23:35:46 +00:00
|
|
|
for currEpoch := epoch; currEpoch > parentEpoch; currEpoch-- {
|
|
|
|
currRound := beacon.MaxBeaconRoundForEpoch(nv, currEpoch)
|
|
|
|
rch := beacon.Entry(ctx, currRound)
|
2020-03-25 23:16:17 +00:00
|
|
|
select {
|
|
|
|
case resp := <-rch:
|
|
|
|
if resp.Err != nil {
|
|
|
|
return nil, xerrors.Errorf("beacon entry request returned error: %w", resp.Err)
|
|
|
|
}
|
|
|
|
|
|
|
|
out = append(out, resp.Entry)
|
|
|
|
case <-ctx.Done():
|
2020-09-08 20:28:06 +00:00
|
|
|
return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for epoch %d: %w", epoch, ctx.Err())
|
2020-03-25 23:16:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 14:43:14 +00:00
|
|
|
log.Debugw("fetching beacon entries", "took", build.Clock.Since(start), "numEntries", len(out))
|
2020-04-08 17:49:50 +00:00
|
|
|
reverse(out)
|
2020-03-25 23:16:17 +00:00
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:49:50 +00:00
|
|
|
func reverse(arr []types.BeaconEntry) {
|
|
|
|
for i := 0; i < len(arr)/2; i++ {
|
|
|
|
arr[i], arr[len(arr)-(1+i)] = arr[len(arr)-(1+i)], arr[i]
|
|
|
|
}
|
|
|
|
}
|