Skip to content

Commit

Permalink
Merge pull request #138 from Squads-Protocol/release
Browse files Browse the repository at this point in the history
Release - Final V4 Upgrade
  • Loading branch information
0xRigel-squads authored Nov 22, 2024
2 parents 9439231 + dcac867 commit 8ba270d
Show file tree
Hide file tree
Showing 70 changed files with 6,291 additions and 680 deletions.
2 changes: 1 addition & 1 deletion Anchor.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[toolchain]
anchor_version = "0.29.0" # `anchor-cli` version to use
solana_version = "1.17.0" # Solana version to use
solana_version = "1.18.16" # Solana version to use

[features]
seeds = false
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 28 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
{
"private": true,
"workspaces": [
"sdk/*"
],
"scripts": {
"build": "turbo run build",
"test": "turbo run build && anchor test -- --features=testing && echo \"\n⚠️ Don't forget to recompile the .so file before deployment\n\"",
"pretest": "mkdir -p target/deploy && cp ./test-program-keypair.json ./target/deploy/squads_multisig_program-keypair.json",
"ts": "turbo run ts && yarn tsc --noEmit"
},
"devDependencies": {
"@solana/spl-token": "*",
"@solana/spl-memo": "^0.2.3",
"@types/bn.js": "5.1.0",
"@types/mocha": "10.0.1",
"@types/node-fetch": "2.6.2",
"mocha": "10.2.0",
"prettier": "2.6.2",
"ts-node": "10.9.1",
"turbo": "1.6.3",
"typescript": "*"
},
"resolutions": {
"@solana/web3.js": "1.70.3",
"@solana/spl-token": "0.3.6",
"typescript": "4.9.4"
}
"private": true,
"workspaces": [
"sdk/*"
],
"scripts": {
"build": "turbo run build",
"test:detached": "turbo run build && anchor test --detach -- --features=testing && echo \"\n⚠️ Don't forget to recompile the .so file before deployment\n\"",
"test": "turbo run build && anchor test -- --features=testing && echo \"\n⚠️ Don't forget to recompile the .so file before deployment\n\"",
"pretest": "mkdir -p target/deploy && cp ./test-program-keypair.json ./target/deploy/squads_multisig_program-keypair.json",
"ts": "turbo run ts && yarn tsc --noEmit"
},
"devDependencies": {
"@solana/spl-token": "*",
"@solana/spl-memo": "^0.2.3",
"@types/bn.js": "5.1.0",
"@types/mocha": "10.0.1",
"@types/node-fetch": "2.6.2",
"mocha": "10.2.0",
"prettier": "2.6.2",
"ts-node": "10.9.1",
"turbo": "1.6.3",
"typescript": "*"
},
"resolutions": {
"@solana/web3.js": "1.70.3",
"@solana/spl-token": "0.3.6",
"typescript": "4.9.4"
}
}
5 changes: 3 additions & 2 deletions programs/squads_multisig_program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "squads-multisig-program"
version = "2.0.0"
version = "2.1.0"
description = "Squads Multisig Program V4"
edition = "2021"
license-file = "../../LICENSE"
Expand All @@ -10,12 +10,13 @@ crate-type = ["cdylib", "lib"]
name = "squads_multisig_program"

[features]
default = ["custom-heap"]
custom-heap = []
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
testing = []
default = []

