From 4887bf4c1a752bdb6e1300e53dfd2648b97c6409 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Sat, 18 Feb 2017 01:36:17 -0500 Subject: [PATCH] [FAB-2349] Change channel create to CONFIG_UPDATE https://jira.hyperledger.org/browse/FAB-2349 Channel creation is currently being done via a CONFIG transaction but this needs to be modified to be a CONFIG_UPDATE transaction. This will enable future support of partial config channel creation. Change-Id: I7bc116120adf411f0020a827fcd6c7a36d0d8b66 Signed-off-by: Jason Yellick --- common/configtx/api/api.go | 7 +- common/configtx/manager.go | 75 +++++++++++---- common/configtx/manager_test.go | 99 +++++++------------- common/configtx/template.go | 28 +----- common/mocks/configtx/configtx.go | 19 +++- core/committer/txvalidator/validator.go | 4 +- core/common/validation/config_test.go | 21 ++++- core/common/validation/msgvalidation.go | 3 + core/mocks/txvalidator/support.go | 2 +- orderer/common/broadcast/broadcast.go | 2 +- orderer/common/broadcast/broadcast_test.go | 2 +- orderer/common/configtxfilter/filter.go | 6 +- orderer/common/configtxfilter/filter_test.go | 2 +- orderer/multichain/manager_test.go | 22 +++-- orderer/multichain/systemchain.go | 73 ++++++++++++++- orderer/multichain/systemchain_test.go | 20 +++- orderer/multichain/util_test.go | 39 ++------ 17 files changed, 252 insertions(+), 172 deletions(-) diff --git a/common/configtx/api/api.go b/common/configtx/api/api.go index 63ba8b0abff..3d6a3d9b6bd 100644 --- a/common/configtx/api/api.go +++ b/common/configtx/api/api.go @@ -29,10 +29,13 @@ type Manager interface { Resources // Apply attempts to apply a configtx to become the new config - Apply(configtx *cb.Envelope) error + Apply(configEnv *cb.ConfigEnvelope) error + + // Validate attempts to apply a configtx to become the new config + Validate(configEnv *cb.ConfigEnvelope) error // Validate attempts to validate a new configtx against the current config state - Validate(configtx *cb.Envelope) error + ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) // ConfigEnvelope returns the *cb.ConfigEnvelope from the last successful Apply ConfigEnvelope() *cb.ConfigEnvelope diff --git a/common/configtx/manager.go b/common/configtx/manager.go index dc9bf1ae5e9..f67908f6d37 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -18,6 +18,7 @@ package configtx import ( "fmt" + "reflect" "regexp" "github.com/hyperledger/fabric/common/configtx/api" @@ -146,49 +147,92 @@ func (cm *configManager) commitCallbacks() { } // Validate attempts to validate a new configtx against the current config state -func (cm *configManager) Validate(configtx *cb.Envelope) error { +// It requires an Envelope of type CONFIG_UPDATE +func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) { configUpdateEnv, err := envelopeToConfigUpdate(configtx) if err != nil { - return err + return nil, err } configMap, err := cm.authorizeUpdate(configUpdateEnv) if err != nil { - return err + return nil, err } channelGroup, err := configMapToConfig(configMap) if err != nil { - return fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) + return nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) } result, err := cm.processConfig(channelGroup) if err != nil { - return err + return nil, err } result.rollback() - return nil + + return &cb.ConfigEnvelope{ + Config: &cb.Config{ + Header: &cb.ChannelHeader{ + ChannelId: cm.chainID, + }, + Channel: channelGroup, + }, + LastUpdate: configtx, + }, nil } -// Apply attempts to apply a configtx to become the new config -func (cm *configManager) Apply(configtx *cb.Envelope) error { - configUpdateEnv, err := envelopeToConfigUpdate(configtx) +func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) { + if configEnv == nil { + return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope") + } + + configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate) if err != nil { - return err + return nil, nil, err } configMap, err := cm.authorizeUpdate(configUpdateEnv) if err != nil { - return err + return nil, nil, err } channelGroup, err := configMapToConfig(configMap) if err != nil { - return fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) + return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) + } + + if configEnv.Config == nil { + return nil, nil, fmt.Errorf("Config cannot be nil") + } + + if !reflect.DeepEqual(channelGroup, configEnv.Config.Channel) { + return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result") } result, err := cm.processConfig(channelGroup) + if err != nil { + return nil, nil, err + } + + return configMap, result, nil +} + +// Validate simulates applying a ConfigEnvelope to become the new config +func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error { + _, result, err := cm.prepareApply(configEnv) + if err != nil { + return err + } + + result.rollback() + + return nil +} + +// Apply attempts to apply a ConfigEnvelope to become the new config +func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error { + configMap, result, err := cm.prepareApply(configEnv) if err != nil { return err } @@ -199,13 +243,6 @@ func (cm *configManager) Apply(configtx *cb.Envelope) error { cm.config = configMap cm.sequence++ - cm.configEnv = &cb.ConfigEnvelope{ - Config: &cb.Config{ - // XXX add header - Channel: channelGroup, - }, - LastUpdate: configtx, - } return nil } diff --git a/common/configtx/manager_test.go b/common/configtx/manager_test.go index 2248cca5f9f..ed3e1abea2a 100644 --- a/common/configtx/manager_test.go +++ b/common/configtx/manager_test.go @@ -134,14 +134,9 @@ func TestDifferentChainID(t *testing.T) { newConfig := makeConfigUpdateEnvelope("wrongChain", makeConfigPair("foo", "foo", 1, []byte("foo"))) - err = cm.Validate(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored when validating a new config set the wrong chain ID") - } - - err = cm.Apply(newConfig) - if err == nil { - t.Error("Should have errored when applying a new config with the wrong chain ID") + t.Error("Should have errored when proposing a new config set the wrong chain ID") } } @@ -157,14 +152,9 @@ func TestOldConfigReplay(t *testing.T) { newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))) - err = cm.Validate(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored when validating a config that is not a newer sequence number") - } - - err = cm.Apply(newConfig) - if err == nil { - t.Error("Should have errored when applying a config that is not a newer sequence number") + t.Error("Should have errored when proposing a config that is not a newer sequence number") } } @@ -180,12 +170,17 @@ func TestValidConfigChange(t *testing.T) { newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))) - err = cm.Validate(newConfig) + configEnv, err := cm.ProposeConfigUpdate(newConfig) + if err != nil { + t.Errorf("Should not have errored proposing config: %s", err) + } + + err = cm.Validate(configEnv) if err != nil { t.Errorf("Should not have errored validating config: %s", err) } - err = cm.Apply(newConfig) + err = cm.Apply(configEnv) if err != nil { t.Errorf("Should not have errored applying config: %s", err) } @@ -208,14 +203,9 @@ func TestConfigChangeRegressedSequence(t *testing.T) { makeConfigPair("bar", "bar", 2, []byte("bar")), ) - err = cm.Validate(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored validating config because foo's sequence number regressed") - } - - err = cm.Apply(newConfig) - if err == nil { - t.Error("Should have errored applying config because foo's sequence number regressed") + t.Error("Should have errored proposing config because foo's sequence number regressed") } } @@ -236,14 +226,9 @@ func TestConfigChangeOldSequence(t *testing.T) { makeConfigPair("bar", "bar", 1, []byte("bar")), ) - err = cm.Validate(newConfig) - if err == nil { - t.Error("Should have errored validating config because bar was new but its sequence number was old") - } - - err = cm.Apply(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored applying config because bar was new but its sequence number was old") + t.Error("Should have errored proposing config because bar was new but its sequence number was old") } } @@ -267,14 +252,9 @@ func TestConfigImplicitDelete(t *testing.T) { makeConfigPair("bar", "bar", 1, []byte("bar")), ) - err = cm.Validate(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored validating config because foo was implicitly deleted") - } - - err = cm.Apply(newConfig) - if err == nil { - t.Error("Should have errored applying config because foo was implicitly deleted") + t.Error("Should have errored proposing config because foo was implicitly deleted") } } @@ -290,14 +270,9 @@ func TestEmptyConfigUpdate(t *testing.T) { newConfig := &cb.Envelope{} - err = cm.Validate(newConfig) - if err == nil { - t.Error("Should not errored validating config because new config is empty") - } - - err = cm.Apply(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should not errored applying config because new config is empty") + t.Error("Should not errored proposing config because new config is empty") } } @@ -323,14 +298,9 @@ func TestSilentConfigModification(t *testing.T) { makeConfigPair("bar", "bar", 1, []byte("bar")), ) - err = cm.Validate(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should not errored validating config because foo was silently modified (despite modification allowed by policy)") - } - - err = cm.Apply(newConfig) - if err == nil { - t.Error("Should not errored applying config because foo was silently modified (despite modification allowed by policy)") + t.Error("Should have errored proposing config because foo was silently modified (despite modification allowed by policy)") } } @@ -350,14 +320,9 @@ func TestConfigChangeViolatesPolicy(t *testing.T) { newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))) - err = cm.Validate(newConfig) - if err == nil { - t.Error("Should have errored validating config because policy rejected modification") - } - - err = cm.Apply(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored applying config because policy rejected modification") + t.Error("Should have errored proposing config because policy rejected modification") } } @@ -383,12 +348,17 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) { makeConfigPair("bar", "bar", 1, []byte("foo")), ) - err = cm.Validate(newConfig) + configEnv, err := cm.ProposeConfigUpdate(newConfig) + if err != nil { + t.Errorf("Should not have errored proposing config, but got %s", err) + } + + err = cm.Validate(configEnv) if err != nil { t.Errorf("Should not have errored validating config, but got %s", err) } - err = cm.Apply(newConfig) + err = cm.Apply(configEnv) if err != nil { t.Errorf("Should not have errored applying config, but got %s", err) } @@ -410,14 +380,9 @@ func TestInvalidProposal(t *testing.T) { newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))) - err = cm.Validate(newConfig) - if err == nil { - t.Error("Should have errored validating config because the handler rejected it") - } - - err = cm.Apply(newConfig) + _, err = cm.ProposeConfigUpdate(newConfig) if err == nil { - t.Error("Should have errored applying config because the handler rejected it") + t.Error("Should have errored proposing config because the handler rejected it") } } diff --git a/common/configtx/template.go b/common/configtx/template.go index 021b1bdda39..b1da257b278 100644 --- a/common/configtx/template.go +++ b/common/configtx/template.go @@ -195,34 +195,10 @@ func MakeChainCreationTransaction(creationPolicy string, chainID string, signer return nil, err } - configUpdate, err := UnmarshalConfigUpdate(newConfigUpdateEnv.ConfigUpdate) - if err != nil { - return nil, err - } - - newConfigEnv := &cb.ConfigEnvelope{ - // Config is an XXX temporary workaround until the orderer generates the real configtx from the WriteSet - Config: &cb.Config{ - Header: configUpdate.Header, - Channel: configUpdate.WriteSet, - }, - LastUpdate: &cb.Envelope{ - Payload: utils.MarshalOrPanic(&cb.Payload{ - Header: &cb.Header{ - ChannelHeader: &cb.ChannelHeader{ - ChannelId: chainID, - Type: int32(cb.HeaderType_CONFIG_UPDATE), - }, - }, - Data: utils.MarshalOrPanic(newConfigUpdateEnv), - }), - }, - } - - payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG, msgVersion, chainID, epoch) + payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, chainID, epoch) payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic()) payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader) - payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(newConfigEnv)} + payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(newConfigUpdateEnv)} paylBytes := utils.MarshalOrPanic(payload) // sign the payload diff --git a/common/mocks/configtx/configtx.go b/common/mocks/configtx/configtx.go index 0fb94eba738..29ca7a42a4e 100644 --- a/common/mocks/configtx/configtx.go +++ b/common/mocks/configtx/configtx.go @@ -159,10 +159,16 @@ type Manager struct { ApplyVal error // AppliedConfigUpdateEnvelope is set by Apply - AppliedConfigUpdateEnvelope *cb.Envelope + AppliedConfigUpdateEnvelope *cb.ConfigEnvelope // ValidateVal is returned by Validate ValidateVal error + + // ProposeConfigUpdateError is returned as the error value for ProposeConfigUpdate + ProposeConfigUpdateError error + + // ProposeConfigUpdateVal is returns as the value for ProposeConfigUpdate + ProposeConfigUpdateVal *cb.ConfigEnvelope } // ConfigEnvelope is currently unimplemented @@ -180,13 +186,18 @@ func (cm *Manager) Sequence() uint64 { return cm.SequenceVal } +// ProposeConfigUpdate +func (cm *Manager) ProposeConfigUpdate(update *cb.Envelope) (*cb.ConfigEnvelope, error) { + return cm.ProposeConfigUpdateVal, cm.ProposeConfigUpdateError +} + // Apply returns ApplyVal -func (cm *Manager) Apply(configtx *cb.Envelope) error { - cm.AppliedConfigUpdateEnvelope = configtx +func (cm *Manager) Apply(configEnv *cb.ConfigEnvelope) error { + cm.AppliedConfigUpdateEnvelope = configEnv return cm.ApplyVal } // Validate returns ValidateVal -func (cm *Manager) Validate(configtx *cb.Envelope) error { +func (cm *Manager) Validate(configEnv *cb.ConfigEnvelope) error { return cm.ValidateVal } diff --git a/core/committer/txvalidator/validator.go b/core/committer/txvalidator/validator.go index 094458fc3ef..64412d303a2 100644 --- a/core/committer/txvalidator/validator.go +++ b/core/committer/txvalidator/validator.go @@ -43,7 +43,7 @@ type Support interface { MSPManager() msp.MSPManager // Apply attempts to apply a configtx to become the new config - Apply(configtx *common.Envelope) error + Apply(configtx *common.ConfigEnvelope) error } //Validator interface which defines API to validate block transactions @@ -149,7 +149,7 @@ func (v *txValidator) Validate(block *common.Block) error { return err } - if err := v.support.Apply(configEnvelope.LastUpdate); err != nil { + if err := v.support.Apply(configEnvelope); err != nil { err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err) logger.Critical(err) return err diff --git a/core/common/validation/config_test.go b/core/common/validation/config_test.go index 0356f56d79a..2d5f0e7648a 100644 --- a/core/common/validation/config_test.go +++ b/core/common/validation/config_test.go @@ -22,6 +22,8 @@ import ( "github.com/hyperledger/fabric/common/configtx" configtxtest "github.com/hyperledger/fabric/common/configtx/test" "github.com/hyperledger/fabric/common/util" + cb "github.com/hyperledger/fabric/protos/common" + "github.com/hyperledger/fabric/protos/utils" ) func TestValidateConfigTx(t *testing.T) { @@ -32,7 +34,24 @@ func TestValidateConfigTx(t *testing.T) { return } - _, err = ValidateTransaction(chCrtEnv) + updateResult := &cb.Envelope{ + Payload: utils.MarshalOrPanic(&cb.Payload{Header: &cb.Header{ + ChannelHeader: &cb.ChannelHeader{ + Type: int32(cb.HeaderType_CONFIG), + ChannelId: chainID, + }, + SignatureHeader: &cb.SignatureHeader{ + Creator: signerSerialized, + Nonce: utils.CreateNonceOrPanic(), + }, + }, + Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{ + LastUpdate: chCrtEnv, + }), + }), + } + updateResult.Signature, _ = signer.Sign(updateResult.Payload) + _, err = ValidateTransaction(updateResult) if err != nil { t.Fatalf("ValidateTransaction failed, err %s", err) return diff --git a/core/common/validation/msgvalidation.go b/core/common/validation/msgvalidation.go index 0d7c8a79259..f70de28ccec 100644 --- a/core/common/validation/msgvalidation.go +++ b/core/common/validation/msgvalidation.go @@ -365,6 +365,9 @@ func ValidateTransaction(e *common.Envelope) (*common.Payload, error) { putilsLogger.Infof("ValidateTransactionEnvelope returns err %s", err) return payload, err case common.HeaderType_CONFIG: + // Config transactions have signatures inside which will be validated, especially at genesis there may be no creator or + // signature on the outermost envelope + err = validateConfigTransaction(payload.Data, payload.Header) putilsLogger.Infof("ValidateTransactionEnvelope returns err %s", err) return payload, err diff --git a/core/mocks/txvalidator/support.go b/core/mocks/txvalidator/support.go index a6dc0baab4e..770c9de926a 100644 --- a/core/mocks/txvalidator/support.go +++ b/core/mocks/txvalidator/support.go @@ -39,6 +39,6 @@ func (ms *Support) MSPManager() msp.MSPManager { } // Apply returns ApplyVal -func (ms *Support) Apply(configtx *common.Envelope) error { +func (ms *Support) Apply(configtx *common.ConfigEnvelope) error { return ms.ApplyVal } diff --git a/orderer/common/broadcast/broadcast.go b/orderer/common/broadcast/broadcast.go index 5d5a37cf395..67deba1f399 100644 --- a/orderer/common/broadcast/broadcast.go +++ b/orderer/common/broadcast/broadcast.go @@ -87,7 +87,7 @@ func (bh *handlerImpl) Handle(srv ab.AtomicBroadcast_BroadcastServer) error { support, ok := bh.sm.GetChain(payload.Header.ChannelHeader.ChannelId) if !ok { // Chain not found, maybe create one? - if payload.Header.ChannelHeader.Type != int32(cb.HeaderType_CONFIG) { + if payload.Header.ChannelHeader.Type != int32(cb.HeaderType_CONFIG_UPDATE) { return srv.Send(&ab.BroadcastResponse{Status: cb.Status_NOT_FOUND}) } diff --git a/orderer/common/broadcast/broadcast_test.go b/orderer/common/broadcast/broadcast_test.go index d396b5ce55e..4dcf3be37e9 100644 --- a/orderer/common/broadcast/broadcast_test.go +++ b/orderer/common/broadcast/broadcast_test.go @@ -103,7 +103,7 @@ func makeConfigMessage(chainID string) *cb.Envelope { Header: &cb.Header{ ChannelHeader: &cb.ChannelHeader{ ChannelId: chainID, - Type: int32(cb.HeaderType_CONFIG), + Type: int32(cb.HeaderType_CONFIG_UPDATE), }, }, } diff --git a/orderer/common/configtxfilter/filter.go b/orderer/common/configtxfilter/filter.go index 18532eefd49..2c2084e888c 100644 --- a/orderer/common/configtxfilter/filter.go +++ b/orderer/common/configtxfilter/filter.go @@ -39,7 +39,7 @@ func NewFilter(manager api.Manager) filter.Rule { type configCommitter struct { manager api.Manager - configEnvelope *cb.Envelope + configEnvelope *cb.ConfigEnvelope } func (cc *configCommitter) Commit() { @@ -69,13 +69,13 @@ func (cf *configFilter) Apply(message *cb.Envelope) (filter.Action, filter.Commi return filter.Reject, nil } - err = cf.configManager.Validate(configEnvelope.LastUpdate) + err = cf.configManager.Validate(configEnvelope) if err != nil { return filter.Reject, nil } return filter.Accept, &configCommitter{ manager: cf.configManager, - configEnvelope: configEnvelope.LastUpdate, + configEnvelope: configEnvelope, } } diff --git a/orderer/common/configtxfilter/filter_test.go b/orderer/common/configtxfilter/filter_test.go index 5e4cd1b0f06..62c6e399d63 100644 --- a/orderer/common/configtxfilter/filter_test.go +++ b/orderer/common/configtxfilter/filter_test.go @@ -75,7 +75,7 @@ func TestAcceptGoodConfig(t *testing.T) { committer.Commit() - if !reflect.DeepEqual(mcm.AppliedConfigUpdateEnvelope, configEnv.LastUpdate) { + if !reflect.DeepEqual(mcm.AppliedConfigUpdateEnvelope, configEnv) { t.Fatalf("Should have applied new config on commit got %+v and %+v", mcm.AppliedConfigUpdateEnvelope, configEnv.LastUpdate) } } diff --git a/orderer/multichain/manager_test.go b/orderer/multichain/manager_test.go index 4ee516ecd03..9101cbab116 100644 --- a/orderer/multichain/manager_test.go +++ b/orderer/multichain/manager_test.go @@ -252,9 +252,15 @@ func TestNewChain(t *testing.T) { if len(block.Data.Data) != 1 { t.Fatalf("Should have had only one message in the orderer transaction block") } - genesisConfigTx := utils.UnmarshalEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(utils.ExtractEnvelopeOrPanic(block, 0).Payload).Data) - if !reflect.DeepEqual(genesisConfigTx, newChainMessage) { - t.Errorf("Orderer config block contains wrong transaction, expected %v got %v", genesisConfigTx, newChainMessage) + configEnv, err := configtx.UnmarshalConfigEnvelope(utils.UnmarshalPayloadOrPanic( + utils.UnmarshalEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(utils.ExtractEnvelopeOrPanic(block, 0).Payload).Data).Payload).Data) + + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(configEnv.LastUpdate, newChainMessage) { + t.Errorf("Orderer config block contains wrong transaction, expected %v got %v", configEnv.LastUpdate, newChainMessage) } case <-time.After(time.Second): t.Fatalf("Block 1 not produced after timeout in system chain") @@ -285,9 +291,13 @@ func TestNewChain(t *testing.T) { if len(block.Data.Data) != 1 { t.Fatalf("Should have had only one message in the new genesis block") } - genesisConfigTx := utils.ExtractEnvelopeOrPanic(block, 0) - if !reflect.DeepEqual(genesisConfigTx, newChainMessage) { - t.Errorf("Genesis block contains wrong transaction, expected %v got %v", genesisConfigTx, newChainMessage) + configEnv, err := configtx.ConfigEnvelopeFromBlock(block) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(configEnv.LastUpdate, newChainMessage) { + t.Errorf("Genesis block contains wrong transaction, expected %v got %v", configEnv.LastUpdate, newChainMessage) } case <-time.After(time.Second): t.Fatalf("Block 1 not produced after timeout in system chain") diff --git a/orderer/multichain/systemchain.go b/orderer/multichain/systemchain.go index aeb02c1be93..d5c1360584f 100644 --- a/orderer/multichain/systemchain.go +++ b/orderer/multichain/systemchain.go @@ -42,6 +42,8 @@ type limitedSupport interface { SharedConfig() configvaluesapi.Orderer ChannelConfig() configvalueschannel.ConfigReader Enqueue(env *cb.Envelope) bool + NewSignatureHeader() (*cb.SignatureHeader, error) + Sign([]byte) ([]byte, error) } type systemChainCommitter struct { @@ -106,7 +108,65 @@ func newSystemChain(support limitedSupport) *systemChain { } } -func (sc *systemChain) proposeChain(configTx *cb.Envelope) cb.Status { +// proposeChain takes in an envelope of type CONFIG_UPDATE, generates the new CONFIG and passes it along to the orderer system channel +func (sc *systemChain) proposeChain(configUpdateTx *cb.Envelope) cb.Status { + configUpdatePayload, err := utils.UnmarshalPayload(configUpdateTx.Payload) + if err != nil { + logger.Debugf("Failing to propose channel creation because of payload unmarshaling error: %s", err) + return cb.Status_BAD_REQUEST + } + + configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data) + if err != nil { + logger.Debugf("Failing to propose channel creation because of config update envelope unmarshaling error: %s", err) + return cb.Status_BAD_REQUEST + } + + configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) + if err != nil { + logger.Debugf("Failing to propose channel creation because of config update unmarshaling error: %s", err) + return cb.Status_BAD_REQUEST + } + + if configUpdate.Header == nil { + logger.Debugf("Failing to propose channel creation because of config update had no header") + return cb.Status_BAD_REQUEST + } + + sigHeader, err := sc.support.NewSignatureHeader() + if err != nil { + logger.Errorf("Error generating signature header: %s", err) + return cb.Status_INTERNAL_SERVER_ERROR + } + + configTx := &cb.Envelope{ + Payload: utils.MarshalOrPanic(&cb.Payload{ + Header: &cb.Header{ + ChannelHeader: &cb.ChannelHeader{ + ChannelId: configUpdate.Header.ChannelId, + Type: int32(cb.HeaderType_CONFIG), + }, + SignatureHeader: sigHeader, + }, + Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{ + Config: &cb.Config{ + Header: &cb.ChannelHeader{ + ChannelId: configUpdate.Header.ChannelId, + Type: int32(cb.HeaderType_CONFIG), + }, + Channel: configUpdate.WriteSet, + }, + LastUpdate: configUpdateTx, + }), + }), + } + + configTx.Signature, err = sc.support.Sign(configTx.Payload) + if err != nil { + logger.Errorf("Error generating signature: %s", err) + return cb.Status_INTERNAL_SERVER_ERROR + } + status := sc.authorizeAndInspect(configTx) if status != cb.Status_SUCCESS { return status @@ -169,13 +229,18 @@ func (sc *systemChain) authorize(configEnvelope *cb.ConfigEnvelope) cb.Status { return cb.Status_BAD_REQUEST } - configNext, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) + config, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) if err != nil { logger.Debugf("Failing to validate chain creation because of unmarshaling error: %s", err) return cb.Status_BAD_REQUEST } - ordererGroup, ok := configNext.WriteSet.Groups[configtxorderer.GroupKey] + if config.WriteSet == nil { + logger.Debugf("Failing to validate channel creation because WriteSet is nil") + return cb.Status_BAD_REQUEST + } + + ordererGroup, ok := config.WriteSet.Groups[configtxorderer.GroupKey] if !ok { logger.Debugf("Rejecting channel creation because it is missing orderer group") return cb.Status_BAD_REQUEST @@ -244,7 +309,7 @@ func (sc *systemChain) authorizeAndInspect(configTx *cb.Envelope) cb.Status { } if payload.Header == nil || payload.Header.ChannelHeader == nil || payload.Header.ChannelHeader.Type != int32(cb.HeaderType_CONFIG) { - logger.Debugf("Rejecting chain proposal: Not a config transaction: %s", err) + logger.Debugf("Rejecting chain proposal: Not a config transaction") return cb.Status_BAD_REQUEST } diff --git a/orderer/multichain/systemchain_test.go b/orderer/multichain/systemchain_test.go index 4b125f199ef..abcee2fb763 100644 --- a/orderer/multichain/systemchain_test.go +++ b/orderer/multichain/systemchain_test.go @@ -71,6 +71,14 @@ func (ms *mockSupport) ChannelConfig() configvalueschannel.ConfigReader { return ms.chainConfig } +func (ms *mockSupport) Sign(data []byte) ([]byte, error) { + return data, nil +} + +func (ms *mockSupport) NewSignatureHeader() (*cb.SignatureHeader, error) { + return &cb.SignatureHeader{}, nil +} + type mockChainCreator struct { newChains []*cb.Envelope ms *mockSupport @@ -121,8 +129,12 @@ func TestGoodProposal(t *testing.T) { if payload.Header.ChannelHeader.Type != int32(cb.HeaderType_ORDERER_TRANSACTION) { t.Fatalf("Wrapped transaction should be of type ORDERER_TRANSACTION") } - envelope := utils.UnmarshalEnvelopeOrPanic(payload.Data) - if !reflect.DeepEqual(envelope, ingressTx) { + outConfigEnv, err := configtx.UnmarshalConfigEnvelope(utils.UnmarshalPayloadOrPanic(utils.UnmarshalEnvelopeOrPanic(payload.Data).Payload).Data) + if err != nil { + t.Fatalf("Error unmarshaling: %s", err) + } + + if !reflect.DeepEqual(outConfigEnv.LastUpdate, ingressTx) { t.Fatalf("Received different configtx than ingressed into the system") } @@ -142,7 +154,9 @@ func TestGoodProposal(t *testing.T) { t.Fatalf("Proposal should only have created 1 new chain") } - if !reflect.DeepEqual(mcc.newChains[0], ingressTx) { + outConfigEnv, err = configtx.UnmarshalConfigEnvelope(utils.UnmarshalPayloadOrPanic(mcc.newChains[0].Payload).Data) + + if !reflect.DeepEqual(ingressTx, outConfigEnv.LastUpdate) { t.Fatalf("New chain should have been created with ingressTx") } } diff --git a/orderer/multichain/util_test.go b/orderer/multichain/util_test.go index fbf4ddd979d..6e0da292a1e 100644 --- a/orderer/multichain/util_test.go +++ b/orderer/multichain/util_test.go @@ -96,40 +96,17 @@ func makeConfigTx(chainID string, i int) *cb.Envelope { } func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.ConfigUpdateEnvelope) *cb.Envelope { - configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) - if err != nil { - panic(err) - } - - payload := &cb.Payload{ - Header: &cb.Header{ - ChannelHeader: &cb.ChannelHeader{ - Type: int32(cb.HeaderType_CONFIG), - ChannelId: chainID, - }, - SignatureHeader: &cb.SignatureHeader{}, - }, - Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{ - Config: &cb.Config{ - Header: configUpdate.Header, - Channel: configUpdate.WriteSet, - }, - LastUpdate: &cb.Envelope{ - Payload: utils.MarshalOrPanic(&cb.Payload{ - Header: &cb.Header{ - ChannelHeader: &cb.ChannelHeader{ - Type: int32(cb.HeaderType_CONFIG_UPDATE), - ChannelId: chainID, - }, - }, - Data: utils.MarshalOrPanic(configUpdateEnv), - }), + return &cb.Envelope{ + Payload: utils.MarshalOrPanic(&cb.Payload{ + Header: &cb.Header{ + ChannelHeader: &cb.ChannelHeader{ + Type: int32(cb.HeaderType_CONFIG_UPDATE), + ChannelId: chainID, + }, }, + Data: utils.MarshalOrPanic(configUpdateEnv), }), } - return &cb.Envelope{ - Payload: utils.MarshalOrPanic(payload), - } } func makeNormalTx(chainID string, i int) *cb.Envelope {