OSDN Git Service

Hulk did something
[bytom/vapor.git] / api / miner.go
1 package api
2
3 import (
4         "context"
5         "strconv"
6
7         chainjson "github.com/vapor/encoding/json"
8         "github.com/vapor/errors"
9         "github.com/vapor/event"
10         "github.com/vapor/protocol/bc"
11         "github.com/vapor/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 }