Skip to content

Commit a6d4d69

Browse files
authored
consensus/pbft: add new field for header.Extra (#72)
* consensus/pbft: add new field for header.Extra * consensus/pbft: add format check for headerExtra * consensus/pbft: fix pr comments * consensus/pbft: ensure the extra data has all it's components * consensus/pbft: FIx test errors
1 parent 52f2e91 commit a6d4d69

File tree

4 files changed

+219
-19
lines changed

4 files changed

+219
-19
lines changed

consensus/pbft/backends/simple/backend.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ import (
3434
"github.com/ethereum/go-ethereum/rlp"
3535
)
3636

37-
const (
38-
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
39-
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
40-
)
41-
4237
func New(config *pbft.Config, eventMux *event.TypeMux, privateKey *ecdsa.PrivateKey, db ethdb.Database) consensus.PBFT {
4338
backend := &simpleBackend{
4439
config: config,

consensus/pbft/backends/simple/engine.go

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ import (
3535
"github.com/ethereum/go-ethereum/rpc"
3636
)
3737

38+
const (
39+
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
40+
extraValidatorSize = 1 // Fixed number of extra-data infix bytes reserved for validator size
41+
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
42+
)
43+
3844
var (
3945
// errUnknownBlock is returned when the list of signers is requested for a block
4046
// that is not part of the local blockchain.
@@ -97,12 +103,8 @@ func (sb *simpleBackend) verifyHeader(chain consensus.ChainReader, header *types
97103
return consensus.ErrFutureBlock
98104
}
99105

100-
// Check that the extra-data contains both the vanity and signature
101-
length := len(header.Extra)
102-
if length < extraVanity+extraSeal {
103-
return errInvalidExtraDataFormat
104-
}
105-
if !validator.ValidExtraData(sb.getValidatorBytes(header)) {
106+
// Ensure that the extra data format is satisfied
107+
if !sb.validExtraFormat(header) {
106108
return errInvalidExtraDataFormat
107109
}
108110

@@ -241,11 +243,11 @@ func (sb *simpleBackend) Prepare(chain consensus.ChainReader, header *types.Head
241243
if parent == nil {
242244
return consensus.ErrUnknownAncestor
243245
}
246+
if !sb.validExtraFormat(parent) {
247+
return errInvalidExtraDataFormat
248+
}
244249
// Ensure the extra data has all it's components
245-
length := len(parent.Extra)
246-
header.Extra = make([]byte, 0)
247-
header.Extra = append(header.Extra, parent.Extra[:length-extraSeal]...)
248-
header.Extra = append(header.Extra, make([]byte, extraSeal)...)
250+
header.Extra = sb.prepareExtra(header, parent)
249251
// use the same difficulty for all blocks
250252
header.Difficulty = defaultDifficulty
251253
return nil
@@ -339,7 +341,10 @@ func (sb *simpleBackend) updateBlock(parent *types.Header, block *types.Block) (
339341
if err != nil {
340342
return nil, err
341343
}
342-
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
344+
345+
start, end := sb.signaturePosition(header)
346+
copy(header.Extra[start:end], sighash)
347+
343348
return block.WithSeal(header), nil
344349
}
345350

@@ -452,9 +457,56 @@ func (sb *simpleBackend) initValidatorSet(chain consensus.ChainReader) error {
452457
return nil
453458
}
454459

455-
func (sb *simpleBackend) getValidatorBytes(header *types.Header) []byte {
460+
func (sb *simpleBackend) validExtraFormat(header *types.Header) bool {
456461
length := len(header.Extra)
457-
return header.Extra[extraVanity : length-extraSeal]
462+
// ensure the bytes is enough
463+
if length < extraVanity+extraValidatorSize+extraSeal {
464+
return false
465+
}
466+
467+
vl := sb.validatorLength(header)
468+
// validator length cannot be 0
469+
if vl == 0 {
470+
return false
471+
}
472+
if length != extraVanity+extraValidatorSize+vl+extraSeal {
473+
return false
474+
}
475+
476+
return true
477+
}
478+
479+
func (sb *simpleBackend) getValidatorBytes(header *types.Header) []byte {
480+
return header.Extra[extraVanity+extraValidatorSize : extraVanity+extraValidatorSize+sb.validatorLength(header)]
481+
}
482+
483+
// prepareExtra creates a copy that includes vanity, validators, and a clean seal for the given header
484+
//
485+
// note that the header.Extra consisted of vanity, validator size, validators, seal, and committed signatures
486+
func (sb *simpleBackend) prepareExtra(header, parent *types.Header) []byte {
487+
buf := make([]byte, 0)
488+
if len(header.Extra) < extraVanity {
489+
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
490+
}
491+
buf = header.Extra[:extraVanity]
492+
493+
buf = append(buf, parent.Extra[extraVanity:extraVanity+extraValidatorSize+sb.validatorLength(parent)]...)
494+
buf = append(buf, make([]byte, extraSeal)...)
495+
return buf
496+
}
497+
498+
// signaturePosition returns start and end position for the given header
499+
func (sb *simpleBackend) signaturePosition(header *types.Header) (int, int) {
500+
start := extraVanity + extraValidatorSize + sb.validatorLength(header)
501+
end := start + extraSeal
502+
return int(start), int(end)
503+
}
504+
505+
// validatorLength returns the validator length for the given header
506+
func (sb *simpleBackend) validatorLength(header *types.Header) int {
507+
validatorSize := int(header.Extra[extraVanity : extraVanity+extraValidatorSize][0])
508+
validatorLength := validatorSize * common.AddressLength
509+
return int(validatorLength)
458510
}
459511

460512
// FIXME: Need to update this for PBFT
@@ -495,6 +547,7 @@ func (sb *simpleBackend) ecrecover(header *types.Header) (common.Address, error)
495547
if len(header.Extra) < extraSeal {
496548
return common.Address{}, consensus.ErrMissingSignature
497549
}
498-
signature := header.Extra[len(header.Extra)-extraSeal:]
550+
start, end := sb.signaturePosition(header)
551+
signature := header.Extra[start:end]
499552
return sb.getSignatureAddress(sigHash(header).Bytes(), signature)
500553
}

consensus/pbft/backends/simple/engine_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ func appendValidators(genesis *core.Genesis, addrs []common.Address) {
8282
}
8383
genesis.ExtraData = genesis.ExtraData[:extraVanity]
8484

85+
validatorSize := byte(len(addrs))
86+
genesis.ExtraData = append(genesis.ExtraData, validatorSize)
87+
8588
for _, addr := range addrs {
8689
genesis.ExtraData = append(genesis.ExtraData, addr[:]...)
8790
}
@@ -428,3 +431,149 @@ OUT3:
428431
}
429432
}
430433
}
434+
435+
func TestPrepareExtra(t *testing.T) {
436+
validatorN := 4
437+
buf := make([]byte, 0)
438+
buf = append(buf, make([]byte, extraVanity)...)
439+
buf = append(buf, byte(validatorN))
440+
buf = append(buf, make([]byte, validatorN*common.AddressLength)...)
441+
buf = append(buf, make([]byte, extraSeal)...)
442+
443+
parentHeader := &types.Header{}
444+
parentHeader.Extra = buf
445+
446+
header := &types.Header{}
447+
header.Extra = common.StringToHash("123").Bytes()
448+
449+
expectedExtra := parentHeader.Extra
450+
copy(expectedExtra[0:extraVanity], header.Extra)
451+
452+
b, _, _ := newSimpleBackend()
453+
extra := b.prepareExtra(header, parentHeader)
454+
if bytes.Compare(extra, expectedExtra) != 0 {
455+
t.Errorf("expected: %v, got: %v", expectedExtra, extra)
456+
}
457+
458+
// append useless information
459+
buf = append(buf, make([]byte, 15)...)
460+
header.Extra = buf
461+
462+
extra = b.prepareExtra(header, parentHeader)
463+
if bytes.Compare(extra, expectedExtra) != 0 {
464+
t.Errorf("expected: %v, got: %v", expectedExtra, extra)
465+
}
466+
}
467+
468+
func TestSignaturePosition(t *testing.T) {
469+
validatorN := 2
470+
buf := make([]byte, 0)
471+
buf = append(buf, common.StringToHash("123").Bytes()...)
472+
buf = append(buf, byte(validatorN))
473+
buf = append(buf, make([]byte, validatorN*common.AddressLength)...)
474+
buf = append(buf, make([]byte, extraSeal)...)
475+
476+
expectedStart := extraVanity + extraValidatorSize + validatorN*common.AddressLength
477+
expectedtEnd := expectedStart + extraSeal
478+
479+
header := &types.Header{}
480+
header.Extra = buf
481+
482+
b, _, _ := newSimpleBackend()
483+
start, end := b.signaturePosition(header)
484+
if expectedStart != start && expectedtEnd != end {
485+
t.Errorf("expected start: %v, got: %v, expected end: %v, got: %v", expectedStart, start, expectedtEnd, end)
486+
}
487+
}
488+
489+
func TestValidExtra(t *testing.T) {
490+
491+
testCases := []struct {
492+
extra []byte
493+
expectedValid bool
494+
}{
495+
{
496+
// normal case
497+
func() []byte {
498+
validatorN := 4
499+
buf := make([]byte, 0)
500+
buf = append(buf, common.StringToHash("123").Bytes()...)
501+
buf = append(buf, byte(validatorN))
502+
buf = append(buf, make([]byte, validatorN*common.AddressLength)...)
503+
buf = append(buf, make([]byte, extraSeal)...)
504+
return buf
505+
}(),
506+
true,
507+
},
508+
{
509+
// missing validator
510+
func() []byte {
511+
validatorN := 4
512+
buf := make([]byte, 0)
513+
buf = append(buf, common.StringToHash("123").Bytes()...)
514+
buf = append(buf, byte(validatorN))
515+
buf = append(buf, make([]byte, extraSeal)...)
516+
return buf
517+
}(),
518+
false,
519+
},
520+
{
521+
// validator N is 0
522+
func() []byte {
523+
validatorN := 0
524+
buf := make([]byte, 0)
525+
buf = append(buf, common.StringToHash("123").Bytes()...)
526+
buf = append(buf, byte(validatorN))
527+
buf = append(buf, make([]byte, validatorN*common.AddressLength)...)
528+
buf = append(buf, make([]byte, extraSeal)...)
529+
return buf
530+
}(),
531+
false,
532+
},
533+
{
534+
// validator N is 0, but have 1 validator in field
535+
func() []byte {
536+
validatorN := 0
537+
buf := make([]byte, 0)
538+
buf = append(buf, common.StringToHash("123").Bytes()...)
539+
buf = append(buf, byte(validatorN))
540+
buf = append(buf, make([]byte, common.AddressLength)...)
541+
buf = append(buf, make([]byte, extraSeal)...)
542+
return buf
543+
}(),
544+
false,
545+
},
546+
{
547+
// missing seal
548+
func() []byte {
549+
validatorN := 4
550+
buf := make([]byte, 0)
551+
buf = append(buf, common.StringToHash("123").Bytes()...)
552+
buf = append(buf, byte(validatorN))
553+
buf = append(buf, make([]byte, validatorN*common.AddressLength)...)
554+
return buf
555+
}(),
556+
false,
557+
},
558+
{
559+
// missing few data
560+
func() []byte {
561+
buf := make([]byte, 0)
562+
return buf
563+
}(),
564+
false,
565+
},
566+
}
567+
568+
b, _, _ := newSimpleBackend()
569+
570+
for _, test := range testCases {
571+
header := &types.Header{}
572+
header.Extra = test.extra
573+
574+
valid := b.validExtraFormat(header)
575+
if valid != test.expectedValid {
576+
t.Errorf("expected: %v, but: %v", test.expectedValid, valid)
577+
}
578+
}
579+
}

consensus/pbft/backends/simulation/backend.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ func AppendValidators(genesis *core.Genesis, addrs []common.Address) {
234234
}
235235
genesis.ExtraData = genesis.ExtraData[:extraVanity]
236236

237+
validatorSize := byte(len(addrs))
238+
genesis.ExtraData = append(genesis.ExtraData, validatorSize)
239+
237240
for _, addr := range addrs {
238241
genesis.ExtraData = append(genesis.ExtraData, addr[:]...)
239242
}

0 commit comments

Comments
 (0)