Skip to content

Commit 0cf9d51

Browse files
authored
add config request to control event (#305)
* add config request to control event Signed-off-by: Natalie Morad <natalie.morad@ibm.com> * fix review Signed-off-by: Natalie Morad <natalie.morad@ibm.com> --------- Signed-off-by: Natalie Morad <natalie.morad@ibm.com>
1 parent 5f0b170 commit 0cf9d51

File tree

5 files changed

+109
-21
lines changed

5 files changed

+109
-21
lines changed

node/consensus/consensus.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type SigVerifier interface {
5353
}
5454

5555
type Arma interface {
56-
SimulateStateTransition(prevState *state.State, events [][]byte) (*state.State, [][]arma_types.BatchAttestationFragment)
56+
SimulateStateTransition(prevState *state.State, events [][]byte) (*state.State, [][]arma_types.BatchAttestationFragment, *state.ConfigRequest)
5757
Commit(events [][]byte)
5858
}
5959

@@ -198,7 +198,7 @@ func (c *Consensus) VerifyProposal(proposal smartbft_types.Proposal) ([]smartbft
198198
}
199199

200200
c.stateLock.Lock()
201-
computedState, attestations := c.Arma.SimulateStateTransition(c.State, batch)
201+
computedState, attestations, _ := c.Arma.SimulateStateTransition(c.State, batch)
202202
c.stateLock.Unlock()
203203

204204
availableBlocks := make([]state.AvailableBlock, len(attestations))
@@ -413,7 +413,7 @@ func (c *Consensus) SignProposal(proposal smartbft_types.Proposal, _ []byte) *sm
413413
}
414414

415415
c.stateLock.Lock()
416-
_, bafs := c.Arma.SimulateStateTransition(c.State, requests)
416+
_, bafs, _ := c.Arma.SimulateStateTransition(c.State, requests)
417417
c.stateLock.Unlock()
418418