[dependencies]
anchor-lang = { version = "=0.29.0", features = ["allow-missing-optionals"] }
Expand Down
133 changes: 133 additions & 0 deletions programs/squads_multisig_program/src/allocator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
Optimizing Bump Heap Allocation
Objective: Increase available heap memory while maintaining flexibility in program invocation.
1. Initial State: Default 32 KiB Heap
Memory Layout:
0x300000000 0x300008000
| |
v v
[--------------------]
^ ^
| |
VM Lower VM Upper
Boundary Boundary
Default Allocator (Allocates Backwards / Top Down) (Default 32 KiB):
0x300000000 0x300008000
| |
[--------------------]
^
|
Allocation starts here (SAFE)
2. Naive Approach: Increase HEAP_LENGTH to 8 * 32 KiB + Default Allocator
Memory Layout with Increased HEAP_LENGTH:
0x300000000 0x300008000 0x300040000
| | |
v v v
[--------------------|------------------------------------|]
^ ^ ^
| | |
VM Lower VM Upper Allocation starts here
Boundary Boundary (ACCESS VIOLATION!)
Issue: Access violation occurs without requestHeapFrame, requiring it for every transaction.
3. Optimized Solution: Forward Allocation with Flexible Heap Usage
Memory Layout (Same as Naive Approach):
0x300000000 0x300008000 0x300040000
| | |
v v v
[--------------------|------------------------------------|]
^ ^ ^
| | |
VM Lower VM Upper Allocator & VM
Boundary Boundary Heap Limit
Forward Allocator Behavior:
a) Without requestHeapFrame:
0x300000000 0x300008000
| |
[--------------------]
^ ^
| |
VM Lower VM Upper
Boundary Boundary
Allocation
starts here (SAFE)
b) With requestHeapFrame:
0x300000000 0x300008000 0x300040000
| | |
[--------------------|------------------------------------|]
^ ^ ^
| | |
VM Lower | VM Upper
Boundary Boundary
Allocation Allocation continues Maximum allocation
starts here with requestHeapFrame with requestHeapFrame
(SAFE)
Key Advantages:
1. Compatibility: Functions without requestHeapFrame for allocations ≤32 KiB.
2. Extensibility: Supports larger allocations when requestHeapFrame is invoked.
3. Efficiency: Eliminates mandatory requestHeapFrame calls for all transactions.
Conclusion:
The forward allocation strategy offers a robust solution, providing both backward
compatibility for smaller heap requirements and the flexibility to utilize extended
heap space when necessary.
The following allocator is a copy of the bump allocator found in
solana_program::entrypoint and
https://github.com/solana-labs/solana-program-library/blob/master/examples/rust/custom-heap/src/entrypoint.rs
but with changes to its HEAP_LENGTH and its
starting allocation address.
*/

use solana_program::entrypoint::HEAP_START_ADDRESS;
use std::{alloc::Layout, mem::size_of, ptr::null_mut};

/// Length of the memory region used for program heap.
pub const HEAP_LENGTH: usize = 8 * 32 * 1024;

struct BumpAllocator;

unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
const POS_PTR: *mut usize = HEAP_START_ADDRESS as *mut usize;
const TOP_ADDRESS: usize = HEAP_START_ADDRESS as usize + HEAP_LENGTH;
const BOTTOM_ADDRESS: usize = HEAP_START_ADDRESS as usize + size_of::<*mut u8>();
let mut pos = *POS_PTR;
if pos == 0 {
// First time, set starting position to bottom address
pos = BOTTOM_ADDRESS;
}
// Align the position upwards
pos = (pos + layout.align() - 1) & !(layout.align() - 1);
let next_pos = pos.saturating_add(layout.size());
if next_pos > TOP_ADDRESS {
return null_mut();
}
*POS_PTR = next_pos;
pos as *mut u8
}

#[inline]
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
// I'm a bump allocator, I don't free
}
}

// Only use the allocator if we're not in a no-entrypoint context
#[cfg(not(feature = "no-entrypoint"))]
#[global_allocator]
static A: BumpAllocator = BumpAllocator;
10 changes: 10 additions & 0 deletions programs/squads_multisig_program/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,14 @@ pub enum MultisigError {
BatchNotEmpty,
#[msg("Invalid SpendingLimit amount")]
SpendingLimitInvalidAmount,
#[msg("Invalid Instruction Arguments")]
InvalidInstructionArgs,
#[msg("Final message buffer hash doesnt match the expected hash")]
FinalBufferHashMismatch,
#[msg("Final buffer size cannot exceed 4000 bytes")]
FinalBufferSizeExceeded,
#[msg("Final buffer size mismatch")]
FinalBufferSizeMismatch,
#[msg("multisig_create has been deprecated. Use multisig_create_v2 instead.")]
MultisigCreateDeprecated,
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ impl BatchExecuteTransaction<'_> {
let multisig = &mut ctx.accounts.multisig;
let proposal = &mut ctx.accounts.proposal;
let batch = &mut ctx.accounts.batch;
let transaction = &mut ctx.accounts.transaction;

// NOTE: After `take()` is called, the VaultTransaction is reduced to
// its default empty value, which means it should no longer be referenced or
// used after this point to avoid faulty behavior.
// Instead only make use of the returned `transaction` value.
let transaction = ctx.accounts.transaction.take();

let multisig_key = multisig.key();
let batch_key = batch.key();
Expand All @@ -121,7 +126,7 @@ impl BatchExecuteTransaction<'_> {
&[batch.vault_bump],
];

let transaction_message = &transaction.message;
let transaction_message = transaction.message;
let num_lookups = transaction_message.address_table_lookups.len();

