diff --git a/orderer/common/filter/filter.go b/orderer/common/filter/filter.go index eb78192a8b6..257815fda0a 100644 --- a/orderer/common/filter/filter.go +++ b/orderer/common/filter/filter.go @@ -34,7 +34,7 @@ const ( Forward ) -// Rule defines a filter function which accepts, rejects, or forwards (to the next rule) a Envelope +// Rule defines a filter function which accepts, rejects, or forwards (to the next rule) an Envelope type Rule interface { // Apply applies the rule to the given Envelope, replying with the Action to take for the message // If the filter Accepts a message, it should provide a committer to use when writing the message to the chain @@ -55,7 +55,7 @@ type noopCommitter struct{} func (nc noopCommitter) Commit() {} func (nc noopCommitter) Isolated() bool { return false } -// NoopCommitter does nothing on commit and is not isolate +// NoopCommitter does nothing on commit and is not isolated var NoopCommitter = Committer(noopCommitter{}) // EmptyRejectRule rejects empty messages @@ -91,7 +91,7 @@ func NewRuleSet(rules []Rule) *RuleSet { } } -// Filter applies the rules given for this set in order, returning the committer, nil on valid, or nil, err on invalid +// Apply applies the rules given for this set in order, returning the committer, nil on valid, or nil, err on invalid func (rs *RuleSet) Apply(message *ab.Envelope) (Committer, error) { for _, rule := range rs.rules { action, committer := rule.Apply(message) diff --git a/orderer/common/sizefilter/sizefilter.go b/orderer/common/sizefilter/sizefilter.go new file mode 100644 index 00000000000..6bfc42eb5bd --- /dev/null +++ b/orderer/common/sizefilter/sizefilter.go @@ -0,0 +1,46 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sizefilter + +import ( + "github.com/hyperledger/fabric/orderer/common/filter" + ab "github.com/hyperledger/fabric/protos/common" + logging "github.com/op/go-logging" +) + +var logger = logging.MustGetLogger("orderer/common/sizefilter") + +// MaxBytesRule rejects messages larger than maxBytes +func MaxBytesRule(maxBytes uint32) filter.Rule { + return &maxBytesRule{maxBytes: maxBytes} +} + +type maxBytesRule struct { + maxBytes uint32 +} + +func (r *maxBytesRule) Apply(message *ab.Envelope) (filter.Action, filter.Committer) { + if size := messageByteSize(message); size > r.maxBytes { + logger.Warningf("%d byte message payload exceeds maximum allowed %d bytes", size, r.maxBytes) + return filter.Reject, nil + } + return filter.Forward, nil +} + +func messageByteSize(message *ab.Envelope) uint32 { + return uint32(len(message.Payload) + len(message.Signature)) +} diff --git a/orderer/common/sizefilter/sizefilter_test.go b/orderer/common/sizefilter/sizefilter_test.go new file mode 100644 index 00000000000..874a38670cf --- /dev/null +++ b/orderer/common/sizefilter/sizefilter_test.go @@ -0,0 +1,62 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sizefilter + +import ( + "testing" + + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/orderer/common/filter" + cb "github.com/hyperledger/fabric/protos/common" +) + +func TestMaxBytesRule(t *testing.T) { + dataSize := uint32(100) + maxBytes := calcMessageBytesForPayloadDataSize(dataSize) + rs := filter.NewRuleSet([]filter.Rule{MaxBytesRule(maxBytes), filter.AcceptRule}) + + t.Run("LessThan", func(t *testing.T) { + _, err := rs.Apply(makeMessage(make([]byte, dataSize-1))) + if err != nil { + t.Fatalf("Should have accepted") + } + }) + t.Run("Exact", func(t *testing.T) { + _, err := rs.Apply(makeMessage(make([]byte, dataSize))) + if err != nil { + t.Fatalf("Should have accepted") + } + }) + t.Run("TooBig", func(t *testing.T) { + _, err := rs.Apply(makeMessage(make([]byte, dataSize+1))) + if err == nil { + t.Fatalf("Should have rejected") + } + }) +} + +func calcMessageBytesForPayloadDataSize(dataSize uint32) uint32 { + return messageByteSize(makeMessage(make([]byte, dataSize))) +} + +func makeMessage(data []byte) *cb.Envelope { + data, err := proto.Marshal(&cb.Payload{Data: data}) + if err != nil { + panic(err) + } + return &cb.Envelope{Payload: data} +} diff --git a/orderer/multichain/chainsupport.go b/orderer/multichain/chainsupport.go index d4bcea0e494..64086b6dfb7 100644 --- a/orderer/multichain/chainsupport.go +++ b/orderer/multichain/chainsupport.go @@ -25,6 +25,7 @@ import ( "github.com/hyperledger/fabric/orderer/common/filter" "github.com/hyperledger/fabric/orderer/common/sharedconfig" "github.com/hyperledger/fabric/orderer/common/sigfilter" + "github.com/hyperledger/fabric/orderer/common/sizefilter" "github.com/hyperledger/fabric/orderer/rawledger" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" @@ -138,6 +139,7 @@ func newChainSupport( func createStandardFilters(configManager configtx.Manager, policyManager policies.Manager, sharedConfig sharedconfig.Manager) *filter.RuleSet { return filter.NewRuleSet([]filter.Rule{ filter.EmptyRejectRule, + sizefilter.MaxBytesRule(sharedConfig.BatchSize().AbsoluteMaxBytes), sigfilter.New(sharedConfig.IngressPolicy, policyManager), configtx.NewFilter(configManager), filter.AcceptRule, @@ -149,6 +151,7 @@ func createStandardFilters(configManager configtx.Manager, policyManager policie func createSystemChainFilters(ml *multiLedger, configManager configtx.Manager, policyManager policies.Manager, sharedConfig sharedconfig.Manager) *filter.RuleSet { return filter.NewRuleSet([]filter.Rule{ filter.EmptyRejectRule, + sizefilter.MaxBytesRule(sharedConfig.BatchSize().AbsoluteMaxBytes), sigfilter.New(sharedConfig.IngressPolicy, policyManager), newSystemChainFilter(ml), configtx.NewFilter(configManager),