go_study/fabric-main/common/configtx/validator_test.go

420 lines
14 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package configtx
import (
"fmt"
"strings"
"testing"
cb "github.com/hyperledger/fabric-protos-go/common"
mockpolicies "github.com/hyperledger/fabric/common/configtx/mock"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/require"
)
//go:generate counterfeiter -o mock/policy_manager.go --fake-name PolicyManager . policyManager
type policyManager interface {
policies.Manager
}
//go:generate counterfeiter -o mock/policy.go --fake-name Policy . policy
type policy interface {
policies.Policy
}
var defaultChannel = "default.channel.id"
func defaultPolicyManager() *mockpolicies.PolicyManager {
fakePolicy := &mockpolicies.Policy{}
fakePolicy.EvaluateSignedDataReturns(nil)
fakePolicyManager := &mockpolicies.PolicyManager{}
fakePolicyManager.GetPolicyReturns(fakePolicy, true)
fakePolicyManager.ManagerReturns(fakePolicyManager, true)
return fakePolicyManager
}
type configPair struct {
key string
value *cb.ConfigValue
}
func makeConfigPair(id, modificationPolicy string, lastModified uint64, data []byte) *configPair {
return &configPair{
key: id,
value: &cb.ConfigValue{
ModPolicy: modificationPolicy,
Version: lastModified,
Value: data,
},
}
}
func makeConfig(configPairs ...*configPair) *cb.Config {
channelGroup := protoutil.NewConfigGroup()
for _, pair := range configPairs {
channelGroup.Values[pair.key] = pair.value
}
return &cb.Config{
ChannelGroup: channelGroup,
}
}
func makeConfigSet(configPairs ...*configPair) *cb.ConfigGroup {
result := protoutil.NewConfigGroup()
for _, pair := range configPairs {
result.Values[pair.key] = pair.value
}
return result
}
func makeConfigUpdateEnvelope(channelID string, readSet, writeSet *cb.ConfigGroup) *cb.Envelope {
return &cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG_UPDATE),
}),
},
Data: protoutil.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
ConfigUpdate: protoutil.MarshalOrPanic(&cb.ConfigUpdate{
ChannelId: channelID,
ReadSet: readSet,
WriteSet: writeSet,
}),
}),
}),
}
}
func TestEmptyChannel(t *testing.T) {
_, err := NewValidatorImpl("foo", &cb.Config{}, "foonamespace", defaultPolicyManager())
require.Error(t, err)
}
// TestDifferentChannelID tests that a config update for a different channel ID fails
func TestDifferentChannelID(t *testing.T) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope("wrongChannel", makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
_, err = vi.ProposeConfigUpdate(newConfig)
if err == nil {
t.Error("Should have errored when proposing a new config set the wrong channel ID")
}
}
// TestOldConfigReplay tests that resubmitting a config for a sequence number which is not newer is ignored
func TestOldConfigReplay(t *testing.T) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(defaultChannel, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))))
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating DeltaSet: attempt to set key [Value] /foonamespace/foo to version 0, but key is at version 0")
}
// TestValidConfigChange tests the happy path of updating a config value with no defaultModificationPolicy
func TestValidConfigChange(t *testing.T) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(defaultChannel, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
configEnv, err := vi.ProposeConfigUpdate(newConfig)
if err != nil {
t.Errorf("Should not have errored proposing config: %s", err)
}
err = vi.Validate(configEnv)
if err != nil {
t.Errorf("Should not have errored validating config: %s", err)
}
}
// 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) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 1, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(
defaultChannel,
makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))),
makeConfigSet(makeConfigPair("bar", "bar", 2, []byte("bar"))),
)
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating ReadSet: proposed update requires that key [Value] /foonamespace/foo be at version 0, but it is currently at version 1")
}
// 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) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 1, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(
defaultChannel,
makeConfigSet(),
makeConfigSet(
makeConfigPair("foo", "foo", 2, []byte("foo")),
makeConfigPair("bar", "bar", 1, []byte("bar")),
),
)
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating DeltaSet: attempted to set key [Value] /foonamespace/bar to version 1, but key does not exist")
}
// TestConfigPartialUpdate tests to make sure that a new config can set only part
// of the config and still be accepted
func TestConfigPartialUpdate(t *testing.T) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(
makeConfigPair("foo", "foo", 0, []byte("foo")),
makeConfigPair("bar", "bar", 0, []byte("bar")),
),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(
defaultChannel,
makeConfigSet(),
makeConfigSet(makeConfigPair("bar", "bar", 1, []byte("bar"))),
)
_, err = vi.ProposeConfigUpdate(newConfig)
require.NoError(t, err, "Should have allowed partial update")
}
// TestEmptyConfigUpdate tests to make sure that an empty config is rejected as an update
func TestEmptyConfigUpdate(t *testing.T) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := &cb.Envelope{}
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error converting envelope to config update: envelope must have a Header")
}
// TestSilentConfigModification tests to make sure that even if a validly signed new config for an existing sequence number
// 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) {
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(
makeConfigPair("foo", "foo", 0, []byte("foo")),
makeConfigPair("bar", "bar", 0, []byte("bar")),
),
"foonamespace",
defaultPolicyManager())
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(
defaultChannel,
makeConfigSet(),
makeConfigSet(
makeConfigPair("foo", "foo", 0, []byte("different")),
makeConfigPair("bar", "bar", 1, []byte("bar")),
),
)
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating DeltaSet: attempt to set key [Value] /foonamespace/foo to version 0, but key is at version 0")
}
// 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) {
pm := defaultPolicyManager()
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
pm)
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
// Set the mock policy to error
fakePolicy := &mockpolicies.Policy{}
fakePolicy.EvaluateSignedDataReturns(fmt.Errorf("err"))
pm.GetPolicyReturns(fakePolicy, true)
newConfig := makeConfigUpdateEnvelope(defaultChannel, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating DeltaSet: policy for [Value] /foonamespace/foo not satisfied: err")
}
// TestUnchangedConfigViolatesPolicy checks to make sure that existing config items are not revalidated against their modification policies
// as the policy may have changed, certs revoked, etc. since the config was adopted.
func TestUnchangedConfigViolatesPolicy(t *testing.T) {
pm := defaultPolicyManager()
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
pm)
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
newConfig := makeConfigUpdateEnvelope(
defaultChannel,
makeConfigSet(makeConfigPair("foo", "foo", 0, []byte("foo"))),
makeConfigSet(makeConfigPair("bar", "bar", 0, []byte("foo"))),
)
configEnv, err := vi.ProposeConfigUpdate(newConfig)
if err != nil {
t.Errorf("Should not have errored proposing config, but got %s", err)
}
err = vi.Validate(configEnv)
if err != nil {
t.Errorf("Should not have errored validating config, but got %s", err)
}
}
// TestInvalidProposal checks that even if the policy allows the transaction and the sequence etc. is well formed,
// that if the handler does not accept the config, it is rejected
func TestInvalidProposal(t *testing.T) {
pm := defaultPolicyManager()
vi, err := NewValidatorImpl(
defaultChannel,
makeConfig(makeConfigPair("foo", "foo", 0, []byte("foo"))),
"foonamespace",
pm)
if err != nil {
t.Fatalf("Error constructing config manager: %s", err)
}
fakePolicy := &mockpolicies.Policy{}
fakePolicy.EvaluateSignedDataReturns(fmt.Errorf("err"))
pm.GetPolicyReturns(fakePolicy, true)
newConfig := makeConfigUpdateEnvelope(defaultChannel, makeConfigSet(), makeConfigSet(makeConfigPair("foo", "foo", 1, []byte("foo"))))
_, err = vi.ProposeConfigUpdate(newConfig)
require.EqualError(t, err, "error authorizing update: error validating DeltaSet: policy for [Value] /foonamespace/foo not satisfied: err")
}
func TestValidateErrors(t *testing.T) {
t.Run("TestNilConfigEnv", func(t *testing.T) {
err := (&ValidatorImpl{}).Validate(nil)
require.Error(t, err)
require.Regexp(t, "config envelope is nil", err.Error())
})
t.Run("TestNilConfig", func(t *testing.T) {
err := (&ValidatorImpl{}).Validate(&cb.ConfigEnvelope{})
require.Error(t, err)
require.Regexp(t, "config envelope has nil config", err.Error())
})
t.Run("TestSequenceSkip", func(t *testing.T) {
err := (&ValidatorImpl{}).Validate(&cb.ConfigEnvelope{
Config: &cb.Config{
Sequence: 2,
},
})
require.Error(t, err)
require.Regexp(t, "config currently at sequence 0", err.Error())
})
}
func TestConstructionErrors(t *testing.T) {
t.Run("NilConfig", func(t *testing.T) {
v, err := NewValidatorImpl("test", nil, "foonamespace", &mockpolicies.PolicyManager{})
require.Nil(t, v)
require.Error(t, err)
require.Regexp(t, "nil config parameter", err.Error())
})
t.Run("NilChannelGroup", func(t *testing.T) {
v, err := NewValidatorImpl("test", &cb.Config{}, "foonamespace", &mockpolicies.PolicyManager{})
require.Nil(t, v)
require.Error(t, err)
require.Regexp(t, "nil channel group", err.Error())
})
t.Run("BadChannelID", func(t *testing.T) {
v, err := NewValidatorImpl("*&$#@*&@$#*&", &cb.Config{ChannelGroup: &cb.ConfigGroup{}}, "foonamespace", &mockpolicies.PolicyManager{})
require.Nil(t, v)
require.Error(t, err)
require.Regexp(t, "bad channel ID", err.Error())
require.EqualError(t, err, "bad channel ID: '*&$#@*&@$#*&' contains illegal characters")
})
t.Run("EmptyChannelID", func(t *testing.T) {
v, err := NewValidatorImpl("", &cb.Config{ChannelGroup: &cb.ConfigGroup{}}, "foonamespace", &mockpolicies.PolicyManager{})
require.Nil(t, v)
require.Error(t, err)
require.Regexp(t, "bad channel ID", err.Error())
require.EqualError(t, err, "bad channel ID: channel ID illegal, cannot be empty")
})
t.Run("MaxLengthChannelID", func(t *testing.T) {
maxChannelID := strings.Repeat("a", 250)
v, err := NewValidatorImpl(maxChannelID, &cb.Config{ChannelGroup: &cb.ConfigGroup{}}, "foonamespace", &mockpolicies.PolicyManager{})
require.Nil(t, v)
require.Error(t, err)
require.EqualError(t, err, "bad channel ID: channel ID illegal, cannot be longer than 249")
})
}