-
Notifications
You must be signed in to change notification settings - Fork 10
feat: Add DelegateWithActions #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -17,7 +17,14 @@ name = "dlp" | |||||
|
|
||||||
| [features] | ||||||
| no-entrypoint = [] | ||||||
| sdk = ["no-entrypoint"] | ||||||
| sdk = [ | ||||||
| "no-entrypoint", | ||||||
| "dep:solana-sdk", | ||||||
| "dep:rand", | ||||||
| "dep:sha2", | ||||||
| "dep:x25519-dalek", | ||||||
| "dep:curve25519-dalek", | ||||||
| ] | ||||||
| program = [ | ||||||
| "dep:pinocchio", | ||||||
| "dep:pinocchio-log", | ||||||
|
|
@@ -60,7 +67,12 @@ pinocchio-associated-token-account = { version = "0.3.0" , optional = true } | |||||
| solana-address = { version = "2.0", features = ["bytemuck", "decode", "syscalls", "curve25519", "std"] } | ||||||
|
|
||||||
| # manually resolves the conflict with a pinned version of serde | ||||||
| serde = "=1.0.226" | ||||||
| serde = { version = "=1.0.226", features = ["derive"] } | ||||||
| sha2 = { version = "0.10", optional = true } | ||||||
| x25519-dalek = { version = "2", features = ["static_secrets"], optional = true } | ||||||
| curve25519-dalek = { version = "4", optional = true } | ||||||
| solana-sdk = { version = ">=1.16", optional = true } | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unbounded
💡 Suggested fix-solana-sdk = { version = ">=1.16", optional = true }
+solana-sdk = { version = ">=1.16, <3.0.0", optional = true }This would match the constraint already used for 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| rand = { version = "=0.8.5", features = ["small_rng"], optional = true } | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exact
🛡️ Proposed fix-rand = { version = "=0.8.5", features = ["small_rng"], optional = true }
+rand = { version = "^0.8.5", features = ["small_rng"], optional = true }The same relaxation should also be applied to the identical dev-dependency pin on line 80. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| [dev-dependencies] | ||||||
| assertables = "9.8.2" | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,5 +4,8 @@ build: | |||||||||||||||||
| test: | ||||||||||||||||||
| RUST_LOG=off cargo test-sbf --features unit_test_config | ||||||||||||||||||
|
|
||||||||||||||||||
| lint: | ||||||||||||||||||
| cargo clippy -- --deny=warnings | ||||||||||||||||||
|
Comment on lines
+7
to
+8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 🧩 Analysis chain🏁 Script executed: # First, examine the full Makefile context
cat MakefileRepository: magicblock-labs/delegation-program Length of output: 232 🏁 Script executed: # Find all Cargo.toml files and check their features sections
fd 'Cargo.toml' --exec grep -A 20 '\[features\]' {}Repository: magicblock-labs/delegation-program Length of output: 722 🏁 Script executed: # Also check the root directory structure to understand if this is a workspace
ls -la | head -20Repository: magicblock-labs/delegation-program Length of output: 1174 🏁 Script executed: # Verify what code is gated by sdk feature to confirm it's material
rg '#\[cfg.*feature.*sdk' src/ --type rust -A 2 | head -30Repository: magicblock-labs/delegation-program Length of output: 1183 🏁 Script executed: # Check if there are test files or cfg(test) blocks in src/
rg '#\[cfg\(test\)' src/ --type rustRepository: magicblock-labs/delegation-program Length of output: 299
Two gaps:
🔧 Proposed fix lint:
- cargo clippy -- --deny=warnings
+ cargo clippy --all-targets --all-features -- -D warningsIf linting every feature combination is too broad, at minimum mirror the test target's feature set: lint:
- cargo clippy -- --deny=warnings
+ cargo clippy --all-targets --features sdk,unit_test_config -- -D warnings📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| fmt: | ||||||||||||||||||
| cargo +nightly fmt | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| use borsh::{BorshDeserialize, BorshSerialize}; | ||
| use serde::{Deserialize, Serialize}; | ||
| use solana_program::pubkey::Pubkey; | ||
|
|
||
| #[derive(Default, Debug, BorshSerialize, BorshDeserialize)] | ||
| #[derive( | ||
| Default, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize, | ||
| )] | ||
|
Comment on lines
+2
to
+7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find all Cargo.toml files in the repository
fd -n 'Cargo.toml$'Repository: magicblock-labs/delegation-program Length of output: 308 🏁 Script executed: #!/bin/bash
# Find all Cargo.toml files in the repository
fd 'Cargo.toml$'Repository: magicblock-labs/delegation-program Length of output: 213 🏁 Script executed: #!/bin/bash
# Check root Cargo.toml for solana-program dependency
echo "=== Root Cargo.toml ==="
cat Cargo.toml
echo -e "\n=== test-delegation program Cargo.toml ==="
cat tests/integration/programs/test-delegation/Cargo.tomlRepository: magicblock-labs/delegation-program Length of output: 3750 🏁 Script executed: #!/bin/bash
# Read the src/args/delegate.rs file around the mentioned lines
cat -n src/args/delegate.rs | head -20Repository: magicblock-labs/delegation-program Length of output: 796 🏁 Script executed: #!/bin/bash
# Search for other structs with Pubkey and Serialize derives
rg -B3 'Serialize.*Deserialize' --type rust | head -40
# Also check if this compiles by looking for build outputs or CI
echo "=== Looking for CI configuration ==="
fd -e yml -e yaml | head -10Repository: magicblock-labs/delegation-program Length of output: 2342 🏁 Script executed: #!/bin/bash
# Search for any uses of Pubkey with serde in the codebase
rg 'Pubkey' src/args/delegate.rs -A 2 -B 2
# Check if there's any solana-program documentation or version notes
rg 'solana-program' . --type toml -A 3
# Look for any error messages or compilation warnings
fd -e log -e txtRepository: magicblock-labs/delegation-program Length of output: 12041 🌐 Web query:
💡 Result:
Cargo.toml [dependencies]
serde = { version = "1", features = ["derive"] }
# Enable Pubkey's Serde support
solana-program = { version = "4", features = ["serde"] }
# (Alternatively, depend on solana-pubkey directly with features = ["serde"])Then you can use it normally: use serde::{Serialize, Deserialize};
use solana_program::pubkey::Pubkey;
#[derive(Serialize, Deserialize)]
struct X {
owner: Pubkey,
}Sources Citations:
Enable The solana-program = { version = ">=1.16, <3.0.0", features = ["serde"] }Without this, deriving 🤖 Prompt for AI Agents |
||
| pub struct DelegateArgs { | ||
| /// The frequency at which the validator should commit the account data | ||
| /// if no commit is triggered by the owning program | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| use serde::{Deserialize, Serialize}; | ||
| use solana_program::{instruction::Instruction, pubkey::Pubkey}; | ||
|
|
||
| use super::DelegateArgs; | ||
| use crate::compact; | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize)] | ||
| pub struct DelegateWithActionsArgs { | ||
| /// Standard delegation parameters. | ||
| pub delegate: DelegateArgs, | ||
|
|
||
| /// Compact post-delegation actions. | ||
| pub actions: PostDelegationActions, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize)] | ||
| pub struct PostDelegationActions { | ||
| /// Number of signer pubkeys in the `pubkeys` prefix. | ||
| /// First `signer_count` entries of `pubkeys` are required signers. | ||
| pub signer_count: u8, | ||
|
|
||
| /// Shared pubkey table. Account metas and program IDs reference this table by index. | ||
| pub pubkeys: Vec<Pubkey>, | ||
|
|
||
| /// Instruction payload in compact cleartext or encrypted bytes. | ||
| pub instructions: Instructions, | ||
| } | ||
|
|
||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| pub enum Instructions { | ||
| /// Compact cleartext instructions. | ||
| ClearText { | ||
| instructions: Vec<compact::Instruction>, | ||
| }, | ||
|
|
||
| /// Encrypted compact instruction bytes. | ||
| Encrypted { instructions: Vec<u8> }, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize)] | ||
| pub struct DecryptedInstructions { | ||
| /// Sender-provided nonce/salt to randomize ciphertext so identical | ||
| /// plaintext does not always map to identical encrypted bytes. | ||
| pub random_salt: u64, | ||
|
|
||
| /// Decrypted instructions ready for execution. | ||
| pub instructions: Vec<Instruction>, | ||
| } | ||
|
Comment on lines
+40
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: rg -n 'DecryptedInstructions' --type rustRepository: magicblock-labs/delegation-program Length of output: 149 🏁 Script executed: cat -n src/args/delegate_with_actions.rs | sed -n '35,55p'Repository: magicblock-labs/delegation-program Length of output: 602 🏁 Script executed: cat -n src/args/delegate_with_actions.rs | sed -n '1,60p'Repository: magicblock-labs/delegation-program Length of output: 1869 🏁 Script executed: rg -n 'decrypt' --type rust -iRepository: magicblock-labs/delegation-program Length of output: 996 🏁 Script executed: cat -n tests/test_delegate_with_actions.rs | sed -n '150,165p'Repository: magicblock-labs/delegation-program Length of output: 836
The struct is not referenced anywhere in the codebase. The test decrypts into 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| const ACCOUNT_INDEX_MASK: u8 = 0b0011_1111; | ||
| const SIGNER_MASK: u8 = 0b0100_0000; | ||
| const WRITABLE_MASK: u8 = 0b1000_0000; | ||
|
|
||
| /// | ||
| /// MAX_PUBKEYS = 64 | ||
| /// | ||
| pub const MAX_PUBKEYS: u8 = ACCOUNT_INDEX_MASK + 1; | ||
|
|
||
| /// Compact account meta packed into one byte. | ||
| /// Bits `0..=5` encode the pubkey-table index (`0..MAX_PUBKEYS-1`). | ||
| /// Bit `6` is `is_signer`, and bit `7` is `is_writable`. | ||
| #[derive(Clone, Copy, Debug, Serialize, Deserialize)] | ||
| pub struct AccountMeta(u8); | ||
|
|
||
| impl AccountMeta { | ||
| pub fn new(index: u8, is_signer: bool) -> Self { | ||
| Self::try_new(index, is_signer, true).expect("index is out of range") | ||
| } | ||
| pub fn new_readonly(index: u8, is_signer: bool) -> Self { | ||
| Self::try_new(index, is_signer, false).expect("index is out of range") | ||
| } | ||
|
Comment on lines
+18
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
These constructors call 💡 Minor improvement: include the bad index in the panic pub fn new(index: u8, is_signer: bool) -> Self {
- Self::try_new(index, is_signer, true).expect("index is out of range")
+ Self::try_new(index, is_signer, true)
+ .unwrap_or_else(|| panic!("compact AccountMeta index {index} is out of range (max {})", MAX_PUBKEYS - 1))
}
pub fn new_readonly(index: u8, is_signer: bool) -> Self {
- Self::try_new(index, is_signer, false).expect("index is out of range")
+ Self::try_new(index, is_signer, false)
+ .unwrap_or_else(|| panic!("compact AccountMeta index {index} is out of range (max {})", MAX_PUBKEYS - 1))
}🤖 Prompt for AI Agents |
||
|
|
||
| pub fn try_new( | ||
| index: u8, | ||
| is_signer: bool, | ||
| is_writable: bool, | ||
| ) -> Option<Self> { | ||
| if index >= MAX_PUBKEYS { | ||
| return None; | ||
| } | ||
| let mut packed = index; | ||
| if is_signer { | ||
| packed |= SIGNER_MASK; | ||
| } | ||
| if is_writable { | ||
| packed |= WRITABLE_MASK; | ||
| } | ||
| Some(Self(packed)) | ||
| } | ||
|
|
||
| pub fn index(self) -> u8 { | ||
| self.0 & ACCOUNT_INDEX_MASK | ||
| } | ||
|
|
||
| pub fn is_signer(self) -> bool { | ||
| (self.0 & SIGNER_MASK) != 0 | ||
| } | ||
|
|
||
| pub fn is_writable(self) -> bool { | ||
| (self.0 & WRITABLE_MASK) != 0 | ||
| } | ||
|
|
||
| pub fn set_index(&mut self, new_index: u8) { | ||
| *self = Self::try_new(new_index, self.is_signer(), self.is_writable()) | ||
| .expect("index is out of range"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| use crate::compact; | ||
|
|
||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| pub struct Instruction { | ||
| pub program_id: u8, | ||
| pub accounts: Vec<compact::AccountMeta>, | ||
| pub data: Vec<u8>, | ||
| } | ||
|
|
||
| impl Instruction { | ||
| pub fn from_instruction( | ||
| ix: solana_program::instruction::Instruction, | ||
| index_of: &mut impl FnMut( | ||
| /*account_key*/ solana_program::pubkey::Pubkey, | ||
| /*signer*/ bool, | ||
| ) -> u8, | ||
| ) -> Instruction { | ||
| Instruction { | ||
| program_id: index_of(ix.program_id, false), | ||
|
|
||
| accounts: ix | ||
| .accounts | ||
| .iter() | ||
| .map(|meta| { | ||
| compact::AccountMeta::try_new( | ||
| index_of(meta.pubkey, meta.is_signer), | ||
| meta.is_signer, | ||
| meta.is_writable, | ||
| ) | ||
| .expect("compact account index must fit in 6 bits") | ||
| }) | ||
| .collect(), | ||
|
|
||
| data: ix.data, | ||
| } | ||
snawaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
snawaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| mod account_meta; | ||
| mod instruction; | ||
|
|
||
| pub use account_meta::*; | ||
| pub use instruction::*; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Nightly fmt: consider pinning the nightly version for reproducibility.
Using
cargo +nightly fmtis fine for accessing unstable formatting options, but an unpinned nightly means formatting results can change day-to-day. Consider pinning to a specific nightly date (e.g.,nightly-2025-12-01) to avoid spurious CI failures.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to pin and where?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snawaz, to pin the nightly version, you need to specify a date in the formatnightly-YYYY-MM-DDinstead of justnightly. Here's how:Where: Line 54 in
.github/workflows/run-tests.ymlCurrent code:
Pinned version:
How to pick a date:
nightly-2026-02-01)--toolchainflag and the+nightly-*prefix should use the same dateYYYY-MM-DDThis ensures the formatter behavior stays consistent across CI runs, preventing unexpected formatting changes when nightly Rust updates.