146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
package consensus
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/Secured-Finance/dione/ethclient"
|
|
"github.com/Secured-Finance/dione/types"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
type Miner struct {
|
|
address peer.ID
|
|
ethAddress common.Address
|
|
api MinerAPI
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
type MinerAPI interface {
|
|
WalletSign(context.Context, peer.ID, []byte) (*types.Signature, error)
|
|
// 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) UpdateStake(c *ethclient.EthereumClient, miner common.Address) error {
|
|
mStake, err := c.GetMinerStake(miner)
|
|
|
|
if err != nil {
|
|
logrus.Warn("Can't get miner stake", err)
|
|
return err
|
|
}
|
|
|
|
nStake, err := c.GetTotalStake()
|
|
|
|
if err != nil {
|
|
logrus.Warn("Can't get miner stake", err)
|
|
return err
|
|
}
|
|
|
|
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]
|
|
}
|
|
|
|
if err := mb.UpdateStake(ethClient, m.ethAddress); err != nil {
|
|
return nil, xerrors.Errorf("failed to update miner stake: %w", err)
|
|
}
|
|
|
|
ticket, err := m.computeTicket(ctx, &rbase, base, mb)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
|
|
}
|
|
|
|
winner, err := IsRoundWinner(ctx, round, m.address, rbase, mb, m.api)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("failed to check if we win next round: %w", err)
|
|
}
|
|
|
|
if winner == nil {
|
|
return nil, nil
|
|
}
|
|
return &types.DioneTask{
|
|
Miner: m.address,
|
|
Ticket: ticket,
|
|
ElectionProof: winner,
|
|
BeaconEntries: mb.BeaconEntries, // TODO decide what we need to do with multiple beacon entries
|
|
// TODO: signature
|
|
Epoch: round,
|
|
}, nil
|
|
}
|
|
|
|
func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, base *MiningBase, mb *MinerBase) (*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
|
|
|
|
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)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Ticket{
|
|
VRFProof: vrfOut,
|
|
}, nil
|
|
}
|