Skip to content

Commit 73ea179

Browse files
author
Jason Yellick
committed
Hook configuration manager into solo broadcast
With the completion of the broadcast filtering framework, the configuration manager, the policy manager, and the genesis block creation, it is now possible for the orderer to filter and apply configuration received over the wire as a transaction. This changeset adds that functionality for the solo ordering service. https://jira.hyperledger.org/browse/FAB-593 Change-Id: I4e50c1f811d8cff02f67de7de278fbffe230f882 Signed-off-by: Jason Yellick <[email protected]>
1 parent c883319 commit 73ea179

File tree

8 files changed

+423
-59
lines changed

8 files changed

+423
-59
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package configfilter
18+
19+
import (
20+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
21+
"github.com/hyperledger/fabric/orderer/common/broadcastfilter"
22+
"github.com/hyperledger/fabric/orderer/common/configtx"
23+
24+
"github.com/golang/protobuf/proto"
25+
)
26+
27+
type configFilter struct {
28+
configManager configtx.Manager
29+
}
30+
31+
func New(manager configtx.Manager) broadcastfilter.Rule {
32+
return &configFilter{
33+
configManager: manager,
34+
}
35+
}
36+
37+
// Apply applies the rule to the given BroadcastMessage, replying with the Action to take for the message
38+
func (cf *configFilter) Apply(message *ab.BroadcastMessage) broadcastfilter.Action {
39+
config := &ab.ConfigurationEnvelope{}
40+
41+
err := proto.Unmarshal(message.Data, config)
42+
if err != nil {
43+
return broadcastfilter.Forward
44+
}
45+
46+
err = cf.configManager.Validate(config)
47+
if err != nil {
48+
return broadcastfilter.Reject
49+
}
50+
51+
return broadcastfilter.Reconfigure
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package configfilter
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
24+
"github.com/hyperledger/fabric/orderer/common/broadcastfilter"
25+
26+
"github.com/golang/protobuf/proto"
27+
)
28+
29+
type mockConfigManager struct {
30+
err error
31+
}
32+
33+
func (mcm *mockConfigManager) Validate(configtx *ab.ConfigurationEnvelope) error {
34+
return mcm.err
35+
}
36+
37+
func (mcm *mockConfigManager) Apply(configtx *ab.ConfigurationEnvelope) error {
38+
return mcm.err
39+
}
40+
41+
func TestForwardNonConfig(t *testing.T) {
42+
cf := New(&mockConfigManager{})
43+
result := cf.Apply(&ab.BroadcastMessage{
44+
Data: []byte("Opaque"),
45+
})
46+
if result != broadcastfilter.Forward {
47+
t.Fatalf("Should have forwarded opaque message")
48+
}
49+
}
50+
51+
func TestAcceptGoodConfig(t *testing.T) {
52+
cf := New(&mockConfigManager{})
53+
config := &ab.ConfigurationEnvelope{}
54+
configBytes, _ := proto.Marshal(config)
55+
result := cf.Apply(&ab.BroadcastMessage{
56+
Data: configBytes,
57+
})
58+
if result != broadcastfilter.Reconfigure {
59+
t.Fatalf("Should have indiated a good config message causes a reconfiguration")
60+
}
61+
}
62+
63+
func TestRejectBadConfig(t *testing.T) {
64+
cf := New(&mockConfigManager{err: fmt.Errorf("Error")})
65+
config := &ab.ConfigurationEnvelope{}
66+
configBytes, _ := proto.Marshal(config)
67+
result := cf.Apply(&ab.BroadcastMessage{
68+
Data: configBytes,
69+
})
70+
if result != broadcastfilter.Reject {
71+
t.Fatalf("Should have rejected bad config message")
72+
}
73+
}

orderer/main.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
2929
"github.com/hyperledger/fabric/orderer/common/bootstrap"
3030
"github.com/hyperledger/fabric/orderer/common/bootstrap/static"
31+
"github.com/hyperledger/fabric/orderer/common/broadcastfilter"
32+
"github.com/hyperledger/fabric/orderer/common/broadcastfilter/configfilter"
3133
"github.com/hyperledger/fabric/orderer/common/configtx"
3234
"github.com/hyperledger/fabric/orderer/common/policies"
3335
"github.com/hyperledger/fabric/orderer/config"
@@ -119,6 +121,14 @@ func bootstrapConfigManager(lastConfigTx *ab.ConfigurationEnvelope) configtx.Man
119121
return configManager
120122
}
121123

