solitaire: remove recursive FromAccounts and 'c lifetime.

Solitaire parses accounts using two traits, FromAccounts and Peel. The
FromAccounts derive macro generates a function, `FromAccounts::from()`,
which calls `Peel::peel` to construct each field in the struct:

```
let example = Example {
    foo: Peel::peel(next_account_iter(accs)?),
    bar: Peel::peel(next_account_iter(accs)?),
    baz: Peel::peel(next_account_iter(accs)?),
    ...,
}
```

The FromAccounts derivation also attempts to implement Peel for the
structure itself however, which means `Example` itself is embeddable as
a field in another accounts struct. This is only used in ClaimableVAA
which is a large source of confusion and the complexity is not worth
maintaining.

This commit removes the recursion by:

1) Removing the `impl Peel` derived by FromAccounts.
2) Removes the AccountInfo iterator from Context as it is no longer needed.
3) Adds the current parsed account to Context instead, safe thanks to (2)
4) Move message out of ClaimableVAA and pass in to verify everywhere instead.
5) Removes Peel/FromAccounts from ClaimableVAA.
This commit is contained in:
Reisen 2022-05-30 18:55:54 +00:00 committed by Csongor Kiss
parent ec3bcfb40c
commit 82ea938317
12 changed files with 190 additions and 212 deletions

View File

