From 1b777a6e93ac27a94f526ae417c2ef0e167723ad Mon Sep 17 00:00:00 2001 From: gallynaut Date: Thu, 21 Sep 2023 14:38:42 -0600 Subject: [PATCH] chore: pull monorepo changes (#147) * Project import generated by Copybara. GitOrigin-RevId: cd4e94f983d8c7d3596f88ac821a5bd49c5bbf54 * manual fix --------- Co-authored-by: Copybara --- .../functions/01_basic_oracle/package.json | 2 +- .../switchboard-function/Cargo.lock | 63 +++- .../switchboard-function/Cargo.toml | 4 +- .../switchboard-function/src/main.rs | 27 +- .../solana.js/src/accounts/functionAccount.ts | 69 ++++- pnpm-lock.yaml | 100 +++---- rust/switchboard-solana/Cargo.anchor27.lock | 4 +- rust/switchboard-solana/Cargo.anchor27.toml | 6 +- rust/switchboard-solana/Cargo.lock | 4 +- rust/switchboard-solana/Cargo.toml | 6 +- .../attestation_program/accounts/function.rs | 5 +- .../accounts/request.anchor27.rs | 269 +++++++++++++++++- .../attestation_program/accounts/request.rs | 142 ++++++++- .../instructions/function_verify.rs | 10 +- .../attestation_program/instructions/mod.rs | 2 +- .../instructions/request_close.rs | 21 +- .../instructions/request_verify.rs | 2 +- .../src/client/function_runner.rs | 47 ++- rust/switchboard-solana/src/decimal.rs | 4 +- rust/switchboard-solana/src/lib.rs | 9 +- rust/switchboard-solana/src/macros.rs | 23 +- .../src/oracle_program/accounts/aggregator.rs | 2 +- .../oracle_program/accounts/buffer_relayer.rs | 18 +- .../src/oracle_program/accounts/ecvrf.rs | 56 ++-- .../oracle_program/accounts/history_buffer.rs | 2 +- rust/switchboard-solana/src/prelude.rs | 3 +- 26 files changed, 716 insertions(+), 184 deletions(-) diff --git a/examples/functions/01_basic_oracle/package.json b/examples/functions/01_basic_oracle/package.json index 9a555e3ad..6ce9909cc 100644 --- a/examples/functions/01_basic_oracle/package.json +++ b/examples/functions/01_basic_oracle/package.json @@ -15,7 +15,7 @@ "@coral-xyz/anchor": "^0.28.0", "@solana/spl-token": "^0.3.6", "@solana/web3.js": "^1.78.0", - "@switchboard-xyz/solana.js": "workspace:^" + "@switchboard-xyz/solana.js": "workspace:*" }, "devDependencies": { "@types/bn.js": "^5.1.0", diff --git a/examples/functions/01_basic_oracle/switchboard-function/Cargo.lock b/examples/functions/01_basic_oracle/switchboard-function/Cargo.lock index e69c0dbd1..9b829883f 100644 --- a/examples/functions/01_basic_oracle/switchboard-function/Cargo.lock +++ b/examples/functions/01_basic_oracle/switchboard-function/Cargo.lock @@ -593,7 +593,7 @@ dependencies = [ "futures", "serde", "serde_json", - "switchboard-solana", + "switchboard-solana 0.28.20", "switchboard-utils", "tokio", ] @@ -603,7 +603,7 @@ name = "basic_oracle" version = "0.1.0" dependencies = [ "bytemuck", - "switchboard-solana", + "switchboard-solana 0.28.20 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1435,6 +1435,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -4514,10 +4523,24 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "switchboard-common" -version = "0.8.6" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa7b85e2005ce058c626ce0d4d618dc18ca8f45a24bd66f3e11a6bf43f20082" +checksum = "1c9e28848b864786d8b1835f1f0c4c7e65190d9c17b246e5e21cc2457a70b176" dependencies = [ + "envy", + "getrandom 0.2.10", + "hex", + "serde", + "serde_json", + "sgx-quote", + "sha2 0.10.7", +] + +[[package]] +name = "switchboard-common" +version = "0.8.19" +dependencies = [ + "envy", "getrandom 0.2.10", "hex", "serde", @@ -4528,9 +4551,32 @@ dependencies = [ [[package]] name = "switchboard-solana" -version = "0.28.0" +version = "0.28.20" +dependencies = [ + "anchor-client", + "anchor-lang", + "anchor-spl", + "bincode", + "bytemuck", + "chrono", + "cron", + "hex", + "rust_decimal", + "sgx-quote", + "solana-address-lookup-table-program", + "solana-client", + "solana-program", + "superslice", + "switchboard-common 0.8.19", + "tokio", + "url", +] + +[[package]] +name = "switchboard-solana" +version = "0.28.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793a8429875a9386f07c10324119dfd20455cd9f22ee4a101a4731e94d122184" +checksum = "2a978513e0d3f54444698efb1737c8bcf366e2131e03ccfdf24c22210b8d755c" dependencies = [ "anchor-client", "anchor-lang", @@ -4546,8 +4592,9 @@ dependencies = [ "solana-client", "solana-program", "superslice", - "switchboard-common", + "switchboard-common 0.8.18", "tokio", + "url", ] [[package]] @@ -4567,7 +4614,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "switchboard-common", + "switchboard-common 0.8.18", "tokio", "url", ] diff --git a/examples/functions/01_basic_oracle/switchboard-function/Cargo.toml b/examples/functions/01_basic_oracle/switchboard-function/Cargo.toml index 3c1f9564b..6228abe9e 100644 --- a/examples/functions/01_basic_oracle/switchboard-function/Cargo.toml +++ b/examples/functions/01_basic_oracle/switchboard-function/Cargo.toml @@ -16,6 +16,6 @@ futures = "0.3" serde = "^1" serde_json = "^1" switchboard-utils = "0.8.0" -switchboard-solana = { version = "0.28.19" } -# switchboard-solana = { version = "0.28.19", path = "../../../../rust/switchboard-solana" } +# switchboard-solana = { version = "0.28.19" } +switchboard-solana = { version = "0.28.19", path = "../../../../rust/switchboard-solana" } # switchboard-utils = { version = "0.8.0", path = "../../../../../../rust/switchboard-utils" } diff --git a/examples/functions/01_basic_oracle/switchboard-function/src/main.rs b/examples/functions/01_basic_oracle/switchboard-function/src/main.rs index 3983807f6..f2bd0a671 100644 --- a/examples/functions/01_basic_oracle/switchboard-function/src/main.rs +++ b/examples/functions/01_basic_oracle/switchboard-function/src/main.rs @@ -3,26 +3,41 @@ pub use switchboard_solana::prelude::*; pub mod binance; pub use binance::*; +type Result = std::result::Result>; + pub use basic_oracle::{ self, OracleData, OracleDataWithTradingSymbol, RefreshPrices, RefreshPricesParams, SwitchboardDecimal, TradingSymbol, ID as PROGRAM_ID, }; -#[tokio::main(worker_threads = 12)] -async fn main() { - // First, initialize the runner instance with a freshly generated Gramine keypair - let runner: FunctionRunner = FunctionRunner::from_env(None).unwrap(); +pub async fn perform(runner: &FunctionRunner) -> Result<()> { msg!("function runner loaded!"); // Then, write your own Rust logic and build a Vec of instructions. // Should be under 700 bytes after serialization - let binance = Binance::fetch().await.unwrap(); + let binance = Binance::fetch().await?; let ixs: Vec = binance.to_ixns(&runner); msg!("sending transaction"); // Finally, emit the signed quote and partially signed transaction to the functionRunner oracle // The functionRunner oracle will use the last outputted word to stdout as the serialized result. This is what gets executed on-chain. - runner.emit(ixs).await.unwrap(); + runner.emit(ixs).await?; + Ok(()) +} + +#[tokio::main(worker_threads = 12)] +async fn main() -> Result<()> { + // First, initialize the runner instance with a freshly generated Gramine keypair + let runner = FunctionRunner::from_env(None)?; + if runner.assert_mr_enclave().is_err() { + runner.emit_error(199).await?; + } + + let res = perform(&runner).await; + if let Some(e) = res.err() { + runner.emit_error(1).await?; + } + Ok(()) } diff --git a/javascript/solana.js/src/accounts/functionAccount.ts b/javascript/solana.js/src/accounts/functionAccount.ts index 63875d5d0..12c8dd0a4 100644 --- a/javascript/solana.js/src/accounts/functionAccount.ts +++ b/javascript/solana.js/src/accounts/functionAccount.ts @@ -51,6 +51,10 @@ import type { RawBuffer } from "@switchboard-xyz/common"; import { BN, parseRawMrEnclave, toUtf8 } from "@switchboard-xyz/common"; import assert from "assert"; +const addressLookupProgram = new PublicKey( + "AddressLookupTab1e1111111111111111111111111" +); + export type ContainerRegistryType = "dockerhub" | "ipfs"; export type FunctionAccountInitSeeds = { @@ -121,9 +125,15 @@ export interface FunctionSetAuthorityParams { } /** - * Parameters for an {@linkcode types.functionVerify} instruction. + * Parameters for setting a {@linkcode types.functionClose} authority */ +export interface FunctionCloseAccountParams { + authority?: Keypair; +} +/** + * Parameters for an {@linkcode types.functionVerify} instruction. + */ export interface FunctionVerifySyncParams { observedTime: anchor.BN; nextAllowedTimestamp: anchor.BN; @@ -145,7 +155,6 @@ export interface FunctionVerifySyncParams { /** * Parameters for an {@linkcode types.functionVerify} instruction. */ - export interface FunctionVerifyParams { observedTime: anchor.BN; nextAllowedTimestamp: anchor.BN; @@ -163,7 +172,6 @@ export interface FunctionVerifyParams { /** * Parameters for an {@linkcode types.functionTrigger} instruction. */ - export interface FunctionTriggerParams { authority?: Keypair; } @@ -173,8 +181,6 @@ export type CreateFunctionRequestParams = Omit< "functionAccount" > & { user?: Keypair }; -/** - /** * Account type representing a Switchboard Function. * @@ -197,10 +203,6 @@ export class FunctionAccount extends Account { public static getMetadata = (functionState: types.FunctionAccountData) => toUtf8(functionState.metadata); - /** - * Load an existing {@linkcode FunctionAccount} with its current on-chain state - */ - /** * Get the size of an {@linkcode FunctionAccount} on-chain. */ @@ -304,6 +306,9 @@ export class FunctionAccount extends Account { return [functionAccount, data]; } + /** + * Load an existing {@linkcode FunctionAccount} with its current on-chain state + */ public static async load( program: SwitchboardProgram, address: PublicKey | string @@ -349,10 +354,6 @@ export class FunctionAccount extends Account { recentSlot ); - const addressLookupProgram = new PublicKey( - "AddressLookupTab1e1111111111111111111111111" - ); - const [addressLookupTable] = PublicKey.findProgramAddressSync( [functionAccount.publicKey.toBuffer(), recentSlot.toBuffer("le", 8)], addressLookupProgram @@ -928,6 +929,48 @@ export class FunctionAccount extends Account { ).then((txn) => this.program.signAndSend(txn, options)); } + public async closeAccountInstruction( + payer: PublicKey, + params: FunctionCloseAccountParams, + options?: TransactionObjectOptions + ): Promise { + const signers: Keypair[] = []; + if (params.authority) { + signers.push(params.authority); + } + + const functionState = await this.loadData(); + const wallet = await this.wallet; + + const functionCloseIxn = types.functionClose( + this.program, + { params: {} }, + { + function: this.publicKey, + authority: functionState.authority, + addressLookupProgram: addressLookupProgram, + addressLookupTable: functionState.addressLookupTable, + escrowWallet: wallet.publicKey, + solDest: payer, + escrowDest: this.program.mint.getAssociatedAddress(payer), + tokenProgram: anchor.utils.token.TOKEN_PROGRAM_ID, + systemProgram: anchor.web3.SystemProgram.programId, + } + ); + return new TransactionObject(payer, [functionCloseIxn], signers, options); + } + + public async closeAccount( + params: FunctionCloseAccountParams, + options?: SendTransactionObjectOptions + ): Promise { + return await this.closeAccountInstruction( + this.program.walletPubkey, + params, + options + ).then((txn) => this.program.signAndSend(txn, options)); + } + public verifyInstructionSync( params: FunctionVerifySyncParams ): TransactionInstruction { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fd5d367b..1dc99373c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -149,7 +149,7 @@ importers: specifier: ^1.78.0 version: 1.78.3 '@switchboard-xyz/solana.js': - specifier: workspace:^ + specifier: workspace:* version: link:../../../javascript/solana.js devDependencies: '@types/bn.js': @@ -1657,12 +1657,12 @@ packages: /@switchboard-xyz/eslint-config@0.1.9: resolution: {integrity: sha512-ZvCAsXn4NsBCECWvew/GIrkvDGzPhhDzT77jkEKQ7Xd2ftrjFJOeYoREDQIOhD88uN3qBl0cIKHbS4t0amkzGw==} dependencies: - '@typescript-eslint/eslint-plugin': 6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6) - '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.0.4) - '@typescript-eslint/type-utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6) + '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.1.6) + '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.0.4) + '@typescript-eslint/type-utils': 6.7.2(eslint@8.49.0)(typescript@5.1.6) eslint: 8.49.0 eslint-config-prettier: 9.0.0(eslint@8.49.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.0)(eslint@8.49.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0) eslint-plugin-node: 11.1.0(eslint@8.49.0) eslint-plugin-prettier: 5.0.0(eslint-config-prettier@9.0.0)(eslint@8.49.0)(prettier@2.8.8) eslint-plugin-simple-import-sort: 10.0.0(eslint@8.49.0) @@ -1869,8 +1869,8 @@ packages: '@types/node': 20.4.2 optional: true - /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.1.6): - resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==} + /@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.1.6): + resolution: {integrity: sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1881,11 +1881,11 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.0.4) - '@typescript-eslint/scope-manager': 6.7.0 - '@typescript-eslint/type-utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6) - '@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.7.0 + '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.0.4) + '@typescript-eslint/scope-manager': 6.7.2 + '@typescript-eslint/type-utils': 6.7.2(eslint@8.49.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4(supports-color@8.1.1) eslint: 8.49.0 graphemer: 1.4.0 @@ -1898,8 +1898,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.0(eslint@8.49.0)(typescript@5.0.4): - resolution: {integrity: sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==} + /@typescript-eslint/parser@6.7.2(eslint@8.49.0)(typescript@5.0.4): + resolution: {integrity: sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1908,10 +1908,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.7.0 - '@typescript-eslint/types': 6.7.0 - '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.0.4) - '@typescript-eslint/visitor-keys': 6.7.0 + '@typescript-eslint/scope-manager': 6.7.2 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.0.4) + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4(supports-color@8.1.1) eslint: 8.49.0 typescript: 5.0.4 @@ -1919,16 +1919,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.7.0: - resolution: {integrity: sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==} + /@typescript-eslint/scope-manager@6.7.2: + resolution: {integrity: sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.0 - '@typescript-eslint/visitor-keys': 6.7.0 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/visitor-keys': 6.7.2 dev: true - /@typescript-eslint/type-utils@6.7.0(eslint@8.49.0)(typescript@5.1.6): - resolution: {integrity: sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==} + /@typescript-eslint/type-utils@6.7.2(eslint@8.49.0)(typescript@5.1.6): + resolution: {integrity: sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1937,8 +1937,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.1.6) - '@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.1.6) + '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.49.0 ts-api-utils: 1.0.1(typescript@5.1.6) @@ -1947,13 +1947,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types@6.7.0: - resolution: {integrity: sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==} + /@typescript-eslint/types@6.7.2: + resolution: {integrity: sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.7.0(typescript@5.0.4): - resolution: {integrity: sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==} + /@typescript-eslint/typescript-estree@6.7.2(typescript@5.0.4): + resolution: {integrity: sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1961,8 +1961,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.7.0 - '@typescript-eslint/visitor-keys': 6.7.0 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -1973,8 +1973,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.7.0(typescript@5.1.6): - resolution: {integrity: sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==} + /@typescript-eslint/typescript-estree@6.7.2(typescript@5.1.6): + resolution: {integrity: sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1982,8 +1982,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.7.0 - '@typescript-eslint/visitor-keys': 6.7.0 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -1994,8 +1994,8 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.7.0(eslint@8.49.0)(typescript@5.1.6): - resolution: {integrity: sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==} + /@typescript-eslint/utils@6.7.2(eslint@8.49.0)(typescript@5.1.6): + resolution: {integrity: sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2003,9 +2003,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.7.0 - '@typescript-eslint/types': 6.7.0 - '@typescript-eslint/typescript-estree': 6.7.0(typescript@5.1.6) + '@typescript-eslint/scope-manager': 6.7.2 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.1.6) eslint: 8.49.0 semver: 7.5.4 transitivePeerDependencies: @@ -2013,11 +2013,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.7.0: - resolution: {integrity: sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==} + /@typescript-eslint/visitor-keys@6.7.2: + resolution: {integrity: sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.0 + '@typescript-eslint/types': 6.7.2 eslint-visitor-keys: 3.4.3 dev: true @@ -3139,7 +3139,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -3160,7 +3160,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.0.4) + '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.0.4) debug: 3.2.7 eslint: 8.49.0 eslint-import-resolver-node: 0.3.7 @@ -3179,7 +3179,7 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.0)(eslint@8.49.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -3189,7 +3189,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.0(eslint@8.49.0)(typescript@5.0.4) + '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.0.4) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 @@ -3198,7 +3198,7 @@ packages: doctrine: 2.1.0 eslint: 8.49.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 diff --git a/rust/switchboard-solana/Cargo.anchor27.lock b/rust/switchboard-solana/Cargo.anchor27.lock index 5970a381a..035898ff6 100644 --- a/rust/switchboard-solana/Cargo.anchor27.lock +++ b/rust/switchboard-solana/Cargo.anchor27.lock @@ -4078,7 +4078,7 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "switchboard-common" -version = "0.8.18" +version = "0.8.20" dependencies = [ "envy", "getrandom 0.2.10", @@ -4091,7 +4091,7 @@ dependencies = [ [[package]] name = "switchboard-solana" -version = "0.27.18" +version = "0.27.25" dependencies = [ "anchor-client", "anchor-lang", diff --git a/rust/switchboard-solana/Cargo.anchor27.toml b/rust/switchboard-solana/Cargo.anchor27.toml index 5ce75d6d9..d98abd29b 100644 --- a/rust/switchboard-solana/Cargo.anchor27.toml +++ b/rust/switchboard-solana/Cargo.anchor27.toml @@ -1,6 +1,6 @@ [package] name = "switchboard-solana" -version = "0.27.18" +version = "0.27.26" edition = "2021" description = "A Rust library to interact with Switchboard accounts." readme = "README.md" @@ -29,11 +29,11 @@ bytemuck = "^1" superslice = "1" [target.'cfg(target_os = "solana")'.dependencies] -switchboard-common = { version = "0.8.15", path = "../switchboard-common" } +switchboard-common = { version = "0.8.20", path = "../switchboard-common" } anchor-lang = { version = "0.27.0" } [target.'cfg(not(target_os = "solana"))'.dependencies] -switchboard-common = { version = "0.8.15", path = "../switchboard-common", features = [ +switchboard-common = { version = "0.8.20", path = "../switchboard-common", features = [ "client", "solana", ] } diff --git a/rust/switchboard-solana/Cargo.lock b/rust/switchboard-solana/Cargo.lock index 5d7084deb..8245ae1c2 100644 --- a/rust/switchboard-solana/Cargo.lock +++ b/rust/switchboard-solana/Cargo.lock @@ -4439,7 +4439,7 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "switchboard-common" -version = "0.8.18" +version = "0.8.20" dependencies = [ "envy", "getrandom 0.2.10", @@ -4452,7 +4452,7 @@ dependencies = [ [[package]] name = "switchboard-solana" -version = "0.28.19" +version = "0.28.21" dependencies = [ "anchor-client", "anchor-lang", diff --git a/rust/switchboard-solana/Cargo.toml b/rust/switchboard-solana/Cargo.toml index 1e8660e6d..863b6f95a 100644 --- a/rust/switchboard-solana/Cargo.toml +++ b/rust/switchboard-solana/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "switchboard-solana" -version = "0.28.19" +version = "0.28.21" edition = "2021" description = "A Rust library to interact with Switchboard accounts." readme = "README.md" @@ -29,11 +29,11 @@ bytemuck = "^1" superslice = "1" [target.'cfg(target_os = "solana")'.dependencies] -switchboard-common = { version = "0.8.18" } +switchboard-common = { version = "0.8.20" } anchor-lang = { version = "0.28.0" } [target.'cfg(not(target_os = "solana"))'.dependencies] -switchboard-common = { version = "0.8.18", features = [ +switchboard-common = { version = "0.8.20", features = [ "client", "solana", ] } diff --git a/rust/switchboard-solana/src/attestation_program/accounts/function.rs b/rust/switchboard-solana/src/attestation_program/accounts/function.rs index ef587e1c5..56ec964a9 100644 --- a/rust/switchboard-solana/src/attestation_program/accounts/function.rs +++ b/rust/switchboard-solana/src/attestation_program/accounts/function.rs @@ -135,8 +135,11 @@ pub struct FunctionAccountData { /// The reward_escrow_wallet TokenAccount used to acrue rewards from requests made with custom parameters. pub reward_escrow_token_wallet: Pubkey, + /// The last reported error code if the most recent response was a failure + pub error_status: u8, + /// Reserved. - pub _ebuf: [u8; 1024], + pub _ebuf: [u8; 1023], } impl Default for FunctionAccountData { diff --git a/rust/switchboard-solana/src/attestation_program/accounts/request.anchor27.rs b/rust/switchboard-solana/src/attestation_program/accounts/request.anchor27.rs index 92eafc7a9..d67027797 100644 --- a/rust/switchboard-solana/src/attestation_program/accounts/request.anchor27.rs +++ b/rust/switchboard-solana/src/attestation_program/accounts/request.anchor27.rs @@ -1,6 +1,16 @@ use crate::{cfg_client, prelude::*}; use solana_program::borsh::get_instance_packed_len; +fn serialize_slice( + slice: &[T], + writer: &mut W, +) -> std::result::Result<(), std::io::Error> { + for item in slice { + item.serialize(writer)?; + } + Ok(()) +} + #[repr(u8)] #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)] pub enum RequestStatus { @@ -66,6 +76,15 @@ impl Default for FunctionRequestTriggerRound { unsafe { std::mem::zeroed() } } } + +fn deserialize_round_ebuf_slice( + reader: &mut R, +) -> std::result::Result<[u8; 56], std::io::Error> { + let mut buffer = [0u8; 56]; + reader.read_exact(&mut buffer)?; + Ok(buffer) +} + impl borsh::ser::BorshSerialize for FunctionRequestTriggerRound where RequestStatus: borsh::ser::BorshSerialize, @@ -89,7 +108,8 @@ where borsh::BorshSerialize::serialize(&self.verifier, writer)?; borsh::BorshSerialize::serialize(&self.enclave_signer, writer)?; borsh::BorshSerialize::serialize(&self.valid_after_slot, writer)?; - writer.write_all(&[0u8; 56])?; + serialize_slice(&self._ebuf, writer)?; + // writer.write_all(&[0u8; 56])?; // borsh::BorshSerialize::serialize(&[0u8; 56], writer)?; Ok(()) } @@ -115,13 +135,13 @@ where verifier: borsh::BorshDeserialize::deserialize(buf)?, enclave_signer: borsh::BorshDeserialize::deserialize(buf)?, valid_after_slot: borsh::BorshDeserialize::deserialize(buf)?, - _ebuf: [0u8; 56], + _ebuf: deserialize_round_ebuf_slice(buf)?, }) } } // #[account] -#[derive(AnchorDeserialize, AnchorSerialize, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct FunctionRequestAccountData { // Up-Front Params for RPC filtering /// Whether the request is ready to be processed. @@ -162,8 +182,10 @@ pub struct FunctionRequestAccountData { /// The slot when the account can be garbage collected and closed by anyone for a portion of the rent. pub garbage_collection_slot: Option, + pub error_status: u8, + /// Reserved. - pub _ebuf: [u8; 256], + pub _ebuf: [u8; 255], } impl Default for FunctionRequestAccountData { fn default() -> Self { @@ -182,11 +204,113 @@ impl Default for FunctionRequestAccountData { container_params: Vec::new(), created_at: 0, garbage_collection_slot: None, - _ebuf: [0u8; 256], + error_status: 0, + _ebuf: [0u8; 255], } } } +// pub struct U8Array255([u8; 255]); + +// impl borsh::de::BorshDeserialize for U8Array255 { +// // fn deserialize_reader( +// // reader: &mut R, +// // ) -> ::core::result::Result { +// // let mut buffer = [0u8; 255]; +// // reader.read_exact(&mut buffer)?; +// // Ok(U8Array255(buffer)) +// // } + +// fn deserialize(reader: &mut &[u8]) -> std::result::Result { +// // Ok([0u8; 255]) +// Ok(U8Array255([0u8; 255])) +// } +// } + +// fn deserialize_ebuf_slice( +// reader: &mut R, +// ) -> std::result::Result<[u8; 255], std::io::Error> { +// Ok([0u8; 255]) +// } + +fn deserialize_ebuf_slice( + reader: &mut R, +) -> std::result::Result<[u8; 255], std::io::Error> { + let mut buffer = [0u8; 255]; + reader.read_exact(&mut buffer)?; + Ok(buffer) +} + +impl borsh::ser::BorshSerialize for FunctionRequestAccountData +where + RequestStatus: borsh::ser::BorshSerialize, + u64: borsh::ser::BorshSerialize, + u64: borsh::ser::BorshSerialize, + u64: borsh::ser::BorshSerialize, + u64: borsh::ser::BorshSerialize, + Pubkey: borsh::ser::BorshSerialize, + Pubkey: borsh::ser::BorshSerialize, + u64: borsh::ser::BorshSerialize, + FunctionRequestTriggerRound: borsh::ser::BorshSerialize, + Vec: borsh::ser::BorshSerialize, +{ + fn serialize( + &self, + writer: &mut W, + ) -> ::core::result::Result<(), borsh::maybestd::io::Error> { + borsh::BorshSerialize::serialize(&self.is_triggered, writer)?; + borsh::BorshSerialize::serialize(&self.status, writer)?; + borsh::BorshSerialize::serialize(&self.authority, writer)?; + borsh::BorshSerialize::serialize(&self.payer, writer)?; + borsh::BorshSerialize::serialize(&self.function, writer)?; + borsh::BorshSerialize::serialize(&self.escrow, writer)?; + borsh::BorshSerialize::serialize(&self.attestation_queue, writer)?; + borsh::BorshSerialize::serialize(&self.active_request, writer)?; + borsh::BorshSerialize::serialize(&self.previous_request, writer)?; + borsh::BorshSerialize::serialize(&self.max_container_params_len, writer)?; + borsh::BorshSerialize::serialize(&self.container_params_hash, writer)?; + borsh::BorshSerialize::serialize(&self.container_params, writer)?; + borsh::BorshSerialize::serialize(&self.created_at, writer)?; + borsh::BorshSerialize::serialize(&self.garbage_collection_slot, writer)?; + borsh::BorshSerialize::serialize(&self.error_status, writer)?; + serialize_slice(&self._ebuf, writer)?; + Ok(()) + } +} +impl borsh::de::BorshDeserialize for FunctionRequestAccountData +where + RequestStatus: borsh::BorshDeserialize, + u64: borsh::BorshDeserialize, + u64: borsh::BorshDeserialize, + u64: borsh::BorshDeserialize, + u64: borsh::BorshDeserialize, + Pubkey: borsh::BorshDeserialize, + Pubkey: borsh::BorshDeserialize, + u64: borsh::BorshDeserialize, + FunctionRequestTriggerRound: borsh::BorshDeserialize, +{ + fn deserialize(buf: &mut &[u8]) -> ::core::result::Result { + Ok(Self { + is_triggered: borsh::BorshDeserialize::deserialize(buf)?, + status: borsh::BorshDeserialize::deserialize(buf)?, + authority: borsh::BorshDeserialize::deserialize(buf)?, + payer: borsh::BorshDeserialize::deserialize(buf)?, + function: borsh::BorshDeserialize::deserialize(buf)?, + escrow: borsh::BorshDeserialize::deserialize(buf)?, + attestation_queue: borsh::BorshDeserialize::deserialize(buf)?, + active_request: borsh::BorshDeserialize::deserialize(buf)?, + previous_request: borsh::BorshDeserialize::deserialize(buf)?, + max_container_params_len: borsh::BorshDeserialize::deserialize(buf)?, + container_params_hash: borsh::BorshDeserialize::deserialize(buf)?, + container_params: borsh::BorshDeserialize::deserialize(buf)?, + created_at: borsh::BorshDeserialize::deserialize(buf)?, + garbage_collection_slot: borsh::BorshDeserialize::deserialize(buf)?, + error_status: borsh::BorshDeserialize::deserialize(buf)?, + _ebuf: deserialize_ebuf_slice(buf)?, + }) + } +} + impl anchor_lang::AccountSerialize for FunctionRequestAccountData { fn try_serialize(&self, writer: &mut W) -> anchor_lang::Result<()> { if writer @@ -345,3 +469,138 @@ impl FunctionRequestAccountData { } } } + +#[cfg(test)] +mod tests { + use super::*; + + use std::str::FromStr; + + const REQUEST_DATA: [u8; 1309] = [ + 8, 14, 177, 85, 144, 65, 148, 246, 0, 4, 191, 163, 95, 149, 251, 196, 61, 38, 170, 30, 192, + 210, 238, 210, 121, 251, 115, 80, 136, 183, 116, 88, 7, 195, 127, 225, 4, 177, 167, 250, + 214, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 200, 91, 33, 133, 251, 77, 4, 45, 33, 88, 160, 219, 74, 253, 191, 56, 191, + 52, 130, 87, 44, 197, 78, 47, 64, 1, 9, 49, 23, 46, 248, 118, 67, 254, 215, 187, 179, 81, + 198, 84, 39, 244, 16, 113, 89, 56, 41, 133, 66, 71, 68, 238, 198, 34, 226, 219, 65, 150, + 252, 243, 229, 140, 153, 112, 174, 177, 70, 231, 73, 196, 214, 194, 190, 219, 159, 24, 162, + 119, 159, 16, 120, 53, 239, 102, 225, 241, 66, 97, 108, 144, 152, 47, 53, 76, 242, 215, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 212, 153, 154, 14, 0, 0, 0, 0, 225, 153, 154, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 214, 176, 30, 24, 236, 238, 245, 97, 218, 201, 34, 20, 25, 94, 235, 88, + 235, 48, 114, 193, 144, 126, 220, 233, 142, 238, 32, 191, 233, 220, 175, 23, 80, 233, 228, + 192, 87, 36, 180, 107, 5, 182, 70, 125, 89, 139, 68, 5, 118, 218, 209, 167, 207, 52, 20, + 76, 217, 241, 92, 50, 106, 53, 253, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 159, 54, 102, 77, 67, 235, 26, 94, 144, 172, 18, 65, 45, 54, 127, 59, 100, 213, 206, + 91, 40, 101, 248, 189, 195, 19, 165, 190, 123, 227, 54, 103, 125, 0, 0, 0, 80, 73, 68, 61, + 69, 53, 77, 65, 115, 122, 106, 122, 56, 113, 90, 90, 68, 72, 75, 113, 81, 50, 49, 103, 53, + 119, 89, 117, 104, 77, 84, 106, 77, 98, 107, 49, 76, 52, 76, 52, 106, 66, 70, 88, 77, 103, + 113, 71, 44, 77, 73, 78, 95, 82, 69, 83, 85, 76, 84, 61, 49, 44, 77, 65, 88, 95, 82, 69, + 83, 85, 76, 84, 61, 49, 48, 44, 85, 83, 69, 82, 61, 68, 117, 53, 77, 111, 52, 89, 70, 70, + 70, 76, 113, 84, 57, 75, 90, 81, 75, 80, 77, 119, 52, 67, 109, 101, 111, 53, 86, 102, 71, + 76, 117, 114, 106, 99, 82, 104, 120, 104, 98, 51, 112, 106, 75, 130, 192, 8, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + const REQUEST_DATA_HEX: &str = "080eb155904194f60004bfa35f95fbc43d26aa1ec0d2eed279fb735088b7745807c37fe104b1a7fad6620000000000000000000000000000000000000000000000000000000000000000c85b2185fb4d042d2158a0db4afdbf38bf3482572cc54e2f40010931172ef87643fed7bbb351c65427f4107159382985424744eec622e2db4196fcf3e58c9970aeb146e749c4d6c2bedb9f18a2779f107835ef66e1f142616c90982f354cf2d7040000000000000000d4999a0e00000000e1999a0e000000000000000000000000d6b01e18eceef561dac92214195eeb58eb3072c1907edce98eee20bfe9dcaf1750e9e4c05724b46b05b6467d598b440576dad1a7cf34144cd9f15c326a35fd3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200009f36664d43eb1a5e90ac12412d367f3b64d5ce5b2865f8bdc313a5be7be336677d0000005049443d45354d41737a6a7a38715a5a44484b715132316735775975684d546a4d626b314c344c346a4246584d6771472c4d494e5f524553554c543d312c4d41585f524553554c543d31302c555345523d4475354d6f34594646464c7154394b5a514b504d7734436d656f355666474c75726a63526878686233706a4b82c00865000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + // Already has discriminator removed + const REQUEST_DATA_HEX_NO_DISC: &str = "0004bfa35f95fbc43d26aa1ec0d2eed279fb735088b7745807c37fe104b1a7fad6620000000000000000000000000000000000000000000000000000000000000000c85b2185fb4d042d2158a0db4afdbf38bf3482572cc54e2f40010931172ef87643fed7bbb351c65427f4107159382985424744eec622e2db4196fcf3e58c9970aeb146e749c4d6c2bedb9f18a2779f107835ef66e1f142616c90982f354cf2d7040000000000000000d4999a0e00000000e1999a0e000000000000000000000000d6b01e18eceef561dac92214195eeb58eb3072c1907edce98eee20bfe9dcaf1750e9e4c05724b46b05b6467d598b440576dad1a7cf34144cd9f15c326a35fd3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200009f36664d43eb1a5e90ac12412d367f3b64d5ce5b2865f8bdc313a5be7be336677d0000005049443d45354d41737a6a7a38715a5a44484b715132316735775975684d546a4d626b314c344c346a4246584d6771472c4d494e5f524553554c543d312c4d41585f524553554c543d31302c555345523d4475354d6f34594646464c7154394b5a514b504d7734436d656f355666474c75726a63526878686233706a4b82c00865000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + const EXPECTED_CONTAINER_PARAMS: &str = "PID=E5MAszjz8qZZDHKqQ21g5wYuhMTjMbk1L4L4jBFXMgqG,MIN_RESULT=1,MAX_RESULT=10,USER=Du5Mo4YFFFLqT9KZQKPMw4Cmeo5VfGLurjcRhxhb3pjK"; + + #[test] + fn test_request_deserialization() { + let request = + FunctionRequestAccountData::try_deserialize_unchecked(&mut REQUEST_DATA.as_slice()) + .unwrap(); + + let container_params = std::str::from_utf8(&request.container_params) + .unwrap() + .to_string(); + + println!("Max params Len: {}", request.max_container_params_len); + println!("Params Len: {}", request.container_params.len()); + + assert_eq!(container_params, EXPECTED_CONTAINER_PARAMS.to_string()); + assert_eq!( + request.function, + Pubkey::from_str("EV78uGX5CKioM7MyY8tY1nQFJtNXnjVGTCV2tamWdXGh").unwrap() + ); + assert_eq!( + request.escrow, + Pubkey::from_str("5aRhbaGeoe7HTwoMwGgeENGXuVCLcRBNxFrmFnWGG1bM").unwrap() + ); + } + + #[test] + fn test_hex_decode() { + let account_bytes = hex::decode(REQUEST_DATA_HEX).unwrap(); + let request = + FunctionRequestAccountData::try_deserialize(&mut account_bytes.as_slice()).unwrap(); + + let container_params = std::str::from_utf8(&request.container_params) + .unwrap() + .to_string(); + + assert_eq!(container_params, EXPECTED_CONTAINER_PARAMS.to_string()); + + assert_eq!( + request.function, + Pubkey::from_str("EV78uGX5CKioM7MyY8tY1nQFJtNXnjVGTCV2tamWdXGh").unwrap() + ); + assert_eq!( + request.escrow, + Pubkey::from_str("5aRhbaGeoe7HTwoMwGgeENGXuVCLcRBNxFrmFnWGG1bM").unwrap() + ); + } + + #[test] + fn test_hex_encode() { + // Encode the bytes a hex value + let request = + FunctionRequestAccountData::try_deserialize(&mut REQUEST_DATA.as_slice()).unwrap(); + let request_data = [ + &FunctionRequestAccountData::DISCRIMINATOR[..], + &request.try_to_vec().unwrap()[..], + ] + .concat(); + + // Decode the serialized bytes + let decoded_request = + FunctionRequestAccountData::try_deserialize(&mut request_data.as_slice()).unwrap(); + + assert_eq!(request.created_at, decoded_request.created_at); + assert_eq!( + request.container_params.len(), + decoded_request.container_params.len() + ); + } +} diff --git a/rust/switchboard-solana/src/attestation_program/accounts/request.rs b/rust/switchboard-solana/src/attestation_program/accounts/request.rs index b82ea9283..4e4d3b25e 100644 --- a/rust/switchboard-solana/src/attestation_program/accounts/request.rs +++ b/rust/switchboard-solana/src/attestation_program/accounts/request.rs @@ -1,5 +1,5 @@ use crate::{cfg_client, prelude::*}; -use solana_program::borsh::get_instance_packed_len; +use solana_program::borsh0_10::get_instance_packed_len; #[repr(u8)] #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)] @@ -162,8 +162,11 @@ pub struct FunctionRequestAccountData { /// The slot when the account can be garbage collected and closed by anyone for a portion of the rent. pub garbage_collection_slot: Option, + /// The last recorded error code if most recent response was an error. + pub error_status: u8, + /// Reserved. - pub _ebuf: [u8; 256], + pub _ebuf: [u8; 255], } impl Default for FunctionRequestAccountData { fn default() -> Self { @@ -182,7 +185,8 @@ impl Default for FunctionRequestAccountData { container_params: Vec::new(), created_at: 0, garbage_collection_slot: None, - _ebuf: [0u8; 256], + error_status: 0, + _ebuf: [0u8; 255], } } } @@ -345,3 +349,135 @@ impl FunctionRequestAccountData { } } } + +#[cfg(test)] +mod tests { + use super::*; + + use std::str::FromStr; + + const REQUEST_DATA: [u8; 1309] = [ + 8, 14, 177, 85, 144, 65, 148, 246, 0, 4, 191, 163, 95, 149, 251, 196, 61, 38, 170, 30, 192, + 210, 238, 210, 121, 251, 115, 80, 136, 183, 116, 88, 7, 195, 127, 225, 4, 177, 167, 250, + 214, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 200, 91, 33, 133, 251, 77, 4, 45, 33, 88, 160, 219, 74, 253, 191, 56, 191, + 52, 130, 87, 44, 197, 78, 47, 64, 1, 9, 49, 23, 46, 248, 118, 67, 254, 215, 187, 179, 81, + 198, 84, 39, 244, 16, 113, 89, 56, 41, 133, 66, 71, 68, 238, 198, 34, 226, 219, 65, 150, + 252, 243, 229, 140, 153, 112, 174, 177, 70, 231, 73, 196, 214, 194, 190, 219, 159, 24, 162, + 119, 159, 16, 120, 53, 239, 102, 225, 241, 66, 97, 108, 144, 152, 47, 53, 76, 242, 215, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 212, 153, 154, 14, 0, 0, 0, 0, 225, 153, 154, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 214, 176, 30, 24, 236, 238, 245, 97, 218, 201, 34, 20, 25, 94, 235, 88, + 235, 48, 114, 193, 144, 126, 220, 233, 142, 238, 32, 191, 233, 220, 175, 23, 80, 233, 228, + 192, 87, 36, 180, 107, 5, 182, 70, 125, 89, 139, 68, 5, 118, 218, 209, 167, 207, 52, 20, + 76, 217, 241, 92, 50, 106, 53, 253, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 159, 54, 102, 77, 67, 235, 26, 94, 144, 172, 18, 65, 45, 54, 127, 59, 100, 213, 206, + 91, 40, 101, 248, 189, 195, 19, 165, 190, 123, 227, 54, 103, 125, 0, 0, 0, 80, 73, 68, 61, + 69, 53, 77, 65, 115, 122, 106, 122, 56, 113, 90, 90, 68, 72, 75, 113, 81, 50, 49, 103, 53, + 119, 89, 117, 104, 77, 84, 106, 77, 98, 107, 49, 76, 52, 76, 52, 106, 66, 70, 88, 77, 103, + 113, 71, 44, 77, 73, 78, 95, 82, 69, 83, 85, 76, 84, 61, 49, 44, 77, 65, 88, 95, 82, 69, + 83, 85, 76, 84, 61, 49, 48, 44, 85, 83, 69, 82, 61, 68, 117, 53, 77, 111, 52, 89, 70, 70, + 70, 76, 113, 84, 57, 75, 90, 81, 75, 80, 77, 119, 52, 67, 109, 101, 111, 53, 86, 102, 71, + 76, 117, 114, 106, 99, 82, 104, 120, 104, 98, 51, 112, 106, 75, 130, 192, 8, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + const REQUEST_DATA_HEX: &str = "080eb155904194f60004bfa35f95fbc43d26aa1ec0d2eed279fb735088b7745807c37fe104b1a7fad6620000000000000000000000000000000000000000000000000000000000000000c85b2185fb4d042d2158a0db4afdbf38bf3482572cc54e2f40010931172ef87643fed7bbb351c65427f4107159382985424744eec622e2db4196fcf3e58c9970aeb146e749c4d6c2bedb9f18a2779f107835ef66e1f142616c90982f354cf2d7040000000000000000d4999a0e00000000e1999a0e000000000000000000000000d6b01e18eceef561dac92214195eeb58eb3072c1907edce98eee20bfe9dcaf1750e9e4c05724b46b05b6467d598b440576dad1a7cf34144cd9f15c326a35fd3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200009f36664d43eb1a5e90ac12412d367f3b64d5ce5b2865f8bdc313a5be7be336677d0000005049443d45354d41737a6a7a38715a5a44484b715132316735775975684d546a4d626b314c344c346a4246584d6771472c4d494e5f524553554c543d312c4d41585f524553554c543d31302c555345523d4475354d6f34594646464c7154394b5a514b504d7734436d656f355666474c75726a63526878686233706a4b82c00865000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + const EXPECTED_CONTAINER_PARAMS: &str = "PID=E5MAszjz8qZZDHKqQ21g5wYuhMTjMbk1L4L4jBFXMgqG,MIN_RESULT=1,MAX_RESULT=10,USER=Du5Mo4YFFFLqT9KZQKPMw4Cmeo5VfGLurjcRhxhb3pjK"; + + #[test] + fn test_request_deserialization() { + let request = + FunctionRequestAccountData::try_deserialize_unchecked(&mut REQUEST_DATA.as_slice()) + .unwrap(); + + let container_params = std::str::from_utf8(&request.container_params) + .unwrap() + .to_string(); + + println!("Max params Len: {}", request.max_container_params_len); + println!("Params Len: {}", request.container_params.len()); + + assert_eq!(container_params, EXPECTED_CONTAINER_PARAMS.to_string()); + assert_eq!( + request.function, + Pubkey::from_str("EV78uGX5CKioM7MyY8tY1nQFJtNXnjVGTCV2tamWdXGh").unwrap() + ); + assert_eq!( + request.escrow, + Pubkey::from_str("5aRhbaGeoe7HTwoMwGgeENGXuVCLcRBNxFrmFnWGG1bM").unwrap() + ); + } + + #[test] + fn test_hex_decode() { + let account_bytes = hex::decode(REQUEST_DATA_HEX).unwrap(); + let request = + FunctionRequestAccountData::try_deserialize(&mut account_bytes.as_slice()).unwrap(); + + let container_params = std::str::from_utf8(&request.container_params) + .unwrap() + .to_string(); + + assert_eq!(container_params, EXPECTED_CONTAINER_PARAMS.to_string()); + + assert_eq!( + request.function, + Pubkey::from_str("EV78uGX5CKioM7MyY8tY1nQFJtNXnjVGTCV2tamWdXGh").unwrap() + ); + assert_eq!( + request.escrow, + Pubkey::from_str("5aRhbaGeoe7HTwoMwGgeENGXuVCLcRBNxFrmFnWGG1bM").unwrap() + ); + } + + #[test] + fn test_hex_encode() { + // Encode the bytes a hex value + let request = + FunctionRequestAccountData::try_deserialize(&mut REQUEST_DATA.as_slice()).unwrap(); + let request_data = [ + &FunctionRequestAccountData::DISCRIMINATOR[..], + &request.try_to_vec().unwrap()[..], + ] + .concat(); + + // Decode the serialized bytes + let decoded_request = + FunctionRequestAccountData::try_deserialize(&mut request_data.as_slice()).unwrap(); + + assert_eq!(request.created_at, decoded_request.created_at); + assert_eq!( + request.container_params.len(), + decoded_request.container_params.len() + ); + } +} diff --git a/rust/switchboard-solana/src/attestation_program/instructions/function_verify.rs b/rust/switchboard-solana/src/attestation_program/instructions/function_verify.rs index 2bed6b565..66473632f 100644 --- a/rust/switchboard-solana/src/attestation_program/instructions/function_verify.rs +++ b/rust/switchboard-solana/src/attestation_program/instructions/function_verify.rs @@ -25,7 +25,7 @@ pub struct FunctionVerify<'info> { pub struct FunctionVerifyParams { pub observed_time: i64, pub next_allowed_timestamp: i64, - pub is_failure: bool, + pub error_code: u8, pub mr_enclave: [u8; 32], } impl InstructionData for FunctionVerifyParams {} @@ -57,13 +57,13 @@ impl<'info> FunctionVerify<'info> { program: AccountInfo<'info>, observed_time: i64, next_allowed_timestamp: i64, - is_failure: bool, + error_code: u8, mr_enclave: [u8; 32], ) -> ProgramResult { let cpi_params = FunctionVerifyParams { observed_time, next_allowed_timestamp, - is_failure, + error_code, mr_enclave, }; let instruction = self.get_instruction(*program.key, cpi_params)?; @@ -77,14 +77,14 @@ impl<'info> FunctionVerify<'info> { program: AccountInfo<'info>, observed_time: i64, next_allowed_timestamp: i64, - is_failure: bool, + error_code: u8, mr_enclave: [u8; 32], signer_seeds: &[&[&[u8]]], ) -> ProgramResult { let cpi_params = FunctionVerifyParams { observed_time, next_allowed_timestamp, - is_failure, + error_code, mr_enclave, }; let instruction = self.get_instruction(*program.key, cpi_params)?; diff --git a/rust/switchboard-solana/src/attestation_program/instructions/mod.rs b/rust/switchboard-solana/src/attestation_program/instructions/mod.rs index efc2a0a1d..10a7ef374 100644 --- a/rust/switchboard-solana/src/attestation_program/instructions/mod.rs +++ b/rust/switchboard-solana/src/attestation_program/instructions/mod.rs @@ -41,4 +41,4 @@ pub mod wallet_fund; pub use wallet_fund::*; pub mod wallet_withdraw; -pub use wallet_withdraw::*; \ No newline at end of file +pub use wallet_withdraw::*; diff --git a/rust/switchboard-solana/src/attestation_program/instructions/request_close.rs b/rust/switchboard-solana/src/attestation_program/instructions/request_close.rs index df5f7cb3b..ad7932c32 100644 --- a/rust/switchboard-solana/src/attestation_program/instructions/request_close.rs +++ b/rust/switchboard-solana/src/attestation_program/instructions/request_close.rs @@ -37,8 +37,12 @@ impl Discriminator for FunctionRequestClose<'_> { } impl<'info> FunctionRequestClose<'info> { - pub fn get_instruction(&self, program_id: Pubkey) -> anchor_lang::Result { - let accounts = self.to_account_metas(None); + pub fn get_instruction( + &self, + program_id: Pubkey, + is_authority_signer: Option, + ) -> anchor_lang::Result { + let accounts = self.to_account_metas(is_authority_signer); let mut data: Vec = FunctionRequestClose::discriminator().try_to_vec()?; let params = FunctionRequestCloseParams {}; @@ -48,8 +52,12 @@ impl<'info> FunctionRequestClose<'info> { Ok(instruction) } - pub fn invoke(&self, program: AccountInfo<'info>) -> ProgramResult { - let instruction = self.get_instruction(*program.key)?; + pub fn invoke( + &self, + program: AccountInfo<'info>, + is_authority_signer: Option, + ) -> ProgramResult { + let instruction = self.get_instruction(*program.key, is_authority_signer)?; let account_infos = self.to_account_infos(); invoke(&instruction, &account_infos[..]) @@ -58,9 +66,10 @@ impl<'info> FunctionRequestClose<'info> { pub fn invoke_signed( &self, program: AccountInfo<'info>, + is_authority_signer: Option, signer_seeds: &[&[&[u8]]], ) -> ProgramResult { - let instruction = self.get_instruction(*program.key)?; + let instruction = self.get_instruction(*program.key, is_authority_signer)?; let account_infos = self.to_account_infos(); invoke_signed(&instruction, &account_infos[..], signer_seeds) @@ -84,7 +93,7 @@ impl<'info> FunctionRequestClose<'info> { fn to_account_metas(&self, is_signer: Option) -> Vec { let mut account_metas = Vec::new(); account_metas.extend(self.request.to_account_metas(None)); - account_metas.extend(self.authority.to_account_metas(None)); + account_metas.extend(self.authority.to_account_metas(is_signer)); account_metas.extend(self.escrow.to_account_metas(None)); account_metas.extend(self.function.to_account_metas(None)); account_metas.extend(self.sol_dest.to_account_metas(None)); diff --git a/rust/switchboard-solana/src/attestation_program/instructions/request_verify.rs b/rust/switchboard-solana/src/attestation_program/instructions/request_verify.rs index 5ff3375e8..864aa4439 100644 --- a/rust/switchboard-solana/src/attestation_program/instructions/request_verify.rs +++ b/rust/switchboard-solana/src/attestation_program/instructions/request_verify.rs @@ -28,7 +28,7 @@ pub struct FunctionRequestVerify<'info> { #[derive(Clone, AnchorSerialize, AnchorDeserialize)] pub struct FunctionRequestVerifyParams { pub observed_time: i64, - pub is_failure: bool, + pub error_code: u8, pub mr_enclave: [u8; 32], pub request_slot: u64, pub container_params_hash: [u8; 32], diff --git a/rust/switchboard-solana/src/client/function_runner.rs b/rust/switchboard-solana/src/client/function_runner.rs index 300030cd8..cb6caca0a 100644 --- a/rust/switchboard-solana/src/client/function_runner.rs +++ b/rust/switchboard-solana/src/client/function_runner.rs @@ -182,6 +182,19 @@ impl FunctionRunner { }) } + pub fn assert_mr_enclave(&self) -> Result<(), SwitchboardClientError> { + if let Some(function_data) = self.function_data.clone() { + let quote_raw = Gramine::generate_quote(&self.signer.to_bytes()).unwrap_or_default(); + if let Ok(quote) = Quote::parse("e_raw) { + let mr_enclave: MrEnclave = quote.isv_report.mrenclave.try_into().unwrap(); + if !function_data.mr_enclaves.contains(&mr_enclave) { + return Err(SwitchboardClientError::MrEnclaveMismatch); + } + } + } + Ok(()) + } + pub fn new( url: &str, commitment: Option, @@ -327,6 +340,7 @@ impl FunctionRunner { async fn build_fn_verify_ixn( &self, mr_enclave: MrEnclave, + error_code: u8, ) -> Result { if self.function == Pubkey::default() { return Err(SwitchboardClientError::CustomMessage( @@ -359,7 +373,7 @@ impl FunctionRunner { let ixn_params = FunctionVerifyParams { observed_time: unix_timestamp(), next_allowed_timestamp, - is_failure: false, + error_code, mr_enclave, }; @@ -384,9 +398,16 @@ impl FunctionRunner { async fn build_fn_request_verify_ixn( &self, mr_enclave: MrEnclave, + error_code: u8, ) -> Result { - let function_request_data = self.load_request_data().await?; - let function_request_key = self.function_request_key.unwrap_or_default(); // verified in load_request_data + if self.function_request_data.is_none() || self.function_request_key.is_none() { + return Err(SwitchboardClientError::CustomMessage( + "function_request_verify instruction needs request environment present." + .to_string(), + )); + } + let function_request_data = self.function_request_data.clone().unwrap_or_default(); + let function_request_key = self.function_request_key.unwrap_or_default(); if function_request_data.function != self.function { return Err(SwitchboardClientError::CustomMessage(format!( @@ -408,12 +429,14 @@ impl FunctionRunner { &self.verifier, ); + let container_params_hash = + solana_program::hash::hash(&function_request_data.container_params).to_bytes(); let ixn_params = FunctionRequestVerifyParams { observed_time: unix_timestamp(), - is_failure: false, + error_code, mr_enclave, request_slot: function_request_data.active_request.request_slot, - container_params_hash: function_request_data.container_params_hash, + container_params_hash, }; let (state_pubkey, _state_bump) = @@ -443,6 +466,7 @@ impl FunctionRunner { async fn get_result( &self, mut ixs: Vec, + error_code: u8, ) -> Result { let quote_raw = Gramine::generate_quote(&self.signer.to_bytes()).unwrap(); let quote = Quote::parse("e_raw).unwrap(); @@ -450,9 +474,10 @@ impl FunctionRunner { let function_request_key = self.function_request_key.unwrap_or_default(); let verify_ixn = if function_request_key == Pubkey::default() { - self.build_fn_verify_ixn(mr_enclave).await? + self.build_fn_verify_ixn(mr_enclave, error_code).await? } else { - self.build_fn_request_verify_ixn(mr_enclave).await? + self.build_fn_request_verify_ixn(mr_enclave, error_code) + .await? }; ixs.insert(0, verify_ixn); let message = Message::new(&ixs, Some(&self.payer)); @@ -477,13 +502,14 @@ impl FunctionRunner { chain_result_info: Solana(SOLFunctionResult { serialized_tx: bincode::serialize(&tx).unwrap(), }), + error_code, }) } /// Emits a serialized FunctionResult object to send to the quote verification /// sidecar. pub async fn emit(&self, ixs: Vec) -> Result<(), SwitchboardClientError> { - self.get_result(ixs) + self.get_result(ixs, 0) .await .map_err(|e| { SwitchboardClientError::CustomMessage(format!("failed to get verify ixn: {}", e)) @@ -493,6 +519,11 @@ impl FunctionRunner { Ok(()) } + + pub async fn emit_error(&self, error_code: u8) -> Result<(), SwitchboardClientError> { + self.get_result(vec![], error_code).await.unwrap().emit(); + Ok(()) + } } // Useful for building ixns on the client side diff --git a/rust/switchboard-solana/src/decimal.rs b/rust/switchboard-solana/src/decimal.rs index 2bc567873..7f6d5f935 100644 --- a/rust/switchboard-solana/src/decimal.rs +++ b/rust/switchboard-solana/src/decimal.rs @@ -182,13 +182,13 @@ mod tests { scale: 0, }; let b: bool = swb_decimal.into(); - assert_eq!(b, false); + assert!(!b); let swb_decimal_neg = SwitchboardDecimal { mantissa: -0, scale: 0, }; let b: bool = swb_decimal_neg.into(); - assert_eq!(b, false); + assert!(!b); } #[test] diff --git a/rust/switchboard-solana/src/lib.rs b/rust/switchboard-solana/src/lib.rs index bd0fe7998..818630a32 100644 --- a/rust/switchboard-solana/src/lib.rs +++ b/rust/switchboard-solana/src/lib.rs @@ -125,12 +125,11 @@ pub mod prelude; cfg_client! { pub mod client; pub use client::*; +} - // Only enable this feature if client is already enabled - cfg_secrets! { - pub mod secrets; - pub use secrets::*; - } +cfg_secrets! { + pub mod secrets; + pub use secrets::*; } /// Program id for the Switchboard oracle program diff --git a/rust/switchboard-solana/src/macros.rs b/rust/switchboard-solana/src/macros.rs index 26c1d37e1..f20a9b302 100644 --- a/rust/switchboard-solana/src/macros.rs +++ b/rust/switchboard-solana/src/macros.rs @@ -1,6 +1,5 @@ -// #[cfg(any(not(target_os = "solana"), feature = "client"))] -// #[cfg_attr(doc_cfg, doc(cfg(any(not(target_os = "solana"), feature = "client"))))] - +/// Macro used to include code if the target_os is not 'solana'. +/// This is intended to be used for code that is primarily for off-chain Switchboard Functions. #[macro_export] macro_rules! cfg_client { ($($item:item)*) => { @@ -12,23 +11,29 @@ macro_rules! cfg_client { } } +/// Macro used to include code only if the target_os is 'solana'. +/// This is intended to be used for code that is primarily for on-chain programs. #[macro_export] -macro_rules! cfg_secrets { +macro_rules! cfg_program { ($($item:item)*) => { $( - #[cfg(feature = "secrets")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "secrets")))] + #[cfg(target_os = "solana")] + #[cfg_attr(doc_cfg, doc(cfg(target_os = "solana")))] $item )* } } +/// Macro used to include code if the feature 'secrets' is enabled. +/// This is intended to be used for code that is primarily for off-chain Switchboard Secrets. #[macro_export] -macro_rules! cfg_program { +macro_rules! cfg_secrets { ($($item:item)*) => { $( - #[cfg(target_os = "solana")] - #[cfg_attr(doc_cfg, doc(cfg(target_os = "solana")))] + #[cfg(not(target_os = "solana"))] + #[cfg(feature = "secrets")] + #[cfg_attr(doc_cfg, doc(cfg(not(target_os = "solana"))))] + #[cfg_attr(doc_cfg, doc(cfg(feature = "secrets")))] $item )* } diff --git a/rust/switchboard-solana/src/oracle_program/accounts/aggregator.rs b/rust/switchboard-solana/src/oracle_program/accounts/aggregator.rs index 38b3f5c28..358d68bdd 100644 --- a/rust/switchboard-solana/src/oracle_program/accounts/aggregator.rs +++ b/rust/switchboard-solana/src/oracle_program/accounts/aggregator.rs @@ -296,7 +296,7 @@ impl AggregatorAccountData { #[cfg(test)] mod tests { use super::*; - impl<'info> Default for AggregatorAccountData { + impl Default for AggregatorAccountData { fn default() -> Self { unsafe { std::mem::zeroed() } } diff --git a/rust/switchboard-solana/src/oracle_program/accounts/buffer_relayer.rs b/rust/switchboard-solana/src/oracle_program/accounts/buffer_relayer.rs index 2bcad2290..c1b698145 100644 --- a/rust/switchboard-solana/src/oracle_program/accounts/buffer_relayer.rs +++ b/rust/switchboard-solana/src/oracle_program/accounts/buffer_relayer.rs @@ -1,6 +1,7 @@ use crate::prelude::*; #[account] +#[derive(Default)] pub struct BufferRelayerAccountData { /// Name of the buffer account to store on-chain. pub name: [u8; 32], @@ -25,23 +26,6 @@ pub struct BufferRelayerAccountData { /// The buffer holding the latest confirmed result. pub result: Vec, } -impl Default for BufferRelayerAccountData { - fn default() -> Self { - Self { - name: [0u8; 32], - queue_pubkey: Pubkey::default(), - escrow: Pubkey::default(), - authority: Pubkey::default(), - job_pubkey: Pubkey::default(), - job_hash: [0u8; 32], - min_update_delay_seconds: 0, - is_locked: false, - current_round: BufferRelayerRound::default(), - latest_confirmed_round: BufferRelayerRound::default(), - result: Vec::new(), - } - } -} #[derive(Default, Clone, AnchorSerialize, AnchorDeserialize)] pub struct BufferRelayerRound { diff --git a/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs b/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs index 28b09c922..f5cbe3063 100644 --- a/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs +++ b/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs @@ -171,14 +171,14 @@ impl Default for FieldElementZC { } unsafe impl Pod for FieldElementZC {} unsafe impl Zeroable for FieldElementZC {} -impl Into for FieldElement51 { - fn into(self) -> FieldElementZC { - FieldElementZC { bytes: self.0 } +impl From for FieldElementZC { + fn from(val: FieldElement51) -> Self { + FieldElementZC { bytes: val.0 } } } -impl Into for FieldElementZC { - fn into(self) -> FieldElement51 { - FieldElement51(self.bytes) +impl From for FieldElement51 { + fn from(val: FieldElementZC) -> Self { + FieldElement51(val.bytes) } } @@ -213,23 +213,23 @@ impl Default for CompletedPointZC { } unsafe impl Pod for CompletedPoint {} unsafe impl Zeroable for CompletedPoint {} -impl Into for CompletedPoint { - fn into(self) -> CompletedPointZC { +impl From for CompletedPointZC { + fn from(val: CompletedPoint) -> Self { CompletedPointZC { - X: self.X.into(), - Y: self.Y.into(), - Z: self.Z.into(), - T: self.T.into(), + X: val.X.into(), + Y: val.Y.into(), + Z: val.Z.into(), + T: val.T.into(), } } } -impl Into for CompletedPointZC { - fn into(self) -> CompletedPoint { +impl From for CompletedPoint { + fn from(val: CompletedPointZC) -> Self { CompletedPoint { - X: self.X.into(), - Y: self.Y.into(), - Z: self.Z.into(), - T: self.T.into(), + X: val.X.into(), + Y: val.Y.into(), + Z: val.Z.into(), + T: val.T.into(), } } } @@ -286,21 +286,21 @@ impl Default for ProjectivePointZC { } unsafe impl Pod for ProjectivePoint {} unsafe impl Zeroable for ProjectivePoint {} -impl Into for ProjectivePoint { - fn into(self) -> ProjectivePointZC { +impl From for ProjectivePointZC { + fn from(val: ProjectivePoint) -> Self { ProjectivePointZC { - X: self.X.into(), - Y: self.Y.into(), - Z: self.Z.into(), + X: val.X.into(), + Y: val.Y.into(), + Z: val.Z.into(), } } } -impl Into for ProjectivePointZC { - fn into(self) -> ProjectivePoint { +impl From for ProjectivePoint { + fn from(val: ProjectivePointZC) -> Self { ProjectivePoint { - X: self.X.into(), - Y: self.Y.into(), - Z: self.Z.into(), + X: val.X.into(), + Y: val.Y.into(), + Z: val.Z.into(), } } } diff --git a/rust/switchboard-solana/src/oracle_program/accounts/history_buffer.rs b/rust/switchboard-solana/src/oracle_program/accounts/history_buffer.rs index 09e126ce6..bd3d493e9 100644 --- a/rust/switchboard-solana/src/oracle_program/accounts/history_buffer.rs +++ b/rust/switchboard-solana/src/oracle_program/accounts/history_buffer.rs @@ -101,7 +101,7 @@ impl<'a> Owner for AggregatorHistoryBuffer<'a> { mod tests { use super::*; - impl<'info, 'a> Default for AggregatorHistoryBuffer<'a> { + impl<'a> Default for AggregatorHistoryBuffer<'a> { fn default() -> Self { unsafe { std::mem::zeroed() } } diff --git a/rust/switchboard-solana/src/prelude.rs b/rust/switchboard-solana/src/prelude.rs index 261c2289a..671a53b25 100644 --- a/rust/switchboard-solana/src/prelude.rs +++ b/rust/switchboard-solana/src/prelude.rs @@ -41,7 +41,8 @@ cfg_program! { pub use anchor_lang::prelude::*; pub use anchor_lang::{ - AnchorDeserialize, AnchorSerialize, Discriminator, InstructionData, Owner, ZeroCopy, + AccountDeserialize, AccountSerialize, AnchorDeserialize, AnchorSerialize, Discriminator, + InstructionData, Owner, ZeroCopy, }; pub use anchor_spl::associated_token::AssociatedToken; pub use anchor_spl::token::{Mint, Token, TokenAccount};