diff --git a/common/configtx/manager.go b/common/configtx/manager.go index 6b2d0a956d5..94149144db9 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -17,7 +17,6 @@ limitations under the License. package configtx import ( - "errors" "fmt" "regexp" @@ -77,32 +76,6 @@ func computeSequence(configGroup *cb.ConfigGroup) uint64 { return max } -// computeChannelIdAndSequence returns the chain id and the sequence number for a config envelope -// or an error if there is a problem with the config envelope -func computeChannelIdAndSequence(config *cb.ConfigUpdate) (string, uint64, error) { - if config.WriteSet == nil { - return "", 0, errors.New("Empty envelope unsupported") - } - - if config.Header == nil { - return "", 0, fmt.Errorf("Header not set") - } - - if config.Header.ChannelId == "" { - return "", 0, fmt.Errorf("Header chainID was not set") - } - - chainID := config.Header.ChannelId - - if err := validateChainID(chainID); err != nil { - return "", 0, err - } - - m := computeSequence(config.WriteSet) - - return chainID, m, nil -} - // validateChainID makes sure that proposed chain IDs (i.e. channel names) // comply with the following restrictions: // 1. Contain only ASCII alphanumerics, dots '.', dashes '-' @@ -133,36 +106,43 @@ func validateChainID(chainID string) error { return nil } -func NewManagerImpl(configtx *cb.ConfigEnvelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) { - // XXX as a temporary hack to get the new protos working, we assume entire config is always in the ConfigUpdate.WriteSet +func NewManagerImpl(configEnv *cb.ConfigEnvelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) { + if configEnv == nil { + return nil, fmt.Errorf("Nil config envelope") + } - if configtx.LastUpdate == nil { - return nil, fmt.Errorf("Must have ConfigEnvelope.LastUpdate set") + if configEnv.Config == nil { + return nil, fmt.Errorf("Nil config envelope Config") } - config, err := UnmarshalConfigUpdate(configtx.LastUpdate.ConfigUpdate) - if err != nil { - return nil, err + if configEnv.Config.Header == nil { + return nil, fmt.Errorf("Nil config envelop Config Header") } - chainID, seq, err := computeChannelIdAndSequence(config) + if err := validateChainID(configEnv.Config.Header.ChannelId); err != nil { + return nil, fmt.Errorf("Bad channel id: %s", err) + } + + configMap, err := mapConfig(configEnv.Config.Channel) if err != nil { - return nil, fmt.Errorf("Error computing chain ID and sequence: %s", err) + return nil, fmt.Errorf("Error converting config to map: %s", err) } cm := &configManager{ Resources: initializer, initializer: initializer, - sequence: seq - 1, - chainID: chainID, - config: make(map[string]comparable), + sequence: computeSequence(configEnv.Config.Channel), + chainID: configEnv.Config.Header.ChannelId, + config: configMap, callOnUpdate: callOnUpdate, } - err = cm.Apply(configtx.LastUpdate) - if err != nil { - return nil, fmt.Errorf("Error applying config transaction: %s", err) + cm.beginHandlers() + if err := cm.proposeConfig(configMap); err != nil { + cm.rollbackHandlers() + return nil, err } + cm.commitHandlers() return cm, nil } @@ -213,8 +193,6 @@ func (cm *configManager) proposeConfig(config map[string]comparable) error { // authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set // it returns a map of the modified config func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) { - // XXX as a temporary hack to get the new protos working, we assume entire config is always in the ConfigUpdate.WriteSet - if configUpdateEnv == nil { return nil, fmt.Errorf("Cannot process nil ConfigUpdateEnvelope") } @@ -224,7 +202,11 @@ func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelop return nil, err } - chainID, seq, err := computeChannelIdAndSequence(config) + if config.Header == nil { + return nil, fmt.Errorf("Must have header set") + } + + seq := computeSequence(config.WriteSet) if err != nil { return nil, err } @@ -240,15 +222,8 @@ func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelop } // Verify config is intended for this globally unique chain ID - if chainID != cm.chainID { - return nil, fmt.Errorf("Config is for the wrong chain, expected %s, got %s", cm.chainID, chainID) - } - - defaultModificationPolicy, defaultPolicySet := cm.PolicyManager().GetPolicy(NewConfigItemPolicyKey) - - // If the default modification policy is not set, it indicates this is an uninitialized chain, so be permissive of modification - if !defaultPolicySet { - defaultModificationPolicy = &acceptAllPolicy{} + if config.Header.ChannelId != cm.chainID { + return nil, fmt.Errorf("Config is for the wrong chain, expected %s, got %s", cm.chainID, config.Header.ChannelId) } configMap, err := mapConfig(config.WriteSet) @@ -277,25 +252,23 @@ func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelop // If a config item was modified, its Version must be set correctly, and it must satisfy the modification policy if isModified { - logger.Debugf("Proposed config item %s on channel %s has been modified", key, chainID) + logger.Debugf("Proposed config item %s on channel %s has been modified", key, cm.chainID) if value.version() != seq { return nil, fmt.Errorf("Key %s was modified, but its Version %d does not equal current configtx Sequence %d", key, value.version(), seq) } // Get the modification policy for this config item if one was previously specified - // or the default if this is a new config item + // or accept it if it is new, as the group policy will be evaluated for its inclusion var policy policies.Policy if ok { policy, _ = cm.PolicyManager().GetPolicy(oldValue.modPolicy()) - } else { - policy = defaultModificationPolicy + // Ensure the policy is satisfied + if err = policy.Evaluate(signedData); err != nil { + return nil, err + } } - // Ensure the policy is satisfied - if err = policy.Evaluate(signedData); err != nil { - return nil, err - } } } diff --git a/common/configtx/manager_test.go b/common/configtx/manager_test.go index 5f2916b8194..bcbdae54fdf 100644 --- a/common/configtx/manager_test.go +++ b/common/configtx/manager_test.go @@ -56,6 +56,22 @@ func makeConfigPair(id, modificationPolicy string, lastModified uint64, data []b } } +func makeConfigEnvelope(chainID string, configPairs ...*configPair) *cb.ConfigEnvelope { + values := make(map[string]*cb.ConfigValue) + for _, pair := range configPairs { + values[pair.key] = pair.value + } + + return &cb.ConfigEnvelope{ + Config: &cb.Config{ + Header: &cb.ChannelHeader{ChannelId: chainID}, + Channel: &cb.ConfigGroup{ + Values: values, + }, + }, + } +} + func makeConfigUpdateEnvelope(chainID string, configPairs ...*configPair) *cb.ConfigUpdateEnvelope { values := make(map[string]*cb.ConfigValue) for _, pair := range configPairs { @@ -79,9 +95,9 @@ func TestCallback(t *testing.T) { calledBack = m } - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), []func(api.Manager){callback}) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), []func(api.Manager){callback}) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -94,9 +110,9 @@ func TestCallback(t *testing.T) { // TestDifferentChainID tests that a config update for a different chain ID fails func TestDifferentChainID(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -117,9 +133,9 @@ func TestDifferentChainID(t *testing.T) { // TestOldConfigReplay tests that resubmitting a config for a sequence number which is not newer is ignored func TestOldConfigReplay(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -138,24 +154,11 @@ func TestOldConfigReplay(t *testing.T) { } } -// TestInvalidInitialConfigByStructure tests to make sure that if the config contains corrupted config that construction results in error -func TestInvalidInitialConfigByStructure(t *testing.T) { - _, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: &cb.ConfigUpdateEnvelope{ - ConfigUpdate: []byte("Corrupted"), - }, - }, defaultInitializer(), nil) - - if err == nil { - t.Fatal("Should have failed to construct config by policy") - } -} - // TestValidConfigChange tests the happy path of updating a config value with no defaultModificationPolicy func TestValidConfigChange(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -177,9 +180,9 @@ func TestValidConfigChange(t *testing.T) { // TestConfigChangeRegressedSequence tests to make sure that a new config cannot roll back one of the // config values while advancing another func TestConfigChangeRegressedSequence(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -205,9 +208,9 @@ func TestConfigChangeRegressedSequence(t *testing.T) { // TestConfigChangeOldSequence tests to make sure that a new config cannot roll back one of the // config values while advancing another func TestConfigChangeOldSequence(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -233,13 +236,13 @@ func TestConfigChangeOldSequence(t *testing.T) { // TestConfigImplicitDelete tests to make sure that a new config does not implicitly delete config items // by omitting them in the new config func TestConfigImplicitDelete(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope( + cm, err := NewManagerImpl( + makeConfigEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 0, []byte("bar")), ), - }, defaultInitializer(), nil) + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -263,9 +266,9 @@ func TestConfigImplicitDelete(t *testing.T) { // TestEmptyConfigUpdate tests to make sure that an empty config is rejected as an update func TestEmptyConfigUpdate(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -288,13 +291,13 @@ func TestEmptyConfigUpdate(t *testing.T) { // is substituted into an otherwise valid new config, that the new config is rejected for attempting a modification without // increasing the config item's LastModified func TestSilentConfigModification(t *testing.T) { - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope( + cm, err := NewManagerImpl( + makeConfigEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 0, []byte("bar")), ), - }, defaultInitializer(), nil) + defaultInitializer(), nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -317,27 +320,13 @@ func TestSilentConfigModification(t *testing.T) { } } -// TestInvalidInitialConfigByPolicy tests to make sure that if an existing policies does not validate the config that -// even construction fails -func TestInvalidInitialConfigByPolicy(t *testing.T) { - initializer := defaultInitializer() - initializer.Resources.PolicyManagerVal.Policy.Err = fmt.Errorf("err") - _, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, initializer, nil) - - if err == nil { - t.Fatal("Should have failed to construct config by policy") - } -} - // TestConfigChangeViolatesPolicy checks to make sure that if policy rejects the validation of a config item that // it is rejected in a config update func TestConfigChangeViolatesPolicy(t *testing.T) { initializer := defaultInitializer() - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, initializer, nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + initializer, nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -362,9 +351,9 @@ func TestConfigChangeViolatesPolicy(t *testing.T) { // as the policy may have changed, certs revoked, etc. since the config was adopted. func TestUnchangedConfigViolatesPolicy(t *testing.T) { initializer := defaultInitializer() - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, initializer, nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + initializer, nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -395,9 +384,9 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) { // that if the handler does not accept the config, it is rejected func TestInvalidProposal(t *testing.T) { initializer := defaultInitializer() - cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, initializer, nil) + cm, err := NewManagerImpl( + makeConfigEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + initializer, nil) if err != nil { t.Fatalf("Error constructing config manager: %s", err) @@ -418,16 +407,13 @@ func TestInvalidProposal(t *testing.T) { } } -// TestMissingHeader checks that a config item with a missing header causes the config to be rejected +// TestMissingHeader checks that a config envelope with a missing header causes the config to be rejected func TestMissingHeader(t *testing.T) { group := cb.NewConfigGroup() group.Values["foo"] = &cb.ConfigValue{} - data := utils.MarshalOrPanic(&cb.Config{Channel: group}) - _, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: &cb.ConfigUpdateEnvelope{ - ConfigUpdate: data, - }, - }, defaultInitializer(), nil) + _, err := NewManagerImpl( + &cb.ConfigEnvelope{Config: &cb.Config{Channel: group}}, + defaultInitializer(), nil) if err == nil { t.Error("Should have errored creating the config manager because of the missing header") @@ -436,9 +422,9 @@ func TestMissingHeader(t *testing.T) { // TestMissingChainID checks that a config item with a missing chainID causes the config to be rejected func TestMissingChainID(t *testing.T) { - _, err := NewManagerImpl(&cb.ConfigEnvelope{ - LastUpdate: makeConfigUpdateEnvelope("", makeConfigPair("foo", "foo", 0, []byte("foo"))), - }, defaultInitializer(), nil) + _, err := NewManagerImpl( + makeConfigEnvelope("", makeConfigPair("foo", "foo", 0, []byte("foo"))), + defaultInitializer(), nil) if err == nil { t.Error("Should have errored creating the config manager because of the missing header") diff --git a/common/configtx/template.go b/common/configtx/template.go index a7aaa855572..a6a9571c1b4 100644 --- a/common/configtx/template.go +++ b/common/configtx/template.go @@ -188,7 +188,17 @@ 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: newConfigUpdateEnv, } diff --git a/common/genesis/genesis.go b/common/genesis/genesis.go index e627f2a4214..3c4399b6e03 100644 --- a/common/genesis/genesis.go +++ b/common/genesis/genesis.go @@ -17,6 +17,7 @@ limitations under the License. package genesis import ( + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/configtx" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" @@ -48,10 +49,16 @@ func (f *factory) Block(chainID string) (*cb.Block, error) { return nil, err } + configUpdate := &cb.ConfigUpdate{} + err = proto.Unmarshal(configEnv.ConfigUpdate, configUpdate) + if err != nil { + return nil, err + } + payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG, msgVersion, chainID, epoch) payloadSignatureHeader := utils.MakeSignatureHeader(nil, utils.CreateNonceOrPanic()) payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader) - payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{LastUpdate: configEnv})} + payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{Config: &cb.Config{Header: configUpdate.Header, Channel: configUpdate.WriteSet}})} envelope := &cb.Envelope{Payload: utils.MarshalOrPanic(payload), Signature: nil} block := cb.NewBlock(0, nil) diff --git a/orderer/multichain/util_test.go b/orderer/multichain/util_test.go index c82a18dff45..46e6e427c8c 100644 --- a/orderer/multichain/util_test.go +++ b/orderer/multichain/util_test.go @@ -96,6 +96,11 @@ 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{ @@ -105,6 +110,10 @@ func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.Co SignatureHeader: &cb.SignatureHeader{}, }, Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{ + Config: &cb.Config{ + Header: configUpdate.Header, + Channel: configUpdate.WriteSet, + }, LastUpdate: configUpdateEnv, }), }