Skip to content

Commit 498cd8d

Browse files
committed
feat: add multisig state
1 parent 4f47ad2 commit 498cd8d

File tree

4 files changed

+220
-0
lines changed

4 files changed

+220
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod account_state;
22
mod mint;
3+
mod multisig;
34
mod token;
45

56
pub use account_state::*;
67
pub use mint::*;
8+
pub use multisig::*;
79
pub use token::*;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use core::mem::size_of;
2+
use pinocchio::{
3+
account_info::{AccountInfo, Ref},
4+
program_error::ProgramError,
5+
pubkey::Pubkey,
6+
};
7+
8+
use crate::{instructions::MAX_MULTISIG_SIGNERS, ID};
9+
10+
/// Multisignature data.
11+
#[repr(C)]
12+
pub struct Multisig {
13+
/// Number of signers required
14+
m: u8,
15+
/// Number of valid signers
16+
n: u8,
17+
/// Is `true` if this structure has been initialized
18+
is_initialized: u8,
19+
/// Signer public keys
20+
signers: [Pubkey; MAX_MULTISIG_SIGNERS],
21+
}
22+
23+
impl Multisig {
24+
/// The length of the `Multisig` account data.
25+
pub const LEN: usize = size_of::<Multisig>();
26+
27+
/// Return a `Multisig` from the given account info.
28+
///
29+
/// This method performs owner and length validation on `AccountInfo`, safe borrowing
30+
/// the account data.
31+
#[inline]
32+
pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Multisig>, ProgramError> {
33+
if account_info.data_len() != Self::LEN {
34+
return Err(ProgramError::InvalidAccountData);
35+
}
36+
if !account_info.is_owned_by(&ID) {
37+
return Err(ProgramError::InvalidAccountOwner);
38+
}
39+
Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
40+
Self::from_bytes_unchecked(data)
41+
}))
42+
}
43+
44+
/// Return a `Multisig` from the given account info.
45+
///
46+
/// This method performs owner and length validation on `AccountInfo`, but does not
47+
/// perform the borrow check.
48+
///
49+
/// # Safety
50+
///
51+
/// The caller must ensure that it is safe to borrow the account data (e.g., there are
52+
/// no mutable borrows of the account data).
53+
#[inline]
54+
pub unsafe fn from_account_info_unchecked(
55+
account_info: &AccountInfo,
56+
) -> Result<&Self, ProgramError> {
57+
if account_info.data_len() != Self::LEN {
58+
return Err(ProgramError::InvalidAccountData);
59+
}
60+
if account_info.owner() != &ID {
61+
return Err(ProgramError::InvalidAccountOwner);
62+
}
63+
Ok(Self::from_bytes_unchecked(
64+
account_info.borrow_data_unchecked(),
65+
))
66+
}
67+
68+
/// Return a `Multisig` from the given bytes.
69+
///
70+
/// # Safety
71+
///
72+
/// The caller must ensure that `bytes` contains a valid representation of `Multisig`, and
73+
/// it has the correct length to be interpreted as an instance of `Multisig`.
74+
#[inline(always)]
75+
pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
76+
&*(bytes.as_ptr() as *const Multisig)
77+
}
78+
79+
/// Number of signers required to validate the `Multisig` signature.
80+
#[inline(always)]
81+
pub const fn required_signers(&self) -> u8 {
82+
self.m
83+
}
84+
85+
/// Number of signer addresses present on the `Multisig`.
86+
#[inline(always)]
87+
pub const fn signers_len(&self) -> usize {
88+
self.n as usize
89+
}
90+
91+
/// Return the signer addresses of the `Multisig`.
92+
#[inline(always)]
93+
pub fn signers(&self) -> &[Pubkey] {
94+
// SAFETY: `self.signers` is an array of `Pubkey` with a fixed size of
95+
// `MAX_MULTISIG_SIGNERS` and `n` field indicates how many of these signers
96+
// are valid.
97+
unsafe { self.signers.get_unchecked(..self.signers_len()) }
98+
}
99+
100+
/// Check whether the multisig is initialized or not.
101+
//
102+
// It will return a boolean value indicating whether [`self.is_initialized`]
103+
// is different than `0` or not.
104+
#[inline(always)]
105+
pub fn is_initialized(&self) -> bool {
106+
self.is_initialized != 0
107+
}
108+
}

