Skip to content

Commit

Permalink
Check denylist v2 during execution (#18374)
Browse files Browse the repository at this point in the history
## Description 

This PR checks denylist v2 during execution.
At the end of execution, it collects all the coin objects that are
written with an address owner, and check whether the address is denied
for the coin.

## Test plan 

I am working on tests separately using the Move branch, since the Move
code hasn't been merged yet.
---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
  • Loading branch information
lxfind authored Jun 25, 2024
1 parent f645673 commit ada9bd5
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 3 deletions.
10 changes: 10 additions & 0 deletions crates/sui-core/tests/staged/sui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,16 @@ ExecutionFailureStatus:
STRUCT:
- congested_objects:
TYPENAME: CongestedObjects
34:
AddressDeniedForCoin:
STRUCT:
- address:
TYPENAME: SuiAddress
- coin_type: STR
35:
CoinTypeGlobalPause:
STRUCT:
- coin_type: STR
ExecutionStatus:
ENUM:
0:
Expand Down
52 changes: 50 additions & 2 deletions crates/sui-types/src/deny_list_v2.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::base_types::{EpochId, SuiAddress};
use crate::base_types::{EpochId, ObjectID, SuiAddress};
use crate::config::{Config, Setting};
use crate::deny_list_v1::{
get_deny_list_root_object, input_object_coin_types_for_denylist_check,
DENY_LIST_COIN_TYPE_INDEX, DENY_LIST_MODULE,
};
use crate::dynamic_field::{get_dynamic_field_from_store, DOFWrapper};
use crate::error::{UserInputError, UserInputResult};
use crate::error::{ExecutionError, ExecutionErrorKind, UserInputError, UserInputResult};
use crate::id::UID;
use crate::object::Object;
use crate::storage::ObjectStore;
use crate::transaction::{CheckedInputObjects, ReceivingObjects};
use crate::{MoveTypeTagTrait, SUI_FRAMEWORK_PACKAGE_ID};
use move_core_types::ident_str;
use move_core_types::language_storage::{StructTag, TypeTag};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;

/// Rust representation of the Move type 0x2::coin::DenyCapV2.
Expand Down Expand Up @@ -118,6 +120,52 @@ pub fn check_coin_deny_list_v2_during_signing(
Ok(())
}

pub fn check_coin_deny_list_v2_during_execution(
written_objects: &BTreeMap<ObjectID, Object>,
cur_epoch: EpochId,
object_store: &dyn ObjectStore,
) -> Result<(), ExecutionError> {
let mut new_coin_owners = BTreeMap::new();
for obj in written_objects.values() {
if obj.is_gas_coin() {
continue;
}
let Some(coin_type) = obj.coin_type_maybe() else {
continue;
};
let Ok(owner) = obj.owner.get_address_owner_address() else {
continue;
};
new_coin_owners
.entry(coin_type.to_canonical_string(false))
.or_insert_with(BTreeSet::new)
.insert(owner);
}
for (coin_type, owners) in new_coin_owners {
let Some(deny_list) = get_per_type_coin_deny_list_v2(&coin_type, object_store) else {
continue;
};
if check_global_pause(&deny_list, object_store, Some(cur_epoch)) {
return Err(ExecutionError::new(
ExecutionErrorKind::CoinTypeGlobalPause { coin_type },
None,
));
}
for owner in owners {
if check_address_denied_by_config(&deny_list, owner, object_store, Some(cur_epoch)) {
return Err(ExecutionError::new(
ExecutionErrorKind::AddressDeniedForCoin {
address: owner,
coin_type,
},
None,
));
}
}
}
Ok(())
}

pub fn get_per_type_coin_deny_list_v2(
coin_type: &String,
object_store: &dyn ObjectStore,
Expand Down
10 changes: 10 additions & 0 deletions crates/sui-types/src/execution_status.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::base_types::SuiAddress;
use crate::ObjectID;
use move_binary_format::file_format::{CodeOffset, TypeParameterIndex};
use move_core_types::language_storage::ModuleId;
Expand Down Expand Up @@ -203,6 +204,15 @@ pub enum ExecutionFailureStatus {

#[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")]
ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects },

#[error("Address {address:?} is denied for coin {coin_type}")]
AddressDeniedForCoin {
address: SuiAddress,
coin_type: String,
},

#[error("Coin type is globally paused for use: {coin_type}")]
CoinTypeGlobalPause { coin_type: String },
// NOTE: if you want to add a new enum,
// please add it at the end for Rust SDK backward compatibility.
}
Expand Down
7 changes: 6 additions & 1 deletion crates/sui-types/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod write_store;

use crate::base_types::{TransactionDigest, VersionNumber};
use crate::committee::EpochId;
use crate::error::SuiError;
use crate::error::{ExecutionError, SuiError};
use crate::execution::{DynamicallyLoadedObjectMetadata, ExecutionResults};
use crate::move_package::MovePackage;
use crate::transaction::{SenderSignedData, TransactionDataAPI, TransactionKey};
Expand Down Expand Up @@ -206,6 +206,11 @@ pub trait Storage {
&mut self,
wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
);

fn check_coin_deny_list(
&self,
written_objects: &BTreeMap<ObjectID, Object>,
) -> Result<(), ExecutionError>;
}

