2021-09-18 17:57:04 +00:00
|
|
|
package rand
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/binary"
|
|
|
|
|
|
|
|
"github.com/ipfs/go-cid"
|
2022-06-14 15:00:51 +00:00
|
|
|
logging "github.com/ipfs/go-log/v2"
|
2021-09-18 17:57:04 +00:00
|
|
|
"github.com/minio/blake2b-simd"
|
|
|
|
"go.opencensus.io/trace"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/network"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/beacon"
|
2021-09-18 17:57:04 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
var log = logging.Logger("rand")
|
|
|
|
|
2022-12-18 19:59:34 +00:00
|
|
|
func DrawRandomnessFromBase(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
2022-12-18 20:05:39 +00:00
|
|
|
return DrawRandomnessFromDigest(blake2b.Sum256(rbase), pers, round, entropy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func DrawRandomnessFromDigest(digest [32]byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
2021-09-18 17:57:04 +00:00
|
|
|
h := blake2b.New256()
|
|
|
|
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
|
|
|
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
|
|
|
}
|
2022-12-18 20:05:39 +00:00
|
|
|
_, err := h.Write(digest[:])
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("hashing VRFDigest: %w", err)
|
|
|
|
}
|
|
|
|
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
|
|
|
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
|
|
|
}
|
|
|
|
_, err = h.Write(entropy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("hashing entropy: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return h.Sum(nil), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *stateRand) GetBeaconRandomnessTipset(ctx context.Context, round abi.ChainEpoch, lookback bool) (*types.TipSet, error) {
|
|
|
|
_, span := trace.StartSpan(ctx, "store.GetBeaconRandomness")
|
|
|
|
defer span.End()
|
|
|
|
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ts, err := sr.cs.LoadTipSet(ctx, types.NewTipSetKey(sr.blks...))
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if round > ts.Height() {
|
|
|
|
return nil, xerrors.Errorf("cannot draw randomness from the future")
|
|
|
|
}
|
|
|
|
|
|
|
|
searchHeight := round
|
|
|
|
if searchHeight < 0 {
|
|
|
|
searchHeight = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
randTs, err := sr.cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return randTs, nil
|
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) getChainRandomness(ctx context.Context, round abi.ChainEpoch, lookback bool) ([32]byte, error) {
|
2021-09-18 17:57:04 +00:00
|
|
|
_, span := trace.StartSpan(ctx, "store.GetChainRandomness")
|
|
|
|
defer span.End()
|
|
|
|
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ts, err := sr.cs.LoadTipSet(ctx, types.NewTipSetKey(sr.blks...))
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if round > ts.Height() {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, xerrors.Errorf("cannot draw randomness from the future")
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
searchHeight := round
|
|
|
|
if searchHeight < 0 {
|
|
|
|
searchHeight = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
randTs, err := sr.cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
return blake2b.Sum256(randTs.MinTicketBlock().Ticket.VRFProof), nil
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-17 23:54:17 +00:00
|
|
|
type NetworkVersionGetter func(context.Context, abi.ChainEpoch) network.Version
|
|
|
|
|
2021-09-18 17:57:04 +00:00
|
|
|
type stateRand struct {
|
2021-12-17 23:54:17 +00:00
|
|
|
cs *store.ChainStore
|
|
|
|
blks []cid.Cid
|
|
|
|
beacon beacon.Schedule
|
|
|
|
networkVersionGetter NetworkVersionGetter
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 15:14:17 +00:00
|
|
|
type Rand interface {
|
|
|
|
GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error)
|
|
|
|
GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewStateRand(cs *store.ChainStore, blks []cid.Cid, b beacon.Schedule, networkVersionGetter NetworkVersionGetter) Rand {
|
2021-09-18 17:57:04 +00:00
|
|
|
return &stateRand{
|
2021-12-17 23:54:17 +00:00
|
|
|
cs: cs,
|
|
|
|
blks: blks,
|
|
|
|
beacon: b,
|
|
|
|
networkVersionGetter: networkVersionGetter,
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// network v0-12
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) getBeaconRandomnessV1(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) {
|
2021-09-18 17:57:04 +00:00
|
|
|
randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, true)
|
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
be, err := sr.cs.GetLatestBeaconEntry(ctx, randTs)
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
return blake2b.Sum256(be.Data), nil
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// network v13
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) getBeaconRandomnessV2(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) {
|
2021-09-18 17:57:04 +00:00
|
|
|
randTs, err := sr.GetBeaconRandomnessTipset(ctx, round, false)
|
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
be, err := sr.cs.GetLatestBeaconEntry(ctx, randTs)
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
return blake2b.Sum256(be.Data), nil
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// network v14 and on
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) getBeaconRandomnessV3(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) {
|
2021-09-18 17:57:04 +00:00
|
|
|
if filecoinEpoch < 0 {
|
2022-12-18 19:14:50 +00:00
|
|
|
return sr.getBeaconRandomnessV2(ctx, filecoinEpoch)
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
be, err := sr.extractBeaconEntryForEpoch(ctx, filecoinEpoch)
|
|
|
|
if err != nil {
|
2022-06-14 06:13:44 +00:00
|
|
|
log.Errorf("failed to get beacon entry as expected: %s", err)
|
2022-12-18 20:05:39 +00:00
|
|
|
return [32]byte{}, err
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
return blake2b.Sum256(be.Data), nil
|
2021-09-18 17:57:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) GetChainRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) {
|
2021-12-17 23:54:17 +00:00
|
|
|
nv := sr.networkVersionGetter(ctx, filecoinEpoch)
|
|
|
|
|
2021-12-17 05:58:20 +00:00
|
|
|
if nv >= network.Version13 {
|
2022-12-18 19:14:50 +00:00
|
|
|
return sr.getChainRandomness(ctx, filecoinEpoch, false)
|
2021-12-17 05:58:20 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 19:14:50 +00:00
|
|
|
return sr.getChainRandomness(ctx, filecoinEpoch, true)
|
2021-12-17 05:58:20 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
func (sr *stateRand) GetBeaconRandomness(ctx context.Context, filecoinEpoch abi.ChainEpoch) ([32]byte, error) {
|
2021-12-17 23:54:17 +00:00
|
|
|
nv := sr.networkVersionGetter(ctx, filecoinEpoch)
|
|
|
|
|
2021-12-17 05:58:20 +00:00
|
|
|
if nv >= network.Version14 {
|
2022-12-18 19:14:50 +00:00
|
|
|
return sr.getBeaconRandomnessV3(ctx, filecoinEpoch)
|
2021-12-17 05:58:20 +00:00
|
|
|
} else if nv == network.Version13 {
|
2022-12-18 19:14:50 +00:00
|
|
|
return sr.getBeaconRandomnessV2(ctx, filecoinEpoch)
|
|
|
|
}
|
2024-05-09 02:15:35 +00:00
|
|
|
return sr.getBeaconRandomnessV1(ctx, filecoinEpoch)
|
2022-12-18 19:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *stateRand) DrawChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
2022-12-18 20:05:39 +00:00
|
|
|
digest, err := sr.GetChainRandomness(ctx, filecoinEpoch)
|
2022-12-18 19:14:50 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to get chain randomness: %w", err)
|
2021-12-17 05:58:20 +00:00
|
|
|
}
|
2022-12-18 19:14:50 +00:00
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
ret, err := DrawRandomnessFromDigest(digest, pers, filecoinEpoch, entropy)
|
2022-12-18 19:14:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to draw chain randomness: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *stateRand) DrawBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, filecoinEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
2022-12-18 20:05:39 +00:00
|
|
|
digest, err := sr.GetBeaconRandomness(ctx, filecoinEpoch)
|
2022-12-18 19:14:50 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2023-08-22 15:14:17 +00:00
|
|
|
return nil, xerrors.Errorf("failed to get beacon randomness: %w", err)
|
2022-12-18 19:14:50 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 20:05:39 +00:00
|
|
|
ret, err := DrawRandomnessFromDigest(digest, pers, filecoinEpoch, entropy)
|
2022-12-18 19:14:50 +00:00
|
|
|
if err != nil {
|
2023-08-22 15:14:17 +00:00
|
|
|
return nil, xerrors.Errorf("failed to draw beacon randomness: %w", err)
|
2022-12-18 19:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
2021-12-17 05:58:20 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:57:04 +00:00
|
|
|
func (sr *stateRand) extractBeaconEntryForEpoch(ctx context.Context, filecoinEpoch abi.ChainEpoch) (*types.BeaconEntry, error) {
|
|
|
|
randTs, err := sr.GetBeaconRandomnessTipset(ctx, filecoinEpoch, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-27 01:23:41 +00:00
|
|
|
nv := sr.networkVersionGetter(ctx, filecoinEpoch)
|
|
|
|
|
|
|
|
round := sr.beacon.BeaconForEpoch(filecoinEpoch).MaxBeaconRoundForEpoch(nv, filecoinEpoch)
|
2021-09-18 17:57:04 +00:00
|
|
|
|
|
|
|
for i := 0; i < 20; i++ {
|
|
|
|
cbe := randTs.Blocks()[0].BeaconEntries
|
|
|
|
for _, v := range cbe {
|
|
|
|
if v.Round == round {
|
|
|
|
return &v, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
next, err := sr.cs.LoadTipSet(ctx, randTs.Parents())
|
2021-09-18 17:57:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to load parents when searching back for beacon entry: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
randTs = next
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, xerrors.Errorf("didn't find beacon for round %d (epoch %d)", round, filecoinEpoch)
|
|
|
|
}
|