diff --git a/common/configtx/manager.go b/common/configtx/manager.go index 14eee395e4e..d1676586d1f 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -79,8 +79,8 @@ func computeSequence(configGroup *cb.ConfigGroup) uint64 { // 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.Config) (string, uint64, error) { - if config.Channel == nil { +func computeChannelIdAndSequence(config *cb.ConfigUpdate) (string, uint64, error) { + if config.WriteSet == nil { return "", 0, errors.New("Empty envelope unsupported") } @@ -98,7 +98,7 @@ func computeChannelIdAndSequence(config *cb.Config) (string, uint64, error) { return "", 0, err } - m := computeSequence(config.Channel) + m := computeSequence(config.WriteSet) return chainID, m, nil } @@ -134,7 +134,13 @@ func validateChainID(chainID string) error { } func NewManagerImpl(configtx *cb.ConfigEnvelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) { - config, err := UnmarshalConfig(configtx.Config) + // XXX as a temporary hack to get the new protos working, we assume entire config is always in the ConfigUpdate.WriteSet + + if configtx.LastUpdate == nil { + return nil, fmt.Errorf("Must have ConfigEnvelope.LastUpdate set") + } + + config, err := UnmarshalConfigUpdate(configtx.LastUpdate.ConfigUpdate) if err != nil { return nil, err } @@ -240,7 +246,13 @@ func (cm *configManager) recurseConfig(result map[string]comparable, path []stri } func (cm *configManager) processConfig(configtx *cb.ConfigEnvelope) (configMap map[string]comparable, err error) { - config, err := UnmarshalConfig(configtx.Config) + // XXX as a temporary hack to get the new protos working, we assume entire config is always in the ConfigUpdate.WriteSet + + if configtx.LastUpdate == nil { + return nil, fmt.Errorf("Must have ConfigEnvelope.LastUpdate set") + } + + config, err := UnmarshalConfigUpdate(configtx.LastUpdate.ConfigUpdate) if err != nil { return nil, err } @@ -250,7 +262,7 @@ func (cm *configManager) processConfig(configtx *cb.ConfigEnvelope) (configMap m return nil, err } - signedData, err := configtx.AsSignedData() + signedData, err := configtx.LastUpdate.AsSignedData() if err != nil { return nil, err } @@ -274,7 +286,7 @@ func (cm *configManager) processConfig(configtx *cb.ConfigEnvelope) (configMap m configMap = make(map[string]comparable) - if err := cm.recurseConfig(configMap, []string{}, config.Channel); err != nil { + if err := cm.recurseConfig(configMap, []string{}, config.WriteSet); err != nil { return nil, err } diff --git a/common/configtx/manager_test.go b/common/configtx/manager_test.go index 5ef8b872402..4e2d198cd58 100644 --- a/common/configtx/manager_test.go +++ b/common/configtx/manager_test.go @@ -56,19 +56,21 @@ func makeConfigPair(id, modificationPolicy string, lastModified uint64, data []b } } -func makeMarshaledConfig(chainID string, configPairs ...*configPair) []byte { +func makeConfigUpdateEnvelope(chainID string, configPairs ...*configPair) *cb.ConfigUpdateEnvelope { values := make(map[string]*cb.ConfigValue) for _, pair := range configPairs { values[pair.key] = pair.value } - config := &cb.Config{ + config := &cb.ConfigUpdate{ Header: &cb.ChannelHeader{ChannelId: chainID}, - Channel: &cb.ConfigGroup{ + WriteSet: &cb.ConfigGroup{ Values: values, }, } - return utils.MarshalOrPanic(config) + return &cb.ConfigUpdateEnvelope{ + ConfigUpdate: utils.MarshalOrPanic(config), + } } func TestCallback(t *testing.T) { @@ -78,7 +80,7 @@ func TestCallback(t *testing.T) { } cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), []func(api.Manager){callback}) if err != nil { @@ -93,7 +95,7 @@ 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{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -101,7 +103,7 @@ func TestDifferentChainID(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig("wrongChain", makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope("wrongChain", makeConfigPair("foo", "foo", 1, []byte("foo"))), } err = cm.Validate(newConfig) @@ -118,7 +120,7 @@ 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{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -126,7 +128,7 @@ func TestOldConfigReplay(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), } err = cm.Validate(newConfig) @@ -143,7 +145,9 @@ 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{ - Config: []byte("Corrupted"), + LastUpdate: &cb.ConfigUpdateEnvelope{ + ConfigUpdate: []byte("Corrupted"), + }, }, defaultInitializer(), nil) if err == nil { @@ -154,7 +158,7 @@ func TestInvalidInitialConfigByStructure(t *testing.T) { // TestValidConfigChange tests the happy path of updating a config value with no defaultModificationPolicy func TestValidConfigChange(t *testing.T) { cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -162,7 +166,7 @@ func TestValidConfigChange(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), } err = cm.Validate(newConfig) @@ -180,7 +184,7 @@ func TestValidConfigChange(t *testing.T) { // config values while advancing another func TestConfigChangeRegressedSequence(t *testing.T) { cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -188,7 +192,7 @@ func TestConfigChangeRegressedSequence(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 2, []byte("bar")), @@ -210,7 +214,7 @@ func TestConfigChangeRegressedSequence(t *testing.T) { // config values while advancing another func TestConfigChangeOldSequence(t *testing.T) { cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -218,7 +222,7 @@ func TestConfigChangeOldSequence(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 2, []byte("foo")), makeConfigPair("bar", "bar", 1, []byte("bar")), @@ -240,7 +244,7 @@ func TestConfigChangeOldSequence(t *testing.T) { // by omitting them in the new config func TestConfigImplicitDelete(t *testing.T) { cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 0, []byte("bar")), @@ -252,7 +256,7 @@ func TestConfigImplicitDelete(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("bar", "bar", 1, []byte("bar")), ), @@ -272,7 +276,7 @@ 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{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), nil) if err != nil { @@ -297,7 +301,7 @@ func TestEmptyConfigUpdate(t *testing.T) { // increasing the config item's LastModified func TestSilentConfigModification(t *testing.T) { cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 0, []byte("bar")), @@ -309,7 +313,7 @@ func TestSilentConfigModification(t *testing.T) { } newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("different")), makeConfigPair("bar", "bar", 1, []byte("bar")), @@ -333,7 +337,7 @@ func TestInvalidInitialConfigByPolicy(t *testing.T) { initializer := defaultInitializer() initializer.Resources.PolicyManagerVal.Policy.Err = fmt.Errorf("err") _, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, initializer, nil) if err == nil { @@ -346,7 +350,7 @@ func TestInvalidInitialConfigByPolicy(t *testing.T) { func TestConfigChangeViolatesPolicy(t *testing.T) { initializer := defaultInitializer() cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, initializer, nil) if err != nil { @@ -356,7 +360,7 @@ func TestConfigChangeViolatesPolicy(t *testing.T) { initializer.Resources.PolicyManagerVal.Policy.Err = fmt.Errorf("err") newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), } err = cm.Validate(newConfig) @@ -375,7 +379,7 @@ func TestConfigChangeViolatesPolicy(t *testing.T) { func TestUnchangedConfigViolatesPolicy(t *testing.T) { initializer := defaultInitializer() cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, initializer, nil) if err != nil { @@ -387,7 +391,7 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) { initializer.Resources.PolicyManagerVal.PolicyMap["foo"] = &mockpolicies.Policy{Err: fmt.Errorf("err")} newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig( + LastUpdate: makeConfigUpdateEnvelope( defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo")), makeConfigPair("bar", "bar", 1, []byte("foo")), @@ -410,7 +414,7 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) { func TestInvalidProposal(t *testing.T) { initializer := defaultInitializer() cm, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 0, []byte("foo"))), }, initializer, nil) if err != nil { @@ -420,7 +424,7 @@ func TestInvalidProposal(t *testing.T) { initializer.HandlerVal = &mockconfigtx.Handler{ErrorForProposeConfig: fmt.Errorf("err")} newConfig := &cb.ConfigEnvelope{ - Config: makeMarshaledConfig(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo"))), } err = cm.Validate(newConfig) @@ -440,7 +444,9 @@ func TestMissingHeader(t *testing.T) { group.Values["foo"] = &cb.ConfigValue{} data := utils.MarshalOrPanic(&cb.Config{Channel: group}) _, err := NewManagerImpl(&cb.ConfigEnvelope{ - Config: data, + LastUpdate: &cb.ConfigUpdateEnvelope{ + ConfigUpdate: data, + }, }, defaultInitializer(), nil) if err == nil { @@ -451,7 +457,7 @@ 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{ - Config: makeMarshaledConfig("", makeConfigPair("foo", "foo", 0, []byte("foo"))), + LastUpdate: makeConfigUpdateEnvelope("", makeConfigPair("foo", "foo", 0, []byte("foo"))), }, defaultInitializer(), nil) if err == nil { diff --git a/common/configtx/template.go b/common/configtx/template.go index 2f9db61804b..99e0fc0cd85 100644 --- a/common/configtx/template.go +++ b/common/configtx/template.go @@ -41,8 +41,8 @@ const ( // Template can be used to faciliate creation of config transactions type Template interface { - // Items returns a set of ConfigEnvelopes for the given chainID - Envelope(chainID string) (*cb.ConfigEnvelope, error) + // Items returns a set of ConfigUpdateEnvelopes for the given chainID + Envelope(chainID string) (*cb.ConfigUpdateEnvelope, error) } type simpleTemplate struct { @@ -60,22 +60,22 @@ func NewSimpleTemplate(configGroups ...*cb.ConfigGroup) Template { return NewCompositeTemplate(sts...) } -// Envelope returns a ConfigEnvelopes for the given chainID -func (st *simpleTemplate) Envelope(chainID string) (*cb.ConfigEnvelope, error) { - config, err := proto.Marshal(&cb.Config{ +// Envelope returns a ConfigUpdateEnvelopes for the given chainID +func (st *simpleTemplate) Envelope(chainID string) (*cb.ConfigUpdateEnvelope, error) { + config, err := proto.Marshal(&cb.ConfigUpdate{ Header: &cb.ChannelHeader{ ChannelId: chainID, Type: int32(cb.HeaderType_CONFIGURATION_ITEM), }, - Channel: st.configGroup, + WriteSet: st.configGroup, }) if err != nil { return nil, err } - return &cb.ConfigEnvelope{ - Config: config, + return &cb.ConfigUpdateEnvelope{ + ConfigUpdate: config, }, nil } @@ -119,8 +119,8 @@ func copyGroup(source *cb.ConfigGroup, target *cb.ConfigGroup) error { return nil } -// Items returns a set of ConfigEnvelopes for the given chainID, and errors only on marshaling errors -func (ct *compositeTemplate) Envelope(chainID string) (*cb.ConfigEnvelope, error) { +// Envelope returns the ConfigUpdateEnvelope for the given chainID +func (ct *compositeTemplate) Envelope(chainID string) (*cb.ConfigUpdateEnvelope, error) { channel := cb.NewConfigGroup() for i := range ct.templates { @@ -128,32 +128,32 @@ func (ct *compositeTemplate) Envelope(chainID string) (*cb.ConfigEnvelope, error if err != nil { return nil, err } - config, err := UnmarshalConfig(configEnv.Config) + config, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate) if err != nil { return nil, err } - err = copyGroup(config.Channel, channel) + err = copyGroup(config.WriteSet, channel) if err != nil { return nil, err } } - marshaledConfig, err := proto.Marshal(&cb.Config{ + marshaledConfig, err := proto.Marshal(&cb.ConfigUpdate{ Header: &cb.ChannelHeader{ ChannelId: chainID, Type: int32(cb.HeaderType_CONFIGURATION_ITEM), }, - Channel: channel, + WriteSet: channel, }) if err != nil { return nil, err } - return &cb.ConfigEnvelope{Config: marshaledConfig}, nil + return &cb.ConfigUpdateEnvelope{ConfigUpdate: marshaledConfig}, nil } // NewChainCreationTemplate takes a CreationPolicy and a Template to produce a Template which outputs an appropriately -// constructed list of ConfigEnvelope. Note, using this Template in +// constructed list of ConfigUpdateEnvelope. Note, using this Template in // a CompositeTemplate will invalidate the CreationPolicy func NewChainCreationTemplate(creationPolicy string, template Template) Template { result := cb.NewConfigGroup() @@ -175,19 +175,23 @@ func MakeChainCreationTransaction(creationPolicy string, chainID string, signer } newChainTemplate := NewChainCreationTemplate(creationPolicy, NewCompositeTemplate(templates...)) - newConfigEnv, err := newChainTemplate.Envelope(chainID) + newConfigUpdateEnv, err := newChainTemplate.Envelope(chainID) if err != nil { return nil, err } - - newConfigEnv.Signatures = []*cb.ConfigSignature{&cb.ConfigSignature{ + newConfigUpdateEnv.Signatures = []*cb.ConfigSignature{&cb.ConfigSignature{ SignatureHeader: utils.MarshalOrPanic(utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())), }} - newConfigEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigEnv.Signatures[0].SignatureHeader, newConfigEnv.Config)) + + newConfigUpdateEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigUpdateEnv.Signatures[0].SignatureHeader, newConfigUpdateEnv.ConfigUpdate)) if err != nil { return nil, err } + newConfigEnv := &cb.ConfigEnvelope{ + LastUpdate: newConfigUpdateEnv, + } + payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, msgVersion, chainID, epoch) payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic()) payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader) diff --git a/common/configtx/template_test.go b/common/configtx/template_test.go index 596daca763d..fc0a79f4191 100644 --- a/common/configtx/template_test.go +++ b/common/configtx/template_test.go @@ -34,15 +34,15 @@ func verifyItemsResult(t *testing.T, template Template, count int) { t.Fatalf("Should not have errored: %s", err) } - configNext, err := UnmarshalConfig(configEnv.Config) + configNext, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate) if err != nil { t.Fatalf("Should not have errored: %s", err) } - assert.Equal(t, len(configNext.Channel.Values), count, "Not the right number of config values") + assert.Equal(t, len(configNext.WriteSet.Values), count, "Not the right number of config values") - for i := 0; i < len(configNext.Channel.Values); i++ { - _, ok := configNext.Channel.Values[fmt.Sprintf("%d", i)] + for i := 0; i < len(configNext.WriteSet.Values); i++ { + _, ok := configNext.WriteSet.Values[fmt.Sprintf("%d", i)] assert.True(t, ok, "Expected %d but did not find it", i) } } @@ -90,18 +90,18 @@ func TestNewChainTemplate(t *testing.T) { t.Fatalf("Error creation a chain creation config") } - configNext, err := UnmarshalConfig(configEnv.Config) + configNext, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate) if err != nil { t.Fatalf("Should not have errored: %s", err) } - assert.Equal(t, len(configNext.Channel.Values), 2, "Not the right number of config values") + assert.Equal(t, len(configNext.WriteSet.Values), 2, "Not the right number of config values") for i := 0; i < 2; i++ { - _, ok := configNext.Channel.Values[fmt.Sprintf("%d", i)] + _, ok := configNext.WriteSet.Values[fmt.Sprintf("%d", i)] assert.True(t, ok, "Expected to find %d but did not", i) } - _, ok := configNext.Channel.Groups[configtxorderer.GroupKey].Values[CreationPolicyKey] + _, ok := configNext.WriteSet.Groups[configtxorderer.GroupKey].Values[CreationPolicyKey] assert.True(t, ok, "Did not find creation policy") } diff --git a/common/configtx/util.go b/common/configtx/util.go index 99859b885ac..2be69908bba 100644 --- a/common/configtx/util.go +++ b/common/configtx/util.go @@ -35,6 +35,16 @@ func UnmarshalConfig(data []byte) (*cb.Config, error) { return config, nil } +// UnmarshalConfigUpdate attempts to unmarshal bytes to a *cb.ConfigUpdate +func UnmarshalConfigUpdate(data []byte) (*cb.ConfigUpdate, error) { + configUpdate := &cb.ConfigUpdate{} + err := proto.Unmarshal(data, configUpdate) + if err != nil { + return nil, err + } + return configUpdate, nil +} + // UnmarshalConfigEnvelope attempts to unmarshal bytes to a *cb.ConfigEnvelope func UnmarshalConfigEnvelope(data []byte) (*cb.ConfigEnvelope, error) { configEnv := &cb.ConfigEnvelope{} diff --git a/common/genesis/genesis.go b/common/genesis/genesis.go index e14bab55408..21a652a4a19 100644 --- a/common/genesis/genesis.go +++ b/common/genesis/genesis.go @@ -51,7 +51,7 @@ func (f *factory) Block(chainID string) (*cb.Block, error) { payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, msgVersion, chainID, epoch) payloadSignatureHeader := utils.MakeSignatureHeader(nil, utils.CreateNonceOrPanic()) payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader) - payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(configEnv)} + payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{LastUpdate: configEnv})} envelope := &cb.Envelope{Payload: utils.MarshalOrPanic(payload), Signature: nil} block := cb.NewBlock(0, nil) diff --git a/orderer/multichain/systemchain.go b/orderer/multichain/systemchain.go index 584d4aa6e6b..e24beb5ef39 100644 --- a/orderer/multichain/systemchain.go +++ b/orderer/multichain/systemchain.go @@ -148,14 +148,21 @@ func (sc *systemChain) proposeChain(configTx *cb.Envelope) cb.Status { } func (sc *systemChain) authorize(configEnvelope *cb.ConfigEnvelope) cb.Status { - configNext := &cb.Config{} - err := proto.Unmarshal(configEnvelope.Config, configNext) + // XXX as a temporary hack to get the protos in, we assume the write set contains the whole config + + if configEnvelope.LastUpdate == nil { + logger.Debugf("Must include a config update") + return cb.Status_BAD_REQUEST + } + + configNext := &cb.ConfigUpdate{} + err := proto.Unmarshal(configEnvelope.LastUpdate.ConfigUpdate, configNext) if err != nil { logger.Debugf("Failing to validate chain creation because of unmarshaling error: %s", err) return cb.Status_BAD_REQUEST } - ordererGroup, ok := configNext.Channel.Groups[configtxorderer.GroupKey] + ordererGroup, ok := configNext.WriteSet.Groups[configtxorderer.GroupKey] if !ok { logger.Debugf("Rejecting channel creation because it is missing orderer group") return cb.Status_BAD_REQUEST @@ -193,7 +200,7 @@ func (sc *systemChain) authorize(configEnvelope *cb.ConfigEnvelope) cb.Status { return cb.Status_INTERNAL_SERVER_ERROR } - signedData, err := configEnvelope.AsSignedData() + signedData, err := configEnvelope.LastUpdate.AsSignedData() if err != nil { logger.Debugf("Failed to validate chain creation because config envelope could not be converted to signed data: %s", err) return cb.Status_BAD_REQUEST diff --git a/orderer/multichain/systemchain_test.go b/orderer/multichain/systemchain_test.go index 4b75addfc95..96e8418540e 100644 --- a/orderer/multichain/systemchain_test.go +++ b/orderer/multichain/systemchain_test.go @@ -118,7 +118,7 @@ func TestGoodProposal(t *testing.T) { if err != nil { t.Fatalf("Error constructing configtx") } - ingressTx := makeConfigTxFromConfigEnvelope(newChainID, configEnv) + ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv) status := mcc.sysChain.proposeChain(ingressTx) if status != cb.Status_SUCCESS { @@ -171,7 +171,7 @@ func TestProposalWithBadPolicy(t *testing.T) { if err != nil { t.Fatalf("Error constructing configtx") } - ingressTx := makeConfigTxFromConfigEnvelope(newChainID, configEnv) + ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv) status := mcc.sysChain.proposeChain(ingressTx) @@ -190,7 +190,7 @@ func TestProposalWithMissingPolicy(t *testing.T) { if err != nil { t.Fatalf("Error constructing configtx") } - ingressTx := makeConfigTxFromConfigEnvelope(newChainID, configEnv) + ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv) status := mcc.sysChain.proposeChain(ingressTx) diff --git a/orderer/multichain/util_test.go b/orderer/multichain/util_test.go index 6c5854677a7..30808cc22de 100644 --- a/orderer/multichain/util_test.go +++ b/orderer/multichain/util_test.go @@ -92,10 +92,10 @@ func makeConfigTx(chainID string, i int) *cb.Envelope { if err != nil { panic(err) } - return makeConfigTxFromConfigEnvelope(chainID, configEnv) + return makeConfigTxFromConfigUpdateEnvelope(chainID, configEnv) } -func makeConfigTxFromConfigEnvelope(chainID string, configEnv *cb.ConfigEnvelope) *cb.Envelope { +func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.ConfigUpdateEnvelope) *cb.Envelope { payload := &cb.Payload{ Header: &cb.Header{ ChannelHeader: &cb.ChannelHeader{ @@ -104,7 +104,9 @@ func makeConfigTxFromConfigEnvelope(chainID string, configEnv *cb.ConfigEnvelope }, SignatureHeader: &cb.SignatureHeader{}, }, - Data: utils.MarshalOrPanic(configEnv), + Data: utils.MarshalOrPanic(&cb.ConfigEnvelope{ + LastUpdate: configUpdateEnv, + }), } return &cb.Envelope{ Payload: utils.MarshalOrPanic(payload), diff --git a/protos/common/common.pb.go b/protos/common/common.pb.go index 99dc39a8cb7..cb252fc8d23 100644 --- a/protos/common/common.pb.go +++ b/protos/common/common.pb.go @@ -31,6 +31,8 @@ It has these top-level messages: ConfigValueSchema ConfigPolicySchema Config + ConfigUpdateEnvelope + ConfigUpdate ConfigGroup ConfigValue ConfigPolicy diff --git a/protos/common/configtx.pb.go b/protos/common/configtx.pb.go index 2e8af9956f0..8706be7e8ae 100644 --- a/protos/common/configtx.pb.go +++ b/protos/common/configtx.pb.go @@ -18,25 +18,25 @@ var _ = math.Inf // // It is generated with the following scheme: // 1. Retrieve the existing configuration -// 2. Note the highest configuration sequence number, store it and increment it by one -// 3. Modify desired ConfigItems, setting each LastModified to the stored and incremented sequence number -// a) Note that the ConfigItem has a ChannelHeader header attached to it, who's type is set to CONFIGURATION_ITEM -// 4. Create Config message containing the new configuration, marshal it into ConfigEnvelope.config and encode the required signatures +// 2. Note the config properties (ConfigValue, ConfigPolicy, ConfigGroup) to be modified +// 3. Add any intermediate ConfigGroups to the ConfigUpdate.read_set (sparsely) +// 4. Add any additional desired dependencies to ConfigUpdate.read_set (sparsely) +// 5. Modify the config properties, incrementing each version by 1, set them in the ConfigUpdate.write_set +// Note: any element not modified but specified should already be in the read_set, so may be specified sparsely +// 6. Create ConfigUpdate message and marshal it into ConfigUpdateEnvelope.update and encode the required signatures // a) Each signature is of type ConfigSignature -// b) The ConfigSignature signature is over the concatenation of signatureHeader and the Config bytes (which includes a ChannelHeader) +// b) The ConfigSignature signature is over the concatenation of signature_header and the ConfigUpdate bytes (which includes a ChainHeader) // 5. Submit new Config for ordering in Envelope signed by submitter // a) The Envelope Payload has data set to the marshaled ConfigEnvelope -// b) The Envelope Payload has a header of type Header.Type.CONFIGURATION_TRANSACTION +// b) The Envelope Payload has a header of type Header.Type.CONFIG_UPDATE // XXX change CONFIGURATION_ITEM to CONFIG_UPDATE in common.proto // // The configuration manager will verify: -// 1. All configuration items and the envelope refer to the correct chain -// 2. Some configuration item has been added or modified -// 3. No existing configuration item has been ommitted -// 4. All configuration changes have a LastModification of one more than the last configuration's highest LastModification number -// 5. All configuration changes satisfy the corresponding modification policy +// 1. All items in the read_set exist at the read versions +// 2. All items in the write_set at a different version than, or not in, the read_set have been appropriately signed according to their mod_policy +// 3. The new configuration satisfies the ConfigSchema type ConfigEnvelope struct { - Config []byte `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - Signatures []*ConfigSignature `protobuf:"bytes,2,rep,name=signatures" json:"signatures,omitempty"` + Config *Config `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + LastUpdate *ConfigUpdateEnvelope `protobuf:"bytes,2,opt,name=last_update,json=lastUpdate" json:"last_update,omitempty"` } func (m *ConfigEnvelope) Reset() { *m = ConfigEnvelope{} } @@ -44,9 +44,16 @@ func (m *ConfigEnvelope) String() string { return proto.CompactTextSt func (*ConfigEnvelope) ProtoMessage() {} func (*ConfigEnvelope) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } -func (m *ConfigEnvelope) GetSignatures() []*ConfigSignature { +func (m *ConfigEnvelope) GetConfig() *Config { if m != nil { - return m.Signatures + return m.Config + } + return nil +} + +func (m *ConfigEnvelope) GetLastUpdate() *ConfigUpdateEnvelope { + if m != nil { + return m.LastUpdate } return nil } @@ -124,6 +131,64 @@ func (m *Config) GetChannel() *ConfigGroup { return nil } +type ConfigUpdateEnvelope struct { + ConfigUpdate []byte `protobuf:"bytes,1,opt,name=config_update,json=configUpdate,proto3" json:"config_update,omitempty"` + Signatures []*ConfigSignature `protobuf:"bytes,2,rep,name=signatures" json:"signatures,omitempty"` +} + +func (m *ConfigUpdateEnvelope) Reset() { *m = ConfigUpdateEnvelope{} } +func (m *ConfigUpdateEnvelope) String() string { return proto.CompactTextString(m) } +func (*ConfigUpdateEnvelope) ProtoMessage() {} +func (*ConfigUpdateEnvelope) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } + +func (m *ConfigUpdateEnvelope) GetSignatures() []*ConfigSignature { + if m != nil { + return m.Signatures + } + return nil +} + +// ConfigUpdate is used to submit a subset of config and to have the orderer apply to Config +// it is always submitted inside a ConfigUpdateEnvelope which allows the addition of signatures +// resulting in a new total configuration. The update is applied as follows: +// 1. The versions from all of the elements in the read_set is verified against the versions in the existing config. +// If there is a mismatch in the read versions, then the config update fails and is rejected. +// 2. Any elements in the write_set with the same version as the read_set are ignored. +// 3. The corresponding mod_policy for every remaining element in the write_set is collected. +// 4. Each policy is checked against the signatures from the ConfigUpdateEnvelope, any failing to verify are rejected +// 5. The write_set is applied to the Config and the ConfigGroupSchema verifies that the updates were legal +type ConfigUpdate struct { + Header *ChannelHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` + ReadSet *ConfigGroup `protobuf:"bytes,2,opt,name=read_set,json=readSet" json:"read_set,omitempty"` + WriteSet *ConfigGroup `protobuf:"bytes,3,opt,name=write_set,json=writeSet" json:"write_set,omitempty"` +} + +func (m *ConfigUpdate) Reset() { *m = ConfigUpdate{} } +func (m *ConfigUpdate) String() string { return proto.CompactTextString(m) } +func (*ConfigUpdate) ProtoMessage() {} +func (*ConfigUpdate) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } + +func (m *ConfigUpdate) GetHeader() *ChannelHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *ConfigUpdate) GetReadSet() *ConfigGroup { + if m != nil { + return m.ReadSet + } + return nil +} + +func (m *ConfigUpdate) GetWriteSet() *ConfigGroup { + if m != nil { + return m.WriteSet + } + return nil +} + // ConfigGroup is the hierarchical data structure for holding config type ConfigGroup struct { Version uint64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` @@ -136,7 +201,7 @@ type ConfigGroup struct { func (m *ConfigGroup) Reset() { *m = ConfigGroup{} } func (m *ConfigGroup) String() string { return proto.CompactTextString(m) } func (*ConfigGroup) ProtoMessage() {} -func (*ConfigGroup) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } +func (*ConfigGroup) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } func (m *ConfigGroup) GetGroups() map[string]*ConfigGroup { if m != nil { @@ -169,7 +234,7 @@ type ConfigValue struct { func (m *ConfigValue) Reset() { *m = ConfigValue{} } func (m *ConfigValue) String() string { return proto.CompactTextString(m) } func (*ConfigValue) ProtoMessage() {} -func (*ConfigValue) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } +func (*ConfigValue) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } type ConfigPolicy struct { Version uint64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` @@ -180,7 +245,7 @@ type ConfigPolicy struct { func (m *ConfigPolicy) Reset() { *m = ConfigPolicy{} } func (m *ConfigPolicy) String() string { return proto.CompactTextString(m) } func (*ConfigPolicy) ProtoMessage() {} -func (*ConfigPolicy) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } +func (*ConfigPolicy) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } func (m *ConfigPolicy) GetPolicy() *Policy { if m != nil { @@ -197,7 +262,7 @@ type ConfigSignature struct { func (m *ConfigSignature) Reset() { *m = ConfigSignature{} } func (m *ConfigSignature) String() string { return proto.CompactTextString(m) } func (*ConfigSignature) ProtoMessage() {} -func (*ConfigSignature) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } +func (*ConfigSignature) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{10} } func init() { proto.RegisterType((*ConfigEnvelope)(nil), "common.ConfigEnvelope") @@ -205,6 +270,8 @@ func init() { proto.RegisterType((*ConfigValueSchema)(nil), "common.ConfigValueSchema") proto.RegisterType((*ConfigPolicySchema)(nil), "common.ConfigPolicySchema") proto.RegisterType((*Config)(nil), "common.Config") + proto.RegisterType((*ConfigUpdateEnvelope)(nil), "common.ConfigUpdateEnvelope") + proto.RegisterType((*ConfigUpdate)(nil), "common.ConfigUpdate") proto.RegisterType((*ConfigGroup)(nil), "common.ConfigGroup") proto.RegisterType((*ConfigValue)(nil), "common.ConfigValue") proto.RegisterType((*ConfigPolicy)(nil), "common.ConfigPolicy") @@ -214,41 +281,46 @@ func init() { func init() { proto.RegisterFile("common/configtx.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 565 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x95, 0x51, 0x8f, 0x93, 0x40, - 0x10, 0xc7, 0x43, 0xb9, 0xe3, 0xec, 0x50, 0xef, 0xce, 0x6d, 0x4f, 0x91, 0x68, 0xac, 0x24, 0x6a, - 0x4f, 0xd3, 0x62, 0xea, 0x43, 0x8d, 0xc9, 0xbd, 0x78, 0xb9, 0xe8, 0xd3, 0x45, 0x39, 0xa3, 0xc9, - 0xc5, 0xa4, 0xa1, 0x74, 0x0b, 0x44, 0x60, 0x09, 0xd0, 0x46, 0x3e, 0xab, 0xdf, 0xc0, 0x4f, 0x61, - 0xd8, 0x5d, 0x70, 0x69, 0x69, 0x9b, 0x7b, 0x82, 0xdd, 0x99, 0xff, 0x6f, 0x86, 0xd9, 0x3f, 0x00, - 0x67, 0x0e, 0x09, 0x43, 0x12, 0x99, 0x0e, 0x89, 0x16, 0xbe, 0x9b, 0xfd, 0x1e, 0xc5, 0x09, 0xc9, - 0x08, 0x52, 0xd8, 0xb6, 0xde, 0xad, 0xc2, 0xc5, 0x85, 0x05, 0xf5, 0x52, 0x13, 0x93, 0xc0, 0x77, - 0x7c, 0x9c, 0xb2, 0x6d, 0xc3, 0x86, 0xe3, 0x4b, 0x4a, 0xb9, 0x8a, 0x56, 0x38, 0x20, 0x31, 0x46, - 0x0f, 0x41, 0x61, 0x5c, 0x4d, 0xea, 0x4b, 0x83, 0x8e, 0xc5, 0x57, 0x68, 0x02, 0x90, 0xfa, 0x6e, - 0x64, 0x67, 0xcb, 0x04, 0xa7, 0x5a, 0xab, 0x2f, 0x0f, 0xd4, 0xf1, 0xa3, 0x11, 0xaf, 0xc1, 0x18, - 0x37, 0x65, 0xdc, 0x12, 0x52, 0x8d, 0x3f, 0x32, 0x3c, 0x60, 0xf1, 0x4f, 0x09, 0x59, 0xc6, 0x37, - 0x8e, 0x87, 0x43, 0x1b, 0x5d, 0x80, 0xe2, 0x16, 0xcb, 0x54, 0x93, 0x28, 0xea, 0x45, 0x1d, 0x25, - 0xa4, 0x8e, 0xe8, 0x7d, 0x7a, 0x15, 0x65, 0x49, 0x6e, 0x71, 0x51, 0x21, 0x5f, 0xd9, 0xc1, 0xb2, - 0xea, 0x64, 0x87, 0xfc, 0x3b, 0xcd, 0xe3, 0x72, 0x26, 0x42, 0x97, 0x70, 0xaf, 0x1c, 0x84, 0x26, - 0x53, 0xc0, 0xab, 0xed, 0x80, 0x2f, 0x3c, 0x93, 0x21, 0x2a, 0xa1, 0xfe, 0x0d, 0x54, 0xa1, 0x35, - 0x74, 0x0a, 0xf2, 0x2f, 0x9c, 0xd3, 0xa9, 0xb5, 0xad, 0xe2, 0x16, 0x99, 0x70, 0x48, 0xeb, 0x69, - 0xad, 0xbe, 0x34, 0x50, 0xc7, 0x8f, 0xb7, 0x96, 0xb0, 0x58, 0xde, 0x87, 0xd6, 0x7b, 0xa9, 0xa0, - 0x0a, 0x1d, 0xdf, 0x99, 0x4a, 0xb5, 0x9b, 0xd4, 0x1f, 0x70, 0xbf, 0xf6, 0x18, 0x0d, 0xdc, 0xb7, - 0x75, 0xae, 0x5e, 0xe7, 0x52, 0x75, 0xbe, 0x01, 0x36, 0xba, 0xe5, 0xe1, 0x0a, 0x85, 0x8d, 0x1e, - 0xa0, 0x4d, 0x95, 0xb1, 0x00, 0x85, 0xed, 0xa2, 0x21, 0x28, 0x1e, 0xb6, 0xe7, 0x38, 0xa1, 0xf5, - 0xd5, 0xf1, 0x59, 0x55, 0xcb, 0xb3, 0xa3, 0x08, 0x07, 0x9f, 0x69, 0xd0, 0xe2, 0x49, 0x68, 0x08, - 0x47, 0x0e, 0x0b, 0xf0, 0xde, 0xba, 0x0d, 0x93, 0xb4, 0xca, 0x1c, 0xe3, 0xaf, 0x0c, 0xaa, 0x10, - 0x40, 0x1a, 0x1c, 0xad, 0x70, 0x92, 0xfa, 0x24, 0xa2, 0xe5, 0x0e, 0xac, 0x72, 0x89, 0x26, 0x95, - 0x09, 0x99, 0x8b, 0x9e, 0x35, 0x70, 0x1b, 0xed, 0x37, 0xa9, 0xec, 0x27, 0x6f, 0x17, 0x36, 0x19, - 0xef, 0x42, 0x30, 0xde, 0x01, 0x95, 0x3e, 0x6f, 0x92, 0x6e, 0xb1, 0x1c, 0x7a, 0x0a, 0x10, 0x92, - 0xf9, 0x94, 0xae, 0x73, 0xed, 0x90, 0x1e, 0x5e, 0x3b, 0x24, 0x73, 0x36, 0x67, 0xfd, 0x7a, 0x9f, - 0x23, 0xcf, 0xeb, 0x67, 0xdc, 0x38, 0x47, 0xc1, 0x35, 0xd7, 0xfb, 0xbc, 0xb8, 0x9b, 0x47, 0xb5, - 0x22, 0xef, 0xeb, 0x7e, 0x17, 0xbe, 0xae, 0x13, 0x7b, 0x4d, 0x2e, 0x14, 0xfd, 0xf7, 0xb3, 0x3c, - 0x6b, 0x5a, 0x6c, 0xc7, 0x59, 0xf7, 0x44, 0x70, 0x87, 0x23, 0xd6, 0x06, 0x2a, 0xaf, 0x0d, 0xd4, - 0x20, 0xd0, 0x11, 0x0b, 0xef, 0xc0, 0xbf, 0x04, 0x85, 0x43, 0x58, 0xe3, 0xc7, 0x65, 0xe3, 0xbc, - 0x65, 0x1e, 0xdd, 0x57, 0xf0, 0x16, 0x4e, 0xd6, 0xbe, 0xa5, 0xe8, 0x1c, 0x4e, 0xab, 0xaf, 0xe9, - 0x54, 0x78, 0x6d, 0x3a, 0xd6, 0x49, 0xb5, 0xcf, 0x5e, 0x18, 0xf4, 0x04, 0xda, 0xd5, 0x16, 0x7f, - 0xce, 0xff, 0x1b, 0x1f, 0x87, 0xb7, 0x6f, 0x5c, 0x3f, 0xf3, 0x96, 0xb3, 0xa2, 0x35, 0xd3, 0xcb, - 0x63, 0x9c, 0x04, 0x78, 0xee, 0xe2, 0xc4, 0x5c, 0xd8, 0xb3, 0xc4, 0x77, 0x4c, 0xfa, 0x4b, 0x48, - 0xf9, 0x7f, 0x63, 0xa6, 0xd0, 0xe5, 0xbb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x6c, 0xc3, - 0x6a, 0x6e, 0x06, 0x00, 0x00, + // 650 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x6e, 0xda, 0x4c, + 0x10, 0x15, 0x38, 0x71, 0xc2, 0x40, 0x7e, 0xbe, 0x0d, 0xd1, 0xe7, 0xa2, 0x54, 0x4d, 0x5d, 0x35, + 0x4d, 0x5a, 0x01, 0x51, 0x7a, 0x41, 0x55, 0x89, 0x9b, 0x46, 0x51, 0x7b, 0x15, 0xb5, 0x4b, 0x7f, + 0xa4, 0xa8, 0x12, 0x32, 0xf6, 0x02, 0x56, 0x8d, 0xd7, 0x5a, 0x2f, 0xa4, 0x3c, 0x4d, 0x1f, 0xac, + 0x6f, 0xd0, 0xa7, 0xa8, 0xbc, 0xbb, 0x76, 0xd7, 0x60, 0x40, 0xb9, 0x82, 0x9d, 0x39, 0xe7, 0xcc, + 0xec, 0x78, 0x8e, 0x16, 0x8e, 0x5d, 0x3a, 0x99, 0xd0, 0xb0, 0xed, 0xd2, 0x70, 0xe8, 0x8f, 0xf8, + 0xcf, 0x56, 0xc4, 0x28, 0xa7, 0xc8, 0x94, 0xe1, 0xc6, 0x51, 0x96, 0x4e, 0x7e, 0x64, 0xb2, 0x91, + 0x72, 0x22, 0x1a, 0xf8, 0xae, 0x4f, 0x62, 0x19, 0xb6, 0xef, 0x61, 0xff, 0x5a, 0xa8, 0xdc, 0x84, + 0x33, 0x12, 0xd0, 0x88, 0xa0, 0x33, 0x30, 0xa5, 0xae, 0x55, 0x3a, 0x2d, 0x9d, 0x57, 0xaf, 0xf6, + 0x5b, 0x4a, 0x47, 0xe2, 0xb0, 0xca, 0xa2, 0x2e, 0x54, 0x03, 0x27, 0xe6, 0xfd, 0x69, 0xe4, 0x39, + 0x9c, 0x58, 0x65, 0x01, 0x3e, 0xc9, 0x83, 0xbf, 0x88, 0x5c, 0x2a, 0x8d, 0x21, 0x21, 0xc8, 0x98, + 0xfd, 0xdb, 0x80, 0xff, 0x24, 0xe8, 0x3d, 0xa3, 0xd3, 0xa8, 0xe7, 0x8e, 0xc9, 0xc4, 0x41, 0x5d, + 0x30, 0x47, 0xc9, 0x31, 0xb6, 0x4a, 0xa7, 0xc6, 0x79, 0xf5, 0xea, 0x79, 0x5e, 0x4f, 0x83, 0xb6, + 0xc4, 0xff, 0xf8, 0x26, 0xe4, 0x6c, 0x8e, 0x15, 0x29, 0xa1, 0xcf, 0x9c, 0x60, 0x4a, 0x62, 0xab, + 0xbc, 0x89, 0xfe, 0x55, 0xe0, 0x14, 0x5d, 0x92, 0xd0, 0x35, 0xec, 0xa6, 0xe3, 0xb1, 0x0c, 0x21, + 0xf0, 0x62, 0xb5, 0xc0, 0x47, 0x85, 0x94, 0x12, 0x19, 0xb1, 0xf1, 0x19, 0xaa, 0x5a, 0x6b, 0xe8, + 0x10, 0x8c, 0x1f, 0x64, 0x2e, 0x66, 0x59, 0xc1, 0xc9, 0x5f, 0xd4, 0x86, 0x6d, 0x51, 0x4f, 0x8d, + 0xec, 0xd1, 0xca, 0x12, 0x58, 0xe2, 0xde, 0x96, 0xdf, 0x94, 0x12, 0x55, 0xad, 0xe3, 0x07, 0xab, + 0x0a, 0xee, 0xb2, 0xea, 0x37, 0xd8, 0xcb, 0x5d, 0xa3, 0x40, 0xf7, 0x32, 0xaf, 0xdb, 0xc8, 0xeb, + 0x0a, 0xf6, 0x7c, 0x49, 0xd8, 0x3e, 0x4a, 0x3f, 0xae, 0x56, 0xd8, 0xae, 0x03, 0x5a, 0x66, 0xd9, + 0x43, 0x30, 0x65, 0x14, 0x35, 0xc1, 0x1c, 0x13, 0xc7, 0x23, 0x4c, 0x6d, 0xde, 0x71, 0x56, 0x6b, + 0xec, 0x84, 0x21, 0x09, 0x3e, 0x88, 0x24, 0x56, 0x20, 0xd4, 0x84, 0x1d, 0x57, 0x26, 0x54, 0x6f, + 0x47, 0x05, 0x93, 0xc4, 0x29, 0xc6, 0xe6, 0x50, 0x2f, 0x5a, 0x4a, 0xf4, 0x0c, 0xf6, 0xe4, 0x46, + 0xa7, 0x9b, 0x9c, 0x14, 0xaf, 0xe1, 0x9a, 0xab, 0x81, 0x51, 0x07, 0x20, 0xf6, 0x47, 0xa1, 0xc3, + 0xa7, 0x2c, 0x5b, 0xae, 0xff, 0xf3, 0xe5, 0x7a, 0x69, 0x1e, 0x6b, 0x50, 0xfb, 0x57, 0x09, 0x6a, + 0x7a, 0xd9, 0x87, 0x5e, 0xb2, 0x05, 0xbb, 0x8c, 0x38, 0x5e, 0x3f, 0x26, 0x7c, 0xed, 0x2d, 0x13, + 0x50, 0x8f, 0x70, 0x74, 0x09, 0x95, 0x7b, 0xe6, 0x73, 0x22, 0x08, 0xc6, 0x6a, 0xc2, 0xae, 0x40, + 0xf5, 0x08, 0xb7, 0xff, 0x18, 0x50, 0xd5, 0x32, 0xc8, 0x82, 0x9d, 0x19, 0x61, 0xb1, 0x4f, 0x43, + 0xd1, 0xe1, 0x16, 0x4e, 0x8f, 0xa8, 0x93, 0x99, 0x53, 0x0e, 0xe0, 0x49, 0x81, 0x70, 0xa1, 0x2d, + 0x3b, 0x99, 0x2d, 0x8d, 0xd5, 0xc4, 0x22, 0x43, 0x76, 0x35, 0x43, 0x6e, 0x09, 0xea, 0xd3, 0x22, + 0xea, 0x0a, 0x2b, 0xa2, 0xc7, 0x00, 0x13, 0xea, 0xf5, 0xc5, 0x79, 0x6e, 0x6d, 0x8b, 0xa5, 0xae, + 0x4c, 0xa8, 0x27, 0xf7, 0xaf, 0x71, 0xbb, 0xc9, 0xa9, 0x17, 0xf9, 0xdd, 0x2f, 0x1c, 0xa4, 0xe6, + 0xa6, 0xdb, 0x4d, 0x1e, 0x5d, 0xaf, 0x27, 0xb8, 0xba, 0xde, 0xa7, 0xcd, 0xee, 0x7c, 0x99, 0x57, + 0xac, 0x17, 0xb9, 0x53, 0xf7, 0xe5, 0xf7, 0xf4, 0x5b, 0x8b, 0x62, 0x6b, 0xbe, 0x75, 0x5d, 0x17, + 0xae, 0x29, 0x89, 0x85, 0x81, 0x1a, 0x0b, 0x03, 0xb5, 0x69, 0xba, 0xeb, 0xf2, 0xbc, 0x46, 0xfe, + 0x0c, 0x4c, 0x25, 0x52, 0xce, 0x3f, 0x32, 0xaa, 0x65, 0x95, 0xdd, 0x54, 0xf0, 0x0e, 0x0e, 0x16, + 0xcc, 0x87, 0x2e, 0xe0, 0x30, 0xb3, 0x5f, 0x5f, 0x73, 0x5a, 0x0d, 0x1f, 0x64, 0x71, 0xe9, 0x31, + 0x74, 0x02, 0x95, 0x2c, 0xa4, 0xee, 0xf9, 0x2f, 0xf0, 0xae, 0x79, 0xf7, 0x6a, 0xe4, 0xf3, 0xf1, + 0x74, 0x90, 0xb4, 0xd6, 0x1e, 0xcf, 0x23, 0xc2, 0x02, 0xe2, 0x8d, 0x08, 0x6b, 0x0f, 0x9d, 0x01, + 0xf3, 0xdd, 0xb6, 0x78, 0x40, 0x63, 0xf5, 0xca, 0x0e, 0x4c, 0x71, 0x7c, 0xfd, 0x37, 0x00, 0x00, + 0xff, 0xff, 0x73, 0xbb, 0xbf, 0x3e, 0x9c, 0x07, 0x00, 0x00, } diff --git a/protos/common/configtx.proto b/protos/common/configtx.proto index e29514eb2ff..6c602b614d1 100644 --- a/protos/common/configtx.proto +++ b/protos/common/configtx.proto @@ -31,25 +31,25 @@ package common; // // It is generated with the following scheme: // 1. Retrieve the existing configuration -// 2. Note the highest configuration sequence number, store it and increment it by one -// 3. Modify desired ConfigItems, setting each LastModified to the stored and incremented sequence number -// a) Note that the ConfigItem has a ChannelHeader header attached to it, who's type is set to CONFIGURATION_ITEM -// 4. Create Config message containing the new configuration, marshal it into ConfigEnvelope.config and encode the required signatures +// 2. Note the config properties (ConfigValue, ConfigPolicy, ConfigGroup) to be modified +// 3. Add any intermediate ConfigGroups to the ConfigUpdate.read_set (sparsely) +// 4. Add any additional desired dependencies to ConfigUpdate.read_set (sparsely) +// 5. Modify the config properties, incrementing each version by 1, set them in the ConfigUpdate.write_set +// Note: any element not modified but specified should already be in the read_set, so may be specified sparsely +// 6. Create ConfigUpdate message and marshal it into ConfigUpdateEnvelope.update and encode the required signatures // a) Each signature is of type ConfigSignature -// b) The ConfigSignature signature is over the concatenation of signatureHeader and the Config bytes (which includes a ChannelHeader) +// b) The ConfigSignature signature is over the concatenation of signature_header and the ConfigUpdate bytes (which includes a ChainHeader) // 5. Submit new Config for ordering in Envelope signed by submitter // a) The Envelope Payload has data set to the marshaled ConfigEnvelope -// b) The Envelope Payload has a header of type Header.Type.CONFIGURATION_TRANSACTION +// b) The Envelope Payload has a header of type Header.Type.CONFIG_UPDATE // XXX change CONFIGURATION_ITEM to CONFIG_UPDATE in common.proto // // The configuration manager will verify: -// 1. All configuration items and the envelope refer to the correct chain -// 2. Some configuration item has been added or modified -// 3. No existing configuration item has been ommitted -// 4. All configuration changes have a LastModification of one more than the last configuration's highest LastModification number -// 5. All configuration changes satisfy the corresponding modification policy +// 1. All items in the read_set exist at the read versions +// 2. All items in the write_set at a different version than, or not in, the read_set have been appropriately signed according to their mod_policy +// 3. The new configuration satisfies the ConfigSchema message ConfigEnvelope { - bytes config = 1; // A marshaled Config structure - repeated ConfigSignature signatures = 2; // Signatures over the config + Config config = 1; // A marshaled Config structure + ConfigUpdateEnvelope last_update = 2; // The most recent ConfigUpdateEnvelope whose diff generated the current config } message ConfigGroupSchema { @@ -68,6 +68,26 @@ message Config { ConfigGroup channel = 2; } +message ConfigUpdateEnvelope { + bytes config_update = 1; // A marshaled ConfigUpdate structure + repeated ConfigSignature signatures = 2; // Signatures over the config_update +} + +// ConfigUpdate is used to submit a subset of config and to have the orderer apply to Config +// it is always submitted inside a ConfigUpdateEnvelope which allows the addition of signatures +// resulting in a new total configuration. The update is applied as follows: +// 1. The versions from all of the elements in the read_set is verified against the versions in the existing config. +// If there is a mismatch in the read versions, then the config update fails and is rejected. +// 2. Any elements in the write_set with the same version as the read_set are ignored. +// 3. The corresponding mod_policy for every remaining element in the write_set is collected. +// 4. Each policy is checked against the signatures from the ConfigUpdateEnvelope, any failing to verify are rejected +// 5. The write_set is applied to the Config and the ConfigGroupSchema verifies that the updates were legal +message ConfigUpdate { + ChannelHeader header = 1; // Header scopes the update to a particular Channel + ConfigGroup read_set = 2; // ReadSet explicitly lists the portion of the config which was read, this should be sparse with only Version set + ConfigGroup write_set = 3; // WriteSet lists the portion of the config which was written, this should included updated Versions +} + // ConfigGroup is the hierarchical data structure for holding config message ConfigGroup { uint64 version = 1; diff --git a/protos/common/signed_data.go b/protos/common/signed_data.go index 94bf904f6bc..585467b0742 100644 --- a/protos/common/signed_data.go +++ b/protos/common/signed_data.go @@ -40,8 +40,8 @@ type Signable interface { AsSignedData() ([]*SignedData, error) } -// AsSignedData returns the set of signatures for the SignedCOnfigurationItem as SignedData or an error indicating why this was not possible -func (ce *ConfigEnvelope) AsSignedData() ([]*SignedData, error) { +// AsSignedData returns the set of signatures for the ConfigUpdateEnvelope as SignedData or an error indicating why this was not possible +func (ce *ConfigUpdateEnvelope) AsSignedData() ([]*SignedData, error) { if ce == nil { return nil, fmt.Errorf("No signatures for nil SignedConfigItem") } @@ -55,7 +55,7 @@ func (ce *ConfigEnvelope) AsSignedData() ([]*SignedData, error) { } result[i] = &SignedData{ - Data: util.ConcatenateBytes(configSig.SignatureHeader, ce.Config), + Data: util.ConcatenateBytes(configSig.SignatureHeader, ce.ConfigUpdate), Identity: sigHeader.Creator, Signature: configSig.Signature, } diff --git a/protos/common/signed_data_test.go b/protos/common/signed_data_test.go index e46c3ebc5c0..403549e3d1b 100644 --- a/protos/common/signed_data_test.go +++ b/protos/common/signed_data_test.go @@ -33,7 +33,7 @@ func marshalOrPanic(msg proto.Message) []byte { } func TestNilConfigEnvelopeAsSignedData(t *testing.T) { - var ce *ConfigEnvelope + var ce *ConfigUpdateEnvelope _, err := ce.AsSignedData() if err == nil { t.Fatalf("Should have errored trying to convert a nil signed config item to signed data") @@ -55,9 +55,9 @@ func TestConfigEnvelopeAsSignedData(t *testing.T) { } } - ce := &ConfigEnvelope{ - Config: configBytes, - Signatures: configSignatures, + ce := &ConfigUpdateEnvelope{ + ConfigUpdate: configBytes, + Signatures: configSignatures, } signedData, err := ce.AsSignedData()