request.go 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. package query
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "math"
  7. "strings"
  8. "github.com/certusone/wormhole/node/pkg/common"
  9. gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
  10. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  11. ethCommon "github.com/ethereum/go-ethereum/common"
  12. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  13. solana "github.com/gagliardetto/solana-go"
  14. )
  15. // MSG_VERSION is the current version of the CCQ message protocol.
  16. const MSG_VERSION uint8 = 1
  17. // QueryRequest defines a cross chain query request to be submitted to the guardians.
  18. // It is the payload of the SignedQueryRequest gossip message.
  19. type QueryRequest struct {
  20. Nonce uint32
  21. PerChainQueries []*PerChainQueryRequest
  22. }
  23. // PerChainQueryRequest represents a query request for a single chain.
  24. type PerChainQueryRequest struct {
  25. // ChainId indicates which chain this query is destine for.
  26. ChainId vaa.ChainID
  27. // Query is the chain specific query data.
  28. Query ChainSpecificQuery
  29. }
  30. // ChainSpecificQuery is the interface that must be implemented by a chain specific query.
  31. type ChainSpecificQuery interface {
  32. Type() ChainSpecificQueryType
  33. Marshal() ([]byte, error)
  34. Unmarshal(data []byte) error
  35. UnmarshalFromReader(reader *bytes.Reader) error
  36. Validate() error
  37. }
  38. // ChainSpecificQueryType is used to interpret the data in a per chain query request.
  39. type ChainSpecificQueryType uint8
  40. // EthCallQueryRequestType is the type of an EVM eth_call query request.
  41. const EthCallQueryRequestType ChainSpecificQueryType = 1
  42. // EthCallQueryRequest implements ChainSpecificQuery for an EVM eth_call query request.
  43. type EthCallQueryRequest struct {
  44. // BlockId identifies the block to be queried. It must be a hex string starting with 0x. It may be a block number or a block hash.
  45. BlockId string
  46. // CallData is an array of specific queries to be performed on the specified block, in a single RPC call.
  47. CallData []*EthCallData
  48. }
  49. func (ecr *EthCallQueryRequest) CallDataList() []*EthCallData {
  50. return ecr.CallData
  51. }
  52. // EthCallByTimestampQueryRequestType is the type of an EVM eth_call_by_timestamp query request.
  53. const EthCallByTimestampQueryRequestType ChainSpecificQueryType = 2
  54. // EthCallByTimestampQueryRequest implements ChainSpecificQuery for an EVM eth_call_by_timestamp query request.
  55. type EthCallByTimestampQueryRequest struct {
  56. // TargetTimeInUs specifies the desired timestamp in microseconds.
  57. TargetTimestamp uint64
  58. // TargetBlockIdHint is optional. If specified, it identifies the block prior to the desired timestamp. It must be a hex string starting with 0x. It may be a block number or a block hash.
  59. TargetBlockIdHint string
  60. // FollowingBlockIdHint is optional. If specified, it identifies the block immediately following the desired timestamp. It must be a hex string starting with 0x. It may be a block number or a block hash.
  61. FollowingBlockIdHint string
  62. // CallData is an array of specific queries to be performed on the specified block, in a single RPC call.
  63. CallData []*EthCallData
  64. }
  65. func (ecr *EthCallByTimestampQueryRequest) CallDataList() []*EthCallData {
  66. return ecr.CallData
  67. }
  68. // EthCallWithFinalityQueryRequestType is the type of an EVM eth_call_with_finality query request.
  69. const EthCallWithFinalityQueryRequestType ChainSpecificQueryType = 3
  70. // EthCallWithFinalityQueryRequest implements ChainSpecificQuery for an EVM eth_call_with_finality query request.
  71. type EthCallWithFinalityQueryRequest struct {
  72. // BlockId identifies the block to be queried. It must be a hex string starting with 0x. It may be a block number or a block hash.
  73. BlockId string
  74. // Finality is required. It identifies the level of finality the block must reach before the query is performed. Valid values are "finalized" and "safe".
  75. Finality string
  76. // CallData is an array of specific queries to be performed on the specified block, in a single RPC call.
  77. CallData []*EthCallData
  78. }
  79. func (ecr *EthCallWithFinalityQueryRequest) CallDataList() []*EthCallData {
  80. return ecr.CallData
  81. }
  82. // EthCallData specifies the parameters to a single EVM eth_call request.
  83. type EthCallData struct {
  84. // To specifies the contract address to be queried.
  85. To []byte
  86. // Data is the ABI encoded parameters to the query.
  87. Data []byte
  88. }
  89. const EvmContractAddressLength = 20
  90. ////////////////////////////////// Solana Queries ////////////////////////////////////////////////
  91. // SolanaAccountQueryRequestType is the type of a Solana sol_account query request.
  92. const SolanaAccountQueryRequestType ChainSpecificQueryType = 4
  93. // SolanaAccountQueryRequest implements ChainSpecificQuery for a Solana sol_account query request.
  94. type SolanaAccountQueryRequest struct {
  95. // Commitment identifies the commitment level to be used in the queried. Currently it may only "finalized".
  96. // Before we can support "confirmed", we need a way to read the account data and the block information atomically.
  97. // We would also need to deal with the fact that queries are only handled in the finalized watcher and it does not
  98. // have access to the latest confirmed slot needed for MinContextSlot retries.
  99. Commitment string
  100. // The minimum slot that the request can be evaluated at. Zero means unused.
  101. MinContextSlot uint64
  102. // The offset of the start of data to be returned. Unused if DataSliceLength is zero.
  103. DataSliceOffset uint64
  104. // The length of the data to be returned. Zero means all data is returned.
  105. DataSliceLength uint64
  106. // Accounts is an array of accounts to be queried.
  107. Accounts [][SolanaPublicKeyLength]byte
  108. }
  109. // Solana public keys are fixed length.
  110. const SolanaPublicKeyLength = solana.PublicKeyLength
  111. // According to the Solana spec, the longest comment string is nine characters. Allow a few more, just in case.
  112. // https://pkg.go.dev/github.com/gagliardetto/solana-go/rpc#CommitmentType
  113. const SolanaMaxCommitmentLength = 12
  114. // According to the spec, the query only supports up to 100 accounts.
  115. // https://github.com/solana-labs/solana/blob/9d132441fdc6282a8be4bff0bc77d6a2fefe8b59/rpc-client-api/src/request.rs#L204
  116. const SolanaMaxAccountsPerQuery = 100
  117. func (saq *SolanaAccountQueryRequest) AccountList() [][SolanaPublicKeyLength]byte {
  118. return saq.Accounts
  119. }
  120. // SolanaPdaQueryRequestType is the type of a Solana sol_pda query request.
  121. const SolanaPdaQueryRequestType ChainSpecificQueryType = 5
  122. // SolanaPdaQueryRequest implements ChainSpecificQuery for a Solana sol_pda query request.
  123. type SolanaPdaQueryRequest struct {
  124. // Commitment identifies the commitment level to be used in the queried. Currently it may only "finalized".
  125. // Before we can support "confirmed", we need a way to read the account data and the block information atomically.
  126. // We would also need to deal with the fact that queries are only handled in the finalized watcher and it does not
  127. // have access to the latest confirmed slot needed for MinContextSlot retries.
  128. Commitment string
  129. // The minimum slot that the request can be evaluated at. Zero means unused.
  130. MinContextSlot uint64
  131. // The offset of the start of data to be returned. Unused if DataSliceLength is zero.
  132. DataSliceOffset uint64
  133. // The length of the data to be returned. Zero means all data is returned.
  134. DataSliceLength uint64
  135. // PDAs is an array of PDAs to be queried.
  136. PDAs []SolanaPDAEntry
  137. }
  138. // SolanaPDAEntry defines a single Solana Program derived address (PDA).
  139. type SolanaPDAEntry struct {
  140. ProgramAddress [SolanaPublicKeyLength]byte
  141. Seeds [][]byte
  142. }
  143. // According to the spec, there may be at most 16 seeds.
  144. // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L559
  145. const SolanaMaxSeeds = solana.MaxSeeds
  146. // According to the spec, a seed may be at most 32 bytes.
  147. // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L557
  148. const SolanaMaxSeedLen = solana.MaxSeedLength
  149. func (spda *SolanaPdaQueryRequest) PDAList() []SolanaPDAEntry {
  150. return spda.PDAs
  151. }
  152. // PerChainQueryInternal is an internal representation of a query request that is passed to the watcher.
  153. type PerChainQueryInternal struct {
  154. RequestID string
  155. RequestIdx int
  156. Request *PerChainQueryRequest
  157. }
  158. func (pcqi *PerChainQueryInternal) ID() string {
  159. return fmt.Sprintf("%s:%d", pcqi.RequestID, pcqi.RequestIdx)
  160. }
  161. // QueryRequestDigest returns the query signing prefix based on the environment.
  162. func QueryRequestDigest(env common.Environment, b []byte) ethCommon.Hash {
  163. var queryRequestPrefix []byte
  164. if env == common.MainNet {
  165. queryRequestPrefix = []byte("mainnet_query_request_000000000000|")
  166. } else if env == common.TestNet {
  167. queryRequestPrefix = []byte("testnet_query_request_000000000000|")
  168. } else {
  169. queryRequestPrefix = []byte("devnet_query_request_0000000000000|")
  170. }
  171. return ethCrypto.Keccak256Hash(append(queryRequestPrefix, b...))
  172. }
  173. // PostSignedQueryRequest posts a signed query request to the specified channel.
  174. func PostSignedQueryRequest(signedQueryReqSendC chan<- *gossipv1.SignedQueryRequest, req *gossipv1.SignedQueryRequest) error {
  175. select {
  176. case signedQueryReqSendC <- req:
  177. return nil
  178. default:
  179. return common.ErrChanFull
  180. }
  181. }
  182. func SignedQueryRequestEqual(left *gossipv1.SignedQueryRequest, right *gossipv1.SignedQueryRequest) bool {
  183. if !bytes.Equal(left.QueryRequest, right.QueryRequest) {
  184. return false
  185. }
  186. if !bytes.Equal(left.Signature, right.Signature) {
  187. return false
  188. }
  189. return true
  190. }
  191. //
  192. // Implementation of QueryRequest.
  193. //
  194. // Marshal serializes the binary representation of a query request.
  195. // This method calls Validate() and relies on it to range checks lengths, etc.
  196. func (queryRequest *QueryRequest) Marshal() ([]byte, error) {
  197. if err := queryRequest.Validate(); err != nil {
  198. return nil, err
  199. }
  200. buf := new(bytes.Buffer)
  201. vaa.MustWrite(buf, binary.BigEndian, MSG_VERSION) // version
  202. vaa.MustWrite(buf, binary.BigEndian, queryRequest.Nonce) // uint32
  203. vaa.MustWrite(buf, binary.BigEndian, uint8(len(queryRequest.PerChainQueries))) // #nosec G115 -- `PerChainQueries` length checked in `Validate`
  204. for _, perChainQuery := range queryRequest.PerChainQueries {
  205. pcqBuf, err := perChainQuery.Marshal()
  206. if err != nil {
  207. return nil, fmt.Errorf("failed to marshal per chain query: %w", err)
  208. }
  209. buf.Write(pcqBuf)
  210. }
  211. return buf.Bytes(), nil
  212. }
  213. // Unmarshal deserializes the binary representation of a query request from a byte array
  214. func (queryRequest *QueryRequest) Unmarshal(data []byte) error {
  215. reader := bytes.NewReader(data[:])
  216. return queryRequest.UnmarshalFromReader(reader)
  217. }
  218. // UnmarshalFromReader deserializes the binary representation of a query request from an existing reader
  219. func (queryRequest *QueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  220. var version uint8
  221. if err := binary.Read(reader, binary.BigEndian, &version); err != nil {
  222. return fmt.Errorf("failed to read message version: %w", err)
  223. }
  224. if version != MSG_VERSION {
  225. return fmt.Errorf("unsupported message version: %d", version)
  226. }
  227. if err := binary.Read(reader, binary.BigEndian, &queryRequest.Nonce); err != nil {
  228. return fmt.Errorf("failed to read request nonce: %w", err)
  229. }
  230. numPerChainQueries := uint8(0)
  231. if err := binary.Read(reader, binary.BigEndian, &numPerChainQueries); err != nil {
  232. return fmt.Errorf("failed to read number of per chain queries: %w", err)
  233. }
  234. for count := 0; count < int(numPerChainQueries); count++ {
  235. perChainQuery := PerChainQueryRequest{}
  236. err := perChainQuery.UnmarshalFromReader(reader)
  237. if err != nil {
  238. return fmt.Errorf("failed to Unmarshal per chain query: %w", err)
  239. }
  240. queryRequest.PerChainQueries = append(queryRequest.PerChainQueries, &perChainQuery)
  241. }
  242. if reader.Len() != 0 {
  243. return fmt.Errorf("excess bytes in unmarshal")
  244. }
  245. if err := queryRequest.Validate(); err != nil {
  246. return fmt.Errorf("unmarshaled request failed validation: %w", err)
  247. }
  248. return nil
  249. }
  250. // Validate does basic validation on a received query request.
  251. func (queryRequest *QueryRequest) Validate() error {
  252. // Nothing to validate on the Nonce.
  253. if len(queryRequest.PerChainQueries) <= 0 {
  254. return fmt.Errorf("request does not contain any per chain queries")
  255. }
  256. if len(queryRequest.PerChainQueries) > math.MaxUint8 {
  257. return fmt.Errorf("too many per chain queries")
  258. }
  259. for idx, perChainQuery := range queryRequest.PerChainQueries {
  260. if err := perChainQuery.Validate(); err != nil {
  261. return fmt.Errorf("failed to validate per chain query %d: %w", idx, err)
  262. }
  263. }
  264. return nil
  265. }
  266. // Equal verifies that two query requests are equal.
  267. func (left *QueryRequest) Equal(right *QueryRequest) bool {
  268. if left.Nonce != right.Nonce {
  269. return false
  270. }
  271. if len(left.PerChainQueries) != len(right.PerChainQueries) {
  272. return false
  273. }
  274. for idx := range left.PerChainQueries {
  275. if !left.PerChainQueries[idx].Equal(right.PerChainQueries[idx]) {
  276. return false
  277. }
  278. }
  279. return true
  280. }
  281. //
  282. // Implementation of PerChainQueryRequest.
  283. //
  284. // Marshal serializes the binary representation of a per chain query request.
  285. // This method calls Validate() and relies on it to range checks lengths, etc.
  286. func (perChainQuery *PerChainQueryRequest) Marshal() ([]byte, error) {
  287. if err := perChainQuery.Validate(); err != nil {
  288. return nil, err
  289. }
  290. buf := new(bytes.Buffer)
  291. vaa.MustWrite(buf, binary.BigEndian, perChainQuery.ChainId)
  292. vaa.MustWrite(buf, binary.BigEndian, perChainQuery.Query.Type())
  293. queryBuf, err := perChainQuery.Query.Marshal()
  294. if err != nil {
  295. return nil, err
  296. }
  297. // Write the length of the query to facilitate on-chain parsing.
  298. if len(queryBuf) > math.MaxUint32 {
  299. return nil, fmt.Errorf("query too long")
  300. }
  301. vaa.MustWrite(buf, binary.BigEndian, uint32(len(queryBuf))) // #nosec G115 -- This conversion is safe as it is checked above
  302. buf.Write(queryBuf)
  303. return buf.Bytes(), nil
  304. }
  305. // Unmarshal deserializes the binary representation of a per chain query request from a byte array
  306. func (perChainQuery *PerChainQueryRequest) Unmarshal(data []byte) error {
  307. reader := bytes.NewReader(data[:])
  308. return perChainQuery.UnmarshalFromReader(reader)
  309. }
  310. // UnmarshalFromReader deserializes the binary representation of a per chain query request from an existing reader
  311. func (perChainQuery *PerChainQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  312. if err := binary.Read(reader, binary.BigEndian, &perChainQuery.ChainId); err != nil {
  313. return fmt.Errorf("failed to read request chain: %w", err)
  314. }
  315. qt := uint8(0)
  316. if err := binary.Read(reader, binary.BigEndian, &qt); err != nil {
  317. return fmt.Errorf("failed to read request type: %w", err)
  318. }
  319. queryType := ChainSpecificQueryType(qt)
  320. if err := ValidatePerChainQueryRequestType(queryType); err != nil {
  321. return err
  322. }
  323. // Skip the query length.
  324. var queryLength uint32
  325. if err := binary.Read(reader, binary.BigEndian, &queryLength); err != nil {
  326. return fmt.Errorf("failed to read query length: %w", err)
  327. }
  328. switch queryType {
  329. case EthCallQueryRequestType:
  330. q := EthCallQueryRequest{}
  331. if err := q.UnmarshalFromReader(reader); err != nil {
  332. return fmt.Errorf("failed to unmarshal eth call request: %w", err)
  333. }
  334. perChainQuery.Query = &q
  335. case EthCallByTimestampQueryRequestType:
  336. q := EthCallByTimestampQueryRequest{}
  337. if err := q.UnmarshalFromReader(reader); err != nil {
  338. return fmt.Errorf("failed to unmarshal eth call by timestamp request: %w", err)
  339. }
  340. perChainQuery.Query = &q
  341. case EthCallWithFinalityQueryRequestType:
  342. q := EthCallWithFinalityQueryRequest{}
  343. if err := q.UnmarshalFromReader(reader); err != nil {
  344. return fmt.Errorf("failed to unmarshal eth call with finality request: %w", err)
  345. }
  346. perChainQuery.Query = &q
  347. case SolanaAccountQueryRequestType:
  348. q := SolanaAccountQueryRequest{}
  349. if err := q.UnmarshalFromReader(reader); err != nil {
  350. return fmt.Errorf("failed to unmarshal solana account query request: %w", err)
  351. }
  352. perChainQuery.Query = &q
  353. case SolanaPdaQueryRequestType:
  354. q := SolanaPdaQueryRequest{}
  355. if err := q.UnmarshalFromReader(reader); err != nil {
  356. return fmt.Errorf("failed to unmarshal solana PDA query request: %w", err)
  357. }
  358. perChainQuery.Query = &q
  359. default:
  360. return fmt.Errorf("unsupported query type: %d", queryType)
  361. }
  362. return nil
  363. }
  364. // Validate does basic validation on a per chain query request.
  365. func (perChainQuery *PerChainQueryRequest) Validate() error {
  366. str := perChainQuery.ChainId.String()
  367. if _, err := vaa.ChainIDFromString(str); err != nil {
  368. return fmt.Errorf("invalid chainID: %d", uint16(perChainQuery.ChainId))
  369. }
  370. if perChainQuery.Query == nil {
  371. return fmt.Errorf("query is nil")
  372. }
  373. if err := ValidatePerChainQueryRequestType(perChainQuery.Query.Type()); err != nil {
  374. return err
  375. }
  376. if err := perChainQuery.Query.Validate(); err != nil {
  377. return fmt.Errorf("chain specific query is invalid: %w", err)
  378. }
  379. return nil
  380. }
  381. func ValidatePerChainQueryRequestType(qt ChainSpecificQueryType) error {
  382. if qt != EthCallQueryRequestType && qt != EthCallByTimestampQueryRequestType && qt != EthCallWithFinalityQueryRequestType &&
  383. qt != SolanaAccountQueryRequestType && qt != SolanaPdaQueryRequestType {
  384. return fmt.Errorf("invalid query request type: %d", qt)
  385. }
  386. return nil
  387. }
  388. // Equal verifies that two query requests are equal.
  389. func (left *PerChainQueryRequest) Equal(right *PerChainQueryRequest) bool {
  390. if left.ChainId != right.ChainId {
  391. return false
  392. }
  393. if left.Query == nil && right.Query == nil {
  394. return true
  395. }
  396. if left.Query == nil || right.Query == nil {
  397. return false
  398. }
  399. if left.Query.Type() != right.Query.Type() {
  400. return false
  401. }
  402. switch leftQuery := left.Query.(type) {
  403. case *EthCallQueryRequest:
  404. switch rightQuery := right.Query.(type) {
  405. case *EthCallQueryRequest:
  406. return leftQuery.Equal(rightQuery)
  407. default:
  408. panic("unsupported query type on right, must be eth_call")
  409. }
  410. case *EthCallByTimestampQueryRequest:
  411. switch rightQuery := right.Query.(type) {
  412. case *EthCallByTimestampQueryRequest:
  413. return leftQuery.Equal(rightQuery)
  414. default:
  415. panic("unsupported query type on right, must be eth_call_by_timestamp")
  416. }
  417. case *EthCallWithFinalityQueryRequest:
  418. switch rightQuery := right.Query.(type) {
  419. case *EthCallWithFinalityQueryRequest:
  420. return leftQuery.Equal(rightQuery)
  421. default:
  422. panic("unsupported query type on right, must be eth_call_with_finality")
  423. }
  424. case *SolanaAccountQueryRequest:
  425. switch rightQuery := right.Query.(type) {
  426. case *SolanaAccountQueryRequest:
  427. return leftQuery.Equal(rightQuery)
  428. default:
  429. panic("unsupported query type on right, must be sol_account")
  430. }
  431. case *SolanaPdaQueryRequest:
  432. switch rightQuery := right.Query.(type) {
  433. case *SolanaPdaQueryRequest:
  434. return leftQuery.Equal(rightQuery)
  435. default:
  436. panic("unsupported query type on right, must be sol_pda")
  437. }
  438. default:
  439. panic("unsupported query type on left")
  440. }
  441. }
  442. //
  443. // Implementation of EthCallQueryRequest, which implements the ChainSpecificQuery interface.
  444. //
  445. func (e *EthCallQueryRequest) Type() ChainSpecificQueryType {
  446. return EthCallQueryRequestType
  447. }
  448. // Marshal serializes the binary representation of an EVM eth_call request.
  449. // This method calls Validate() and relies on it to range checks lengths, etc.
  450. func (ecd *EthCallQueryRequest) Marshal() ([]byte, error) {
  451. if err := ecd.Validate(); err != nil {
  452. return nil, err
  453. }
  454. buf := new(bytes.Buffer)
  455. vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) // #nosec G115 -- This is validated in `Validate`
  456. buf.Write([]byte(ecd.BlockId))
  457. vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate`
  458. for _, callData := range ecd.CallData {
  459. buf.Write(callData.To)
  460. vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate`
  461. buf.Write(callData.Data)
  462. }
  463. return buf.Bytes(), nil
  464. }
  465. // Unmarshal deserializes an EVM eth_call query from a byte array
  466. func (ecd *EthCallQueryRequest) Unmarshal(data []byte) error {
  467. reader := bytes.NewReader(data[:])
  468. return ecd.UnmarshalFromReader(reader)
  469. }
  470. // UnmarshalFromReader deserializes an EVM eth_call query from a byte array
  471. func (ecd *EthCallQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  472. blockIdLen := uint32(0)
  473. if err := binary.Read(reader, binary.BigEndian, &blockIdLen); err != nil {
  474. return fmt.Errorf("failed to read block id len: %w", err)
  475. }
  476. blockId := make([]byte, blockIdLen)
  477. if n, err := reader.Read(blockId[:]); err != nil || n != int(blockIdLen) {
  478. return fmt.Errorf("failed to read block id [%d]: %w", n, err)
  479. }
  480. ecd.BlockId = string(blockId[:])
  481. numCallData := uint8(0)
  482. if err := binary.Read(reader, binary.BigEndian, &numCallData); err != nil {
  483. return fmt.Errorf("failed to read number of call data entries: %w", err)
  484. }
  485. for count := 0; count < int(numCallData); count++ {
  486. to := [EvmContractAddressLength]byte{}
  487. if n, err := reader.Read(to[:]); err != nil || n != EvmContractAddressLength {
  488. return fmt.Errorf("failed to read call To [%d]: %w", n, err)
  489. }
  490. dataLen := uint32(0)
  491. if err := binary.Read(reader, binary.BigEndian, &dataLen); err != nil {
  492. return fmt.Errorf("failed to read call Data len: %w", err)
  493. }
  494. data := make([]byte, dataLen)
  495. if n, err := reader.Read(data[:]); err != nil || n != int(dataLen) {
  496. return fmt.Errorf("failed to read call data [%d]: %w", n, err)
  497. }
  498. callData := &EthCallData{
  499. To: to[:],
  500. Data: data[:],
  501. }
  502. ecd.CallData = append(ecd.CallData, callData)
  503. }
  504. return nil
  505. }
  506. // Validate does basic validation on an EVM eth_call query.
  507. func (ecd *EthCallQueryRequest) Validate() error {
  508. if len(ecd.BlockId) > math.MaxUint32 {
  509. return fmt.Errorf("block id too long")
  510. }
  511. if !strings.HasPrefix(ecd.BlockId, "0x") {
  512. return fmt.Errorf("block id must be a hex number or hash starting with 0x")
  513. }
  514. if len(ecd.CallData) <= 0 {
  515. return fmt.Errorf("does not contain any call data")
  516. }
  517. if len(ecd.CallData) > math.MaxUint8 {
  518. return fmt.Errorf("too many call data entries")
  519. }
  520. for _, callData := range ecd.CallData {
  521. if callData.To == nil || len(callData.To) <= 0 {
  522. return fmt.Errorf("no call data to")
  523. }
  524. if len(callData.To) != EvmContractAddressLength {
  525. return fmt.Errorf("invalid length for To contract")
  526. }
  527. //nolint:dupword // callData.Data is fine in the context of EVM.
  528. if callData.Data == nil || len(callData.Data) <= 0 {
  529. return fmt.Errorf("no call data data")
  530. }
  531. //nolint:dupword // callData.Data is fine in the context of EVM.
  532. if len(callData.Data) > math.MaxUint32 {
  533. return fmt.Errorf("call data data too long")
  534. }
  535. }
  536. return nil
  537. }
  538. // Equal verifies that two EVM eth_call queries are equal.
  539. func (left *EthCallQueryRequest) Equal(right *EthCallQueryRequest) bool {
  540. if left.BlockId != right.BlockId {
  541. return false
  542. }
  543. if len(left.CallData) != len(right.CallData) {
  544. return false
  545. }
  546. for idx := range left.CallData {
  547. if !bytes.Equal(left.CallData[idx].To, right.CallData[idx].To) {
  548. return false
  549. }
  550. if !bytes.Equal(left.CallData[idx].Data, right.CallData[idx].Data) {
  551. return false
  552. }
  553. }
  554. return true
  555. }
  556. //
  557. // Implementation of EthCallByTimestampQueryRequest, which implements the ChainSpecificQuery interface.
  558. //
  559. func (e *EthCallByTimestampQueryRequest) Type() ChainSpecificQueryType {
  560. return EthCallByTimestampQueryRequestType
  561. }
  562. // Marshal serializes the binary representation of an EVM eth_call_by_timestamp request.
  563. // This method calls Validate() and relies on it to range checks lengths, etc.
  564. func (ecd *EthCallByTimestampQueryRequest) Marshal() ([]byte, error) {
  565. if err := ecd.Validate(); err != nil {
  566. return nil, err
  567. }
  568. buf := new(bytes.Buffer)
  569. vaa.MustWrite(buf, binary.BigEndian, ecd.TargetTimestamp)
  570. vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.TargetBlockIdHint))) // #nosec G115 -- This is validated in `Validate`
  571. buf.Write([]byte(ecd.TargetBlockIdHint))
  572. vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.FollowingBlockIdHint))) // #nosec G115 -- This is validated in `Validate`
  573. buf.Write([]byte(ecd.FollowingBlockIdHint))
  574. vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate`
  575. for _, callData := range ecd.CallData {
  576. buf.Write(callData.To)
  577. vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate`
  578. buf.Write(callData.Data)
  579. }
  580. return buf.Bytes(), nil
  581. }
  582. // Unmarshal deserializes an EVM eth_call_by_timestamp query from a byte array
  583. func (ecd *EthCallByTimestampQueryRequest) Unmarshal(data []byte) error {
  584. reader := bytes.NewReader(data[:])
  585. return ecd.UnmarshalFromReader(reader)
  586. }
  587. // UnmarshalFromReader deserializes an EVM eth_call_by_timestamp query from a byte array
  588. func (ecd *EthCallByTimestampQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  589. if err := binary.Read(reader, binary.BigEndian, &ecd.TargetTimestamp); err != nil {
  590. return fmt.Errorf("failed to read timestamp: %w", err)
  591. }
  592. blockIdHintLen := uint32(0)
  593. if err := binary.Read(reader, binary.BigEndian, &blockIdHintLen); err != nil {
  594. return fmt.Errorf("failed to read target block id hint len: %w", err)
  595. }
  596. targetBlockIdHint := make([]byte, blockIdHintLen)
  597. if n, err := reader.Read(targetBlockIdHint[:]); err != nil || n != int(blockIdHintLen) {
  598. return fmt.Errorf("failed to read target block id hint [%d]: %w", n, err)
  599. }
  600. ecd.TargetBlockIdHint = string(targetBlockIdHint[:])
  601. blockIdHintLen = uint32(0)
  602. if err := binary.Read(reader, binary.BigEndian, &blockIdHintLen); err != nil {
  603. return fmt.Errorf("failed to read following block id hint len: %w", err)
  604. }
  605. followingBlockIdHint := make([]byte, blockIdHintLen)
  606. if n, err := reader.Read(followingBlockIdHint[:]); err != nil || n != int(blockIdHintLen) {
  607. return fmt.Errorf("failed to read following block id hint [%d]: %w", n, err)
  608. }
  609. ecd.FollowingBlockIdHint = string(followingBlockIdHint[:])
  610. numCallData := uint8(0)
  611. if err := binary.Read(reader, binary.BigEndian, &numCallData); err != nil {
  612. return fmt.Errorf("failed to read number of call data entries: %w", err)
  613. }
  614. for count := 0; count < int(numCallData); count++ {
  615. to := [EvmContractAddressLength]byte{}
  616. if n, err := reader.Read(to[:]); err != nil || n != EvmContractAddressLength {
  617. return fmt.Errorf("failed to read call To [%d]: %w", n, err)
  618. }
  619. dataLen := uint32(0)
  620. if err := binary.Read(reader, binary.BigEndian, &dataLen); err != nil {
  621. return fmt.Errorf("failed to read call Data len: %w", err)
  622. }
  623. data := make([]byte, dataLen)
  624. if n, err := reader.Read(data[:]); err != nil || n != int(dataLen) {
  625. return fmt.Errorf("failed to read call data [%d]: %w", n, err)
  626. }
  627. callData := &EthCallData{
  628. To: to[:],
  629. Data: data[:],
  630. }
  631. ecd.CallData = append(ecd.CallData, callData)
  632. }
  633. return nil
  634. }
  635. // Validate does basic validation on an EVM eth_call_by_timestamp query.
  636. func (ecd *EthCallByTimestampQueryRequest) Validate() error {
  637. if ecd.TargetTimestamp == 0 {
  638. return fmt.Errorf("target timestamp may not be zero")
  639. }
  640. if len(ecd.TargetBlockIdHint) > math.MaxUint32 {
  641. return fmt.Errorf("target block id hint too long")
  642. }
  643. if (ecd.TargetBlockIdHint == "") != (ecd.FollowingBlockIdHint == "") {
  644. return fmt.Errorf("if either the target or following block id is unset, they both must be unset")
  645. }
  646. if ecd.TargetBlockIdHint != "" && !strings.HasPrefix(ecd.TargetBlockIdHint, "0x") {
  647. return fmt.Errorf("target block id must be a hex number or hash starting with 0x")
  648. }
  649. if len(ecd.FollowingBlockIdHint) > math.MaxUint32 {
  650. return fmt.Errorf("following block id hint too long")
  651. }
  652. if ecd.FollowingBlockIdHint != "" && !strings.HasPrefix(ecd.FollowingBlockIdHint, "0x") {
  653. return fmt.Errorf("following block id must be a hex number or hash starting with 0x")
  654. }
  655. if len(ecd.CallData) <= 0 {
  656. return fmt.Errorf("does not contain any call data")
  657. }
  658. if len(ecd.CallData) > math.MaxUint8 {
  659. return fmt.Errorf("too many call data entries")
  660. }
  661. for _, callData := range ecd.CallData {
  662. if callData.To == nil || len(callData.To) <= 0 {
  663. return fmt.Errorf("no call data to")
  664. }
  665. if len(callData.To) != EvmContractAddressLength {
  666. return fmt.Errorf("invalid length for To contract")
  667. }
  668. //nolint:dupword // callData.Data is fine in the context of EVM.
  669. if callData.Data == nil || len(callData.Data) <= 0 {
  670. return fmt.Errorf("no call data data")
  671. }
  672. //nolint:dupword // callData.Data is fine in the context of EVM.
  673. if len(callData.Data) > math.MaxUint32 {
  674. return fmt.Errorf("call data data too long")
  675. }
  676. }
  677. return nil
  678. }
  679. // Equal verifies that two EVM eth_call_by_timestamp queries are equal.
  680. func (left *EthCallByTimestampQueryRequest) Equal(right *EthCallByTimestampQueryRequest) bool {
  681. if left.TargetTimestamp != right.TargetTimestamp {
  682. return false
  683. }
  684. if left.TargetBlockIdHint != right.TargetBlockIdHint {
  685. return false
  686. }
  687. if left.FollowingBlockIdHint != right.FollowingBlockIdHint {
  688. return false
  689. }
  690. if len(left.CallData) != len(right.CallData) {
  691. return false
  692. }
  693. for idx := range left.CallData {
  694. if !bytes.Equal(left.CallData[idx].To, right.CallData[idx].To) {
  695. return false
  696. }
  697. if !bytes.Equal(left.CallData[idx].Data, right.CallData[idx].Data) {
  698. return false
  699. }
  700. }
  701. return true
  702. }
  703. //
  704. // Implementation of EthCallWithFinalityQueryRequest, which implements the ChainSpecificQuery interface.
  705. //
  706. func (e *EthCallWithFinalityQueryRequest) Type() ChainSpecificQueryType {
  707. return EthCallWithFinalityQueryRequestType
  708. }
  709. // Marshal serializes the binary representation of an EVM eth_call_with_finality request.
  710. // This method calls Validate() and relies on it to range checks lengths, etc.
  711. func (ecd *EthCallWithFinalityQueryRequest) Marshal() ([]byte, error) {
  712. if err := ecd.Validate(); err != nil {
  713. return nil, err
  714. }
  715. buf := new(bytes.Buffer)
  716. vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.BlockId))) // #nosec G115 -- This is validated in `Validate`
  717. buf.Write([]byte(ecd.BlockId))
  718. vaa.MustWrite(buf, binary.BigEndian, uint32(len(ecd.Finality))) // #nosec G115 -- This is validated in `Validate`
  719. buf.Write([]byte(ecd.Finality))
  720. vaa.MustWrite(buf, binary.BigEndian, uint8(len(ecd.CallData))) // #nosec G115 -- This is validated in `Validate`
  721. for _, callData := range ecd.CallData {
  722. buf.Write(callData.To)
  723. vaa.MustWrite(buf, binary.BigEndian, uint32(len(callData.Data))) // #nosec G115 -- This is validated in `Validate`
  724. buf.Write(callData.Data)
  725. }
  726. return buf.Bytes(), nil
  727. }
  728. // Unmarshal deserializes an EVM eth_call_with_finality query from a byte array
  729. func (ecd *EthCallWithFinalityQueryRequest) Unmarshal(data []byte) error {
  730. reader := bytes.NewReader(data[:])
  731. return ecd.UnmarshalFromReader(reader)
  732. }
  733. // UnmarshalFromReader deserializes an EVM eth_call_with_finality query from a byte array
  734. func (ecd *EthCallWithFinalityQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  735. blockIdLen := uint32(0)
  736. if err := binary.Read(reader, binary.BigEndian, &blockIdLen); err != nil {
  737. return fmt.Errorf("failed to read target block id len: %w", err)
  738. }
  739. blockId := make([]byte, blockIdLen)
  740. if n, err := reader.Read(blockId[:]); err != nil || n != int(blockIdLen) {
  741. return fmt.Errorf("failed to read target block id [%d]: %w", n, err)
  742. }
  743. ecd.BlockId = string(blockId[:])
  744. finalityLen := uint32(0)
  745. if err := binary.Read(reader, binary.BigEndian, &finalityLen); err != nil {
  746. return fmt.Errorf("failed to read finality len: %w", err)
  747. }
  748. finality := make([]byte, finalityLen)
  749. if n, err := reader.Read(finality[:]); err != nil || n != int(finalityLen) {
  750. return fmt.Errorf("failed to read finality [%d]: %w", n, err)
  751. }
  752. ecd.Finality = string(finality[:])
  753. numCallData := uint8(0)
  754. if err := binary.Read(reader, binary.BigEndian, &numCallData); err != nil {
  755. return fmt.Errorf("failed to read number of call data entries: %w", err)
  756. }
  757. for count := 0; count < int(numCallData); count++ {
  758. to := [EvmContractAddressLength]byte{}
  759. if n, err := reader.Read(to[:]); err != nil || n != EvmContractAddressLength {
  760. return fmt.Errorf("failed to read call To [%d]: %w", n, err)
  761. }
  762. dataLen := uint32(0)
  763. if err := binary.Read(reader, binary.BigEndian, &dataLen); err != nil {
  764. return fmt.Errorf("failed to read call Data len: %w", err)
  765. }
  766. data := make([]byte, dataLen)
  767. if n, err := reader.Read(data[:]); err != nil || n != int(dataLen) {
  768. return fmt.Errorf("failed to read call data [%d]: %w", n, err)
  769. }
  770. callData := &EthCallData{
  771. To: to[:],
  772. Data: data[:],
  773. }
  774. ecd.CallData = append(ecd.CallData, callData)
  775. }
  776. return nil
  777. }
  778. // Validate does basic validation on an EVM eth_call_with_finality query.
  779. func (ecd *EthCallWithFinalityQueryRequest) Validate() error {
  780. if len(ecd.BlockId) > math.MaxUint32 {
  781. return fmt.Errorf("block id too long")
  782. }
  783. if ecd.BlockId == "" {
  784. return fmt.Errorf("block id is required")
  785. }
  786. if !strings.HasPrefix(ecd.BlockId, "0x") {
  787. return fmt.Errorf("block id must be a hex number or hash starting with 0x")
  788. }
  789. if len(ecd.Finality) > math.MaxUint32 {
  790. return fmt.Errorf("finality too long")
  791. }
  792. if ecd.Finality == "" {
  793. return fmt.Errorf("finality is required")
  794. }
  795. if ecd.Finality != "finalized" && ecd.Finality != "safe" {
  796. return fmt.Errorf(`finality must be "finalized" or "safe", is "%s"`, ecd.Finality)
  797. }
  798. if len(ecd.CallData) <= 0 {
  799. return fmt.Errorf("does not contain any call data")
  800. }
  801. if len(ecd.CallData) > math.MaxUint8 {
  802. return fmt.Errorf("too many call data entries")
  803. }
  804. for _, callData := range ecd.CallData {
  805. if callData.To == nil || len(callData.To) <= 0 {
  806. return fmt.Errorf("no call data to")
  807. }
  808. if len(callData.To) != EvmContractAddressLength {
  809. return fmt.Errorf("invalid length for To contract")
  810. }
  811. //nolint:dupword // callData.Data is fine in the context of EVM.
  812. if callData.Data == nil || len(callData.Data) <= 0 {
  813. return fmt.Errorf("no call data data")
  814. }
  815. //nolint:dupword // callData.Data is fine in the context of EVM.
  816. if len(callData.Data) > math.MaxUint32 {
  817. return fmt.Errorf("call data data too long")
  818. }
  819. }
  820. return nil
  821. }
  822. // Equal verifies that two EVM eth_call_with_finality queries are equal.
  823. func (left *EthCallWithFinalityQueryRequest) Equal(right *EthCallWithFinalityQueryRequest) bool {
  824. if left.BlockId != right.BlockId {
  825. return false
  826. }
  827. if left.Finality != right.Finality {
  828. return false
  829. }
  830. if len(left.CallData) != len(right.CallData) {
  831. return false
  832. }
  833. for idx := range left.CallData {
  834. if !bytes.Equal(left.CallData[idx].To, right.CallData[idx].To) {
  835. return false
  836. }
  837. if !bytes.Equal(left.CallData[idx].Data, right.CallData[idx].Data) {
  838. return false
  839. }
  840. }
  841. return true
  842. }
  843. //
  844. // Implementation of SolanaAccountQueryRequest, which implements the ChainSpecificQuery interface.
  845. //
  846. func (e *SolanaAccountQueryRequest) Type() ChainSpecificQueryType {
  847. return SolanaAccountQueryRequestType
  848. }
  849. // Marshal serializes the binary representation of a Solana sol_account request.
  850. // This method calls Validate() and relies on it to range checks lengths, etc.
  851. func (saq *SolanaAccountQueryRequest) Marshal() ([]byte, error) {
  852. if err := saq.Validate(); err != nil {
  853. return nil, err
  854. }
  855. buf := new(bytes.Buffer)
  856. vaa.MustWrite(buf, binary.BigEndian, uint32(len(saq.Commitment))) // #nosec G115 -- This is validated in `Validate`
  857. buf.Write([]byte(saq.Commitment))
  858. vaa.MustWrite(buf, binary.BigEndian, saq.MinContextSlot)
  859. vaa.MustWrite(buf, binary.BigEndian, saq.DataSliceOffset)
  860. vaa.MustWrite(buf, binary.BigEndian, saq.DataSliceLength)
  861. vaa.MustWrite(buf, binary.BigEndian, uint8(len(saq.Accounts))) // #nosec G115 -- This is validated in `Validate`
  862. for _, acct := range saq.Accounts {
  863. buf.Write(acct[:])
  864. }
  865. return buf.Bytes(), nil
  866. }
  867. // Unmarshal deserializes a Solana sol_account query from a byte array
  868. func (saq *SolanaAccountQueryRequest) Unmarshal(data []byte) error {
  869. reader := bytes.NewReader(data[:])
  870. return saq.UnmarshalFromReader(reader)
  871. }
  872. // UnmarshalFromReader deserializes a Solana sol_account query from a byte array
  873. func (saq *SolanaAccountQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  874. length := uint32(0)
  875. if err := binary.Read(reader, binary.BigEndian, &length); err != nil {
  876. return fmt.Errorf("failed to read commitment len: %w", err)
  877. }
  878. if length > SolanaMaxCommitmentLength {
  879. return fmt.Errorf("commitment string is too long, may not be more than %d characters", SolanaMaxCommitmentLength)
  880. }
  881. commitment := make([]byte, length)
  882. if n, err := reader.Read(commitment[:]); err != nil || n != int(length) {
  883. return fmt.Errorf("failed to read commitment [%d]: %w", n, err)
  884. }
  885. saq.Commitment = string(commitment)
  886. if err := binary.Read(reader, binary.BigEndian, &saq.MinContextSlot); err != nil {
  887. return fmt.Errorf("failed to read min slot: %w", err)
  888. }
  889. if err := binary.Read(reader, binary.BigEndian, &saq.DataSliceOffset); err != nil {
  890. return fmt.Errorf("failed to read data slice offset: %w", err)
  891. }
  892. if err := binary.Read(reader, binary.BigEndian, &saq.DataSliceLength); err != nil {
  893. return fmt.Errorf("failed to read data slice length: %w", err)
  894. }
  895. numAccounts := uint8(0)
  896. if err := binary.Read(reader, binary.BigEndian, &numAccounts); err != nil {
  897. return fmt.Errorf("failed to read number of account entries: %w", err)
  898. }
  899. for count := 0; count < int(numAccounts); count++ {
  900. account := [SolanaPublicKeyLength]byte{}
  901. if n, err := reader.Read(account[:]); err != nil || n != SolanaPublicKeyLength {
  902. return fmt.Errorf("failed to read account [%d]: %w", n, err)
  903. }
  904. saq.Accounts = append(saq.Accounts, account)
  905. }
  906. return nil
  907. }
  908. // Validate does basic validation on a Solana sol_account query.
  909. func (saq *SolanaAccountQueryRequest) Validate() error {
  910. if len(saq.Commitment) > SolanaMaxCommitmentLength {
  911. return fmt.Errorf("commitment too long")
  912. }
  913. if saq.Commitment != "finalized" {
  914. return fmt.Errorf(`commitment must be "finalized"`)
  915. }
  916. if saq.DataSliceLength == 0 && saq.DataSliceOffset != 0 {
  917. return fmt.Errorf("data slice offset may not be set if data slice length is zero")
  918. }
  919. if len(saq.Accounts) <= 0 {
  920. return fmt.Errorf("does not contain any account entries")
  921. }
  922. if len(saq.Accounts) > SolanaMaxAccountsPerQuery {
  923. return fmt.Errorf("too many account entries, may not be more than %d", SolanaMaxAccountsPerQuery)
  924. }
  925. for _, acct := range saq.Accounts {
  926. // The account is fixed length, so don't need to check for nil.
  927. if len(acct) != SolanaPublicKeyLength {
  928. return fmt.Errorf("invalid account length")
  929. }
  930. }
  931. return nil
  932. }
  933. // Equal verifies that two Solana sol_account queries are equal.
  934. func (left *SolanaAccountQueryRequest) Equal(right *SolanaAccountQueryRequest) bool {
  935. if left.Commitment != right.Commitment ||
  936. left.MinContextSlot != right.MinContextSlot ||
  937. left.DataSliceOffset != right.DataSliceOffset ||
  938. left.DataSliceLength != right.DataSliceLength {
  939. return false
  940. }
  941. if len(left.Accounts) != len(right.Accounts) {
  942. return false
  943. }
  944. for idx := range left.Accounts {
  945. if !bytes.Equal(left.Accounts[idx][:], right.Accounts[idx][:]) {
  946. return false
  947. }
  948. }
  949. return true
  950. }
  951. //
  952. // Implementation of SolanaPdaQueryRequest, which implements the ChainSpecificQuery interface.
  953. //
  954. func (e *SolanaPdaQueryRequest) Type() ChainSpecificQueryType {
  955. return SolanaPdaQueryRequestType
  956. }
  957. // Marshal serializes the binary representation of a Solana sol_pda request.
  958. // This method calls Validate() and relies on it to range checks lengths, etc.
  959. func (spda *SolanaPdaQueryRequest) Marshal() ([]byte, error) {
  960. if err := spda.Validate(); err != nil {
  961. return nil, err
  962. }
  963. buf := new(bytes.Buffer)
  964. vaa.MustWrite(buf, binary.BigEndian, uint32(len(spda.Commitment))) // #nosec G115 -- This is validated in `Validate`
  965. buf.Write([]byte(spda.Commitment))
  966. vaa.MustWrite(buf, binary.BigEndian, spda.MinContextSlot)
  967. vaa.MustWrite(buf, binary.BigEndian, spda.DataSliceOffset)
  968. vaa.MustWrite(buf, binary.BigEndian, spda.DataSliceLength)
  969. vaa.MustWrite(buf, binary.BigEndian, uint8(len(spda.PDAs))) // #nosec G115 -- This is validated in `Validate`
  970. for _, pda := range spda.PDAs {
  971. buf.Write(pda.ProgramAddress[:])
  972. vaa.MustWrite(buf, binary.BigEndian, uint8(len(pda.Seeds))) // #nosec G115 -- This is validated in `Validate`
  973. for _, seed := range pda.Seeds {
  974. vaa.MustWrite(buf, binary.BigEndian, uint32(len(seed))) // #nosec G115 -- This is validated in `Validate`
  975. buf.Write(seed)
  976. }
  977. }
  978. return buf.Bytes(), nil
  979. }
  980. // Unmarshal deserializes a Solana sol_pda query from a byte array
  981. func (spda *SolanaPdaQueryRequest) Unmarshal(data []byte) error {
  982. reader := bytes.NewReader(data[:])
  983. return spda.UnmarshalFromReader(reader)
  984. }
  985. // UnmarshalFromReader deserializes a Solana sol_pda query from a byte array
  986. func (spda *SolanaPdaQueryRequest) UnmarshalFromReader(reader *bytes.Reader) error {
  987. length := uint32(0)
  988. if err := binary.Read(reader, binary.BigEndian, &length); err != nil {
  989. return fmt.Errorf("failed to read commitment len: %w", err)
  990. }
  991. if length > SolanaMaxCommitmentLength {
  992. return fmt.Errorf("commitment string is too long, may not be more than %d characters", SolanaMaxCommitmentLength)
  993. }
  994. commitment := make([]byte, length)
  995. if n, err := reader.Read(commitment[:]); err != nil || n != int(length) {
  996. return fmt.Errorf("failed to read commitment [%d]: %w", n, err)
  997. }
  998. spda.Commitment = string(commitment)
  999. if err := binary.Read(reader, binary.BigEndian, &spda.MinContextSlot); err != nil {
  1000. return fmt.Errorf("failed to read min slot: %w", err)
  1001. }
  1002. if err := binary.Read(reader, binary.BigEndian, &spda.DataSliceOffset); err != nil {
  1003. return fmt.Errorf("failed to read data slice offset: %w", err)
  1004. }
  1005. if err := binary.Read(reader, binary.BigEndian, &spda.DataSliceLength); err != nil {
  1006. return fmt.Errorf("failed to read data slice length: %w", err)
  1007. }
  1008. numPDAs := uint8(0)
  1009. if err := binary.Read(reader, binary.BigEndian, &numPDAs); err != nil {
  1010. return fmt.Errorf("failed to read number of PDAs: %w", err)
  1011. }
  1012. for count := 0; count < int(numPDAs); count++ {
  1013. programAddress := [SolanaPublicKeyLength]byte{}
  1014. if n, err := reader.Read(programAddress[:]); err != nil || n != SolanaPublicKeyLength {
  1015. return fmt.Errorf("failed to read program address [%d]: %w", n, err)
  1016. }
  1017. pda := SolanaPDAEntry{ProgramAddress: programAddress}
  1018. numSeeds := uint8(0)
  1019. if err := binary.Read(reader, binary.BigEndian, &numSeeds); err != nil {
  1020. return fmt.Errorf("failed to read number of seeds: %w", err)
  1021. }
  1022. for count := 0; count < int(numSeeds); count++ {
  1023. seedLen := uint32(0)
  1024. if err := binary.Read(reader, binary.BigEndian, &seedLen); err != nil {
  1025. return fmt.Errorf("failed to read call Data len: %w", err)
  1026. }
  1027. seed := make([]byte, seedLen)
  1028. if n, err := reader.Read(seed[:]); err != nil || n != int(seedLen) {
  1029. return fmt.Errorf("failed to read seed [%d]: %w", n, err)
  1030. }
  1031. pda.Seeds = append(pda.Seeds, seed)
  1032. }
  1033. spda.PDAs = append(spda.PDAs, pda)
  1034. }
  1035. return nil
  1036. }
  1037. // Validate does basic validation on a Solana sol_pda query.
  1038. func (spda *SolanaPdaQueryRequest) Validate() error {
  1039. if len(spda.Commitment) > SolanaMaxCommitmentLength {
  1040. return fmt.Errorf("commitment too long")
  1041. }
  1042. if spda.Commitment != "finalized" {
  1043. return fmt.Errorf(`commitment must be "finalized"`)
  1044. }
  1045. if spda.DataSliceLength == 0 && spda.DataSliceOffset != 0 {
  1046. return fmt.Errorf("data slice offset may not be set if data slice length is zero")
  1047. }
  1048. if len(spda.PDAs) <= 0 {
  1049. return fmt.Errorf("does not contain any PDAs entries")
  1050. }
  1051. if len(spda.PDAs) > SolanaMaxAccountsPerQuery {
  1052. return fmt.Errorf("too many PDA entries, may not be more than %d", SolanaMaxAccountsPerQuery)
  1053. }
  1054. for _, pda := range spda.PDAs {
  1055. // The program address is fixed length, so don't need to check for nil.
  1056. if len(pda.ProgramAddress) != SolanaPublicKeyLength {
  1057. return fmt.Errorf("invalid program address length")
  1058. }
  1059. if len(pda.Seeds) == 0 {
  1060. return fmt.Errorf("PDA does not contain any seeds")
  1061. }
  1062. if len(pda.Seeds) > SolanaMaxSeeds {
  1063. return fmt.Errorf("PDA contains too many seeds")
  1064. }
  1065. for _, seed := range pda.Seeds {
  1066. if len(seed) == 0 {
  1067. return fmt.Errorf("seed is null")
  1068. }
  1069. if len(seed) > SolanaMaxSeedLen {
  1070. return fmt.Errorf("seed is too long")
  1071. }
  1072. }
  1073. }
  1074. return nil
  1075. }
  1076. // Equal verifies that two Solana sol_pda queries are equal.
  1077. func (left *SolanaPdaQueryRequest) Equal(right *SolanaPdaQueryRequest) bool {
  1078. if left.Commitment != right.Commitment ||
  1079. left.MinContextSlot != right.MinContextSlot ||
  1080. left.DataSliceOffset != right.DataSliceOffset ||
  1081. left.DataSliceLength != right.DataSliceLength {
  1082. return false
  1083. }
  1084. if len(left.PDAs) != len(right.PDAs) {
  1085. return false
  1086. }
  1087. for idx := range left.PDAs {
  1088. if !bytes.Equal(left.PDAs[idx].ProgramAddress[:], right.PDAs[idx].ProgramAddress[:]) {
  1089. return false
  1090. }
  1091. if len(left.PDAs[idx].Seeds) != len(right.PDAs[idx].Seeds) {
  1092. return false
  1093. }
  1094. for idx2 := range left.PDAs[idx].Seeds {
  1095. if !bytes.Equal(left.PDAs[idx].Seeds[idx2][:], right.PDAs[idx].Seeds[idx2][:]) {
  1096. return false
  1097. }
  1098. }
  1099. }
  1100. return true
  1101. }