124+
func createBroadcastRuleset(configManager configtx.Manager) *broadcastfilter.RuleSet {
125+
return broadcastfilter.NewRuleSet([]broadcastfilter.Rule{
126+
broadcastfilter.EmptyRejectRule,
127+
configfilter.New(configManager),
128+
broadcastfilter.AcceptRule,
129+
})
130+
}
131+
122132
func launchSolo(conf *config.TopLevel) {
123133
grpcServer := grpc.NewServer()
124134

@@ -175,7 +185,15 @@ func launchSolo(conf *config.TopLevel) {
175185
// XXX actually use the config manager in the future
176186
_ = configManager
177187

178-
solo.New(int(conf.General.QueueSize), int(conf.General.BatchSize), int(conf.General.MaxWindowSize), conf.General.BatchTimeout, rawledger, grpcServer)
188+
solo.New(int(conf.General.QueueSize),
189+
int(conf.General.BatchSize),
190+
int(conf.General.MaxWindowSize),
191+
conf.General.BatchTimeout,
192+
rawledger,
193+
grpcServer,
194+
createBroadcastRuleset(configManager),
195+
configManager,
196+
)
179197
grpcServer.Serve(lis)
180198
}
181199

orderer/sample_clients/broadcast_timestamp/client.go

+15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ func (s *broadcastClient) broadcast(transaction []byte) error {
3939
return s.client.Send(&ab.BroadcastMessage{Data: transaction})
4040
}
4141

42+
func (s *broadcastClient) getAck() error {
43+
msg, err := s.client.Recv()
44+
if err != nil {
45+
return err
46+
}
47+
if msg.Status != ab.Status_SUCCESS {
48+
return fmt.Errorf("Got unexpected status: %v", msg.Status)
49+
}
50+
return nil
51+
}
52+
4253
func main() {
4354
config := config.Load()
4455
serverAddr := fmt.Sprintf("%s:%d", config.General.ListenAddress, config.General.ListenPort)
@@ -56,4 +67,8 @@ func main() {
5667

5768
s := newBroadcastClient(client)
5869
s.broadcast([]byte(fmt.Sprintf("Testing %v", time.Now())))
70+
err = s.getAck()
71+
if err != nil {
72+
fmt.Printf("\n\n!!!!!!!!!!!!!!!!!\n%v\n!!!!!!!!!!!!!!!!!\n", err)
73+
}
5974
}

orderer/sample_clients/deliver_stdout/client.go

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func (r *deliverClient) readUntilClose() {
7373
for {
7474
msg, err := r.client.Recv()
7575
if err != nil {
76+
fmt.Println("Error receiving:", err)
7677
return
7778
}
7879

orderer/solo/broadcast.go

+77-48
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,39 @@ import (
2121

2222
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
2323
"github.com/hyperledger/fabric/orderer/common/broadcastfilter"
24+
"github.com/hyperledger/fabric/orderer/common/configtx"
2425
"github.com/hyperledger/fabric/orderer/rawledger"
26+
27+
"github.com/golang/protobuf/proto"
2528
)
2629

2730
type broadcastServer struct {
28-
queueSize int
29-
batchSize int
30-
batchTimeout time.Duration
31-
rl rawledger.Writer
32-
filter *broadcastfilter.RuleSet
33-
sendChan chan *ab.BroadcastMessage
34-
exitChan chan struct{}
31+
queueSize int
32+
batchSize int
33+
batchTimeout time.Duration
34+
rl rawledger.Writer
35+
filter *broadcastfilter.RuleSet
36+
configManager configtx.Manager
37+
sendChan chan *ab.BroadcastMessage
38+
exitChan chan struct{}
3539
}
3640

37-
func newBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer) *broadcastServer {
38-
bs := newPlainBroadcastServer(queueSize, batchSize, batchTimeout, rl)
41+
func newBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer, filters *broadcastfilter.RuleSet, configManager configtx.Manager) *broadcastServer {
42+
bs := newPlainBroadcastServer(queueSize, batchSize, batchTimeout, rl, filters, configManager)
3943
go bs.main()
4044
return bs
4145
}
4246

43-
func newPlainBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer) *broadcastServer {
47+
func newPlainBroadcastServer(queueSize, batchSize int, batchTimeout time.Duration, rl rawledger.Writer, filters *broadcastfilter.RuleSet, configManager configtx.Manager) *broadcastServer {
4448
bs := &broadcastServer{
45-
queueSize: queueSize,
46-
batchSize: batchSize,
47-
batchTimeout: batchTimeout,
48-
rl: rl,
49-
filter: broadcastfilter.NewRuleSet([]broadcastfilter.Rule{broadcastfilter.EmptyRejectRule, broadcastfilter.AcceptRule}),
50-
sendChan: make(chan *ab.BroadcastMessage),
51-
exitChan: make(chan struct{}),
49+
queueSize: queueSize,
50+
batchSize: batchSize,
51+
batchTimeout: batchTimeout,
52+
rl: rl,
53+
filter: filters,
54+
configManager: configManager,
55+
sendChan: make(chan *ab.BroadcastMessage),
56+
exitChan: make(chan struct{}),
5257
}
5358
return bs
5459
}
@@ -59,41 +64,64 @@ func (bs *broadcastServer) halt() {
5964

6065
func (bs *broadcastServer) main() {
6166
var curBatch []*ab.BroadcastMessage
62-
outer:
67+
var timer <-chan time.Time
68+
69+
cutBatch := func() {
70+
bs.rl.Append(curBatch, nil)
71+
curBatch = nil
72+
timer = nil
73+
}
74+
6375
for {
64-
timer := time.After(bs.batchTimeout)
65-
for {
66-
select {
67-
case msg := <-bs.sendChan:
68-
// The messages must be filtered a second time in case configuration has changed since the message was received
69-
action, _ := bs.filter.Apply(msg)
70-
switch action {
71-
case broadcastfilter.Accept:
72-
curBatch = append(curBatch, msg)
73-
if len(curBatch) < bs.batchSize {
74-
continue
75-
}
76+
select {
77+
case msg := <-bs.sendChan:
78+
// The messages must be filtered a second time in case configuration has changed since the message was received
79+
action, _ := bs.filter.Apply(msg)
80+
switch action {
81+
case broadcastfilter.Accept:
82+
curBatch = append(curBatch, msg)
83+
84+
if len(curBatch) >= bs.batchSize {
7685
logger.Debugf("Batch size met, creating block")
77-
case broadcastfilter.Forward:
78-
logger.Debugf("Ignoring message because it was not accepted by a filter")
79-
default:
80-
// TODO add support for other cases, unreachable for now
81-
logger.Fatalf("NOT IMPLEMENTED YET")
86+
cutBatch()
87+
} else if len(curBatch) == 1 {
88+
// If this is the first request in a batch, start the batch timer
89+
timer = time.After(bs.batchTimeout)
8290
}
83-
case <-timer:
84-
if len(curBatch) == 0 {
85-
continue outer
91+
case broadcastfilter.Reconfigure:
92+
// TODO, this is unmarshaling for a second time, we need a cleaner interface, maybe Apply returns a second arg with thing to put in the batch
93+
newConfig := &ab.ConfigurationEnvelope{}
94+
if err := proto.Unmarshal(msg.Data, newConfig); err != nil {
95+
logger.Errorf("A change was flagged as configuration, but could not be unmarshaled: %v", err)
96+
continue
8697
}
87-
logger.Debugf("Batch timer expired, creating block")
88-
case <-bs.exitChan:
89-
logger.Debugf("Exiting")
90-
return
98+
err := bs.configManager.Apply(newConfig)
99+
if err != nil {
100+
logger.Warningf("A configuration change made it through the ingress filter but could not be included in a batch: %v", err)
101+
continue
102+
}
103+
104+
logger.Debugf("Configuration change applied successfully, committing previous block and configuration block")
105+
cutBatch()
106+
bs.rl.Append([]*ab.BroadcastMessage{msg}, nil)
107+
case broadcastfilter.Reject:
108+
fallthrough
109+
case broadcastfilter.Forward:
110+
logger.Debugf("Ignoring message because it was not accepted by a filter")
111+
default:
112+
logger.Fatalf("Received an unknown rule response: %v", action)
91113
}
92-
break
114+
case <-timer:
115+
if len(curBatch) == 0 {
116+
logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug")
117+
continue
118+
}
119+
logger.Debugf("Batch timer expired, creating block")
120+
cutBatch()
121+
case <-bs.exitChan:
122+
logger.Debugf("Exiting")
123+
return
93124
}
94-
95-
bs.rl.Append(curBatch, nil)
96-
curBatch = nil
97125
}
98126
}
99127

@@ -139,6 +167,8 @@ func (b *broadcaster) queueBroadcastMessages(srv ab.AtomicBroadcast_BroadcastSer
139167
action, _ := b.bs.filter.Apply(msg)
140168

141169
switch action {
170+
case broadcastfilter.Reconfigure:
171+
fallthrough
142172
case broadcastfilter.Accept:
143173
select {
144174
case b.queue <- msg:
@@ -151,8 +181,7 @@ func (b *broadcaster) queueBroadcastMessages(srv ab.AtomicBroadcast_BroadcastSer
151181
case broadcastfilter.Reject:
152182
err = srv.Send(&ab.BroadcastResponse{ab.Status_BAD_REQUEST})
153183
default:
154-
// TODO add support for other cases, unreachable for now
155-
logger.Fatalf("NOT IMPLEMENTED YET")
184+
logger.Fatalf("Unknown filter action :%v", action)
156185
}
157186

158187
if err != nil {

0 commit comments

Comments
 (0)