lotus/chain/beacon/drand/drand.go

168 lines
3.6 KiB
Go
Raw Normal View History

2020-04-09 02:55:17 +00:00
package drand
import (
"bytes"
2020-04-09 02:55:17 +00:00
"context"
"sync"
2020-04-14 03:05:19 +00:00
"time"
2020-04-09 02:55:17 +00:00
"github.com/drand/kyber"
"github.com/filecoin-project/lotus/build"
2020-04-09 02:55:17 +00:00
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/abi"
2020-04-14 03:05:19 +00:00
"golang.org/x/xerrors"
2020-04-09 02:55:17 +00:00
2020-04-14 03:05:19 +00:00
logging "github.com/ipfs/go-log"
dchain "github.com/drand/drand/chain"
dclient "github.com/drand/drand/client"
2020-04-09 02:55:17 +00:00
)
2020-04-14 03:05:19 +00:00
var log = logging.Logger("drand")
var drandServers = []string{
"https://pl-eu.testnet.drand.sh",
"https://pl-us.testnet.drand.sh",
"https://pl-sin.testnet.drand.sh",
2020-04-14 03:05:19 +00:00
}
var drandChain *dchain.Info
func init() {
var err error
drandChain, err = dchain.InfoFromJSON(bytes.NewReader([]byte(build.DrandChain)))
if err != nil {
panic("could not unmarshal chain info: " + err.Error())
}
}
2020-04-14 03:05:19 +00:00
type drandPeer struct {
addr string
tls bool
}
func (dp *drandPeer) Address() string {
return dp.addr
}
func (dp *drandPeer) IsTLS() bool {
return dp.tls
}
2020-04-09 02:55:17 +00:00
type DrandBeacon struct {
client dclient.Client
2020-04-14 03:05:19 +00:00
pubkey kyber.Point
2020-04-14 03:05:19 +00:00
// seconds
interval time.Duration
drandGenTime uint64
filGenTime uint64
filRoundTime uint64
2020-04-09 02:55:17 +00:00
cacheLk sync.Mutex
2020-04-14 03:05:19 +00:00
localCache map[uint64]types.BeaconEntry
2020-04-09 02:55:17 +00:00
}
2020-04-14 03:05:19 +00:00
func NewDrandBeacon(genesisTs, interval uint64) (*DrandBeacon, error) {
if genesisTs == 0 {
panic("what are you doing this cant be zero")
}
client, err := dclient.New(
dclient.WithHTTPEndpoints(drandServers),
dclient.WithChainInfo(drandChain),
dclient.WithCacheSize(1024),
)
2020-04-14 03:05:19 +00:00
if err != nil {
return nil, xerrors.Errorf("creating drand client")
2020-04-14 03:05:19 +00:00
}
db := &DrandBeacon{
client: client,
localCache: make(map[uint64]types.BeaconEntry),
}
2020-04-14 03:05:19 +00:00
db.pubkey = drandChain.PublicKey
db.interval = drandChain.Period
db.drandGenTime = uint64(drandChain.GenesisTime)
2020-04-14 03:05:19 +00:00
db.filRoundTime = interval
db.filGenTime = genesisTs
return db, nil
2020-04-09 02:55:17 +00:00
}
func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.Response {
2020-04-09 17:13:09 +00:00
out := make(chan beacon.Response, 1)
if round != 0 {
be := db.getCachedValue(round)
if be != nil {
out <- beacon.Response{Entry: *be}
close(out)
return out
}
}
go func() {
start := time.Now()
log.Infow("start fetching randomness", "round", round)
resp, err := db.client.Get(ctx, round)
var br beacon.Response
if err != nil {
br.Err = xerrors.Errorf("drand failed Get request: %w", err)
} else {
br.Entry.Round = resp.Round()
br.Entry.Data = resp.Signature()
}
log.Infow("done fetching randomness", "round", round, "took", time.Since(start))
out <- br
close(out)
}()
2020-04-09 17:13:09 +00:00
return out
2020-04-09 02:55:17 +00:00
}
func (db *DrandBeacon) cacheValue(e types.BeaconEntry) {
2020-04-14 03:05:19 +00:00
db.cacheLk.Lock()
defer db.cacheLk.Unlock()
db.localCache[e.Round] = e
2020-04-14 03:05:19 +00:00
}
func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry {
db.cacheLk.Lock()
defer db.cacheLk.Unlock()
v, ok := db.localCache[round]
if !ok {
return nil
}
return &v
}
func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error {
if prev.Round == 0 {
// TODO handle genesis better
return nil
}
b := &dchain.Beacon{
2020-04-30 02:53:24 +00:00
PreviousSig: prev.Data,
Round: curr.Round,
Signature: curr.Data,
}
err := dchain.VerifyBeacon(db.pubkey, b)
if err == nil {
db.cacheValue(curr)
}
return err
2020-04-09 02:55:17 +00:00
}
2020-04-14 03:05:19 +00:00
func (db *DrandBeacon) MaxBeaconRoundForEpoch(filEpoch abi.ChainEpoch, prevEntry types.BeaconEntry) uint64 {
// TODO: sometimes the genesis time for filecoin is zero and this goes negative
latestTs := ((uint64(filEpoch) * db.filRoundTime) + db.filGenTime) - db.filRoundTime
dround := (latestTs - db.drandGenTime) / uint64(db.interval.Seconds())
return dround
2020-04-09 02:55:17 +00:00
}
var _ beacon.RandomBeacon = (*DrandBeacon)(nil)