From 88a94a91292284f2fc2c69265bbd32327da241a3 Mon Sep 17 00:00:00 2001 From: Hendrik Hofstadt Date: Wed, 2 Jun 2021 12:40:18 +0200 Subject: [PATCH] Implement Seeded trait and account creation Change-Id: I355b88e07b872f8b4869fa9b7fa8dcae4806527d --- .../programs/solitaire/src/lib.rs | 89 +++++++++++---- .../programs/solitaire/src/seeded.rs | 102 ++++++++++++++++++ 2 files changed, 173 insertions(+), 18 deletions(-) create mode 100644 solana/anchor-bridge/programs/solitaire/src/seeded.rs diff --git a/solana/anchor-bridge/programs/solitaire/src/lib.rs b/solana/anchor-bridge/programs/solitaire/src/lib.rs index 4fed2967c..b5d6f11ef 100644 --- a/solana/anchor-bridge/programs/solitaire/src/lib.rs +++ b/solana/anchor-bridge/programs/solitaire/src/lib.rs @@ -2,6 +2,9 @@ #![feature(const_generics_defaults)] #![allow(warnings)] +pub mod seeded; + +pub use seeded::*; pub use rocksalt::*; // Lacking: @@ -32,6 +35,7 @@ pub use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{ account_info::next_account_info, instruction::AccountMeta, + program::invoke_signed, program_error::ProgramError, program_pack::Pack, rent::Rent, @@ -229,7 +233,7 @@ impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> { } impl<'r, T, const IsInitialized: bool, const Lazy: bool> Deref - for Data<'r, T, IsInitialized, Lazy> +for Data<'r, T, IsInitialized, Lazy> { type Target = T; fn deref(&self) -> &Self::Target { @@ -238,7 +242,7 @@ impl<'r, T, const IsInitialized: bool, const Lazy: bool> Deref } impl<'r, T, const IsInitialized: bool, const Lazy: bool> DerefMut - for Data<'r, T, IsInitialized, Lazy> +for Data<'r, T, IsInitialized, Lazy> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 @@ -297,9 +301,57 @@ impl DerefMut for Derive { } } -impl Seeded for Derive { - fn seeds(self) -> Vec>> { - vec![vec![Seed.try_to_vec().unwrap()]] +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 Keyed for Signer + where + T: Keyed, +{ + fn pubkey(&self) -> &Pubkey { + self.0.pubkey() + } +} + +impl Keyed for Sysvar + where + T: Keyed, +{ + fn pubkey(&self) -> &Pubkey { + self.0.pubkey() + } +} + +impl Keyed for System + where + T: Keyed, +{ + fn pubkey(&self) -> &Pubkey { + self.0.pubkey() + } +} + +impl Keyed for Derive + where + T: Keyed, +{ + fn pubkey(&self) -> &Pubkey { + self.0.pubkey() + } +} + +impl<'r> Keyed for Info<'r> { + fn pubkey(&self) -> &Pubkey { + self.key } } @@ -335,7 +387,7 @@ impl Derive, Seed> { space as u64, owner, ); - invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]]) + Ok(invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes()]])?) } } @@ -350,12 +402,12 @@ impl Derive Derive { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result - where - Self: Sized; + 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 +for Derive { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { // Attempt to Derive Seed @@ -422,7 +474,7 @@ 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, const IsInitialized: bool, const Lazy: bool> - Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy> +Peel<'a, 'b, 'c> for Data<'b, T, IsInitialized, Lazy> { fn peel(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result { // If we're initializing the type, we should emit system/rent as deps. @@ -442,8 +494,8 @@ pub trait Wrap { } impl Wrap for T -where - T: ToAccounts, + where + T: ToAccounts, { fn wrap(&self) -> Vec { self.to() @@ -455,6 +507,7 @@ impl Wrap for Signer { todo!() } } + impl Wrap for Derive { fn wrap(&self) -> Vec { todo!() @@ -462,7 +515,7 @@ impl Wrap for Derive { } impl<'a, T: BorshSerialize, const IsInitialized: bool, const Lazy: bool> Wrap - for Data<'a, T, IsInitialized, Lazy> +for Data<'a, T, IsInitialized, Lazy> { fn wrap(&self) -> Vec { todo!() @@ -470,7 +523,7 @@ impl<'a, T: BorshSerialize, const IsInitialized: bool, const Lazy: bool> Wrap } pub trait InstructionContext<'a> { - fn verify(&self) -> Result<()> { + fn verify(&self, program_id: &Pubkey) -> Result<()> { Ok(()) } @@ -487,8 +540,8 @@ pub trait FromAccounts<'a, 'b: 'a, 'c> { _: &'c mut Iter<'a, AccountInfo<'b>>, _: &'a T, ) -> Result<(Self, Vec)> - where - Self: Sized; + where + Self: Sized; } pub trait ToAccounts { diff --git a/solana/anchor-bridge/programs/solitaire/src/seeded.rs b/solana/anchor-bridge/programs/solitaire/src/seeded.rs new file mode 100644 index 000000000..fe33a4502 --- /dev/null +++ b/solana/anchor-bridge/programs/solitaire/src/seeded.rs @@ -0,0 +1,102 @@ +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 { + fn seeds(&self, accs: I) -> Vec>>; + 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 { + fn create( + &self, + accs: I, + ctx: &ExecutionContext, + payer: &Pubkey, + lamports: CreationLamports, + ) -> Result<()>; +} + +impl AccountSize for Data<'_, T, IsInitialized> { + fn size(&self) -> usize { + self.1.try_to_vec().unwrap().len() + } +} + +impl<'a, 'b: 'a, K, T: AccountSize + Seeded + Keyed> Creatable 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 Creatable> + for Derive, 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()]])?) + } +}