419419
sigs := make([][]byte, 0, len(bafs)+1)
@@ -451,7 +451,8 @@ func (c *Consensus) SignProposal(proposal smartbft_types.Proposal, _ []byte) *sm
451451
// (from SmartBFT API)
452452
func (c *Consensus) AssembleProposal(metadata []byte, requests [][]byte) smartbft_types.Proposal {
453453
c.stateLock.Lock()
454-
newState, attestations := c.Arma.SimulateStateTransition(c.State, requests)
454+
// TODO: config request not handled yet
455+
newState, attestations, _ := c.Arma.SimulateStateTransition(c.State, requests)
455456
c.stateLock.Unlock()
456457

457458
lastCommonBlockHeader := &common.BlockHeader{}

node/consensus/consenter.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type Consenter struct {
2929
State *state.State
3030
}
3131

32-
func (c *Consenter) SimulateStateTransition(prevState *state.State, requests [][]byte) (*state.State, [][]types.BatchAttestationFragment) {
32+
func (c *Consenter) SimulateStateTransition(prevState *state.State, requests [][]byte) (*state.State, [][]types.BatchAttestationFragment, *state.ConfigRequest) {
3333
controlEvents, err := requestsToControlEvents(requests, c.BAFDeserializer.Deserialize)
3434
if err != nil {
3535
panic(err)
@@ -48,17 +48,17 @@ func (c *Consenter) SimulateStateTransition(prevState *state.State, requests [][
4848
filteredControlEvents = append(filteredControlEvents, ce)
4949
}
5050

51-
newState, fragments := prevState.Process(c.Logger, filteredControlEvents...)
51+
newState, fragments, configRequest := prevState.Process(c.Logger, filteredControlEvents...)
5252
batchAttestations := aggregateFragments(fragments)
5353

54-
return newState, batchAttestations
54+
return newState, batchAttestations, configRequest
5555
}
5656

5757
// Commit indexes BAs and updates the state.
5858
// Note that this must hold: Commit(controlEvents) with the same controlEvents is idempotent.
5959
// TODO revise the recovery from failure or shutdown, specifically the order of Commit and Append.
6060
func (c *Consenter) Commit(events [][]byte) {
61-
state, batchAttestations := c.SimulateStateTransition(c.State, events)
61+
state, batchAttestations, _ := c.SimulateStateTransition(c.State, events)
6262
if len(batchAttestations) > 0 {
6363
c.indexAttestationsInDB(batchAttestations)
6464
}

node/consensus/consenter_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package consensus_test
99
import (
1010
"testing"
1111

12+
"github.com/hyperledger/fabric-protos-go-apiv2/common"
1213
arma_types "github.com/hyperledger/fabric-x-orderer/common/types"
1314
"github.com/hyperledger/fabric-x-orderer/node/consensus"
1415
"github.com/hyperledger/fabric-x-orderer/node/consensus/mocks"
@@ -39,7 +40,7 @@ func TestConsenter(t *testing.T) {
3940

4041
// Test with an event that should be filtered out
4142
db.ExistsReturns(true)
42-
newState, batchAttestations := consenter.SimulateStateTransition(s, events)
43+
newState, batchAttestations, _ := consenter.SimulateStateTransition(s, events)
4344
assert.Empty(t, batchAttestations)
4445
assert.Empty(t, newState.Pending)
4546

@@ -49,7 +50,7 @@ func TestConsenter(t *testing.T) {
4950

5051
// Test a valid event below threshold
5152
db.ExistsReturns(false)
52-
newState, batchAttestations = consenter.SimulateStateTransition(s, events)
53+
newState, batchAttestations, _ = consenter.SimulateStateTransition(s, events)
5354
assert.Empty(t, batchAttestations)
5455
assert.Len(t, newState.Pending, 1)
5556

@@ -62,7 +63,7 @@ func TestConsenter(t *testing.T) {
6263
ba2.SetSignature([]byte{1})
6364
events = append(events, (&state.ControlEvent{BAF: ba2}).Bytes())
6465

65-
newState, batchAttestations = consenter.SimulateStateTransition(s, events)
66+
newState, batchAttestations, _ = consenter.SimulateStateTransition(s, events)
6667
assert.Len(t, batchAttestations[0], 2)
6768
assert.Empty(t, newState.Pending)
6869

@@ -82,6 +83,19 @@ func TestConsenter(t *testing.T) {
8283
consenter.Commit(events)
8384
assert.Equal(t, db.PutCallCount(), 1)
8485
assert.Len(t, consenter.State.Complaints, 1)
86+
87+
// Test ConfigRequest is returned by SimulateStateTransition
88+
cr := &state.ConfigRequest{
89+
Envelope: &common.Envelope{
90+
Payload: []byte("config-payload"),
91+
Signature: []byte("config-signature"),
92+
},
93+
}
94+
events = [][]byte{(&state.ControlEvent{ConfigRequest: cr}).Bytes()}
95+
96+
_, _, configRequest := consenter.SimulateStateTransition(s, events)
97+
assert.Equal(t, cr.Envelope.Payload, configRequest.Envelope.Payload)
98+
assert.Equal(t, cr.Envelope.Signature, configRequest.Envelope.Signature)
8599
}
86100

87101
func createConsenter(s *state.State, logger arma_types.Logger) *consensus.Consenter {

node/consensus/state/state.go

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import (
1616
"math"
1717
"slices"
1818

19+
"github.com/hyperledger/fabric-protos-go-apiv2/common"
1920
"github.com/hyperledger/fabric-x-orderer/common/types"
21+
"github.com/hyperledger/fabric/protoutil"
2022
)
2123

2224
type Rule func(*State, types.Logger, ...ControlEvent)
@@ -302,16 +304,47 @@ func (c *Complaint) String() string {
302304
return fmt.Sprintf("Complaint: Signer: %d; Shard: %d; Term %d; Reason: %s", c.Signer, c.Shard, c.Term, c.Reason)
303305
}
304306

307+
type ConfigRequest struct {
308+
Envelope *common.Envelope
309+
}
310+
311+
func (c *ConfigRequest) Bytes() []byte {
312+
bytes, err := protoutil.Marshal(c.Envelope)
313+
if err != nil {
314+
panic(fmt.Sprintf("failed to marshal envelope: %v", err))
315+
}
316+
317+
return bytes
318+
}
319+
320+
func (c *ConfigRequest) FromBytes(bytes []byte) error {
321+
envelope, err := protoutil.UnmarshalEnvelope(bytes)
322+
if err != nil {
323+
return fmt.Errorf("failed to unmarshal envelope: %v", err)
324+
}
325+
326+
c.Envelope = envelope
327+
return nil
328+
}
329+
330+
func (c *ConfigRequest) String() string {
331+
// TODO: add more info to this string
332+
return "Config Request"
333+
}
334+
305335
type ControlEvent struct {
306-
BAF types.BatchAttestationFragment
307-
Complaint *Complaint
336+
BAF types.BatchAttestationFragment
337+
Complaint *Complaint
338+
ConfigRequest *ConfigRequest
308339
}
309340

310341
func (ce *ControlEvent) String() string {
311342
if ce.Complaint != nil {
312343
return ce.Complaint.String()
313344
} else if ce.BAF != nil {
314345
return ce.BAF.String()
346+
} else if ce.ConfigRequest != nil {
347+
return ce.ConfigRequest.String()
315348
}
316349
return "empty control event"
317350
}
@@ -330,6 +363,10 @@ func (ce *ControlEvent) ID() string {
330363
binary.BigEndian.PutUint16(payloadToHash[18:], uint16(ce.BAF.Primary()))
331364
binary.BigEndian.PutUint16(payloadToHash[20:], uint16(ce.BAF.Shard()))
332365
copy(payloadToHash[22:], ce.BAF.Digest())
366+
} else if ce.ConfigRequest != nil {
367+
// TODO: maybe use a different ID for ConfigRequest
368+
ce.ConfigRequest.Envelope.Signature = nil
369+
payloadToHash = ce.ConfigRequest.Bytes()
333370
} else {
334371
return string("")
335372
}
@@ -344,6 +381,7 @@ func (ce *ControlEvent) SignerID() string {
344381
signerID = fmt.Sprintf("%d", ce.Complaint.Signer)
345382
} else if ce.BAF != nil {
346383
signerID = fmt.Sprintf("%d", ce.BAF.Signer())
384+
// TODO: add ConfigRequest SignerID
347385
} else {
348386
return string("")
349387
}
@@ -363,6 +401,11 @@ func (ce *ControlEvent) Bytes() []byte {
363401
bytes = make([]byte, len(rawComplaint)+1)
364402
bytes[0] = 2
365403
copy(bytes[1:], rawComplaint)
404+
case ce.ConfigRequest != nil:
405+
rawConfig := ce.ConfigRequest.Bytes()
406+
bytes = make([]byte, len(rawConfig)+1)
407+
bytes[0] = 3
408+
copy(bytes[1:], rawConfig)
366409
default:
367410
panic("empty control event")
368411
}
@@ -379,12 +422,15 @@ func (ce *ControlEvent) FromBytes(bytes []byte, fragmentFromBytes func([]byte) (
379422
case 2:
380423
ce.Complaint = &Complaint{}
381424
return ce.Complaint.FromBytes(bytes[1:])
425+
case 3:
426+
ce.ConfigRequest = &ConfigRequest{}
427+
return ce.ConfigRequest.FromBytes(bytes[1:])
382428
}
383429

384430
return fmt.Errorf("unknown prefix (%d)", bytes[0])
385431
}
386432

387-
func (s *State) Process(l types.Logger, ces ...ControlEvent) (*State, []types.BatchAttestationFragment) {
433+
func (s *State) Process(l types.Logger, ces ...ControlEvent) (*State, []types.BatchAttestationFragment, *ConfigRequest) {
388434
s2 := s.Clone()
389435

390436
for _, rule := range Rules {
@@ -393,8 +439,8 @@ func (s *State) Process(l types.Logger, ces ...ControlEvent) (*State, []types.Ba
393439

394440
// After applying rules, extract all batch attestations for which enough fragments have been collected.
395441
extracted := ExtractBatchAttestationsFromPending(s2, l)
396-
397-
return s2, extracted
442+
configRequests := ExtractConfigRequest(ces)
443+
return s2, extracted, configRequests
398444
}
399445

400446
func (s *State) Clone() *State {
@@ -658,3 +704,13 @@ func shardExists(shard types.ShardID, shardTerms []ShardTerm) (int, bool) {
658704
}
659705
return -1, false
660706
}
707+
708+
func ExtractConfigRequest(ces []ControlEvent) *ConfigRequest {
709+
// TODO: decide how to handle multiple config requests
710+
for _, ce := range ces {
711+
if ce.ConfigRequest != nil {
712+
return ce.ConfigRequest
713+
}
714+
}
715+
return nil
716+
}

node/consensus/state/state_test.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"math"
1111
"testing"
1212

13+
"github.com/hyperledger/fabric-protos-go-apiv2/common"
1314
"github.com/hyperledger/fabric-x-orderer/common/types"
1415
consensus_state "github.com/hyperledger/fabric-x-orderer/node/consensus/state"
1516
"github.com/hyperledger/fabric-x-orderer/testutil"
@@ -94,7 +95,7 @@ func TestComplaintSerialization(t *testing.T) {
9495

9596
func TestControlEventSerialization(t *testing.T) {
9697
// Serialization and deserialization of ControlEvent with Complaint
97-
ce := consensus_state.ControlEvent{nil, &complaint}
98+
ce := consensus_state.ControlEvent{Complaint: &complaint}
9899

99100
var ce2 consensus_state.ControlEvent
100101

@@ -115,12 +116,28 @@ func TestControlEventSerialization(t *testing.T) {
115116
assert.NoError(t, err)
116117

117118
assert.Equal(t, ce, ce2)
119+
120+
// Serialization and deserialization of ControlEvent with ConfigRequest
121+
cr := &consensus_state.ConfigRequest{
122+
Envelope: &common.Envelope{
123+
Payload: []byte("config-payload"),
124+
Signature: []byte("config-signature"),
125+
},
126+
}
127+
ce = consensus_state.ControlEvent{ConfigRequest: cr}
128+
129+
var ce3 consensus_state.ControlEvent
130+
err = ce3.FromBytes(ce.Bytes(), bafd.Deserialize)
131+
assert.NoError(t, err)
132+
assert.NotNil(t, ce3.ConfigRequest)
133+
assert.Equal(t, cr.Envelope.Payload, ce3.ConfigRequest.Envelope.Payload)
134+
assert.Equal(t, cr.Envelope.Signature, ce3.ConfigRequest.Envelope.Signature)
118135
}
119136

120137
func TestCollectAndDeduplicateEvents(t *testing.T) {
121138
state := initialState
122-
ce := consensus_state.ControlEvent{nil, &complaint}
123-
ce2 := consensus_state.ControlEvent{nil, &complaint}
139+
ce := consensus_state.ControlEvent{Complaint: &complaint}
140+
ce2 := consensus_state.ControlEvent{Complaint: &complaint}
124141
logger := testutil.CreateLogger(t, 0)
125142

126143
// Add a valid Complaint and ensure no duplicates are accepted in the same round
@@ -150,7 +167,7 @@ func TestCollectAndDeduplicateEvents(t *testing.T) {
150167
Signature: []byte{4},
151168
}
152169

153-
ce = consensus_state.ControlEvent{nil, &c}
170+
ce = consensus_state.ControlEvent{Complaint: &c}
154171
consensus_state.CollectAndDeduplicateEvents(&state, logger, ce)
155172
assert.Equal(t, state, expectedState)
156173

@@ -164,7 +181,7 @@ func TestCollectAndDeduplicateEvents(t *testing.T) {
164181
Signature: []byte{4},
165182
}
166183

167-
ce = consensus_state.ControlEvent{nil, &c}
184+
ce = consensus_state.ControlEvent{Complaint: &c}
168185
consensus_state.CollectAndDeduplicateEvents(&state, logger, ce)
169186
assert.Equal(t, state, expectedState)
170187

0 commit comments

Comments
 (0)