Skip to content

Commit 57750cb

Browse files
bailantaotaoalanchchen
authored andcommitted
Merge pull request #97 from getamis/feature/use-rlp-for-header-extra-data
core/types: use rlp to encode/decode istanbul extra-data
2 parents be875de + 6f3fbf2 commit 57750cb

File tree

8 files changed

+335
-393
lines changed

8 files changed

+335
-393
lines changed

consensus/istanbul/backend/backend.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,14 @@ func (sb *simpleBackend) Commit(proposal istanbul.Proposal, seals []byte) error
132132
return errInvalidProposal
133133
}
134134

135+
h := block.Header()
135136
// Append seals into extra-data
136-
err := types.AppendIstanbulCommittedSealExtra(block, seals)
137+
err := writeCommittedSeals(h, seals)
137138
if err != nil {
138139
return err
139140
}
141+
// update block's header
142+
block = block.WithSeal(h)
140143

141144
sb.logger.Info("Committed", "address", sb.Address(), "hash", proposal.Hash(), "number", proposal.Number().Uint64())
142145
// - if the proposed and committed blocks are the same, send the proposed hash

consensus/istanbul/backend/backend_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func TestCommit(t *testing.T) {
134134
},
135135
{
136136
// invalid signature
137-
types.ErrInvalidIstanbulCommittedSeal,
137+
errInvalidCommittedSeals,
138138
nil,
139139
func() *types.Block {
140140
chain, engine := newBlockChain(1)

consensus/istanbul/backend/engine.go

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (sb *simpleBackend) verifyHeader(chain consensus.ChainReader, header *types
119119
}
120120

121121
// Ensure that the extra data format is satisfied
122-
if err := types.ValidateIstanbulSeal(header); err != nil {
122+
if _, err := types.ExtractIstanbulExtra(header); err != nil {
123123
return errInvalidExtraDataFormat
124124
}
125125

@@ -253,7 +253,10 @@ func (sb *simpleBackend) verifyCommittedSeals(chain consensus.ChainReader, heade
253253
return err
254254
}
255255

256-
extra := types.ExtractToIstanbul(header)
256+
extra, err := types.ExtractIstanbulExtra(header)
257+
if err != nil {
258+
return err
259+
}
257260
// The length of Committed seals should be larger than 0
258261
if len(extra.CommittedSeal) == 0 {
259262
return errEmptyCommittedSeals
@@ -352,7 +355,11 @@ func (sb *simpleBackend) Prepare(chain consensus.ChainReader, header *types.Head
352355
}
353356

354357
// add validators in snapshot to extraData's validators section
355-
header.Extra = types.PrepareIstanbulExtra(header, snap.validators())
358+
extra, err := prepareExtra(header, snap.validators())
359+
if err != nil {
360+
return err
361+
}
362+
header.Extra = extra
356363
return nil
357364
}
358365

@@ -450,13 +457,15 @@ func (sb *simpleBackend) updateBlock(parent *types.Header, block *types.Block) (
450457
header.Time = big.NewInt(time)
451458
}
452459
// sign the hash
453-
sighash, err := sb.Sign(sigHash(header).Bytes())
460+
seal, err := sb.Sign(sigHash(header).Bytes())
454461
if err != nil {
455462
return nil, err
456463
}
457464

458-
index := types.ExtractToIstanbulIndex(header)
459-
copy(header.Extra[index.Seal:index.Seal+types.IstanbulExtraSeal], sighash)
465+
err = writeSeal(header, seal)
466+
if err != nil {
467+
return nil, err
468+
}
460469

461470
return block.WithSeal(header), nil
462471
}
@@ -562,8 +571,11 @@ func (sb *simpleBackend) snapshot(chain consensus.ChainReader, number uint64, ha
562571
if err := sb.VerifyHeader(chain, genesis, false); err != nil {
563572
return nil, err
564573
}
565-
istanbul := types.ExtractToIstanbul(genesis)
566-
snap = newSnapshot(sb.config.Epoch, 0, genesis.Hash(), validator.NewSet(istanbul.Validators, sb.config.ProposerPolicy))
574+
istanbulExtra, err := types.ExtractIstanbulExtra(genesis)
575+
if err != nil {
576+
return nil, err
577+
}
578+
snap = newSnapshot(sb.config.Epoch, 0, genesis.Hash(), validator.NewSet(istanbulExtra.Validators, sb.config.ProposerPolicy))
567579
if err := snap.store(sb.db); err != nil {
568580
return nil, err
569581
}
@@ -620,32 +632,90 @@ func (sb *simpleBackend) snapshot(chain consensus.ChainReader, number uint64, ha
620632
func sigHash(header *types.Header) (hash common.Hash) {
621633
hasher := sha3.NewKeccak256()
622634

623-
index := types.ExtractToIstanbulIndex(header)
624-
rlp.Encode(hasher, []interface{}{
625-
header.ParentHash,
626-
header.UncleHash,
627-
header.Coinbase,
628-
header.Root,
629-
header.TxHash,
630-
header.ReceiptHash,
631-
header.Bloom,
632-
header.Difficulty,
633-
header.Number,
634-
header.GasLimit,
635-
header.GasUsed,
636-
header.Time,
637-
header.Extra[:index.Seal], // Yes, this will panic if extra is too short
638-
header.MixDigest,
639-
header.Nonce,
640-
})
635+
// Clean seal is required for calculating proposer seal.
636+
rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false))
641637
hasher.Sum(hash[:0])
642638
return hash
643639
}
644640

645641
// ecrecover extracts the Ethereum account address from a signed header.
646642
func ecrecover(header *types.Header) (common.Address, error) {
647643
// Retrieve the signature from the header extra-data
648-
index := types.ExtractToIstanbulIndex(header)
649-
signature := header.Extra[index.Seal : index.Seal+types.IstanbulExtraSeal]
650-
return istanbul.GetSignatureAddress(sigHash(header).Bytes(), signature)
644+
istanbulExtra, err := types.ExtractIstanbulExtra(header)
645+
if err != nil {
646+
return common.Address{}, err
647+
}
648+
return istanbul.GetSignatureAddress(sigHash(header).Bytes(), istanbulExtra.Seal)
649+
}
650+
651+
// prepareExtra returns a extra-data of the given header and validators
652+
func prepareExtra(header *types.Header, vals []common.Address) ([]byte, error) {
653+
var buf bytes.Buffer
654+
655+
// compensate the lack bytes if header.Extra is not enough IstanbulExtraVanity bytes.
656+
if len(header.Extra) < types.IstanbulExtraVanity {
657+
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity-len(header.Extra))...)
658+
}
659+
buf.Write(header.Extra[:types.IstanbulExtraVanity])
660+
661+
ist := &types.IstanbulExtra{
662+
Validators: vals,
663+
Seal: []byte{},
664+
CommittedSeal: [][]byte{},
665+
}
666+
667+
payload, err := rlp.EncodeToBytes(&ist)
668+
if err != nil {
669+
return nil, err
670+
}
671+
672+
return append(buf.Bytes(), payload...), nil
673+
}
674+
675+
// writeSeal writes the extra-data field of the given header with the given seals.
676+
// suggest to rename to writeSeal.
677+
func writeSeal(h *types.Header, seal []byte) error {
678+
if len(seal)%types.IstanbulExtraSeal != 0 {
679+
return errInvalidSignature
680+
}
681+
682+
istanbulExtra, err := types.ExtractIstanbulExtra(h)
683+
if err != nil {
684+
return err
685+
}
686+
687+
istanbulExtra.Seal = seal
688+
payload, err := rlp.EncodeToBytes(&istanbulExtra)
689+
if err != nil {
690+
return err
691+
}
692+
693+
h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
694+
return nil
695+
}
696+
697+
// writeCommittedSeals writes the extra-data field of a block header with given committed seals.
698+
func writeCommittedSeals(h *types.Header, committedSeals []byte) error {
699+
if len(committedSeals)%types.IstanbulExtraSeal != 0 {
700+
return errInvalidCommittedSeals
701+
}
702+
703+
istanbulExtra, err := types.ExtractIstanbulExtra(h)
704+
if err != nil {
705+
return err
706+
}
707+
708+
istanbulExtra.CommittedSeal = make([][]byte, len(committedSeals)/types.IstanbulExtraSeal)
709+
for i := 0; i < len(istanbulExtra.CommittedSeal); i++ {
710+
istanbulExtra.CommittedSeal[i] = make([]byte, types.IstanbulExtraSeal)
711+
copy(istanbulExtra.CommittedSeal[i][:], committedSeals[i*types.IstanbulExtraSeal:])
712+
}
713+
714+
payload, err := rlp.EncodeToBytes(&istanbulExtra)
715+
if err != nil {
716+
return err
717+
}
718+
719+
h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...)
720+
return nil
651721
}

