Solitaire Refactor
Change-Id: I48306fbb00a813d83e0411a38b15123e922f8766
This commit is contained in:
parent
32b2f11def
commit
05ea24faf7
|
@ -1,15 +1,15 @@
|
|||
use crate::types::*;
|
||||
use solitaire::*;
|
||||
|
||||
type Payer<'a> = Signer<Info<'a>>;
|
||||
type Payer<'a> = Signer<Info<'a>>;
|
||||
type GuardianSet<'a> = Derive<Data<'a, GuardianSetData, Uninitialized>, "GuardianSet">;
|
||||
type Bridge<'a> = Derive<Data<'a, BridgeData, Uninitialized>, "Bridge">;
|
||||
type Bridge<'a> = Derive<Data<'a, BridgeData, Uninitialized>, "Bridge">;
|
||||
|
||||
#[derive(FromAccounts, ToAccounts)]
|
||||
pub struct Initialize<'b> {
|
||||
pub bridge: Bridge<'b>,
|
||||
pub bridge: Bridge<'b>,
|
||||
pub guardian_set: GuardianSet<'b>,
|
||||
pub payer: Payer<'b>,
|
||||
pub payer: Payer<'b>,
|
||||
}
|
||||
|
||||
impl<'b> InstructionContext<'b> for Initialize<'b> {
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
use solitaire::*;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use borsh::{
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
};
|
||||
use byteorder::{
|
||||
BigEndian,
|
||||
WriteBytesExt,
|
||||
};
|
||||
use sha3::Digest;
|
||||
use solana_program::{self, sysvar::clock::Clock};
|
||||
use std::io::{Cursor, Write};
|
||||
use solana_program::{
|
||||
self,
|
||||
sysvar::clock::Clock,
|
||||
};
|
||||
use std::io::{
|
||||
Cursor,
|
||||
Write,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
types::{self, Bridge},
|
||||
types::{
|
||||
self,
|
||||
Bridge,
|
||||
},
|
||||
Error,
|
||||
VAA_TX_FEE,
|
||||
};
|
||||
|
@ -17,9 +32,9 @@ const MIN_BRIDGE_BALANCE: u64 = (((solana_program::rent::ACCOUNT_STORAGE_OVERHEA
|
|||
* solana_program::rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR) as f64
|
||||
* solana_program::rent::DEFAULT_EXEMPTION_THRESHOLD) as u64;
|
||||
|
||||
type GuardianSet<'b> = Derive<Data<'b, types::GuardianSet>, "GuardianSet">;
|
||||
type GuardianSet<'b> = Derive<Data<'b, types::GuardianSet>, "GuardianSet">;
|
||||
type SignatureSet<'b> = Derive<Data<'b, types::SignatureSet>, "Signatures">;
|
||||
type Message<'b> = Derive<Data<'b, types::PostedMessage>, "Message">;
|
||||
type Message<'b> = Derive<Data<'b, types::PostedMessage>, "Message">;
|
||||
|
||||
#[derive(FromAccounts)]
|
||||
pub struct PostVAA<'b> {
|
||||
|
@ -82,11 +97,7 @@ pub struct PostVAAData {
|
|||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn post_vaa(
|
||||
_ctx: &ExecutionContext,
|
||||
accs: &mut PostVAA,
|
||||
vaa: PostVAAData
|
||||
) -> Result<()> {
|
||||
pub fn post_vaa(ctx: &ExecutionContext, accs: &mut PostVAA, vaa: PostVAAData) -> Result<()> {
|
||||
// Verify any required invariants before we process the instruction.
|
||||
check_active(&accs.guardian_set, &accs.clock)?;
|
||||
check_valid_sigs(&accs.guardian_set, &accs.signature_set)?;
|
||||
|
@ -119,82 +130,77 @@ pub fn post_vaa(
|
|||
// Store VAA data in associated message.
|
||||
accs.message.vaa_version = vaa.version;
|
||||
accs.message.vaa_time = vaa.timestamp;
|
||||
accs.message.vaa_signature_account = *accs.signature_set.pubkey();
|
||||
/* accs.message.vaa_signature_account = */
|
||||
*accs.signature_set.info().key;
|
||||
|
||||
// If the bridge has enough balance, refund the SOL to the transaction payer.
|
||||
if VAA_TX_FEE + MIN_BRIDGE_BALANCE < accs.state.to_account_info().lamports() {
|
||||
transfer_sol(
|
||||
&ctx.accounts.state.to_account_info(),
|
||||
&ctx.accounts.payer,
|
||||
VAA_TX_FEE,
|
||||
)?;
|
||||
}
|
||||
//
|
||||
// // Claim the VAA
|
||||
// ctx.accounts.claim.vaa_time = ctx.accounts.clock.unix_timestamp as u32;
|
||||
// if VAA_TX_FEE + MIN_BRIDGE_BALANCE < accs.state.info().lamports() {
|
||||
// transfer_sol(
|
||||
// &accs.state.info(),
|
||||
// &accs.payer,
|
||||
// VAA_TX_FEE,
|
||||
// )?;
|
||||
// }
|
||||
////
|
||||
// // Claim the VAA
|
||||
// ctx.accounts.claim.vaa_time = ctx.accounts.clock.unix_timestamp as u32;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transfer_sol(sender: &Info, recipient: &Info, amount: u64) -> Result<()> {
|
||||
// let mut payer_balance = sender.try_borrow_mut_lamports()?;
|
||||
// **payer_balance = payer_balance
|
||||
// .checked_sub(amount)
|
||||
// .ok_or(ProgramError::InsufficientFunds)?;
|
||||
// let mut recipient_balance = recipient.try_borrow_mut_lamports()?;
|
||||
// **recipient_balance = recipient_balance
|
||||
// .checked_add(amount)
|
||||
// .ok_or(ProgramError::InvalidArgument)?;
|
||||
// let mut payer_balance = sender.try_borrow_mut_lamports()?;
|
||||
// **payer_balance = payer_balance
|
||||
// .checked_sub(amount)
|
||||
// .ok_or(ProgramError::InsufficientFunds)?;
|
||||
// let mut recipient_balance = recipient.try_borrow_mut_lamports()?;
|
||||
// **recipient_balance = recipient_balance
|
||||
// .checked_add(amount)
|
||||
// .ok_or(ProgramError::InvalidArgument)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A guardian set must not have expired.
|
||||
#[inline(always)]
|
||||
fn check_active<'r>(guardian_set: &GuardianSet, clock: &Sysvar<Info<'r>, Clock>) -> Result<()> {
|
||||
// if guardian_set.expiration_time != 0
|
||||
// && (guardian_set.expiration_time as i64) < clock.unix_timestamp
|
||||
// {
|
||||
// return Err(ErrorCode::PostVAAGuardianSetExpired.into());
|
||||
// }
|
||||
// if guardian_set.expiration_time != 0
|
||||
// && (guardian_set.expiration_time as i64) < clock.unix_timestamp
|
||||
// {
|
||||
// return Err(ErrorCode::PostVAAGuardianSetExpired.into());
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The signatures in this instruction must be from the right guardian set.
|
||||
#[inline(always)]
|
||||
fn check_valid_sigs<'r>(
|
||||
guardian_set: &GuardianSet,
|
||||
signatures: &SignatureSet<'r>,
|
||||
) -> Result<()> {
|
||||
// if sig_info.guardian_set_index != guardian_set.index {
|
||||
// return Err(ErrorCode::PostVAAGuardianSetMismatch.into());
|
||||
// }
|
||||
fn check_valid_sigs<'r>(guardian_set: &GuardianSet, signatures: &SignatureSet<'r>) -> Result<()> {
|
||||
// if sig_info.guardian_set_index != guardian_set.index {
|
||||
// return Err(ErrorCode::PostVAAGuardianSetMismatch.into());
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn check_integrity<'r>(
|
||||
vaa: &PostVAAData,
|
||||
signatures: &SignatureSet<'r>,
|
||||
) -> Result<()> {
|
||||
// // Serialize the VAA body into an array of bytes.
|
||||
// let body = {
|
||||
// let mut v = Cursor::new(Vec::new());
|
||||
// v.write_u32::<BigEndian>(vaa.timestamp)?;
|
||||
// v.write_u32::<BigEndian>(vaa.nonce)?;
|
||||
// v.write_u8(vaa.emitter_chain)?;
|
||||
// v.write(&vaa.emitter_address)?;
|
||||
// v.write(&vaa.payload)?;
|
||||
// v.into_inner()
|
||||
// };
|
||||
// // Hash this body, which is expected to be the same as the hash currently stored in the
|
||||
// // signature account, binding that set of signatures to this VAA.
|
||||
// let body_hash: [u8; 32] = {
|
||||
// let mut h = sha3::Keccak256::default();
|
||||
// h.write(body.as_slice())
|
||||
// .map_err(|_| ProgramError::InvalidArgument);
|
||||
// h.finalize().into()
|
||||
// };
|
||||
// if signatures.hash != body_hash {
|
||||
// return Err(ProgramError::InvalidAccountData.into());
|
||||
// }
|
||||
fn check_integrity<'r>(vaa: &PostVAAData, signatures: &SignatureSet<'r>) -> Result<()> {
|
||||
// // Serialize the VAA body into an array of bytes.
|
||||
// let body = {
|
||||
// let mut v = Cursor::new(Vec::new());
|
||||
// v.write_u32::<BigEndian>(vaa.timestamp)?;
|
||||
// v.write_u32::<BigEndian>(vaa.nonce)?;
|
||||
// v.write_u8(vaa.emitter_chain)?;
|
||||
// v.write(&vaa.emitter_address)?;
|
||||
// v.write(&vaa.payload)?;
|
||||
// v.into_inner()
|
||||
// };
|
||||
// // Hash this body, which is expected to be the same as the hash currently stored in the
|
||||
// // signature account, binding that set of signatures to this VAA.
|
||||
// let body_hash: [u8; 32] = {
|
||||
// let mut h = sha3::Keccak256::default();
|
||||
// h.write(body.as_slice())
|
||||
// .map_err(|_| ProgramError::InvalidArgument);
|
||||
// h.finalize().into()
|
||||
// };
|
||||
// if signatures.hash != body_hash {
|
||||
// return Err(ProgramError::InvalidAccountData.into());
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,8 +7,13 @@ mod types;
|
|||
|
||||
use solitaire::*;
|
||||
|
||||
use api::{initialize, Initialize};
|
||||
use api::{post_vaa, PostVAA, PostVAAData};
|
||||
use api::{
|
||||
initialize,
|
||||
post_vaa,
|
||||
Initialize,
|
||||
PostVAA,
|
||||
PostVAAData,
|
||||
};
|
||||
use types::BridgeConfig;
|
||||
|
||||
const VAA_TX_FEE: u64 = 0;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use borsh::{
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
};
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
#[derive(Default, Clone, Copy, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(BorshSerialize, BorshDeserialize, Clone, Copy, Default, PartialEq)]
|
||||
pub struct Index(u8);
|
||||
|
||||
impl Index {
|
||||
|
@ -54,7 +57,7 @@ pub struct Bridge {
|
|||
|
||||
/// Bridge configuration, which is set once upon initialization.
|
||||
pub config: BridgeConfig,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||
pub struct SignatureSet {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use solana_program::{program_error::ProgramError, pubkey::Pubkey};
|
||||
use solana_program::{
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
/// Quality of life Result type for the Solitaire stack.
|
||||
pub type Result<T> = std::result::Result<T, SolitaireError>;
|
||||
|
@ -18,6 +21,9 @@ pub enum SolitaireError {
|
|||
/// The AccountInfo parser tried to derive the provided key, but it did not match.
|
||||
InvalidDerive(Pubkey),
|
||||
|
||||
/// The AccountInfo has an invalid owner.
|
||||
InvalidOwner(Pubkey),
|
||||
|
||||
/// The instruction payload itself could not be deserialized.
|
||||
InstructionDeserializeFailed,
|
||||
|
||||
|
@ -27,6 +33,9 @@ pub enum SolitaireError {
|
|||
/// An solana program error
|
||||
ProgramError(ProgramError),
|
||||
|
||||
/// Owner of the account is ambiguous
|
||||
AmbiguousOwner,
|
||||
|
||||
Custom(u64),
|
||||
}
|
||||
|
||||
|
@ -51,5 +60,3 @@ impl Into<ProgramError> for SolitaireError {
|
|||
ProgramError::Custom(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
#![feature(const_generics_defaults)]
|
||||
#![allow(warnings)]
|
||||
|
||||
pub mod seeded;
|
||||
|
||||
pub use seeded::*;
|
||||
pub use rocksalt::*;
|
||||
|
||||
// Lacking:
|
||||
|
@ -14,7 +11,10 @@ pub use rocksalt::*;
|
|||
|
||||
// We need a few Solana things in scope in order to properly abstract Solana.
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
account_info::{
|
||||
next_account_info,
|
||||
AccountInfo,
|
||||
},
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
instruction::AccountMeta,
|
||||
|
@ -25,125 +25,55 @@ use solana_program::{
|
|||
rent::Rent,
|
||||
system_instruction,
|
||||
system_program,
|
||||
sysvar::{self, SysvarId},
|
||||
sysvar::{
|
||||
self,
|
||||
SysvarId,
|
||||
},
|
||||
};
|
||||
|
||||
use std::{
|
||||
io::{ErrorKind, Write},
|
||||
io::{
|
||||
ErrorKind,
|
||||
Write,
|
||||
},
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{
|
||||
Deref,
|
||||
DerefMut,
|
||||
},
|
||||
slice::Iter,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
pub use crate::{
|
||||
error::{ErrBox, Result, SolitaireError},
|
||||
seeded::Creatable,
|
||||
pub use borsh::{
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
};
|
||||
|
||||
mod error;
|
||||
// Expose all submodules for consumption.
|
||||
pub mod error;
|
||||
pub mod macros;
|
||||
pub mod processors;
|
||||
pub mod types;
|
||||
|
||||
pub trait Persist {
|
||||
fn persist(self);
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Packed<T: Pack + solana_program::program_pack::IsInitialized>(T);
|
||||
|
||||
impl<T: Pack + solana_program::program_pack::IsInitialized> BorshDeserialize for Packed<T> {
|
||||
fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
|
||||
Ok(Packed(
|
||||
T::unpack(buf).map_err(|e| std::io::Error::new(ErrorKind::Other, e))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Pack + solana_program::program_pack::IsInitialized> BorshSerialize for Packed<T> {
|
||||
fn serialize<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
let mut data: [u8; 2000] = [0u8; 2000];
|
||||
T::pack_into_slice(self, &mut data);
|
||||
writer.write(&data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Pack + solana_program::program_pack::IsInitialized> Deref for Packed<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
// The following set of recursive types form the basis of this library, each one represents a
|
||||
// constraint to be fulfilled during parsing an account info.
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Signer<Next>(Next);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct System<Next>(Next);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Sysvar<Next, Var>(Next, PhantomData<Var>);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Derive<Next, const Seed: &'static str>(Next);
|
||||
|
||||
/// A shorter type alias for AccountInfo to make types slightly more readable.
|
||||
pub type Info<'r> = AccountInfo<'r>;
|
||||
|
||||
/// Another alias for an AccountInfo that pairs it with the deserialized data that resides in the
|
||||
/// accounts storage.
|
||||
///
|
||||
/// Note on const generics:
|
||||
///
|
||||
/// Solana's Rust version is JUST old enough that it cannot use constant variables in its default
|
||||
/// parameter assignments. But these DO work in the consumption side so a user can still happily
|
||||
/// use this type by writing for example:
|
||||
///
|
||||
/// Data<(), Uninitialized, Lazy>
|
||||
///
|
||||
/// But here, we must write `Lazy: bool = true` for now unfortunately.
|
||||
#[rustfmt::skip]
|
||||
pub struct Data < 'r, T, const IsInitialized: bool = true, const Lazy: bool = false > (
|
||||
pub Info<'r >,
|
||||
pub T,
|
||||
);
|
||||
|
||||
/// A tag for accounts that should be deserialized lazily.
|
||||
pub const Lazy: bool = true;
|
||||
|
||||
/// A tag for accounts that should be deserialized immediately (the default).
|
||||
pub const Strict: bool = false;
|
||||
|
||||
/// A tag for accounts that are expected to have already been initialized.
|
||||
pub const Initialized: bool = true;
|
||||
|
||||
/// A tag for accounts that must be uninitialized.
|
||||
pub const Uninitialized: bool = false;
|
||||
|
||||
/// The context is threaded through each check. Include anything within this structure that you
|
||||
/// would like to have access to as each layer of dependency is peeled off.
|
||||
pub struct Context<'a, 'b: 'a, 'c, T> {
|
||||
/// A reference to the program_id of the current program.
|
||||
pub this: &'a Pubkey,
|
||||
|
||||
pub iter: &'c mut Iter<'a, AccountInfo<'b>>,
|
||||
|
||||
/// This is a reference to the instruction data we are processing this
|
||||
/// account for.
|
||||
pub data: &'a T,
|
||||
|
||||
/// This is a list of dependent keys that are emitted by this verification pipeline. This
|
||||
/// allows things such as `rent`/`system` to be emitted as required for an account without
|
||||
/// having to specify them in the original instruction account data.
|
||||
pub deps: &'c mut Vec<Pubkey>,
|
||||
|
||||
info: Option<&'a AccountInfo<'b>>,
|
||||
}
|
||||
// We can also re-export a set of types at module scope, this defines the intended API we expect
|
||||
// people to be able to use from top-level.
|
||||
use crate::processors::seeded::Owned;
|
||||
pub use crate::{
|
||||
error::{
|
||||
ErrBox,
|
||||
Result,
|
||||
SolitaireError,
|
||||
},
|
||||
macros::*,
|
||||
processors::{
|
||||
keyed::Keyed,
|
||||
peel::Peel,
|
||||
persist::Persist,
|
||||
seeded::Creatable,
|
||||
},
|
||||
types::*,
|
||||
};
|
||||
|
||||
pub struct ExecutionContext<'a, 'b: 'a> {
|
||||
/// A reference to the program_id of the current program.
|
||||
|
@ -153,157 +83,6 @@ pub struct ExecutionContext<'a, 'b: 'a> {
|
|||
pub accounts: &'a [AccountInfo<'b>],
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> {
|
||||
pub fn new(
|
||||
program: &'a Pubkey,
|
||||
iter: &'c mut Iter<'a, AccountInfo<'b>>,
|
||||
data: &'a T,
|
||||
deps: &'c mut Vec<Pubkey>,
|
||||
) -> Self {
|
||||
Context {
|
||||
this: program,
|
||||
iter,
|
||||
data,
|
||||
deps,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info<'d>(&'d mut self) -> &'a AccountInfo<'b> {
|
||||
match self.info {
|
||||
None => {
|
||||
let info = next_account_info(self.iter).unwrap();
|
||||
self.info = Some(info);
|
||||
info
|
||||
}
|
||||
Some(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, T, const IsInitialized: bool, const Lazy: bool> Deref
|
||||
for Data<'r, T, IsInitialized, Lazy>
|
||||
{
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, T, const IsInitialized: bool, const Lazy: bool> DerefMut
|
||||
for Data<'r, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Signer<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Signer<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Var> Deref for Sysvar<T, Var> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Var> DerefMut for Sysvar<T, Var> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for System<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for System<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const Seed: &'static str> Deref for Derive<T, Seed> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const Seed: &'static str> DerefMut for Derive<T, Seed> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Keyed {
|
||||
fn pubkey(&self) -> &Pubkey;
|
||||
}
|
||||
|
||||
impl<'r, T, const IsInitialized: bool, const Lazy: bool> Keyed
|
||||
for Data<'r, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.key
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Keyed for Signer<T>
|
||||
where
|
||||
T: Keyed,
|
||||
{
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Var> Keyed for Sysvar<T, Var>
|
||||
where
|
||||
T: Keyed,
|
||||
{
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Keyed for System<T>
|
||||
where
|
||||
T: Keyed,
|
||||
{
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const Seed: &'static str> Keyed for Derive<T, Seed>
|
||||
where
|
||||
T: Keyed,
|
||||
{
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Keyed for Info<'r> {
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
/// Lamports to pay to an account being created
|
||||
pub enum CreationLamports {
|
||||
Exempt,
|
||||
|
@ -320,131 +99,13 @@ impl CreationLamports {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
|
||||
pub fn create(
|
||||
&self,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
space: usize,
|
||||
owner: &Pubkey,
|
||||
) -> Result<()> {
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0.key,
|
||||
lamports.amount(space),
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Seed: &'static str, T: BorshSerialize> Derive<Data<'_, T, Uninitialized>, Seed> {
|
||||
pub fn create(
|
||||
&self,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
// Get serialized struct size
|
||||
let size = self.0.try_to_vec().unwrap().len();
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0.0.key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
ctx.program_id,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic Peel trait. This provides a way to describe what each "peeled"
|
||||
/// layer of our constraints should check.
|
||||
pub trait Peel<'a, 'b: 'a, 'c> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// Peel a Derived Key
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b, 'c>
|
||||
for Derive<T, Seed>
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
// Attempt to Derive Seed
|
||||
let (derived, bump) = Pubkey::find_program_address(&[Seed.as_ref()], ctx.this);
|
||||
match derived == *ctx.info().key {
|
||||
true => T::peel(ctx).map(|v| Derive(v)),
|
||||
_ => Err(SolitaireError::InvalidDerive(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peel a Signer.
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match ctx.info().is_signer {
|
||||
true => T::peel(ctx).map(|v| Signer(v)),
|
||||
_ => Err(SolitaireError::InvalidSigner(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expicitly depend upon the System account.
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match true {
|
||||
true => T::peel(ctx).map(|v| System(v)),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peel a Sysvar
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, Var: SysvarId> Peel<'a, 'b, 'c> for Sysvar<T, Var> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match <Var as SysvarId>::check_id(ctx.info().key) {
|
||||
true => T::peel(ctx).map(|v| Sysvar(v, PhantomData)),
|
||||
_ => Err(SolitaireError::InvalidSysvar(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our structural recursion base case, the trait system will stop
|
||||
/// generating new nested calls here.
|
||||
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
Ok(ctx.info().clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our structural recursion base case, the trait system will stop
|
||||
/// generating new nested calls here.
|
||||
impl<'a, 'b: 'a, 'c, T: BorshDeserialize, const IsInitialized: bool, const Lazy: bool>
|
||||
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
// If we're initializing the type, we should emit system/rent as deps.
|
||||
if !IsInitialized {
|
||||
ctx.deps.push(sysvar::rent::ID);
|
||||
ctx.deps.push(system_program::ID);
|
||||
}
|
||||
|
||||
let data = T::try_from_slice(&mut *ctx.info().data.borrow_mut())?;
|
||||
|
||||
Ok(Data(ctx.info().clone(), data))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Wrap {
|
||||
fn wrap(&self) -> Vec<AccountMeta>;
|
||||
}
|
||||
|
||||
impl<T> Wrap for T
|
||||
where
|
||||
T: ToAccounts,
|
||||
where
|
||||
T: ToAccounts,
|
||||
{
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
self.to()
|
||||
|
@ -463,8 +124,8 @@ impl<T, const Seed: &'static str> Wrap for Derive<T, Seed> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: BorshSerialize, const IsInitialized: bool, const Lazy: bool> Wrap
|
||||
for Data<'a, T, IsInitialized, Lazy>
|
||||
impl<'a, T: BorshSerialize + Owned, const IsInitialized: bool, const Lazy: bool> Wrap
|
||||
for Data<'a, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn wrap(&self) -> Vec<AccountMeta> {
|
||||
todo!()
|
||||
|
@ -481,6 +142,10 @@ pub trait InstructionContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToAccounts {
|
||||
fn to(&self) -> Vec<AccountMeta>;
|
||||
}
|
||||
|
||||
/// Trait definition that describes types that can be constructed from a list of solana account
|
||||
/// references. A list of dependent accounts is produced as a side effect of the parsing stage.
|
||||
pub trait FromAccounts<'a, 'b: 'a, 'c> {
|
||||
|
@ -489,199 +154,6 @@ pub trait FromAccounts<'a, 'b: 'a, 'c> {
|
|||
_: &'c mut Iter<'a, AccountInfo<'b>>,
|
||||
_: &'a T,
|
||||
) -> Result<(Self, Vec<Pubkey>)>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
pub trait ToAccounts {
|
||||
fn to(&self) -> Vec<AccountMeta>;
|
||||
}
|
||||
|
||||
/// This is our main codegen macro. It takes as input a list of enum-like variants mapping field
|
||||
/// types to function calls. The generated code produces:
|
||||
///
|
||||
/// - An `Instruction` enum with the enum variants passed in.
|
||||
/// - A set of functions which take as arguments the enum fields.
|
||||
/// - A Dispatcher that deserializes bytes into the enum and dispatches the function call.
|
||||
/// - A set of client calls scoped to the module `api` that can generate instructions.
|
||||
#[macro_export]
|
||||
macro_rules! solitaire {
|
||||
{ $($row:ident($kind:ty) => $fn:ident),+ $(,)* } => {
|
||||
mod instruction {
|
||||
use super::*;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint::ProgramResult,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use solitaire::{FromAccounts, Persist, Result};
|
||||
|
||||
/// Generated:
|
||||
/// This Instruction contains a 1-1 mapping for each enum variant to function call. The
|
||||
/// function calls can be found below in the `api` module.
|
||||
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
enum Instruction {
|
||||
$($row($kind),)*
|
||||
}
|
||||
|
||||
/// This entrypoint is generated from the enum above, it deserializes incoming bytes
|
||||
/// and automatically dispatches to the correct method.
|
||||
pub fn dispatch<'a, 'b: 'a, 'c>(p: &Pubkey, a: &'c [AccountInfo<'b>], d: &[u8]) -> Result<()> {
|
||||
match BorshDeserialize::try_from_slice(d).map_err(|_| SolitaireError::InstructionDeserializeFailed)? {
|
||||
$(
|
||||
Instruction::$row(ix_data) => {
|
||||
let (mut accounts, _deps): ($row, _) = FromAccounts::from(p, &mut a.iter(), &()).unwrap();
|
||||
$fn(&ExecutionContext{program_id: p, accounts: a}, &mut accounts, ix_data)?;
|
||||
accounts.persist();
|
||||
Ok(())
|
||||
}
|
||||
)*
|
||||
|
||||
_ => {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solitaire<'a, 'b: 'a>(p: &Pubkey, a: &'a [AccountInfo<'b>], d: &[u8]) -> ProgramResult {
|
||||
if let Err(err) = dispatch(p, a, d) {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This module contains a 1-1 mapping for each function to an enum variant. The variants
|
||||
/// can be matched to the Instruction found above.
|
||||
mod client {
|
||||
use super::*;
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||
|
||||
/// Generated from Instruction Field
|
||||
$(pub(crate) fn $fn(pid: &Pubkey, accounts: $row, ix_data: $kind) -> std::result::Result<Instruction, ErrBox> {
|
||||
Ok(Instruction {
|
||||
program_id: *pid,
|
||||
accounts: vec![],
|
||||
data: ix_data.try_to_vec()?,
|
||||
})
|
||||
})*
|
||||
}
|
||||
|
||||
use instruction::solitaire;
|
||||
solana_program::entrypoint!(solitaire);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info_wrapper {
|
||||
($name:ident) => {
|
||||
pub struct $name<'b>(Info<'b>);
|
||||
|
||||
impl<'b> Deref for $name<'b> {
|
||||
type Target = Info<'b>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
return &self.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Keyed for $name<'b> {
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for $name<'b> {
|
||||
fn peel<T>(ctx: &'c mut Context<'a, 'b, 'c, T>) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
return Ok($name(ctx.info().clone()));
|
||||
}
|
||||
}
|
||||
};
|
||||
($name:ident, size: $size:expr) => {
|
||||
#[repr(transparent)]
|
||||
pub struct $name<'b>(Info<'b>);
|
||||
|
||||
impl<'b> Deref for $name<'b> {
|
||||
type Target = Info<'b>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> DerefMut for $name<'b> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AccountSize for $name<'b> {
|
||||
fn size(&self) -> usize {
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Keyed for $name<'b> {
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for $name<'b> {
|
||||
fn peel<T>(ctx: &'c mut Context<'a, 'b, 'c, T>) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok($name(ctx.info().clone()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! data_wrapper {
|
||||
($name:ident, $embed:ty) => {
|
||||
#[repr(transparent)]
|
||||
pub struct $name<'b>(Data<'b, $embed>);
|
||||
|
||||
impl<'b> Deref for $name<'b> {
|
||||
type Target = Data<'b, $embed>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
return &self.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> DerefMut for $name<'b> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Keyed for $name<'b> {
|
||||
fn pubkey(&self) -> &Pubkey {
|
||||
self.0.pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AccountSize for $name<'b> {
|
||||
fn size(&self) -> usize {
|
||||
return self.0.size();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for $name<'b> {
|
||||
fn peel<T>(ctx: &'c mut Context<'a, 'b, 'c, T>) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Data::peel(ctx).map(|v| $name(v))
|
||||
}
|
||||
}
|
||||
};
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
use std::ops::{
|
||||
Deref,
|
||||
DerefMut,
|
||||
};
|
||||
|
||||
/// This is our main codegen macro. It takes as input a list of enum-like variants mapping field
|
||||
/// types to function calls. The generated code produces:
|
||||
///
|
||||
/// - An `Instruction` enum with the enum variants passed in.
|
||||
/// - A set of functions which take as arguments the enum fields.
|
||||
/// - A Dispatcher that deserializes bytes into the enum and dispatches the function call.
|
||||
/// - A set of client calls scoped to the module `api` that can generate instructions.
|
||||
#[macro_export]
|
||||
macro_rules! solitaire {
|
||||
{ $($row:ident($kind:ty) => $fn:ident),+ $(,)* } => {
|
||||
mod instruction {
|
||||
use super::*;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint::ProgramResult,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use solitaire::{FromAccounts, Persist, Result};
|
||||
|
||||
/// Generated:
|
||||
/// This Instruction contains a 1-1 mapping for each enum variant to function call. The
|
||||
/// function calls can be found below in the `api` module.
|
||||
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
enum Instruction {
|
||||
$($row($kind),)*
|
||||
}
|
||||
|
||||
/// This entrypoint is generated from the enum above, it deserializes incoming bytes
|
||||
/// and automatically dispatches to the correct method.
|
||||
pub fn dispatch<'a, 'b: 'a, 'c>(p: &Pubkey, a: &'c [AccountInfo<'b>], d: &[u8]) -> Result<()> {
|
||||
match BorshDeserialize::try_from_slice(d).map_err(|_| SolitaireError::InstructionDeserializeFailed)? {
|
||||
$(
|
||||
Instruction::$row(ix_data) => {
|
||||
let (mut accounts, _deps): ($row, _) = FromAccounts::from(p, &mut a.iter(), &()).unwrap();
|
||||
$fn(&ExecutionContext{program_id: p, accounts: a}, &mut accounts, ix_data)?;
|
||||
accounts.persist();
|
||||
Ok(())
|
||||
}
|
||||
)*
|
||||
|
||||
_ => {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solitaire<'a, 'b: 'a>(p: &Pubkey, a: &'a [AccountInfo<'b>], d: &[u8]) -> ProgramResult {
|
||||
if let Err(err) = dispatch(p, a, d) {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This module contains a 1-1 mapping for each function to an enum variant. The variants
|
||||
/// can be matched to the Instruction found above.
|
||||
mod client {
|
||||
use super::*;
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||
|
||||
/// Generated from Instruction Field
|
||||
$(pub(crate) fn $fn(pid: &Pubkey, accounts: $row, ix_data: $kind) -> std::result::Result<Instruction, ErrBox> {
|
||||
Ok(Instruction {
|
||||
program_id: *pid,
|
||||
accounts: vec![],
|
||||
data: ix_data.try_to_vec()?,
|
||||
})
|
||||
})*
|
||||
}
|
||||
|
||||
use instruction::solitaire;
|
||||
solana_program::entrypoint!(solitaire);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! data_wrapper {
|
||||
($name:ident, $embed:ty) => {
|
||||
#[repr(transparent)]
|
||||
pub struct $name<'b>(Data<'b, $embed>);
|
||||
|
||||
impl<'b> std::ops::Deref for $name<'b> {
|
||||
type Target = Data<'b, $embed>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
return &self.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> std::ops::DerefMut for $name<'b> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> solitaire::processors::keyed::Keyed<'a, 'b> for $name<'b> {
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self.0.info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> solitaire::processors::seeded::AccountSize for $name<'b> {
|
||||
fn size(&self) -> usize {
|
||||
return self.0.size();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> solitaire::Peel<'a, 'b, 'c> for $name<'b> {
|
||||
fn peel<T>(ctx: &'c mut Context<'a, 'b, 'c, T>) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Data::peel(ctx).map(|v| $name(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> solitaire::processors::seeded::Owned for $name<'b> {
|
||||
fn owner(&self) -> solitaire::processors::seeded::AccountOwner {
|
||||
return self.1.owner();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pack_type {
|
||||
($name:ident, $embed:ty, $owner:expr) => {
|
||||
#[repr(transparent)]
|
||||
pub struct $name(pub $embed);
|
||||
|
||||
impl BorshDeserialize for $name {
|
||||
fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
|
||||
Ok($name(
|
||||
solana_program::program_pack::Pack::unpack(buf)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl BorshSerialize for $name {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
let mut data = [0u8; <$embed as solana_program::program_pack::Pack>::LEN];
|
||||
solana_program::program_pack::Pack::pack_into_slice(&self.0, &mut data);
|
||||
writer.write(&data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl solitaire::processors::seeded::Owned for $name {
|
||||
fn owner(&self) -> solitaire::processors::seeded::AccountOwner {
|
||||
return $owner;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for $name {
|
||||
type Target = $embed;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod keyed;
|
||||
pub mod peel;
|
||||
pub mod persist;
|
||||
pub mod seeded;
|
|
@ -0,0 +1,65 @@
|
|||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
use crate::{
|
||||
processors::seeded::Owned,
|
||||
Data,
|
||||
Derive,
|
||||
Info,
|
||||
Signer,
|
||||
System,
|
||||
Sysvar,
|
||||
};
|
||||
|
||||
pub trait Keyed<'a, 'b: 'a> {
|
||||
fn info(&'a self) -> &Info<'b>;
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T: Owned, const IsInitialized: bool, const Lazy: bool> Keyed<'a, 'b>
|
||||
for Data<'b, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T> Keyed<'a, 'b> for Signer<T>
|
||||
where
|
||||
T: Keyed<'a, 'b>,
|
||||
{
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self.0.info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T, Var> Keyed<'a, 'b> for Sysvar<T, Var>
|
||||
where
|
||||
T: Keyed<'a, 'b>,
|
||||
{
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self.0.info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T> Keyed<'a, 'b> for System<T>
|
||||
where
|
||||
T: Keyed<'a, 'b>,
|
||||
{
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self.0.info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T, const Seed: &'static str> Keyed<'a, 'b> for Derive<T, Seed>
|
||||
where
|
||||
T: Keyed<'a, 'b>,
|
||||
{
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self.0.info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Keyed<'a, 'b> for Info<'b> {
|
||||
fn info(&'a self) -> &'a Info<'b> {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
//! Peeling.
|
||||
//!
|
||||
//! The accounts in Solitaire programs are defined via layers of types, when each layer is peeled
|
||||
//! off it performs checks, parsing, and any other desired side-effect. The mechanism for this is
|
||||
//! the peel trait, which defines a set of types that recursively construct the desired type.
|
||||
|
||||
use borsh::BorshDeserialize;
|
||||
use solana_program::{
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
sysvar::{
|
||||
self,
|
||||
Sysvar as SolanaSysvar,
|
||||
SysvarId,
|
||||
},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
processors::seeded::{
|
||||
AccountOwner,
|
||||
Owned,
|
||||
},
|
||||
types::*,
|
||||
Context,
|
||||
Result,
|
||||
SolitaireError,
|
||||
};
|
||||
|
||||
/// Generic Peel trait. This provides a way to describe what each "peeled"
|
||||
/// layer of our constraints should check.
|
||||
pub trait Peel<'a, 'b: 'a, 'c> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// Peel a Derived Key
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const Seed: &'static str> Peel<'a, 'b, 'c>
|
||||
for Derive<T, Seed>
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
// Attempt to Derive Seed
|
||||
let (derived, bump) = Pubkey::find_program_address(&[Seed.as_ref()], ctx.this);
|
||||
match derived == *ctx.info().key {
|
||||
true => T::peel(ctx).map(|v| Derive(v)),
|
||||
_ => Err(SolitaireError::InvalidDerive(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peel a Signer.
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match ctx.info().is_signer {
|
||||
true => T::peel(ctx).map(|v| Signer(v)),
|
||||
_ => Err(SolitaireError::InvalidSigner(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expicitly depend upon the System account.
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match true {
|
||||
true => T::peel(ctx).map(|v| System(v)),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peel a Sysvar
|
||||
impl<'a, 'b: 'a, 'c, T, Var> Peel<'a, 'b, 'c> for Sysvar<T, Var>
|
||||
where
|
||||
T: Peel<'a, 'b, 'c>,
|
||||
Var: SolanaSysvar + SysvarId,
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
match <Var as SysvarId>::check_id(ctx.info().key) {
|
||||
true => T::peel(ctx).map(|v| Sysvar(v, PhantomData)),
|
||||
_ => Err(SolitaireError::InvalidSysvar(*ctx.info().key).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our structural recursion base case, the trait system will stop generating new nested
|
||||
/// calls here.
|
||||
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
Ok(ctx.info().clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our structural recursion base case, the trait system will stop generating new nested
|
||||
/// calls here.
|
||||
impl<'a, 'b: 'a, 'c, T: BorshDeserialize + Owned, const IsInitialized: bool, const Lazy: bool>
|
||||
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
// If we're initializing the type, we should emit system/rent as deps.
|
||||
if !IsInitialized {
|
||||
ctx.deps.push(sysvar::rent::ID);
|
||||
ctx.deps.push(system_program::ID);
|
||||
}
|
||||
|
||||
let data = T::try_from_slice(&mut *ctx.info().data.borrow_mut())?;
|
||||
|
||||
if IsInitialized {
|
||||
match data.owner() {
|
||||
AccountOwner::This => {
|
||||
if ctx.info().owner != ctx.this {
|
||||
return Err(SolitaireError::InvalidOwner(*ctx.info().owner));
|
||||
}
|
||||
}
|
||||
AccountOwner::Other(v) => {
|
||||
if *ctx.info().owner != v {
|
||||
return Err(SolitaireError::InvalidOwner(*ctx.info().owner));
|
||||
}
|
||||
}
|
||||
AccountOwner::Any => {}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Data(ctx.info().clone(), data))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub trait Persist {
|
||||
fn persist(self);
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
use super::keyed::Keyed;
|
||||
use crate::{
|
||||
system_instruction,
|
||||
AccountInfo,
|
||||
CreationLamports,
|
||||
Data,
|
||||
Deref,
|
||||
Derive,
|
||||
ExecutionContext,
|
||||
FromAccounts,
|
||||
Info,
|
||||
Peel,
|
||||
Result,
|
||||
Signer,
|
||||
SolitaireError,
|
||||
System,
|
||||
Sysvar,
|
||||
Uninitialized,
|
||||
};
|
||||
use borsh::{
|
||||
BorshSchema,
|
||||
BorshSerialize,
|
||||
};
|
||||
use solana_program::{
|
||||
program::invoke_signed,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
pub trait AccountSize {
|
||||
fn size(&self) -> usize;
|
||||
}
|
||||
|
||||
pub enum AccountOwner {
|
||||
This,
|
||||
Other(Pubkey),
|
||||
Any,
|
||||
}
|
||||
|
||||
pub trait Owned {
|
||||
fn owner(&self) -> AccountOwner;
|
||||
|
||||
fn owner_pubkey(&self, program_id: &Pubkey) -> Result<Pubkey> {
|
||||
match self.owner() {
|
||||
AccountOwner::This => Ok(*program_id),
|
||||
AccountOwner::Other(v) => Ok(v),
|
||||
AccountOwner::Any => Err(SolitaireError::AmbiguousOwner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Owned, const IsInitialized: bool, const Lazy: bool> Owned
|
||||
for Data<'a, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn owner(&self) -> AccountOwner {
|
||||
self.1.owner()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Seeded<I> {
|
||||
fn seeds(&self, accs: I) -> Vec<Vec<Vec<u8>>>;
|
||||
|
||||
fn verify_derivation<'a, 'b: 'a>(&'a self, program_id: &'a Pubkey, accs: I) -> Result<()>
|
||||
where
|
||||
Self: Keyed<'a, 'b>,
|
||||
{
|
||||
let seeds = self.seeds(accs);
|
||||
let (derived, bump) = Pubkey::find_program_address(&[], program_id); //TODO
|
||||
if &derived == self.info().key {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SolitaireError::InvalidDerive(*self.info().key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Creatable<'a, I> {
|
||||
fn create(
|
||||
&'a self,
|
||||
accs: I,
|
||||
ctx: &'a ExecutionContext,
|
||||
payer: &'a Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: BorshSerialize + Owned, const IsInitialized: bool> AccountSize
|
||||
for Data<'_, T, IsInitialized>
|
||||
{
|
||||
fn size(&self) -> usize {
|
||||
self.1.try_to_vec().unwrap().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, K, T: AccountSize + Seeded<K> + Keyed<'a, 'b> + Owned> Creatable<'a, K> for T {
|
||||
fn create(
|
||||
&'a self,
|
||||
accs: K,
|
||||
ctx: &'a ExecutionContext<'_, '_>,
|
||||
payer: &'a Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
let seeds = self.seeds(accs);
|
||||
let size = self.size();
|
||||
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.info().key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
&self.owner_pubkey(ctx.program_id)?,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[])?) // TODO use seeds
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const Seed: &'static str, T: BorshSerialize + Owned> Creatable<'a, Option<()>>
|
||||
for Derive<Data<'_, T, Uninitialized>, Seed>
|
||||
{
|
||||
fn create(
|
||||
&'a self,
|
||||
_: Option<()>,
|
||||
ctx: &'a ExecutionContext,
|
||||
payer: &'a Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
// Get serialized struct size
|
||||
let size = self.0.try_to_vec().unwrap().len();
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0 .0.key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
&self.0 .1.owner_pubkey(ctx.program_id)?,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])?)
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
use crate::{
|
||||
system_instruction,
|
||||
AccountInfo,
|
||||
CreationLamports,
|
||||
Data,
|
||||
Deref,
|
||||
Derive,
|
||||
ExecutionContext,
|
||||
FromAccounts,
|
||||
Info,
|
||||
Keyed,
|
||||
Peel,
|
||||
Result,
|
||||
Signer,
|
||||
SolitaireError,
|
||||
System,
|
||||
Sysvar,
|
||||
Uninitialized,
|
||||
};
|
||||
use borsh::{BorshSchema, BorshSerialize};
|
||||
use solana_program::{program::invoke_signed, pubkey::Pubkey};
|
||||
|
||||
pub trait AccountSize {
|
||||
fn size(&self) -> usize;
|
||||
}
|
||||
|
||||
pub trait Seeded<I> {
|
||||
fn seeds(&self, accs: I) -> Vec<Vec<Vec<u8>>>;
|
||||
fn verify_derivation(&self, program_id: &Pubkey, accs: I) -> Result<()>
|
||||
where
|
||||
Self: Keyed,
|
||||
{
|
||||
let seeds = self.seeds(accs);
|
||||
let (derived, bump) = Pubkey::find_program_address(&[], program_id); //TODO
|
||||
if &derived == self.pubkey() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SolitaireError::InvalidDerive(*self.pubkey()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Creatable<I> {
|
||||
fn create(
|
||||
&self,
|
||||
accs: I,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: BorshSerialize, const IsInitialized: bool> AccountSize for Data<'_, T, IsInitialized> {
|
||||
fn size(&self) -> usize {
|
||||
self.1.try_to_vec().unwrap().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, K, T: AccountSize + Seeded<K> + Keyed> Creatable<K> for T {
|
||||
fn create(
|
||||
&self,
|
||||
accs: K,
|
||||
ctx: &ExecutionContext<'_, '_>,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
let seeds = self.seeds(accs);
|
||||
let size = self.size();
|
||||
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.pubkey(),
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
ctx.program_id,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[])?) // TODO use seeds
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Seed: &'static str, T: BorshSerialize> Creatable<Option<()>>
|
||||
for Derive<Data<'_, T, Uninitialized>, Seed>
|
||||
{
|
||||
fn create(
|
||||
&self,
|
||||
_: Option<()>,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
// Get serialized struct size
|
||||
let size = self.0.try_to_vec().unwrap().len();
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0 .0.key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
ctx.program_id,
|
||||
);
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])?)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
mod accounts;
|
||||
mod context;
|
||||
mod layers;
|
||||
|
||||
pub use accounts::*;
|
||||
pub use context::*;
|
||||
pub use layers::*;
|
|
@ -0,0 +1,103 @@
|
|||
//! Accounts.
|
||||
//!
|
||||
//! Solana provides a single primitive `AccountInfo` that represents an account on Solana. It
|
||||
//! provides no information about what the account means however. This file provides a set of
|
||||
//! types that describe different kinds of accounts to target.
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
program::invoke_signed,
|
||||
pubkey::Pubkey,
|
||||
system_instruction,
|
||||
};
|
||||
use std::ops::{
|
||||
Deref,
|
||||
DerefMut,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
processors::seeded::Owned,
|
||||
CreationLamports,
|
||||
Derive,
|
||||
ExecutionContext,
|
||||
Result,
|
||||
Uninitialized,
|
||||
};
|
||||
|
||||
/// A short alias for AccountInfo.
|
||||
pub type Info<'r> = AccountInfo<'r>;
|
||||
|
||||
/// An account that is known to contain serialized data.
|
||||
///
|
||||
/// Note on const generics:
|
||||
///
|
||||
/// Solana's Rust version is JUST old enough that it cannot use constant variables in its default
|
||||
/// parameter assignments. But these DO work in the consumption side so a user can still happily
|
||||
/// use this type by writing for example:
|
||||
///
|
||||
/// Data<(), Uninitialized, Lazy>
|
||||
///
|
||||
/// But here, we must write `Lazy: bool = true` for now unfortunately.
|
||||
#[rustfmt::skip]
|
||||
pub struct Data < 'r, T: Owned, const IsInitialized: bool = true, const Lazy: bool = false > (
|
||||
pub Info<'r >,
|
||||
pub T,
|
||||
);
|
||||
|
||||
impl<'r, T: Owned, const IsInitialized: bool, const Lazy: bool> Deref
|
||||
for Data<'r, T, IsInitialized, Lazy>
|
||||
{
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, T: Owned, const IsInitialized: bool, const Lazy: bool> DerefMut
|
||||
for Data<'r, T, IsInitialized, Lazy>
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
|
||||
pub fn create(
|
||||
&self,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
space: usize,
|
||||
owner: &Pubkey,
|
||||
) -> Result<()> {
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0.key,
|
||||
lamports.amount(space),
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]]).map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const Seed: &'static str, T: BorshSerialize + Owned> Derive<Data<'_, T, Uninitialized>, Seed> {
|
||||
pub fn create(
|
||||
&self,
|
||||
ctx: &ExecutionContext,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
) -> Result<()> {
|
||||
// Get serialized struct size
|
||||
let size = self.0.try_to_vec().unwrap().len();
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0 .0.key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
ctx.program_id,
|
||||
);
|
||||
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]]).map_err(|e| e.into())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
use solana_program::{
|
||||
account_info::{
|
||||
next_account_info,
|
||||
AccountInfo,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::slice::Iter;
|
||||
|
||||
/// The context is threaded through each check. Include anything within this structure that you
|
||||
/// would like to have access to as each layer of dependency is peeled off.
|
||||
pub struct Context<'a, 'b: 'a, 'c, T> {
|
||||
/// A reference to the program_id of the current program.
|
||||
pub this: &'a Pubkey,
|
||||
|
||||
pub iter: &'c mut Iter<'a, AccountInfo<'b>>,
|
||||
|
||||
/// This is a reference to the instruction data we are processing this
|
||||
/// account for.
|
||||
pub data: &'a T,
|
||||
|
||||
/// This is a list of dependent keys that are emitted by this verification pipeline. This
|
||||
/// allows things such as `rent`/`system` to be emitted as required for an account without
|
||||
/// having to specify them in the original instruction account data.
|
||||
pub deps: &'c mut Vec<Pubkey>,
|
||||
|
||||
pub info: Option<&'a AccountInfo<'b>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> {
|
||||
pub fn new(
|
||||
program: &'a Pubkey,
|
||||
iter: &'c mut Iter<'a, AccountInfo<'b>>,
|
||||
data: &'a T,
|
||||
deps: &'c mut Vec<Pubkey>,
|
||||
) -> Self {
|
||||
Context {
|
||||
this: program,
|
||||
iter,
|
||||
data,
|
||||
deps,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info<'d>(&'d mut self) -> &'a AccountInfo<'b> {
|
||||
match self.info {
|
||||
None => {
|
||||
let info = next_account_info(self.iter).unwrap();
|
||||
self.info = Some(info);
|
||||
info
|
||||
}
|
||||
Some(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
//! This file contains several single-field wrapper structs. Each one represents a layer that must
|
||||
//! be checked in order to parse a Solana account.
|
||||
//!
|
||||
//! These structs are always single field (or single + PhantomData) and so can be represented with
|
||||
//! the transparent repr layout. When each layer is removed the data can be transmuted safely to
|
||||
//! the layer below, allowing for optimized recursion.
|
||||
|
||||
use std::{
|
||||
io::{
|
||||
ErrorKind,
|
||||
Write,
|
||||
},
|
||||
marker::PhantomData,
|
||||
ops::{
|
||||
Deref,
|
||||
DerefMut,
|
||||
},
|
||||
};
|
||||
|
||||
use borsh::{
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Signer<Next>(pub Next);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct System<Next>(pub Next);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Sysvar<Next, Var>(pub Next, pub PhantomData<Var>);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Derive<Next, const Seed: &'static str>(pub Next);
|
||||
|
||||
/// A tag for accounts that should be deserialized lazily.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Lazy: bool = true;
|
||||
|
||||
/// A tag for accounts that should be deserialized immediately (the default).
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Strict: bool = false;
|
||||
|
||||
/// A tag for accounts that are expected to have already been initialized.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Initialized: bool = true;
|
||||
|
||||
/// A tag for accounts that must be uninitialized.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Uninitialized: bool = false;
|
||||
|
||||
// Several traits are required for types defined here, they cannot be defined in another file due
|
||||
// to orphan instance limitations.
|
||||
|
||||
impl<T> Deref for Signer<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Signer<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Var> Deref for Sysvar<T, Var> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Var> DerefMut for Sysvar<T, Var> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for System<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for System<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const Seed: &'static str> Deref for Derive<T, Seed> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { std::mem::transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const Seed: &'static str> DerefMut for Derive<T, Seed> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { std::mem::transmute(&mut self.0) }
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
#![feature(const_generics)]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(warnings)]
|
||||
|
||||
// #![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]
|
||||
|
||||
mod api;
|
||||
mod messages;
|
||||
mod types;
|
||||
mod vaa;
|
||||
|
||||
use api::{initialize, Initialize};
|
||||
|
||||
use solitaire::*;
|
||||
use std::error::Error;
|
||||
|
||||
pub enum TokenBridgeError {
|
||||
InvalidPayload,
|
||||
Unknown(String),
|
||||
InvalidMint,
|
||||
WrongAccountOwner,
|
||||
InvalidUTF8String,
|
||||
AlreadyExecuted,
|
||||
}
|
||||
|
||||
impl<T: Error> From<T> for TokenBridgeError {
|
||||
fn from(t: T) -> Self {
|
||||
return TokenBridgeError::Unknown(t.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SolitaireError> for TokenBridgeError {
|
||||
fn into(self) -> SolitaireError {
|
||||
SolitaireError::Custom(0)
|
||||
}
|
||||
}
|
||||
|
||||
solitaire! {
|
||||
Initialize(Pubkey) => initialize,
|
||||
}
|
|
@ -13,7 +13,10 @@ use solana_program::{
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{quote, quote_spanned};
|
||||
use quote::{
|
||||
quote,
|
||||
quote_spanned,
|
||||
};
|
||||
use std::borrow::BorrowMut;
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{quote, quote_spanned};
|
||||
use quote::{
|
||||
quote,
|
||||
quote_spanned,
|
||||
};
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
parse_quote,
|
||||
|
|
|
@ -5,7 +5,7 @@ imports_granularity = "Crate"
|
|||
empty_item_single_line = false
|
||||
|
||||
# Easier editing when arbitrary mixed use statements do not collapse.
|
||||
imports_layout = "HorizontalVertical"
|
||||
imports_layout = "Vertical"
|
||||
|
||||
# Default rustfmt formatting of match arms with branches is awful.
|
||||
match_arm_leading_pipes = "Preserve"
|
||||
|
|
Loading…
Reference in New Issue