programs/token/src/state/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod account_state;
22
mod mint;
3+
mod multisig;
34
mod token;
45

56
pub use account_state::*;
67
pub use mint::*;
8+
pub use multisig::*;
79
pub use token::*;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use core::mem::size_of;
2+
use pinocchio::{
3+
account_info::{AccountInfo, Ref},
4+
program_error::ProgramError,
5+
pubkey::Pubkey,
6+
};
7+
8+
use crate::{instructions::MAX_MULTISIG_SIGNERS, ID};
9+
10+
/// Multisignature data.
11+
#[repr(C)]
12+
pub struct Multisig {
13+
/// Number of signers required
14+
m: u8,
15+
/// Number of valid signers
16+
n: u8,
17+
/// Is `true` if this structure has been initialized
18+
is_initialized: u8,
19+
/// Signer public keys
20+
signers: [Pubkey; MAX_MULTISIG_SIGNERS],
21+
}
22+
23+
impl Multisig {
24+
/// The length of the `Multisig` account data.
25+
pub const LEN: usize = size_of::<Multisig>();
26+
27+
/// Return a `Multisig` from the given account info.
28+
///
29+
/// This method performs owner and length validation on `AccountInfo`, safe borrowing
30+
/// the account data.
31+
#[inline]
32+
pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Multisig>, ProgramError> {
33+
if account_info.data_len() != Self::LEN {
34+
return Err(ProgramError::InvalidAccountData);
35+
}
36+
if !account_info.is_owned_by(&ID) {
37+
return Err(ProgramError::InvalidAccountOwner);
38+
}
39+
Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
40+
Self::from_bytes_unchecked(data)
41+
}))
42+
}
43+
44+
/// Return a `Multisig` from the given account info.
45+
///
46+
/// This method performs owner and length validation on `AccountInfo`, but does not
47+
/// perform the borrow check.
48+
///
49+
/// # Safety
50+
///
51+
/// The caller must ensure that it is safe to borrow the account data (e.g., there are
52+
/// no mutable borrows of the account data).
53+
#[inline]
54+
pub unsafe fn from_account_info_unchecked(
55+
account_info: &AccountInfo,
56+
) -> Result<&Self, ProgramError> {
57+
if account_info.data_len() != Self::LEN {
58+
return Err(ProgramError::InvalidAccountData);
59+
}
60+
if account_info.owner() != &ID {
61+
return Err(ProgramError::InvalidAccountOwner);
62+
}
63+
Ok(Self::from_bytes_unchecked(
64+
account_info.borrow_data_unchecked(),
65+
))
66+
}
67+
68+
/// Return a `Multisig` from the given bytes.
69+
///
70+
/// # Safety
71+
///
72+
/// The caller must ensure that `bytes` contains a valid representation of `Multisig`, and
73+
/// it has the correct length to be interpreted as an instance of `Multisig`.
74+
#[inline(always)]
75+
pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
76+
&*(bytes.as_ptr() as *const Multisig)
77+
}
78+
79+
/// Number of signers required to validate the `Multisig` signature.
80+
#[inline(always)]
81+
pub const fn required_signers(&self) -> u8 {
82+
self.m
83+
}
84+
85+
/// Number of signer addresses present on the `Multisig`.
86+
#[inline(always)]
87+
pub const fn signers_len(&self) -> usize {
88+
self.n as usize
89+
}
90+
91+
/// Return the signer addresses of the `Multisig`.
92+
#[inline(always)]
93+
pub fn signers(&self) -> &[Pubkey] {
94+
// SAFETY: `self.signers` is an array of `Pubkey` with a fixed size of
95+
// `MAX_MULTISIG_SIGNERS` and `n` field indicates how many of these signers
96+
// are valid.
97+
unsafe { self.signers.get_unchecked(..self.signers_len()) }
98+
}
99+
100+
/// Check whether the multisig is initialized or not.
101+
//
102+
// It will return a boolean value indicating whether [`self.is_initialized`]
103+
// is different than `0` or not.
104+
#[inline(always)]
105+
pub fn is_initialized(&self) -> bool {
106+
self.is_initialized != 0
107+
}
108+
}

0 commit comments

Comments
 (0)