2020-03-25 23:16:17 +00:00
|
|
|
package beacon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
|
|
|
logging "github.com/ipfs/go-log"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
)
|
|
|
|
|
|
|
|
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-04-08 22:12:36 +00:00
|
|
|
type RandomBeacon interface {
|
2020-03-25 23:16:17 +00:00
|
|
|
Entry(context.Context, uint64) <-chan Response
|
2020-04-08 15:11:42 +00:00
|
|
|
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
|
2020-04-08 17:49:50 +00:00
|
|
|
MaxBeaconRoundForEpoch(abi.ChainEpoch, types.BeaconEntry) uint64
|
2020-03-25 23:16:17 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 22:12:36 +00:00
|
|
|
func ValidateBlockValues(b RandomBeacon, h *types.BlockHeader, prevEntry types.BeaconEntry) error {
|
2020-04-08 17:49:50 +00:00
|
|
|
maxRound := b.MaxBeaconRoundForEpoch(h.Height, prevEntry)
|
|
|
|
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-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
|
|
|
|
2020-04-08 17:49:50 +00:00
|
|
|
for i, e := range h.BeaconEntries {
|
2020-04-08 15:11:42 +00:00
|
|
|
if err := b.VerifyEntry(e, prevEntry); err != nil {
|
|
|
|
return xerrors.Errorf("beacon entry %d was invalid: %w", i, err)
|
|
|
|
}
|
|
|
|
prevEntry = e
|
2020-04-07 18:23:16 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 23:16:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-08 22:12:36 +00:00
|
|
|
func BeaconEntriesForBlock(ctx context.Context, beacon RandomBeacon, round abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
|
2020-03-25 23:16:17 +00:00
|
|
|
start := time.Now()
|
|
|
|
|
2020-04-08 17:49:50 +00:00
|
|
|
maxRound := beacon.MaxBeaconRoundForEpoch(round, prev)
|
|
|
|
if maxRound == prev.Round {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
cur := maxRound
|
2020-04-06 12:47:14 +00:00
|
|
|
var out []types.BeaconEntry
|
2020-04-08 17:49:50 +00:00
|
|
|
for cur > prev.Round {
|
|
|
|
rch := beacon.Entry(ctx, cur)
|
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)
|
2020-04-08 17:49:50 +00:00
|
|
|
cur = resp.Entry.Round - 1
|
2020-03-25 23:16:17 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for round %d: %w", round, ctx.Err())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debugw("fetching beacon entries", "took", time.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]
|
|
|
|
}
|
|
|
|
}
|