Fix task round/beacon system - remove task round and use latest drand round when creating task
This commit is contained in:
parent
89f3bcda90
commit
e8102dba70
110
beacon/beacon.go
110
beacon/beacon.go
@ -17,7 +17,7 @@ type BeaconResult struct {
|
||||
|
||||
type BeaconNetworks []BeaconNetwork
|
||||
|
||||
func (bn BeaconNetworks) BeaconNetworkForEpoch(e types.TaskEpoch) BeaconAPI {
|
||||
func (bn BeaconNetworks) BeaconNetworkForRound(e types.DrandRound) BeaconAPI {
|
||||
for i := len(bn) - 1; i >= 0; i-- {
|
||||
bp := bn[i]
|
||||
if e >= bp.Start {
|
||||
@ -28,7 +28,7 @@ func (bn BeaconNetworks) BeaconNetworkForEpoch(e types.TaskEpoch) BeaconAPI {
|
||||
}
|
||||
|
||||
type BeaconNetwork struct {
|
||||
Start types.TaskEpoch
|
||||
Start types.DrandRound
|
||||
Beacon BeaconAPI
|
||||
}
|
||||
|
||||
@ -39,12 +39,12 @@ type BeaconNetwork struct {
|
||||
type BeaconAPI interface {
|
||||
Entry(context.Context, uint64) <-chan BeaconResult
|
||||
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
|
||||
MaxBeaconRoundForEpoch(types.TaskEpoch) uint64
|
||||
LatestBeaconRound() uint64
|
||||
}
|
||||
|
||||
func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEpoch types.TaskEpoch, prevEntry types.BeaconEntry) error {
|
||||
parentBeacon := beaconNetworks.BeaconNetworkForEpoch(prevEpoch)
|
||||
currBeacon := beaconNetworks.BeaconNetworkForEpoch(t.Epoch)
|
||||
func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEpoch types.DrandRound, prevEntry types.BeaconEntry) error {
|
||||
parentBeacon := beaconNetworks.BeaconNetworkForRound(prevEpoch)
|
||||
currBeacon := beaconNetworks.BeaconNetworkForRound(t.DrandRound)
|
||||
if parentBeacon != currBeacon {
|
||||
if len(t.BeaconEntries) != 2 {
|
||||
return fmt.Errorf("expected two beacon entries at beacon fork, got %d", len(t.BeaconEntries))
|
||||
@ -58,9 +58,8 @@ func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prev
|
||||
}
|
||||
|
||||
// TODO: fork logic
|
||||
bNetwork := beaconNetworks.BeaconNetworkForEpoch(t.Epoch)
|
||||
maxRound := bNetwork.MaxBeaconRoundForEpoch(t.Epoch)
|
||||
if maxRound == prevEntry.Round {
|
||||
bNetwork := beaconNetworks.BeaconNetworkForRound(t.DrandRound)
|
||||
if uint64(t.DrandRound) == prevEntry.Round {
|
||||
if len(t.BeaconEntries) != 0 {
|
||||
return fmt.Errorf("expected not to have any beacon entries in this task, got %d", len(t.BeaconEntries))
|
||||
}
|
||||
@ -72,8 +71,8 @@ func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prev
|
||||
}
|
||||
|
||||
last := t.BeaconEntries[len(t.BeaconEntries)-1]
|
||||
if last.Round != maxRound {
|
||||
return fmt.Errorf("expected final beacon entry in task to be at round %d, got %d", maxRound, last.Round)
|
||||
if last.Round != uint64(t.DrandRound) {
|
||||
return fmt.Errorf("expected final beacon entry in task to be at round %d, got %d", uint64(t.DrandRound), last.Round)
|
||||
}
|
||||
|
||||
for i, e := range t.BeaconEntries {
|
||||
@ -86,61 +85,58 @@ func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prev
|
||||
return nil
|
||||
}
|
||||
|
||||
func BeaconEntriesForTask(ctx context.Context, beaconNetworks BeaconNetworks, epoch types.TaskEpoch, prevEpoch types.TaskEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
|
||||
prevBeacon := beaconNetworks.BeaconNetworkForEpoch(prevEpoch)
|
||||
currBeacon := beaconNetworks.BeaconNetworkForEpoch(epoch)
|
||||
if prevBeacon != currBeacon {
|
||||
// Fork logic
|
||||
round := currBeacon.MaxBeaconRoundForEpoch(epoch)
|
||||
out := make([]types.BeaconEntry, 2)
|
||||
rch := currBeacon.Entry(ctx, round-1)
|
||||
res := <-rch
|
||||
if res.Err != nil {
|
||||
return nil, fmt.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, fmt.Errorf("getting entry %d returned error: %w", round, res.Err)
|
||||
}
|
||||
out[1] = res.Entry
|
||||
return out, nil
|
||||
}
|
||||
func BeaconEntriesForTask(ctx context.Context, beaconNetworks BeaconNetworks) ([]types.BeaconEntry, error) {
|
||||
beacon := beaconNetworks.BeaconNetworkForRound(0)
|
||||
round := beacon.LatestBeaconRound()
|
||||
|
||||
beacon := beaconNetworks.BeaconNetworkForEpoch(epoch)
|
||||
//prevBeacon := beaconNetworks.BeaconNetworkForRound(prevRound)
|
||||
//currBeacon := beaconNetworks.BeaconNetworkForRound(round)
|
||||
//if prevBeacon != currBeacon {
|
||||
// // Fork logic
|
||||
// round := currBeacon.LatestBeaconRound()
|
||||
// out := make([]types.BeaconEntry, 2)
|
||||
// rch := currBeacon.Entry(ctx, round-1)
|
||||
// res := <-rch
|
||||
// if res.Err != nil {
|
||||
// return nil, fmt.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, fmt.Errorf("getting entry %d returned error: %w", round, res.Err)
|
||||
// }
|
||||
// out[1] = res.Entry
|
||||
// return out, nil
|
||||
//}
|
||||
|
||||
start := lib.Clock.Now()
|
||||
|
||||
maxRound := beacon.MaxBeaconRoundForEpoch(epoch)
|
||||
if maxRound == prev.Round {
|
||||
return nil, nil
|
||||
}
|
||||
//if round == prev.Round {
|
||||
// return nil, nil
|
||||
//}
|
||||
//
|
||||
//// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
|
||||
//if prev.Round == 0 {
|
||||
// prev.Round = round - 1
|
||||
//}
|
||||
|
||||
// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
|
||||
if prev.Round == 0 {
|
||||
prev.Round = maxRound - 1
|
||||
out := make([]types.BeaconEntry, 2)
|
||||
rch := beacon.Entry(ctx, round-1)
|
||||
res := <-rch
|
||||
if res.Err != nil {
|
||||
return nil, fmt.Errorf("getting entry %d returned error: %w", round-1, res.Err)
|
||||
}
|
||||
|
||||
cur := maxRound
|
||||
var out []types.BeaconEntry
|
||||
for cur > prev.Round {
|
||||
rch := beacon.Entry(ctx, cur)
|
||||
select {
|
||||
case resp := <-rch:
|
||||
if resp.Err != nil {
|
||||
return nil, fmt.Errorf("beacon entry request returned error: %w", resp.Err)
|
||||
}
|
||||
|
||||
out = append(out, resp.Entry)
|
||||
cur = resp.Entry.Round - 1
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("context timed out waiting on beacon entry to come back for epoch %d: %w", epoch, ctx.Err())
|
||||
}
|
||||
out[0] = res.Entry
|
||||
rch = beacon.Entry(ctx, round)
|
||||
res = <-rch
|
||||
if res.Err != nil {
|
||||
return nil, fmt.Errorf("getting entry %d returned error: %w", round, res.Err)
|
||||
}
|
||||
out[1] = res.Entry
|
||||
|
||||
logrus.Debug("fetching beacon entries", "took", lib.Clock.Since(start), "numEntries", len(out))
|
||||
reverse(out)
|
||||
//reverse(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,8 @@ func VerifyVRF(ctx context.Context, worker peer.ID, vrfBase, vrfproof []byte) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsRoundWinner(ctx context.Context, round types.TaskEpoch,
|
||||
worker peer.ID, brand types.BeaconEntry, mb *MinerBase, a MinerAPI) (*types.ElectionProof, error) {
|
||||
func IsRoundWinner(ctx context.Context, round types.DrandRound,
|
||||
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, a MinerAPI) (*types.ElectionProof, error) {
|
||||
|
||||
buf, err := worker.MarshalBinary()
|
||||
if err != nil {
|
||||
@ -53,13 +53,13 @@ func IsRoundWinner(ctx context.Context, round types.TaskEpoch,
|
||||
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, mb.WorkerKey, electionRand)
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, worker, electionRand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
||||
}
|
||||
|
||||
ep := &types.ElectionProof{VRFProof: vrfout}
|
||||
j := ep.ComputeWinCount(mb.MinerStake, mb.NetworkStake)
|
||||
j := ep.ComputeWinCount(minerStake, networkStake)
|
||||
ep.WinCount = j
|
||||
if j < 1 {
|
||||
return nil, nil
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/Secured-Finance/dione/beacon"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/Secured-Finance/dione/ethclient"
|
||||
@ -15,10 +17,14 @@ import (
|
||||
)
|
||||
|
||||
type Miner struct {
|
||||
address peer.ID
|
||||
ethAddress common.Address
|
||||
api MinerAPI
|
||||
mutex sync.Mutex
|
||||
address peer.ID
|
||||
ethAddress common.Address
|
||||
api MinerAPI
|
||||
mutex sync.Mutex
|
||||
beacon beacon.BeaconNetworks
|
||||
ethClient *ethclient.EthereumClient
|
||||
minerStake types.BigInt
|
||||
networkStake types.BigInt
|
||||
}
|
||||
|
||||
type MinerAPI interface {
|
||||
@ -26,84 +32,46 @@ type MinerAPI interface {
|
||||
// TODO: get miner base based on epoch;
|
||||
}
|
||||
|
||||
type MinerBase struct {
|
||||
MinerStake types.BigInt
|
||||
NetworkStake types.BigInt
|
||||
WorkerKey peer.ID
|
||||
EthWallet common.Address
|
||||
PrevBeaconEntry types.BeaconEntry
|
||||
BeaconEntries []types.BeaconEntry
|
||||
NullRounds types.TaskEpoch
|
||||
}
|
||||
|
||||
type MiningBase struct {
|
||||
epoch types.TaskEpoch
|
||||
nullRounds types.TaskEpoch // currently not used
|
||||
}
|
||||
|
||||
func NewMinerBase(minerStake, networkStake types.BigInt, minerAddress peer.ID,
|
||||
minerEthWallet common.Address, prev types.BeaconEntry, entries []types.BeaconEntry) *MinerBase {
|
||||
return &MinerBase{
|
||||
MinerStake: minerStake,
|
||||
NetworkStake: networkStake,
|
||||
WorkerKey: minerAddress,
|
||||
EthWallet: minerEthWallet,
|
||||
PrevBeaconEntry: prev,
|
||||
BeaconEntries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMiningBase() *MiningBase {
|
||||
return &MiningBase{
|
||||
nullRounds: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MinerBase) UpdateCurrentStakeInfo(c *ethclient.EthereumClient, miner common.Address) error {
|
||||
mStake, err := c.GetMinerStake(miner)
|
||||
func (m *Miner) UpdateCurrentStakeInfo() error {
|
||||
mStake, err := m.ethClient.GetMinerStake(m.ethAddress)
|
||||
|
||||
if err != nil {
|
||||
logrus.Warn("Can't get miner stake", err)
|
||||
return err
|
||||
}
|
||||
|
||||
nStake, err := c.GetTotalStake()
|
||||
nStake, err := m.ethClient.GetTotalStake()
|
||||
|
||||
if err != nil {
|
||||
logrus.Warn("Can't get miner stake", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.MinerStake = *mStake
|
||||
m.NetworkStake = *nStake
|
||||
m.minerStake = *mStake
|
||||
m.networkStake = *nStake
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start, Stop mining functions
|
||||
|
||||
func (m *Miner) MineTask(ctx context.Context, base *MiningBase, mb *MinerBase, ethClient *ethclient.EthereumClient) (*types.DioneTask, error) {
|
||||
round := base.epoch + base.nullRounds + 1
|
||||
logrus.Debug("attempting to mine the task at epoch: ", round)
|
||||
|
||||
prevEntry := mb.PrevBeaconEntry
|
||||
bvals := mb.BeaconEntries
|
||||
|
||||
rbase := prevEntry
|
||||
if len(bvals) > 0 {
|
||||
rbase = bvals[len(bvals)-1]
|
||||
func (m *Miner) MineTask(ctx context.Context) (*types.DioneTask, error) {
|
||||
bvals, err := beacon.BeaconEntriesForTask(ctx, m.beacon)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get beacon entries: %w", err)
|
||||
}
|
||||
logrus.Debug("attempting to mine the task at epoch: ", bvals[1].Round)
|
||||
|
||||
if err := mb.UpdateCurrentStakeInfo(ethClient, m.ethAddress); err != nil {
|
||||
rbase := bvals[1]
|
||||
|
||||
if err := m.UpdateCurrentStakeInfo(); err != nil {
|
||||
return nil, xerrors.Errorf("failed to update miner stake: %w", err)
|
||||
}
|
||||
|
||||
ticket, err := m.computeTicket(ctx, &rbase, base, mb)
|
||||
ticket, err := m.computeTicket(ctx, &rbase)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
|
||||
}
|
||||
|
||||
winner, err := IsRoundWinner(ctx, round, m.address, rbase, mb, m.api)
|
||||
winner, err := IsRoundWinner(ctx, types.DrandRound(rbase.Round), m.address, rbase, m.minerStake, m.networkStake, m.api)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to check if we win next round: %w", err)
|
||||
}
|
||||
@ -115,26 +83,26 @@ func (m *Miner) MineTask(ctx context.Context, base *MiningBase, mb *MinerBase, e
|
||||
Miner: m.address,
|
||||
Ticket: ticket,
|
||||
ElectionProof: winner,
|
||||
BeaconEntries: mb.BeaconEntries, // TODO decide what we need to do with multiple beacon entries
|
||||
BeaconEntries: bvals,
|
||||
// TODO: signature
|
||||
Epoch: round,
|
||||
DrandRound: types.DrandRound(rbase.Round),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, base *MiningBase, mb *MinerBase) (*types.Ticket, error) {
|
||||
func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry) (*types.Ticket, error) {
|
||||
buf, err := m.address.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to marshal address: %w", err)
|
||||
}
|
||||
|
||||
round := base.epoch + base.nullRounds + 1
|
||||
round := types.DrandRound(brand.Round)
|
||||
|
||||
input, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-types.TicketRandomnessLookback, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vrfOut, err := ComputeVRF(ctx, m.api.WalletSign, mb.WorkerKey, input)
|
||||
vrfOut, err := ComputeVRF(ctx, m.api.WalletSign, m.address, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.TaskEpoch, entropy []byte) ([]byte, error) {
|
||||
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.DrandRound, entropy []byte) ([]byte, error) {
|
||||
h := blake2b.New256()
|
||||
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
||||
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
||||
|
@ -159,16 +159,12 @@ func (db *DrandBeacon) VerifyEntry(curr, prev types.BeaconEntry) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) MaxBeaconRoundForEpoch(taskEpoch types.TaskEpoch) uint64 {
|
||||
var latestTs uint64
|
||||
if taskEpoch == 0 {
|
||||
latestTs = db.chainGenesisTime
|
||||
} else {
|
||||
latestTs = ((uint64(taskEpoch) * db.chainRoundTime) + db.chainGenesisTime) - db.chainRoundTime
|
||||
func (db *DrandBeacon) LatestBeaconRound() uint64 {
|
||||
latestDround, err := db.DrandClient.Get(context.TODO(), 0)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to get latest drand round: %w", err)
|
||||
}
|
||||
|
||||
dround := (latestTs - db.drandGenesisTime) / uint64(db.Interval.Seconds())
|
||||
return dround
|
||||
return latestDround.Round()
|
||||
}
|
||||
|
||||
var _ beacon.BeaconAPI = (*DrandBeacon)(nil)
|
||||
|
@ -3,19 +3,22 @@ package node
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Secured-Finance/dione/types"
|
||||
|
||||
"github.com/Secured-Finance/dione/beacon"
|
||||
"github.com/Secured-Finance/dione/config"
|
||||
"github.com/Secured-Finance/dione/drand"
|
||||
)
|
||||
|
||||
// NewBeaconQueue creates a new beacon chain schedule
|
||||
func (n *Node) NewBeaconQueue() (beacon.BeaconNetworks, error) {
|
||||
schedule := beacon.BeaconNetworks{}
|
||||
// NewBeaconClient creates a new beacon chain client
|
||||
func (n *Node) NewBeaconClient() (beacon.BeaconNetworks, error) {
|
||||
networks := beacon.BeaconNetworks{}
|
||||
bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, n.PubSubRouter.Pubsub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating drand beacon: %w", err)
|
||||
}
|
||||
schedule = append(schedule, beacon.BeaconNetwork{Start: 0, Beacon: bc})
|
||||
networks = append(networks, beacon.BeaconNetwork{Start: types.DrandRound(config.ChainGenesis), Beacon: bc})
|
||||
// NOTE: currently we use only one network
|
||||
|
||||
return schedule, nil
|
||||
return networks, nil
|
||||
}
|
||||
|
@ -16,12 +16,12 @@ const (
|
||||
SolanaTaskType
|
||||
)
|
||||
|
||||
// TaskEpoch represents the timestamp of Task computed by the Dione miner
|
||||
type TaskEpoch int64
|
||||
// DrandRound represents the round number in DRAND
|
||||
type DrandRound int64
|
||||
|
||||
const TicketRandomnessLookback = TaskEpoch(1)
|
||||
const TicketRandomnessLookback = DrandRound(1)
|
||||
|
||||
func (e TaskEpoch) String() string {
|
||||
func (e DrandRound) String() string {
|
||||
return strconv.FormatInt(int64(e), 10)
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ type DioneTask struct {
|
||||
ElectionProof *ElectionProof
|
||||
BeaconEntries []BeaconEntry
|
||||
Signature *Signature
|
||||
Epoch TaskEpoch
|
||||
DrandRound DrandRound
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user