consensus/istanbul/backend/engine_test.go

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/ethereum/go-ethereum/ethdb"
3636
"github.com/ethereum/go-ethereum/event"
3737
"github.com/ethereum/go-ethereum/params"
38+
"github.com/ethereum/go-ethereum/rlp"
3839
)
3940

4041
// in this test, we can set n to 1, and it means we can process Istanbul and commit a
@@ -105,22 +106,23 @@ func getGenesisAndKeys(n int) (*core.Genesis, []*ecdsa.PrivateKey) {
105106
}
106107

107108
func appendValidators(genesis *core.Genesis, addrs []common.Address) {
109+
108110
if len(genesis.ExtraData) < types.IstanbulExtraVanity {
109111
genesis.ExtraData = append(genesis.ExtraData, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)...)
110112
}
111113
genesis.ExtraData = genesis.ExtraData[:types.IstanbulExtraVanity]
112114

113-
validatorSize := byte(len(addrs))
114-
genesis.ExtraData = append(genesis.ExtraData, validatorSize)
115-
116-
for _, addr := range addrs {
117-
genesis.ExtraData = append(genesis.ExtraData, addr[:]...)
115+
ist := &types.IstanbulExtra{
116+
Validators: addrs,
117+
Seal: []byte{},
118+
CommittedSeal: [][]byte{},
118119
}
119-
genesis.ExtraData = append(genesis.ExtraData, make([]byte, types.IstanbulExtraSeal)...)
120120

121-
// committed seal
122-
genesis.ExtraData = append(genesis.ExtraData, byte(0x01))
123-
genesis.ExtraData = append(genesis.ExtraData, make([]byte, types.IstanbulExtraSeal)...)
121+
istPayload, err := rlp.EncodeToBytes(&ist)
122+
if err != nil {
123+
panic("failed to encode istanbul extra")
124+
}
125+
genesis.ExtraData = append(genesis.ExtraData, istPayload...)
124126
}
125127

