Skip to content

Commit e9419ef

Browse files
committed
[Malleability] Header: add malleability test
1 parent bb21ed4 commit e9419ef

File tree

2 files changed

+37
-30
lines changed

2 files changed

+37
-30
lines changed

model/flow/header.go

+27-15
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,32 @@ type Header struct {
4747
LastViewTC *TimeoutCertificate
4848
}
4949

50+
// Body is a type only used to calculate ID of a Header, by converting
51+
// Timestamp from time.Time to unix time (uint64) and LastViewTC to its identifier LastViewTCID.
52+
type Body struct {
53+
ChainID ChainID
54+
ParentID Identifier
55+
Height uint64
56+
PayloadHash Identifier
57+
Timestamp uint64
58+
View uint64
59+
ParentView uint64
60+
ParentVoterIndices []byte
61+
ParentVoterSigData []byte
62+
ProposerID Identifier
63+
ProposerSigData []byte
64+
LastViewTCID Identifier
65+
}
66+
67+
// ID is implemented for the header Body so that it can be verified
68+
// by the malleability checker.
69+
func (b Body) ID() Identifier {
70+
return MakeID(b)
71+
}
72+
5073
// Body returns the immutable part of the block header.
51-
func (h Header) Body() interface{} {
52-
return struct {
53-
ChainID ChainID
54-
ParentID Identifier
55-
Height uint64
56-
PayloadHash Identifier
57-
Timestamp uint64
58-
View uint64
59-
ParentView uint64
60-
ParentVoterIndices []byte
61-
ParentVoterSigData []byte
62-
ProposerID Identifier
63-
LastViewTCID Identifier
64-
}{
74+
func (h Header) Body() *Body {
75+
return &Body{
6576
ChainID: h.ChainID,
6677
ParentID: h.ParentID,
6778
Height: h.Height,
@@ -72,6 +83,7 @@ func (h Header) Body() interface{} {
7283
ParentVoterIndices: h.ParentVoterIndices,
7384
ParentVoterSigData: h.ParentVoterSigData,
7485
ProposerID: h.ProposerID,
86+
ProposerSigData: h.ProposerSigData,
7587
LastViewTCID: h.LastViewTC.ID(),
7688
}
7789
}
@@ -93,7 +105,7 @@ func (h Header) Fingerprint() []byte {
93105
// ID returns a unique ID to singularly identify the header and its block
94106
// within the flow system.
95107
func (h Header) ID() Identifier {
96-
return MakeID(h)
108+
return MakeID(h.Body())
97109
}
98110

99111
// Checksum returns the checksum of the header.

model/flow/header_test.go

+10-15
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"time"
77

88
"github.com/fxamacker/cbor/v2"
9-
"github.com/onflow/crypto"
109
"github.com/stretchr/testify/assert"
1110
"github.com/stretchr/testify/require"
1211
"github.com/vmihailenco/msgpack/v4"
@@ -35,19 +34,7 @@ func TestHeaderFingerprint(t *testing.T) {
3534
header.LastViewTC = helper.MakeTC()
3635
headerID := header.ID()
3736
data := header.Fingerprint()
38-
var decoded struct {
39-
ChainID flow.ChainID
40-
ParentID flow.Identifier
41-
Height uint64
42-
PayloadHash flow.Identifier
43-
Timestamp uint64
44-
View uint64
45-
ParentView uint64
46-
ParentVoterIndices []byte
47-
ParentVoterSigData crypto.Signature
48-
ProposerID flow.Identifier
49-
LastViewTC interface{}
50-
}
37+
var decoded flow.Body
5138
rlp.NewMarshaler().MustUnmarshal(data, &decoded)
5239
decHeader := &flow.Header{
5340
ChainID: decoded.ChainID,
@@ -60,7 +47,7 @@ func TestHeaderFingerprint(t *testing.T) {
6047
ParentVoterIndices: decoded.ParentVoterIndices,
6148
ParentVoterSigData: decoded.ParentVoterSigData,
6249
ProposerID: decoded.ProposerID,
63-
ProposerSigData: header.ProposerSigData, // since this field is not encoded/decoded, just set it to the original value to pass test
50+
ProposerSigData: decoded.ProposerSigData,
6451
LastViewTC: header.LastViewTC,
6552
}
6653
decodedID := decHeader.ID()
@@ -102,3 +89,11 @@ func TestNonUTCTimestampSameHashAsUTC(t *testing.T) {
10289
checkedID := header.ID()
10390
assert.Equal(t, headerID, checkedID)
10491
}
92+
93+
func TestHeaderMalleability(t *testing.T) {
94+
header := unittest.BlockHeaderFixture()
95+
// Require that LastViewTC (TimeoutCertificate) is not malleable, since its ID is incorporated in Body
96+
unittest.RequireEntityNonMalleable(t, helper.MakeTC())
97+
// Body contains all data from the header, but with the timestamp converted to Unix time, and LastViewTC replaced with its ID
98+
unittest.RequireEntityNonMalleable(t, header.Body())
99+
}

0 commit comments

Comments
 (0)