Improve sysvar and account state handling

Change-Id: Ic60433f921c14ce66f6039245a2964dee4f60e8f
This commit is contained in:
Hendrik Hofstadt 2021-06-07 09:03:25 +02:00
parent b7c5e08117
commit 13c7e693c2
9 changed files with 92 additions and 178 deletions

View File

@ -183,12 +183,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-mac"
version = "0.8.0"
@ -212,17 +206,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.8.1"
@ -277,15 +260,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
[[package]]
name = "fixed-hash"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
dependencies = [
"static_assertions",
]
[[package]]
name = "generic-array"
version = "0.12.4"
@ -418,28 +392,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066"
dependencies = [
"derivative",
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "opaque-debug"
version = "0.3.0"
@ -452,16 +404,6 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "primitive-types"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2415937401cb030a2a0a4d922483f945fa068f52a7dbb22ce0fe5f2b6f6adace"
dependencies = [
"fixed-hash",
"uint",
]
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
@ -745,26 +687,6 @@ dependencies = [
"solana-program",
]
[[package]]
name = "spl-token"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbfa8fd791aeb4d7ad5fedb7872478de9f4e8b4fcb02dfd9e7f2f9ae3f3ddd73"
dependencies = [
"arrayref",
"num-derive",
"num-traits",
"num_enum",
"solana-program",
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "subtle"
version = "2.4.0"
@ -811,20 +733,6 @@ dependencies = [
"syn",
]
[[package]]
name = "token-bridge"
version = "0.1.0"
dependencies = [
"borsh",
"byteorder",
"primitive-types",
"rocksalt",
"sha3",
"solana-program",
"solitaire",
"spl-token",
]
[[package]]
name = "toml"
version = "0.5.8"
@ -840,18 +748,6 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "uint"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11fe9a9348741cf134085ad57c249508345fe16411b3d7fb4ff2da2f1d6382e"
dependencies = [
"byteorder",
"crunchy",
"hex",
"static_assertions",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"

View File

@ -36,6 +36,9 @@ pub enum SolitaireError {
/// Owner of the account is ambiguous
AmbiguousOwner,
/// Account has already been initialized
AlreadyInitialized(Pubkey),
Custom(u64),
}

View File

@ -1,5 +1,4 @@
#![feature(const_generics)]
#![feature(const_generics_defaults)]
#![allow(warnings)]
pub use rocksalt::*;
@ -124,8 +123,8 @@ impl<T, const Seed: &'static str> Wrap for Derive<T, Seed> {
}
}
impl<'a, T: BorshSerialize + Owned, const IsInitialized: bool, const Lazy: bool> Wrap
for Data<'a, T, IsInitialized, Lazy>
impl<'a, T: BorshSerialize + Owned + Default, const IsInitialized: AccountState> Wrap
for Data<'a, T, IsInitialized>
{
fn wrap(&self) -> Vec<AccountMeta> {
todo!()

View File

@ -84,10 +84,10 @@ macro_rules! solitaire {
macro_rules! data_wrapper {
($name:ident, $embed:ty) => {
#[repr(transparent)]
pub struct $name<'b>(Data<'b, $embed>);
pub struct $name<'b>(Data<'b, $embed, { solitaire::AccountState::Uninitialized }>);
impl<'b> std::ops::Deref for $name<'b> {
type Target = Data<'b, $embed>;
type Target = Data<'b, $embed, { solitaire::AccountState::Uninitialized }>;
fn deref(&self) -> &Self::Target {
return &self.0;

View File

@ -1,7 +1,11 @@
use solana_program::pubkey::Pubkey;
use solana_program::{
pubkey::Pubkey,
sysvar::Sysvar as SolanaSysvar,
};
use crate::{
processors::seeded::Owned,
AccountState,
Data,
Derive,
Info,
@ -14,8 +18,8 @@ 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>
impl<'a, 'b: 'a, T: Owned + Default, const IsInitialized: AccountState> Keyed<'a, 'b>
for Data<'b, T, IsInitialized>
{
fn info(&'a self) -> &'a Info<'b> {
&self.0
@ -31,12 +35,9 @@ where
}
}
impl<'a, 'b: 'a, T, Var> Keyed<'a, 'b> for Sysvar<T, Var>
where
T: Keyed<'a, 'b>,
{
impl<'a, 'b: 'a, Var: SolanaSysvar> Keyed<'a, 'b> for Sysvar<'b, Var> {
fn info(&'a self) -> &'a Info<'b> {
self.0.info()
&self.0
}
}

View File

@ -70,14 +70,16 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
}
/// Peel a Sysvar
impl<'a, 'b: 'a, 'c, T, Var> Peel<'a, 'b, 'c> for Sysvar<T, Var>
impl<'a, 'b: 'a, 'c, Var> Peel<'a, 'b, 'c> for Sysvar<'b, Var>
where
T: Peel<'a, 'b, 'c>,
Var: SolanaSysvar + SysvarId,
Var: SolanaSysvar,
{
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)),
match Var::check_id(ctx.info().key) {
true => Ok(Sysvar(
ctx.info().clone(),
Var::from_account_info(ctx.info())?,
)),
_ => Err(SolitaireError::InvalidSysvar(*ctx.info().key).into()),
}
}
@ -93,19 +95,40 @@ impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
/// 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>
impl<'a, 'b: 'a, 'c, T: BorshDeserialize + Owned + Default, const IsInitialized: AccountState>
Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized>
{
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
let mut initialized = false;
// 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 = match IsInitialized {
AccountState::Uninitialized => {
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 **ctx.info().lamports.borrow() != 0 {
return Err(SolitaireError::AlreadyInitialized(*ctx.info().key));
}
T::default()
}
AccountState::Initialized => {
initialized = true;
T::try_from_slice(&mut *ctx.info().data.borrow_mut())?
}
AccountState::MaybeInitialized => {
ctx.deps.push(sysvar::rent::ID);
ctx.deps.push(system_program::ID);
if IsInitialized {
if **ctx.info().lamports.borrow() == 0 {
T::default()
} else {
initialized = true;
T::try_from_slice(&mut *ctx.info().data.borrow_mut())?
}
}
};
if initialized {
match data.owner() {
AccountOwner::This => {
if ctx.info().owner != ctx.this {

View File

@ -2,6 +2,7 @@ use super::keyed::Keyed;
use crate::{
system_instruction,
AccountInfo,
AccountState,
CreationLamports,
Data,
Deref,
@ -15,7 +16,6 @@ use crate::{
SolitaireError,
System,
Sysvar,
Uninitialized,
};
use borsh::{
BorshSchema,
@ -48,8 +48,8 @@ pub trait Owned {
}
}
impl<'a, T: Owned, const IsInitialized: bool, const Lazy: bool> Owned
for Data<'a, T, IsInitialized, Lazy>
impl<'a, T: Owned + Default, const IsInitialized: AccountState> Owned
for Data<'a, T, IsInitialized>
{
fn owner(&self) -> AccountOwner {
self.1.owner()
@ -83,7 +83,7 @@ pub trait Creatable<'a, I> {
) -> Result<()>;
}
impl<T: BorshSerialize + Owned, const IsInitialized: bool> AccountSize
impl<T: BorshSerialize + Owned + Default, const IsInitialized: AccountState> AccountSize
for Data<'_, T, IsInitialized>
{
fn size(&self) -> usize {
@ -113,8 +113,8 @@ impl<'a, 'b: 'a, K, T: AccountSize + Seeded<K> + Keyed<'a, 'b> + Owned> Creatabl
}
}
impl<'a, const Seed: &'static str, T: BorshSerialize + Owned> Creatable<'a, Option<()>>
for Derive<Data<'_, T, Uninitialized>, Seed>
impl<'a, const Seed: &'static str, T: BorshSerialize + Owned + Default> Creatable<'a, Option<()>>
for Derive<Data<'_, T, { AccountState::Uninitialized }>, Seed>
{
fn create(
&'a self,

View File

@ -10,6 +10,7 @@ use solana_program::{
program::invoke_signed,
pubkey::Pubkey,
system_instruction,
sysvar::Sysvar as SolanaSysvar,
};
use std::ops::{
Deref,
@ -22,12 +23,18 @@ use crate::{
Derive,
ExecutionContext,
Result,
Uninitialized,
};
/// A short alias for AccountInfo.
pub type Info<'r> = AccountInfo<'r>;
#[derive(Eq, PartialEq)]
pub enum AccountState {
Initialized,
Uninitialized,
MaybeInitialized,
}
/// An account that is known to contain serialized data.
///
/// Note on const generics:
@ -36,17 +43,15 @@ pub type Info<'r> = AccountInfo<'r>;
/// 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.
/// Data<(), { AccountState::Uninitialized }>
#[rustfmt::skip]
pub struct Data < 'r, T: Owned, const IsInitialized: bool = true, const Lazy: bool = false > (
pub struct Data < 'r, T: Owned + Default + Default, const IsInitialized: AccountState> (
pub Info<'r >,
pub T,
);
impl<'r, T: Owned, const IsInitialized: bool, const Lazy: bool> Deref
for Data<'r, T, IsInitialized, Lazy>
impl<'r, T: Owned + Default, const IsInitialized: AccountState> Deref
for Data<'r, T, IsInitialized>
{
type Target = T;
fn deref(&self) -> &Self::Target {
@ -54,14 +59,30 @@ impl<'r, T: Owned, const IsInitialized: bool, const Lazy: bool> Deref
}
}
impl<'r, T: Owned, const IsInitialized: bool, const Lazy: bool> DerefMut
for Data<'r, T, IsInitialized, Lazy>
impl<'r, T: Owned + Default, const IsInitialized: AccountState> DerefMut
for Data<'r, T, IsInitialized>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
impl<'r, T: Owned + Default, const IsInitialized: AccountState> Data<'r, T, IsInitialized> {
/// Is the account already initialized / created
pub fn is_initialized(&self) -> bool {
**self.0.lamports.borrow() != 0
}
}
pub struct Sysvar<'b, Var: SolanaSysvar>(pub AccountInfo<'b>, pub Var);
impl<'b, Var: SolanaSysvar> Deref for Sysvar<'b, Var> {
type Target = Var;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
pub fn create(
&self,
@ -82,7 +103,9 @@ impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
}
}
impl<const Seed: &'static str, T: BorshSerialize + Owned> Derive<Data<'_, T, Uninitialized>, Seed> {
impl<const Seed: &'static str, T: BorshSerialize + Owned + Default>
Derive<Data<'_, T, { AccountState::Uninitialized }>, Seed>
{
pub fn create(
&self,
ctx: &ExecutionContext,

View File

@ -17,6 +17,7 @@ use std::{
},
};
use crate::Info;
use borsh::{
BorshDeserialize,
BorshSerialize,
@ -28,28 +29,9 @@ 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.
@ -66,19 +48,6 @@ impl<T> DerefMut for Signer<T> {
}
}
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 {