Skip to content

Commit 25aed33

Browse files
committed
test: cover FromSignedPayload across type/length branches
1 parent 1930849 commit 25aed33

3 files changed

Lines changed: 199 additions & 14 deletions

File tree

Lines changed: 186 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,206 @@
11
package finalizer
22

33
import (
4+
"bytes"
45
"encoding/binary"
56
"testing"
67

8+
"github.com/ethereum/go-ethereum/crypto"
79
"github.com/stretchr/testify/require"
10+
11+
"github.com/flare-foundation/flare-system-client/client/protocol"
12+
"github.com/flare-foundation/go-flare-common/pkg/payload"
813
)
914

10-
func TestFromSignedPayloadEmpty(t *testing.T) {
11-
cases := []struct {
15+
const (
16+
type0PayloadLen = 1 + 38 + 65 // typeID + message + signature
17+
type1PayloadLen = 1 + 65 // typeID + signature
18+
)
19+
20+
func TestFromSignedPayload(t *testing.T) {
21+
message := bytes.Repeat([]byte{0xAB}, 38)
22+
signature := bytes.Repeat([]byte{0xCD}, 65)
23+
24+
type0Payload := append(append([]byte{0x00}, message...), signature...)
25+
type1Payload := append([]byte{0x01}, signature...)
26+
27+
tests := []struct {
1228
name string
13-
payload []byte
29+
msg payloadMessage
30+
wantErr bool
31+
check func(t *testing.T, s *submitSignaturesPayload)
1432
}{
15-
{"nil", nil},
16-
{"empty", []byte{}},
33+
{
34+
name: "nil payload",
35+
msg: payloadMessage{payload: nil},
36+
wantErr: true,
37+
},
38+
{
39+
name: "empty payload",
40+
msg: payloadMessage{payload: []byte{}},
41+
wantErr: true,
42+
},
43+
{
44+
name: "type 0 only type byte",
45+
msg: payloadMessage{payload: []byte{0x00}},
46+
wantErr: true,
47+
},
48+
{
49+
name: "type 0 one byte short",
50+
msg: payloadMessage{payload: type0Payload[:type0PayloadLen-1]},
51+
wantErr: true,
52+
},
53+
{
54+
name: "type 1 one byte short",
55+
msg: payloadMessage{payload: type1Payload[:type1PayloadLen-1]},
56+
wantErr: true,
57+
},
58+
{
59+
name: "invalid typeID 2",
60+
msg: payloadMessage{payload: append([]byte{0x02}, signature...)},
61+
wantErr: true,
62+
},
63+
{
64+
name: "invalid typeID 0xFF",
65+
msg: payloadMessage{payload: append([]byte{0xFF}, signature...)},
66+
wantErr: true,
67+
},
68+
{
69+
name: "type 0 exact length",
70+
msg: payloadMessage{
71+
protocolID: 7,
72+
votingRoundID: 42,
73+
payload: type0Payload,
74+
},
75+
check: func(t *testing.T, s *submitSignaturesPayload) {
76+
t.Helper()
77+
require.Equal(t, uint8(7), s.protocolID)
78+
require.Equal(t, uint32(42), s.votingRoundID)
79+
require.Equal(t, uint8(0), s.typeID)
80+
require.Equal(t, message, []byte(s.message))
81+
require.Equal(t, signature, s.signature)
82+
require.Equal(t, -1, s.voterIndex)
83+
},
84+
},
85+
{
86+
name: "type 1 exact length",
87+
msg: payloadMessage{
88+
protocolID: 9,
89+
votingRoundID: 1234,
90+
payload: type1Payload,
91+
},
92+
check: func(t *testing.T, s *submitSignaturesPayload) {
93+
t.Helper()
94+
require.Equal(t, uint8(9), s.protocolID)
95+
require.Equal(t, uint32(1234), s.votingRoundID)
96+
require.Equal(t, uint8(1), s.typeID)
97+
require.Nil(t, s.message)
98+
require.Equal(t, signature, s.signature)
99+
require.Equal(t, -1, s.voterIndex)
100+
},
101+
},
102+
{
103+
name: "type 0 with trailing bytes",
104+
msg: payloadMessage{
105+
protocolID: 3,
106+
votingRoundID: 99,
107+
payload: append(append([]byte{}, type0Payload...), 0xAA, 0xBB, 0xCC),
108+
},
109+
check: func(t *testing.T, s *submitSignaturesPayload) {
110+
t.Helper()
111+
require.Equal(t, message, []byte(s.message))
112+
require.Equal(t, signature, s.signature)
113+
require.Len(t, s.signature, 65)
114+
require.Len(t, s.message, 38)
115+
},
116+
},
117+
{
118+
name: "type 1 with trailing bytes",
119+
msg: payloadMessage{
120+
protocolID: 3,
121+
votingRoundID: 99,
122+
payload: append(append([]byte{}, type1Payload...), 0xAA, 0xBB),
123+
},
124+
check: func(t *testing.T, s *submitSignaturesPayload) {
125+
t.Helper()
126+
require.Nil(t, s.message)
127+
require.Equal(t, signature, s.signature)
128+
require.Len(t, s.signature, 65)
129+
},
130+
},
17131
}
18-
for _, tc := range cases {
132+
133+
for _, tc := range tests {
19134
t.Run(tc.name, func(t *testing.T) {
20135
var s submitSignaturesPayload
21-
err := s.FromSignedPayload(payloadMessage{payload: tc.payload})
22-
require.Error(t, err)
136+
err := s.FromSignedPayload(tc.msg)
137+
if tc.wantErr {
138+
require.Error(t, err)
139+
return
140+
}
141+
require.NoError(t, err)
142+
tc.check(t, &s)
23143
})
24144
}
25145
}
26146

27-
func TestExtractPayloadsZeroLengthDoesNotPanic(t *testing.T) {
147+
// TestRoundTripWithEncodePayload generates a tx-style input with protocol.EncodePayload
148+
// for both protocol types, parses it back through ExtractPayloads and FromSignedPayload,
149+
// and confirms the round trip preserves protocolID, votingRoundID, typeID, and message.
150+
func TestRoundTripWithEncodePayload(t *testing.T) {
151+
privateKey, err := crypto.HexToECDSA(testPrivateKeyHex)
152+
require.NoError(t, err)
153+
154+
const votingRound int64 = 1234
155+
156+
type0Message := bytes.Repeat([]byte{0x11}, 38)
157+
type1Message := bytes.Repeat([]byte{0x22}, 16)
158+
159+
cases := []struct {
160+
protocolID uint8
161+
protocolType uint8
162+
data []byte
163+
}{
164+
{protocolID: 1, protocolType: 0, data: type0Message},
165+
{protocolID: 5, protocolType: 1, data: type1Message},
166+
}
167+
168+
buf := new(bytes.Buffer)
169+
buf.Write([]byte{0xde, 0xad, 0xbe, 0xef}) // function selector
170+
171+
for _, c := range cases {
172+
resp := &protocol.SubProtocolResponse{
173+
Status: payload.Ok,
174+
Data: c.data,
175+
}
176+
err := protocol.EncodePayload(buf, votingRound, resp, c.protocolID, c.protocolType, privateKey)
177+
require.NoError(t, err)
178+
}
179+
180+
payloads, err := ExtractPayloads(buf.Bytes())
181+
require.NoError(t, err)
182+
require.Len(t, payloads, len(cases))
183+
184+
for i, c := range cases {
185+
var s submitSignaturesPayload
186+
require.NoError(t, s.FromSignedPayload(payloads[i]))
187+
require.Equal(t, c.protocolID, s.protocolID)
188+
require.Equal(t, uint32(votingRound), s.votingRoundID)
189+
require.Equal(t, c.protocolType, s.typeID)
190+
require.Len(t, s.signature, 65)
191+
require.Equal(t, -1, s.voterIndex)
192+
if c.protocolType == 0 {
193+
require.Equal(t, c.data, []byte(s.message))
194+
} else {
195+
require.Nil(t, s.message)
196+
}
197+
}
198+
}
199+
200+
// TestExtractPayloadsZeroLength confirms ExtractPayloads accepts a header with
201+
// length=0 and that FromSignedPayload rejects the resulting empty payload
202+
// rather than panicking.
203+
func TestExtractPayloadsZeroLength(t *testing.T) {
28204
// 4-byte selector + 1-byte protocol + 4-byte votingRound + 2-byte length=0
29205
data := make([]byte, 4+1+4+2)
30206
binary.BigEndian.PutUint32(data[5:9], 1)
@@ -35,6 +211,5 @@ func TestExtractPayloadsZeroLengthDoesNotPanic(t *testing.T) {
35211
require.Len(t, payloads, 1)
36212

37213
var s submitSignaturesPayload
38-
err = s.FromSignedPayload(payloads[0])
39-
require.Error(t, err)
214+
require.Error(t, s.FromSignedPayload(payloads[0]))
40215
}

client/finalizer/submission_processor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (c *client) ProcessTransaction(tx database.Transaction) error {
2929
return nil
3030
}
3131

32-
signaturePayloads := []*submitSignaturesPayload{}
32+
signaturePayloads := make([]*submitSignaturesPayload, 0, len(payloads))
3333
for i := range payloads {
3434
signaturePayload := new(submitSignaturesPayload)
3535
err := signaturePayload.FromSignedPayload(payloads[i])

client/protocol/submitter.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,21 @@ func newSignatureSubmitter(
257257
}
258258
}
259259

260-
// WritePayload encodes payload to buffer.
260+
// WritePayload encodes payload to buffer using the submitter's signing key.
261261
// Payload data should be valid (data length 38, additional data length <= maxuint16 - 66).
262262
// If an error is returned, the buffer is unchanged.
263263
func (s *SignatureSubmitter) WritePayload(
264264
buffer *bytes.Buffer, epoch int64, data *SubProtocolResponse, protocolID, protocolType uint8,
265+
) error {
266+
return EncodePayload(buffer, epoch, data, protocolID, protocolType, s.protocolContext.signerPrivateKey)
267+
}
268+
269+
// EncodePayload encodes a signed submitSignatures payload to buffer.
270+
// Payload data should be valid (data length 38, additional data length <= maxuint16 - 66).
271+
// If an error is returned, the buffer is unchanged.
272+
func EncodePayload(
273+
buffer *bytes.Buffer, epoch int64, data *SubProtocolResponse, protocolID, protocolType uint8,
274+
signerPrivateKey *ecdsa.PrivateKey,
265275
) error {
266276
var dataLength int
267277
switch protocolType {
@@ -274,7 +284,7 @@ func (s *SignatureSubmitter) WritePayload(
274284
}
275285

276286
dataHash := accounts.TextHash(crypto.Keccak256(data.Data))
277-
signature, err := crypto.Sign(dataHash, s.protocolContext.signerPrivateKey)
287+
signature, err := crypto.Sign(dataHash, signerPrivateKey)
278288
if err != nil {
279289
return errors.Wrap(err, "error signing submitSignatures data")
280290
}

0 commit comments

Comments
 (0)