--auth.disable Disable rpc access authenticate
--chain_id string Select network type
-h, --help help for node
- --mining Enable mining
+ --mining.enable Enable mining
+ --mining.recommit_interval Set mining pool recomit interval in seconds (default 15)
--p2p.dial_timeout int Set dial timeout (default 3)
--p2p.handshake_timeout int Set handshake timeout (default 30)
--p2p.laddr string Node listen address.
"github.com/bytom/protocol/bc/types"
)
+var ErrEmptyWorkSubmission = errors.New("empty work submission")
+
// BlockHeaderJSON struct provides support for get work in json format, when it also follows
// BlockHeader structure
type BlockHeaderJSON struct {
// submitWork submits work in compressed protobuf format
func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
+ if req.BlockHeader == nil {
+ return NewErrorResponse(ErrEmptyWorkSubmission)
+ }
+
if err := a.SubmitWork(req.BlockHeader); err != nil {
return NewErrorResponse(err)
}
// submitWorkJSON submits work in json format
func (a *API) submitWorkJSON(ctx context.Context, req *SubmitWorkJSONReq) Response {
+ if req.BlockHeader == nil {
+ return NewErrorResponse(ErrEmptyWorkSubmission)
+ }
+
bh := &types.BlockHeader{
Version: req.BlockHeader.Version,
Height: req.BlockHeader.Height,
func init() {
runNodeCmd.Flags().String("prof_laddr", config.ProfListenAddress, "Use http to profile bytomd programs")
- runNodeCmd.Flags().Bool("mining", config.Mining, "Enable mining")
+
+ runNodeCmd.Flags().Bool("mining.enable", config.Mining.Enable, "Enable mining")
+ runNodeCmd.Flags().Uint64("mining.recommit_interval", config.Mining.RecommitInterval, "Set mining pool recomit interval in seconds")
runNodeCmd.Flags().Bool("simd.enable", config.Simd.Enable, "Enable SIMD mechan for tensority")
Auth *RPCAuthConfig `mapstructure:"auth"`
Web *WebConfig `mapstructure:"web"`
Simd *SimdConfig `mapstructure:"simd"`
+ Mining *MiningConfig `mapstructure:"mining"`
Websocket *WebsocketConfig `mapstructure:"ws"`
}
Auth: DefaultRPCAuthConfig(),
Web: DefaultWebConfig(),
Simd: DefaultSimdConfig(),
+ Mining: DefaultMiningConfig(),
Websocket: DefaultWebsocketConfig(),
}
}
// TCP or UNIX socket address for the profiling server to listen on
ProfListenAddress string `mapstructure:"prof_laddr"`
- Mining bool `mapstructure:"mining"`
-
// Database backend: leveldb | memdb
DBBackend string `mapstructure:"db_backend"`
return BaseConfig{
Moniker: "anonymous",
ProfListenAddress: "",
- Mining: false,
DBBackend: "leveldb",
DBPath: "data",
KeysPath: "keystore",
Enable bool `mapstructure:"enable"`
}
+type MiningConfig struct {
+ Enable bool `mapstructure:"enable"`
+ RecommitInterval uint64 `mapstructure:"recommit_interval"`
+}
+
type WebsocketConfig struct {
MaxNumWebsockets int `mapstructure:"max_num_websockets"`
MaxNumConcurrentReqs int `mapstructure:"max_num_concurrent_reqs"`
}
}
-// Default configurable web parameters.
+// Default configurable blockheader verification parameters.
func DefaultSimdConfig() *SimdConfig {
return &SimdConfig{
Enable: false,
}
}
+// Default configurable mining parameters.
+func DefaultMiningConfig() *MiningConfig {
+ return &MiningConfig{
+ Enable: false,
+ RecommitInterval: 15,
+ }
+}
+
func DefaultWebsocketConfig() *WebsocketConfig {
return &WebsocketConfig{
MaxNumWebsockets: 25,
"github.com/bytom/event"
"github.com/bytom/mining"
"github.com/bytom/protocol"
+ "github.com/bytom/protocol/bc"
"github.com/bytom/protocol/bc/types"
)
// MiningPool is the support struct for p2p mine pool
type MiningPool struct {
- mutex sync.RWMutex
- block *types.Block
- submitCh chan *submitBlockMsg
+ mutex sync.RWMutex
+ blockHeader *types.BlockHeader
+ submitCh chan *submitBlockMsg
+ commitMap map[bc.Hash]([]*types.Tx)
+ recommitInterval time.Duration
chain *protocol.Chain
accountManager *account.Manager
}
// NewMiningPool will create a new MiningPool
-func NewMiningPool(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *MiningPool {
+func NewMiningPool(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher, recommitInterval uint64) *MiningPool {
m := &MiningPool{
- submitCh: make(chan *submitBlockMsg, maxSubmitChSize),
- chain: c,
- accountManager: accountManager,
- txPool: txPool,
- eventDispatcher: dispatcher,
+ submitCh: make(chan *submitBlockMsg, maxSubmitChSize),
+ commitMap: make(map[bc.Hash]([]*types.Tx)),
+ recommitInterval: time.Duration(recommitInterval) * time.Second,
+ chain: c,
+ accountManager: accountManager,
+ txPool: txPool,
+ eventDispatcher: dispatcher,
}
- m.generateBlock()
+ m.generateBlock(true)
go m.blockUpdater()
return m
}
// blockUpdater is the goroutine for keep update mining block
func (m *MiningPool) blockUpdater() {
+ recommitTicker := time.NewTicker(m.recommitInterval)
for {
select {
+ case <-recommitTicker.C:
+ m.generateBlock(false)
+
case <-m.chain.BlockWaiter(m.chain.BestBlockHeight() + 1):
- m.generateBlock()
+ m.generateBlock(true)
case submitMsg := <-m.submitCh:
err := m.submitWork(submitMsg.blockHeader)
if err == nil {
- m.generateBlock()
+ m.generateBlock(true)
}
submitMsg.reply <- err
}
}
// generateBlock generates a block template to mine
-func (m *MiningPool) generateBlock() {
+func (m *MiningPool) generateBlock(isNextHeight bool) {
m.mutex.Lock()
defer m.mutex.Unlock()
+ if isNextHeight {
+ // make a new commitMap, so that the expired map will be deleted(garbage-collected)
+ m.commitMap = make(map[bc.Hash]([]*types.Tx))
+ }
+
block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager)
if err != nil {
log.Errorf("miningpool: failed on create NewBlockTemplate: %v", err)
return
}
- m.block = block
+
+ // The previous memory will be reclaimed by gc
+ m.blockHeader = &block.BlockHeader
+ m.commitMap[block.TransactionsMerkleRoot] = block.Transactions
}
// GetWork will return a block header for p2p mining
func (m *MiningPool) GetWork() (*types.BlockHeader, error) {
- if m.block != nil {
+ if m.blockHeader != nil {
m.mutex.RLock()
defer m.mutex.RUnlock()
- m.block.BlockHeader.Timestamp = uint64(time.Now().Unix())
- bh := m.block.BlockHeader
- return &bh, nil
+ m.blockHeader.Timestamp = uint64(time.Now().Unix())
+ return m.blockHeader, nil
}
return nil, errors.New("no block is ready for mining")
}
m.mutex.Lock()
defer m.mutex.Unlock()
- if m.block == nil || bh.PreviousBlockHash != m.block.PreviousBlockHash {
+ if m.blockHeader == nil || bh.PreviousBlockHash != m.blockHeader.PreviousBlockHash {
return errors.New("pending mining block has been changed")
}
- m.block.Nonce = bh.Nonce
- m.block.Timestamp = bh.Timestamp
- isOrphan, err := m.chain.ProcessBlock(m.block)
+ txs, ok := m.commitMap[bh.TransactionsMerkleRoot]
+ if !ok {
+ return errors.New("TransactionsMerkleRoot not found in history")
+ }
+
+ block := &types.Block{*bh, txs}
+ isOrphan, err := m.chain.ProcessBlock(block)
if err != nil {
return err
}
return errors.New("submit result is orphan")
}
- if err := m.eventDispatcher.Post(event.NewMinedBlockEvent{Block: m.block}); err != nil {
- return err
- }
-
- return nil
+ return m.eventDispatcher.Post(event.NewMinedBlockEvent{Block: block})
}
wallet: wallet,
chain: chain,
txfeed: txFeed,
- miningEnable: config.Mining,
+ miningEnable: config.Mining.Enable,
notificationMgr: notificationMgr,
}
node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, dispatcher)
- node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, dispatcher)
+ node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, dispatcher, config.Mining.RecommitInterval)
node.BaseService = *cmn.NewBaseService(nil, "Node", node)
func mockConfig() *cfg.Config {
var config = cfg.DefaultConfig()
config.Wallet.Disable = false
- config.Mining = true
+ config.Mining.Enable = true
config.ApiAddress = "127.0.0.1:9888"
return config
}