mirror of https://github.com/poanetwork/quorum.git
556 lines
18 KiB
Go
556 lines
18 KiB
Go
|
// 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 backend
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/ecdsa"
|
||
|
"math/big"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"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/istanbul"
|
||
|
"github.com/ethereum/go-ethereum/core"
|
||
|
"github.com/ethereum/go-ethereum/core/types"
|
||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||
|
"github.com/ethereum/go-ethereum/crypto"
|
||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||
|
"github.com/ethereum/go-ethereum/params"
|
||
|
"github.com/ethereum/go-ethereum/rlp"
|
||
|
)
|
||
|
|
||
|
// in this test, we can set n to 1, and it means we can process Istanbul and commit a
|
||
|
// block by one node. Otherwise, if n is larger than 1, we have to generate
|
||
|
// other fake events to process Istanbul.
|
||
|
func newBlockChain(n int) (*core.BlockChain, *backend) {
|
||
|
genesis, nodeKeys := getGenesisAndKeys(n)
|
||
|
memDB, _ := ethdb.NewMemDatabase()
|
||
|
config := istanbul.DefaultConfig
|
||
|
// Use the first key as private key
|
||
|
b, _ := New(config, nodeKeys[0], memDB).(*backend)
|
||
|
genesis.MustCommit(memDB)
|
||
|
blockchain, err := core.NewBlockChain(memDB, genesis.Config, b, vm.Config{})
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
b.Start(blockchain, blockchain.InsertChain)
|
||
|
snap, err := b.snapshot(blockchain, 0, common.Hash{}, nil)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if snap == nil {
|
||
|
panic("failed to get snapshot")
|
||
|
}
|
||
|
proposerAddr := snap.ValSet.GetProposer().Address()
|
||
|
|
||
|
// find proposer key
|
||
|
for _, key := range nodeKeys {
|
||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||
|
if addr.String() == proposerAddr.String() {
|
||
|
b.privateKey = key
|
||
|
b.address = addr
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return blockchain, b
|
||
|
}
|
||
|
|
||
|
func getGenesisAndKeys(n int) (*core.Genesis, []*ecdsa.PrivateKey) {
|
||
|
// Setup validators
|
||
|
var nodeKeys = make([]*ecdsa.PrivateKey, n)
|
||
|
var addrs = make([]common.Address, n)
|
||
|
for i := 0; i < n; i++ {
|
||
|
nodeKeys[i], _ = crypto.GenerateKey()
|
||
|
addrs[i] = crypto.PubkeyToAddress(nodeKeys[i].PublicKey)
|
||
|
}
|
||
|
|
||
|
// generate genesis block
|
||
|
genesis := core.DefaultGenesisBlock()
|
||
|
genesis.Config = params.TestChainConfig
|
||
|
// force enable Istanbul engine
|
||
|
genesis.Config.Istanbul = ¶ms.IstanbulConfig{}
|
||
|
genesis.Config.Ethash = nil
|
||
|
genesis.Difficulty = defaultDifficulty
|
||
|
genesis.Nonce = emptyNonce.Uint64()
|
||
|
genesis.Mixhash = types.IstanbulDigest
|
||
|
|
||
|
appendValidators(genesis, addrs)
|
||
|
return genesis, nodeKeys
|
||
|
}
|
||
|
|
||
|
func appendValidators(genesis *core.Genesis, addrs []common.Address) {
|
||
|
|
||
|
if len(genesis.ExtraData) < types.IstanbulExtraVanity {
|
||
|
genesis.ExtraData = append(genesis.ExtraData, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)...)
|
||
|
}
|
||
|
genesis.ExtraData = genesis.ExtraData[:types.IstanbulExtraVanity]
|
||
|
|
||
|
ist := &types.IstanbulExtra{
|
||
|
Validators: addrs,
|
||
|
Seal: []byte{},
|
||
|
CommittedSeal: [][]byte{},
|
||
|
}
|
||
|
|
||
|
istPayload, err := rlp.EncodeToBytes(&ist)
|
||
|
if err != nil {
|
||
|
panic("failed to encode istanbul extra")
|
||
|
}
|
||
|
genesis.ExtraData = append(genesis.ExtraData, istPayload...)
|
||
|
}
|
||
|
|
||
|
func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
|
||
|
header := &types.Header{
|
||
|
ParentHash: parent.Hash(),
|
||
|
Number: parent.Number().Add(parent.Number(), common.Big1),
|
||
|
GasLimit: core.CalcGasLimit(parent),
|
||
|
GasUsed: new(big.Int),
|
||
|
Extra: parent.Extra(),
|
||
|
Time: new(big.Int).Add(parent.Time(), new(big.Int).SetUint64(config.BlockPeriod)),
|
||
|
Difficulty: defaultDifficulty,
|
||
|
}
|
||
|
return header
|
||
|
}
|
||
|
|
||
|
func makeBlock(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
|
||
|
block := makeBlockWithoutSeal(chain, engine, parent)
|
||
|
block, _ = engine.Seal(chain, block, nil)
|
||
|
return block
|
||
|
}
|
||
|
|
||
|
func makeBlockWithoutSeal(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
|
||
|
header := makeHeader(parent, engine.config)
|
||
|
engine.Prepare(chain, header)
|
||
|
state, _, _ := chain.StateAt(parent.Root())
|
||
|
block, _ := engine.Finalize(chain, header, state, nil, nil, nil)
|
||
|
return block
|
||
|
}
|
||
|
|
||
|
func TestPrepare(t *testing.T) {
|
||
|
chain, engine := newBlockChain(1)
|
||
|
header := makeHeader(chain.Genesis(), engine.config)
|
||
|
err := engine.Prepare(chain, header)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
header.ParentHash = common.StringToHash("1234567890")
|
||
|
err = engine.Prepare(chain, header)
|
||
|
if err != consensus.ErrUnknownAncestor {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSealStopChannel(t *testing.T) {
|
||
|
chain, engine := newBlockChain(4)
|
||
|
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
stop := make(chan struct{}, 1)
|
||
|
eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
|
||
|
eventLoop := func() {
|
||
|
select {
|
||
|
case ev := <-eventSub.Chan():
|
||
|
_, ok := ev.Data.(istanbul.RequestEvent)
|
||
|
if !ok {
|
||
|
t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
|
||
|
}
|
||
|
stop <- struct{}{}
|
||
|
}
|
||
|
eventSub.Unsubscribe()
|
||
|
}
|
||
|
go eventLoop()
|
||
|
finalBlock, err := engine.Seal(chain, block, stop)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
if finalBlock != nil {
|
||
|
t.Errorf("block mismatch: have %v, want nil", finalBlock)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSealCommittedOtherHash(t *testing.T) {
|
||
|
chain, engine := newBlockChain(4)
|
||
|
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
otherBlock := makeBlockWithoutSeal(chain, engine, block)
|
||
|
eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
|
||
|
eventLoop := func() {
|
||
|
select {
|
||
|
case ev := <-eventSub.Chan():
|
||
|
_, ok := ev.Data.(istanbul.RequestEvent)
|
||
|
if !ok {
|
||
|
t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
|
||
|
}
|
||
|
engine.Commit(otherBlock, [][]byte{})
|
||
|
}
|
||
|
eventSub.Unsubscribe()
|
||
|
}
|
||
|
go eventLoop()
|
||
|
seal := func() {
|
||
|
engine.Seal(chain, block, nil)
|
||
|
t.Error("seal should not be completed")
|
||
|
}
|
||
|
go seal()
|
||
|
|
||
|
const timeoutDura = 2 * time.Second
|
||
|
timeout := time.NewTimer(timeoutDura)
|
||
|
select {
|
||
|
case <-timeout.C:
|
||
|
// wait 2 seconds to ensure we cannot get any blocks from Istanbul
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSealCommitted(t *testing.T) {
|
||
|
chain, engine := newBlockChain(1)
|
||
|
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
|
||
|
|
||
|
finalBlock, err := engine.Seal(chain, block, nil)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
if finalBlock.Hash() != expectedBlock.Hash() {
|
||
|
t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVerifyHeader(t *testing.T) {
|
||
|
chain, engine := newBlockChain(1)
|
||
|
|
||
|
// errEmptyCommittedSeals case
|
||
|
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
block, _ = engine.updateBlock(chain.Genesis().Header(), block)
|
||
|
err := engine.VerifyHeader(chain, block.Header(), false)
|
||
|
if err != errEmptyCommittedSeals {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errEmptyCommittedSeals)
|
||
|
}
|
||
|
|
||
|
// short extra data
|
||
|
header := block.Header()
|
||
|
header.Extra = []byte{}
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidExtraDataFormat {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
|
||
|
}
|
||
|
// incorrect extra format
|
||
|
header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000")
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidExtraDataFormat {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
|
||
|
}
|
||
|
|
||
|
// non zero MixDigest
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
header.MixDigest = common.StringToHash("123456789")
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidMixDigest {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidMixDigest)
|
||
|
}
|
||
|
|
||
|
// invalid uncles hash
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
header.UncleHash = common.StringToHash("123456789")
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidUncleHash {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidUncleHash)
|
||
|
}
|
||
|
|
||
|
// invalid difficulty
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
header.Difficulty = big.NewInt(2)
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidDifficulty {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidDifficulty)
|
||
|
}
|
||
|
|
||
|
// invalid timestamp
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
header.Time = new(big.Int).Add(chain.Genesis().Time(), new(big.Int).SetUint64(engine.config.BlockPeriod-1))
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidTimestamp {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidTimestamp)
|
||
|
}
|
||
|
|
||
|
// future block
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
header.Time = new(big.Int).Add(big.NewInt(now().Unix()), new(big.Int).SetUint64(10))
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != consensus.ErrFutureBlock {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrFutureBlock)
|
||
|
}
|
||
|
|
||
|
// invalid nonce
|
||
|
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
|
||
|
header = block.Header()
|
||
|
copy(header.Nonce[:], hexutil.MustDecode("0x111111111111"))
|
||
|
header.Number = big.NewInt(int64(engine.config.Epoch))
|
||
|
err = engine.VerifyHeader(chain, header, false)
|
||
|
if err != errInvalidNonce {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidNonce)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVerifySeal(t *testing.T) {
|
||
|
chain, engine := newBlockChain(1)
|
||
|
genesis := chain.Genesis()
|
||
|
// cannot verify genesis
|
||
|
err := engine.VerifySeal(chain, genesis.Header())
|
||
|
if err != errUnknownBlock {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errUnknownBlock)
|
||
|
}
|
||
|
|
||
|
block := makeBlock(chain, engine, genesis)
|
||
|
// change block content
|
||
|
header := block.Header()
|
||
|
header.Number = big.NewInt(4)
|
||
|
block1 := block.WithSeal(header)
|
||
|
err = engine.VerifySeal(chain, block1.Header())
|
||
|
if err != errUnauthorized {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errUnauthorized)
|
||
|
}
|
||
|
|
||
|
// unauthorized users but still can get correct signer address
|
||
|
engine.privateKey, _ = crypto.GenerateKey()
|
||
|
err = engine.VerifySeal(chain, block.Header())
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVerifyHeaders(t *testing.T) {
|
||
|
chain, engine := newBlockChain(1)
|
||
|
genesis := chain.Genesis()
|
||
|
|
||
|
// success case
|
||
|
headers := []*types.Header{}
|
||
|
blocks := []*types.Block{}
|
||
|
size := 100
|
||
|
|
||
|
for i := 0; i < size; i++ {
|
||
|
var b *types.Block
|
||
|
if i == 0 {
|
||
|
b = makeBlockWithoutSeal(chain, engine, genesis)
|
||
|
b, _ = engine.updateBlock(genesis.Header(), b)
|
||
|
} else {
|
||
|
b = makeBlockWithoutSeal(chain, engine, blocks[i-1])
|
||
|
b, _ = engine.updateBlock(blocks[i-1].Header(), b)
|
||
|
}
|
||
|
blocks = append(blocks, b)
|
||
|
headers = append(headers, blocks[i].Header())
|
||
|
}
|
||
|
now = func() time.Time {
|
||
|
return time.Unix(headers[size-1].Time.Int64(), 0)
|
||
|
}
|
||
|
_, results := engine.VerifyHeaders(chain, headers, nil)
|
||
|
const timeoutDura = 2 * time.Second
|
||
|
timeout := time.NewTimer(timeoutDura)
|
||
|
index := 0
|
||
|
OUT1:
|
||
|
for {
|
||
|
select {
|
||
|
case err := <-results:
|
||
|
if err != nil {
|
||
|
if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
|
||
|
t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals", err)
|
||
|
break OUT1
|
||
|
}
|
||
|
}
|
||
|
index++
|
||
|
if index == size {
|
||
|
break OUT1
|
||
|
}
|
||
|
case <-timeout.C:
|
||
|
break OUT1
|
||
|
}
|
||
|
}
|
||
|
// abort cases
|
||
|
abort, results := engine.VerifyHeaders(chain, headers, nil)
|
||
|
timeout = time.NewTimer(timeoutDura)
|
||
|
index = 0
|
||
|
OUT2:
|
||
|
for {
|
||
|
select {
|
||
|
case err := <-results:
|
||
|
if err != nil {
|
||
|
if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
|
||
|
t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals", err)
|
||
|
break OUT2
|
||
|
}
|
||
|
}
|
||
|
index++
|
||
|
if index == 5 {
|
||
|
abort <- struct{}{}
|
||
|
}
|
||
|
if index >= size {
|
||
|
t.Errorf("verifyheaders should be aborted")
|
||
|
break OUT2
|
||
|
}
|
||
|
case <-timeout.C:
|
||
|
break OUT2
|
||
|
}
|
||
|
}
|
||
|
// error header cases
|
||
|
headers[2].Number = big.NewInt(100)
|
||
|
abort, results = engine.VerifyHeaders(chain, headers, nil)
|
||
|
timeout = time.NewTimer(timeoutDura)
|
||
|
index = 0
|
||
|
errors := 0
|
||
|
expectedErrors := 2
|
||
|
OUT3:
|
||
|
for {
|
||
|
select {
|
||
|
case err := <-results:
|
||
|
if err != nil {
|
||
|
if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
|
||
|
errors++
|
||
|
}
|
||
|
}
|
||
|
index++
|
||
|
if index == size {
|
||
|
if errors != expectedErrors {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, expectedErrors)
|
||
|
}
|
||
|
break OUT3
|
||
|
}
|
||
|
case <-timeout.C:
|
||
|
break OUT3
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPrepareExtra(t *testing.T) {
|
||
|
validators := make([]common.Address, 4)
|
||
|
validators[0] = common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a"))
|
||
|
validators[1] = common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212"))
|
||
|
validators[2] = common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"))
|
||
|
validators[3] = common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440"))
|
||
|
|
||
|
vanity := make([]byte, types.IstanbulExtraVanity)
|
||
|
expectedResult := append(vanity, hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")...)
|
||
|
|
||
|
h := &types.Header{
|
||
|
Extra: vanity,
|
||
|
}
|
||
|
|
||
|
payload, err := prepareExtra(h, validators)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want: nil", err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(payload, expectedResult) {
|
||
|
t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
|
||
|
}
|
||
|
|
||
|
// append useless information to extra-data
|
||
|
h.Extra = append(vanity, make([]byte, 15)...)
|
||
|
|
||
|
payload, err = prepareExtra(h, validators)
|
||
|
if !reflect.DeepEqual(payload, expectedResult) {
|
||
|
t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestWriteSeal(t *testing.T) {
|
||
|
vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
|
||
|
istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
|
||
|
expectedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
|
||
|
expectedIstExtra := &types.IstanbulExtra{
|
||
|
Validators: []common.Address{
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
|
||
|
},
|
||
|
Seal: expectedSeal,
|
||
|
CommittedSeal: [][]byte{},
|
||
|
}
|
||
|
var expectedErr error
|
||
|
|
||
|
h := &types.Header{
|
||
|
Extra: append(vanity, istRawData...),
|
||
|
}
|
||
|
|
||
|
// normal case
|
||
|
err := writeSeal(h, expectedSeal)
|
||
|
if err != expectedErr {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
|
||
|
}
|
||
|
|
||
|
// verify istanbul extra-data
|
||
|
istExtra, err := types.ExtractIstanbulExtra(h)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(istExtra, expectedIstExtra) {
|
||
|
t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
|
||
|
}
|
||
|
|
||
|
// invalid seal
|
||
|
unexpectedSeal := append(expectedSeal, make([]byte, 1)...)
|
||
|
err = writeSeal(h, unexpectedSeal)
|
||
|
if err != errInvalidSignature {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidSignature)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestWriteCommittedSeals(t *testing.T) {
|
||
|
vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
|
||
|
istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
|
||
|
expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
|
||
|
expectedIstExtra := &types.IstanbulExtra{
|
||
|
Validators: []common.Address{
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
|
||
|
common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
|
||
|
},
|
||
|
Seal: []byte{},
|
||
|
CommittedSeal: [][]byte{expectedCommittedSeal},
|
||
|
}
|
||
|
var expectedErr error
|
||
|
|
||
|
h := &types.Header{
|
||
|
Extra: append(vanity, istRawData...),
|
||
|
}
|
||
|
|
||
|
// normal case
|
||
|
err := writeCommittedSeals(h, [][]byte{expectedCommittedSeal})
|
||
|
if err != expectedErr {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
|
||
|
}
|
||
|
|
||
|
// verify istanbul extra-data
|
||
|
istExtra, err := types.ExtractIstanbulExtra(h)
|
||
|
if err != nil {
|
||
|
t.Errorf("error mismatch: have %v, want nil", err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(istExtra, expectedIstExtra) {
|
||
|
t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
|
||
|
}
|
||
|
|
||
|
// invalid seal
|
||
|
unexpectedCommittedSeal := append(expectedCommittedSeal, make([]byte, 1)...)
|
||
|
err = writeCommittedSeals(h, [][]byte{unexpectedCommittedSeal})
|
||
|
if err != errInvalidCommittedSeals {
|
||
|
t.Errorf("error mismatch: have %v, want %v", err, errInvalidCommittedSeals)
|
||
|
}
|
||
|
}
|