Skip to content

Commit bcd4db4

Browse files
tac0turtletac0turtle
andauthored
test: add more tests to types (#2216)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview This pr aims at only increasing test coverage of the types folder. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved validation when initializing from a genesis document, now preventing the use of an invalid initial height of zero. - **Tests** - Added comprehensive tests for data validation, signature verification, hashing, and state initialization to ensure correctness and robustness of core blockchain data structures and logic. - **Refactor** - Simplified internal validation logic for block data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: tac0turtle <you@example.com>
1 parent 816e34e commit bcd4db4

6 files changed

Lines changed: 429 additions & 8 deletions

File tree

types/block.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ type IntermediateStateRoots struct {
4444
RawRootsList [][]byte
4545
}
4646

47-
// ValidateBasic performs basic validation of block data.
48-
// Actually it's a placeholder, because nothing is checked.
49-
func (d *Data) ValidateBasic() error {
50-
return nil
51-
}
52-
5347
// ValidateBasic performs basic validation of a signature.
5448
func (signature *Signature) ValidateBasic() error {
5549
if len(*signature) == 0 {
@@ -121,8 +115,9 @@ func (d *Data) Verify(untrustedData *Data) error {
121115
}
122116

123117
// Validate performs basic validation of a block.
118+
// this is used to implement the header interface for go header
124119
func (d *Data) Validate() error {
125-
return d.ValidateBasic()
120+
return nil
126121
}
127122

128123
// Size returns size of the block in bytes.

types/block_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package types
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/assert"
8+
"google.golang.org/protobuf/proto"
9+
)
10+
11+
// TestValidateBasic tests the ValidateBasic method of Signature.
12+
func TestValidateBasic(t *testing.T) {
13+
sig := Signature([]byte("signature"))
14+
assert.NoError(t, sig.ValidateBasic())
15+
16+
emptySig := Signature([]byte{})
17+
assert.ErrorIs(t, emptySig.ValidateBasic(), ErrSignatureEmpty)
18+
19+
nilSig := Signature(nil)
20+
assert.ErrorIs(t, nilSig.ValidateBasic(), ErrSignatureEmpty)
21+
}
22+
23+
// TestValidate tests the Validate function for Data against a SignedHeader.
24+
func TestValidate(t *testing.T) {
25+
header, data := GetRandomBlock(1, 10, "testchain")
26+
27+
// Case 1: Valid header and data
28+
t.Run("valid header and data", func(t *testing.T) {
29+
err := Validate(header, data)
30+
assert.NoError(t, err)
31+
})
32+
33+
// Case 2: Mismatched ChainID
34+
t.Run("mismatched chainID", func(t *testing.T) {
35+
invalidData := data.New()
36+
*invalidData = *data
37+
invalidData.Metadata = &Metadata{}
38+
*invalidData.Metadata = *data.Metadata
39+
invalidData.Metadata.ChainID = "wrongchain"
40+
err := Validate(header, invalidData)
41+
assert.Error(t, err)
42+
assert.Contains(t, err.Error(), "header and data do not match")
43+
})
44+
45+
// Case 3: Mismatched Height
46+
t.Run("mismatched height", func(t *testing.T) {
47+
invalidData := data.New()
48+
*invalidData = *data
49+
invalidData.Metadata = &Metadata{}
50+
*invalidData.Metadata = *data.Metadata
51+
invalidData.Metadata.Height = header.Height() + 1
52+
err := Validate(header, invalidData)
53+
assert.Error(t, err)
54+
assert.Contains(t, err.Error(), "header and data do not match")
55+
})
56+
57+
// Case 4: Mismatched Time
58+
t.Run("mismatched time", func(t *testing.T) {
59+
invalidData := data.New()
60+
*invalidData = *data
61+
invalidData.Metadata = &Metadata{}
62+
*invalidData.Metadata = *data.Metadata
63+
invalidData.Metadata.Time = uint64(header.Time().Unix() + 1)
64+
err := Validate(header, invalidData)
65+
assert.Error(t, err)
66+
assert.Contains(t, err.Error(), "header and data do not match")
67+
})
68+
69+
// Case 5: Mismatched DataHash (due to different Txs)
70+
t.Run("mismatched data hash", func(t *testing.T) {
71+
invalidData := data.New()
72+
*invalidData = *data
73+
invalidData.Txs = Txs{Tx("different tx")}
74+
err := Validate(header, invalidData)
75+
assert.Error(t, err)
76+
assert.Contains(t, err.Error(), "dataHash from the header does not match with hash of the block's data")
77+
})
78+
79+
// Case 6: Data with nil Metadata (should still validate DataHash)
80+
t.Run("nil metadata", func(t *testing.T) {
81+
dataWithNilMeta := &Data{
82+
Txs: data.Txs,
83+
}
84+
err := Validate(header, dataWithNilMeta)
85+
assert.NoError(t, err)
86+
87+
// Now test nil metadata with wrong Txs
88+
dataWithNilMetaWrongHash := &Data{
89+
Txs: Txs{Tx("different tx")},
90+
}
91+
err = Validate(header, dataWithNilMetaWrongHash)
92+
assert.Error(t, err)
93+
assert.Contains(t, err.Error(), "dataHash from the header does not match with hash of the block's data")
94+
})
95+
}
96+
97+
// TestDataGetters tests the getter methods on the Data struct.
98+
func TestDataGetters(t *testing.T) {
99+
_, data := GetRandomBlock(5, 3, "getter-test")
100+
101+
assert.Equal(t, "getter-test", data.ChainID())
102+
assert.Equal(t, uint64(5), data.Height())
103+
assert.Equal(t, data.LastDataHash, data.LastHeader())
104+
assert.Equal(t, time.Unix(0, int64(data.Metadata.Time)), data.Time())
105+
106+
nilMetaData := &Data{Txs: data.Txs}
107+
assert.Panics(t, func() { nilMetaData.ChainID() })
108+
assert.Panics(t, func() { nilMetaData.Height() })
109+
assert.Panics(t, func() { nilMetaData.LastHeader() })
110+
assert.Panics(t, func() { nilMetaData.Time() })
111+
}
112+
113+
// TestVerify tests the Verify method for comparing two Data objects.
114+
func TestVerify(t *testing.T) {
115+
_, trustedData := GetRandomBlock(1, 5, "verify-test")
116+
_, untrustedData := GetRandomBlock(2, 5, "verify-test")
117+
118+
trustedDataHash := trustedData.Hash()
119+
untrustedData.LastDataHash = trustedDataHash
120+
121+
// Case 1: Valid verification
122+
t.Run("valid verification", func(t *testing.T) {
123+
err := trustedData.Verify(untrustedData)
124+
assert.NoError(t, err)
125+
})
126+
127+
// Case 2: Invalid verification (wrong LastDataHash)
128+
t.Run("invalid last data hash", func(t *testing.T) {
129+
invalidUntrustedData := untrustedData.New()
130+
*invalidUntrustedData = *untrustedData
131+
invalidUntrustedData.Metadata = &Metadata{}
132+
*invalidUntrustedData.Metadata = *untrustedData.Metadata
133+
invalidUntrustedData.LastDataHash = []byte("clearly wrong hash")
134+
err := trustedData.Verify(invalidUntrustedData)
135+
assert.Error(t, err)
136+
assert.Contains(t, err.Error(), "data hash of the trusted data does not match with last data hash of the untrusted data")
137+
})
138+
139+
// Case 3: Untrusted data is nil
140+
t.Run("nil untrusted data", func(t *testing.T) {
141+
err := trustedData.Verify(nil)
142+
assert.Error(t, err)
143+
assert.Contains(t, err.Error(), "untrusted block cannot be nil")
144+
})
145+
146+
_, data3 := GetRandomBlock(3, 2, "another-chain")
147+
data3.LastDataHash = trustedDataHash
148+
err := trustedData.Verify(data3)
149+
assert.NoError(t, err, "Verify should only compare trusted.Hash() and untrusted.LastDataHash")
150+
}
151+
152+
// TestDataSize tests the Size method of Data.
153+
func TestDataSize(t *testing.T) {
154+
_, data := GetRandomBlock(1, 5, "size-test")
155+
protoData := data.ToProto()
156+
expectedSize := proto.Size(protoData)
157+
assert.Equal(t, expectedSize, data.Size())
158+
assert.True(t, data.Size() > 0)
159+
160+
emptyData := &Data{}
161+
protoEmpty := emptyData.ToProto()
162+
expectedEmptySize := proto.Size(protoEmpty)
163+
assert.Equal(t, expectedEmptySize, emptyData.Size())
164+
}
165+
166+
// TestIsZero tests the IsZero method of Data.
167+
func TestIsZero(t *testing.T) {
168+
var nilData *Data
169+
assert.True(t, nilData.IsZero())
170+
171+
data := &Data{}
172+
assert.False(t, data.IsZero())
173+
174+
_, data = GetRandomBlock(1, 1, "not-zero")
175+
assert.False(t, data.IsZero())
176+
}
177+
178+
// TestDataValidate tests the Validate method of Data returns nil
179+
func TestDataValidate(t *testing.T) {
180+
_, data := GetRandomBlock(1, 5, "validate-test")
181+
assert.NoError(t, data.Validate())
182+
}

types/hashing_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package types
2+
3+
import (
4+
"crypto/sha256"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// TestHeaderHash tests the Hash method of the Header.
12+
func TestHeaderHash(t *testing.T) {
13+
// Create a sample header
14+
header := &Header{
15+
BaseHeader: BaseHeader{
16+
Height: 1,
17+
Time: 1234567890,
18+
},
19+
DataHash: []byte("datahash"),
20+
}
21+
22+
hash1 := header.Hash()
23+
24+
headerBytes, err := header.MarshalBinary()
25+
require.NoError(t, err)
26+
expectedHash := sha256.Sum256(headerBytes)
27+
28+
assert.NotNil(t, hash1)
29+
assert.Len(t, hash1, sha256.Size)
30+
assert.Equal(t, Hash(expectedHash[:]), hash1, "Header hash should match manual calculation")
31+
32+
header.BaseHeader.Height = 2
33+
hash2 := header.Hash()
34+
assert.NotEqual(t, hash1, hash2, "Different headers should have different hashes")
35+
}
36+
37+
// TestDataHash tests the Hash method of the Data.
38+
func TestDataHash(t *testing.T) {
39+
data := &Data{
40+
Txs: Txs{Tx("tx1"), Tx("tx2")},
41+
Metadata: &Metadata{
42+
ChainID: "test-chain",
43+
Height: 1,
44+
Time: 1234567890,
45+
LastDataHash: []byte("lastdatahash"),
46+
},
47+
}
48+
49+
hash1 := data.Hash()
50+
51+
dataBytes, err := data.MarshalBinary()
52+
require.NoError(t, err)
53+
54+
hasher := sha256.New()
55+
hasher.Write(leafPrefix)
56+
hasher.Write(dataBytes)
57+
expectedHash := hasher.Sum(nil)
58+
59+
assert.NotNil(t, hash1)
60+
assert.Len(t, hash1, sha256.Size)
61+
assert.Equal(t, Hash(expectedHash), hash1, "Data hash should match manual calculation with prefix")
62+
63+
data.Txs = Txs{Tx("tx3")}
64+
hash2 := data.Hash()
65+
assert.NotEqual(t, hash1, hash2, "Different data (Txs) should have different hashes")
66+
67+
data.Metadata.Height = 2
68+
hash3 := data.Hash()
69+
assert.NotEqual(t, hash1, hash3, "Different data (Metadata) should have different hashes")
70+
assert.NotEqual(t, hash2, hash3)
71+
}
72+
73+
// TestLeafHashOpt tests the helper function leafHashOpt directly.
74+
func TestLeafHashOpt(t *testing.T) {
75+
data := []byte("some data")
76+
hasher := sha256.New()
77+
78+
hash1 := leafHashOpt(hasher, data)
79+
80+
hasher.Reset()
81+
hasher.Write(leafPrefix)
82+
hasher.Write(data)
83+
expectedHash := hasher.Sum(nil)
84+
85+
assert.Equal(t, expectedHash, hash1)
86+
assert.Len(t, hash1, sha256.Size)
87+
88+
emptyData := []byte{}
89+
hash2 := leafHashOpt(hasher, emptyData)
90+
91+
hasher.Reset()
92+
hasher.Write(leafPrefix)
93+
hasher.Write(emptyData)
94+
expectedHash2 := hasher.Sum(nil)
95+
96+
assert.Equal(t, expectedHash2, hash2)
97+
assert.NotEqual(t, hash1, hash2)
98+
}

0 commit comments

Comments
 (0)