pub type PackageFetchResults<Package> = Result<Vec<Package>, Vec<ObjectID>>;
Expand Down
2 changes: 2 additions & 0 deletions crates/sui-types/tests/staged/exec_failure_status.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@
31: SharedObjectOperationNotAllowed
32: InputObjectDeleted
33: ExecutionCancelledDueToSharedObjectCongestion
34: AddressDeniedForCoin
35: CoinTypeGlobalPause
2 changes: 2 additions & 0 deletions sui-execution/latest/sui-adapter/src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mod checked {
receiving_objects,
transaction_digest,
protocol_config,
*epoch_id,
);

let mut gas_charger =
Expand Down Expand Up @@ -248,6 +249,7 @@ mod checked {
vec![],
tx_context.digest(),
protocol_config,
0,
);
let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
programmable_transactions::execution::execute::<execution_mode::Genesis>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ mod checked {
inputs,
results,
user_events,
state_view,
..
} = self;
let tx_digest = tx_context.digest();
Expand Down Expand Up @@ -828,6 +829,10 @@ mod checked {
}
}

if protocol_config.enable_coin_deny_list_v2() {
state_view.check_coin_deny_list(&written_objects)?;
}

let user_events = user_events
.into_iter()
.map(|(module_id, tag, contents)| {
Expand Down
19 changes: 19 additions & 0 deletions sui-execution/latest/sui-adapter/src/temporary_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
use sui_protocol_config::ProtocolConfig;
use sui_types::base_types::VersionDigest;
use sui_types::committee::EpochId;
use sui_types::deny_list_v2::check_coin_deny_list_v2_during_execution;
use sui_types::effects::{TransactionEffects, TransactionEvents};
use sui_types::execution::{
DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV2, SharedInput,
Expand Down Expand Up @@ -60,6 +61,10 @@ pub struct TemporaryStore<'backing> {
/// The set of objects that we may receive during execution. Not guaranteed to receive all, or
/// any of the objects referenced in this set.
receiving_objects: Vec<ObjectRef>,

// TODO: Now that we track epoch here, there are a few places we don't need to pass it around.
/// The current epoch.
cur_epoch: EpochId,
}

impl<'backing> TemporaryStore<'backing> {
Expand All @@ -71,6 +76,7 @@ impl<'backing> TemporaryStore<'backing> {
receiving_objects: Vec<ObjectRef>,
tx_digest: TransactionDigest,
protocol_config: &'backing ProtocolConfig,
cur_epoch: EpochId,
) -> Self {
let mutable_input_refs = input_objects.mutable_inputs();
let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
Expand Down Expand Up @@ -102,6 +108,7 @@ impl<'backing> TemporaryStore<'backing> {
wrapped_object_containers: BTreeMap::new(),
runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
receiving_objects,
cur_epoch,
}
}

Expand Down Expand Up @@ -1004,6 +1011,18 @@ impl<'backing> Storage for TemporaryStore<'backing> {
) {
TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
}

fn check_coin_deny_list(
&self,
written_objects: &BTreeMap<ObjectID, Object>,
) -> Result<(), ExecutionError> {
// TODO: How do we track runtime loaded objects for replay?
check_coin_deny_list_v2_during_execution(
written_objects,
self.cur_epoch,
self.store.as_object_store(),
)
}
}

impl<'backing> BackingPackageStore for TemporaryStore<'backing> {
Expand Down
7 changes: 7 additions & 0 deletions sui-execution/v0/sui-adapter/src/temporary_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,13 @@ impl<'backing> Storage for TemporaryStore<'backing> {
) {
unreachable!("Unused in v0")
}

fn check_coin_deny_list(
&self,
_written_objects: &BTreeMap<ObjectID, Object>,
) -> Result<(), ExecutionError> {
unreachable!("Coin denylist v2 is not supported in sui-execution v0");
}
}

impl<'backing> BackingPackageStore for TemporaryStore<'backing> {
Expand Down
7 changes: 7 additions & 0 deletions sui-execution/v1/sui-adapter/src/temporary_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,13 @@ impl<'backing> Storage for TemporaryStore<'backing> {
) {
unreachable!("Unused in v1")
}

fn check_coin_deny_list(
&self,
_written_objects: &BTreeMap<ObjectID, Object>,
) -> Result<(), ExecutionError> {
unreachable!("Coin denylist v2 is not supported in sui-execution v1");
}
}

impl<'backing> BackingPackageStore for TemporaryStore<'backing> {
Expand Down
7 changes: 7 additions & 0 deletions sui-execution/v2/sui-adapter/src/temporary_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,13 @@ impl<'backing> Storage for TemporaryStore<'backing> {
) {
TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
}

fn check_coin_deny_list(
&self,
_written_objects: &BTreeMap<ObjectID, Object>,
) -> Result<(), ExecutionError> {
unreachable!("Coin denylist v2 is not supported in sui-execution v2");
}
}

impl<'backing> BackingPackageStore for TemporaryStore<'backing> {
Expand Down

0 comments on commit ada9bd5

Please sign in to comment.