126128
func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
@@ -474,3 +476,120 @@ OUT3:
474476
}
475477
}
476478
}
479+
480+
func TestPrepareExtra(t *testing.T) {
481+
validators := make([]common.Address, 4)
482+
validators[0] = common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a"))
483+
validators[1] = common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212"))
484+
validators[2] = common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"))
485+
validators[3] = common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440"))
486+
487+
vanity := make([]byte, types.IstanbulExtraVanity)
488+
expectedResult := append(vanity, hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")...)
489+
490+
h := &types.Header{
491+
Extra: vanity,
492+
}
493+
494+
payload, err := prepareExtra(h, validators)
495+
if err != nil {
496+
t.Errorf("unexpected error: %v", err)
497+
}
498+
if !reflect.DeepEqual(payload, expectedResult) {
499+
t.Errorf("expected: %v, but got: %v", expectedResult, payload)
500+
}
501+
502+
// append useless information to extra-data
503+
h.Extra = append(vanity, make([]byte, 15)...)
504+
505+
payload, err = prepareExtra(h, validators)
506+
if !reflect.DeepEqual(payload, expectedResult) {
507+
t.Errorf("expected: %v, got: %v", expectedResult, payload)
508+
}
509+
}
510+
511+
func TestWriteSeal(t *testing.T) {
512+
vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
513+
istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
514+
expectedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
515+
expectedIstExtra := &types.IstanbulExtra{
516+
Validators: []common.Address{
517+
common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
518+
common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
519+
common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
520+
common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
521+
},
522+
Seal: expectedSeal,
523+
CommittedSeal: [][]byte{},
524+
}
525+
var expectedErr error
526+
527+
h := &types.Header{
528+
Extra: append(vanity, istRawData...),
529+
}
530+
531+
// normal case
532+
err := writeSeal(h, expectedSeal)
533+
if err != expectedErr {
534+
t.Errorf("expected: %v, but got: %v", expectedErr, err)
535+
}
536+
537+
// verify istanbul extra-data
538+
istExtra, err := types.ExtractIstanbulExtra(h)
539+
if err != nil {
540+
t.Errorf("unexpected error: %v", err)
541+
}
542+
if !reflect.DeepEqual(istExtra, expectedIstExtra) {
543+
t.Errorf("expected: %v, got: %v", expectedIstExtra, istExtra)
544+
}
545+
546+
// invalid seal
547+
unexpectedSeal := append(expectedSeal, make([]byte, 1)...)
548+
err = writeSeal(h, unexpectedSeal)
549+
if err != errInvalidSignature {
550+
t.Errorf("expected: %v, got: %v", errInvalidSignature, err)
551+
}
552+
}
553+
554+
func TestWriteCommittedSeals(t *testing.T) {
555+
vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
556+
istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
557+
expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
558+
expectedIstExtra := &types.IstanbulExtra{
559+
Validators: []common.Address{
560+
common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
561+
common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
562+
common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
563+
common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
564+
},
565+
Seal: []byte{},
566+
CommittedSeal: [][]byte{expectedCommittedSeal},
567+
}
568+
var expectedErr error
569+
570+
h := &types.Header{
571+
Extra: append(vanity, istRawData...),
572+
}
573+
574+
// normal case
575+
err := writeCommittedSeals(h, expectedCommittedSeal)
576+
if err != expectedErr {
577+
t.Errorf("expected: %v, but got: %v", expectedErr, err)
578+
}
579+
580+
// verify istanbul extra-data
581+
istExtra, err := types.ExtractIstanbulExtra(h)
582+
if err != nil {
583+
t.Errorf("unexpected error: %v", err)
584+
}
585+
if !reflect.DeepEqual(istExtra, expectedIstExtra) {
586+
t.Errorf("expected: %v, got: %v", expectedIstExtra, istExtra)
587+
}
588+
589+
// invalid seal
590+
unexpectedCommittedSeal := append(expectedCommittedSeal, make([]byte, 1)...)
591+
err = writeCommittedSeals(h, unexpectedCommittedSeal)
592+
if err != errInvalidCommittedSeals {
593+
t.Errorf("expected: %v, got: %v", errInvalidCommittedSeals, err)
594+
}
595+
}

consensus/istanbul/backend/snapshot_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ func (ap *testerAccountPool) sign(header *types.Header, validator string) {
5959
// Sign the header and embed the signature in extra data
6060
hashData := crypto.Keccak256([]byte(sigHash(header).Bytes()))
6161
sig, _ := crypto.Sign(hashData, ap.accounts[validator])
62-
copy(header.Extra[len(header.Extra)-types.IstanbulExtraSeal:], sig)
62+
63+
writeSeal(header, sig)
6364
}
6465

6566
func (ap *testerAccountPool) address(account string) common.Address {
@@ -334,7 +335,8 @@ func TestVoting(t *testing.T) {
334335
Mixhash: types.IstanbulDigest,
335336
}
336337
b, _ := genesis.ToBlock()
337-
genesis.ExtraData = types.PrepareIstanbulExtra(b.Header(), validators)
338+
extra, _ := prepareExtra(b.Header(), validators)
339+
genesis.ExtraData = extra
338340
// Create a pristine blockchain with the genesis injected
339341
db, _ := ethdb.NewMemDatabase()
340342
genesis.Commit(db)
@@ -357,7 +359,8 @@ func TestVoting(t *testing.T) {
357359
Difficulty: defaultDifficulty,
358360
MixDigest: types.IstanbulDigest,
359361
}
360-
headers[j].Extra = types.PrepareIstanbulExtra(headers[j], validators)
362+
extra, _ := prepareExtra(headers[j], validators)
363+
headers[j].Extra = extra
361364
if j > 0 {
362365
headers[j].ParentHash = headers[j-1].Hash()
363366
}

0 commit comments

Comments
 (0)