mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-08-25 03:11:21 +00:00
feat!: admit data-free default owners
BREAKING Before: Only owning programs can change data After: Default-owned accounts can have their data wiped
This commit is contained in:
@@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use lee_core::{
|
||||
Identifier, InputAccountIdentity, NullifierPublicKey, PrivateWitness, WitnessKind,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||
encryption::ViewingPublicKey,
|
||||
program::{
|
||||
AccountPostState, BlockValidityWindow, CallerData, ChainedCall, Claim,
|
||||
@@ -249,8 +249,8 @@ impl ExecutionState {
|
||||
.filter(|(pre_default, post)| pre_default.account != **post)
|
||||
.map(|(pre, post)| (pre.account_id, post))
|
||||
{
|
||||
assert_ne!(
|
||||
post.program_owner, DEFAULT_PROGRAM_OWNER,
|
||||
assert!(
|
||||
post.program_owner != DEFAULT_PROGRAM_OWNER || post.data == Data::default(),
|
||||
"Account {account_id} was modified but not claimed"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -658,9 +658,9 @@ pub enum ExecutionValidationError {
|
||||
},
|
||||
|
||||
#[error(
|
||||
"Post-state for account {account_id} has default program owner but pre-state was not default"
|
||||
"Post-state for account {account_id} has default program owner but pre-state carries data"
|
||||
)]
|
||||
NonDefaultAccountWithDefaultOwner { account_id: AccountId },
|
||||
DataBearingAccountWithDefaultOwner { account_id: AccountId },
|
||||
|
||||
#[error("Total balance across accounts overflowed 2^256 - 1")]
|
||||
BalanceSumOverflow,
|
||||
@@ -806,12 +806,12 @@ pub fn validate_execution(
|
||||
});
|
||||
}
|
||||
|
||||
// 7. If a post state has default program owner, the pre state must have been a default
|
||||
// account
|
||||
if post.account.program_owner == DEFAULT_PROGRAM_OWNER && pre.account != Account::default()
|
||||
// 7. A post state with default program owner must not have carried data in its pre state
|
||||
if post.account.program_owner == DEFAULT_PROGRAM_OWNER
|
||||
&& pre.account.data != Data::default()
|
||||
{
|
||||
return Err(
|
||||
ExecutionValidationError::NonDefaultAccountWithDefaultOwner {
|
||||
ExecutionValidationError::DataBearingAccountWithDefaultOwner {
|
||||
account_id: pre.account_id,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -375,3 +375,112 @@ fn program_should_fail_if_does_not_preserve_total_balance_by_burning() {
|
||||
))) if total_balance_pre_states == 100.into() && total_balance_post_states == 99.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_owner_credited_without_claim_succeeds() {
|
||||
let sender_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let sender = AccountId::from(&PublicKey::new_from_private_key(&sender_key));
|
||||
let recipient = AccountId::new([9; 32]);
|
||||
let transfer = crate::test_methods::modified_transfer_program();
|
||||
|
||||
let mut state = V03State::new().with_test_programs();
|
||||
state.force_insert_account(
|
||||
sender,
|
||||
Account {
|
||||
program_owner: transfer.id().into(),
|
||||
balance: 500_000,
|
||||
..Account::default()
|
||||
},
|
||||
);
|
||||
assert_eq!(state.get_account_by_id(recipient), Account::default());
|
||||
|
||||
let amount: u128 = 1;
|
||||
let message = public_transaction::Message::try_new(
|
||||
transfer.id(),
|
||||
vec![sender, recipient],
|
||||
vec![Nonce(0)],
|
||||
amount,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&sender_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
state.transition_from_public_transaction(&tx, 1, 0).unwrap();
|
||||
|
||||
let credited = state.get_account_by_id(recipient);
|
||||
assert_eq!(credited.program_owner, Account::default().program_owner);
|
||||
assert_eq!(credited.data, Data::default());
|
||||
assert!(credited.balance > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_owner_balance_is_frozen_even_for_its_signer() {
|
||||
let sender_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let sender = AccountId::from(&PublicKey::new_from_private_key(&sender_key));
|
||||
let recipient = AccountId::new([9; 32]);
|
||||
let transfer = crate::test_methods::modified_transfer_program();
|
||||
|
||||
let mut state = V03State::new().with_test_programs();
|
||||
state.force_insert_account(
|
||||
sender,
|
||||
Account {
|
||||
balance: 500_000,
|
||||
..Account::default()
|
||||
},
|
||||
);
|
||||
|
||||
let amount: u128 = 1;
|
||||
let message = public_transaction::Message::try_new(
|
||||
transfer.id(),
|
||||
vec![sender, recipient],
|
||||
vec![Nonce(0)],
|
||||
amount,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&sender_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(LeeError::InvalidProgramBehavior(InvalidProgramBehaviorError::ExecutionValidationFailed(
|
||||
ExecutionValidationError::UnauthorizedBalanceDecrease { account_id, owner_account_id, executing_program_id }
|
||||
))) if account_id == sender
|
||||
&& owner_account_id == Account::default().program_owner
|
||||
&& executing_program_id == transfer.id()
|
||||
));
|
||||
assert_eq!(state.get_account_by_id(sender).balance, 500_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_owner_left_with_data_still_rejected() {
|
||||
let account_id = AccountId::new([1; 32]);
|
||||
let mut state = V03State::new().with_test_programs();
|
||||
state.force_insert_account(
|
||||
account_id,
|
||||
Account {
|
||||
data: vec![0xca, 0xfe].try_into().unwrap(),
|
||||
..Account::default()
|
||||
},
|
||||
);
|
||||
|
||||
let program_id = crate::test_methods::noop().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx, 1, 0);
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(LeeError::InvalidProgramBehavior(
|
||||
InvalidProgramBehaviorError::ExecutionValidationFailed(
|
||||
ExecutionValidationError::DataBearingAccountWithDefaultOwner {
|
||||
account_id: err_account_id
|
||||
}
|
||||
)
|
||||
)) if err_account_id == account_id
|
||||
));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
|
||||
use lee_core::{
|
||||
BlockId, Commitment, Nullifier, PrivacyPreservingCircuitOutput, PublicAction, Timestamp,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||
program::{
|
||||
CallerData, ChainedCall, Claim, DEFAULT_PROGRAM_ID, DEFAULT_PROGRAM_OWNER,
|
||||
SystemInstruction, compute_public_authorized_pdas, validate_clear, validate_execution,
|
||||
@@ -333,7 +333,7 @@ impl ValidatedStateDiff {
|
||||
Some((*account_id, post))
|
||||
}) {
|
||||
ensure!(
|
||||
post.program_owner != DEFAULT_PROGRAM_OWNER,
|
||||
post.program_owner != DEFAULT_PROGRAM_OWNER || post.data == Data::default(),
|
||||
InvalidProgramBehaviorError::DefaultAccountModifiedWithoutClaim { account_id }
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user