Skip to content

Commit d6ee6ff

Browse files
authored
Merge pull request #26 from cloudstruct/feature/protocol-refactor
Refactor protocol state machine
2 parents 662f679 + 38748ad commit d6ee6ff

File tree

7 files changed

+336
-198
lines changed

7 files changed

+336
-198
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ A Go client implementation of the Cardano Ouroboros network protocol
44

55
This is loosely based on the [official Haskell implementation](https://github.com/input-output-hk/ouroboros-network)
66

7+
NOTE: this library is under heavily development, and the interface should not be considered stable until it reaches `v1.0.0`
8+
79
## Implementation status
810

911
The Ouroboros protocol consists of a simple multiplexer protocol and various mini-protocols that run on top of it.

protocol/blockfetch/blockfetch.go

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,51 @@ var (
2020
STATE_DONE = protocol.NewState(4, "Done")
2121
)
2222

23+
var stateMap = protocol.StateMap{
24+
STATE_IDLE: protocol.StateMapEntry{
25+
Agency: protocol.AGENCY_CLIENT,
26+
Transitions: []protocol.StateTransition{
27+
{
28+
MsgType: MESSAGE_TYPE_REQUEST_RANGE,
29+
NewState: STATE_BUSY,
30+
},
31+
{
32+
MsgType: MESSAGE_TYPE_CLIENT_DONE,
33+
NewState: STATE_DONE,
34+
},
35+
},
36+
},
37+
STATE_BUSY: protocol.StateMapEntry{
38+
Agency: protocol.AGENCY_SERVER,
39+
Transitions: []protocol.StateTransition{
40+
{
41+
MsgType: MESSAGE_TYPE_START_BATCH,
42+
NewState: STATE_STREAMING,
43+
},
44+
{
45+
MsgType: MESSAGE_TYPE_NO_BLOCKS,
46+
NewState: STATE_IDLE,
47+
},
48+
},
49+
},
50+
STATE_STREAMING: protocol.StateMapEntry{
51+
Agency: protocol.AGENCY_SERVER,
52+
Transitions: []protocol.StateTransition{
53+
{
54+
MsgType: MESSAGE_TYPE_BLOCK,
55+
NewState: STATE_STREAMING,
56+
},
57+
{
58+
MsgType: MESSAGE_TYPE_BATCH_DONE,
59+
NewState: STATE_IDLE,
60+
},
61+
},
62+
},
63+
STATE_DONE: protocol.StateMapEntry{
64+
Agency: protocol.AGENCY_NONE,
65+
},
66+
}
67+
2368
type BlockFetch struct {
2469
proto *protocol.Protocol
2570
callbackConfig *BlockFetchCallbackConfig
@@ -42,9 +87,17 @@ func New(m *muxer.Muxer, errorChan chan error, callbackConfig *BlockFetchCallbac
4287
b := &BlockFetch{
4388
callbackConfig: callbackConfig,
4489
}
45-
b.proto = protocol.New(PROTOCOL_NAME, PROTOCOL_ID, m, errorChan, b.messageHandler, NewMsgFromCbor)
46-
// Set initial state
47-
b.proto.SetState(STATE_IDLE)
90+
protoConfig := protocol.ProtocolConfig{
91+
Name: PROTOCOL_NAME,
92+
ProtocolId: PROTOCOL_ID,
93+
Muxer: m,
94+
ErrorChan: errorChan,
95+
MessageHandlerFunc: b.messageHandler,
96+
MessageFromCborFunc: NewMsgFromCbor,
97+
StateMap: stateMap,
98+
InitialState: STATE_IDLE,
99+
}
100+
b.proto = protocol.New(protoConfig)
48101
return b
49102
}
50103

@@ -66,57 +119,32 @@ func (b *BlockFetch) messageHandler(msg protocol.Message) error {
66119
}
67120

68121
func (b *BlockFetch) RequestRange(start []interface{}, end []interface{}) error {
69-
if err := b.proto.LockState([]protocol.State{STATE_IDLE}); err != nil {
70-
return fmt.Errorf("%s: RequestRange: protocol not in expected state", PROTOCOL_NAME)
71-
}
72122
msg := newMsgRequestRange(start, end)
73-
// Unlock and change state when we're done
74-
defer b.proto.UnlockState(STATE_BUSY)
75-
// Send request
76123
return b.proto.SendMessage(msg, false)
77124
}
78125

79126
func (b *BlockFetch) ClientDone() error {
80-
if err := b.proto.LockState([]protocol.State{STATE_IDLE}); err != nil {
81-
return fmt.Errorf("%s: ClientDone: protocol not in expected state", PROTOCOL_NAME)
82-
}
83127
msg := newMsgClientDone()
84-
// Unlock and change state when we're done
85-
defer b.proto.UnlockState(STATE_BUSY)
86-
// Send request
87128
return b.proto.SendMessage(msg, false)
88129
}
89130

90131
func (b *BlockFetch) handleStartBatch() error {
91-
if err := b.proto.LockState([]protocol.State{STATE_BUSY}); err != nil {
92-
return fmt.Errorf("received block-fetch StartBatch message when protocol not in expected state")
93-
}
94132
if b.callbackConfig.StartBatchFunc == nil {
95133
return fmt.Errorf("received block-fetch StartBatch message but no callback function is defined")
96134
}
97-
// Unlock and change state when we're done
98-
defer b.proto.UnlockState(STATE_STREAMING)
99135
// Call the user callback function
100136
return b.callbackConfig.StartBatchFunc()
101137
}
102138

103139
func (b *BlockFetch) handleNoBlocks() error {
104-
if err := b.proto.LockState([]protocol.State{STATE_BUSY}); err != nil {
105-
return fmt.Errorf("received block-fetch NoBlocks message when protocol not in expected state")
106-
}
107140
if b.callbackConfig.NoBlocksFunc == nil {
108141
return fmt.Errorf("received block-fetch NoBlocks message but no callback function is defined")
109142
}
110-
// Unlock and change state when we're done
111-
defer b.proto.UnlockState(STATE_IDLE)
112143
// Call the user callback function
113144
return b.callbackConfig.NoBlocksFunc()
114145
}
115146

116147
func (b *BlockFetch) handleBlock(msgGeneric protocol.Message) error {
117-
if err := b.proto.LockState([]protocol.State{STATE_STREAMING}); err != nil {
118-
return fmt.Errorf("received block-fetch Block message when protocol not in expected state")
119-
}
120148
if b.callbackConfig.BlockFunc == nil {
121149
return fmt.Errorf("received block-fetch Block message but no callback function is defined")
122150
}
@@ -130,21 +158,14 @@ func (b *BlockFetch) handleBlock(msgGeneric protocol.Message) error {
130158
if err != nil {
131159
return err
132160
}
133-
// Unlock and change state when we're done
134-
defer b.proto.UnlockState(STATE_STREAMING)
135161
// Call the user callback function
136162
return b.callbackConfig.BlockFunc(wrapBlock.Type, blk)
137163
}
138164

139165
func (b *BlockFetch) handleBatchDone() error {
140-
if err := b.proto.LockState([]protocol.State{STATE_STREAMING}); err != nil {
141-
return fmt.Errorf("received block-fetch BatchDone message when protocol not in expected state")
142-
}
143166
if b.callbackConfig.BatchDoneFunc == nil {
144167
return fmt.Errorf("received block-fetch BatchDone message but no callback function is defined")
145168
}
146-
// Unlock and change state when we're done
147-
defer b.proto.UnlockState(STATE_IDLE)
148169
// Call the user callback function
149170
return b.callbackConfig.BatchDoneFunc()
150171
}

protocol/chainsync/chainsync.go

Lines changed: 77 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,72 @@ var (
2222
STATE_DONE = protocol.NewState(5, "Done")
2323
)
2424

25+
var stateMap = protocol.StateMap{
26+
STATE_IDLE: protocol.StateMapEntry{
27+
Agency: protocol.AGENCY_CLIENT,
28+
Transitions: []protocol.StateTransition{
29+
{
30+
MsgType: MESSAGE_TYPE_REQUEST_NEXT,
31+
NewState: STATE_CAN_AWAIT,
32+
},
33+
{
34+
MsgType: MESSAGE_TYPE_FIND_INTERSECT,
35+
NewState: STATE_INTERSECT,
36+
},
37+
{
38+
MsgType: MESSAGE_TYPE_DONE,
39+
NewState: STATE_DONE,
40+
},
41+
},
42+
},
43+
STATE_CAN_AWAIT: protocol.StateMapEntry{
44+
Agency: protocol.AGENCY_SERVER,
45+
Transitions: []protocol.StateTransition{
46+
{
47+
MsgType: MESSAGE_TYPE_AWAIT_REPLY,
48+
NewState: STATE_MUST_REPLY,
49+
},
50+
{
51+
MsgType: MESSAGE_TYPE_ROLL_FORWARD,
52+
NewState: STATE_IDLE,
53+
},
54+
{
55+
MsgType: MESSAGE_TYPE_ROLL_BACKWARD,
56+
NewState: STATE_IDLE,
57+
},
58+
},
59+
},
60+
STATE_INTERSECT: protocol.StateMapEntry{
61+
Agency: protocol.AGENCY_SERVER,
62+
Transitions: []protocol.StateTransition{
63+
{
64+
MsgType: MESSAGE_TYPE_INTERSECT_FOUND,
65+
NewState: STATE_IDLE,
66+
},
67+
{
68+
MsgType: MESSAGE_TYPE_INTERSECT_NOT_FOUND,
69+
NewState: STATE_IDLE,
70+
},
71+
},
72+
},
73+
STATE_MUST_REPLY: protocol.StateMapEntry{
74+
Agency: protocol.AGENCY_SERVER,
75+
Transitions: []protocol.StateTransition{
76+
{
77+
MsgType: MESSAGE_TYPE_ROLL_FORWARD,
78+
NewState: STATE_IDLE,
79+
},
80+
{
81+
MsgType: MESSAGE_TYPE_ROLL_BACKWARD,
82+
NewState: STATE_IDLE,
83+
},
84+
},
85+
},
86+
STATE_DONE: protocol.StateMapEntry{
87+
Agency: protocol.AGENCY_NONE,
88+
},
89+
}
90+
2591
type ChainSync struct {
2692
proto *protocol.Protocol
2793
nodeToNode bool
@@ -56,9 +122,17 @@ func New(m *muxer.Muxer, errorChan chan error, nodeToNode bool, callbackConfig *
56122
nodeToNode: nodeToNode,
57123
callbackConfig: callbackConfig,
58124
}
59-
c.proto = protocol.New(PROTOCOL_NAME, protocolId, m, errorChan, c.messageHandler, c.NewMsgFromCbor)
60-
// Set initial state
61-
c.proto.SetState(STATE_IDLE)
125+
protoConfig := protocol.ProtocolConfig{
126+
Name: PROTOCOL_NAME,
127+
ProtocolId: protocolId,
128+
Muxer: m,
129+
ErrorChan: errorChan,
130+
MessageHandlerFunc: c.messageHandler,
131+
MessageFromCborFunc: c.NewMsgFromCbor,
132+
StateMap: stateMap,
133+
InitialState: STATE_IDLE,
134+
}
135+
c.proto = protocol.New(protoConfig)
62136
return c
63137
}
64138

@@ -84,45 +158,24 @@ func (c *ChainSync) messageHandler(msg protocol.Message) error {
84158
}
85159

86160
func (c *ChainSync) RequestNext() error {
87-
if err := c.proto.LockState([]protocol.State{STATE_IDLE}); err != nil {
88-
return fmt.Errorf("%s: RequestNext: protocol not in expected state", PROTOCOL_NAME)
89-
}
90-
// Create our request
91161
msg := newMsgRequestNext()
92-
// Unlock and change state when we're done
93-
defer c.proto.UnlockState(STATE_CAN_AWAIT)
94-
// Send request
95162
return c.proto.SendMessage(msg, false)
96163
}
97164

98165
func (c *ChainSync) FindIntersect(points []interface{}) error {
99-
if err := c.proto.LockState([]protocol.State{STATE_IDLE}); err != nil {
100-
return fmt.Errorf("%s: FindIntersect: protocol not in expected state", PROTOCOL_NAME)
101-
}
102166
msg := newMsgFindIntersect(points)
103-
// Unlock and change state when we're done
104-
defer c.proto.UnlockState(STATE_INTERSECT)
105-
// Send request
106167
return c.proto.SendMessage(msg, false)
107168
}
108169

109170
func (c *ChainSync) handleAwaitReply() error {
110-
if err := c.proto.LockState([]protocol.State{STATE_CAN_AWAIT}); err != nil {
111-
return fmt.Errorf("received chain-sync AwaitReply message when protocol not in expected state")
112-
}
113171
if c.callbackConfig.AwaitReplyFunc == nil {
114172
return fmt.Errorf("received chain-sync AwaitReply message but no callback function is defined")
115173
}
116-
// Unlock and change state when we're done
117-
defer c.proto.UnlockState(STATE_MUST_REPLY)
118174
// Call the user callback function
119175
return c.callbackConfig.AwaitReplyFunc()
120176
}
121177

122178
func (c *ChainSync) handleRollForward(msgGeneric protocol.Message) error {
123-
if err := c.proto.LockState([]protocol.State{STATE_CAN_AWAIT, STATE_MUST_REPLY}); err != nil {
124-
return fmt.Errorf("received chain-sync RollForward message when protocol not in expected state")
125-
}
126179
if c.callbackConfig.RollForwardFunc == nil {
127180
return fmt.Errorf("received chain-sync RollForward message but no callback function is defined")
128181
}
@@ -163,8 +216,6 @@ func (c *ChainSync) handleRollForward(msgGeneric protocol.Message) error {
163216
return err
164217
}
165218
}
166-
// Unlock and change state when we're done
167-
defer c.proto.UnlockState(STATE_IDLE)
168219
// Call the user callback function
169220
return c.callbackConfig.RollForwardFunc(blockType, blockHeader)
170221
} else {
@@ -178,64 +229,42 @@ func (c *ChainSync) handleRollForward(msgGeneric protocol.Message) error {
178229
if err != nil {
179230
return err
180231
}
181-
// Unlock and change state when we're done
182-
defer c.proto.UnlockState(STATE_IDLE)
183232
// Call the user callback function
184233
return c.callbackConfig.RollForwardFunc(wrapBlock.Type, blk)
185234
}
186235
}
187236

188237
func (c *ChainSync) handleRollBackward(msgGeneric protocol.Message) error {
189-
if err := c.proto.LockState([]protocol.State{STATE_CAN_AWAIT, STATE_MUST_REPLY}); err != nil {
190-
return fmt.Errorf("received chain-sync RollBackward message when protocol not in expected state")
191-
}
192238
if c.callbackConfig.RollBackwardFunc == nil {
193239
return fmt.Errorf("received chain-sync RollBackward message but no callback function is defined")
194240
}
195241
msg := msgGeneric.(*msgRollBackward)
196-
// Unlock and change state when we're done
197-
defer c.proto.UnlockState(STATE_IDLE)
198242
// Call the user callback function
199243
return c.callbackConfig.RollBackwardFunc(msg.Point, msg.Tip)
200244
}
201245

202246
func (c *ChainSync) handleIntersectFound(msgGeneric protocol.Message) error {
203-
if err := c.proto.LockState([]protocol.State{STATE_INTERSECT}); err != nil {
204-
return fmt.Errorf("received chain-sync IntersectFound message when protocol not in expected state")
205-
}
206247
if c.callbackConfig.IntersectFoundFunc == nil {
207248
return fmt.Errorf("received chain-sync IntersectFound message but no callback function is defined")
208249
}
209250
msg := msgGeneric.(*msgIntersectFound)
210-
// Unlock and change state when we're done
211-
defer c.proto.UnlockState(STATE_IDLE)
212251
// Call the user callback function
213252
return c.callbackConfig.IntersectFoundFunc(msg.Point, msg.Tip)
214253
}
215254

216255
func (c *ChainSync) handleIntersectNotFound(msgGeneric protocol.Message) error {
217-
if err := c.proto.LockState([]protocol.State{STATE_INTERSECT}); err != nil {
218-
return fmt.Errorf("received chain-sync IntersectNotFound message when protocol not in expected state")
219-
}
220256
if c.callbackConfig.IntersectNotFoundFunc == nil {
221257
return fmt.Errorf("received chain-sync IntersectNotFound message but no callback function is defined")
222258
}
223259
msg := msgGeneric.(*msgIntersectNotFound)
224-
// Unlock and change state when we're done
225-
defer c.proto.UnlockState(STATE_IDLE)
226260
// Call the user callback function
227261
return c.callbackConfig.IntersectNotFoundFunc(msg.Tip)
228262
}
229263

230264
func (c *ChainSync) handleDone() error {
231-
if err := c.proto.LockState([]protocol.State{STATE_IDLE}); err != nil {
232-
return fmt.Errorf("received chain-sync Done message when protocol not in expected state")
233-
}
234265
if c.callbackConfig.DoneFunc == nil {
235266
return fmt.Errorf("received chain-sync Done message but no callback function is defined")
236267
}
237-
// Unlock and change state when we're done
238-
defer c.proto.UnlockState(STATE_DONE)
239268
// Call the user callback function
240269
return c.callbackConfig.DoneFunc()
241270
}

0 commit comments

Comments
 (0)