chainlock.go 23 KB

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