let message_account_infos = ctx
Expand Down Expand Up @@ -149,11 +154,13 @@ impl BatchExecuteTransaction<'_> {
let protected_accounts = &[proposal.key(), batch_key];

// Execute the transaction message instructions one-by-one.
// NOTE: `execute_message()` calls `self.to_instructions_and_accounts()`
// which in turn calls `take()` on
// `self.message.instructions`, therefore after this point no more
// references or usages of `self.message` should be made to avoid
// faulty behavior.
executable_message.execute_message(
&vault_seeds
.iter()
.map(|seed| seed.to_vec())
.collect::<Vec<Vec<u8>>>(),
vault_seeds,
&ephemeral_signer_seeds,
protected_accounts,
)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,6 @@ impl<'info> ConfigTransactionExecute<'info> {
members,
destinations,
} => {
// SpendingLimit members must all be members of the multisig.
for sl_member in members.iter() {
require!(
multisig.is_member(*sl_member).is_some(),
MultisigError::NotAMember
);
}

let (spending_limit_key, spending_limit_bump) = Pubkey::find_program_address(
&[
SEED_PREFIX,
Expand Down
12 changes: 10 additions & 2 deletions programs/squads_multisig_program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ pub use multisig_add_spending_limit::*;
pub use multisig_config::*;
pub use multisig_create::*;
pub use multisig_remove_spending_limit::*;
pub use program_config_init::*;
pub use program_config::*;
pub use program_config_init::*;
pub use proposal_activate::*;
pub use proposal_create::*;
pub use proposal_vote::*;
pub use spending_limit_use::*;
pub use transaction_accounts_close::*;
pub use transaction_buffer_close::*;
pub use transaction_buffer_create::*;
pub use transaction_buffer_extend::*;
pub use vault_transaction_create::*;
pub use vault_transaction_create_from_buffer::*;
pub use vault_transaction_execute::*;

mod batch_add_transaction;
Expand All @@ -26,12 +30,16 @@ mod multisig_add_spending_limit;
mod multisig_config;
mod multisig_create;
mod multisig_remove_spending_limit;
mod program_config_init;
mod program_config;
mod program_config_init;
mod proposal_activate;
mod proposal_create;
mod proposal_vote;
mod spending_limit_use;
mod transaction_accounts_close;
mod transaction_buffer_close;
mod transaction_buffer_create;
mod transaction_buffer_extend;
mod vault_transaction_create;
mod vault_transaction_create_from_buffer;
mod vault_transaction_execute;
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ pub struct MultisigAddSpendingLimitArgs {
/// The reset period of the spending limit.
/// When it passes, the remaining amount is reset, unless it's `Period::OneTime`.
pub period: Period,
/// Members of the multisig that can use the spending limit.
/// In case a member is removed from the multisig, the spending limit will remain existent
/// (until explicitly deleted), but the removed member will not be able to use it anymore.
/// Members of the Spending Limit that can use it.
/// Don't have to be members of the multisig.
pub members: Vec<Pubkey>,
/// The destination addresses the spending limit is allowed to sent funds to.
/// If empty, funds can be sent to any address.
Expand Down Expand Up @@ -73,14 +72,6 @@ impl MultisigAddSpendingLimit<'_> {

// `spending_limit` is partially checked via its seeds.

// SpendingLimit members must all be members of the multisig.
for sl_member in self.spending_limit.members.iter() {
require!(
self.multisig.is_member(*sl_member).is_some(),
MultisigError::NotAMember
);
}

Ok(())
}

Expand All @@ -94,6 +85,10 @@ impl MultisigAddSpendingLimit<'_> {
) -> Result<()> {
let spending_limit = &mut ctx.accounts.spending_limit;

// Make sure there are no duplicate keys in this direct invocation by sorting so the invariant will catch
let mut sorted_members = args.members;
sorted_members.sort();

spending_limit.multisig = ctx.accounts.multisig.key();
spending_limit.create_key = args.create_key;
spending_limit.vault_index = args.vault_index;
Expand All @@ -103,7 +98,7 @@ impl MultisigAddSpendingLimit<'_> {
spending_limit.remaining_amount = args.amount;
spending_limit.last_reset = Clock::get()?.unix_timestamp;
spending_limit.bump = ctx.bumps.spending_limit;
spending_limit.members = args.members;
spending_limit.members = sorted_members;
spending_limit.destinations = args.destinations;

spending_limit.invariant()?;
Expand Down
Loading

0 comments on commit 8ba270d

Please sign in to comment.