diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index 2148444201e..27ded8442d7 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -4118,7 +4118,7 @@ impl AuthorityState { ObjectLockStatus::LockedToTx { locked_by_tx } => locked_by_tx, }; - epoch_store.get_signed_transaction(&lock_info.tx_digest) + epoch_store.get_signed_transaction(&lock_info) } pub async fn get_objects(&self, objects: &[ObjectID]) -> IotaResult>> { diff --git a/crates/iota-core/src/authority/authority_per_epoch_store.rs b/crates/iota-core/src/authority/authority_per_epoch_store.rs index 5fb03ed47e1..3dbdfaa7333 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store.rs @@ -402,7 +402,7 @@ pub struct AuthorityEpochTables { DBMap>, /// Map from ObjectRef to transaction locking that object - #[default_options_override_fn = "owned_object_transaction_locks_table_default_config"] + #[default_options_override_fn = "owned_object_locked_transactions_table_default_config"] owned_object_locked_transactions: DBMap, /// Signatures over transaction effects that we have signed and returned to @@ -607,7 +607,7 @@ fn signed_transactions_table_default_config() -> DBOptions { .optimize_for_large_values_no_scan(1 << 10) } -fn owned_object_transaction_locks_table_default_config() -> DBOptions { +fn owned_object_locked_transactions_table_default_config() -> DBOptions { DBOptions { options: default_db_options() .optimize_for_write_throughput() diff --git a/crates/iota-core/src/authority/authority_store.rs b/crates/iota-core/src/authority/authority_store.rs index de0a8d7d579..563d820f00b 100644 --- a/crates/iota-core/src/authority/authority_store.rs +++ b/crates/iota-core/src/authority/authority_store.rs @@ -27,7 +27,6 @@ use iota_types::{ }; use itertools::izip; use move_core_types::resolver::ModuleResolver; -use serde::{Deserialize, Serialize}; use tokio::{ sync::{RwLockReadGuard, RwLockWriteGuard}, time::Instant, @@ -45,7 +44,7 @@ use super::{ }; use crate::{ authority::{ - authority_per_epoch_store::AuthorityPerEpochStore, + authority_per_epoch_store::{AuthorityPerEpochStore, LockDetails}, authority_store_pruner::{ AuthorityStorePruner, AuthorityStorePruningMetrics, EPOCH_DURATION_MS_FOR_TESTING, }, @@ -712,9 +711,9 @@ impl AuthorityStore { // Update the index if object.get_single_owner().is_some() { - // Only initialize lock for address owned objects. + // Only initialize live object markers for address owned objects. if !object.is_child_object() { - self.initialize_live_object_markers_impl(&mut write_batch, &[object_ref], false)?; + self.initialize_live_object_markers_impl(&mut write_batch, &[object_ref])?; } } @@ -758,11 +757,7 @@ impl AuthorityStore { .map(|(oref, _)| *oref) .collect(); - self.initialize_live_object_markers_impl( - &mut batch, - &non_child_object_refs, - false, // is_force_reset - )?; + self.initialize_live_object_markers_impl(&mut batch, &non_child_object_refs)?; batch.write()?; @@ -801,7 +796,6 @@ impl AuthorityStore { &perpetual_db.live_owned_object_markers, &mut batch, &[object.compute_object_reference()], - false, // is_force_reset )?; } } @@ -922,8 +916,8 @@ impl AuthorityStore { deleted, written, events, - locks_to_delete, - new_locks_to_init, + live_object_markers_to_delete, + new_live_object_markers_to_init, .. } = tx_outputs; @@ -1006,11 +1000,11 @@ impl AuthorityStore { write_batch.insert_batch(&self.perpetual_tables.events, events)?; - self.initialize_live_object_markers_impl(write_batch, new_locks_to_init, false)?; + self.initialize_live_object_markers_impl(write_batch, new_live_object_markers_to_init)?; - // Note: deletes locks for received objects as well (but not for objects that - // were in `Receiving` arguments which were not received) - self.delete_live_object_markers(write_batch, locks_to_delete)?; + // Note: deletes live object markers for received objects as well (but not for + // objects that were in `Receiving` arguments which were not received) + self.delete_live_object_markers(write_batch, live_object_markers_to_delete)?; write_batch .insert_batch(&self.perpetual_tables.effects, [( @@ -1051,13 +1045,12 @@ impl AuthorityStore { transaction: VerifiedSignedTransaction, ) -> IotaResult { let tx_digest = *transaction.digest(); - let epoch = epoch_store.epoch(); // Other writers may be attempting to acquire locks on the same objects, so a // mutex is required. // TODO: replace with optimistic db_transactions (i.e. set lock to tx if none) let _mutexes = self.acquire_locks(owned_input_objects).await; - trace!(?owned_input_objects, "acquire_locks"); + trace!(?owned_input_objects, "acquire_transaction_locks"); let mut locks_to_write = Vec::new(); let live_object_markers = self @@ -1076,33 +1069,18 @@ impl AuthorityStore { locks.into_iter(), owned_input_objects ) { - let Some(live_marker) = live_marker else { - let latest_lock = self.get_latest_live_version_for_object_id(obj_ref.0)?; + if live_marker.is_none() { + // object at that version does not exist + let latest_live_version = self.get_latest_live_version_for_object_id(obj_ref.0)?; fp_bail!( UserInputError::ObjectVersionUnavailableForConsumption { provided_obj_ref: *obj_ref, - current_version: latest_lock.1 + current_version: latest_live_version.1 } .into() ); }; - let live_marker = live_marker.map(|l| l.migrate().into_inner()); - - if let Some(LockDetailsDeprecated { - epoch: previous_epoch, - .. - }) = &live_marker - { - // this must be from a prior epoch, because we no longer write LockDetails to - // owned_object_transaction_locks - assert!( - previous_epoch < &epoch, - "lock for {:?} should be from a prior epoch", - obj_ref - ); - } - if let Some(previous_tx_digest) = &lock { if previous_tx_digest == &tx_digest { // no need to re-write lock @@ -1144,20 +1122,16 @@ impl AuthorityStore { .get(&obj_ref)? .is_none() { + // object at that version does not exist return Ok(ObjectLockStatus::LockedAtDifferentVersion { locked_ref: self.get_latest_live_version_for_object_id(obj_ref.0)?, }); } let tables = epoch_store.tables()?; - let epoch_id = epoch_store.epoch(); - if let Some(tx_digest) = tables.get_locked_transaction(&obj_ref)? { Ok(ObjectLockStatus::LockedToTx { - locked_by_tx: LockDetailsDeprecated { - epoch: epoch_id, - tx_digest, - }, + locked_by_tx: tx_digest, }) } else { Ok(ObjectLockStatus::Initialized) @@ -1200,17 +1174,18 @@ impl AuthorityStore { /// Returns UserInputError::ObjectVersionUnavailableForConsumption if at /// least one object lock is not initialized at the given version. pub fn check_owned_objects_are_live(&self, objects: &[ObjectRef]) -> IotaResult { - let locks = self + let live_markers = self .perpetual_tables .live_owned_object_markers .multi_get(objects)?; - for (lock, obj_ref) in locks.into_iter().zip(objects) { - if lock.is_none() { - let latest_lock = self.get_latest_live_version_for_object_id(obj_ref.0)?; + for (live_marker, obj_ref) in live_markers.into_iter().zip(objects) { + if live_marker.is_none() { + // object at that version does not exist + let latest_live_version = self.get_latest_live_version_for_object_id(obj_ref.0)?; fp_bail!( UserInputError::ObjectVersionUnavailableForConsumption { provided_obj_ref: *obj_ref, - current_version: latest_lock.1 + current_version: latest_live_version.1 } .into() ); @@ -1219,59 +1194,29 @@ impl AuthorityStore { Ok(()) } - /// Initialize a lock to None (but exists) for a given list of ObjectRefs. - /// Returns IotaError::ObjectLockAlreadyInitialized if the lock already - /// exists and is locked to a transaction + /// Initialize live object markers for a given list of ObjectRefs. fn initialize_live_object_markers_impl( &self, write_batch: &mut DBBatch, objects: &[ObjectRef], - is_force_reset: bool, ) -> IotaResult { AuthorityStore::initialize_live_object_markers( &self.perpetual_tables.live_owned_object_markers, write_batch, objects, - is_force_reset, ) } pub fn initialize_live_object_markers( - live_object_marker_table: &DBMap>, + live_object_marker_table: &DBMap, write_batch: &mut DBBatch, objects: &[ObjectRef], - is_force_reset: bool, ) -> IotaResult { - trace!(?objects, "initialize_locks"); - - let live_object_markers = live_object_marker_table.multi_get(objects)?; - - if !is_force_reset { - // If any live_object_markers exist and are not None, return errors for them - // Note we don't check if there is a pre-existing lock. this is because - // initializing the live object marker will not overwrite the lock - // and cause the validator to equivocate. - let existing_live_object_markers: Vec = live_object_markers - .iter() - .zip(objects) - .filter_map(|(lock_opt, objref)| { - lock_opt.clone().flatten().map(|_tx_digest| *objref) - }) - .collect(); - if !existing_live_object_markers.is_empty() { - info!( - ?existing_live_object_markers, - "Cannot initialize live_object_markers because some exist already" - ); - return Err(IotaError::ObjectLockAlreadyInitialized { - refs: existing_live_object_markers, - }); - } - } + trace!(?objects, "initialize_live_object_markers"); write_batch.insert_batch( live_object_marker_table, - objects.iter().map(|obj_ref| (obj_ref, None)), + objects.iter().map(|obj_ref| (obj_ref, ())), )?; Ok(()) } @@ -1282,7 +1227,7 @@ impl AuthorityStore { write_batch: &mut DBBatch, objects: &[ObjectRef], ) -> IotaResult { - trace!(?objects, "delete_locks"); + trace!(?objects, "delete_live_object_markers"); write_batch.delete_batch( &self.perpetual_tables.live_owned_object_markers, objects.iter(), @@ -1291,7 +1236,7 @@ impl AuthorityStore { } #[cfg(test)] - pub(crate) fn reset_locks_for_test( + pub(crate) fn reset_locks_and_live_markers_for_test( &self, transactions: &[TransactionDigest], objects: &[ObjectRef], @@ -1312,7 +1257,7 @@ impl AuthorityStore { batch.write().unwrap(); let mut batch = self.perpetual_tables.live_owned_object_markers.batch(); - self.initialize_live_object_markers_impl(&mut batch, objects, false) + self.initialize_live_object_markers_impl(&mut batch, objects) .unwrap(); batch.write().unwrap(); } @@ -1404,10 +1349,10 @@ impl AuthorityStore { let old_locks: Vec<_> = old_locks.flatten().collect(); - // Re-create old locks. - self.initialize_live_object_markers_impl(&mut write_batch, &old_locks, true)?; + // Re-create old live markers. + self.initialize_live_object_markers_impl(&mut write_batch, &old_locks)?; - // Delete new locks + // Delete new live markers write_batch.delete_batch( &self.perpetual_tables.live_owned_object_markers, new_locks.flatten(), @@ -1963,56 +1908,6 @@ pub type IotaLockResult = IotaResult; #[derive(Debug, PartialEq, Eq)] pub enum ObjectLockStatus { Initialized, - LockedToTx { locked_by_tx: LockDetailsDeprecated }, + LockedToTx { locked_by_tx: LockDetails }, // no need to use wrapper, not stored or serialized LockedAtDifferentVersion { locked_ref: ObjectRef }, } - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum LockDetailsWrapperDeprecated { - V1(LockDetailsV1Deprecated), -} - -impl LockDetailsWrapperDeprecated { - pub fn migrate(self) -> Self { - // TODO: when there are multiple versions, we must iteratively migrate from - // version N to N+1 until we arrive at the latest version - self - } - - // Always returns the most recent version. Older versions are migrated to the - // latest version at read time, so there is never a need to access older - // versions. - pub fn inner(&self) -> &LockDetailsDeprecated { - match self { - Self::V1(v1) => v1, - - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] - _ => panic!("lock details should have been migrated to latest version at read time"), - } - } - pub fn into_inner(self) -> LockDetailsDeprecated { - match self { - Self::V1(v1) => v1, - - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] - _ => panic!("lock details should have been migrated to latest version at read time"), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct LockDetailsV1Deprecated { - pub epoch: EpochId, - pub tx_digest: TransactionDigest, -} - -pub type LockDetailsDeprecated = LockDetailsV1Deprecated; - -impl From for LockDetailsWrapperDeprecated { - fn from(details: LockDetailsDeprecated) -> Self { - // always use latest version. - LockDetailsWrapperDeprecated::V1(details) - } -} diff --git a/crates/iota-core/src/authority/authority_store_tables.rs b/crates/iota-core/src/authority/authority_store_tables.rs index 480727f5122..932b08e9036 100644 --- a/crates/iota-core/src/authority/authority_store_tables.rs +++ b/crates/iota-core/src/authority/authority_store_tables.rs @@ -23,7 +23,6 @@ use typed_store::{ use super::*; use crate::authority::{ - authority_store::LockDetailsWrapperDeprecated, authority_store_types::{ ObjectContentDigest, StoreData, StoreMoveObjectWrapper, StoreObject, StoreObjectPair, StoreObjectValue, StoreObjectWrapper, get_store_object_pair, try_construct_object, @@ -63,15 +62,9 @@ pub struct AuthorityPerpetualTables { #[default_options_override_fn = "indirect_move_objects_table_default_config"] pub(crate) indirect_move_objects: DBMap, - /// This is a map between object references of currently active objects that - /// can be mutated. - /// - /// For old epochs, it may also contain the transaction that they are lock - /// on for use by this specific validator. The transaction locks - /// themselves are now in AuthorityPerEpochStore. - #[default_options_override_fn = "owned_object_transaction_locks_table_default_config"] - #[rename = "owned_object_transaction_locks"] - pub(crate) live_owned_object_markers: DBMap>, + /// Object references of currently active objects that can be mutated. + #[default_options_override_fn = "live_owned_object_markers_table_default_config"] + pub(crate) live_owned_object_markers: DBMap, /// This is a map between the transaction digest and the corresponding /// transaction that's known to be executable. This means that it may @@ -614,7 +607,7 @@ impl Iterator for LiveSetIter<'_> { } // These functions are used to initialize the DB tables -fn owned_object_transaction_locks_table_default_config() -> DBOptions { +fn live_owned_object_markers_table_default_config() -> DBOptions { DBOptions { options: default_db_options() .optimize_for_write_throughput() diff --git a/crates/iota-core/src/execution_cache/passthrough_cache.rs b/crates/iota-core/src/execution_cache/passthrough_cache.rs index 4efc4bf42ee..0261942423a 100644 --- a/crates/iota-core/src/execution_cache/passthrough_cache.rs +++ b/crates/iota-core/src/execution_cache/passthrough_cache.rs @@ -245,20 +245,20 @@ impl ExecutionCacheWrite for PassthroughCache { let tx_digest = *tx_outputs.transaction.digest(); let effects_digest = tx_outputs.effects.digest(); - // NOTE: We just check here that locks exist, not that they are locked to a - // specific TX. Why? - // 1. Lock existence prevents re-execution of old certs when objects have been - // upgraded + // NOTE: We just check here that live markers exist, not that they are locked to + // a specific TX. Why? + // 1. Live markers existence prevents re-execution of old certs when objects + // have been upgraded // 2. Not all validators lock, just 2f+1, so transaction should proceed - // regardless (But the lock should exist which means previous transactions - // finished) + // regardless (But the live markers should exist which means previous + // transactions finished) // 3. Equivocation possible (different TX) but as long as 2f+1 approves current // TX its fine - // 4. Locks may have existed when we started processing this tx, but could have - // since been deleted by a concurrent tx that finished first. In that case, - // check if the tx effects exist. + // 4. Live markers may have existed when we started processing this tx, but + // could have since been deleted by a concurrent tx that finished first. In + // that case, check if the tx effects exist. self.store - .check_owned_objects_are_live(&tx_outputs.locks_to_delete)?; + .check_owned_objects_are_live(&tx_outputs.live_object_markers_to_delete)?; self.store .write_transaction_outputs(epoch_id, &[tx_outputs]) diff --git a/crates/iota-core/src/execution_cache/unit_tests/writeback_cache_tests.rs b/crates/iota-core/src/execution_cache/unit_tests/writeback_cache_tests.rs index 1056e9e8d3d..585c53d2061 100644 --- a/crates/iota-core/src/execution_cache/unit_tests/writeback_cache_tests.rs +++ b/crates/iota-core/src/execution_cache/unit_tests/writeback_cache_tests.rs @@ -159,8 +159,8 @@ impl Scenario { markers: Default::default(), wrapped: Default::default(), deleted: Default::default(), - locks_to_delete: Default::default(), - new_locks_to_init: Default::default(), + live_object_markers_to_delete: Default::default(), + new_live_object_markers_to_init: Default::default(), written: Default::default(), } } @@ -216,7 +216,7 @@ impl Scenario { let owner_id = self.id_map.get(&owner).expect("no such object"); let object = Self::new_child(*owner_id); self.outputs - .new_locks_to_init + .new_live_object_markers_to_init .push(object.compute_object_reference()); let id = object.id(); assert!(self.id_map.insert(short_id, id).is_none()); @@ -229,7 +229,7 @@ impl Scenario { for short_id in short_ids { let object = Self::new_object(); self.outputs - .new_locks_to_init + .new_live_object_markers_to_init .push(object.compute_object_reference()); let id = object.id(); assert!(self.id_map.insert(*short_id, id).is_none()); @@ -270,12 +270,12 @@ impl Scenario { let id = self.id_map.get(short_id).expect("object not found"); let object = self.objects.get(id).cloned().expect("object not found"); self.outputs - .locks_to_delete + .live_object_markers_to_delete .push(object.compute_object_reference()); let object = Self::inc_version_by(object, delta); self.objects.insert(*id, object.clone()); self.outputs - .new_locks_to_init + .new_live_object_markers_to_init .push(object.compute_object_reference()); self.outputs.written.insert(object.id(), object); } @@ -288,7 +288,7 @@ impl Scenario { let id = self.id_map.get(short_id).expect("object not found"); let object = self.objects.remove(id).expect("object not found"); let mut object_ref = object.compute_object_reference(); - self.outputs.locks_to_delete.push(object_ref); + self.outputs.live_object_markers_to_delete.push(object_ref); // in the authority this would be set to the lamport version of the tx object_ref.1.increment(); self.outputs.deleted.push(object_ref.into()); @@ -302,7 +302,7 @@ impl Scenario { let id = self.id_map.get(short_id).expect("object not found"); let object = self.objects.get(id).cloned().expect("object not found"); let mut object_ref = object.compute_object_reference(); - self.outputs.locks_to_delete.push(object_ref); + self.outputs.live_object_markers_to_delete.push(object_ref); // in the authority this would be set to the lamport version of the tx object_ref.1.increment(); self.outputs.wrapped.push(object_ref.into()); @@ -317,7 +317,7 @@ impl Scenario { let id = self.id_map.get(short_id).expect("object not found"); let object = self.objects.get(id).cloned().expect("object not found"); self.outputs - .new_locks_to_init + .new_live_object_markers_to_init .iter() .find(|o| **o == object.compute_object_reference()) .expect("received object must have new lock"); diff --git a/crates/iota-core/src/execution_cache/writeback_cache.rs b/crates/iota-core/src/execution_cache/writeback_cache.rs index 8a62852cbbe..4307f7485da 100644 --- a/crates/iota-core/src/execution_cache/writeback_cache.rs +++ b/crates/iota-core/src/execution_cache/writeback_cache.rs @@ -87,9 +87,7 @@ use crate::{ authority::{ AuthorityStore, authority_per_epoch_store::AuthorityPerEpochStore, - authority_store::{ - ExecutionLockWriteGuard, IotaLockResult, LockDetailsDeprecated, ObjectLockStatus, - }, + authority_store::{ExecutionLockWriteGuard, IotaLockResult, ObjectLockStatus}, authority_store_tables::LiveObject, epoch_start_configuration::{EpochFlag, EpochStartConfiguration}, }, @@ -1577,7 +1575,6 @@ impl ObjectCacheRead for WritebackCache { } fn get_lock(&self, obj_ref: ObjectRef, epoch_store: &AuthorityPerEpochStore) -> IotaLockResult { - let cur_epoch = epoch_store.epoch(); match self.get_object_by_id_cache_only("lock", &obj_ref.0) { CacheResult::Hit((_, obj)) => { let actual_objref = obj.compute_object_reference(); @@ -1593,10 +1590,7 @@ impl ObjectCacheRead for WritebackCache { .get_transaction_lock(&obj_ref, epoch_store)? { Some(tx_digest) => ObjectLockStatus::LockedToTx { - locked_by_tx: LockDetailsDeprecated { - epoch: cur_epoch, - tx_digest, - }, + locked_by_tx: tx_digest, }, None => ObjectLockStatus::Initialized, }, diff --git a/crates/iota-core/src/transaction_outputs.rs b/crates/iota-core/src/transaction_outputs.rs index 1b14b9a9e6d..a43b287725f 100644 --- a/crates/iota-core/src/transaction_outputs.rs +++ b/crates/iota-core/src/transaction_outputs.rs @@ -24,8 +24,8 @@ pub struct TransactionOutputs { pub markers: Vec<(ObjectKey, MarkerValue)>, pub wrapped: Vec, pub deleted: Vec, - pub locks_to_delete: Vec, - pub new_locks_to_init: Vec, + pub live_object_markers_to_delete: Vec, + pub new_live_object_markers_to_init: Vec, pub written: WrittenObjects, } @@ -97,7 +97,7 @@ impl TransactionOutputs { received.chain(deleted).chain(shared_smears).collect() }; - let locks_to_delete: Vec<_> = mutable_inputs + let live_object_markers_to_delete: Vec<_> = mutable_inputs .into_iter() .filter_map(|(id, ((version, digest), owner))| { owner.is_address_owned().then_some((id, version, digest)) @@ -105,7 +105,7 @@ impl TransactionOutputs { .chain(received_objects) .collect(); - let new_locks_to_init: Vec<_> = written + let new_live_object_markers_to_init: Vec<_> = written .values() .filter_map(|new_object| { if new_object.is_address_owned() { @@ -132,8 +132,8 @@ impl TransactionOutputs { markers, wrapped, deleted, - locks_to_delete, - new_locks_to_init, + live_object_markers_to_delete, + new_live_object_markers_to_init, written, } } diff --git a/crates/iota-core/src/unit_tests/authority_tests.rs b/crates/iota-core/src/unit_tests/authority_tests.rs index b9c44885ce9..208a4631bf5 100644 --- a/crates/iota-core/src/unit_tests/authority_tests.rs +++ b/crates/iota-core/src/unit_tests/authority_tests.rs @@ -2081,14 +2081,16 @@ async fn test_conflicting_transactions() { .auth_sig() ); - authority_state.database_for_testing().reset_locks_for_test( - &[*tx1.digest(), *tx2.digest()], - &[ - gas_object.compute_object_reference(), - object.compute_object_reference(), - ], - &authority_state.epoch_store_for_testing(), - ); + authority_state + .database_for_testing() + .reset_locks_and_live_markers_for_test( + &[*tx1.digest(), *tx2.digest()], + &[ + gas_object.compute_object_reference(), + object.compute_object_reference(), + ], + &authority_state.epoch_store_for_testing(), + ); } }