Skip to content

Commit

Permalink
FAB-15817 Prefer last config in signatures metadata field (hyperledge…
Browse files Browse the repository at this point in the history
…r#363)

This commit updates the orderer and peer to prefer retrieving the
last config from the SIGNATURES field and fallback to retrieving
it from the LAST_CONFIG field.

Signed-off-by: Will Lahti <[email protected]>
  • Loading branch information
wlahti authored and Jason Yellick committed Jan 7, 2020
1 parent 269cb04 commit f26c2b4
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 140 deletions.
5 changes: 5 additions & 0 deletions common/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,10 @@ func (f *factory) Block(channelID string) *cb.Block {
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: 0}),
})
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
LastConfig: &cb.LastConfig{Index: 0},
}),
})
return block
}
39 changes: 29 additions & 10 deletions common/genesis/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,39 @@ package genesis
import (
"testing"

"github.com/gogo/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/assert"
)

func TestBasicSanity(t *testing.T) {
impl := NewFactoryImpl(protoutil.NewConfigGroup())
impl.Block("testchannelid")
}

func TestForTransactionID(t *testing.T) {
func TestFactory(t *testing.T) {
impl := NewFactoryImpl(protoutil.NewConfigGroup())
block := impl.Block("testchannelid")
configEnv, _ := protoutil.ExtractEnvelope(block, 0)
configEnvPayload, _ := protoutil.UnmarshalPayload(configEnv.Payload)
configEnvPayloadChannelHeader, _ := protoutil.UnmarshalChannelHeader(configEnvPayload.GetHeader().ChannelHeader)
assert.NotEmpty(t, configEnvPayloadChannelHeader.TxId, "tx_id of configuration transaction should not be empty")

t.Run("test for transaction id", func(t *testing.T) {
configEnv, _ := protoutil.ExtractEnvelope(block, 0)
configEnvPayload, _ := protoutil.UnmarshalPayload(configEnv.Payload)
configEnvPayloadChannelHeader, _ := protoutil.UnmarshalChannelHeader(configEnvPayload.GetHeader().ChannelHeader)
assert.NotEmpty(t, configEnvPayloadChannelHeader.TxId, "tx_id of configuration transaction should not be empty")
})
t.Run("test for last config in SIGNATURES field", func(t *testing.T) {
metadata := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], metadata)
assert.NoError(t, err)
ordererBlockMetadata := &cb.OrdererBlockMetadata{}
err = proto.Unmarshal(metadata.Value, ordererBlockMetadata)
assert.NoError(t, err)
assert.NotNil(t, ordererBlockMetadata.LastConfig)
assert.Equal(t, uint64(0), ordererBlockMetadata.LastConfig.Index)
})
t.Run("test for last config in LAST_CONFIG field", func(t *testing.T) {
metadata := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadata)
assert.NoError(t, err)
lastConfig := &cb.LastConfig{}
err = proto.Unmarshal(metadata.Value, lastConfig)
assert.NoError(t, err)
assert.Equal(t, uint64(0), lastConfig.Index)
})
}
2 changes: 1 addition & 1 deletion common/ledger/blockledger/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (nfei *NotFoundErrorIterator) Close() {}
func CreateNextBlock(rl Reader, messages []*cb.Envelope) *cb.Block {
var nextBlockNumber uint64
var previousBlockHash []byte
var err error

if rl.Height() > 0 {
it, _ := rl.Iterator(&ab.SeekPosition{
Expand All @@ -63,7 +64,6 @@ func CreateNextBlock(rl Reader, messages []*cb.Envelope) *cb.Block {
Data: make([][]byte, len(messages)),
}

var err error
for i, msg := range messages {
data.Data[i], err = proto.Marshal(msg)
if err != nil {
Expand Down
16 changes: 7 additions & 9 deletions internal/peer/channel/fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,18 @@ func getMockDeliverService(block *cb.Block) *mock.DeliverService {
}

func createTestBlock() *cb.Block {
lc := &cb.LastConfig{Index: 0}
lcBytes := protoutil.MarshalOrPanic(lc)
metadata := &cb.Metadata{
Value: lcBytes,
}
metadataBytes := protoutil.MarshalOrPanic(metadata)
blockMetadata := make([][]byte, cb.BlockMetadataIndex_LAST_CONFIG+1)
blockMetadata[cb.BlockMetadataIndex_LAST_CONFIG] = metadataBytes
metadataBytes := protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
LastConfig: &cb.LastConfig{Index: 0},
}),
})

block := &cb.Block{
Header: &cb.BlockHeader{
Number: 0,
},
Metadata: &cb.BlockMetadata{
Metadata: blockMetadata,
Metadata: [][]byte{metadataBytes},
},
}

Expand Down
15 changes: 7 additions & 8 deletions orderer/common/cluster/replication.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func PullLastConfigBlock(puller ChainPuller) (*common.Block, error) {
if lastBlock == nil {
return nil, ErrRetryCountExhausted
}
lastConfNumber, err := lastConfigFromBlock(lastBlock)
lastConfNumber, err := protoutil.GetLastConfigIndexFromBlock(lastBlock)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -510,13 +510,6 @@ func latestHeightAndEndpoint(puller ChainPuller) (string, uint64, error) {
return mostUpToDateEndpoint, maxHeight, nil
}

func lastConfigFromBlock(block *common.Block) (uint64, error) {
if block.Metadata == nil || len(block.Metadata.Metadata) <= int(common.BlockMetadataIndex_LAST_CONFIG) {
return 0, errors.New("no metadata in block")
}
return protoutil.GetLastConfigIndexFromBlock(block)
}

// Close closes the ChainInspector
func (ci *ChainInspector) Close() {
ci.Puller.Close()
Expand Down Expand Up @@ -663,11 +656,17 @@ func ExtractGenesisBlock(logger *flogging.FabricLogger, block *common.Block) (st
metadata := &common.BlockMetadata{
Metadata: make([][]byte, 4),
}
metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{
LastConfig: &common.LastConfig{Index: 0},
// This is a genesis block, peer never verify this signature because we can't bootstrap
// trust from an earlier block, hence there are no signatures here.
})
metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 0}),
// This is a genesis block, peer never verify this signature because we can't bootstrap
// trust from an earlier block, hence there are no signatures here.
})

blockdata := &common.BlockData{Data: [][]byte{payload.Data}}
b := &common.Block{
Header: &common.BlockHeader{DataHash: protoutil.BlockDataHash(blockdata)},
Expand Down
40 changes: 27 additions & 13 deletions orderer/common/cluster/replication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,34 +724,48 @@ func TestParticipant(t *testing.T) {
},
latestBlockSeq: uint64(99),
latestBlock: &common.Block{},
expectedError: "no metadata in block",
expectedError: "failed to retrieve metadata: no metadata in block",
},
{
name: "Pulled block has no last config sequence in metadata",
heightsByEndpoints: map[string]uint64{
"orderer.example.com:7050": 100,
},
latestBlockSeq: uint64(99),
latestBlock: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: nil,
},
},
expectedError: "failed to retrieve metadata: no metadata at index [SIGNATURES]",
},
{
name: "Pulled block's SIGNATURES metadata is malformed",
heightsByEndpoints: map[string]uint64{
"orderer.example.com:7050": 100,
},
latestBlockSeq: uint64(99),
latestBlock: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{1, 2, 3}},
},
},
expectedError: "no metadata in block",
expectedError: "failed to retrieve metadata: error unmarshaling metadata" +
" at index [SIGNATURES]: proto: common.Metadata: illegal tag 0 (wire type 1)",
},
{
name: "Pulled block's metadata is malformed",
name: "Pulled block's LAST_CONFIG metadata is malformed",
heightsByEndpoints: map[string]uint64{
"orderer.example.com:7050": 100,
},
latestBlockSeq: uint64(99),
latestBlock: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{1, 2, 3}, {1, 2, 3}},
Metadata: [][]byte{{}, {1, 2, 3}},
},
},
expectedError: "error unmarshaling metadata from" +
" block at index [LAST_CONFIG]: proto: common.Metadata: illegal tag 0 (wire type 1)",
expectedError: "failed to retrieve metadata: error unmarshaling metadata" +
" at index [LAST_CONFIG]: proto: common.Metadata: illegal tag 0 (wire type 1)",
},
{
name: "Pulled block's metadata is valid and has a last config",
Expand All @@ -761,9 +775,9 @@ func TestParticipant(t *testing.T) {
latestBlockSeq: uint64(99),
latestBlock: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{1, 2, 3}, protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.LastConfig{
Index: 42,
Metadata: [][]byte{protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{
LastConfig: &common.LastConfig{Index: 42},
}),
})},
},
Expand All @@ -790,9 +804,9 @@ func TestParticipant(t *testing.T) {
latestBlockSeq: uint64(99),
latestBlock: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{1, 2, 3}, protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.LastConfig{
Index: 42,
Metadata: [][]byte{protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{
LastConfig: &common.LastConfig{Index: 42},
}),
})},
},
Expand All @@ -819,7 +833,7 @@ func TestParticipant(t *testing.T) {
assert.Len(t, configBlocks, 0)
} else {
assert.Len(t, configBlocks, 1)
assert.Equal(t, err, testCase.predicateReturns)
assert.Equal(t, testCase.predicateReturns, err)
}
})
}
Expand Down
3 changes: 0 additions & 3 deletions orderer/common/cluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,9 +652,6 @@ func LastConfigBlock(block *common.Block, blockRetriever BlockRetriever) (*commo
if blockRetriever == nil {
return nil, errors.New("nil blockRetriever")
}
if block.Metadata == nil || len(block.Metadata.Metadata) <= int(common.BlockMetadataIndex_LAST_CONFIG) {
return nil, errors.New("no metadata in block")
}
lastConfigBlockNum, err := protoutil.GetLastConfigIndexFromBlock(block)
if err != nil {
return nil, err
Expand Down
23 changes: 1 addition & 22 deletions orderer/common/cluster/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,31 +891,10 @@ func TestLastConfigBlock(t *testing.T) {
},
{
name: "nil metadata",
expectedError: "no metadata in block",
expectedError: "failed to retrieve metadata: no metadata in block",
blockRetriever: blockRetriever,
block: &common.Block{},
},
{
name: "no last config block metadata",
expectedError: "no metadata in block",
blockRetriever: blockRetriever,
block: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{}},
},
},
},
{
name: "bad metadata in block",
blockRetriever: blockRetriever,
expectedError: "error unmarshaling metadata from block at index " +
"[LAST_CONFIG]: proto: common.Metadata: illegal tag 0 (wire type 1)",
block: &common.Block{
Metadata: &common.BlockMetadata{
Metadata: [][]byte{{}, {1, 2, 3}},
},
},
},
{
name: "no block with index",
block: &common.Block{
Expand Down
19 changes: 19 additions & 0 deletions orderer/common/multichannel/blockwriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"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"
Expand Down Expand Up @@ -366,3 +367,21 @@ func TestRaceWriteConfig(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, consenterMetadata1, omd.Value)
}

func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) {
metadata := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], metadata)
assert.NoError(t, err, "Block should carry SIGNATURES metadata item")
obm := &cb.OrdererBlockMetadata{}
err = proto.Unmarshal(metadata.Value, obm)
assert.NoError(t, err, "Block SIGNATURES should carry OrdererBlockMetadata")
assert.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)
assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
lastConfig := &cb.LastConfig{}
err = proto.Unmarshal(metadata.Value, lastConfig)
assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
assert.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block")
}
25 changes: 8 additions & 17 deletions orderer/common/multichannel/registrar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ func testMessageOrderAndRetrieval(maxMessageCount uint32, chainID string, chainS
}

