github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/api/miner.go (about)

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  
     7  	chainjson "github.com/bytom/bytom/encoding/json"
     8  	"github.com/bytom/bytom/errors"
     9  	"github.com/bytom/bytom/event"
    10  	"github.com/bytom/bytom/protocol/bc"
    11  	"github.com/bytom/bytom/protocol/bc/types"
    12  )
    13  
    14  // BlockHeaderJSON struct provides support for get work in json format, when it also follows
    15  // BlockHeader structure
    16  type BlockHeaderJSON struct {
    17  	Version           uint64                 `json:"version"`             // The version of the block.
    18  	Height            uint64                 `json:"height"`              // The height of the block.
    19  	PreviousBlockHash bc.Hash                `json:"previous_block_hash"` // The hash of the previous block.
    20  	Timestamp         uint64                 `json:"timestamp"`           // The time of the block in seconds.
    21  	Nonce             uint64                 `json:"nonce"`               // Nonce used to generate the block.
    22  	Bits              uint64                 `json:"bits"`                // Difficulty target for the block.
    23  	BlockCommitment   *types.BlockCommitment `json:"block_commitment"`    // Block commitment
    24  }
    25  
    26  type CoinbaseArbitrary struct {
    27  	Arbitrary chainjson.HexBytes `json:"arbitrary"`
    28  }
    29  
    30  func (a *API) getCoinbaseArbitrary() Response {
    31  	arbitrary := a.wallet.AccountMgr.GetCoinbaseArbitrary()
    32  	resp := &CoinbaseArbitrary{
    33  		Arbitrary: arbitrary,
    34  	}
    35  	return NewSuccessResponse(resp)
    36  }
    37  
    38  // setCoinbaseArbitrary add arbitary data to the reserved coinbase data.
    39  // check function createCoinbaseTx in mining/mining.go for detail.
    40  // arbitraryLenLimit is 107 and can be calculated by:
    41  // 	maxHeight := ^uint64(0)
    42  // 	reserved := append([]byte{0x00}, []byte(strconv.FormatUint(maxHeight, 10))...)
    43  // 	arbitraryLenLimit := consensus.CoinbaseArbitrarySizeLimit - len(reserved)
    44  func (a *API) setCoinbaseArbitrary(ctx context.Context, req CoinbaseArbitrary) Response {
    45  	arbitraryLenLimit := 107
    46  	if len(req.Arbitrary) > arbitraryLenLimit {
    47  		err := errors.New("Arbitrary exceeds limit: " + strconv.FormatUint(uint64(arbitraryLenLimit), 10))
    48  		return NewErrorResponse(err)
    49  	}
    50  	a.wallet.AccountMgr.SetCoinbaseArbitrary(req.Arbitrary)
    51  	return a.getCoinbaseArbitrary()
    52  }
    53  
    54  // getWork gets work in compressed protobuf format
    55  func (a *API) getWork() Response {
    56  	work, err := a.GetWork()
    57  	if err != nil {
    58  		return NewErrorResponse(err)
    59  	}
    60  	return NewSuccessResponse(work)
    61  }
    62  
    63  // getWorkJSON gets work in json format
    64  func (a *API) getWorkJSON() Response {
    65  	work, err := a.GetWorkJSON()
    66  	if err != nil {
    67  		return NewErrorResponse(err)
    68  	}
    69  	return NewSuccessResponse(work)
    70  }
    71  
    72  // SubmitBlockReq is req struct for submit-block API
    73  type SubmitBlockReq struct {
    74  	Block *types.Block `json:"raw_block"`
    75  }
    76  
    77  // submitBlock trys to submit a raw block to the chain
    78  func (a *API) submitBlock(ctx context.Context, req *SubmitBlockReq) Response {
    79  	isOrphan, err := a.chain.ProcessBlock(req.Block)
    80  	if err != nil {
    81  		return NewErrorResponse(err)
    82  	}
    83  
    84  	if isOrphan {
    85  		return NewErrorResponse(errors.New("block submitted is orphan"))
    86  	}
    87  
    88  	if err = a.eventDispatcher.Post(event.NewMinedBlockEvent{Block: *req.Block}); err != nil {
    89  		return NewErrorResponse(err)
    90  	}
    91  
    92  	return NewSuccessResponse(true)
    93  }
    94  
    95  // SubmitWorkReq is req struct for submit-work API
    96  type SubmitWorkReq struct {
    97  	BlockHeader *types.BlockHeader `json:"block_header"`
    98  }
    99  
   100  // submitWork submits work in compressed protobuf format
   101  func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
   102  	if err := a.SubmitWork(req.BlockHeader); err != nil {
   103  		return NewErrorResponse(err)
   104  	}
   105  	return NewSuccessResponse(true)
   106  }
   107  
   108  // SubmitWorkJSONReq is req struct for submit-work-json API
   109  type SubmitWorkJSONReq struct {
   110  	BlockHeader *BlockHeaderJSON `json:"block_header"`
   111  }
   112  
   113  // submitWorkJSON submits work in json format
   114  func (a *API) submitWorkJSON(ctx context.Context, req *SubmitWorkJSONReq) Response {
   115  	bh := &types.BlockHeader{
   116  		Version:           req.BlockHeader.Version,
   117  		Height:            req.BlockHeader.Height,
   118  		PreviousBlockHash: req.BlockHeader.PreviousBlockHash,
   119  		Timestamp:         req.BlockHeader.Timestamp,
   120  		Nonce:             req.BlockHeader.Nonce,
   121  		Bits:              req.BlockHeader.Bits,
   122  		BlockCommitment:   *req.BlockHeader.BlockCommitment,
   123  	}
   124  
   125  	if err := a.SubmitWork(bh); err != nil {
   126  		return NewErrorResponse(err)
   127  	}
   128  	return NewSuccessResponse(true)
   129  }
   130  
   131  // GetWorkResp is resp struct for get-work API
   132  type GetWorkResp struct {
   133  	BlockHeader *types.BlockHeader `json:"block_header"`
   134  	Seed        *bc.Hash           `json:"seed"`
   135  }
   136  
   137  // GetWork gets work in compressed protobuf format
   138  func (a *API) GetWork() (*GetWorkResp, error) {
   139  	bh, err := a.miningPool.GetWork()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return &GetWorkResp{
   150  		BlockHeader: bh,
   151  		Seed:        seed,
   152  	}, nil
   153  }
   154  
   155  // GetWorkJSONResp is resp struct for get-work-json API
   156  type GetWorkJSONResp struct {
   157  	BlockHeader *BlockHeaderJSON `json:"block_header"`
   158  	Seed        *bc.Hash         `json:"seed"`
   159  }
   160  
   161  // GetWorkJSON gets work in json format
   162  func (a *API) GetWorkJSON() (*GetWorkJSONResp, error) {
   163  	bh, err := a.miningPool.GetWork()
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	return &GetWorkJSONResp{
   174  		BlockHeader: &BlockHeaderJSON{
   175  			Version:           bh.Version,
   176  			Height:            bh.Height,
   177  			PreviousBlockHash: bh.PreviousBlockHash,
   178  			Timestamp:         bh.Timestamp,
   179  			Nonce:             bh.Nonce,
   180  			Bits:              bh.Bits,
   181  			BlockCommitment:   &bh.BlockCommitment,
   182  		},
   183  		Seed: seed,
   184  	}, nil
   185  }
   186  
   187  // SubmitWork tries to submit work to the chain
   188  func (a *API) SubmitWork(bh *types.BlockHeader) error {
   189  	return a.miningPool.SubmitWork(bh)
   190  }
   191  
   192  func (a *API) setMining(in struct {
   193  	IsMining bool `json:"is_mining"`
   194  }) Response {
   195  	if in.IsMining {
   196  		if _, err := a.wallet.AccountMgr.GetMiningAddress(); err != nil {
   197  			return NewErrorResponse(errors.New("Mining address does not exist"))
   198  		}
   199  		return a.startMining()
   200  	}
   201  	return a.stopMining()
   202  }
   203  
   204  func (a *API) startMining() Response {
   205  	a.cpuMiner.Start()
   206  	if !a.IsMining() {
   207  		return NewErrorResponse(errors.New("Failed to start mining"))
   208  	}
   209  	return NewSuccessResponse("")
   210  }
   211  
   212  func (a *API) stopMining() Response {
   213  	a.cpuMiner.Stop()
   214  	if a.IsMining() {
   215  		return NewErrorResponse(errors.New("Failed to stop mining"))
   216  	}
   217  	return NewSuccessResponse("")
   218  }