|
|
|
package worker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/big"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/harmony-one/harmony/log"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
|
"github.com/harmony-one/harmony/consensus"
|
|
|
|
"github.com/harmony-one/harmony/core"
|
|
|
|
"github.com/harmony-one/harmony/core/state"
|
|
|
|
"github.com/harmony-one/harmony/core/types"
|
|
|
|
"github.com/harmony-one/harmony/core/vm"
|
|
|
|
)
|
|
|
|
|
|
|
|
// environment is the worker's current environment and holds all of the current state information.
|
|
|
|
type environment struct {
|
|
|
|
state *state.StateDB // apply state changes here
|
|
|
|
gasPool *core.GasPool // available gas used to pack transactions
|
|
|
|
|
|
|
|
header *types.Header
|
|
|
|
txs []*types.Transaction
|
|
|
|
receipts []*types.Receipt
|
|
|
|
}
|
|
|
|
|
|
|
|
// Worker is the main object which takes care of submitting new work to consensus engine
|
|
|
|
// and gathering the sealing result.
|
|
|
|
type Worker struct {
|
|
|
|
config *params.ChainConfig
|
|
|
|
chain *core.BlockChain
|
|
|
|
current *environment // An environment for current running cycle.
|
|
|
|
|
|
|
|
coinbase common.Address
|
|
|
|
engine consensus.Engine
|
|
|
|
|
|
|
|
gasFloor uint64
|
|
|
|
gasCeil uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// SelectTransactionsForNewBlock selects transactions for new block.
|
|
|
|
func (w *Worker) SelectTransactionsForNewBlock(txs types.Transactions, maxNumTxs int) (types.Transactions, types.Transactions, types.Transactions) {
|
|
|
|
if w.current.gasPool == nil {
|
|
|
|
w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit)
|
|
|
|
}
|
|
|
|
selected := types.Transactions{}
|
|
|
|
unselected := types.Transactions{}
|
|
|
|
invalid := types.Transactions{}
|
|
|
|
for _, tx := range txs {
|
|
|
|
snap := w.current.state.Snapshot()
|
|
|
|
_, err := w.commitTransaction(tx, w.coinbase)
|
|
|
|
if len(selected) > maxNumTxs {
|
|
|
|
unselected = append(unselected, tx)
|
|
|
|
} else {
|
|
|
|
if err != nil {
|
|
|
|
w.current.state.RevertToSnapshot(snap)
|
|
|
|
invalid = append(invalid, tx)
|
|
|
|
} else {
|
|
|
|
selected = append(selected, tx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err := w.UpdateCurrent()
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Failed updating worker's state", "Error", err)
|
|
|
|
}
|
|
|
|
return selected, unselected, invalid
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
|
|
|
|
snap := w.current.state.Snapshot()
|
|
|
|
|
|
|
|
receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config{})
|
|
|
|
if err != nil {
|
|
|
|
w.current.state.RevertToSnapshot(snap)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
w.current.txs = append(w.current.txs, tx)
|
|
|
|
w.current.receipts = append(w.current.receipts, receipt)
|
|
|
|
|
|
|
|
return receipt.Logs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CommitTransactions commits transactions.
|
|
|
|
func (w *Worker) CommitTransactions(txs types.Transactions) error {
|
|
|
|
if w.current.gasPool == nil {
|
|
|
|
w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit)
|
|
|
|
}
|
|
|
|
for _, tx := range txs {
|
|
|
|
snap := w.current.state.Snapshot()
|
|
|
|
_, err := w.commitTransaction(tx, w.coinbase)
|
|
|
|
if err != nil {
|
|
|
|
w.current.state.RevertToSnapshot(snap)
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateCurrent updates ...
|
|
|
|
func (w *Worker) UpdateCurrent() error {
|
|
|
|
parent := w.chain.CurrentBlock()
|
|
|
|
num := parent.Number()
|
|
|
|
timestamp := time.Now().Unix()
|
|
|
|
header := &types.Header{
|
|
|
|
ParentHash: parent.Hash(),
|
|
|
|
Number: num.Add(num, common.Big1),
|
|
|
|
GasLimit: core.CalcGasLimit(parent, w.gasFloor, w.gasCeil),
|
|
|
|
Time: big.NewInt(timestamp),
|
|
|
|
ShardID: types.EncodeShardID(w.chain.ShardID()),
|
|
|
|
}
|
|
|
|
return w.makeCurrent(parent, header)
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeCurrent creates a new environment for the current cycle.
|
|
|
|
func (w *Worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
|
|
|
state, err := w.chain.StateAt(parent.Root())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
env := &environment{
|
|
|
|
state: state,
|
|
|
|
header: header,
|
|
|
|
}
|
|
|
|
|
|
|
|
w.current = env
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCurrentState ...
|
|
|
|
func (w *Worker) GetCurrentState() *state.StateDB {
|
|
|
|
return w.current.state
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCurrentReceipts ...
|
|
|
|
func (w *Worker) GetCurrentReceipts() []*types.Receipt {
|
|
|
|
return w.current.receipts
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit ...
|
|
|
|
func (w *Worker) Commit() (*types.Block, error) {
|
|
|
|
s := w.current.state.Copy()
|
|
|
|
block, err := w.engine.Finalize(w.chain, w.current.header, s, w.current.txs, w.current.receipts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return block, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// New ...
|
|
|
|
func New(config *params.ChainConfig, chain *core.BlockChain, engine consensus.Engine, coinbase common.Address) *Worker {
|
|
|
|
worker := &Worker{
|
|
|
|
config: config,
|
|
|
|
chain: chain,
|
|
|
|
engine: engine,
|
|
|
|
}
|
|
|
|
worker.gasFloor = 0
|
|
|
|
worker.gasCeil = 1000000000000000
|
|
|
|
worker.coinbase = coinbase
|
|
|
|
|
|
|
|
parent := worker.chain.CurrentBlock()
|
|
|
|
num := parent.Number()
|
|
|
|
timestamp := time.Now().Unix()
|
|
|
|
header := &types.Header{
|
|
|
|
ParentHash: parent.Hash(),
|
|
|
|
Number: num.Add(num, common.Big1),
|
|
|
|
GasLimit: core.CalcGasLimit(parent, worker.gasFloor, worker.gasCeil),
|
|
|
|
Time: big.NewInt(timestamp),
|
|
|
|
ShardID: types.EncodeShardID(worker.chain.ShardID()),
|
|
|
|
}
|
|
|
|
worker.makeCurrent(parent, header)
|
|
|
|
|
|
|
|
return worker
|
|
|
|
}
|