Skip to content

Commit

Permalink
FAB-17177] Config block shouldn't verify itself in block replication
Browse files Browse the repository at this point in the history
The cluster replication verifies blocks by pulling them in batches,
and performs hash chain verification + verifies the signatures of the last block in the chain.

It uses the bundle stored in the global config structures,
but in case it comes across a config block in the middle of the batch,
it switches to use a bundle constructed from that config block.

However, if the config block is the last block in the batch -
it accidentally verifies it twice:
 - once with the config block before it (as expected)
 - once with the config block itself (which is unexpected).

This change set simply adds a check to see if the last block in the batch
is a config block, and if so - doesn't verify it if it was already verified.

Change-Id: Id123c36e445a21b3081273ef0395aae759162818
Signed-off-by: yacovm <yacovm@il.ibm.com>
  • Loading branch information
yacovm authored and mastersingh24 committed Dec 19, 2019
1 parent 0ce6685 commit 778f87b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 8 deletions.
9 changes: 9 additions & 0 deletions orderer/common/cluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,14 @@ func VerifyBlocks(blockBuff []*common.Block, signatureVerifier BlockVerifier) er
}

var config *common.ConfigEnvelope
var isLastBlockConfigBlock bool
// Verify all configuration blocks that are found inside the block batch,
// with the configuration that was committed (nil) or with one that is picked up
// during iteration over the block batch.
for _, block := range blockBuff {
configFromBlock, err := ConfigFromBlock(block)
if err == errNotAConfig {
isLastBlockConfigBlock = false
continue
}
if err != nil {
Expand All @@ -226,10 +228,17 @@ func VerifyBlocks(blockBuff []*common.Block, signatureVerifier BlockVerifier) er
return err
}
config = configFromBlock
isLastBlockConfigBlock = true
}

// Verify the last block's signature
lastBlock := blockBuff[len(blockBuff)-1]

// If last block is a config block, we verified it using the policy of the previous block, so it's valid.
if isLastBlockConfigBlock {
return nil
}

return VerifyBlockSignature(lastBlock, signatureVerifier, config)
}

Expand Down
61 changes: 54 additions & 7 deletions orderer/common/cluster/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,11 @@ func TestVerifyBlocks(t *testing.T) {
}

for _, testCase := range []struct {
name string
configureVerifier func(*mocks.BlockVerifier)
mutateBlockSequence func([]*common.Block) []*common.Block
expectedError string
name string
configureVerifier func(*mocks.BlockVerifier)
mutateBlockSequence func([]*common.Block) []*common.Block
expectedError string
verifierExpectedCalls int
}{
{
name: "empty sequence",
Expand Down Expand Up @@ -375,7 +376,8 @@ func TestVerifyBlocks(t *testing.T) {
var nilEnvelope *common.ConfigEnvelope
verifier.On("VerifyBlockSignature", mock.Anything, nilEnvelope).Return(errors.New("bad signature"))
},
expectedError: "bad signature",
expectedError: "bad signature",
verifierExpectedCalls: 1,
},
{
name: "block that its type cannot be classified",
Expand Down Expand Up @@ -423,10 +425,11 @@ func TestVerifyBlocks(t *testing.T) {
proto.Unmarshal(utils.MarshalOrPanic(configEnvelope1), confEnv1)
verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(errors.New("bad signature")).Once()
},
expectedError: "bad signature",
expectedError: "bad signature",
verifierExpectedCalls: 2,
},
{
name: "config block in the sequence needs to be verified, and it isproperly signed",
name: "config block in the sequence needs to be verified, and it is properly signed",
mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block {
var err error
// Put a config transaction in block n / 4
Expand All @@ -452,6 +455,48 @@ func TestVerifyBlocks(t *testing.T) {
verifier.On("VerifyBlockSignature", sigSet1, nilEnvelope).Return(nil).Once()
verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(nil).Once()
},
// We have a single config block in the 'middle' of the chain, so we have 2 verifications total:
// The last block, and the config block.
verifierExpectedCalls: 2,
},
{
name: "last two blocks are config blocks, last block only verified once",
mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block {
var err error
// Put a config transaction in block n-2 and in n-1
blockSequence[len(blockSequence)-2].Data = &common.BlockData{
Data: [][]byte{utils.MarshalOrPanic(configTransaction(configEnvelope1))},
}
blockSequence[len(blockSequence)-2].Header.DataHash = blockSequence[len(blockSequence)-2].Data.Hash()

blockSequence[len(blockSequence)-1].Data = &common.BlockData{
Data: [][]byte{utils.MarshalOrPanic(configTransaction(configEnvelope2))},
}
blockSequence[len(blockSequence)-1].Header.DataHash = blockSequence[len(blockSequence)-1].Data.Hash()

assignHashes(blockSequence)

sigSet1, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)-2])
assert.NoError(t, err)

sigSet2, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)-1])
assert.NoError(t, err)

return blockSequence
},
configureVerifier: func(verifier *mocks.BlockVerifier) {
var nilEnvelope *common.ConfigEnvelope
confEnv1 := &common.ConfigEnvelope{}
proto.Unmarshal(utils.MarshalOrPanic(configEnvelope1), confEnv1)
verifier.On("VerifyBlockSignature", sigSet1, nilEnvelope).Return(nil).Once()
// We ensure that the signature set of the last block is verified using the config envelope of the block
// before it.
verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(nil).Once()
// Note that we do not record a call to verify the last block, with the config envelope extracted from the block itself.
},
// We have 2 config blocks, yet we only verify twice- the first config block, and the next config block, but no more,
// since the last block is a config block.
verifierExpectedCalls: 2,
},
} {
testCase := testCase
Expand All @@ -465,6 +510,8 @@ func TestVerifyBlocks(t *testing.T) {
err := cluster.VerifyBlocks(blockchain, verifier)
if testCase.expectedError != "" {
assert.EqualError(t, err, testCase.expectedError)
} else {
assert.NoError(t, err)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion orderer/common/server/onboarding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ func TestReplicate(t *testing.T) {
},
{
name: "Explicit replication is requested, but the channel shouldn't be pulled",
verificationCount: 20,
verificationCount: 10,
shouldConnect: true,
systemLedgerHeight: 10,
bootBlock: &bootBlock,
Expand Down

0 comments on commit 778f87b

Please sign in to comment.