Unverified Commit fc87b4ff authored by yacovm's avatar yacovm Committed by Artem Barger
Browse files

[FAB-14041] Validate boot block is system channel block



This change set makes the orderer validate that the bootstrap block
it is spawned with (or created from configtx.yaml), contains
a ConsortiumsConfig.

Change-Id: I26abf8ac8719fbb472351b036137debc7a911665
Signed-off-by: default avataryacovm <yacovm@il.ibm.com>
parent 9e9000a2
......@@ -45,6 +45,16 @@ func TestSpawnEtcdRaft(t *testing.T) {
defer gexec.CleanupBuildArtifacts()
t.Run("EtcdRaft launch failure", func(t *testing.T) {
testEtcdRaftOSNFailure(gt, tempDir, orderer, fabricRootDir)
})
t.Run("EtcdRaft launch success", func(t *testing.T) {
testEtcdRaftOSNSuccess(gt, tempDir, configtxgen, cwd, orderer, fabricRootDir)
})
}
func testEtcdRaftOSNSuccess(gt *GomegaWithT, tempDir, configtxgen, cwd, orderer, fabricRootDir string) {
// Create the genesis block for the system channel
genesisBlockPath := filepath.Join(tempDir, "genesis.block")
cmd := exec.Command(configtxgen, "-channelID", "system", "-profile", "SampleDevModeEtcdRaft",
......@@ -57,7 +67,7 @@ func TestSpawnEtcdRaft(t *testing.T) {
gt.Expect(configtxgenProcess.Err).To(gbytes.Say("Writing genesis block"))
// Launch the OSN
ordererProcess := launchOrderer(gt, cmd, orderer, tempDir, genesisBlockPath, fabricRootDir)
ordererProcess := launchOrderer(gt, orderer, tempDir, genesisBlockPath, fabricRootDir)
defer ordererProcess.Kill()
// General.Cluster.ReplicationMaxRetries is not specified in the orderer.yaml, so let's ensure
// it is really configured autonomously via the localconfig code.
......@@ -68,12 +78,31 @@ func TestSpawnEtcdRaft(t *testing.T) {
gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
}
func launchOrderer(gt *GomegaWithT, cmd *exec.Cmd, orderer, tempDir, genesisBlockPath, fabricRootDir string) *gexec.Session {
func testEtcdRaftOSNFailure(gt *GomegaWithT, tempDir, orderer, fabricRootDir string) {
// Grab an application channel genesis block
genesisBlockPath := filepath.Join(filepath.Join("testdata", "mychannel.block"))
genesisBlockBytes, err := ioutil.ReadFile(genesisBlockPath)
gt.Expect(err).NotTo(HaveOccurred())
// Copy it to the designated location in the temporary folder
genesisBlockPath = filepath.Join(tempDir, "genesis.block")
err = ioutil.WriteFile(genesisBlockPath, genesisBlockBytes, 0644)
gt.Expect(err).NotTo(HaveOccurred())
// Launch the OSN
ordererProcess := launchOrderer(gt, orderer, tempDir, genesisBlockPath, fabricRootDir)
defer ordererProcess.Kill()
expectedErr := "Failed validating bootstrap block: the block isn't a system channel block because it lacks ConsortiumsConfig"
gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say(expectedErr))
}
func launchOrderer(gt *GomegaWithT, orderer, tempDir, genesisBlockPath, fabricRootDir string) *gexec.Session {
cwd, err := filepath.Abs(".")
gt.Expect(err).NotTo(HaveOccurred())
// Launch the orderer process
cmd = exec.Command(orderer)
cmd := exec.Command(orderer)
cmd.Env = []string{
"ORDERER_GENERAL_LISTENPORT=5611",
"ORDERER_GENERAL_GENESISMETHOD=file",
......
......@@ -94,6 +94,10 @@ func Main() {
// Start provides a layer of abstraction for benchmark test
func Start(cmd string, conf *localconfig.TopLevel) {
bootstrapBlock := extractBootstrapBlock(conf)
if err := ValidateBootstrapBlock(bootstrapBlock); err != nil {
logger.Panicf("Failed validating bootstrap block: %v", err)
}
clusterType := isClusterType(bootstrapBlock)
signer := localmsp.NewSigner()
......
......@@ -10,6 +10,8 @@ import (
"sync"
"time"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/crypto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/ledger/blockledger"
......@@ -19,6 +21,7 @@ import (
"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/pkg/errors"
)
const (
......@@ -310,3 +313,31 @@ func (vl *verifierLoader) loadVerifiers() verifiersByChannel {
return res
}
// ValidateBootstrapBlock returns whether this block can be used as a bootstrap block.
// A bootstrap block is a block of a system channel, and needs to have a ConsortiumsConfig.
func ValidateBootstrapBlock(block *common.Block) error {
if block == nil {
return errors.New("nil block")
}
if block.Data == nil || len(block.Data.Data) == 0 {
return errors.New("empty block data")
}
firstTransaction := &common.Envelope{}
if err := proto.Unmarshal(block.Data.Data[0], firstTransaction); err != nil {
return errors.Wrap(err, "failed extracting envelope from block")
}
bundle, err := channelconfig.NewBundleFromEnvelope(firstTransaction)
if err != nil {
return err
}
_, exists := bundle.ConsortiumsConfig()
if !exists {
return errors.New("the block isn't a system channel block because it lacks ConsortiumsConfig")
}
return nil
}
......@@ -1035,3 +1035,64 @@ func TestVerifierLoader(t *testing.T) {
})
}
}
func TestValidateBootstrapBlock(t *testing.T) {
systemChannelBlockBytes, err := ioutil.ReadFile(filepath.Join("testdata", "system.block"))
assert.NoError(t, err)
applicationChannelBlockBytes, err := ioutil.ReadFile(filepath.Join("testdata", "mychannel.block"))
assert.NoError(t, err)
appBlock := &common.Block{}
err = proto.Unmarshal(applicationChannelBlockBytes, appBlock)
assert.NoError(t, err)
systemBlock := &common.Block{}
err = proto.Unmarshal(systemChannelBlockBytes, systemBlock)
assert.NoError(t, err)
for _, testCase := range []struct {
description string
block *common.Block
expectedError string
}{
{
description: "nil block",
expectedError: "nil block",
},
{
description: "empty block",
block: &common.Block{},
expectedError: "empty block data",
},
{
description: "bad envelope",
block: &common.Block{
Data: &common.BlockData{
Data: [][]byte{{1, 2, 3}},
},
},
expectedError: "failed extracting envelope from block: " +
"proto: common.Envelope: illegal tag 0 (wire type 1)",
},
{
description: "application channel block",
block: appBlock,
expectedError: "the block isn't a system channel block because it lacks ConsortiumsConfig",
},
{
description: "system channel block",
block: systemBlock,
},
} {
t.Run(testCase.description, func(t *testing.T) {
err := ValidateBootstrapBlock(testCase.block)
if testCase.expectedError == "" {
assert.NoError(t, err)
return
}
assert.EqualError(t, err, testCase.expectedError)
})
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment