186 lines
4.5 KiB
Go
186 lines
4.5 KiB
Go
package consensus
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
big2 "github.com/filecoin-project/go-state-types/big"
|
|
|
|
"github.com/Secured-Finance/dione/sigs"
|
|
|
|
"github.com/Secured-Finance/dione/rpc"
|
|
|
|
"github.com/Secured-Finance/dione/beacon"
|
|
oracleEmitter "github.com/Secured-Finance/dione/contracts/oracleemitter"
|
|
"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
|
|
mutex sync.Mutex
|
|
beacon beacon.BeaconNetworks
|
|
ethClient *ethclient.EthereumClient
|
|
minerStake types.BigInt
|
|
networkStake types.BigInt
|
|
privateKey []byte
|
|
}
|
|
|
|
func NewMiner(
|
|
address peer.ID,
|
|
ethAddress common.Address,
|
|
beacon beacon.BeaconNetworks,
|
|
ethClient *ethclient.EthereumClient,
|
|
privateKey []byte,
|
|
) *Miner {
|
|
return &Miner{
|
|
address: address,
|
|
ethAddress: ethAddress,
|
|
beacon: beacon,
|
|
ethClient: ethClient,
|
|
privateKey: privateKey,
|
|
}
|
|
}
|
|
|
|
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) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt, error) {
|
|
mStake, err := m.ethClient.GetMinerStake(miner)
|
|
|
|
if err != nil {
|
|
logrus.Warn("Can't get miner stake", err)
|
|
return nil, nil, err
|
|
}
|
|
|
|
nStake, err := m.ethClient.GetTotalStake()
|
|
|
|
if err != nil {
|
|
logrus.Warn("Can't get miner stake", err)
|
|
return nil, nil, err
|
|
}
|
|
|
|
return mStake, nStake, nil
|
|
}
|
|
|
|
func (m *Miner) MineTask(ctx context.Context, event *oracleEmitter.OracleEmitterNewOracleRequest) (*types.DioneTask, error) {
|
|
beaconValues, 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: ", beaconValues[1].Round)
|
|
|
|
randomBase := beaconValues[1]
|
|
|
|
if err := m.UpdateCurrentStakeInfo(); err != nil {
|
|
return nil, xerrors.Errorf("failed to update miner stake: %w", err)
|
|
}
|
|
|
|
ticket, err := m.computeTicket(&randomBase)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
|
|
}
|
|
|
|
winner, err := IsRoundWinner(
|
|
types.DrandRound(randomBase.Round),
|
|
m.address,
|
|
randomBase,
|
|
m.minerStake,
|
|
m.networkStake,
|
|
func(id peer.ID, bytes []byte) (*types.Signature, error) {
|
|
return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes)
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("failed to check if we win next round: %w", err)
|
|
}
|
|
|
|
if winner == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
rpcMethod := rpc.GetRPCMethod(event.OriginChain, event.RequestType)
|
|
if rpcMethod == nil {
|
|
return nil, xerrors.Errorf("invalid rpc method name/type")
|
|
}
|
|
res, err := rpcMethod(event.RequestParams)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("couldn't do rpc request: %w", err)
|
|
}
|
|
|
|
return &types.DioneTask{
|
|
OriginChain: event.OriginChain,
|
|
RequestType: event.RequestType,
|
|
RequestParams: event.RequestParams,
|
|
Miner: m.address,
|
|
MinerEth: m.ethAddress.Hex(),
|
|
Ticket: ticket,
|
|
ElectionProof: winner,
|
|
BeaconEntries: beaconValues,
|
|
Payload: res,
|
|
DrandRound: types.DrandRound(randomBase.Round),
|
|
}, nil
|
|
}
|
|
|
|
func (m *Miner) computeTicket(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(func(id peer.ID, bytes []byte) (*types.Signature, error) {
|
|
return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes)
|
|
}, m.address, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Ticket{
|
|
VRFProof: vrfOut,
|
|
}, nil
|
|
}
|
|
|
|
func (m *Miner) IsMinerEligibleToProposeTask(ethAddress common.Address) error {
|
|
mStake, err := m.ethClient.GetMinerStake(ethAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ok := mStake.GreaterThanEqual(big2.NewInt(ethclient.MinMinerStake))
|
|
if !ok {
|
|
return xerrors.Errorf("miner doesn't have enough staked tokens")
|
|
}
|
|
return nil
|
|
}
|