func TestConfigTx(t *testing.T) {
//system channel
// system channel
confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
genesisBlockSys := encoder.New(confSys).GenesisBlock()

// Tests for a normal chain which contains 3 config transactions and other normal transactions to make
// sure the right one returned
// Tests for a normal channel which contains 3 config transactions and other
// normal transactions to make sure the right one returned
t.Run("GetConfigTx - ok", func(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "registrar_test-")
require.NoError(t, err)
Expand All @@ -131,8 +131,12 @@ func TestConfigTx(t *testing.T) {
ctx := makeConfigTx("testchannelid", 6)
rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{ctx}))

// block with LAST_CONFIG metadata in SIGNATURES field
block := blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", 7)})
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: 7})})
blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
LastConfig: &cb.LastConfig{Index: 7},
})
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: blockSignatureValue})
rl.Append(block)

pctx := configTx(rl)
Expand Down Expand Up @@ -262,7 +266,6 @@ func TestCreateChain(t *testing.T) {

// This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain
t.Run("New chain", func(t *testing.T) {
expectedLastConfigBlockNumber := uint64(0)
expectedLastConfigSeq := uint64(1)
newChainID := "test-new-chain"

Expand Down Expand Up @@ -332,7 +335,6 @@ func TestCreateChain(t *testing.T) {
if status != cb.Status_SUCCESS {
t.Fatalf("Could not retrieve new chain genesis block")
}
testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber)
if len(block.Data.Data) != 1 {
t.Fatalf("Should have had only one message in the new genesis block")
}
Expand All @@ -343,7 +345,6 @@ func TestCreateChain(t *testing.T) {
if status != cb.Status_SUCCESS {
t.Fatalf("Could not retrieve block on new chain")
}
testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber)
for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ {
if !proto.Equal(protoutil.ExtractEnvelopeOrPanic(block, i), messages[i]) {
t.Errorf("Block contents wrong at index %d in new chain", i)
Expand All @@ -357,16 +358,6 @@ func TestCreateChain(t *testing.T) {
})
}

func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) {
metadataItem := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadataItem)
assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
lastConfig := &cb.LastConfig{}
err = proto.Unmarshal(metadataItem.Value, lastConfig)
assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
assert.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block")
}

func TestResourcesCheck(t *testing.T) {
mockOrderer := &mocks.OrdererConfig{}
mockOrdererCaps := &mocks.OrdererCapabilities{}
Expand Down
5 changes: 5 additions & 0 deletions orderer/common/server/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ func TestExtractSysChanLastConfig(t *testing.T) {
require.NoError(t, err)

nextBlock := blockledger.CreateNextBlock(rl, []*common.Envelope{configTx})
nextBlock.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{
LastConfig: &common.LastConfig{Index: rl.Height()},
}),
})
nextBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{
Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}),
})
Expand Down
Loading

0 comments on commit f26c2b4

Please sign in to comment.