dione/consensus/miner.go
2020-11-24 01:59:27 +04:00

146 lines
3.6 KiB
Go

package consensus
import (
"context"
"sync"
"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
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, event *oracleEmitter.OracleEmitterNewOracleRequest, sign SignFunc) (*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
}
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)
}
bres := []byte(res)
signature, err := sign(ctx, m.address, bres)
if err != nil {
return nil, xerrors.Errorf("Couldn't sign solana response: %w", err)
}
return &types.DioneTask{
Ticket: ticket,
ElectionProof: winner,
BeaconEntries: bvals,
Payload: bres,
Signature: 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
}