Skip to content
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

Release #109

Merged
merged 56 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
19dcb3e
feat(spending_limits): allow spending limits for non-members
vovacodes Aug 8, 2024
dff1ac6
fix(multisig_add_member)
ogmedia Aug 12, 2024
3b7e714
fix(multisig_add_spending_limit)
ogmedia Aug 12, 2024
8264ed0
fix(close_account)
ogmedia Aug 12, 2024
e801095
fix(cancellation_realloc)
ogmedia Aug 12, 2024
660ea21
Merge pull request #108 from Squads-Protocol/feat/spending-limits-dro…
ogmedia Aug 12, 2024
ce9bd91
Execute optimizations (#110)
ogmedia Aug 19, 2024
a0a8afa
chore(typo): fixed
ogmedia Aug 19, 2024
bb946c6
fix: Heap Optimization (#111)
0xRigel Aug 19, 2024
f25fc92
chore(cancel-tests): added test and sdk methods for cancel realloc
ogmedia Aug 19, 2024
12a12c8
chore(cancel-test): verify account size changed
ogmedia Aug 20, 2024
1fdb66f
Feat: Incremental Transaction Uploading (#113)
0xRigel Aug 21, 2024
63c4975
Feat: Custom Bump Allocator (#114)
0xRigel Aug 21, 2024
ac11589
WIP
0xRigel Sep 5, 2024
63a068d
refactor: vault_transaction_from_buffer
0xRigel Sep 6, 2024
a481def
feat: wrap context for createFromBuffer
0xRigel Sep 11, 2024
5ab4abc
add: comments
0xRigel Sep 11, 2024
c173a71
chore: fmt
0xRigel Sep 11, 2024
abff445
refactor: wrap ProposalVote context inside ProposalCancelV2
0xRigel Sep 11, 2024
3057116
fix: typo
0xRigel Sep 11, 2024
4a23a3d
fix: comments from certora preliminary findings
0xRigel Sep 13, 2024
bfd31d8
fix: transaction_buffer_close seed constraints
0xRigel Sep 22, 2024
1d5d7bc
Merge pull request #122 from Squads-Protocol/comment-fixes
0xRigel Sep 24, 2024
af23d63
Merge pull request #124 from Squads-Protocol/fix-buffer-close-seeds
0xRigel Sep 24, 2024
a761ea7
Merge pull request #121 from Squads-Protocol/comment-fixes
0xRigel Sep 24, 2024
714f71e
Merge pull request #123 from Squads-Protocol/fix-buffer-close-seeds
0xRigel Sep 24, 2024
7b7c0c5
fix: confusing semantics
0xRigel Sep 30, 2024
1ae5408
Merge pull request #126 from Squads-Protocol/transaction-buffer-impro…
0xRigel Sep 30, 2024
17983bc
fix: buffer vec size
0xRigel Sep 30, 2024
7596c1a
small comment fixes
0xRigel Sep 30, 2024
0996f21
refactor: lamport mutation into system transfer
0xRigel Oct 6, 2024
ca85338
feat: u8 for buffer seeding
0xRigel Oct 7, 2024
b445684
Merge pull request #127 from Squads-Protocol/final-review-fixes
0xRigel Oct 7, 2024
74fafb1
fix: bind transaction_buffer seeds to creator
0xRigel Oct 9, 2024
ff9c93e
remove: current member check for transaction_buffer_close
0xRigel Oct 9, 2024
749a81d
fix: creator check on vault_tx_create_from_buffer
0xRigel Oct 9, 2024
712ad05
remove: multisig mut in transaction_buffer instructions
0xRigel Oct 9, 2024
821e9a8
remove: testing code from allocator
0xRigel Oct 9, 2024
a663f7f
fix: tests
0xRigel Oct 9, 2024
7551bf0
fix: MAX_BUFFER_SIZE saturation in invariant check
0xRigel Oct 10, 2024
676766d
remove: heap testing instruction
0xRigel Oct 10, 2024
d53c7f9
Merge pull request #128 from Squads-Protocol/osec-fixes
0xRigel Oct 11, 2024
3afed84
deprecate: multisig_create
0xRigel Oct 14, 2024
1fb5dc1
refactor: sdk for multisig_create deprecation
0xRigel Oct 14, 2024
a19df49
refactor: tests for multisig_create deprecation
0xRigel Oct 14, 2024
d7f1698
add: custom deprecation error
0xRigel Oct 14, 2024
e0b6c55
tests: uncomment and fix
0xRigel Oct 14, 2024
a3cad82
Merge pull request #131 from Squads-Protocol/deprecate-mulitisig-create
0xRigel Oct 15, 2024
87ae2c9
(tests)fix: broken tests from multisig_create deprecation
0xRigel Oct 15, 2024
6d5235d
bump: program version to 2.1.0
0xRigel Oct 21, 2024
7360d8a
fix(program): space calculation transaction_buffer
0xRigel Nov 7, 2024
b9b1c36
bump: Anchor.toml solana-cli to 1.18.16
0xRigel Nov 7, 2024
39c92c1
bump(idl): to 2.1.0
0xRigel Nov 7, 2024
2c4d169
modify: max transaction_buffer size
0xRigel Nov 19, 2024
64af733
fix(tests): accomodate new max buffer size
0xRigel Nov 19, 2024
dcac867
fix(tests): custom heap
0xRigel Nov 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading