@@ -28,6 +28,7 @@ import (
28
28
"github.com/celo-org/celo-blockchain/core/types"
29
29
"github.com/celo-org/celo-blockchain/crypto"
30
30
blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"
31
+ "github.com/celo-org/celo-blockchain/log"
31
32
"github.com/celo-org/celo-blockchain/p2p/enode"
32
33
"github.com/celo-org/celo-blockchain/rlp"
33
34
)
@@ -126,6 +127,10 @@ func (v *View) Cmp(y *View) int {
126
127
}
127
128
128
129
// ## RoundChangeCertificate ##############################################################
130
+ // To considerably reduce the bandwidth used by the RoundChangeCertificate type (which often
131
+ // contains repeated Proposal from different RoundChange messages), we break it apart during
132
+ // RLP encoding and then build it back during decoding. Proposals are sent just once, and
133
+ // Messages referencing them will use their Hash instead.
129
134
130
135
type RoundChangeCertificate struct {
131
136
RoundChangeMessages []Message
@@ -135,6 +140,134 @@ func (b *RoundChangeCertificate) IsEmpty() bool {
135
140
return len (b .RoundChangeMessages ) == 0
136
141
}
137
142
143
+ // EncodeRLP serializes b into the Ethereum RLP format.
144
+ func (c * RoundChangeCertificate ) EncodeRLP (w io.Writer ) error {
145
+ proposals , messages , err := c .asValues ()
146
+ if err != nil {
147
+ return err
148
+ }
149
+ log .Debug ("Round change certificate proposals" , "count" , len (proposals ))
150
+ return rlp .Encode (w , []interface {}{proposals , messages })
151
+ }
152
+
153
+ // DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
154
+ func (c * RoundChangeCertificate ) DecodeRLP (s * rlp.Stream ) error {
155
+ var decodestr struct {
156
+ Proposals []* types.Block
157
+ IndexedMessages []IndexedRoundChangeMessage
158
+ }
159
+
160
+ if err := s .Decode (& decodestr ); err != nil {
161
+ return err
162
+ }
163
+ return c .setValues (decodestr .Proposals , decodestr .IndexedMessages )
164
+ }
165
+
166
+ // setValues recreates the RoundChange messages from the props (Proposal set/index) and the
167
+ // list of IndexedRoundChangeMessage, which is supposed to be the same as the RoundChange
168
+ // Messages but with the proposals just referenced to the Proposals set.
169
+ func (c * RoundChangeCertificate ) setValues (props []* types.Block , iMess []IndexedRoundChangeMessage ) error {
170
+ // create a Proposal index from the list
171
+ propIndex := make (map [common.Hash ]Proposal )
172
+ for _ , prop := range props {
173
+ propIndex [prop .Hash ()] = prop
174
+ }
175
+ // Recreate Messages one by one
176
+ mess := make ([]Message , len (iMess ))
177
+ for i , im := range iMess {
178
+ mess [i ] = Message {
179
+ Code : im .Message .Code ,
180
+ Address : im .Message .Address ,
181
+ Signature : im .Message .Signature ,
182
+ }
183
+
184
+ // Add the proposal to the message if it had one
185
+ roundChange , err := im .Message .TryRoundChange ()
186
+ if err != nil {
187
+ return err
188
+ }
189
+
190
+ if proposal , ok := propIndex [im .ProposalHash ]; ok {
191
+ roundChange .PreparedCertificate .Proposal = proposal
192
+ }
193
+
194
+ setMessageBytes (& mess [i ], roundChange )
195
+ mess [i ].roundChange = roundChange
196
+ }
197
+ c .RoundChangeMessages = mess
198
+ return nil
199
+ }
200
+
201
+ type IndexedRoundChangeMessage struct {
202
+ ProposalHash common.Hash
203
+ Message Message // PreparedCertificate.Proposal = nil if any
204
+ }
205
+
206
+ // asValues presents the RoundChangeCertificate as values for RLP Serialization.
207
+ // This is done using a list of proposals, and the RoundChange messages using
208
+ // hash references instead of the full proposal objects, to reduce bandwidth.
209
+ func (c * RoundChangeCertificate ) asValues () ([]* types.Block , []* IndexedRoundChangeMessage , error ) {
210
+ var err error
211
+
212
+ messages := make ([]* IndexedRoundChangeMessage , len (c .RoundChangeMessages ))
213
+ proposalsMap := make (map [common.Hash ]* types.Block )
214
+
215
+ for i , message := range c .RoundChangeMessages {
216
+ var proposal * types.Block
217
+ proposal , messages [i ], err = extractProposal (& message )
218
+ if err != nil {
219
+ return nil , nil , err
220
+ }
221
+
222
+ if proposal != nil {
223
+ // we don't use the height since we know they MUST be the same
224
+ proposalsMap [proposal .Hash ()] = proposal
225
+ }
226
+ }
227
+
228
+ // Iterate values. RLP does not support maps
229
+ proposals := make ([]* types.Block , len (proposalsMap ))
230
+ var i = 0
231
+ for _ , p := range proposalsMap {
232
+ proposals [i ] = p
233
+ i ++
234
+ }
235
+ return proposals , messages , nil
236
+ }
237
+
238
+ func extractProposal (message * Message ) (* types.Block , * IndexedRoundChangeMessage , error ) {
239
+ roundChange , err := message .TryRoundChange ()
240
+ if err != nil {
241
+ return nil , nil , err
242
+ }
243
+
244
+ pc := roundChange .PreparedCertificate
245
+
246
+ // Assume message.Code = MsgRoundChange
247
+ indexedMsg := IndexedRoundChangeMessage {
248
+ Message : Message {
249
+ Code : message .Code ,
250
+ Address : message .Address ,
251
+ Signature : message .Signature ,
252
+ },
253
+ }
254
+
255
+ if pc .Proposal != nil {
256
+ indexedMsg .ProposalHash = pc .Proposal .Hash ()
257
+ }
258
+
259
+ curatedPC := EmptyPreparedCertificate ()
260
+ curatedPC .PrepareOrCommitMessages = pc .PrepareOrCommitMessages
261
+
262
+ setMessageBytes (& indexedMsg .Message ,
263
+ & RoundChange {
264
+ View : roundChange .View ,
265
+ PreparedCertificate : curatedPC ,
266
+ })
267
+
268
+ return pc .Proposal .(* types.Block ), & indexedMsg , nil
269
+ }
270
+
138
271
// ## Preprepare ##############################################################
139
272
140
273
// NewPreprepareMessage constructs a Message instance with the given sender and
@@ -522,7 +655,7 @@ type Message struct {
522
655
func setMessageBytes (msg * Message , innerMessage interface {}) {
523
656
bytes , err := rlp .EncodeToBytes (innerMessage )
524
657
if err != nil {
525
- panic (fmt .Sprintf ("attempt to serialise inner message of type %T failed" , innerMessage ))
658
+ panic (fmt .Sprintf ("attempt to serialise inner message of type %T failed. %s " , innerMessage , err ))
526
659
}
527
660
msg .Msg = bytes
528
661
}
@@ -537,20 +670,8 @@ func (m *Message) Sign(signingFn func(data []byte) ([]byte, error)) error {
537
670
return err
538
671
}
539
672
540
- func (m * Message ) DecodeRLP (stream * rlp.Stream ) error {
541
- type decodable Message
542
- var d decodable
543
- err := stream .Decode (& d )
544
- if err != nil {
545
- return err
546
- }
547
- * m = Message (d )
548
-
549
- if len (m .Msg ) == 0 && len (m .Signature ) == 0 {
550
- // Empty validator handshake message
551
- return nil
552
- }
553
-
673
+ func (m * Message ) DecodeMessage () error {
674
+ var err error
554
675
switch m .Code {
555
676
case MsgPreprepare :
556
677
var p * Preprepare
@@ -598,7 +719,23 @@ func (m *Message) DecodeRLP(stream *rlp.Stream) error {
598
719
err = fmt .Errorf ("unrecognised message code %d" , m .Code )
599
720
}
600
721
return err
722
+ }
723
+
724
+ func (m * Message ) DecodeRLP (stream * rlp.Stream ) error {
725
+ type decodable Message
726
+ var d decodable
727
+ err := stream .Decode (& d )
728
+ if err != nil {
729
+ return err
730
+ }
731
+ * m = Message (d )
732
+
733
+ if len (m .Msg ) == 0 && len (m .Signature ) == 0 {
734
+ // Empty validator handshake message
735
+ return nil
736
+ }
601
737
738
+ return m .DecodeMessage ()
602
739
}
603
740
604
741
// FromPayload decodes b into a Message instance it will set one of the private
@@ -666,6 +803,20 @@ func (m *Message) Prepare() *Subject {
666
803
return m .prepare
667
804
}
668
805
806
+ // Prepare returns round change if this is a round change message.
807
+ func (m * Message ) TryRoundChange () (* RoundChange , error ) {
808
+ if m .roundChange != nil {
809
+ return m .roundChange , nil
810
+ }
811
+ if m .Code != MsgRoundChange {
812
+ return nil , fmt .Errorf ("expected round change message, received code: %d" , m .Code )
813
+ }
814
+ if err := m .DecodeMessage (); err != nil {
815
+ return nil , err
816
+ }
817
+ return m .roundChange , nil
818
+ }
819
+
669
820
// Prepare returns round change if this is a round change message.
670
821
func (m * Message ) RoundChange () * RoundChange {
671
822
return m .roundChange
0 commit comments