130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
package consensus
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/Secured-Finance/dione/beacon"
|
|
|
|
"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 WalletAPI
|
|
mutex sync.Mutex
|
|
beacon beacon.BeaconNetworks
|
|
ethClient *ethclient.EthereumClient
|
|
minerStake types.BigInt
|
|
networkStake types.BigInt
|
|
}
|
|
|
|
func NewMiner(
|
|
address peer.ID,
|
|
ethAddress common.Address,
|
|
api WalletAPI,
|
|
beacon beacon.BeaconNetworks,
|
|
ethClient *ethclient.EthereumClient,
|
|
) *Miner {
|
|
return &Miner{
|
|
address: address,
|
|
ethAddress: ethAddress,
|
|
api: api,
|
|
beacon: beacon,
|
|
ethClient: ethClient,
|
|
}
|
|
}
|
|
|
|
type WalletAPI interface {
|
|
WalletSign(context.Context, peer.ID, []byte) (*types.Signature, error)
|
|
}
|
|
|
|
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 := m.ethClient.GetTotalStake()
|
|
|
|
if err != nil {
|
|
logrus.Warn("Can't get miner stake", err)
|
|
return err
|
|
}
|
|
|
|
m.minerStake = *mStake
|
|
m.networkStake = *nStake
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Miner) MineTask(ctx context.Context, payload []byte) (*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)
|
|
|
|
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)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
if winner == nil {
|
|
return nil, nil
|
|
}
|
|
return &types.DioneTask{
|
|
Miner: m.address,
|
|
Ticket: ticket,
|
|
ElectionProof: winner,
|
|
BeaconEntries: bvals,
|
|
Payload: payload,
|
|
// TODO: signature
|
|
DrandRound: types.DrandRound(rbase.Round),
|
|
}, nil
|
|
}
|
|
|
|
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 := 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, m.address, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Ticket{
|
|
VRFProof: vrfOut,
|
|
}, nil
|
|
}
|