-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove accounts with zero balance from storage (#2959)
* Make `open_chain` method asynchronous Prepare to be able to call a new asynchronous `debit` method. * Refactor to add a `debit` helper method Move common code into the new method. * Remove accounts with zero balances Prune storage a little once an account becomes empty. * Update tests to not have empty accounts Update the computation of the state execution hash, considering that now empty accounts aren't included in it. * Move system unit tests to a separate file Follow the convention used for other unit tests. * Test if empty accounts are pruned Ensure that they are removed from the system execution state view.
- Loading branch information
Showing
4 changed files
with
175 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright (c) Zefchain Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use linera_base::{ | ||
data_types::{Blob, BlockHeight, Bytecode}, | ||
identifiers::ApplicationId, | ||
}; | ||
use linera_views::context::MemoryContext; | ||
|
||
use super::*; | ||
use crate::{ExecutionOutcome, ExecutionStateView, TestExecutionRuntimeContext}; | ||
|
||
/// Returns an execution state view and a matching operation context, for epoch 1, with root | ||
/// chain 0 as the admin ID and one empty committee. | ||
async fn new_view_and_context() -> ( | ||
ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>>, | ||
OperationContext, | ||
) { | ||
let description = ChainDescription::Root(5); | ||
let context = OperationContext { | ||
chain_id: ChainId::from(description), | ||
authenticated_signer: None, | ||
authenticated_caller_id: None, | ||
height: BlockHeight::from(7), | ||
index: Some(2), | ||
}; | ||
let state = SystemExecutionState { | ||
description: Some(description), | ||
epoch: Some(Epoch(1)), | ||
admin_id: Some(ChainId::root(0)), | ||
committees: BTreeMap::new(), | ||
..SystemExecutionState::default() | ||
}; | ||
let view = state.into_view().await; | ||
(view, context) | ||
} | ||
|
||
#[tokio::test] | ||
async fn application_message_index() -> anyhow::Result<()> { | ||
let (mut view, context) = new_view_and_context().await; | ||
let contract = Bytecode::new(b"contract".into()); | ||
let service = Bytecode::new(b"service".into()); | ||
let contract_blob = Blob::new_contract_bytecode(contract.compress()); | ||
let service_blob = Blob::new_service_bytecode(service.compress()); | ||
let bytecode_id = BytecodeId::new(contract_blob.id().hash, service_blob.id().hash); | ||
|
||
let operation = SystemOperation::CreateApplication { | ||
bytecode_id, | ||
parameters: vec![], | ||
instantiation_argument: vec![], | ||
required_application_ids: vec![], | ||
}; | ||
let mut txn_tracker = TransactionTracker::default(); | ||
view.context() | ||
.extra() | ||
.add_blobs([contract_blob, service_blob]) | ||
.await?; | ||
let new_application = view | ||
.system | ||
.execute_operation(context, operation, &mut txn_tracker) | ||
.await?; | ||
let [ExecutionOutcome::System(result)] = &txn_tracker.destructure()?.0[..] else { | ||
panic!("Unexpected outcome"); | ||
}; | ||
assert_eq!( | ||
result.messages[CREATE_APPLICATION_MESSAGE_INDEX as usize].message, | ||
SystemMessage::ApplicationCreated | ||
); | ||
let creation = MessageId { | ||
chain_id: context.chain_id, | ||
height: context.height, | ||
index: CREATE_APPLICATION_MESSAGE_INDEX, | ||
}; | ||
let id = ApplicationId { | ||
bytecode_id, | ||
creation, | ||
}; | ||
assert_eq!(new_application, Some((id, vec![]))); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[tokio::test] | ||
async fn open_chain_message_index() { | ||
let (mut view, context) = new_view_and_context().await; | ||
let epoch = view.system.epoch.get().unwrap(); | ||
let admin_id = view.system.admin_id.get().unwrap(); | ||
let committees = view.system.committees.get().clone(); | ||
let ownership = ChainOwnership::single(PublicKey::test_key(0)); | ||
let config = OpenChainConfig { | ||
ownership, | ||
committees, | ||
epoch, | ||
admin_id, | ||
balance: Amount::ZERO, | ||
application_permissions: Default::default(), | ||
}; | ||
let mut txn_tracker = TransactionTracker::default(); | ||
let operation = SystemOperation::OpenChain(config.clone()); | ||
let new_application = view | ||
.system | ||
.execute_operation(context, operation, &mut txn_tracker) | ||
.await | ||
.unwrap(); | ||
assert_eq!(new_application, None); | ||
let [ExecutionOutcome::System(result)] = &txn_tracker.destructure().unwrap().0[..] else { | ||
panic!("Unexpected outcome"); | ||
}; | ||
assert_eq!( | ||
result.messages[OPEN_CHAIN_MESSAGE_INDEX as usize].message, | ||
SystemMessage::OpenChain(config) | ||
); | ||
} | ||
|
||
/// Tests if an account is removed from storage if it is drained. | ||
#[tokio::test] | ||
async fn empty_accounts_are_removed() -> anyhow::Result<()> { | ||
let owner = Owner(CryptoHash::test_hash("account owner")); | ||
let amount = Amount::from_tokens(99); | ||
|
||
let mut view = SystemExecutionState { | ||
description: Some(ChainDescription::Root(0)), | ||
balances: BTreeMap::from([(owner, amount)]), | ||
..SystemExecutionState::default() | ||
} | ||
.into_view() | ||
.await; | ||
|
||
view.system.debit(Some(&owner), amount).await?; | ||
|
||
assert!(view.system.balances.indices().await?.is_empty()); | ||
|
||
Ok(()) | ||
} |