Skip to content

Commit

Permalink
Value visitor (#17045)
Browse files Browse the repository at this point in the history
## Description 

Cherry pick of the value visitor change

## Test Plan 

CI + manually tested
  • Loading branch information
tzakian authored Apr 4, 2024
1 parent 5eec999 commit 2d07a1f
Show file tree
Hide file tree
Showing 30 changed files with 1,715 additions and 142 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/data-transform/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use once_cell::sync::Lazy;
use std::process::exit;
use std::str::FromStr;
use std::sync::Arc;
use sui_types::object::bounded_visitor::BoundedVisitor;

use move_bytecode_utils::module_cache::SyncModuleCache;
use move_core_types::annotated_value::MoveStruct;
use sui_types::object::MoveObject;

use self::models::*;
Expand Down Expand Up @@ -286,7 +286,7 @@ fn main() {

match layout {
Ok(l) => {
let move_object = MoveStruct::simple_deserialize(&event.event_bcs, &l)
let move_object = BoundedVisitor::deserialize_struct(&event.event_bcs, &l)
.map_err(|e| IndexerError::SerdeError(e.to_string()));

match move_object {
Expand Down
3 changes: 2 additions & 1 deletion crates/sui-analytics-indexer/src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use sui_package_resolver::{PackageStore, Resolver};
use sui_types::base_types::ObjectID;
use sui_types::effects::TransactionEffects;
use sui_types::effects::TransactionEffectsAPI;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::object::{Object, Owner};
use sui_types::transaction::TransactionData;
use sui_types::transaction::TransactionDataAPI;
Expand Down Expand Up @@ -174,7 +175,7 @@ async fn get_move_struct<T: PackageStore>(
.await?
{
MoveTypeLayout::Struct(move_struct_layout) => {
MoveStruct::simple_deserialize(contents, &move_struct_layout)
BoundedVisitor::deserialize_struct(contents, &move_struct_layout)
}
_ => Err(anyhow!("Object is not a move struct")),
}?;
Expand Down
5 changes: 3 additions & 2 deletions crates/sui-core/src/unit_tests/subscription_handler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use move_core_types::account_address::AccountAddress;
use move_core_types::identifier::Identifier;

use move_core_types::{
annotated_value::{MoveFieldLayout, MoveStruct, MoveStructLayout, MoveTypeLayout},
annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
ident_str,
language_storage::StructTag,
};
Expand All @@ -17,6 +17,7 @@ use sui_json_rpc_types::SuiMoveStruct;

use sui_types::base_types::ObjectID;
use sui_types::gas_coin::GasCoin;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::{MOVE_STDLIB_ADDRESS, SUI_FRAMEWORK_ADDRESS};

#[test]
Expand All @@ -33,7 +34,7 @@ fn test_to_json_value() {
};
let event_bytes = bcs::to_bytes(&move_event).unwrap();
let sui_move_struct: SuiMoveStruct =
MoveStruct::simple_deserialize(&event_bytes, &TestEvent::layout())
BoundedVisitor::deserialize_struct(&event_bytes, &TestEvent::layout())
.unwrap()
.into();
let json_value = sui_move_struct.to_json_value();
Expand Down
6 changes: 3 additions & 3 deletions crates/sui-graphql-rpc/src/types/move_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use move_core_types::{
};
use serde::{Deserialize, Serialize};
use sui_package_resolver::Resolver;
use sui_types::object::bounded_visitor::BoundedVisitor;

use crate::context_data::package_cache::PackageCache;
use crate::{error::Error, types::json::Json, types::move_type::unexpected_signer_error};
Expand Down Expand Up @@ -123,9 +124,8 @@ impl MoveValue {
}

fn value_impl(&self, layout: A::MoveTypeLayout) -> Result<A::MoveValue, Error> {
// TODO: If this becomes a performance bottleneck, it can be made more efficient by not
// deserializing via `value::MoveValue` (but this is significantly more code).
bcs::from_bytes_seed(&layout, &self.bcs.0[..]).map_err(|_| {
// TODO (annotated-visitor): deserializing directly using a custom visitor.
BoundedVisitor::deserialize_value(&self.bcs.0[..], &layout).map_err(|_| {
let type_tag: TypeTag = (&layout).into();
Error::Internal(format!(
"Failed to deserialize Move value for type: {}",
Expand Down
5 changes: 4 additions & 1 deletion crates/sui-graphql-rpc/src/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use sui_indexer::schema::{objects, objects_history, objects_snapshot};
use sui_indexer::types::ObjectStatus as NativeObjectStatus;
use sui_indexer::types::OwnerType;
use sui_package_resolver::Resolver;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::object::{
MoveObject as NativeMoveObject, Object as NativeObject, Owner as NativeOwner,
};
Expand Down Expand Up @@ -1352,7 +1353,9 @@ pub(crate) async fn deserialize_move_struct(
return Err(Error::Internal("Object is not a move struct".to_string()));
};

let move_struct = MoveStruct::simple_deserialize(contents, &layout).map_err(|e| {
// TODO (annotated-visitor): Use custom visitors for extracting a dynamic field, and for
// creating a GraphQL MoveValue directly (not via an annotated visitor).
let move_struct = BoundedVisitor::deserialize_struct(contents, &layout).map_err(|e| {
Error::Internal(format!(
"Error deserializing move struct for type {}: {e}",
struct_tag.to_canonical_string(/* with_prefix */ true)
Expand Down
4 changes: 2 additions & 2 deletions crates/sui-indexer/src/models/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use std::str::FromStr;

use diesel::prelude::*;
use move_bytecode_utils::module_cache::GetModule;
use move_core_types::annotated_value::MoveStruct;
use move_core_types::identifier::Identifier;

use sui_json_rpc_types::{SuiEvent, SuiMoveStruct};
use sui_types::base_types::{ObjectID, SuiAddress};
use sui_types::digests::TransactionDigest;
use sui_types::event::EventID;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::object::MoveObject;
use sui_types::parse_sui_struct_tag;

Expand Down Expand Up @@ -108,7 +108,7 @@ impl StoredEvent {
let type_ = parse_sui_struct_tag(&self.event_type)?;

let layout = MoveObject::get_layout_from_struct_tag(type_.clone(), module_cache)?;
let move_object = MoveStruct::simple_deserialize(&self.bcs, &layout)
let move_object = BoundedVisitor::deserialize_struct(&self.bcs, &layout)
.map_err(|e| IndexerError::SerdeError(e.to_string()))?;
let parsed_json = SuiMoveStruct::from(move_object).to_json_value();
let tx_digest =
Expand Down
3 changes: 2 additions & 1 deletion crates/sui-json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use sui_types::base_types::{
};
use sui_types::id::{ID, RESOLVED_SUI_ID};
use sui_types::move_package::MovePackage;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::transfer::RESOLVED_RECEIVING_STRUCT;
use sui_types::MOVE_STDLIB_ADDRESS;

Expand Down Expand Up @@ -154,7 +155,7 @@ impl SuiJsonValue {
if let Some(s) = try_parse_string(layout, bytes) {
json!(s)
} else {
let result = bcs::from_bytes_seed(layout, bytes).map_or_else(
let result = BoundedVisitor::deserialize_value(bytes, layout).map_or_else(
|_| {
// fallback to array[u8] if fail to convert to json.
JsonValue::Array(
Expand Down
23 changes: 3 additions & 20 deletions crates/sui-replay/src/displays/transaction_displays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fmt::{Display, Formatter};
use std::sync::Arc;
use sui_execution::Executor;
use sui_types::execution_mode::ExecutionResult;
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::transaction::CallArg::Pure;
use sui_types::transaction::{
write_sep, Argument, CallArg, Command, ObjectArg, ProgrammableMoveCall, ProgrammableTransaction,
Expand Down Expand Up @@ -321,26 +322,8 @@ fn resolve_value(
executor: &Arc<dyn Executor + Send + Sync>,
store_factory: &LocalExec,
) -> anyhow::Result<MoveValue> {
let mut layout_resolver = executor.type_layout_resolver(Box::new(store_factory.clone()));
match type_tag {
TypeTag::Vector(inner_type_tag) => {
let inner_layout = resolve_to_layout(inner_type_tag, executor, store_factory);
MoveValue::simple_deserialize(bytes, &MoveTypeLayout::Vector(Box::from(inner_layout)))
}
TypeTag::Struct(struct_tag) => {
let annotated = layout_resolver.get_annotated_layout(struct_tag)?;
MoveValue::simple_deserialize(bytes, &MoveTypeLayout::Struct(annotated))
}
TypeTag::Bool => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::Bool),
TypeTag::U8 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U8),
TypeTag::U64 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U64),
TypeTag::U128 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U128),
TypeTag::Address => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::Address),
TypeTag::Signer => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::Signer),
TypeTag::U16 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U16),
TypeTag::U32 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U32),
TypeTag::U256 => MoveValue::simple_deserialize(bytes, &MoveTypeLayout::U256),
}
let layout = resolve_to_layout(type_tag, executor, store_factory);
BoundedVisitor::deserialize_value(bytes, &layout)
}

pub fn transform_command_results_to_annotated(
Expand Down
5 changes: 3 additions & 2 deletions crates/sui-transactional-test-runner/src/test_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use move_compiler::{
use move_core_types::ident_str;
use move_core_types::{
account_address::AccountAddress,
annotated_value::MoveStruct,
identifier::IdentStr,
language_storage::{ModuleId, TypeTag},
};
Expand Down Expand Up @@ -69,6 +68,7 @@ use sui_types::effects::{TransactionEffects, TransactionEffectsAPI, TransactionE
use sui_types::messages_checkpoint::{
CheckpointContents, CheckpointContentsDigest, CheckpointSequenceNumber, VerifiedCheckpoint,
};
use sui_types::object::bounded_visitor::BoundedVisitor;
use sui_types::storage::ObjectStore;
use sui_types::storage::ReadStore;
use sui_types::transaction::Command;
Expand Down Expand Up @@ -648,7 +648,8 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter {
object::Data::Move(move_obj) => {
let layout = move_obj.get_layout(&&*self).unwrap();
let move_struct =
MoveStruct::simple_deserialize(move_obj.contents(), &layout).unwrap();
BoundedVisitor::deserialize_struct(move_obj.contents(), &layout)
.unwrap();

self.stabilize_str(format!(
"Owner: {}\nVersion: {}\nContents: {}",
Expand Down
3 changes: 2 additions & 1 deletion crates/sui-types/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use serde_with::Bytes;

use crate::base_types::{ObjectID, SuiAddress, TransactionDigest};
use crate::error::{SuiError, SuiResult};
use crate::object::bounded_visitor::BoundedVisitor;
use crate::sui_serde::BigInt;
use crate::sui_serde::Readable;
use crate::SUI_SYSTEM_ADDRESS;
Expand Down Expand Up @@ -128,7 +129,7 @@ impl Event {
contents: &[u8],
layout: MoveStructLayout,
) -> SuiResult<MoveStruct> {
MoveStruct::simple_deserialize(contents, &layout).map_err(|e| {
BoundedVisitor::deserialize_struct(contents, &layout).map_err(|e| {
SuiError::ObjectSerializationError {
error: e.to_string(),
}
Expand Down
94 changes: 20 additions & 74 deletions crates/sui-types/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ use std::sync::Arc;
use move_binary_format::CompiledModule;
use move_bytecode_utils::layout::TypeLayoutBuilder;
use move_bytecode_utils::module_cache::GetModule;
use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue};
use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout};
use move_core_types::language_storage::StructTag;
use move_core_types::language_storage::TypeTag;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde_with::Bytes;

use crate::balance::Balance;
use crate::base_types::{MoveObjectType, ObjectIDParseError};
use crate::coin::{Coin, CoinMetadata, TreasuryCap};
use crate::crypto::{default_hash, deterministic_random_account_key};
Expand All @@ -36,6 +35,12 @@ use crate::{
};
use sui_protocol_config::ProtocolConfig;

use self::balance_traversal::BalanceTraversal;
use self::bounded_visitor::BoundedVisitor;

mod balance_traversal;
pub mod bounded_visitor;

pub const GAS_VALUE_FOR_TESTING: u64 = 300_000_000_000_000;
pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1);

Expand Down Expand Up @@ -318,7 +323,7 @@ impl MoveObject {

/// Convert `self` to the JSON representation dictated by `layout`.
pub fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, SuiError> {
MoveStruct::simple_deserialize(&self.contents, layout).map_err(|e| {
BoundedVisitor::deserialize_struct(&self.contents, layout).map_err(|e| {
SuiError::ObjectSerializationError {
error: e.to_string(),
}
Expand Down Expand Up @@ -358,90 +363,31 @@ impl MoveObject {

// Helpers for extracting Coin<T> balances for all T
impl MoveObject {
fn is_balance(s: &StructTag) -> Option<&TypeTag> {
(Balance::is_balance(s) && s.type_params.len() == 1).then(|| &s.type_params[0])
}

/// Get the total balances for all `Coin<T>` embedded in `self`.
pub fn get_coin_balances(
&self,
layout_resolver: &mut dyn LayoutResolver,
) -> Result<BTreeMap<TypeTag, u64>, SuiError> {
let mut balances = BTreeMap::default();

// Fast path without deserialization.
if let Some(type_tag) = self.type_.coin_type_maybe() {
let balance = self.get_coin_value_unsafe();
if balance > 0 {
*balances.entry(type_tag).or_insert(0) += balance;
}
Ok(if balance > 0 {
BTreeMap::from([(type_tag.clone(), balance)])
} else {
BTreeMap::default()
})
} else {
let layout = layout_resolver.get_annotated_layout(&self.type_().clone().into())?;
let move_struct = self.to_move_struct(&layout)?;
Self::get_coin_balances_in_struct(&move_struct, &mut balances, 0)?;
}

Ok(balances)
}

/// Get the total balances for all `Coin<T>` embedded in `s`, eitehr directly or in its
/// (transitive fields).
fn get_coin_balances_in_struct(
s: &MoveStruct,
balances: &mut BTreeMap<TypeTag, u64>,
value_depth: u64,
) -> Result<(), SuiError> {
let MoveStruct {
type_: struct_type,
fields,
} = s;

if let Some(type_tag) = Self::is_balance(struct_type) {
let balance = match fields[0].1 {
MoveValue::U64(n) => n,
_ => unreachable!(), // a Balance<T> object should have exactly one field, of type int
};

// Accumulate the found balance
if balance > 0 {
*balances.entry(type_tag.clone()).or_insert(0) += balance;
}
} else {
for field in fields {
Self::get_coin_balances_in_value(&field.1, balances, value_depth)?;
}
}

Ok(())
}

fn get_coin_balances_in_value(
v: &MoveValue,
balances: &mut BTreeMap<TypeTag, u64>,
value_depth: u64,
) -> Result<(), SuiError> {
const MAX_MOVE_VALUE_DEPTH: u64 = 256; // This is 2x was the current value of
// `max_move_value_depth` is from protocol config

let value_depth = value_depth + 1;

if value_depth > MAX_MOVE_VALUE_DEPTH {
return Err(SuiError::GenericAuthorityError {
error: "exceeded max move value depth".to_owned(),
});
}
let mut traversal = BalanceTraversal::default();
MoveStruct::visit_deserialize(&self.contents, &layout, &mut traversal).map_err(
|e| SuiError::ObjectSerializationError {
error: e.to_string(),
},
)?;

match v {
MoveValue::Struct(s) => Self::get_coin_balances_in_struct(s, balances, value_depth)?,
MoveValue::Vector(vec) => {
for entry in vec {
Self::get_coin_balances_in_value(entry, balances, value_depth)?;
}
}
_ => {}
Ok(traversal.finish())
}

Ok(())
}
}

Expand Down
Loading

0 comments on commit 2d07a1f

Please sign in to comment.