@ -33,20 +33,18 @@ use crate::{
}, },
vaa::ClaimableVAA, vaa::ClaimableVAA,
DeserializePayload, DeserializePayload,
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
fn verify_governance<T>(vaa: &ClaimableVAA<T>) -> Result<()> fn verify_governance<T>(vaa: &PayloadMessage<T>) -> Result<()>
where where
T: DeserializePayload, T: DeserializePayload,
{ {
let expected_emitter = std::env!("EMITTER_ADDRESS"); let expected_emitter = std::env!("EMITTER_ADDRESS");
let current_emitter = format!( let current_emitter = format!("{}", Pubkey::new_from_array(vaa.meta().emitter_address));
"{}",
Pubkey::new_from_array(vaa.message.meta().emitter_address)
);
// Fail if the emitter is not the known governance key, or the emitting chain is not Solana. // Fail if the emitter is not the known governance key, or the emitting chain is not Solana.
if expected_emitter != current_emitter || vaa.message.meta().emitter_chain != CHAIN_ID_SOLANA { if expected_emitter != current_emitter || vaa.meta().emitter_chain != CHAIN_ID_SOLANA {
Err(InvalidGovernanceKey.into()) Err(InvalidGovernanceKey.into())
} else { } else {
Ok(()) Ok(())
@ -62,7 +60,10 @@ pub struct UpgradeContract<'b> {
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>, pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// GuardianSet change VAA /// GuardianSet change VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadUpgrade>, pub vaa: PayloadMessage<'b, GovernancePayloadUpgrade>,
/// Claim account representing whether the vaa has already been consumed.
pub vaa_claim: ClaimableVAA<'b>,
/// PDA authority for the loader /// PDA authority for the loader
pub upgrade_authority: Derive<Info<'b>, "upgrade">, pub upgrade_authority: Derive<Info<'b>, "upgrade">,
@ -95,12 +96,11 @@ pub fn upgrade_contract(
_data: UpgradeContractData, _data: UpgradeContractData,
) -> Result<()> { ) -> Result<()> {
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade( let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade(
ctx.program_id, ctx.program_id,
&accs.vaa.message.new_contract, &accs.vaa.new_contract,
accs.upgrade_authority.key, accs.upgrade_authority.key,
accs.spill.key, accs.spill.key,
); );
@ -124,7 +124,10 @@ pub struct UpgradeGuardianSet<'b> {
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>, pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// GuardianSet change VAA /// GuardianSet change VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadGuardianSetChange>, pub vaa: PayloadMessage<'b, GovernancePayloadGuardianSetChange>,
/// Claim account representing whether the vaa has already been consumed.
pub vaa_claim: ClaimableVAA<'b>,
/// Old guardian set /// Old guardian set
pub guardian_set_old: Mut<GuardianSet<'b, { AccountState::Initialized }>>, pub guardian_set_old: Mut<GuardianSet<'b, { AccountState::Initialized }>>,
@ -152,7 +155,6 @@ pub fn upgrade_guardian_set(
} }
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?;
accs.guardian_set_old.verify_derivation( accs.guardian_set_old.verify_derivation(
ctx.program_id, ctx.program_id,
&GuardianSetDerivationData { &GuardianSetDerivationData {
@ -166,7 +168,7 @@ pub fn upgrade_guardian_set(
}, },
)?; )?;
accs.vaa.claim(ctx, accs.payer.key)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
// Set expiration time for the old set // Set expiration time for the old set
accs.guardian_set_old.expiration_time = accs.guardian_set_old.expiration_time =
@ -203,7 +205,10 @@ pub struct SetFees<'b> {
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>, pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// Governance VAA /// Governance VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadSetMessageFee>, pub vaa: PayloadMessage<'b, GovernancePayloadSetMessageFee>,
/// Claim account representing whether the vaa has already been consumed.
pub vaa_claim: ClaimableVAA<'b>,
} }
#[derive(BorshDeserialize, BorshSerialize, Default)] #[derive(BorshDeserialize, BorshSerialize, Default)]
@ -211,8 +216,7 @@ pub struct SetFeesData {}
pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> { pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> {
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
accs.bridge.config.fee = accs.vaa.fee.as_u64(); accs.bridge.config.fee = accs.vaa.fee.as_u64();
Ok(()) Ok(())
@ -227,7 +231,10 @@ pub struct TransferFees<'b> {
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>, pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// Governance VAA /// Governance VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadTransferFees>, pub vaa: PayloadMessage<'b, GovernancePayloadTransferFees>,
/// Claim account representing whether the vaa has already been consumed.
pub vaa_claim: ClaimableVAA<'b>,
/// Account collecting tx fees /// Account collecting tx fees
pub fee_collector: Mut<Derive<Info<'b>, "fee_collector">>, pub fee_collector: Mut<Derive<Info<'b>, "fee_collector">>,
@ -266,7 +273,8 @@ pub fn transfer_fees(
accs.bridge.last_lamports = new_balance; accs.bridge.last_lamports = new_balance;
accs.vaa.claim(ctx, accs.payer.key)?; verify_governance(&accs.vaa)?;
accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
// Transfer fees // Transfer fees
let transfer_ix = solana_program::system_instruction::transfer( let transfer_ix = solana_program::system_instruction::transfer(

View File

@ -115,8 +115,8 @@ pub struct PayloadMessage<'b, T: DeserializePayload>(
T, T,
); );
impl<'a, 'b: 'a, 'c, T: DeserializePayload> Peel<'a, 'b, 'c> for PayloadMessage<'b, T> { impl<'a, 'b: 'a, T: DeserializePayload> Peel<'a, 'b> for PayloadMessage<'b, T> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self>
where where
Self: Sized, Self: Sized,
{ {
@ -148,62 +148,73 @@ impl<'b, T: DeserializePayload> PayloadMessage<'b, T> {
} }
} }
#[derive(FromAccounts)] /// Claim account to prevent double spending.
pub struct ClaimableVAA<'b, T: DeserializePayload> { pub struct ClaimableVAA<'b>(Mut<Claim<'b, { AccountState::Uninitialized }>>);
// Signed message for the transfer
pub message: PayloadMessage<'b, T>,
// Claim account to prevent double spending impl<'a, 'b: 'a> Peel<'a, 'b> for ClaimableVAA<'b> {
pub claim: Mut<Claim<'b, { AccountState::Uninitialized }>>, fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
} Mut::<Claim<'b, { AccountState::Uninitialized }>>::peel(ctx).map(|c| Self(c))
}
impl<'b, T: DeserializePayload> Deref for ClaimableVAA<'b, T> { fn persist(&self, program_id: &Pubkey) -> Result<()> {
type Target = PayloadMessage<'b, T>; Mut::<Claim<'b, { AccountState::Uninitialized }>>::persist(&self.0, program_id)
fn deref(&self) -> &Self::Target {
&self.message
} }
} }
impl<'b, T: DeserializePayload> ClaimableVAA<'b, T> { impl<'b> ClaimableVAA<'b> {
pub fn verify(&self, program_id: &Pubkey) -> Result<()> { pub fn verify<T>(&self, ctx: &ExecutionContext, message: &PayloadMessage<'b, T>) -> Result<()>
trace!("Seq: {}", self.message.meta().sequence); where
T: DeserializePayload,
{
trace!("Seq: {}", message.meta().sequence);
// Verify that the claim account is derived correctly // Verify that the claim account is derived correctly
self.claim.verify_derivation( self.0.verify_derivation(
program_id, ctx.program_id,
&ClaimDerivationData { &ClaimDerivationData {
emitter_address: self.message.meta().emitter_address, emitter_address: message.meta().emitter_address,
emitter_chain: self.message.meta().emitter_chain, emitter_chain: message.meta().emitter_chain,
sequence: self.message.meta().sequence, sequence: message.meta().sequence,
}, },
)?; )?;
Ok(()) Ok(())
} }
}
impl<'b, T: DeserializePayload> ClaimableVAA<'b, T> {
pub fn is_claimed(&self) -> bool { pub fn is_claimed(&self) -> bool {
self.claim.claimed self.0.claimed
} }
pub fn claim(&mut self, ctx: &ExecutionContext, payer: &Pubkey) -> Result<()> { pub fn claim<T>(
&mut self,
ctx: &ExecutionContext,
payer: &Pubkey,
message: &PayloadMessage<'b, T>,
) -> Result<()>
where
T: DeserializePayload,
{
// Verify that the claim account is derived correctly before claiming.
self.verify(ctx, message)?;
// Exit if the claim has already been made.
if self.is_claimed() { if self.is_claimed() {
return Err(VAAAlreadyExecuted.into()); return Err(VAAAlreadyExecuted.into());
} }
self.claim.create( // Claim the account by initializing it with a true boolean.
self.0.create(
&ClaimDerivationData { &ClaimDerivationData {
emitter_address: self.message.meta().emitter_address, emitter_address: message.meta().emitter_address,
emitter_chain: self.message.meta().emitter_chain, emitter_chain: message.meta().emitter_chain,
sequence: self.message.meta().sequence, sequence: message.meta().sequence,
}, },
ctx, ctx,
payer, payer,
Exempt, Exempt,
)?; )?;
self.claim.claimed = true; self.0.claimed = true;
Ok(()) Ok(())
} }

View File

@ -44,7 +44,8 @@ pub struct CompleteNative<'b> {
pub payer: Mut<Signer<AccountInfo<'b>>>, pub payer: Mut<Signer<AccountInfo<'b>>>,
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
pub vaa: ClaimableVAA<'b, PayloadTransfer>, pub vaa: PayloadMessage<'b, PayloadTransfer>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
pub to: Mut<Data<'b, SplAccount, { AccountState::MaybeInitialized }>>, pub to: Mut<Data<'b, SplAccount, { AccountState::MaybeInitialized }>>,
@ -119,8 +120,7 @@ pub fn complete_native(
} }
// Prevent vaa double signing // Prevent vaa double signing
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
if !accs.to.is_initialized() { if !accs.to.is_initialized() {
let associated_addr = spl_associated_token_account::get_associated_token_address( let associated_addr = spl_associated_token_account::get_associated_token_address(
@ -161,7 +161,8 @@ pub struct CompleteWrapped<'b> {
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
// Signed message for the transfer // Signed message for the transfer
pub vaa: ClaimableVAA<'b, PayloadTransfer>, pub vaa: PayloadMessage<'b, PayloadTransfer>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
@ -226,8 +227,7 @@ pub fn complete_wrapped(
return Err(InvalidRecipient.into()); return Err(InvalidRecipient.into());
} }
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
// Initialize the NFT if it doesn't already exist // Initialize the NFT if it doesn't already exist
if !accs.meta.is_initialized() { if !accs.meta.is_initialized() {

View File

@ -18,6 +18,7 @@ use bridge::{
ClaimableVAA, ClaimableVAA,
DeserializePayload, DeserializePayload,
}, },
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use solana_program::{ use solana_program::{
@ -36,17 +37,14 @@ use solitaire::{
}; };
// Confirm that a ClaimableVAA came from the correct chain, signed by the right emitter. // Confirm that a ClaimableVAA came from the correct chain, signed by the right emitter.
fn verify_governance<T>(vaa: &ClaimableVAA<T>) -> Result<()> fn verify_governance<T>(vaa: &PayloadMessage<T>) -> Result<()>
where where
T: DeserializePayload, T: DeserializePayload,
{ {
let expected_emitter = std::env!("EMITTER_ADDRESS"); let expected_emitter = std::env!("EMITTER_ADDRESS");
let current_emitter = format!( let current_emitter = format!("{}", Pubkey::new_from_array(vaa.meta().emitter_address));
"{}",
Pubkey::new_from_array(vaa.message.meta().emitter_address)
);
// Fail if the emitter is not the known governance key, or the emitting chain is not Solana. // Fail if the emitter is not the known governance key, or the emitting chain is not Solana.
if expected_emitter != current_emitter || vaa.message.meta().emitter_chain != CHAIN_ID_SOLANA { if expected_emitter != current_emitter || vaa.meta().emitter_chain != CHAIN_ID_SOLANA {
Err(InvalidGovernanceKey.into()) Err(InvalidGovernanceKey.into())
} else { } else {
Ok(()) Ok(())
@ -59,7 +57,10 @@ pub struct UpgradeContract<'b> {
pub payer: Mut<Signer<Info<'b>>>, pub payer: Mut<Signer<Info<'b>>>,
/// GuardianSet change VAA /// GuardianSet change VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadUpgrade>, pub vaa: PayloadMessage<'b, GovernancePayloadUpgrade>,
/// Claim account representing whether the vaa has already been consumed.
pub vaa_claim: ClaimableVAA<'b>,
/// PDA authority for the loader /// PDA authority for the loader
pub upgrade_authority: Derive<Info<'b>, "upgrade">, pub upgrade_authority: Derive<Info<'b>, "upgrade">,
@ -92,13 +93,11 @@ pub fn upgrade_contract(
_data: UpgradeContractData, _data: UpgradeContractData,
) -> Result<()> { ) -> Result<()> {
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade( let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade(
ctx.program_id, ctx.program_id,
&accs.vaa.message.new_contract, &accs.vaa.new_contract,
accs.upgrade_authority.key, accs.upgrade_authority.key,
accs.spill.key, accs.spill.key,
); );
@ -117,10 +116,9 @@ pub fn upgrade_contract(
pub struct RegisterChain<'b> { pub struct RegisterChain<'b> {
pub payer: Mut<Signer<AccountInfo<'b>>>, pub payer: Mut<Signer<AccountInfo<'b>>>,
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
pub endpoint: Mut<Endpoint<'b, { AccountState::Uninitialized }>>, pub endpoint: Mut<Endpoint<'b, { AccountState::Uninitialized }>>,
pub vaa: PayloadMessage<'b, PayloadGovernanceRegisterChain>,
pub vaa: ClaimableVAA<'b, PayloadGovernanceRegisterChain>, pub vaa_claim: ClaimableVAA<'b>,
} }
impl<'a> From<&RegisterChain<'a>> for EndpointDerivationData { impl<'a> From<&RegisterChain<'a>> for EndpointDerivationData {
@ -146,8 +144,7 @@ pub fn register_chain(
// Claim VAA // Claim VAA
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
if accs.vaa.chain == CHAIN_ID_SOLANA { if accs.vaa.chain == CHAIN_ID_SOLANA {
return Err(InvalidChain.into()); return Err(InvalidChain.into());

View File

@ -19,6 +19,7 @@ use crate::{
}; };
use bridge::{ use bridge::{
vaa::ClaimableVAA, vaa::ClaimableVAA,
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use solana_program::account_info::AccountInfo; use solana_program::account_info::AccountInfo;
@ -35,7 +36,8 @@ pub struct CompleteNative<'b> {
pub payer: Mut<Signer<AccountInfo<'b>>>, pub payer: Mut<Signer<AccountInfo<'b>>>,
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
pub vaa: ClaimableVAA<'b, PayloadTransfer>, pub vaa: PayloadMessage<'b, PayloadTransfer>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
pub to: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>, pub to: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
@ -108,13 +110,12 @@ pub fn complete_native(
if accs.vaa.to != accs.to.info().key.to_bytes() { if accs.vaa.to != accs.to.info().key.to_bytes() {
return Err(InvalidRecipient.into()); return Err(InvalidRecipient.into());
} }
if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { if INVALID_VAAS.contains(&&*accs.vaa.info().key.to_string()) {
return Err(InvalidVAA.into()); return Err(InvalidVAA.into());
} }
// Prevent vaa double signing // Prevent vaa double signing
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let mut amount = accs.vaa.amount.as_u64(); let mut amount = accs.vaa.amount.as_u64();
let mut fee = accs.vaa.fee.as_u64(); let mut fee = accs.vaa.fee.as_u64();
@ -156,7 +157,8 @@ pub struct CompleteWrapped<'b> {
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
// Signed message for the transfer // Signed message for the transfer
pub vaa: ClaimableVAA<'b, PayloadTransfer>, pub vaa: PayloadMessage<'b, PayloadTransfer>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
@ -227,12 +229,11 @@ pub fn complete_wrapped(
if accs.vaa.to != accs.to.info().key.to_bytes() { if accs.vaa.to != accs.to.info().key.to_bytes() {
return Err(InvalidRecipient.into()); return Err(InvalidRecipient.into());
} }
if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { if INVALID_VAAS.contains(&&*accs.vaa.info().key.to_string()) {
return Err(InvalidVAA.into()); return Err(InvalidVAA.into());
} }
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
// Mint tokens // Mint tokens
let mint_ix = spl_token::instruction::mint_to( let mint_ix = spl_token::instruction::mint_to(

View File

@ -18,6 +18,7 @@ use crate::{
}; };
use bridge::{ use bridge::{
vaa::ClaimableVAA, vaa::ClaimableVAA,
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use solana_program::account_info::AccountInfo; use solana_program::account_info::AccountInfo;
@ -94,7 +95,8 @@ pub struct CompleteNativeWithPayload<'b> {
pub payer: Mut<Signer<AccountInfo<'b>>>, pub payer: Mut<Signer<AccountInfo<'b>>>,
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
pub vaa: ClaimableVAA<'b, PayloadTransferWithPayload>, pub vaa: PayloadMessage<'b, PayloadTransferWithPayload>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
pub to: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>, pub to: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
@ -179,8 +181,7 @@ pub fn complete_native_with_payload(
} }
// Prevent vaa double signing // Prevent vaa double signing
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let mut amount = accs.vaa.amount.as_u64(); let mut amount = accs.vaa.amount.as_u64();
@ -209,7 +210,8 @@ pub struct CompleteWrappedWithPayload<'b> {
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
/// Signed message for the transfer /// Signed message for the transfer
pub vaa: ClaimableVAA<'b, PayloadTransferWithPayload>, pub vaa: PayloadMessage<'b, PayloadTransferWithPayload>,
pub vaa_claim: ClaimableVAA<'b>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
@ -291,8 +293,7 @@ pub fn complete_wrapped_with_payload(
return Err(InvalidRecipient.into()); return Err(InvalidRecipient.into());
} }
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
// Mint tokens // Mint tokens
let mint_ix = spl_token::instruction::mint_to( let mint_ix = spl_token::instruction::mint_to(

View File

@ -21,6 +21,7 @@ use crate::{
}; };
use bridge::{ use bridge::{
vaa::ClaimableVAA, vaa::ClaimableVAA,
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use solana_program::{ use solana_program::{
@ -48,7 +49,8 @@ pub struct CreateWrapped<'b> {
pub config: ConfigAccount<'b, { AccountState::Initialized }>, pub config: ConfigAccount<'b, { AccountState::Initialized }>,
pub chain_registration: Endpoint<'b, { AccountState::Initialized }>, pub chain_registration: Endpoint<'b, { AccountState::Initialized }>,
pub vaa: ClaimableVAA<'b, PayloadAssetMeta>, pub vaa: PayloadMessage<'b, PayloadAssetMeta>,
pub vaa_claim: ClaimableVAA<'b>,
// New Wrapped // New Wrapped
pub mint: Mut<WrappedMint<'b, { AccountState::MaybeInitialized }>>, pub mint: Mut<WrappedMint<'b, { AccountState::MaybeInitialized }>>,
@ -111,12 +113,11 @@ pub fn create_wrapped(
accs.chain_registration accs.chain_registration
.verify_derivation(ctx.program_id, &derivation_data)?; .verify_derivation(ctx.program_id, &derivation_data)?;
if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { if INVALID_VAAS.contains(&&*accs.vaa.info().key.to_string()) {
return Err(InvalidVAA.into()); return Err(InvalidVAA.into());
} }
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
if accs.mint.is_initialized() { if accs.mint.is_initialized() {
update_accounts(ctx, accs, data) update_accounts(ctx, accs, data)

View File

@ -19,6 +19,7 @@ use bridge::{
ClaimableVAA, ClaimableVAA,
DeserializePayload, DeserializePayload,
}, },
PayloadMessage,
CHAIN_ID_SOLANA, CHAIN_ID_SOLANA,
}; };
use solana_program::{ use solana_program::{
@ -36,18 +37,15 @@ use solitaire::{
*, *,
}; };
/// Confirm that a ClaimableVAA came from the correct chain, signed by the right emitter. // Confirm that a ClaimableVAA came from the correct chain, signed by the right emitter.
fn verify_governance<T>(vaa: &ClaimableVAA<T>) -> Result<()> fn verify_governance<T>(vaa: &PayloadMessage<T>) -> Result<()>
where where
T: DeserializePayload, T: DeserializePayload,
{ {
let expected_emitter = std::env!("EMITTER_ADDRESS"); let expected_emitter = std::env!("EMITTER_ADDRESS");
let current_emitter = format!( let current_emitter = format!("{}", Pubkey::new_from_array(vaa.meta().emitter_address));
"{}",
Pubkey::new_from_array(vaa.message.meta().emitter_address)
);
// Fail if the emitter is not the known governance key, or the emitting chain is not Solana. // Fail if the emitter is not the known governance key, or the emitting chain is not Solana.
if expected_emitter != current_emitter || vaa.message.meta().emitter_chain != CHAIN_ID_SOLANA { if expected_emitter != current_emitter || vaa.meta().emitter_chain != CHAIN_ID_SOLANA {
Err(InvalidGovernanceKey.into()) Err(InvalidGovernanceKey.into())
} else { } else {
Ok(()) Ok(())
@ -60,7 +58,8 @@ pub struct UpgradeContract<'b> {
pub payer: Mut<Signer<Info<'b>>>, pub payer: Mut<Signer<Info<'b>>>,
/// GuardianSet change VAA /// GuardianSet change VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadUpgrade>, pub vaa: PayloadMessage<'b, GovernancePayloadUpgrade>,
pub vaa_claim: ClaimableVAA<'b>,
/// PDA authority for the loader /// PDA authority for the loader
pub upgrade_authority: Derive<Info<'b>, "upgrade">, pub upgrade_authority: Derive<Info<'b>, "upgrade">,
@ -92,17 +91,16 @@ pub fn upgrade_contract(
accs: &mut UpgradeContract, accs: &mut UpgradeContract,
_data: UpgradeContractData, _data: UpgradeContractData,
) -> Result<()> { ) -> Result<()> {
if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { if INVALID_VAAS.contains(&&*accs.vaa.info().key.to_string()) {
return Err(InvalidVAA.into()); return Err(InvalidVAA.into());
} }
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade( let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade(
ctx.program_id, ctx.program_id,
&accs.vaa.message.new_contract, &accs.vaa.new_contract,
accs.upgrade_authority.key, accs.upgrade_authority.key,
accs.spill.key, accs.spill.key,
); );
@ -124,7 +122,8 @@ pub struct RegisterChain<'b> {
pub endpoint: Mut<Endpoint<'b, { AccountState::Uninitialized }>>, pub endpoint: Mut<Endpoint<'b, { AccountState::Uninitialized }>>,
pub vaa: ClaimableVAA<'b, PayloadGovernanceRegisterChain>, pub vaa: PayloadMessage<'b, PayloadGovernanceRegisterChain>,
pub vaa_claim: ClaimableVAA<'b>,
} }
impl<'a> From<&RegisterChain<'a>> for EndpointDerivationData { impl<'a> From<&RegisterChain<'a>> for EndpointDerivationData {
@ -148,14 +147,13 @@ pub fn register_chain(
accs.endpoint accs.endpoint
.verify_derivation(ctx.program_id, &derivation_data)?; .verify_derivation(ctx.program_id, &derivation_data)?;
if INVALID_VAAS.contains(&&*accs.vaa.message.info().key.to_string()) { if INVALID_VAAS.contains(&&*accs.vaa.info().key.to_string()) {
return Err(InvalidVAA.into()); return Err(InvalidVAA.into());
} }
// Claim VAA // Claim VAA
verify_governance(&accs.vaa)?; verify_governance(&accs.vaa)?;
accs.vaa.verify(ctx.program_id)?; accs.vaa_claim.claim(ctx, accs.payer.key, &accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
// Create endpoint // Create endpoint
accs.endpoint accs.endpoint

View File

@ -83,8 +83,8 @@ impl CreationLamports {
/// Trait definition that describes types that can be constructed from a list of solana account /// 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. /// references. A list of dependent accounts is produced as a side effect of the parsing stage.
pub trait FromAccounts<'a, 'b: 'a, 'c> { pub trait FromAccounts<'a, 'b: 'a> {
fn from<T>(_: &'a Pubkey, _: &'c mut Iter<'a, AccountInfo<'b>>, _: &'a T) -> Result<Self> fn from<T>(_: &'a Pubkey, _: &mut Iter<'a, AccountInfo<'b>>, _: &'a T) -> Result<Self>
where where
Self: Sized; Self: Sized;
} }

View File

@ -25,8 +25,8 @@ use borsh::BorshSerialize;
/// Generic Peel trait. This provides a way to describe what each "peeled" /// Generic Peel trait. This provides a way to describe what each "peeled"
/// layer of our constraints should check. /// layer of our constraints should check.
pub trait Peel<'a, 'b: 'a, 'c> { pub trait Peel<'a, 'b: 'a> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self>
where where
Self: Sized; Self: Sized;
@ -34,10 +34,10 @@ pub trait Peel<'a, 'b: 'a, 'c> {
} }
/// Peel a nullable value (0-account means None) /// Peel a nullable value (0-account means None)
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Option<T> { impl<'a, 'b: 'a, T: Peel<'a, 'b>> Peel<'a, 'b> for Option<T> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
// Check for 0-account // Check for 0-account
if ctx.info().key == &Pubkey::new_from_array([0u8; 32]) { if ctx.info.key == &Pubkey::new_from_array([0u8; 32]) {
trace!(&format!( trace!(&format!(
"Peeled {} is None, returning", "Peeled {} is None, returning",
std::any::type_name::<Option<T>>() std::any::type_name::<Option<T>>()
@ -62,15 +62,13 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Option<T> {
} }
/// Peel a Derived Key /// Peel a Derived Key
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const SEED: &'static str> Peel<'a, 'b, 'c> impl<'a, 'b: 'a, T: Peel<'a, 'b>, const SEED: &'static str> Peel<'a, 'b> for Derive<T, SEED> {
for Derive<T, SEED> fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
{ // Attempt to Derive 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); let (derived, _bump) = Pubkey::find_program_address(&[SEED.as_ref()], ctx.this);
match derived == *ctx.info().key { match derived == *ctx.info.key {
true => T::peel(ctx).map(|v| Derive(v)), true => T::peel(ctx).map(|v| Derive(v)),
_ => Err(SolitaireError::InvalidDerive(*ctx.info().key, derived)), _ => Err(SolitaireError::InvalidDerive(*ctx.info.key, derived)),
} }
} }
@ -80,14 +78,15 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>, const SEED: &'static str> Peel<'a, 'b,
} }
/// Peel a Mutable key. /// Peel a Mutable key.
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Mut<T> { impl<'a, 'b: 'a, T: Peel<'a, 'b>> Peel<'a, 'b> for Mut<T> {
fn peel<I>(mut ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(mut ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
ctx.immutable = false; ctx.immutable = false;
match ctx.info().is_writable { match ctx.info.is_writable {
true => T::peel(ctx).map(|v| Mut(v)), true => T::peel(ctx).map(|v| Mut(v)),
_ => Err( _ => Err(SolitaireError::InvalidMutability(
SolitaireError::InvalidMutability(*ctx.info().key, ctx.info().is_writable), *ctx.info.key,
), ctx.info.is_writable,
)),
} }
} }
@ -96,9 +95,9 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Mut<T> {
} }
} }
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for MaybeMut<T> { impl<'a, 'b: 'a, T: Peel<'a, 'b>> Peel<'a, 'b> for MaybeMut<T> {
fn peel<I>(mut ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(mut ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
ctx.immutable = !ctx.info().is_writable; ctx.immutable = !ctx.info.is_writable;
T::peel(ctx).map(|v| MaybeMut(v)) T::peel(ctx).map(|v| MaybeMut(v))
} }
@ -108,11 +107,11 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for MaybeMut<T> {
} }
/// Peel a Signer. /// Peel a Signer.
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> { impl<'a, 'b: 'a, T: Peel<'a, 'b>> Peel<'a, 'b> for Signer<T> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
match ctx.info().is_signer { match ctx.info.is_signer {
true => T::peel(ctx).map(|v| Signer(v)), true => T::peel(ctx).map(|v| Signer(v)),
_ => Err(SolitaireError::InvalidSigner(*ctx.info().key)), _ => Err(SolitaireError::InvalidSigner(*ctx.info.key)),
} }
} }
@ -122,8 +121,8 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Signer<T> {
} }
/// Expicitly depend upon the System account. /// Expicitly depend upon the System account.
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> { impl<'a, 'b: 'a, T: Peel<'a, 'b>> Peel<'a, 'b> for System<T> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
match true { match true {
true => T::peel(ctx).map(|v| System(v)), true => T::peel(ctx).map(|v| System(v)),
_ => panic!(), _ => panic!(),
@ -136,17 +135,14 @@ impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for System<T> {
} }
/// Peel a Sysvar /// Peel a Sysvar
impl<'a, 'b: 'a, 'c, Var> Peel<'a, 'b, 'c> for Sysvar<'b, Var> impl<'a, 'b: 'a, Var> Peel<'a, 'b> for Sysvar<'b, Var>
where where
Var: SolanaSysvar, Var: SolanaSysvar,
{ {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
match Var::check_id(ctx.info().key) { match Var::check_id(ctx.info.key) {
true => Ok(Sysvar( true => Ok(Sysvar(ctx.info.clone(), Var::from_account_info(ctx.info)?)),
ctx.info().clone(), _ => Err(SolitaireError::InvalidSysvar(*ctx.info.key)),
Var::from_account_info(ctx.info())?,
)),
_ => Err(SolitaireError::InvalidSysvar(*ctx.info().key)),
} }
} }
@ -157,15 +153,16 @@ where
/// This is our structural recursion base case, the trait system will stop generating new nested /// This is our structural recursion base case, the trait system will stop generating new nested
/// calls here. /// calls here.
impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> { impl<'a, 'b: 'a> Peel<'a, 'b> for Info<'b> {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
if ctx.immutable && ctx.info().is_writable { if ctx.immutable && ctx.info.is_writable {
return Err( return Err(SolitaireError::InvalidMutability(
SolitaireError::InvalidMutability(*ctx.info().key, ctx.info().is_writable), *ctx.info.key,
); ctx.info.is_writable,
));
} }
Ok(ctx.info().clone()) Ok(ctx.info.clone())
} }
fn persist(&self, _program_id: &Pubkey) -> Result<()> { fn persist(&self, _program_id: &Pubkey) -> Result<()> {
@ -178,34 +175,32 @@ impl<'a, 'b: 'a, 'c> Peel<'a, 'b, 'c> for Info<'b> {
impl< impl<
'a, 'a,
'b: 'a, 'b: 'a,
'c,
T: BorshDeserialize + BorshSerialize + Owned + Default, T: BorshDeserialize + BorshSerialize + Owned + Default,
const IS_INITIALIZED: AccountState, const IS_INITIALIZED: AccountState,
> Peel<'a, 'b, 'c> for Data<'b, T, IS_INITIALIZED> > Peel<'a, 'b> for Data<'b, T, IS_INITIALIZED>
{ {
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> { fn peel<I>(ctx: &mut Context<'a, 'b, I>) -> Result<Self> {
if ctx.immutable && ctx.info().is_writable { if ctx.immutable && ctx.info.is_writable {
return Err( return Err(SolitaireError::InvalidMutability(
SolitaireError::InvalidMutability(*ctx.info().key, ctx.info().is_writable), *ctx.info.key,
); ctx.info.is_writable,
));
} }
// If we're initializing the type, we should emit system/rent as deps. // If we're initializing the type, we should emit system/rent as deps.
let (initialized, data): (bool, T) = match IS_INITIALIZED { let (initialized, data): (bool, T) = match IS_INITIALIZED {
AccountState::Uninitialized => { AccountState::Uninitialized => {
if !ctx.info().data.borrow().is_empty() { if !ctx.info.data.borrow().is_empty() {
return Err(SolitaireError::AlreadyInitialized(*ctx.info().key)); return Err(SolitaireError::AlreadyInitialized(*ctx.info.key));
} }
(false, T::default()) (false, T::default())
} }
AccountState::Initialized => { AccountState::Initialized => (true, T::try_from_slice(*ctx.info.data.borrow_mut())?),
(true, T::try_from_slice(*ctx.info().data.borrow_mut())?)
}
AccountState::MaybeInitialized => { AccountState::MaybeInitialized => {
if ctx.info().data.borrow().is_empty() { if ctx.info.data.borrow().is_empty() {
(false, T::default()) (false, T::default())
} else { } else {
(true, T::try_from_slice(*ctx.info().data.borrow_mut())?) (true, T::try_from_slice(*ctx.info.data.borrow_mut())?)
} }
} }
}; };
@ -213,20 +208,20 @@ impl<
if initialized { if initialized {
match data.owner() { match data.owner() {
AccountOwner::This => { AccountOwner::This => {
if ctx.info().owner != ctx.this { if ctx.info.owner != ctx.this {
return Err(SolitaireError::InvalidOwner(*ctx.info().owner)); return Err(SolitaireError::InvalidOwner(*ctx.info.owner));
} }
} }
AccountOwner::Other(v) => { AccountOwner::Other(v) => {
if *ctx.info().owner != v { if *ctx.info.owner != v {
return Err(SolitaireError::InvalidOwner(*ctx.info().owner)); return Err(SolitaireError::InvalidOwner(*ctx.info.owner));
} }
} }
AccountOwner::Any => {} AccountOwner::Any => {}
}; };
} }
Ok(Data(Box::new(ctx.info().clone()), data)) Ok(Data(Box::new(ctx.info.clone()), data))
} }
fn persist(&self, program_id: &Pubkey) -> Result<()> { fn persist(&self, program_id: &Pubkey) -> Result<()> {

View File

@ -1,54 +1,31 @@
use crate::trace;
use solana_program::{ use solana_program::{
account_info::{ account_info::AccountInfo,
next_account_info,
AccountInfo,
},
pubkey::Pubkey, pubkey::Pubkey,
}; };
use std::slice::Iter;
/// The context is threaded through each check. Include anything within this structure that you /// 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. /// would like to have access to as each layer of dependency is peeled off.
pub struct Context<'a, 'b: 'a, 'c, T> { pub struct Context<'a, 'b: 'a, T> {
/// A reference to the program_id of the current program. /// A reference to the program_id of the current program.
pub this: &'a Pubkey, pub this: &'a Pubkey,
/// A reference to the instructions account list, one or more keys may be extracted during /// This is a reference to the AccountInfo of the field that is currently being parsed.
/// the peeling process. pub info: &'a AccountInfo<'b>,
pub iter: &'c mut Iter<'a, AccountInfo<'b>>,
/// Reference to the data passed to the current instruction. /// Reference to the data passed to the current instruction.
pub data: &'a T, pub data: &'a T,
/// An optional account info for this Peelable item, some fields may be other structures that
/// do not themselves have an account info associated with the field.
pub info: Option<&'a AccountInfo<'b>>,
/// Whether to enforce immutability. /// Whether to enforce immutability.
pub immutable: bool, pub immutable: bool,
} }
impl<'a, 'b: 'a, 'c, T> Context<'a, 'b, 'c, T> { impl<'a, 'b: 'a, T> Context<'a, 'b, T> {
pub fn new(program: &'a Pubkey, iter: &'c mut Iter<'a, AccountInfo<'b>>, data: &'a T) -> Self { pub fn new(this: &'a Pubkey, info: &'a AccountInfo<'b>, data: &'a T) -> Self {
Context { Context {
this: program,
info: None,
immutable: true, immutable: true,
iter, this,
info,
data, data,
} }
} }
pub fn info<'d>(&'d mut self) -> &'a AccountInfo<'b> {
match self.info {
None => {
let info = next_account_info(self.iter).unwrap();
trace!("{}", info.key);
self.info = Some(info);
info
}
Some(v) => v,
}
}
} }

View File

@ -27,7 +27,7 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
// Generics lifetimes of the peel type // Generics lifetimes of the peel type
let mut peel_g = input.generics.clone(); let mut peel_g = input.generics.clone();
peel_g.params = parse_quote!('a, 'b: 'a, 'c); peel_g.params = parse_quote!('a, 'b: 'a);
let (_, peel_type_g, _) = peel_g.split_for_impl(); let (_, peel_type_g, _) = peel_g.split_for_impl();
// Params of the instruction context // Params of the instruction context
@ -53,22 +53,11 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
let expanded = quote! { let expanded = quote! {
/// Macro generated implementation of FromAccounts by Solitaire. /// Macro generated implementation of FromAccounts by Solitaire.
impl #combined_impl_g solitaire::FromAccounts #peel_type_g for #name #type_g { impl #combined_impl_g solitaire::FromAccounts #peel_type_g for #name #type_g {
fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &'c mut std::slice::Iter<'a, solana_program::account_info::AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<Self> { fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &mut std::slice::Iter<'a, solana_program::account_info::AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<Self> {
#from_method #from_method
} }
} }
impl #combined_impl_g solitaire::Peel<'a, 'b, 'c> for #name #type_g {
fn peel<I>(ctx: &'c mut solitaire::Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized {
let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data)?;
Ok(v)
}
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
solitaire::Persist::persist(self, program_id)
}
}
/// Macro generated implementation of Persist by Solitaire. /// Macro generated implementation of Persist by Solitaire.
impl #type_impl_g solitaire::Persist for #name #type_g { impl #type_impl_g solitaire::Persist for #name #type_g {
fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> { fn persist(&self, program_id: &solana_program::pubkey::Pubkey) -> solitaire::Result<()> {
@ -102,7 +91,7 @@ fn generate_fields(name: &syn::Ident, data: &Data) -> TokenStream2 {
trace!(stringify!(#name)); trace!(stringify!(#name));
let #name: #ty = solitaire::Peel::peel(&mut solitaire::Context::new( let #name: #ty = solitaire::Peel::peel(&mut solitaire::Context::new(
pid, pid,
iter, next_account_info(iter)?,
data, data,
))?; ))?;
} }