mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-05 06:43:08 +00:00
add account post state struct with claiming request field
This commit is contained in:
parent
2d34925725
commit
d677db7f4e
@ -20,11 +20,34 @@ pub struct ChainedCall {
|
||||
pub pre_states: Vec<AccountWithMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
||||
pub struct AccountPostState {
|
||||
pub account: Account,
|
||||
pub claim: bool,
|
||||
}
|
||||
|
||||
impl From<Account> for AccountPostState {
|
||||
fn from(account: Account) -> Self {
|
||||
AccountPostState {
|
||||
account,
|
||||
claim: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountPostState {
|
||||
pub fn with_claim_request(mut self) -> Self {
|
||||
self.claim = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
||||
pub struct ProgramOutput {
|
||||
pub pre_states: Vec<AccountWithMetadata>,
|
||||
pub post_states: Vec<Account>,
|
||||
pub post_states: Vec<AccountPostState>,
|
||||
pub chained_calls: Vec<ChainedCall>,
|
||||
}
|
||||
|
||||
@ -38,7 +61,10 @@ pub fn read_nssa_inputs<T: DeserializeOwned>() -> ProgramInput<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec<Account>) {
|
||||
pub fn write_nssa_outputs(
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: Vec<AccountPostState>,
|
||||
) {
|
||||
let output = ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
@ -49,7 +75,7 @@ pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec
|
||||
|
||||
pub fn write_nssa_outputs_with_chained_call(
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: Vec<Account>,
|
||||
post_states: Vec<AccountPostState>,
|
||||
chained_calls: Vec<ChainedCall>,
|
||||
) {
|
||||
let output = ProgramOutput {
|
||||
@ -68,7 +94,7 @@ pub fn write_nssa_outputs_with_chained_call(
|
||||
/// - `executing_program_id`: The identifier of the program that was executed.
|
||||
pub fn validate_execution(
|
||||
pre_states: &[AccountWithMetadata],
|
||||
post_states: &[Account],
|
||||
post_states: &[AccountPostState],
|
||||
executing_program_id: ProgramId,
|
||||
) -> bool {
|
||||
// 1. Lengths must match
|
||||
@ -78,25 +104,27 @@ pub fn validate_execution(
|
||||
|
||||
for (pre, post) in pre_states.iter().zip(post_states) {
|
||||
// 2. Nonce must remain unchanged
|
||||
if pre.account.nonce != post.nonce {
|
||||
if pre.account.nonce != post.account.nonce {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Program ownership changes are not allowed
|
||||
if pre.account.program_owner != post.program_owner {
|
||||
if pre.account.program_owner != post.account.program_owner {
|
||||
return false;
|
||||
}
|
||||
|
||||
let account_program_owner = pre.account.program_owner;
|
||||
|
||||
// 4. Decreasing balance only allowed if owned by executing program
|
||||
if post.balance < pre.account.balance && account_program_owner != executing_program_id {
|
||||
if post.account.balance < pre.account.balance
|
||||
&& account_program_owner != executing_program_id
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. Data changes only allowed if owned by executing program or if account pre state has
|
||||
// default values
|
||||
if pre.account.data != post.data
|
||||
if pre.account.data != post.account.data
|
||||
&& pre.account != Account::default()
|
||||
&& account_program_owner != executing_program_id
|
||||
{
|
||||
@ -105,17 +133,37 @@ pub fn validate_execution(
|
||||
|
||||
// 6. If a post state has default program owner, the pre state must have been a default
|
||||
// account
|
||||
if post.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() {
|
||||
if post.account.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Total balance is preserved
|
||||
let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum();
|
||||
let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum();
|
||||
let total_balance_post_states: u128 = post_states.iter().map(|post| post.account.balance).sum();
|
||||
if total_balance_pre_states != total_balance_post_states {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_account_post_state_from_account_constructor() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
data: vec![0xde, 0xad, 0xbe, 0xef],
|
||||
nonce: 10,
|
||||
};
|
||||
|
||||
let account_post_state: AccountPostState = account.clone().into();
|
||||
|
||||
assert_eq!(account, account_post_state.account);
|
||||
assert!(!account_post_state.claim);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
use nssa_core::{
|
||||
account::{Account, AccountWithMetadata},
|
||||
program::{ProgramInput, read_nssa_inputs, write_nssa_outputs},
|
||||
program::{AccountPostState, ProgramInput, read_nssa_inputs, write_nssa_outputs},
|
||||
};
|
||||
|
||||
/// Initializes a default account under the ownership of this program.
|
||||
/// This is achieved by a noop.
|
||||
fn initialize_account(pre_state: AccountWithMetadata) {
|
||||
let account_to_claim = pre_state.account.clone();
|
||||
let account_to_claim: AccountPostState = pre_state.account.clone().into();
|
||||
let is_authorized = pre_state.is_authorized;
|
||||
|
||||
// Continue only if the account to claim has default values
|
||||
if account_to_claim != Account::default() {
|
||||
if account_to_claim.account != Account::default() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -41,7 +41,10 @@ fn transfer(sender: AccountWithMetadata, recipient: AccountWithMetadata, balance
|
||||
sender_post.balance -= balance_to_move;
|
||||
recipient_post.balance += balance_to_move;
|
||||
|
||||
write_nssa_outputs(vec![sender, recipient], vec![sender_post, recipient_post]);
|
||||
write_nssa_outputs(
|
||||
vec![sender, recipient],
|
||||
vec![sender_post.into(), recipient_post.into()],
|
||||
);
|
||||
}
|
||||
|
||||
/// A transfer of balance program.
|
||||
|
||||
@ -66,5 +66,5 @@ fn main() {
|
||||
pinata_post.data = data.next_data().to_vec();
|
||||
winner_post.balance += PRIZE;
|
||||
|
||||
write_nssa_outputs(vec![pinata, winner], vec![pinata_post, winner_post]);
|
||||
write_nssa_outputs(vec![pinata, winner], vec![pinata_post.into(), winner_post.into()]);
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ fn main() {
|
||||
// Public account
|
||||
public_pre_states.push(pre_states[i].clone());
|
||||
|
||||
let mut post = post_states[i].clone();
|
||||
let mut post = post_states[i].account.clone();
|
||||
if pre_states[i].is_authorized {
|
||||
post.nonce += 1;
|
||||
}
|
||||
@ -126,7 +126,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Update post-state with new nonce
|
||||
let mut post_with_updated_values = post_states[i].clone();
|
||||
let mut post_with_updated_values = post_states[i].account.clone();
|
||||
post_with_updated_values.nonce = *new_nonce;
|
||||
|
||||
if post_with_updated_values.program_owner == DEFAULT_PROGRAM_ID {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||
program::{ProgramInput, read_nssa_inputs, write_nssa_outputs},
|
||||
program::{read_nssa_inputs, write_nssa_outputs, AccountPostState, ProgramInput},
|
||||
};
|
||||
|
||||
// The token program has three functions:
|
||||
@ -112,7 +112,7 @@ impl TokenHolding {
|
||||
}
|
||||
}
|
||||
|
||||
fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Account> {
|
||||
fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<AccountPostState> {
|
||||
if pre_states.len() != 2 {
|
||||
panic!("Invalid number of input accounts");
|
||||
}
|
||||
@ -156,14 +156,14 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Ac
|
||||
this
|
||||
};
|
||||
|
||||
vec![sender_post, recipient_post]
|
||||
vec![sender_post.into(), recipient_post.into()]
|
||||
}
|
||||
|
||||
fn new_definition(
|
||||
pre_states: &[AccountWithMetadata],
|
||||
name: [u8; 6],
|
||||
total_supply: u128,
|
||||
) -> Vec<Account> {
|
||||
) -> Vec<AccountPostState> {
|
||||
if pre_states.len() != 2 {
|
||||
panic!("Invalid number of input accounts");
|
||||
}
|
||||
@ -196,10 +196,10 @@ fn new_definition(
|
||||
let mut holding_target_account_post = holding_target_account.account.clone();
|
||||
holding_target_account_post.data = token_holding.into_data();
|
||||
|
||||
vec![definition_target_account_post, holding_target_account_post]
|
||||
vec![definition_target_account_post.into(), holding_target_account_post.into()]
|
||||
}
|
||||
|
||||
fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<Account> {
|
||||
fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<AccountPostState> {
|
||||
if pre_states.len() != 2 {
|
||||
panic!("Invalid number of accounts");
|
||||
}
|
||||
@ -223,7 +223,7 @@ fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<Account> {
|
||||
let mut account_to_initialize_post = account_to_initialize.account.clone();
|
||||
account_to_initialize_post.data = holding_values.into_data();
|
||||
|
||||
vec![definition_post, account_to_initialize_post]
|
||||
vec![definition_post.into(), account_to_initialize_post.into()]
|
||||
}
|
||||
|
||||
type Instruction = [u8; 23];
|
||||
@ -387,14 +387,14 @@ mod tests {
|
||||
let post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10);
|
||||
let [definition_account, holding_account] = post_states.try_into().ok().unwrap();
|
||||
assert_eq!(
|
||||
definition_account.data,
|
||||
definition_account.account.data,
|
||||
vec![
|
||||
0, 0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
holding_account.data,
|
||||
holding_account.account.data,
|
||||
vec![
|
||||
1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@ -619,14 +619,14 @@ mod tests {
|
||||
let post_states = transfer(&pre_states, 11);
|
||||
let [sender_post, recipient_post] = post_states.try_into().ok().unwrap();
|
||||
assert_eq!(
|
||||
sender_post.data,
|
||||
sender_post.account.data,
|
||||
vec![
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
recipient_post.data,
|
||||
recipient_post.account.data,
|
||||
vec![
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
@ -657,9 +657,9 @@ mod tests {
|
||||
];
|
||||
let post_states = initialize_account(&pre_states);
|
||||
let [definition, holding] = post_states.try_into().ok().unwrap();
|
||||
assert_eq!(definition.data, pre_states[0].account.data);
|
||||
assert_eq!(definition.account.data, pre_states[0].account.data);
|
||||
assert_eq!(
|
||||
holding.data,
|
||||
holding.account.data,
|
||||
vec![
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
@ -239,8 +239,8 @@ mod tests {
|
||||
|
||||
let [sender_post, recipient_post] = program_output.post_states.try_into().unwrap();
|
||||
|
||||
assert_eq!(sender_post, expected_sender_post);
|
||||
assert_eq!(recipient_post, expected_recipient_post);
|
||||
assert_eq!(sender_post.account, expected_sender_post);
|
||||
assert_eq!(recipient_post.account, expected_recipient_post);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -155,8 +155,8 @@ impl PublicTransaction {
|
||||
|
||||
// The invoked program claims the accounts with default program id.
|
||||
for post in program_output.post_states.iter_mut() {
|
||||
if post.program_owner == DEFAULT_PROGRAM_ID {
|
||||
post.program_owner = chained_call.program_id;
|
||||
if post.account.program_owner == DEFAULT_PROGRAM_ID {
|
||||
post.account.program_owner = chained_call.program_id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ impl PublicTransaction {
|
||||
.iter()
|
||||
.zip(program_output.post_states.iter())
|
||||
{
|
||||
state_diff.insert(pre.account_id, post.clone());
|
||||
state_diff.insert(pre.account_id, post.account.clone());
|
||||
}
|
||||
|
||||
for new_call in program_output.chained_calls.into_iter().rev() {
|
||||
|
||||
@ -17,5 +17,5 @@ fn main() {
|
||||
let mut account_post = account_pre.clone();
|
||||
account_post.balance -= balance_to_burn;
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_post]);
|
||||
write_nssa_outputs(vec![pre], vec![account_post.into()]);
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ fn main() {
|
||||
|
||||
write_nssa_outputs_with_chained_call(
|
||||
vec![sender_pre.clone(), receiver_pre.clone()],
|
||||
vec![sender_pre.account, receiver_pre.account],
|
||||
vec![sender_pre.account.into(), receiver_pre.account.into()],
|
||||
chained_call,
|
||||
);
|
||||
}
|
||||
|
||||
@ -14,5 +14,5 @@ fn main() {
|
||||
let mut account_post = account_pre.clone();
|
||||
account_post.data.push(0);
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_post]);
|
||||
write_nssa_outputs(vec![pre], vec![account_post.into()]);
|
||||
}
|
||||
|
||||
@ -15,5 +15,5 @@ fn main() {
|
||||
|
||||
let account_pre = pre.account.clone();
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_pre, Account::default()]);
|
||||
write_nssa_outputs(vec![pre], vec![account_pre.into(), Account::default().into()]);
|
||||
}
|
||||
|
||||
@ -14,5 +14,5 @@ fn main() {
|
||||
let mut account_post = account_pre.clone();
|
||||
account_post.balance += 1;
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_post]);
|
||||
write_nssa_outputs(vec![pre], vec![account_post.into()]);
|
||||
}
|
||||
|
||||
@ -12,5 +12,5 @@ fn main() {
|
||||
|
||||
let account_pre1 = pre1.account.clone();
|
||||
|
||||
write_nssa_outputs(vec![pre1, pre2], vec![account_pre1]);
|
||||
write_nssa_outputs(vec![pre1, pre2], vec![account_pre1.into()]);
|
||||
}
|
||||
|
||||
@ -14,5 +14,5 @@ fn main() {
|
||||
let mut account_post = account_pre.clone();
|
||||
account_post.nonce += 1;
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_post]);
|
||||
write_nssa_outputs(vec![pre], vec![account_post.into()]);
|
||||
}
|
||||
|
||||
@ -14,5 +14,5 @@ fn main() {
|
||||
let mut account_post = account_pre.clone();
|
||||
account_post.program_owner = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
write_nssa_outputs(vec![pre], vec![account_post]);
|
||||
write_nssa_outputs(vec![pre], vec![account_post.into()]);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use nssa_core::program::{read_nssa_inputs, write_nssa_outputs, ProgramInput};
|
||||
use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs};
|
||||
|
||||
type Instruction = u128;
|
||||
|
||||
@ -20,6 +20,6 @@ fn main() {
|
||||
|
||||
write_nssa_outputs(
|
||||
vec![sender_pre, receiver_pre],
|
||||
vec![sender_post, receiver_post],
|
||||
vec![sender_post.into(), receiver_post.into()],
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user