diff --git a/programs/mpl-asset/src/processor/transfer.rs b/programs/mpl-asset/src/processor/transfer.rs index 34bdd859..6d6ca326 100644 --- a/programs/mpl-asset/src/processor/transfer.rs +++ b/programs/mpl-asset/src/processor/transfer.rs @@ -1,6 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; use mpl_utils::assert_signer; -use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, program::invoke}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult}; use crate::{ error::MplAssetError, @@ -27,22 +27,14 @@ pub(crate) fn transfer<'a>(accounts: &'a [AccountInfo<'a>], args: TransferArgs) match load_key(ctx.accounts.asset_address, 0)? { Key::HashedAsset => { - // TODO: Needs to be in helper. - // Check that arguments passed in result in on-chain hash. - let mut asset = Asset::from(args.compression_proof); - let args_asset_hash = asset.hash()?; - let current_account_hash = HashedAsset::load(ctx.accounts.asset_address, 0)?.hash; - if args_asset_hash != current_account_hash { - return Err(MplAssetError::IncorrectAssetHash.into()); - } - - // TODO: Needs to be in helper. - // Update owner and send Noop instruction. + let mut asset = + Asset::verify_proof(ctx.accounts.asset_address, args.compression_proof)?; + asset.owner = *ctx.accounts.new_owner.key; - let serialized_data = asset.try_to_vec()?; - invoke(&spl_noop::instruction(serialized_data), &[])?; - // Make a new hashed asset with updated owner. + asset.wrap()?; + + // Make a new hashed asset with updated owner and save to account. HashedAsset::new(asset.hash()?).save(ctx.accounts.asset_address, 0) } Key::Asset => { diff --git a/programs/mpl-asset/src/state/asset.rs b/programs/mpl-asset/src/state/asset.rs index 8ca6e494..e8cdfc55 100644 --- a/programs/mpl-asset/src/state/asset.rs +++ b/programs/mpl-asset/src/state/asset.rs @@ -1,10 +1,14 @@ use borsh::{BorshDeserialize, BorshSerialize}; use shank::ShankAccount; -use solana_program::{keccak, program_error::ProgramError, pubkey::Pubkey}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, keccak, program::invoke, + program_error::ProgramError, pubkey::Pubkey, +}; -use crate::state::{CompressionProof, DataBlob, Key, SolanaAccount}; - -use super::Compressible; +use crate::{ + error::MplAssetError, + state::{Compressible, CompressionProof, DataBlob, HashedAsset, Key, SolanaAccount}, +}; #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, ShankAccount)] pub struct Asset { @@ -17,14 +21,33 @@ pub struct Asset { impl Asset { pub const BASE_LENGTH: usize = 1 + 32 + 32 + 4 + 4; + + // Check that a compression proof results in same on-chain hash. + pub fn verify_proof( + hashed_asset: &AccountInfo, + compression_proof: CompressionProof, + ) -> Result { + let asset = Self::from(compression_proof); + let asset_hash = asset.hash()?; + let current_account_hash = HashedAsset::load(hashed_asset, 0)?.hash; + if asset_hash != current_account_hash { + return Err(MplAssetError::IncorrectAssetHash.into()); + } + + Ok(asset) + } } impl Compressible for Asset { fn hash(&self) -> Result<[u8; 32], ProgramError> { let serialized_data = self.try_to_vec()?; - Ok(keccak::hash(serialized_data.as_slice()).to_bytes()) } + + fn wrap(&self) -> ProgramResult { + let serialized_data = self.try_to_vec()?; + invoke(&spl_noop::instruction(serialized_data), &[]) + } } impl DataBlob for Asset { diff --git a/programs/mpl-asset/src/state/mod.rs b/programs/mpl-asset/src/state/mod.rs index 2d1e6655..756f67ad 100644 --- a/programs/mpl-asset/src/state/mod.rs +++ b/programs/mpl-asset/src/state/mod.rs @@ -47,11 +47,6 @@ pub enum ExtraAccounts { owner_pda: Option, }, } - -pub trait Compressible { - fn hash(&self) -> Result<[u8; 32], ProgramError>; -} - #[derive(Clone, Copy, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, FromPrimitive)] pub enum Key { Uninitialized, diff --git a/programs/mpl-asset/src/state/traits.rs b/programs/mpl-asset/src/state/traits.rs index 85832acc..7a284e60 100644 --- a/programs/mpl-asset/src/state/traits.rs +++ b/programs/mpl-asset/src/state/traits.rs @@ -1,6 +1,5 @@ -use crate::{error::MplAssetError, state::Key}; +use crate::{error::MplAssetError, state::Key, utils::load_key}; use borsh::{BorshDeserialize, BorshSerialize}; -use num_traits::FromPrimitive; use solana_program::account_info::AccountInfo; use solana_program::entrypoint::ProgramResult; use solana_program::msg; @@ -36,9 +35,7 @@ pub trait SolanaAccount: BorshSerialize + BorshDeserialize { } } -pub fn load_key(account: &AccountInfo, offset: usize) -> Result { - let key = Key::from_u8((*account.data).borrow()[offset]) - .ok_or(MplAssetError::DeserializationError)?; - - Ok(key) +pub trait Compressible { + fn hash(&self) -> Result<[u8; 32], ProgramError>; + fn wrap(&self) -> ProgramResult; } diff --git a/programs/mpl-asset/src/utils.rs b/programs/mpl-asset/src/utils.rs index 0e8f8412..be1a516d 100644 --- a/programs/mpl-asset/src/utils.rs +++ b/programs/mpl-asset/src/utils.rs @@ -1,7 +1,9 @@ -use num_traits::FromPrimitive; -use solana_program::{account_info::AccountInfo, program_error::ProgramError}; - use crate::{error::MplAssetError, state::Key}; +use num_traits::FromPrimitive; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, + program_error::ProgramError, +}; pub fn load_key(account: &AccountInfo, offset: usize) -> Result { let key = Key::from_u8((*account.data).borrow()[offset])