-
Notifications
You must be signed in to change notification settings - Fork 3
/
pbft.go
438 lines (388 loc) · 13.8 KB
/
pbft.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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
package pbft
import (
"bytes"
"errors"
"io"
"math/big"
_ "math/rand"
"sync"
"time"
"runtime"
"fmt"
"sort"
"strings"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
_ "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
_ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
_ "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
)
var (
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
maxClients = 4
uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
)
var (
// errUnknownBlock is returned when the list of signers is requested for a block
// that is not part of the local blockchain.
errUnknownBlock = errors.New("unknown block")
// errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
errUnauthorizedSigner = errors.New("unauthorized signer")
// errMissingVanity is returned if a block's extra-data section is shorter than
// 32 bytes, which is required to store the signer vanity.
errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
// errMissingSignature is returned if a block's extra-data section doesn't seem
// to contain a 65 byte secp256k1 signature.
errMissingSignature = errors.New("extra-data 65 byte signature suffix missing")
// errInvalidMixDigest is returned if a block's mix digest is non-zero.
errInvalidMixDigest = errors.New("non-zero mix digest")
// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
errInvalidUncleHash = errors.New("non empty uncle hash")
)
func trace() {
pc := make([]uintptr, 10) // at least 1 entry needed
runtime.Callers(2, pc)
f := runtime.FuncForPC(pc[0])
file, line := f.FileLine(pc[0])
fmt.Printf("%s:%d %s\n", file, line, f.Name())
}
type SignerFn func(accounts.Account, string, []byte) ([]byte, error)
type PbftClients []common.Address
func (s PbftClients) Len() int { return len(s) }
func (s PbftClients) Less(i, j int) bool { return strings.Compare(string(s[i].Bytes()), string(s[j].Bytes())) < 0 }
func (s PbftClients) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type Pbft struct {
config *params.PbftConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
clients PbftClients //signer list - should be ordered
signer common.Address // Ethereum address of the signing key
signFn SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields
}
func New(config *params.PbftConfig, db ethdb.Database) *Pbft {
trace()
// Set any missing consensus parameters to their defaults
conf := *config
return &Pbft{
config: &conf,
db: db,
clients: make([]common.Address, 0), //signer list - should be ordered
}
}
func (p *Pbft) AddClient(client common.Address) bool {
trace()
if len(p.clients) == maxClients {
return false
}
if len(p.clients)+1 == cap(p.clients) {
clients := make([]common.Address, len(p.clients), len(p.clients)+1)
copy(clients, p.clients)
p.clients = clients
}
p.clients = append(p.clients, client)
sort.Sort(p.clients)
fmt.Printf("===============%x\n",p.clients)
return true
}
func (p *Pbft) IsPrimary(producer common.Address, blockNumber uint64) bool {
trace()
fmt.Printf("===============%x\n",blockNumber)
fmt.Printf("===============%x\n",producer)
fmt.Printf("===============%x\n",p.clients)
fmt.Printf("===============%x\n",len(p.clients))
peerCount := len(p.clients)
if p.clients[blockNumber % uint64(peerCount)] == producer {
if peerCount == 1 {
return true
} else {
if p.clients[blockNumber % uint64(peerCount)] == producer {
return true
}
}
}
return false
}
func PbftRLP(header *types.Header) []byte {
trace()
b := new(bytes.Buffer)
encodeSigHeader(b, header)
return b.Bytes()
}
func encodeSigHeader(w io.Writer, header *types.Header) {
trace()
err := rlp.Encode(w, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
})
if err != nil {
panic("can't encode: " + err.Error())
}
}
// SealHash returns the hash of a block prior to it being sealed.
func SealHash(header *types.Header) (hash common.Hash) {
trace()
hasher := sha3.NewLegacyKeccak256()
encodeSigHeader(hasher, header)
hasher.Sum(hash[:0])
return hash
}
func (p *Pbft) Authorize(signer common.Address, signFn SignerFn) {
trace()
p.lock.Lock()
defer p.lock.Unlock()
p.signer = signer
p.signFn = signFn
}
// Author retrieves the Ethereum address of the account that minted the given
// block, which may be different from the header's coinbase if a consensus
// engine is based on signatures.
func (p *Pbft) Author(header *types.Header) (common.Address, error) {
trace()
return header.Coinbase, nil
}
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
func (p *Pbft) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
trace()
return p.verifyHeader(chain, header, nil)
}
func (p *Pbft) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
trace()
if header.Number == nil {
return errUnknownBlock
}
number := header.Number.Uint64()
// Don't waste time checking blocks from the future
if header.Time > uint64(time.Now().Unix()) {
return consensus.ErrFutureBlock
}
/*
// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) {
return errInvalidVote
}
if checkpoint && !bytes.Equal(header.Nonce[:], nonceDropVote) {
return errInvalidCheckpointVote
}
*/
// Check that the extra-data contains both the vanity and signature
if len(header.Extra) < extraVanity {
return errMissingVanity
}
if len(header.Extra) < extraVanity+extraSeal {
return errMissingSignature
}
/*
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
signersBytes := len(header.Extra) - extraVanity - extraSeal
if !checkpoint && signersBytes != 0 {
return errExtraSigners
}
if checkpoint && signersBytes%common.AddressLength != 0 {
return errInvalidCheckpointSigners
}
*/
// Ensure that the mix digest is zero as we don't have fork protection currently
if header.MixDigest != (common.Hash{}) {
return errInvalidMixDigest
}
// Ensure that the block doesn't contain any uncles which are meaningless in PoA
if header.UncleHash != uncleHash {
return errInvalidUncleHash
}
/*
// Ensure that the block's difficulty is meaningful (may not be correct at this point)
if number > 0 {
if header.Difficulty == nil || (header.Difficulty.Cmp(diffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) {
return errInvalidDifficulty
}
}
*/
// If all checks passed, validate any special fields for hard forks
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
return err
}
// All basic checks passed, verify cascading fields
if ok := p.IsPrimary(header.Coinbase, number); !ok {
fmt.Printf("c===Out turn",p.clients)
return errUnauthorizedSigner
}
fmt.Printf("c===good pass",p.clients)
return nil
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of
// the input slice).
func (p *Pbft) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
trace()
abort := make(chan struct{})
results := make(chan error, len(headers))
go func() {
for i, header := range headers {
err := p.verifyHeader(chain, header, headers[:i])
select {
case <-abort:
return
case results <- err:
}
}
}()
return abort, results
}
// VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine.
func (p *Pbft) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
trace()
return nil
}
// VerifySeal checks whether the crypto seal on a header is valid according to
// the consensus rules of the given engine.
func (p *Pbft) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
trace()
return nil
}
func CalcDifficulty() *big.Int {
return big.NewInt(10)
}
// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
func (p *Pbft) Prepare(chain consensus.ChainReader, header *types.Header) error {
trace()
// If the block isn't a checkpoint, cast a random vote (good enough for now)
//header.Coinbase = common.Address{}
header.Nonce = types.BlockNonce{}
number := header.Number.Uint64()
// Set the correct difficulty
header.Difficulty = CalcDifficulty()
// Ensure the extra data has all it's components
if len(header.Extra) < extraVanity {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
}
header.Extra = header.Extra[:extraVanity]
header.Extra = append(header.Extra, make([]byte, extraSeal)...)
// Mix digest is reserved for now, set to empty
header.MixDigest = common.Hash{}
// Ensure the timestamp has the correct delay
parent := chain.GetHeader(header.ParentHash, number-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
header.Time = parent.Time + p.config.Period
if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
return nil
}
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
func (p *Pbft) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
trace()
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
// Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts), nil
}
// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
func (p *Pbft) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
trace()
header := block.Header()
// Sealing the genesis block is not supported
number := header.Number.Uint64()
if number == 0 {
return errUnknownBlock
}
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
if p.config.Period == 0 && len(block.Transactions()) == 0 {
log.Info("Sealing paused, waiting for transactions")
return nil
}
// Don't hold the signer fields for the entire sealing procedure
p.lock.RLock()
signer, signFn := p.signer, p.signFn
p.lock.RUnlock()
if ok := p.IsPrimary(signer, number); !ok {
fmt.Printf("Out turn %x\n",number)
return errUnauthorizedSigner
}
// Sweet, the protocol permits us to sign the block, wait for our time
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
// Sign all the things!
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypePbft, PbftRLP(header))
if err != nil {
return err
}
log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
go func() {
select {
case <-stop:
return
case <-time.After(delay):
}
select {
case results <- block.WithSeal(header):
default:
log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header))
}
}()
return nil
}
// SealHash returns the hash of a block prior to it being sealed.
func (p *Pbft) SealHash(header *types.Header) common.Hash {
trace()
return SealHash(header)
}
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have.
func (p *Pbft) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
trace()
return new(big.Int).Set(big.NewInt(1))
}
// APIs returns the RPC APIs this consensus engine provides.
func (p *Pbft) APIs(chain consensus.ChainReader) []rpc.API {
trace()
return []rpc.API{{
Namespace: "pbft",
Version: "0.1",
Service: &API{chain: chain, pbft: p},
Public: false,
}}
}
// Close terminates any background threads maintained by the consensus engine.
func (p *Pbft) Close() error {
trace()
return nil
}