533 lines
18 KiB
Go
533 lines
18 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package multichannel
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric-protos-go/orderer"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/bccsp/sw"
|
|
"github.com/hyperledger/fabric/common/channelconfig"
|
|
"github.com/hyperledger/fabric/common/configtx"
|
|
"github.com/hyperledger/fabric/common/ledger/blockledger"
|
|
"github.com/hyperledger/fabric/common/ledger/blockledger/fileledger"
|
|
"github.com/hyperledger/fabric/common/metrics/disabled"
|
|
"github.com/hyperledger/fabric/core/config/configtest"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/hyperledger/fabric/orderer/common/blockcutter/mock"
|
|
"github.com/hyperledger/fabric/orderer/common/multichannel/mocks"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
//go:generate counterfeiter -o mocks/configtx_validator.go --fake-name ConfigTXValidator . configtxValidator
|
|
|
|
type configtxValidator interface {
|
|
configtx.Validator
|
|
}
|
|
|
|
type mockBlockWriterSupport struct {
|
|
*mocks.ConfigTXValidator
|
|
identity.SignerSerializer
|
|
blockledger.ReadWriter
|
|
fakeConfig *mock.OrdererConfig
|
|
bccsp bccsp.BCCSP
|
|
}
|
|
|
|
func (mbws mockBlockWriterSupport) Update(bundle *channelconfig.Bundle) {}
|
|
|
|
func (mbws mockBlockWriterSupport) CreateBundle(channelID string, config *cb.Config) (*channelconfig.Bundle, error) {
|
|
return channelconfig.NewBundle(channelID, config, mbws.bccsp)
|
|
}
|
|
|
|
func (mbws mockBlockWriterSupport) SharedConfig() channelconfig.Orderer {
|
|
return mbws.fakeConfig
|
|
}
|
|
|
|
func TestCreateBlock(t *testing.T) {
|
|
seedBlock := protoutil.NewBlock(7, []byte("lasthash"))
|
|
seedBlock.Data.Data = [][]byte{[]byte("somebytes")}
|
|
|
|
bw := &BlockWriter{lastBlock: seedBlock}
|
|
block := bw.CreateNextBlock([]*cb.Envelope{
|
|
{Payload: []byte("some other bytes")},
|
|
})
|
|
|
|
require.Equal(t, seedBlock.Header.Number+1, block.Header.Number)
|
|
require.Equal(t, protoutil.BlockDataHash(block.Data), block.Header.DataHash)
|
|
require.Equal(t, protoutil.BlockHeaderHash(seedBlock.Header), block.Header.PreviousHash)
|
|
}
|
|
|
|
func TestBlockSignature(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
rlf, err := fileledger.New(dir, &disabled.Provider{})
|
|
require.NoError(t, err)
|
|
|
|
l, err := rlf.GetOrCreate("mychannel")
|
|
require.NoError(t, err)
|
|
lastBlock := protoutil.NewBlock(0, nil)
|
|
l.Append(lastBlock)
|
|
|
|
bw := &BlockWriter{
|
|
lastConfigBlockNum: 42,
|
|
support: &mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ConfigTXValidator: &mocks.ConfigTXValidator{},
|
|
ReadWriter: l,
|
|
},
|
|
lastBlock: protoutil.NewBlock(1, protoutil.BlockHeaderHash(lastBlock.Header)),
|
|
}
|
|
|
|
consensusMetadata := []byte("bar")
|
|
bw.commitBlock(consensusMetadata)
|
|
|
|
it, seq := l.Iterator(&orderer.SeekPosition{Type: &orderer.SeekPosition_Newest{}})
|
|
require.Equal(t, uint64(1), seq)
|
|
committedBlock, status := it.Next()
|
|
require.Equal(t, cb.Status_SUCCESS, status)
|
|
|
|
md := protoutil.GetMetadataFromBlockOrPanic(committedBlock, cb.BlockMetadataIndex_SIGNATURES)
|
|
|
|
expectedMetadataValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
|
|
LastConfig: &cb.LastConfig{Index: 42},
|
|
ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: consensusMetadata}),
|
|
})
|
|
|
|
require.Equal(t, expectedMetadataValue, md.Value, "Value contains the consensus metadata and the last config")
|
|
require.NotNil(t, md.Signatures, "Should have signature")
|
|
}
|
|
|
|
func TestBlockLastConfig(t *testing.T) {
|
|
lastConfigSeq := uint64(6)
|
|
newConfigSeq := lastConfigSeq + 1
|
|
newBlockNum := uint64(9)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
mockValidator.SequenceReturns(newConfigSeq)
|
|
bw := &BlockWriter{
|
|
support: &mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ConfigTXValidator: mockValidator,
|
|
},
|
|
lastConfigSeq: lastConfigSeq,
|
|
}
|
|
|
|
block := protoutil.NewBlock(newBlockNum, []byte("foo"))
|
|
bw.addLastConfig(block)
|
|
|
|
require.Equal(t, newBlockNum, bw.lastConfigBlockNum)
|
|
require.Equal(t, newConfigSeq, bw.lastConfigSeq)
|
|
|
|
md := protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_LAST_CONFIG)
|
|
require.NotNil(t, md.Value, "Value not be empty in this case")
|
|
require.Nil(t, md.Signatures, "Should not have signature")
|
|
|
|
lc := protoutil.GetLastConfigIndexFromBlockOrPanic(block)
|
|
require.Equal(t, newBlockNum, lc)
|
|
}
|
|
|
|
func TestWriteConfigBlock(t *testing.T) {
|
|
t.Run("EmptyBlock", func(t *testing.T) {
|
|
require.PanicsWithValue(t,
|
|
"Told to write a config block, but could not get configtx: block data is nil",
|
|
func() {
|
|
(&BlockWriter{}).WriteConfigBlock(&cb.Block{}, nil)
|
|
})
|
|
})
|
|
t.Run("BadPayload", func(t *testing.T) {
|
|
didPanic, pMsg := testPanic(
|
|
func() {
|
|
(&BlockWriter{}).WriteConfigBlock(&cb.Block{
|
|
Data: &cb.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&cb.Envelope{Payload: []byte("bad")}),
|
|
},
|
|
},
|
|
}, nil)
|
|
})
|
|
|
|
require.True(t, didPanic)
|
|
require.Contains(t, pMsg, "Told to write a config block, but configtx payload is invalid: error unmarshalling Payload: proto:")
|
|
})
|
|
t.Run("MissingHeader", func(t *testing.T) {
|
|
require.PanicsWithValue(t,
|
|
"Told to write a config block, but configtx payload header is missing",
|
|
func() {
|
|
(&BlockWriter{}).WriteConfigBlock(&cb.Block{
|
|
Data: &cb.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&cb.Envelope{
|
|
Payload: protoutil.MarshalOrPanic(&cb.Payload{}),
|
|
}),
|
|
},
|
|
},
|
|
}, nil)
|
|
})
|
|
})
|
|
t.Run("BadChannelHeader", func(t *testing.T) {
|
|
didPanic, pMsg := testPanic(
|
|
func() {
|
|
(&BlockWriter{}).WriteConfigBlock(&cb.Block{
|
|
Data: &cb.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&cb.Envelope{
|
|
Payload: protoutil.MarshalOrPanic(&cb.Payload{
|
|
Header: &cb.Header{
|
|
ChannelHeader: []byte("bad"),
|
|
},
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}, nil)
|
|
})
|
|
|
|
require.True(t, didPanic)
|
|
require.Contains(t, pMsg, "Told to write a config block with an invalid channel header: error unmarshalling ChannelHeader: proto:")
|
|
})
|
|
t.Run("BadChannelHeaderType", func(t *testing.T) {
|
|
require.PanicsWithValue(t,
|
|
"Told to write a config block with unknown header type: 0",
|
|
func() {
|
|
(&BlockWriter{}).WriteConfigBlock(&cb.Block{
|
|
Data: &cb.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&cb.Envelope{
|
|
Payload: protoutil.MarshalOrPanic(&cb.Payload{
|
|
Header: &cb.Header{
|
|
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{}),
|
|
},
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}, nil)
|
|
})
|
|
})
|
|
t.Run("UnsupportedChannelHeaderType", func(t *testing.T) {
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
mockValidator.ChannelIDReturns("mychannel")
|
|
mockSupport := &mockBlockWriterSupport{
|
|
ConfigTXValidator: mockValidator,
|
|
}
|
|
|
|
require.PanicsWithValue(t,
|
|
"[channel: mychannel] Told to write a config block of type HeaderType_ORDERER_TRANSACTION, but the system channel is no longer supported",
|
|
func() {
|
|
(&BlockWriter{support: mockSupport}).WriteConfigBlock(&cb.Block{
|
|
Data: &cb.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&cb.Envelope{
|
|
Payload: protoutil.MarshalOrPanic(&cb.Payload{
|
|
Header: &cb.Header{
|
|
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
|
|
Type: int32(cb.HeaderType_ORDERER_TRANSACTION),
|
|
ChannelId: "mychannel",
|
|
TxId: "tx1",
|
|
}),
|
|
},
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}, nil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGoodWriteConfig(t *testing.T) {
|
|
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
|
|
genesisBlockSys := encoder.New(confSys).GenesisBlock()
|
|
|
|
tmpdir := t.TempDir()
|
|
|
|
_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
|
|
|
|
fakeConfig := &mock.OrdererConfig{}
|
|
fakeConfig.ConsensusTypeReturns("solo")
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
mockValidator.ChannelIDReturns("testchannelid")
|
|
bw := newBlockWriter(genesisBlockSys,
|
|
&mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ReadWriter: l,
|
|
ConfigTXValidator: mockValidator,
|
|
fakeConfig: fakeConfig,
|
|
bccsp: cryptoProvider,
|
|
})
|
|
|
|
ctx := makeConfigTxFull("testchannelid", 1)
|
|
block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
|
|
block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata := []byte("foo")
|
|
bw.WriteConfigBlock(block, consenterMetadata)
|
|
|
|
// Wait for the commit to complete
|
|
bw.committingBlock.Lock()
|
|
bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
|
|
|
|
cBlock := blockledger.GetBlock(l, block.Header.Number)
|
|
require.Equal(t, block.Header, cBlock.Header)
|
|
require.Equal(t, block.Data, cBlock.Data)
|
|
|
|
omd, err := protoutil.GetConsenterMetadataFromBlock(block)
|
|
require.NoError(t, err)
|
|
require.Equal(t, consenterMetadata, omd.Value)
|
|
}
|
|
|
|
func TestWriteConfigSynchronously(t *testing.T) {
|
|
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
|
|
genesisBlockSys := encoder.New(confSys).GenesisBlock()
|
|
|
|
tmpdir := t.TempDir()
|
|
|
|
_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
|
|
|
|
fakeConfig := &mock.OrdererConfig{}
|
|
fakeConfig.ConsensusTypeReturns("solo")
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
mockValidator.ChannelIDReturns("testchannelid")
|
|
bw := newBlockWriter(genesisBlockSys,
|
|
&mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ReadWriter: l,
|
|
ConfigTXValidator: mockValidator,
|
|
fakeConfig: fakeConfig,
|
|
bccsp: cryptoProvider,
|
|
})
|
|
|
|
ctx := makeConfigTxFull("testchannelid", 1)
|
|
block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
|
|
block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata := []byte("foo")
|
|
bw.WriteConfigBlock(block, consenterMetadata)
|
|
|
|
cBlock, err := blockledger.GetBlockByNumber(l, block.Header.Number)
|
|
require.Nil(t, err)
|
|
require.Equal(t, block.Header, cBlock.Header)
|
|
require.Equal(t, block.Data, cBlock.Data)
|
|
|
|
omd, err := protoutil.GetConsenterMetadataFromBlock(block)
|
|
require.NoError(t, err)
|
|
require.Equal(t, consenterMetadata, omd.Value)
|
|
}
|
|
|
|
func TestMigrationWriteConfig(t *testing.T) {
|
|
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
|
|
genesisBlockSys := encoder.New(confSys).GenesisBlock()
|
|
|
|
tmpdir := t.TempDir()
|
|
|
|
_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
|
|
|
|
fakeConfig := &mock.OrdererConfig{}
|
|
fakeConfig.ConsensusTypeReturns("solo")
|
|
fakeConfig.ConsensusStateReturns(orderer.ConsensusType_STATE_MAINTENANCE)
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
mockValidator.ChannelIDReturns("testchannelid")
|
|
bw := newBlockWriter(genesisBlockSys,
|
|
&mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ReadWriter: l,
|
|
ConfigTXValidator: mockValidator,
|
|
fakeConfig: fakeConfig,
|
|
bccsp: cryptoProvider,
|
|
})
|
|
|
|
ctx := makeConfigTxMig("testchannelid", 1)
|
|
block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
|
|
block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata := []byte("foo")
|
|
|
|
bw.WriteConfigBlock(block, consenterMetadata)
|
|
|
|
// Wait for the commit to complete
|
|
bw.committingBlock.Lock()
|
|
bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
|
|
|
|
cBlock := blockledger.GetBlock(l, block.Header.Number)
|
|
require.Equal(t, block.Header, cBlock.Header)
|
|
require.Equal(t, block.Data, cBlock.Data)
|
|
|
|
omd := protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
|
|
require.Equal(t, []byte(nil), omd.Value)
|
|
}
|
|
|
|
func TestRaceWriteConfig(t *testing.T) {
|
|
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
|
|
genesisBlockSys := encoder.New(confSys).GenesisBlock()
|
|
|
|
tmpdir := t.TempDir()
|
|
|
|
_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
|
|
|
|
fakeConfig := &mock.OrdererConfig{}
|
|
fakeConfig.ConsensusTypeReturns("solo")
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
bw := newBlockWriter(genesisBlockSys,
|
|
&mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ReadWriter: l,
|
|
ConfigTXValidator: mockValidator,
|
|
fakeConfig: fakeConfig,
|
|
bccsp: cryptoProvider,
|
|
})
|
|
|
|
ctx := makeConfigTxFull("testchannelid", 1)
|
|
block1 := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
|
|
block1.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata1 := []byte("foo")
|
|
mockValidator.SequenceReturnsOnCall(1, 1)
|
|
|
|
ctx = makeConfigTxFull("testchannelid", 1)
|
|
block2 := protoutil.NewBlock(2, protoutil.BlockHeaderHash(block1.Header))
|
|
block2.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata2 := []byte("bar")
|
|
mockValidator.SequenceReturnsOnCall(2, 2)
|
|
|
|
bw.WriteConfigBlock(block1, consenterMetadata1)
|
|
bw.WriteConfigBlock(block2, consenterMetadata2)
|
|
|
|
// Wait for the commit to complete
|
|
bw.committingBlock.Lock()
|
|
bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
|
|
|
|
cBlock := blockledger.GetBlock(l, block1.Header.Number)
|
|
require.Equal(t, block1.Header, cBlock.Header)
|
|
require.Equal(t, block1.Data, cBlock.Data)
|
|
expectedLastConfigBlockNumber := block1.Header.Number
|
|
testLastConfigBlockNumber(t, block1, expectedLastConfigBlockNumber)
|
|
|
|
cBlock = blockledger.GetBlock(l, block2.Header.Number)
|
|
require.Equal(t, block2.Header, cBlock.Header)
|
|
require.Equal(t, block2.Data, cBlock.Data)
|
|
expectedLastConfigBlockNumber = block2.Header.Number
|
|
testLastConfigBlockNumber(t, block2, expectedLastConfigBlockNumber)
|
|
|
|
omd, err := protoutil.GetConsenterMetadataFromBlock(block1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, consenterMetadata1, omd.Value)
|
|
}
|
|
|
|
func TestRaceWriteBlocks(t *testing.T) {
|
|
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
|
|
genesisBlockSys := encoder.New(confSys).GenesisBlock()
|
|
|
|
tmpdir := t.TempDir()
|
|
|
|
_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
|
|
|
|
fakeConfig := &mock.OrdererConfig{}
|
|
fakeConfig.ConsensusTypeReturns("solo")
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mockValidator := &mocks.ConfigTXValidator{}
|
|
bw := newBlockWriter(genesisBlockSys,
|
|
&mockBlockWriterSupport{
|
|
SignerSerializer: mockCrypto(),
|
|
ReadWriter: l,
|
|
ConfigTXValidator: mockValidator,
|
|
fakeConfig: fakeConfig,
|
|
bccsp: cryptoProvider,
|
|
})
|
|
|
|
ctx := makeConfigTxFull("testchannelid", 1)
|
|
block1 := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
|
|
block1.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata1 := []byte("foo")
|
|
mockValidator.SequenceReturnsOnCall(1, 1)
|
|
|
|
ctx = makeConfigTxFull("testchannelid", 1)
|
|
block2 := protoutil.NewBlock(2, protoutil.BlockHeaderHash(block1.Header))
|
|
block2.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata2 := []byte("bar")
|
|
mockValidator.SequenceReturnsOnCall(2, 2)
|
|
|
|
ctx = makeConfigTxFull("testchannelid", 1)
|
|
block3 := protoutil.NewBlock(3, protoutil.BlockHeaderHash(block2.Header))
|
|
block3.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
|
|
consenterMetadata3 := []byte("3")
|
|
mockValidator.SequenceReturnsOnCall(3, 3)
|
|
|
|
bw.WriteBlock(block1, consenterMetadata1)
|
|
bw.WriteBlock(block2, consenterMetadata2)
|
|
bw.WriteConfigBlock(block3, consenterMetadata3)
|
|
|
|
cBlock, err := blockledger.GetBlockByNumber(l, block1.Header.Number)
|
|
require.Nil(t, err)
|
|
require.Equal(t, block1.Header, cBlock.Header)
|
|
require.Equal(t, block1.Data, cBlock.Data)
|
|
|
|
cBlock, err = blockledger.GetBlockByNumber(l, block2.Header.Number)
|
|
require.Nil(t, err)
|
|
require.Equal(t, block2.Header, cBlock.Header)
|
|
require.Equal(t, block2.Data, cBlock.Data)
|
|
|
|
cBlock, err = blockledger.GetBlockByNumber(l, block3.Header.Number)
|
|
require.Nil(t, err)
|
|
require.Equal(t, block3.Header, cBlock.Header)
|
|
require.Equal(t, block3.Data, cBlock.Data)
|
|
|
|
expectedLastConfigBlockNumber := block3.Header.Number
|
|
testLastConfigBlockNumber(t, block3, expectedLastConfigBlockNumber)
|
|
}
|
|
|
|
func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) {
|
|
metadata := &cb.Metadata{}
|
|
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], metadata)
|
|
require.NoError(t, err, "Block should carry SIGNATURES metadata item")
|
|
obm := &cb.OrdererBlockMetadata{}
|
|
err = proto.Unmarshal(metadata.Value, obm)
|
|
require.NoError(t, err, "Block SIGNATURES should carry OrdererBlockMetadata")
|
|
require.Equal(t, expectedBlockNumber, obm.LastConfig.Index, "SIGNATURES value should point to last config block")
|
|
|
|
metadata = &cb.Metadata{}
|
|
err = proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadata)
|
|
require.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
|
|
lastConfig := &cb.LastConfig{}
|
|
err = proto.Unmarshal(metadata.Value, lastConfig)
|
|
require.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
|
|
require.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block")
|
|
}
|
|
|
|
func testPanic(f func()) (didPanic bool, message interface{}) {
|
|
didPanic = true
|
|
|
|
defer func() {
|
|
message = recover()
|
|
}()
|
|
|
|
f()
|
|
didPanic = false
|
|
|
|
return
|
|
}
|