diff --git a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs index 7b15184c7..f602af292 100644 --- a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs +++ b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs @@ -15,24 +15,27 @@ pub struct PullLever { pub fn handle_pull_lever(accounts: &PullLever, name: &str) -> Result<(), ProgramError> { log("Hand is pulling the lever!"); - // Build the switch_power instruction data for the lever program: - // [disc=1] [name: u32 len + bytes] - // 128 bytes is enough for any reasonable name. + // Build the switch_power instruction data for the lever program. + // + // Wire format: [discriminator = 1] [name: u8 length prefix + bytes]. + // + // The lever's switch_power instruction takes `String<50>`, which Quasar + // serialises with a single-byte length prefix (matching every other + // Quasar program: account-data, close-account, rent, realloc, + // repository-layout). An earlier version of this builder used a u32 + // length prefix, which sent a malformed payload on every CPI call. + // + // 128 bytes is enough for any reasonable name (max 50 + 1 + 1 = 52). let mut data = [0u8; 128]; let name_bytes = name.as_bytes(); - let data_len = 1 + 4 + name_bytes.len(); + let data_len = 1 + 1 + name_bytes.len(); data[0] = 1; - - let len_bytes = (name_bytes.len() as u32).to_le_bytes(); - data[1] = len_bytes[0]; - data[2] = len_bytes[1]; - data[3] = len_bytes[2]; - data[4] = len_bytes[3]; + data[1] = name_bytes.len() as u8; let mut i = 0; while i < name_bytes.len() { - data[5 + i] = name_bytes[i]; + data[2 + i] = name_bytes[i]; i += 1; } diff --git a/basics/cross-program-invocation/quasar/hand/src/tests.rs b/basics/cross-program-invocation/quasar/hand/src/tests.rs index 8ec1c3232..05ede5562 100644 --- a/basics/cross-program-invocation/quasar/hand/src/tests.rs +++ b/basics/cross-program-invocation/quasar/hand/src/tests.rs @@ -30,10 +30,17 @@ fn power_account(address: Pubkey, is_on: bool) -> Account { } /// Build pull_lever instruction data (discriminator = 0). -/// Wire format: [disc=0] [name: String] +/// +/// Wire format: [discriminator = 0] [name: u8 length prefix + bytes]. +/// +/// The hand's pull_lever instruction takes `String<50>`, which Quasar +/// serialises with a single-byte length prefix. The CPI builder in +/// `pull_lever.rs` re-serialises the same name into the lever's +/// instruction data using the same u8 prefix. fn build_pull_lever(name: &str) -> Vec { - let mut data = vec![0u8]; // discriminator = 0 - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + name.len()); + data.push(0u8); // discriminator = 0 + data.push(name.len() as u8); data.extend_from_slice(name.as_bytes()); data } @@ -72,6 +79,14 @@ fn test_pull_lever_turns_on() { assert!(logs.contains("Hand is pulling"), "hand should log"); assert!(logs.contains("pulling the power switch"), "lever should log"); assert!(logs.contains("now on"), "power should turn on"); + // Verifies the CPI wire format: the lever logs the name it + // deserialised. A stale u32 length prefix on either the inbound + // `pull_lever` payload or the CPI to `switch_power` would corrupt + // this (e.g. "\0\0\0Al" instead of "Alice"). + assert!( + logs.contains("Alice"), + "name should round-trip through hand → lever CPI; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 1, "power should be on"); @@ -107,6 +122,10 @@ fn test_pull_lever_turns_off() { let logs = result.logs.join("\n"); assert!(logs.contains("now off"), "power should turn off"); + assert!( + logs.contains("Bob"), + "name should round-trip through hand → lever CPI; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 0, "power should be off"); diff --git a/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs b/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs index 527360342..832a8732b 100644 --- a/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs +++ b/basics/cross-program-invocation/quasar/lever/src/instructions/initialize.rs @@ -1,21 +1,21 @@ use { - crate::state::PowerStatus, + crate::state::{PowerStatus, PowerStatusInner}, quasar_lang::prelude::*, }; /// Accounts for initialising the power status (PDA seeded by "power"). #[derive(Accounts)] -pub struct InitializeLever<'info> { +pub struct InitializeLever { #[account(mut)] - pub payer: &'info mut Signer, - #[account(mut, init, payer = payer, seeds = [b"power"], bump)] - pub power: &'info mut Account, - pub system_program: &'info Program, + pub payer: Signer, + #[account(mut, init, payer = payer, seeds = PowerStatus::seeds(), bump)] + pub power: Account, + pub system_program: Program, } #[inline(always)] pub fn handle_initialize(accounts: &mut InitializeLever) -> Result<(), ProgramError> { - // Power starts off (false). - accounts.power.set_inner(PodBool::from(false)); + // Power starts off (false). Counter-style fixed-size set_inner takes only the inner value. + accounts.power.set_inner(PowerStatusInner { is_on: PodBool::from(false) }); Ok(()) } diff --git a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs index d5f376c16..52ac4401e 100644 --- a/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs +++ b/basics/cross-program-invocation/quasar/lever/src/instructions/switch_power.rs @@ -5,19 +5,24 @@ use { /// Accounts for toggling the power switch. #[derive(Accounts)] -pub struct SwitchPower<'info> { +pub struct SwitchPower { #[account(mut)] - pub power: &'info mut Account, + pub power: Account, } #[inline(always)] -pub fn handle_switch_power(accounts: &mut SwitchPower, _name: &str) -> Result<(), ProgramError> { +pub fn handle_switch_power(accounts: &mut SwitchPower, name: &str) -> Result<(), ProgramError> { let current: bool = accounts.power.is_on.into(); let new_state = !current; accounts.power.is_on = PodBool::from(new_state); // Quasar's log() takes &str — no format! in no_std. + // Logging the name verifies the wire format end-to-end: a stale u32 + // length prefix would surface here as a corrupted name (e.g. the + // first three bytes parsed as zeros, leaving "\0\0\0Al" instead of + // "Alice"). log("Someone is pulling the power switch!"); + log(name); if new_state { log("The power is now on."); diff --git a/basics/cross-program-invocation/quasar/lever/src/lib.rs b/basics/cross-program-invocation/quasar/lever/src/lib.rs index 00aa9464e..1922feca8 100644 --- a/basics/cross-program-invocation/quasar/lever/src/lib.rs +++ b/basics/cross-program-invocation/quasar/lever/src/lib.rs @@ -22,7 +22,7 @@ mod quasar_lever { /// Toggle the power switch. Logs who is pulling the lever. #[instruction(discriminator = 1)] - pub fn switch_power(ctx: Ctx, name: String) -> Result<(), ProgramError> { + pub fn switch_power(ctx: Ctx, name: String<50>) -> Result<(), ProgramError> { instructions::handle_switch_power(&mut ctx.accounts, name) } } diff --git a/basics/cross-program-invocation/quasar/lever/src/state.rs b/basics/cross-program-invocation/quasar/lever/src/state.rs index 4b4cc2abe..84610a4f3 100644 --- a/basics/cross-program-invocation/quasar/lever/src/state.rs +++ b/basics/cross-program-invocation/quasar/lever/src/state.rs @@ -1,7 +1,9 @@ use quasar_lang::prelude::*; /// Onchain power status: a single boolean toggle. -#[account(discriminator = 1)] +/// Derived as a PDA from the seed "power" (single global account). +#[account(discriminator = 1, set_inner)] +#[seeds(b"power")] pub struct PowerStatus { pub is_on: PodBool, } diff --git a/basics/cross-program-invocation/quasar/lever/src/tests.rs b/basics/cross-program-invocation/quasar/lever/src/tests.rs index b6005e4a5..ed2c32e12 100644 --- a/basics/cross-program-invocation/quasar/lever/src/tests.rs +++ b/basics/cross-program-invocation/quasar/lever/src/tests.rs @@ -47,10 +47,19 @@ fn build_initialize() -> Vec { } /// Build switch_power instruction data (discriminator = 1). -/// Wire format: [disc=1] [name: String] +/// +/// Wire format: [discriminator = 1] [name: u8 length prefix + bytes]. +/// +/// The lever's switch_power instruction takes `String<50>`, which Quasar +/// serialises with a single-byte length prefix (matching every other +/// Quasar program: account-data, close-account, rent, realloc, +/// repository-layout). An earlier version of this builder used a u32 +/// length prefix, which produced a malformed payload that happened to +/// pass because the handler ignored the deserialised name. fn build_switch_power(name: &str) -> Vec { - let mut data = vec![1u8]; // discriminator = 1 - data.extend_from_slice(&(name.len() as u32).to_le_bytes()); + let mut data = Vec::with_capacity(2 + name.len()); + data.push(1u8); // discriminator = 1 + data.push(name.len() as u8); data.extend_from_slice(name.as_bytes()); data } @@ -104,6 +113,12 @@ fn test_switch_power_on() { let logs = result.logs.join("\n"); assert!(logs.contains("pulling the power switch"), "should log switch"); assert!(logs.contains("now on"), "should say power is on"); + // Verifies wire format: a stale u32 length prefix would corrupt the + // deserialised name (e.g. "\0\0\0Al" instead of "Alice"). + assert!( + logs.contains("Alice"), + "deserialised name should round-trip exactly; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 1, "power should now be on"); @@ -128,6 +143,10 @@ fn test_switch_power_off() { let logs = result.logs.join("\n"); assert!(logs.contains("now off"), "should say power is off"); + assert!( + logs.contains("Bob"), + "deserialised name should round-trip exactly; logs: {logs}" + ); let account = result.account(&power_addr).unwrap(); assert_eq!(account.data[1], 0, "power should now be off"); diff --git a/tokens/token-extensions/basics/quasar/Cargo.toml b/tokens/token-extensions/basics/quasar/Cargo.toml index 46facab14..983c74fc5 100644 --- a/tokens/token-extensions/basics/quasar/Cargo.toml +++ b/tokens/token-extensions/basics/quasar/Cargo.toml @@ -26,6 +26,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/cpi-guard/quasar/Cargo.toml b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml index 4d7009639..9c158e5ad 100644 --- a/tokens/token-extensions/cpi-guard/quasar/Cargo.toml +++ b/tokens/token-extensions/cpi-guard/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/default-account-state/quasar/Cargo.toml b/tokens/token-extensions/default-account-state/quasar/Cargo.toml index 2d4d4482b..91fe73004 100644 --- a/tokens/token-extensions/default-account-state/quasar/Cargo.toml +++ b/tokens/token-extensions/default-account-state/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/group/quasar/Cargo.toml b/tokens/token-extensions/group/quasar/Cargo.toml index 5c02b0cd8..45a9244ef 100644 --- a/tokens/token-extensions/group/quasar/Cargo.toml +++ b/tokens/token-extensions/group/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/immutable-owner/quasar/Cargo.toml b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml index f8f488857..3ec07d69e 100644 --- a/tokens/token-extensions/immutable-owner/quasar/Cargo.toml +++ b/tokens/token-extensions/immutable-owner/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/interest-bearing/quasar/Cargo.toml b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml index 6e9ec58dd..c918193d5 100644 --- a/tokens/token-extensions/interest-bearing/quasar/Cargo.toml +++ b/tokens/token-extensions/interest-bearing/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/memo-transfer/quasar/Cargo.toml b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml index 07f0ac8e7..2c8abc21f 100644 --- a/tokens/token-extensions/memo-transfer/quasar/Cargo.toml +++ b/tokens/token-extensions/memo-transfer/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml index c6c41957e..5f12408a7 100644 --- a/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml +++ b/tokens/token-extensions/mint-close-authority/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/non-transferable/quasar/Cargo.toml b/tokens/token-extensions/non-transferable/quasar/Cargo.toml index be7d11b69..b72ebeff5 100644 --- a/tokens/token-extensions/non-transferable/quasar/Cargo.toml +++ b/tokens/token-extensions/non-transferable/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml index 470299022..875193ec7 100644 --- a/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml +++ b/tokens/token-extensions/permanent-delegate/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" } diff --git a/tokens/token-extensions/transfer-fee/quasar/Cargo.toml b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml index 907756375..cfa93d5a9 100644 --- a/tokens/token-extensions/transfer-fee/quasar/Cargo.toml +++ b/tokens/token-extensions/transfer-fee/quasar/Cargo.toml @@ -25,6 +25,6 @@ quasar-spl = { git = "https://github.com/blueshift-gg/quasar", branch = "master" solana-instruction = { version = "3.2.0" } [dev-dependencies] -quasar-svm = { version = "0.1" } +quasar-svm = { git = "https://github.com/blueshift-gg/quasar-svm" } spl-token-interface = { version = "2.0.0" } solana-program-pack = { version = "3.1.0" }