-
Notifications
You must be signed in to change notification settings - Fork 0
/
sealer.go
382 lines (338 loc) · 10.7 KB
/
sealer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package minerva
import (
crand "crypto/rand"
"math"
"math/big"
"math/rand"
"runtime"
"sync"
"github.com/taiyuechain/taipublicchain/common"
"github.com/taiyuechain/taipublicchain/consensus"
"github.com/taiyuechain/taipublicchain/core/types"
"github.com/taiyuechain/taipublicchain/log"
)
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
func (m *Minerva) Seal(chain consensus.SnailChainReader, block *types.SnailBlock, stop <-chan struct{}) (*types.SnailBlock, error) {
// If we're running a fake PoW, simply return a 0 nonce immediately
log.Debug("? in Seal ? ")
if m.config.PowMode == ModeFake || m.config.PowMode == ModeFullFake {
header := block.Header()
header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
return block.WithSeal(header), nil
}
// If we're running a shared PoW, delegate sealing to it
if m.shared != nil {
return m.shared.Seal(chain, block, stop)
}
// Create a runner and the multiple search threads it directs
abort := make(chan struct{})
found := make(chan *types.SnailBlock)
m.lock.Lock()
threads := m.threads
if m.rand == nil {
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
m.lock.Unlock()
return nil, err
}
m.rand = rand.New(rand.NewSource(seed.Int64()))
}
m.lock.Unlock()
if threads == 0 {
threads = runtime.NumCPU()
}
if threads < 0 {
threads = 0 // Allows disabling local mining without extra logic around local/remote
}
var pend sync.WaitGroup
for i := 0; i < threads; i++ {
pend.Add(1)
go func(id int, nonce uint64) {
defer pend.Done()
m.mineSnail(block, id, nonce, abort, found)
}(i, uint64(m.rand.Int63()))
}
// Wait until sealing is terminated or a nonce is found
var result *types.SnailBlock
select {
case <-stop:
// Outside abort, stop all miner threads
close(abort)
//TODO found function
case <-m.update:
// Thread count was changed on user request, restart
close(abort)
pend.Wait()
return m.Seal(chain, block, stop)
}
// Wait for all miners to terminate and return the block
pend.Wait()
return result, nil
}
// ConSeal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
func (m *Minerva) ConSeal(chain consensus.SnailChainReader, block *types.SnailBlock, stop <-chan struct{}, send chan *types.SnailBlock) {
// If we're running a fake PoW, simply return a 0 nonce immediately
if m.config.PowMode == ModeFake || m.config.PowMode == ModeFullFake {
header := block.Header()
header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
send <- block.WithSeal(header)
log.Debug(" ------- fake mode ----- ", "fb number", block.FastNumber(), "threads", m.threads)
return
}
// If we're running a shared PoW, delegate sealing to it
if m.shared != nil {
m.shared.ConSeal(chain, block, stop, send)
}
// Create a runner and the multiple search threads it directs
abort := make(chan struct{})
found := make(chan *types.SnailBlock)
m.lock.Lock()
threads := m.threads
if m.rand == nil {
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
m.lock.Unlock()
send <- nil
//return nil, err
}
m.rand = rand.New(rand.NewSource(seed.Int64()))
}
m.lock.Unlock()
if threads == 0 {
cpuNumber := runtime.NumCPU()
log.Info("Seal get cpu number", "number", cpuNumber)
// remain one cpu to process fast block
threads = cpuNumber - 1
if threads <= 0 {
threads = 1
}
}
if threads < 0 {
threads = 0 // Allows disabling local mining without extra logic around local/remote
//log.Error("Stop mining for CPU number less than 2 or set threads number error.")
}
var pend sync.WaitGroup
for i := 0; i < threads; i++ {
pend.Add(1)
go func(id int, nonce uint64) {
defer pend.Done()
m.mineSnail(block, id, nonce, abort, found)
}(i, uint64(m.rand.Int63()))
}
// Wait until sealing is terminated or a nonce is found
var result *types.SnailBlock
mineloop:
for {
select {
case <-stop:
// Outside abort, stop all miner threads
close(abort)
pend.Wait()
break mineloop
case result = <-found:
// One of the threads found a block or fruit return it
send <- result
if block.Fruits() != nil {
if !result.IsFruit() {
// stop threads when get a block, wait for outside abort when result is fruit
close(abort)
pend.Wait()
break mineloop
}
} else {
close(abort)
pend.Wait()
break mineloop
}
break
case <-m.update:
// Thread count was changed on user request, restart
close(abort)
pend.Wait()
m.ConSeal(chain, block, stop, send)
break mineloop
}
}
// Wait for all miners to terminate and return the block
//send <- result
//return result, nil
}
func (m *Minerva) mineSnail(block *types.SnailBlock, id int, seed uint64, abort chan struct{}, found chan *types.SnailBlock) {
// Extract some data from the header
var (
header = block.Header()
hash = header.HashNoNonce().Bytes()
target = new(big.Int).Div(maxUint128, header.Difficulty)
fruitTarget = new(big.Int).Div(maxUint128, header.FruitDifficulty)
dataset = m.getDataset(block.Number().Uint64())
)
log.Debug("start mine,", "epoch is:", block.Number().Uint64()/epochLength)
// Start generating random nonces until we abort or find a good one
var (
attempts = int64(0)
nonce = seed
)
if dataset == nil {
log.Error("get dataset is nil")
return
}
logger := log.New("miner", id)
log.Trace("mineSnail", "miner", id, "block num", block.Number(), "fb num", block.FastNumber())
logger.Trace("Started truehash search for new nonces", "seed", seed)
search:
for {
select {
case <-abort:
// Mining terminated, update stats and abort
logger.Trace("m nonce search aborted", "attempts", nonce-seed)
m.hashrate.Mark(attempts)
break search
default:
// We don't have to update hash rate on every nonce, so update after after 2^X nonces
attempts++
if (attempts % (1 << 12)) == 0 {
m.hashrate.Mark(attempts)
attempts = 0
}
// Compute the PoW value of this nonce
digest, result := truehashFull(dataset.dataset, hash, nonce)
headResult := result[:16]
if new(big.Int).SetBytes(headResult).Cmp(target) <= 0 {
// Correct nonce found, create a new header with it
if block.Fruits() != nil {
header = types.CopySnailHeader(header)
header.Nonce = types.EncodeNonce(nonce)
header.MixDigest = common.BytesToHash(digest)
// Seal and return a block (if still needed)
//set signs is nill
blockR := block.WithSeal(header)
blockR.SetSnailBlockSigns(nil)
select {
case found <- blockR:
logger.Trace("Truehash nonce found and reported", "attempts", nonce-seed, "nonce", nonce)
case <-abort:
logger.Trace("Truehash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
}
break search
}
} else {
lastResult := result[16:]
if header.FastNumber.Uint64() != 0 {
if new(big.Int).SetBytes(lastResult).Cmp(fruitTarget) <= 0 {
// last 128 bit < Dpf, get a fruit
header = types.CopySnailHeader(header)
header.Nonce = types.EncodeNonce(nonce)
header.MixDigest = common.BytesToHash(digest)
//log.Debug("sealer mineSnail", "miner fruit fb", header.Number)
// set fruits
//block.SetSnailBlockFruits(nil)
blockR := block.WithSeal(header)
blockR.SetSnailBlockFruits(nil)
// Seal and return a block (if still needed)
select {
case found <- blockR:
logger.Trace("IsFruit nonce found and reported", "attempts", nonce-seed, "nonce", nonce)
case <-abort:
logger.Trace("IsFruit nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
}
}
}
}
nonce++
}
}
// Datasets are unmapped in a finalizer. Ensure that the dataset stays live
// during sealing so it's not unmapped while being read.
runtime.KeepAlive(dataset)
}
func (d *Dataset) truehashTableInit(tableLookup []uint64) {
log.Debug("truehashTableInit start ")
var table [TBLSIZE * DATALENGTH * PMTSIZE]uint32
for k := 0; k < TBLSIZE; k++ {
for x := 0; x < DATALENGTH*PMTSIZE; x++ {
table[k*DATALENGTH*PMTSIZE+x] = tableOrg[k][x]
}
//fmt.Printf("%d,", k+1)
}
genLookupTable(tableLookup[:], table[:])
}
func (d *Dataset) updateLookupTBL(plookupTbl []uint64, headershash *[STARTUPDATENUM][]byte) (bool, []uint64, string) {
const offsetCnst = 0x7
const skipCnst = 0x3
var offset [OFF_SKIP_LEN]int
var skip [OFF_SKIP_LEN]int
var cont string
//local way
if len(headershash[0]) == 0 {
log.Error("snail block head hash is nil ")
return false, nil, ""
}
//get offset cnst 8192 lenght
for i := 0; i < OFF_CYCLE_LEN; i++ {
var val []byte
val = headershash[i]
offset[i*4] = (int(val[0]) & offsetCnst) - 4
offset[i*4+1] = (int(val[1]) & offsetCnst) - 4
offset[i*4+2] = (int(val[2]) & offsetCnst) - 4
offset[i*4+3] = (int(val[3]) & offsetCnst) - 4
//cont += header.Hash().String()
}
//get skip cnst 2048 lenght
for i := 0; i < SKIP_CYCLE_LEN; i++ {
var val []byte
val = headershash[i+OFF_CYCLE_LEN]
for k := 0; k < 16; k++ {
skip[i*16+k] = (int(val[k]) & skipCnst) + 1
}
}
ds := d.UpdateTBL(offset, skip, plookupTbl)
return true, ds, cont
}
//UpdateTBL Update dataset information
func (d *Dataset) UpdateTBL(offset [OFF_SKIP_LEN]int, skip [OFF_SKIP_LEN]int, plookupTbl []uint64) []uint64 {
lktWz := uint32(DATALENGTH / 64)
lktSz := uint32(DATALENGTH) * lktWz
for k := 0; k < TBLSIZE; k++ {
plkt := uint32(k) * lktSz
for x := 0; x < DATALENGTH; x++ {
idx := k*DATALENGTH + x
pos := offset[idx] + x
sk := skip[idx]
y := pos - sk*PMTSIZE/2
c := 0
for i := 0; i < PMTSIZE; i++ {
if y >= 0 && y < SKIP_CYCLE_LEN {
vI := uint32(y / 64)
vR := uint32(y % 64)
plookupTbl[plkt+vI] |= 1 << vR
c = c + 1
}
y = y + sk
}
if c == 0 {
vI := uint32(x / 64)
vR := uint32(x % 64)
plookupTbl[plkt+vI] |= 1 << vR
}
plkt += lktWz
}
}
return plookupTbl
}