dione/consensus/miner.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
}