Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion concordium-base
12 changes: 7 additions & 5 deletions plt/plt-block-state/src/block_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,11 @@ impl<C: EntityContextTypes> BlockStateOperations for ExecutionTimeBlockStateP9<C
self.block_state.update_token(&self.context, token).unwrap();
}

fn create_lock(&mut self, _lock_id: LockId, _configuration: LockConfiguration) {
fn create_lock(&mut self, _configuration: LockConfiguration) {
panic!("no locks on P9")
}

fn delete_lock(&mut self, _lock_id: &LockId) -> Option<LockP11> {
fn delete_lock(&mut self, _: &LockId) -> bool {
panic!("no locks on P9")
}

Expand Down Expand Up @@ -478,6 +478,8 @@ impl<C: EntityContextTypes> BlockStateQuery for ExecutionTimeBlockStateP11<C> {

fn lock_configuration(&self, lock: &LockP11) -> LockConfiguration {
lock.lock_configuration(&self.context)
.expect("lock must contain the configuration")
.to_owned()
}

fn lock_balances(&self, lock: &LockP11) -> impl Iterator<Item = (AccountIndex, Self::Token)> {
Expand Down Expand Up @@ -550,13 +552,13 @@ impl<C: EntityContextTypes> BlockStateOperations for ExecutionTimeBlockStateP11<
self.block_state.update_token(&self.context, token).unwrap();
}

fn create_lock(&mut self, lock_id: LockId, configuration: LockConfiguration) {
fn create_lock(&mut self, configuration: LockConfiguration) {
self.block_state
.create_lock(&self.context, lock_id, configuration)
.create_lock(&self.context, configuration)
.unwrap();
}

fn delete_lock(&mut self, lock_id: &LockId) -> Option<LockP11> {
fn delete_lock(&mut self, lock_id: &LockId) -> bool {
self.block_state
.delete_lock(&self.context, lock_id)
.unwrap()
Expand Down
19 changes: 16 additions & 3 deletions plt/plt-block-state/src/block_state_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ pub trait BlockStateQuery {
///
/// If the protocol version does not support protocol-level locks, this will return the empty
/// list.
///
/// # Ordering
///
/// The order of the returned lock IDs is **not guaranteed**. Callers must not rely on any
/// particular ordering.
///
/// # Warning — consensus safety
///
/// Do **not** use this iterator directly to drive block-state mutations in the scheduler.
/// All nodes must execute transactions in identical order to reach the same state hash;
/// iterating in an unspecified order and acting on each element would produce diverging
/// state across nodes. Sort the result (or otherwise canonicalise it) before using it
/// to determine the sequence of any state-changing operations.
fn lock_list(&self) -> impl ExactSizeIterator<Item = LockId>;

/// Get the lock associated with a [`LockId`] (if it exists). If the protocol
Expand Down Expand Up @@ -298,9 +311,9 @@ pub trait BlockStateOperations: BlockStateQuery {
/// - The `lock` of the given configuration MUST NOT already be in use by a protocol-level
/// lock, i.e. `assert_eq!(s.lock_by_id(lock_id).ok(), None)`.
/// - The protocol version of the block state MUST support PLT locks.
fn create_lock(&mut self, lock_id: LockId, configuration: LockConfiguration);
fn create_lock(&mut self, configuration: LockConfiguration);

/// Delete a PLT lock with the given Lock ID. Returns the lock if it existed, or `None`
/// Delete a PLT lock with the given Lock ID. Returns `true` if it existed, or `false`
/// if it did not exist.
///
/// # Arguments
Expand All @@ -310,7 +323,7 @@ pub trait BlockStateOperations: BlockStateQuery {
/// # Preconditions
///
/// This function may panic if the protocol version does not support locks.
fn delete_lock(&mut self, lock_id: &LockId) -> Option<LockP11>;
fn delete_lock(&mut self, lock_id: &LockId) -> bool;

/// Track that a lock holds a balance for the given account and token.
///
Expand Down
13 changes: 6 additions & 7 deletions plt/plt-block-state/src/entity/block_state/p11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,35 +137,33 @@ impl BlockStateP11 {
pub fn create_lock<C: EntityContextTypes>(
&mut self,
context: &EntityContext<C>,
lock_id: LockId,
configuration: LockConfiguration,
) -> BlockStateResult<()> {
let mut new_locks = self.persistent.locks.value(&context.loader)?.into_owned();
protocol_level_locks::p11::create_lock(context, &mut new_locks, lock_id, configuration)?;
protocol_level_locks::p11::create_lock(context, &mut new_locks, configuration)?;

self.persistent.locks = HashedCacheableRef::new(new_locks);

Ok(())
}

/// Delete the lock with the given [`LockId`] if it exists. Returns the
/// deleted lock if it existed, or `None` if it did not exist.
/// Delete the lock with the given [`LockId`] if it exists. Returns `true` if it existed, or
/// `false` if it did not exist.
///
/// # Arguments
/// - `lock_id` The ID of the PLT lock to delete.
pub fn delete_lock<C: EntityContextTypes>(
&mut self,
context: &EntityContext<C>,
lock_id: &LockId,
) -> BlockStateResult<Option<LockP11>> {
) -> BlockStateResult<bool> {
let mut new_locks = self.persistent.locks.value(&context.loader)?.into_owned();
let existing = protocol_level_locks::p11::delete_lock(context, &mut new_locks, lock_id)?;
if existing.is_some() {
if existing {
// We only need to update the locks if a lock was actually deleted,
// otherwise we would be unnecessarily updating the block state.
self.persistent.locks = HashedCacheableRef::new(new_locks);
}

Ok(existing)
}

Expand All @@ -179,6 +177,7 @@ impl BlockStateP11 {
context,
&*self.persistent.locks.value(&context.loader)?,
)
.cloned()
.collect())
}

Expand Down
99 changes: 66 additions & 33 deletions plt/plt-block-state/src/entity/protocol_level_locks/p11.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
use crate::entity::{EntityContext, EntityContextTypes};
use crate::failure::{BlockStateFailure, BlockStateResult};
use crate::persistent::blob_reference::hashed_cacheable_reference::HashedCacheableRef;
use crate::persistent::blob_store::StoreSerialized;
use crate::persistent::protocol_level_locks::p11::{
LockConfiguration, PersistentLockP11, PersistentLocksP11,
LockConfiguration, LockIndex, PersistentLockP11, PersistentLocksP11,
};
use crate::persistent::protocol_level_tokens::p9::TokenIndex;
use crate::utils;
use concordium_base::base::AccountIndex;
use concordium_base::protocol_level_locks::LockId;

pub(crate) fn lock_list<C: EntityContextTypes>(
/// List all non-deleted lock ids in *no particular order*.
pub(crate) fn lock_list<'a, C: EntityContextTypes>(
_context: &EntityContext<C>,
persistent_locks: &PersistentLocksP11,
) -> impl ExactSizeIterator<Item = LockId> {
persistent_locks.locks.0.keys().cloned()
persistent_locks: &'a PersistentLocksP11,
) -> impl Iterator<Item = &'a LockId> {
persistent_locks.lock_id_map.keys()
}

pub(crate) fn create_lock<C: EntityContextTypes>(
_context: &EntityContext<C>,
context: &EntityContext<C>,
persistent_locks: &mut PersistentLocksP11,
lock_id: LockId,
configuration: LockConfiguration,
) -> BlockStateResult<()> {
let lock_id = configuration.lock_id().clone();
let persistent = PersistentLockP11 {
locked_balances: Default::default(),
configuration,
configuration: HashedCacheableRef::new(StoreSerialized(configuration)),
};
let existing = persistent_locks.locks.0.insert(lock_id.clone(), persistent);
let (lock_index, updated_locks) = persistent_locks
.locks
.insert_value(&context.loader, Some(persistent))?;
persistent_locks.locks = updated_locks;
let existing = persistent_locks
.lock_id_map
.insert(lock_id.clone(), lock_index);
Comment thread
limemloh marked this conversation as resolved.
if existing.is_some() {
return Err(BlockStateFailure::Invariant(format!(
"lock with id {:?} already exists",
Expand All @@ -36,65 +46,88 @@ pub(crate) fn create_lock<C: EntityContextTypes>(
}

pub(crate) fn delete_lock<C: EntityContextTypes>(
_context: &EntityContext<C>,
context: &EntityContext<C>,
persistent_locks: &mut PersistentLocksP11,
lock_id: &LockId,
) -> BlockStateResult<Option<LockP11>> {
let existing = persistent_locks.locks.0.remove(lock_id);
Ok(existing.map(|persistent| LockP11 {
lock_id: lock_id.clone(),
persistent,
}))
) -> BlockStateResult<bool> {
let Some(lock_index) = persistent_locks.lock_id_map.remove(lock_id) else {
return Ok(false);
};
persistent_locks.locks = persistent_locks
.locks
.update_value(&context.loader, lock_index, |_| Ok(None))?
.ok_or_else(|| {
BlockStateFailure::Invariant(format!("Lock not found by index: {:?}", lock_id))
})?;
Ok(true)
}

pub(crate) fn update_lock<C: EntityContextTypes>(
_context: &EntityContext<C>,
context: &EntityContext<C>,
persistent_locks: &mut PersistentLocksP11,
lock: LockP11,
) -> BlockStateResult<()> {
persistent_locks
persistent_locks.locks = persistent_locks
.locks
.0
.insert(lock.lock_id, lock.persistent);
.update_value(&context.loader, lock.lock_index, |_| {
Ok(Some(lock.persistent))
})?
.ok_or_else(|| {
BlockStateFailure::Invariant(format!("Lock not found by index: {:?}", lock.lock_index))
})?;
Ok(())
}

pub(crate) fn lock_by_id<C: EntityContextTypes>(
_context: &EntityContext<C>,
context: &EntityContext<C>,
persistent_locks: &PersistentLocksP11,
lock_id: LockId,
) -> BlockStateResult<Option<LockP11>> {
let Some(persistent) = persistent_locks.locks.0.get(&lock_id) else {
let Some(&lock_index) = persistent_locks.lock_id_map.get(&lock_id) else {
return Ok(None);
};
let Some(persistent) = persistent_locks
.locks
.lookup_value(&context.loader, lock_index)?
else {
return Err(BlockStateFailure::Invariant(format!(
"No lock entry found for lock index {} ({lock_id})",
lock_index.0
)));
};
let Some(persistent) = persistent.to_owned() else {
// Lock is deleted.
return Ok(None);
};

Ok(Some(LockP11 {
lock_id,
persistent: persistent.clone(),
lock_index,
persistent,
}))
}

/// Representation of protocol-level lock on P11 and later protocols with compatible model.
#[derive(Debug)]
pub struct LockP11 {
/// Lock ID
pub(crate) lock_id: LockId,
pub(crate) lock_index: LockIndex,
/// Persistent model of the protocol-level lock.
pub(crate) persistent: PersistentLockP11,
}

impl LockP11 {
/// Get the Lock ID of the lock.
pub fn lock_id(&self) -> &LockId {
&self.lock_id
/// Get the internal block state index of the lock.
pub fn lock_index(&self) -> LockIndex {
self.lock_index
}

/// Get the configuration of the protocol-level lock.
pub fn lock_configuration<C: EntityContextTypes>(
&self,
_context: &EntityContext<C>,
) -> LockConfiguration {
self.persistent.configuration.clone()
context: &EntityContext<C>,
) -> BlockStateResult<utils::Cow<'_, LockConfiguration>> {
self.persistent
.configuration
.value(&context.loader)
.map(|cow| cow.cow_project())
}

/// Get the set of account/token balances currently tracked under the lock.
Expand Down
Loading
Loading