Skip to content

Commit ad5cb34

Browse files
committed
fix: verificationState not included in unmarshal size check
- include all fields in size check - add unit test to catch regressions in this area - move constant near others so divergences are easier to track - move AddressLength constant to where Address is defined
1 parent b2bb2f4 commit ad5cb34

3 files changed

Lines changed: 35 additions & 21 deletions

File tree

node/pkg/common/chainlock.go

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import (
1717
)
1818

1919
const (
20-
// AddressLength is the length of a normalized Wormhole address in bytes.
21-
AddressLength = 32
22-
2320
// Wormhole supports arbitrary payloads due to the variance in transaction and block sizes between chains.
2421
// However, during serialization, payload lengths are limited by Go slice length constraints and violations
2522
// of these limits can cause panics.
@@ -54,6 +51,20 @@ const (
5451
// minMsgIdLen is the minimum length of a message ID. It is used to uniquely identify
5552
// messages in the case of a duplicate message ID and is stored in the database.
5653
MinMsgIdLen = len("1/0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16/0")
54+
55+
// fixedFieldsLen is the minimum length of the fixed portion of a message publication.
56+
// It is the sum of the sizes of each of the fields plus length information for the Payload.
57+
// This is used to check that the data is long enough for the rest of the message after reading the TxID.
58+
fixedFieldsLen = 8 + // Timestamp (int64)
59+
4 + // Nonce (uint32)
60+
8 + // Sequence (uint64)
61+
1 + // ConsistencyLevel (uint8)
62+
2 + // EmitterChain (uint16)
63+
32 + // EmitterAddress (32 bytes)
64+
1 + // IsReobservation (bool)
65+
1 + // Unreliable (bool)
66+
1 + // verificationState (uint8)
67+
8 // Payload length (uint64)
5768
)
5869

5970
var (
@@ -467,7 +478,7 @@ func UnmarshalMessagePublication(data []byte) (*MessagePublication, error) {
467478
}
468479

469480
emitterAddress := vaa.Address{}
470-
if n, err := reader.Read(emitterAddress[:]); err != nil || n != AddressLength {
481+
if n, err := reader.Read(emitterAddress[:]); err != nil || n != int(vaa.AddressLength) {
471482
return nil, fmt.Errorf("failed to read emitter address [%d]: %w", n, err)
472483
}
473484
msg.EmitterAddress = emitterAddress
@@ -492,19 +503,6 @@ func UnmarshalMessagePublication(data []byte) (*MessagePublication, error) {
492503
// UnmarshalBinary implements the BinaryUnmarshaler interface for MessagePublication.
493504
func (m *MessagePublication) UnmarshalBinary(data []byte) error {
494505

495-
// fixedFieldsLen is the minimum length of the fixed portion of a message publication.
496-
// It is the sum of the sizes of each of the fields plus length information for the Payload.
497-
// This is used to check that the data is long enough for the rest of the message after reading the TxID.
498-
const fixedFieldsLen = 8 + // Timestamp (int64)
499-
4 + // Nonce (uint32)
500-
8 + // Sequence (uint64)
501-
1 + // ConsistencyLevel (uint8)
502-
2 + // EmitterChain (uint16)
503-
32 + // EmitterAddress (32 bytes)
504-
1 + // IsReobservation (bool)
505-
1 + // Unreliable (bool)
506-
8 // Payload length (uint64)
507-
508506
// Calculate minimum required length for the fixed portion
509507
// (excluding variable-length fields: TxID and Payload)
510508
if len(data) < marshaledMsgLenMin {

node/pkg/common/chainlock_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,19 @@ func TestMessagePublicationUnmarshalBinaryErrors(t *testing.T) {
135135
assert.Contains(t, inputSizeErr.Error(), "data too short")
136136
},
137137
},
138+
{
139+
name: "data too short - after non-empty TxID",
140+
errorChecker: func(t *testing.T, err error) {
141+
var inputSizeErr ErrInputSize
142+
require.ErrorAs(t, err, &inputSizeErr)
143+
assert.Contains(t, inputSizeErr.Error(), "data too short after reading TxID")
144+
},
145+
setupData: func() []byte {
146+
data := make([]byte, 1+1+fixedFieldsLen-1)
147+
data[offsetTxIDLength] = 1
148+
return data
149+
},
150+
},
138151
{
139152
name: "invalid IsReobservation boolean - value 0x02",
140153
expectedErr: ErrInvalidBinaryBool,

sdk/vaa/structs.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ type (
5454
// Address is a Wormhole protocol address otherwise referred to as a "wormhole normalized address." It typically
5555
// contains the native chain's address for chains where the addresses are <= 32 bytes.
5656
//
57-
// If the address data type of a chain is < 32 bytes, the value is zero-padded on the left.
57+
// If the address data type of a chain is less than [AddressLength] bytes, the value is zero-padded on the left.
5858
//
59-
// For chains with addresses that support lengths greater than 32 bytes, this type MAY be used
59+
// For chains with addresses that support lengths greater than [AddressLength] bytes, this type MAY be used
6060
// to represent an on-chain address by hashing it using an algorithm like sha256.
6161
//
6262
// For chains with variable length addresses, users MUST use this type consistently such that addresses
6363
// can be uniquely identified. For example, if a chain supports addresses between length 2 and 64, this type
6464
// should always encode addresses for that chain as sha256 digests. This helps to avoid a situation where
65-
// addresses with length < 32 have zero-padded EVM-like addresses, but larger addresses on the same chain
65+
// addresses with length < [AddressLength] have zero-padded EVM-like addresses, but larger addresses on the same chain
6666
// are represented as digests.
6767
//
6868
// Other uses of this type are NOT RECOMMENDED given that they can overload the semantic meaning of this field
6969
// which already does a lot of conceptual work across disparate blockchain implementations.
70-
Address [32]byte
70+
Address [AddressLength]byte
7171

7272
// Signature of a single guardian
7373
Signature struct {
@@ -126,6 +126,9 @@ const (
126126
ConsistencyLevelSafe = uint8(201)
127127
ConsistencyLevelFinalized = uint8(202)
128128
ConsistencyLevelCustom = uint8(203)
129+
130+
// AddressLength is the length of an normalized [Address] in bytes.
131+
AddressLength uint8 = 32
129132
)
130133

131134
//nolint:unparam // error is always nil here but the return type is required to satisfy the interface.

0 commit comments

Comments
 (0)