-
Notifications
You must be signed in to change notification settings - Fork 0
setup reclaim order #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 29 commits
9ce4ad9
555239a
013557a
b892ae7
9199880
83e3556
ce3280c
68a3bea
8919f68
76e8434
af95669
3d25f1f
908681b
c6627d7
8a4a989
9ed99a6
ffdefe4
a7afebd
4c045d4
fee918a
e0c8467
ba0fd79
bfb85ae
09b2d87
ed4b7e1
a2d2d99
a227027
b3134f2
d279b62
ab25eff
496d7d6
bd7e61d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,14 +22,18 @@ use core::mem::size_of; | |
|
|
||
| use arrayref::{array_refs, mut_array_refs}; | ||
| use derive_more::Deref; | ||
| use solana_account_view::AccountView; | ||
| use solana_address::Address; | ||
| use solana_hash::Hash; | ||
| use solana_program_error::ProgramError; | ||
| use solana_pubkey::Pubkey; | ||
|
|
||
| use crate::data::intent::{self, EncodedOrderIntent, OrderIntent}; | ||
| use crate::pda::order::order_pda_signer_seeds; | ||
| use crate::SettlementError; | ||
|
|
||
| /// Idiomatic representation of an order PDA's body. | ||
| #[derive(Clone, Debug, Eq, PartialEq)] | ||
| #[derive(Clone, Debug, Eq, PartialEq, Default)] | ||
| pub struct OrderAccount { | ||
| /// `false` = the order is still active and can be filled; `true` = the | ||
| /// order has been cancelled by the owner and must not be filled. | ||
|
|
@@ -51,6 +55,33 @@ pub struct OrderAccount { | |
| pub intent: OrderIntent, | ||
| } | ||
|
|
||
| impl OrderAccount { | ||
| /// Load and decode the order at the given PDA, and confirm | ||
| /// the PDA is reached by its own data and the provided bump | ||
| pub fn load_from_pda( | ||
| order_pda: &AccountView, | ||
| program_id: &Address, | ||
| bump: u8, | ||
| ) -> Result<Self, ProgramError> { | ||
| let (account, uid) = { | ||
| let data = order_pda.try_borrow()?; | ||
| let bytes: &[u8; EncodedOrderAccount::SIZE] = (&*data) | ||
| .try_into() | ||
| .map_err(|_| ProgramError::InvalidAccountData)?; | ||
| EncodedOrderAccount::decode_and_hash(bytes)? | ||
| }; | ||
|
|
||
| let expected = | ||
| Address::create_program_address(&order_pda_signer_seeds(&uid, &[bump]), program_id) | ||
| .map_err(|_| SettlementError::AccountNotDerivable)?; | ||
| if &expected != order_pda.address() { | ||
| return Err(SettlementError::AccountNotDerivable.into()); | ||
| } | ||
|
|
||
| Ok(account) | ||
| } | ||
| } | ||
|
Comment on lines
+58
to
+83
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to have traits for account deserialization just like we do for Instruction parsing? (ref)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would but as of right now this is the only I didn't see any great stdlib traits that would have made sense here either so if we do make a trait it would literally just be defining this function and thats it; and as you mention there are the bump consideration issues. |
||
|
|
||
| /// Canonical 200-byte representation of an [`OrderAccount`]. The bytes | ||
|
kaze-cow marked this conversation as resolved.
|
||
| /// written to/read from the order PDA's data area. | ||
| /// | ||
|
|
@@ -388,6 +419,72 @@ mod tests { | |
| assert_eq!(err, ProgramError::InvalidAccountData); | ||
| } | ||
|
|
||
| mod load_from_pda { | ||
| use super::*; | ||
| use crate::instruction::fixtures::fake_account_with_data; | ||
| use crate::pda::order::find_order_pda; | ||
|
|
||
| const PROGRAM_ID: Address = Address::new_from_array([9; 32]); | ||
|
|
||
| #[test] | ||
| fn accepts_the_canonical_pda() { | ||
| let account = sample_account(false); | ||
| let (pda_address, bump) = find_order_pda(&PROGRAM_ID, &account.intent.uid()); | ||
| let order_pda = fake_account_with_data( | ||
| pda_address, | ||
| &EncodedOrderAccount::from(account.clone())[..], | ||
| ); | ||
|
|
||
| let loaded = OrderAccount::load_from_pda(&order_pda, &PROGRAM_ID, bump) | ||
| .expect("canonical PDA must load"); | ||
| assert_eq!(loaded, account); | ||
| } | ||
|
|
||
| #[test] | ||
| fn rejects_a_non_canonical_address() { | ||
| let account = sample_account(false); | ||
| let (_, bump) = find_order_pda(&PROGRAM_ID, &account.intent.uid()); | ||
| // An address unrelated to the intent's canonical seeds. | ||
| let wrong_address = Pubkey::new_from_array([0x42; 32]); | ||
| let order_pda = | ||
| fake_account_with_data(wrong_address, &EncodedOrderAccount::from(account)[..]); | ||
|
|
||
| let err = OrderAccount::load_from_pda(&order_pda, &PROGRAM_ID, bump) | ||
| .expect_err("a non-canonical address must be rejected"); | ||
| assert_eq!(err, SettlementError::AccountNotDerivable.into()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn rejects_a_wrong_bump() { | ||
| let account = sample_account(false); | ||
| let (pda_address, bump) = find_order_pda(&PROGRAM_ID, &account.intent.uid()); | ||
| let order_pda = | ||
| fake_account_with_data(pda_address, &EncodedOrderAccount::from(account)[..]); | ||
|
|
||
| // Any bump other than the canonical one either derives a | ||
| // different address or fails to derive one at all (falling on | ||
| // curve); either way, the PDA can no longer be proven canonical. | ||
|
fedgiac marked this conversation as resolved.
|
||
| let wrong_bump = bump.wrapping_sub(1); | ||
| let err = OrderAccount::load_from_pda(&order_pda, &PROGRAM_ID, wrong_bump) | ||
| .expect_err("a non-canonical bump must be rejected"); | ||
| assert_eq!(err, SettlementError::AccountNotDerivable.into()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn propagates_decode_errors() { | ||
| let account = sample_account(false); | ||
| let (pda_address, bump) = find_order_pda(&PROGRAM_ID, &account.intent.uid()); | ||
| let mut bytes: [u8; EncodedOrderAccount::SIZE] = | ||
| EncodedOrderAccount::from(account).into(); | ||
| bytes[CANCELLED_OFFSET] = 0xff; | ||
| let order_pda = fake_account_with_data(pda_address, &bytes); | ||
|
|
||
| let err = OrderAccount::load_from_pda(&order_pda, &PROGRAM_ID, bump) | ||
| .expect_err("a corrupt account must fail to decode"); | ||
| assert_eq!(err, ProgramError::InvalidAccountData); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn direct_write_account_matches_order_account_decoding() { | ||
| let cancelled = true; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.