chainlock.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. package common
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "math"
  11. "time"
  12. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  13. "go.uber.org/zap"
  14. )
  15. const (
  16. // TxIDLenMin is the minimum length of a txID.
  17. TxIDLenMin = 32
  18. // AddressLength is the length of a normalized Wormhole address in bytes.
  19. AddressLength = 32
  20. // Wormhole supports arbitrary payloads due to the variance in transaction and block sizes between chains.
  21. // However, during serialization, payload lengths are limited by Go slice length constraints and violations
  22. // of these limits can cause panics.
  23. // (https://go.dev/src/runtime/slice.go)
  24. // This limit is chosen to be large enough to prevent such panics but it should comfortably handle all payloads.
  25. // If not, the limit can be increased.
  26. PayloadLenMax = 1024 * 1024 * 1024 * 10 // 10 GB
  27. // The minimum size of a marshaled message publication. It is the sum of the sizes of each of
  28. // the fields plus length information for fields with variable lengths (TxID and Payload).
  29. marshaledMsgLenMin = 1 + // TxID length (uint8)
  30. TxIDLenMin + // TxID ([]byte), minimum length of 32 bytes (but may be longer)
  31. 8 + // Timestamp (int64)
  32. 4 + // Nonce (uint32)
  33. 8 + // Sequence (uint64)
  34. 1 + // ConsistencyLevel (uint8)
  35. 2 + // EmitterChain (uint16)
  36. 32 + // EmitterAddress (32 bytes)
  37. 1 + // IsReobservation (bool)
  38. 1 + // Unreliable (bool)
  39. 1 + // verificationState (uint8)
  40. 8 // Payload length (int64), may be zero
  41. // Deprecated: represents the minimum message length for a marshaled message publication
  42. // before the Unreliable and verificationState fields were added.
  43. // Use [marshaledMsgSizeMin] instead.
  44. minMsgLength = 88
  45. // minMsgIdLen is the minimum length of a message ID. It is used to uniquely identify
  46. // messages in the case of a duplicate message ID and is stored in the database.
  47. MinMsgIdLen = len("1/0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16/0")
  48. )
  49. var (
  50. ErrInvalidBinaryBool = errors.New("invalid binary bool (neither 0x00 nor 0x01)")
  51. ErrInvalidVerificationState = errors.New("invalid verification state")
  52. )
  53. type ErrUnexpectedEndOfRead struct {
  54. expected int
  55. got int
  56. }
  57. func (e ErrUnexpectedEndOfRead) Error() string {
  58. return fmt.Sprintf("data position after unmarshal does not match data length. expected: %d got: %d", e.expected, e.got)
  59. }
  60. // ErrInputSize is returned when the input size is not the expected size during marshaling.
  61. type ErrInputSize struct {
  62. Msg string
  63. Got int
  64. }
  65. func (e ErrInputSize) Error() string {
  66. return fmt.Sprintf("wrong size: %s. expected >= %d bytes, got %d", e.Msg, marshaledMsgLenMin, e.Got)
  67. }
  68. // MaxSafeInputSize defines the maximum safe size for untrusted input from `io` Readers.
  69. // It should be configured so that it can comfortably contain all valid reads while
  70. // providing a strict upper bound to prevent unlimited reads.
  71. const MaxSafeInputSize = 128 * 1024 * 1024 // 128MB (arbitrary)
  72. var ErrInputTooLarge = errors.New("input data exceeds maximum allowed size")
  73. var (
  74. ErrBinaryWrite = errors.New("failed to write binary data")
  75. ErrTxIDTooLong = errors.New("field TxID too long")
  76. ErrTxIDTooShort = errors.New("field TxID too short")
  77. ErrInvalidPayload = errors.New("field payload too long")
  78. ErrDataTooShort = errors.New("data too short")
  79. ErrTimestampTooShort = errors.New("data too short for timestamp")
  80. ErrNonceTooShort = errors.New("data too short for nonce")
  81. ErrSequenceTooShort = errors.New("data too short for sequence")
  82. ErrConsistencyTooShort = errors.New("data too short for consistency level")
  83. ErrChainTooShort = errors.New("data too short for emitter chain")
  84. ErrAddressTooShort = errors.New("data too short for emitter address")
  85. ErrReobsTooShort = errors.New("data too short for IsReobservation")
  86. ErrUnreliableTooShort = errors.New("data too short for Unreliable")
  87. ErrVerStateTooShort = errors.New("data too short for verification state")
  88. ErrPayloadLenTooShort = errors.New("data too short for payload length")
  89. ErrPayloadTooShort = errors.New("data too short for payload")
  90. )
  91. // The `VerificationState` is the result of applying transfer verification to the transaction associated with the `MessagePublication`.
  92. // While this could likely be extended to additional security controls in the future, it is only used for `txverifier` at present.
  93. // Consequently, its status should be set to `NotVerified` or `NotApplicable` for all messages that aren't token transfers.
  94. type VerificationState uint8
  95. const (
  96. // The default state for a message. This can be used before verification occurs. If no verification is required, `NotApplicable` should be used instead.
  97. NotVerified VerificationState = iota
  98. // Represents a "known bad" status where a Message has been validated and the result indicates an erroneous or invalid message. The message should be discarded.
  99. Rejected
  100. // Represents a successful validation, neither confirmed to be good or bad, but unusual.
  101. Anomalous
  102. // Represents a "known good" status where a Message has been validated and the result is good. The message should be processed normally.
  103. Valid
  104. // Indicates that no verification is necessary.
  105. NotApplicable
  106. // The message could not complete the verification process.
  107. CouldNotVerify
  108. )
  109. // NumVariantsVerificationState is the number of variants in the VerificationState enum.
  110. // Used to validate deserialization.
  111. const NumVariantsVerificationState = 6
  112. func (v VerificationState) String() string {
  113. switch v {
  114. case NotVerified:
  115. return "NotVerified"
  116. case Valid:
  117. return "Valid"
  118. case Anomalous:
  119. return "Anomalous"
  120. case Rejected:
  121. return "Rejected"
  122. case NotApplicable:
  123. return "NotApplicable"
  124. case CouldNotVerify:
  125. return "CouldNotVerify"
  126. default:
  127. return ""
  128. }
  129. }
  130. type MessagePublication struct {
  131. TxID []byte
  132. Timestamp time.Time
  133. Nonce uint32
  134. Sequence uint64
  135. ConsistencyLevel uint8
  136. EmitterChain vaa.ChainID
  137. EmitterAddress vaa.Address
  138. // NOTE: there is no upper bound on the size of the payload. Wormhole supports arbitrary payloads
  139. // due to the variance in transaction and block sizes between chains. However, during deserialization,
  140. // payload lengths are bounds-checked against [PayloadLenMax] to prevent makeslice panics from malformed input.
  141. Payload []byte
  142. IsReobservation bool
  143. // Unreliable indicates if this message can be reobserved. If a message is considered unreliable it cannot be
  144. // reobserved.
  145. Unreliable bool
  146. // The `VerificationState` is the result of applying transfer
  147. // verification to the transaction associated with the
  148. // `MessagePublication`. While this could likely be extended to
  149. // additional security controls in the future, it is only used for
  150. // `txverifier` at present. Consequently, its status should be set to
  151. // `NotVerified` or `NotApplicable` for all messages that aren't token
  152. // transfers.
  153. // This field is intentionally private so that it must be
  154. // updated using the setter, which performs verification on the new
  155. // state.
  156. verificationState VerificationState
  157. }
  158. // TxIDString returns a hex-encoded representation of the TxID field, prefixed with '0x'.
  159. func (msg *MessagePublication) TxIDString() string {
  160. return "0x" + hex.EncodeToString(msg.TxID)
  161. }
  162. func (msg *MessagePublication) MessageID() []byte {
  163. return []byte(msg.MessageIDString())
  164. }
  165. func (msg *MessagePublication) MessageIDString() string {
  166. return fmt.Sprintf("%v/%v/%v", uint16(msg.EmitterChain), msg.EmitterAddress, msg.Sequence)
  167. }
  168. func (msg *MessagePublication) VerificationState() VerificationState {
  169. return msg.verificationState
  170. }
  171. // SetVerificationState is the setter for verificationState. Returns an error if called in a way that likely indicates a programming mistake.
  172. // This includes cases where:
  173. // - an existing state would be overwritten by the NotVerified state
  174. // - the argument is equal to the existing value
  175. func (msg *MessagePublication) SetVerificationState(s VerificationState) error {
  176. // Avoid rewriting an existing state with the default value. There shouldn't be a reason to overwrite an existing verification,
  177. // and if it happens it's probably a bug.
  178. if s == NotVerified && msg.verificationState != NotVerified {
  179. return fmt.Errorf("SetVerificationState: refusing to overwrite existing VerificationState %s to NotVerified state", s)
  180. }
  181. // Not a problem per se but likely indicates a programming error.
  182. if s == msg.verificationState {
  183. return fmt.Errorf("SetVerificationState: called with value %s but Message Publication already has this value", s)
  184. }
  185. msg.verificationState = s
  186. return nil
  187. }
  188. // Deprecated: This function does not unmarshal the Unreliable or verificationState fields.
  189. // Use [MessagePublication.MarshalBinary] instead.
  190. func (msg *MessagePublication) Marshal() ([]byte, error) {
  191. buf := new(bytes.Buffer)
  192. if len(msg.TxID) > math.MaxUint8 {
  193. return nil, errors.New("TxID too long")
  194. }
  195. vaa.MustWrite(buf, binary.BigEndian, uint8(len(msg.TxID))) // #nosec G115 -- This is validated above
  196. buf.Write(msg.TxID)
  197. vaa.MustWrite(buf, binary.BigEndian, uint32(msg.Timestamp.Unix())) // #nosec G115 -- This conversion is safe until year 2106
  198. vaa.MustWrite(buf, binary.BigEndian, msg.Nonce)
  199. vaa.MustWrite(buf, binary.BigEndian, msg.Sequence)
  200. vaa.MustWrite(buf, binary.BigEndian, msg.ConsistencyLevel)
  201. vaa.MustWrite(buf, binary.BigEndian, msg.EmitterChain)
  202. buf.Write(msg.EmitterAddress[:])
  203. vaa.MustWrite(buf, binary.BigEndian, msg.IsReobservation)
  204. // Unreliable and verificationState are not marshalled because they are not used in the Governor code,
  205. // which is currently the only place in the node where marshalling this struct is done.
  206. buf.Write(msg.Payload)
  207. return buf.Bytes(), nil
  208. }
  209. // MarshalBinary implements the BinaryMarshaler interface for MessagePublication.
  210. func (msg *MessagePublication) MarshalBinary() ([]byte, error) {
  211. // Marshalled Message Publication layout:
  212. //
  213. // - TxID length
  214. // - TxID
  215. // - Timestamp
  216. // - Nonce
  217. // - Sequence
  218. // - ConsistencyLevel
  219. // - EmitterChain
  220. // - EmitterAddress
  221. // - IsReobservation
  222. // - Unreliable
  223. // - verificationState
  224. // - Payload length
  225. // - Payload
  226. // TxID is an alias for []byte.
  227. const TxIDSizeMax = math.MaxUint8
  228. // Check preconditions
  229. txIDLen := len(msg.TxID)
  230. if txIDLen > TxIDSizeMax {
  231. return nil, ErrInputSize{Msg: "TxID too long"}
  232. }
  233. if txIDLen < TxIDLenMin {
  234. return nil, ErrInputSize{Msg: "TxID too short"}
  235. }
  236. payloadLen := len(msg.Payload)
  237. // Set up for serialization
  238. var (
  239. be = binary.BigEndian
  240. // Size of the buffer needed to hold the serialized message.
  241. // TxIDLenMin is already accounted for in the marshaledMsgLenMin calculation.
  242. bufSize = (marshaledMsgLenMin - TxIDLenMin) + txIDLen + payloadLen
  243. buf = make([]byte, 0, bufSize)
  244. )
  245. // TxID (and length)
  246. buf = append(buf, uint8(txIDLen))
  247. buf = append(buf, msg.TxID...)
  248. // Timestamp
  249. tsBytes := make([]byte, 8)
  250. // #nosec G115 -- int64 and uint64 have the same number of bytes, and Unix time won't be negative.
  251. be.PutUint64(tsBytes, uint64(msg.Timestamp.Unix()))
  252. buf = append(buf, tsBytes...)
  253. // Nonce
  254. nonceBytes := make([]byte, 4)
  255. be.PutUint32(nonceBytes, msg.Nonce)
  256. buf = append(buf, nonceBytes...)
  257. // Sequence
  258. seqBytes := make([]byte, 8)
  259. be.PutUint64(seqBytes, msg.Sequence)
  260. buf = append(buf, seqBytes...)
  261. // ConsistencyLevel
  262. buf = append(buf, msg.ConsistencyLevel)
  263. // EmitterChain
  264. chainBytes := make([]byte, 2)
  265. be.PutUint16(chainBytes, uint16(msg.EmitterChain))
  266. buf = append(buf, chainBytes...)
  267. // EmitterAddress
  268. buf = append(buf, msg.EmitterAddress.Bytes()...)
  269. // IsReobservation
  270. if msg.IsReobservation {
  271. buf = append(buf, byte(1))
  272. } else {
  273. buf = append(buf, byte(0))
  274. }
  275. // Unreliable
  276. if msg.Unreliable {
  277. buf = append(buf, byte(1))
  278. } else {
  279. buf = append(buf, byte(0))
  280. }
  281. // verificationState
  282. buf = append(buf, uint8(msg.verificationState))
  283. // Payload (and length)
  284. // There is no upper bound on the size of the payload as Wormhole supports arbitrary payloads. A uint64 should suffice to hold the length of the payload.
  285. payloadLenBytes := make([]byte, 8)
  286. be.PutUint64(payloadLenBytes, uint64(payloadLen))
  287. buf = append(buf, payloadLenBytes...)
  288. buf = append(buf, msg.Payload...)
  289. return buf, nil
  290. }
  291. // Deprecated: UnmarshalMessagePublication deserializes a MessagePublication.
  292. // This function does not unmarshal the Unreliable or verificationState fields.
  293. // Use [MessagePublication.UnmarshalBinary] instead.
  294. func UnmarshalMessagePublication(data []byte) (*MessagePublication, error) {
  295. if len(data) < minMsgLength {
  296. return nil, errors.New("message is too short")
  297. }
  298. msg := &MessagePublication{}
  299. reader := bytes.NewReader(data[:])
  300. txIdLen := uint8(0)
  301. if err := binary.Read(reader, binary.BigEndian, &txIdLen); err != nil {
  302. return nil, fmt.Errorf("failed to read TxID len: %w", err)
  303. }
  304. msg.TxID = make([]byte, txIdLen)
  305. if n, err := reader.Read(msg.TxID[:]); err != nil || n != int(txIdLen) {
  306. return nil, fmt.Errorf("failed to read TxID [%d]: %w", n, err)
  307. }
  308. unixSeconds := uint32(0)
  309. if err := binary.Read(reader, binary.BigEndian, &unixSeconds); err != nil {
  310. return nil, fmt.Errorf("failed to read timestamp: %w", err)
  311. }
  312. msg.Timestamp = time.Unix(int64(unixSeconds), 0)
  313. if err := binary.Read(reader, binary.BigEndian, &msg.Nonce); err != nil {
  314. return nil, fmt.Errorf("failed to read nonce: %w", err)
  315. }
  316. if err := binary.Read(reader, binary.BigEndian, &msg.Sequence); err != nil {
  317. return nil, fmt.Errorf("failed to read sequence: %w", err)
  318. }
  319. if err := binary.Read(reader, binary.BigEndian, &msg.ConsistencyLevel); err != nil {
  320. return nil, fmt.Errorf("failed to read consistency level: %w", err)
  321. }
  322. if err := binary.Read(reader, binary.BigEndian, &msg.EmitterChain); err != nil {
  323. return nil, fmt.Errorf("failed to read emitter chain: %w", err)
  324. }
  325. emitterAddress := vaa.Address{}
  326. if n, err := reader.Read(emitterAddress[:]); err != nil || n != AddressLength {
  327. return nil, fmt.Errorf("failed to read emitter address [%d]: %w", n, err)
  328. }
  329. msg.EmitterAddress = emitterAddress
  330. if err := binary.Read(reader, binary.BigEndian, &msg.IsReobservation); err != nil {
  331. return nil, fmt.Errorf("failed to read isReobservation: %w", err)
  332. }
  333. // Unreliable and verificationState are not unmarshalled because they are not used in the Governor code,
  334. // which is currently the only place in the node where unmarshalling this struct is done.
  335. payload := make([]byte, reader.Len())
  336. n, err := reader.Read(payload)
  337. if err != nil || n == 0 {
  338. return nil, fmt.Errorf("failed to read payload [%d]: %w", n, err)
  339. }
  340. msg.Payload = payload[:n]
  341. return msg, nil
  342. }
  343. // UnmarshalBinary implements the BinaryUnmarshaler interface for MessagePublication.
  344. func (m *MessagePublication) UnmarshalBinary(data []byte) error {
  345. // fixedFieldsLen is the minimum length of the fixed portion of a message publication.
  346. // It is the sum of the sizes of each of the fields plus length information for the Payload.
  347. // This is used to check that the data is long enough for the rest of the message after reading the TxID.
  348. const fixedFieldsLen = 8 + // Timestamp (int64)
  349. 4 + // Nonce (uint32)
  350. 8 + // Sequence (uint64)
  351. 1 + // ConsistencyLevel (uint8)
  352. 2 + // EmitterChain (uint16)
  353. 32 + // EmitterAddress (32 bytes)
  354. 1 + // IsReobservation (bool)
  355. 1 + // Unreliable (bool)
  356. 8 // Payload length (uint64)
  357. // Calculate minimum required length for the fixed portion
  358. // (excluding variable-length fields: TxID and Payload)
  359. if len(data) < marshaledMsgLenMin {
  360. return ErrInputSize{Msg: "data too short", Got: len(data)}
  361. }
  362. mp := &MessagePublication{}
  363. // Set up deserialization
  364. be := binary.BigEndian
  365. pos := 0
  366. // TxID length
  367. txIDLen := uint8(data[pos])
  368. pos++
  369. // Bounds checks. TxID length should be at least TxIDLenMin, but not larger than the length of the data.
  370. // The second check is to avoid panics.
  371. if int(txIDLen) < TxIDLenMin || int(txIDLen) > len(data) {
  372. return ErrInputSize{Msg: "TxID length is invalid"}
  373. }
  374. // Read TxID
  375. mp.TxID = make([]byte, txIDLen)
  376. copy(mp.TxID, data[pos:pos+int(txIDLen)])
  377. pos += int(txIDLen)
  378. // TxID has a dynamic length, so now that we've read it, check that the remaining data is long enough for the rest of the message. This means that all fixed-length fields can be parsed with a payload of 0 or more bytes.
  379. // Concretely, we're checking that the data is at least long enough to contain information for all of
  380. // the fields except for the Payload itself.
  381. if len(data)-pos < fixedFieldsLen {
  382. return ErrInputSize{Msg: "data too short after reading TxID", Got: len(data)}
  383. }
  384. // Timestamp
  385. timestamp := be.Uint64(data[pos : pos+8])
  386. // Nanoseconds are not serialized as they are not used in Wormhole, so set them to zero.
  387. // #nosec G115 -- int64 and uint64 have the same number of bytes, and Unix time won't be negative.
  388. mp.Timestamp = time.Unix(int64(timestamp), 0)
  389. pos += 8
  390. // Nonce
  391. mp.Nonce = be.Uint32(data[pos : pos+4])
  392. pos += 4
  393. // Sequence
  394. mp.Sequence = be.Uint64(data[pos : pos+8])
  395. pos += 8
  396. // ConsistencyLevel
  397. // TODO: This could be validated against the valid range of values for ConsistencyLevel.
  398. mp.ConsistencyLevel = data[pos]
  399. pos++
  400. // EmitterChain
  401. mp.EmitterChain = vaa.ChainID(be.Uint16(data[pos : pos+2]))
  402. pos += 2
  403. // EmitterAddress
  404. copy(mp.EmitterAddress[:], data[pos:pos+32])
  405. pos += 32
  406. // IsReobservation
  407. if !validBinaryBool(data[pos]) {
  408. return ErrInvalidBinaryBool
  409. }
  410. mp.IsReobservation = data[pos] != 0
  411. pos++
  412. // Unreliable
  413. if !validBinaryBool(data[pos]) {
  414. return ErrInvalidBinaryBool
  415. }
  416. mp.Unreliable = data[pos] != 0
  417. pos++
  418. // verificationState. NumVariantsVerificationState is the number of variants of the enum,
  419. // which begins at 0. This means the valid range is [0, NumVariantsVerificationState-1].
  420. if data[pos] > NumVariantsVerificationState-1 {
  421. return ErrInvalidVerificationState
  422. }
  423. mp.verificationState = VerificationState(data[pos])
  424. pos++
  425. // Payload length
  426. payloadLen := be.Uint64(data[pos : pos+8])
  427. pos += 8
  428. // Check if payload length is within reasonable bounds to prevent makeslice panic.
  429. // Since payloadLen is read as uint64 from untrusted input, it could potentially
  430. // exceed this limit and cause a runtime panic when passed to make([]byte, payloadLen).
  431. // This bounds check prevents such panics by rejecting oversized payload lengths early.
  432. if payloadLen > PayloadLenMax {
  433. return ErrInputSize{Msg: "payload length too large", Got: len(data)}
  434. }
  435. // Check if we have enough data for the payload
  436. // #nosec G115 -- payloadLen is read from data, bounds checked above
  437. if len(data) < pos+int(payloadLen) {
  438. return ErrInputSize{Msg: "invalid payload length"}
  439. }
  440. // Read payload
  441. mp.Payload = make([]byte, payloadLen)
  442. // #nosec G115 -- payloadLen is read from data, bounds checked above
  443. copy(mp.Payload, data[pos:pos+int(payloadLen)])
  444. // #nosec G115 -- payloadLen is read from data, bounds checked above
  445. pos += int(payloadLen)
  446. // Check that exactly the correct number of bytes was read.
  447. if pos != len(data) {
  448. return ErrUnexpectedEndOfRead{expected: len(data), got: pos}
  449. }
  450. // Overwrite the receiver with the deserialized message.
  451. *m = *mp
  452. return nil
  453. }
  454. // The standard json Marshal / Unmarshal of time.Time gets confused between local and UTC time.
  455. func (msg *MessagePublication) MarshalJSON() ([]byte, error) {
  456. type Alias MessagePublication
  457. return json.Marshal(&struct {
  458. Timestamp int64
  459. *Alias
  460. }{
  461. Timestamp: msg.Timestamp.Unix(),
  462. Alias: (*Alias)(msg),
  463. })
  464. }
  465. func (msg *MessagePublication) UnmarshalJSON(data []byte) error {
  466. type Alias MessagePublication
  467. aux := &struct {
  468. Timestamp int64
  469. *Alias
  470. }{
  471. Alias: (*Alias)(msg),
  472. }
  473. if err := json.Unmarshal(data, &aux); err != nil {
  474. return err
  475. }
  476. msg.Timestamp = time.Unix(aux.Timestamp, 0)
  477. return nil
  478. }
  479. func (msg *MessagePublication) CreateVAA(gsIndex uint32) *vaa.VAA {
  480. return &vaa.VAA{
  481. Version: vaa.SupportedVAAVersion,
  482. GuardianSetIndex: gsIndex,
  483. Signatures: nil,
  484. Timestamp: msg.Timestamp,
  485. Nonce: msg.Nonce,
  486. EmitterChain: msg.EmitterChain,
  487. EmitterAddress: msg.EmitterAddress,
  488. Payload: msg.Payload,
  489. Sequence: msg.Sequence,
  490. ConsistencyLevel: msg.ConsistencyLevel,
  491. }
  492. }
  493. func (msg *MessagePublication) CreateDigest() string {
  494. v := msg.CreateVAA(0) // The guardian set index is not part of the digest, so we can pass in zero.
  495. db := v.SigningDigest()
  496. return hex.EncodeToString(db.Bytes())
  497. }
  498. // ZapFields takes some zap fields and appends zap fields related to the message. Example usage:
  499. // `logger.Info("logging something with a message", msg.ZapFields(zap.Int("some_other_field", 100))...)“
  500. // TODO refactor the codebase to use this function instead of manually logging the message with inconsistent fields
  501. func (msg *MessagePublication) ZapFields(fields ...zap.Field) []zap.Field {
  502. return append(fields,
  503. // MessageID contains EmitterChain, EmitterAddress, and Sequence
  504. zap.String("msgID", string(msg.MessageID())),
  505. zap.String("txID", msg.TxIDString()),
  506. zap.Time("timestamp", msg.Timestamp),
  507. zap.Uint32("nonce", msg.Nonce),
  508. zap.Uint8("consistency", msg.ConsistencyLevel),
  509. zap.Bool("unreliable", msg.Unreliable),
  510. zap.Bool("isReobservation", msg.IsReobservation),
  511. zap.String("verificationState", msg.verificationState.String()),
  512. )
  513. }
  514. // VAAHash returns a hash corresponding to the fields of the Message Publication that are ultimately
  515. // encoded in a VAA. This is a helper function used to uniquely identify a Message Publication.
  516. func (msg *MessagePublication) VAAHash() string {
  517. v := msg.CreateVAA(0) // We can pass zero in as the guardian set index because it is not part of the digest.
  518. digest := v.SigningDigest()
  519. return hex.EncodeToString(digest.Bytes())
  520. }
  521. // validBinaryBool returns true if the byte is either 0x00 or 0x01.
  522. // Go marshals booleans as strictly 0x00 or 0x01, so this function is used to validate
  523. // that a given byte is a valid boolean. When reading, any non-zero value is considered true,
  524. // but here we want to validate that the value is strictly either 0x00 or 0x01.
  525. func validBinaryBool(b byte) bool {
  526. return b == 0x00 || b == 0x01
  527. }
  528. // SafeRead reads from r with a size limit to prevent memory exhaustion attacks.
  529. // It returns an error if the input exceeds MaxSafeInputSize.
  530. func SafeRead(r io.Reader) ([]byte, error) {
  531. // Create a LimitReader that allows reading up to MaxSafeInputSize + 1 bytes.
  532. // The extra byte is specifically to detect if the input stream *exceeds* MaxSafeInputSize.
  533. lr := io.LimitReader(r, MaxSafeInputSize+1)
  534. //nolint:forbidigo // SafeRead is intended as a convenient and safe wrapper for ReadAll.
  535. b, err := io.ReadAll(lr)
  536. if err != nil {
  537. // Propagate any actual read errors from the underlying reader.
  538. return nil, err
  539. }
  540. // If the length of the read bytes is greater than MaxSafeInputSize,
  541. // it means the original reader contained more data than allowed.
  542. // In this case, we return an error instead of silently truncating.
  543. if len(b) > MaxSafeInputSize {
  544. return nil, ErrInputTooLarge
  545. }
  546. // If err was nil and len(b) <= MaxSafeInputSize, it means we read all
  547. // available input (or up to the limit) without exceeding the maximum.
  548. return b, nil
  549. }