139 lines
3.0 KiB
Go
139 lines
3.0 KiB
Go
package consensus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"sync"
|
|
|
|
"github.com/Secured-Finance/dione/blockchain/pool"
|
|
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
|
|
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/Secured-Finance/dione/ethclient"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
ErrNoTxForBlock = fmt.Errorf("no transactions for including into block")
|
|
)
|
|
|
|
type Miner struct {
|
|
address peer.ID
|
|
ethAddress common.Address
|
|
mutex sync.Mutex
|
|
ethClient *ethclient.EthereumClient
|
|
minerStake *big.Int
|
|
networkStake *big.Int
|
|
privateKey crypto.PrivKey
|
|
mempool *pool.Mempool
|
|
}
|
|
|
|
func NewMiner(
|
|
address peer.ID,
|
|
ethAddress common.Address,
|
|
ethClient *ethclient.EthereumClient,
|
|
privateKey crypto.PrivKey,
|
|
mempool *pool.Mempool,
|
|
) *Miner {
|
|
return &Miner{
|
|
address: address,
|
|
ethAddress: ethAddress,
|
|
ethClient: ethClient,
|
|
privateKey: privateKey,
|
|
mempool: mempool,
|
|
}
|
|
}
|
|
|
|
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) (*big.Int, *big.Int, 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) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) {
|
|
logrus.WithField("height", lastBlockHeader.Height+1).Debug("Trying to mine new block...")
|
|
|
|
if err := m.UpdateCurrentStakeInfo(); err != nil {
|
|
return nil, fmt.Errorf("failed to update miner stake: %w", err)
|
|
}
|
|
|
|
winner, err := IsRoundWinner(
|
|
lastBlockHeader.Height+1,
|
|
m.address,
|
|
randomness,
|
|
randomnessRound,
|
|
m.minerStake,
|
|
m.networkStake,
|
|
m.privateKey,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to check if we winned in next round: %w", err)
|
|
}
|
|
|
|
if winner == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
txs := m.mempool.GetTransactionsForNewBlock()
|
|
if txs == nil {
|
|
return nil, ErrNoTxForBlock // skip new consensus round because there is no transaction for processing
|
|
}
|
|
|
|
newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey, winner)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create new block: %w", err)
|
|
}
|
|
|
|
return newBlock, nil
|
|
}
|
|
|
|
func (m *Miner) IsMinerEligibleToProposeBlock(ethAddress common.Address) error {
|
|
mStake, err := m.ethClient.GetMinerStake(ethAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if mStake.Cmp(big.NewInt(ethclient.MinMinerStake)) == -1 {
|
|
return errors.New("miner doesn't have enough staked tokens")
|
|
}
|
|
return nil
|
|
}
|