From ed3d6ed85c63f7fd87942d9638f80bf8b0e0b6c8 Mon Sep 17 00:00:00 2001 From: Timothy Zakian Date: Tue, 10 Sep 2024 16:23:02 -0700 Subject: [PATCH] [Move] box type layout vectors and increase max type nodes --- .../tests/entry_points/large_enum.exp | 4 +- .../tests/entry_points/large_enum_v58.exp | 22 ++++++++ .../tests/entry_points/large_enum_v58.move | 41 ++++++++++++++ .../unit_tests/subscription_handler_tests.rs | 14 +++-- crates/sui-graphql-rpc/src/types/move_type.rs | 4 +- .../sui-graphql-rpc/src/types/move_value.rs | 8 +-- .../src/handlers/checkpoint_handler.rs | 2 +- crates/sui-indexer/src/models/objects.rs | 2 +- crates/sui-json/src/lib.rs | 55 +++++++++---------- crates/sui-json/src/tests.rs | 50 ++++++++--------- crates/sui-open-rpc/spec/openrpc.json | 1 + crates/sui-open-rpc/src/examples.rs | 2 +- crates/sui-package-resolver/src/lib.rs | 10 ++-- crates/sui-protocol-config/src/lib.rs | 9 ++- ...ocol_config__test__Mainnet_version_59.snap | 1 + ...ocol_config__test__Testnet_version_59.snap | 1 + ...sui_protocol_config__test__version_59.snap | 1 + crates/sui-types/src/balance.rs | 4 +- crates/sui-types/src/coin.rs | 8 +-- crates/sui-types/src/id.rs | 10 ++-- crates/sui-types/src/layout_resolver.rs | 2 +- crates/sui-types/src/object.rs | 2 +- .../sui-types/src/object/balance_traversal.rs | 7 ++- .../sui-types/src/object/bounded_visitor.rs | 7 ++- .../crates/move-bytecode-utils/src/layout.rs | 31 ++++++----- .../tests/functions/large_enum.exp | 10 +++- .../tests/functions/large_enum.move | 11 ++++ .../move-core-types/src/annotated_value.rs | 21 ++++--- .../move-core-types/src/runtime_value.rs | 20 +++---- .../src/unit_tests/value_test.rs | 52 +++++++++++------- .../src/unit_tests/visitor_test.rs | 7 ++- .../move/crates/move-vm-config/src/runtime.rs | 4 ++ .../move/crates/move-vm-runtime/src/loader.rs | 38 ++++++++----- .../move-vm-types/src/values/values_impl.rs | 13 +++-- .../v0/crates/move-vm-runtime/src/loader.rs | 21 +++---- .../v1/crates/move-vm-runtime/src/loader.rs | 21 +++---- .../v2/crates/move-vm-runtime/src/loader.rs | 21 +++---- .../latest/sui-adapter/src/adapter.rs | 1 + sui-execution/v0/sui-adapter/src/adapter.rs | 1 + sui-execution/v1/sui-adapter/src/adapter.rs | 1 + sui-execution/v2/sui-adapter/src/adapter.rs | 1 + 41 files changed, 335 insertions(+), 206 deletions(-) create mode 100644 crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.exp create mode 100644 crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.move diff --git a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp index 4c3435ef46f66..5e576af8aa286 100644 --- a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp +++ b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp @@ -18,5 +18,5 @@ gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0 task 3, lines 40-41: //# programmable --sender A //> test::m::x3() -Error: Transaction Effects Status: Move Bytecode Verification Error. Please run the Bytecode Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: VMVerificationOrDeserializationError, source: Some(VMError { major_status: TOO_MANY_TYPE_NODES, sub_status: None, message: None, exec_state: None, location: Undefined, indices: [], offsets: [] }), command: Some(0) } } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 diff --git a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.exp b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.exp new file mode 100644 index 0000000000000..4c3435ef46f66 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.exp @@ -0,0 +1,22 @@ +processed 4 tasks + +init: +A: object(0,0) + +task 1, lines 8-35: +//# publish +created: object(1,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 6452400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, lines 37-38: +//# programmable --sender A +//> test::m::x1() +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, lines 40-41: +//# programmable --sender A +//> test::m::x3() +Error: Transaction Effects Status: Move Bytecode Verification Error. Please run the Bytecode Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: VMVerificationOrDeserializationError, source: Some(VMError { major_status: TOO_MANY_TYPE_NODES, sub_status: None, message: None, exec_state: None, location: Undefined, indices: [], offsets: [] }), command: Some(0) } } diff --git a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.move b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.move new file mode 100644 index 0000000000000..b266f17ae52c0 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum_v58.move @@ -0,0 +1,41 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// tests error after serializing a large enum return value + +//# init --addresses test=0x0 --accounts A --protocol-version 58 + +//# publish + +module test::m { + +public enum X1 has drop { + Big1(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8), +} + +public enum X2 has drop { + V1(X1, X1, X1), + V2(X1, X1, X1), + V3(X1, X1, X1), +} + +public enum X3 has drop { + X2(X2, X2, X2), + U64(u64), +} + +entry fun x1(): X1 { + X1::Big1(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +} + +entry fun x3(): X3 { + X3::U64(0) +} + +} + +//# programmable --sender A +//> test::m::x1() + +//# programmable --sender A +//> test::m::x3() diff --git a/crates/sui-core/src/unit_tests/subscription_handler_tests.rs b/crates/sui-core/src/unit_tests/subscription_handler_tests.rs index 67b2e78b3110a..1e67cf8cb34a6 100644 --- a/crates/sui-core/src/unit_tests/subscription_handler_tests.rs +++ b/crates/sui-core/src/unit_tests/subscription_handler_tests.rs @@ -85,11 +85,11 @@ impl TestEvent { fn layout() -> MoveStructLayout { MoveStructLayout { type_: Self::type_(), - fields: vec![ + fields: Box::new(vec![ MoveFieldLayout::new(ident_str!("creator").to_owned(), MoveTypeLayout::Address), MoveFieldLayout::new( ident_str!("name").to_owned(), - MoveTypeLayout::Struct(UTF8String::layout()), + MoveTypeLayout::Struct(Box::new(UTF8String::layout())), ), MoveFieldLayout::new( ident_str!("data").to_owned(), @@ -97,9 +97,11 @@ impl TestEvent { ), MoveFieldLayout::new( ident_str!("coins").to_owned(), - MoveTypeLayout::Vector(Box::new(MoveTypeLayout::Struct(GasCoin::layout()))), + MoveTypeLayout::Vector(Box::new(MoveTypeLayout::Struct(Box::new( + GasCoin::layout(), + )))), ), - ], + ]), } } } @@ -131,10 +133,10 @@ impl UTF8String { fn layout() -> MoveStructLayout { MoveStructLayout { type_: Self::type_(), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("bytes").to_owned(), MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - )], + )]), } } } diff --git a/crates/sui-graphql-rpc/src/types/move_type.rs b/crates/sui-graphql-rpc/src/types/move_type.rs index 67a7ee6a73c41..4697739ce9fd6 100644 --- a/crates/sui-graphql-rpc/src/types/move_type.rs +++ b/crates/sui-graphql-rpc/src/types/move_type.rs @@ -259,8 +259,8 @@ impl TryFrom for MoveTypeLayout { TL::Address => Self::Address, TL::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)), - TL::Struct(s) => Self::Struct(s.try_into()?), - TL::Enum(e) => Self::Enum(e.try_into()?), + TL::Struct(s) => Self::Struct((*s).try_into()?), + TL::Enum(e) => Self::Enum((*e).try_into()?), }) } } diff --git a/crates/sui-graphql-rpc/src/types/move_value.rs b/crates/sui-graphql-rpc/src/types/move_value.rs index cc498144ebbe1..f786c74f59c57 100644 --- a/crates/sui-graphql-rpc/src/types/move_value.rs +++ b/crates/sui-graphql-rpc/src/types/move_value.rs @@ -474,13 +474,13 @@ mod tests { macro_rules! struct_layout { ($type:literal { $($name:literal : $layout:expr),* $(,)?}) => { - A::MoveTypeLayout::Struct(S { + A::MoveTypeLayout::Struct(Box::new(S { type_: StructTag::from_str($type).expect("Failed to parse struct"), - fields: vec![$(MoveFieldLayout { + fields: Box::new(vec![$(MoveFieldLayout { name: ident_str!($name).to_owned(), layout: $layout, - }),*] - }) + }),*]) + })) } } diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index b1cfa63796870..ffce172a1f071 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -709,7 +709,7 @@ async fn get_move_struct_layout_map( move_core_types::annotated_value::MoveStructLayout, ), IndexerError, - >((struct_tag, move_struct_layout)) + >((struct_tag, *move_struct_layout)) } }) .collect::>(); diff --git a/crates/sui-indexer/src/models/objects.rs b/crates/sui-indexer/src/models/objects.rs index ed9dfe835158a..31f7bd4b4e49d 100644 --- a/crates/sui-indexer/src/models/objects.rs +++ b/crates/sui-indexer/src/models/objects.rs @@ -314,7 +314,7 @@ impl StoredObject { )), }?; - Ok(ObjectRead::Exists(oref, object, Some(move_struct_layout))) + Ok(ObjectRead::Exists(oref, object, Some(*move_struct_layout))) } pub fn get_object_ref(&self) -> Result { diff --git a/crates/sui-json/src/lib.rs b/crates/sui-json/src/lib.rs index 4782ed9e57116..f012dcbbbc720 100644 --- a/crates/sui-json/src/lib.rs +++ b/crates/sui-json/src/lib.rs @@ -223,7 +223,7 @@ impl SuiJsonValue { with one field of address or u8 vector type" ), }, - MoveTypeLayout::Struct(MoveStructLayout { type_, .. }) if type_ == &ID::type_() => { + MoveTypeLayout::Struct(struct_layout) if struct_layout.type_ == ID::type_() => { Ok(R::MoveValue::Struct(R::MoveStruct(vec![ Self::to_move_value(val, &inner_vec[0].layout.clone())?, ]))) @@ -277,27 +277,26 @@ impl SuiJsonValue { R::MoveValue::U256(convert_string_to_u256(s.as_str())?) } // For ascii and utf8 strings - ( - JsonValue::String(s), - MoveTypeLayout::Struct(MoveStructLayout { type_, fields: _ }), - ) if is_move_string_type(type_) => { + (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout)) + if is_move_string_type(&struct_layout.type_) => + { R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect()) } // For ID - (JsonValue::String(s), MoveTypeLayout::Struct(MoveStructLayout { type_, fields })) - if type_ == &ID::type_() => + (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout)) + if struct_layout.type_ == ID::type_() => { - if fields.len() != 1 { + if struct_layout.fields.len() != 1 { bail!( - "Cannot convert string arg {s} to {type_} which is expected to be a struct with one field" + "Cannot convert string arg {s} to {} which is expected to be a struct with one field", struct_layout.type_ ); }; let addr = SuiAddress::from_str(s)?; R::MoveValue::Address(addr.into()) } - (JsonValue::Object(o), MoveTypeLayout::Struct(MoveStructLayout { fields, .. })) => { + (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => { let mut field_values = vec![]; - for layout in fields { + for layout in struct_layout.fields.iter() { let field = o .get(layout.name.as_str()) .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?; @@ -306,10 +305,8 @@ impl SuiJsonValue { R::MoveValue::Struct(R::MoveStruct(field_values)) } // Unnest fields - (value, MoveTypeLayout::Struct(MoveStructLayout { fields, .. })) - if fields.len() == 1 => - { - Self::to_move_value(value, &fields[0].layout)? + (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => { + Self::to_move_value(value, &struct_layout.fields[0].layout)? } (JsonValue::String(s), MoveTypeLayout::Vector(t)) => { match &**t { @@ -332,8 +329,8 @@ impl SuiJsonValue { }; R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect()) } - MoveTypeLayout::Struct(MoveStructLayout { fields: inner, .. }) => { - Self::handle_inner_struct_layout(inner, val, ty, s)? + MoveTypeLayout::Struct(struct_layout) => { + Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)? } _ => bail!("Cannot convert string arg {s} to {ty}"), } @@ -594,36 +591,36 @@ pub fn primitive_type( if resolved_struct == RESOLVED_ASCII_STR { ( true, - Some(MoveTypeLayout::Struct(MoveStructLayout { + Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: resolved_to_struct(RESOLVED_ASCII_STR), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("bytes").into(), MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - )], - })), + )]), + }))), ) } else if resolved_struct == RESOLVED_UTF8_STR { // both structs structs representing strings have one field - a vector of type u8 ( true, - Some(MoveTypeLayout::Struct(MoveStructLayout { + Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: resolved_to_struct(RESOLVED_UTF8_STR), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("bytes").into(), MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - )], - })), + )]), + }))), ) } else if resolved_struct == RESOLVED_SUI_ID { ( true, - Some(MoveTypeLayout::Struct(MoveStructLayout { + Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: resolved_to_struct(RESOLVED_SUI_ID), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("bytes").into(), MoveTypeLayout::Address, - )], - })), + )]), + }))), ) } else { (false, None) diff --git a/crates/sui-json/src/tests.rs b/crates/sui-json/src/tests.rs index a73e51e8cecfc..3d7b4e26e2667 100644 --- a/crates/sui-json/src/tests.rs +++ b/crates/sui-json/src/tests.rs @@ -618,18 +618,18 @@ fn test_from_str() { fn test_sui_call_arg_string_type() { let arg1 = bcs::to_bytes("Some String").unwrap(); - let string_layout = Some(MoveTypeLayout::Struct(MoveStructLayout { + let string_layout = Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_ASCII_MODULE_NAME.into(), name: STD_ASCII_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout { + fields: Box::new(vec![MoveFieldLayout { name: ident_str!("bytes").into(), layout: MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - }], - })); + }]), + }))); let v = SuiJsonValue::from_bcs_bytes(string_layout.as_ref(), &arg1).unwrap(); assert_eq!(json! {"Some String"}, v.to_json_value()); @@ -639,31 +639,31 @@ fn test_sui_call_arg_string_type() { fn test_sui_call_arg_option_type() { let arg1 = bcs::to_bytes(&Some("Some String")).unwrap(); - let string_layout = MoveTypeLayout::Struct(MoveStructLayout { + let string_layout = MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_ASCII_MODULE_NAME.into(), name: STD_ASCII_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout { + fields: Box::new(vec![MoveFieldLayout { name: ident_str!("bytes").into(), layout: MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - }], - }); + }]), + })); - let option_layout = MoveTypeLayout::Struct(MoveStructLayout { + let option_layout = MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_OPTION_MODULE_NAME.into(), name: STD_OPTION_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout { + fields: Box::new(vec![MoveFieldLayout { name: ident_str!("vec").into(), layout: MoveTypeLayout::Vector(Box::new(string_layout.clone())), - }], - }); + }]), + })); let v = SuiJsonValue::from_bcs_bytes(Some(option_layout).as_ref(), &arg1).unwrap(); @@ -680,7 +680,7 @@ fn test_sui_call_arg_option_type() { #[test] fn test_convert_struct() { - let layout = MoveTypeLayout::Struct(GasCoin::layout()); + let layout = MoveTypeLayout::Struct(Box::new(GasCoin::layout())); let value = json!({"id":"0xf1416fe18c7baa1673187375777a7606708481311cb3548509ec91a5871c6b9a", "balance": "1000000"}); let sui_json = SuiJsonValue::new(value).unwrap(); @@ -702,18 +702,18 @@ fn test_convert_struct() { fn test_convert_string_vec() { let test_vec = vec!["0xbbb", "test_str"]; let bcs = bcs::to_bytes(&test_vec).unwrap(); - let string_layout = MoveTypeLayout::Struct(MoveStructLayout { + let string_layout = MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_ASCII_MODULE_NAME.into(), name: STD_ASCII_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout { + fields: Box::new(vec![MoveFieldLayout { name: ident_str!("bytes").into(), layout: MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - }], - }); + }]), + })); let layout = MoveTypeLayout::Vector(Box::new(string_layout)); @@ -737,31 +737,31 @@ fn test_string_vec_df_name_child_id_eq() { ] }); - let string_layout = MoveTypeLayout::Struct(MoveStructLayout { + let string_layout = MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_ASCII_MODULE_NAME.into(), name: STD_ASCII_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout { + fields: Box::new(vec![MoveFieldLayout { name: ident_str!("bytes").into(), layout: MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - }], - }); + }]), + })); - let layout = MoveTypeLayout::Struct(MoveStructLayout { + let layout = MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_: StructTag { address: MOVE_STDLIB_ADDRESS, module: STD_ASCII_MODULE_NAME.into(), name: STD_ASCII_STRUCT_NAME.into(), type_params: vec![], }, - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( Identifier::from_str("labels").unwrap(), MoveTypeLayout::Vector(Box::new(string_layout)), - )], - }); + )]), + })); let sui_json = SuiJsonValue::new(name).unwrap(); let bcs2 = sui_json.to_bcs_bytes(&layout).unwrap(); diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 7533666822c9e..40ee5134e1318 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1814,6 +1814,7 @@ "max_type_nodes": { "u64": "256" }, + "max_type_to_layout_nodes": null, "max_value_stack_size": { "u64": "1024" }, diff --git a/crates/sui-open-rpc/src/examples.rs b/crates/sui-open-rpc/src/examples.rs index 9c2f8d8ba6805..d7732756a7829 100644 --- a/crates/sui-open-rpc/src/examples.rs +++ b/crates/sui-open-rpc/src/examples.rs @@ -1206,7 +1206,7 @@ impl RpcExampleProvider { }, MoveStructLayout { type_: struct_tag, - fields: Vec::new(), + fields: Box::new(Vec::new()), }, ) .unwrap(), diff --git a/crates/sui-package-resolver/src/lib.rs b/crates/sui-package-resolver/src/lib.rs index a6c1f3d3c0043..ace9d8efc511e 100644 --- a/crates/sui-package-resolver/src/lib.rs +++ b/crates/sui-package-resolver/src/lib.rs @@ -1453,10 +1453,10 @@ impl<'l> ResolutionContext<'l> { } ( - MoveTypeLayout::Struct(MoveStructLayout { + MoveTypeLayout::Struct(Box::new(MoveStructLayout { type_, - fields: resolved_fields, - }), + fields: Box::new(resolved_fields), + })), field_depth + 1, ) } @@ -1481,10 +1481,10 @@ impl<'l> ResolutionContext<'l> { } ( - MoveTypeLayout::Enum(MoveEnumLayout { + MoveTypeLayout::Enum(Box::new(MoveEnumLayout { type_, variants: resolved_variants, - }), + })), field_depth + 1, ) } diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 6b6011142096b..faec97bd8ed79 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -178,6 +178,7 @@ const MAX_PROTOCOL_VERSION: u64 = 59; // Finalize bridge committee on mainnet. // Switch to distributed vote scoring in consensus in devnet // Version 59: Validation of public inputs for Groth16 verification. +// Enable configuration of maximum number of type nodes in a type layout. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -861,6 +862,9 @@ pub struct ProtocolConfig { // entire object, just consulting an ID -> tx digest map obj_access_cost_verify_per_byte: Option, + // Maximal nodes which are allowed when converting to a type layout. + max_type_to_layout_nodes: Option, + /// === Gas version. gas model === /// Gas model version, what code we are using to charge gas @@ -1766,6 +1770,7 @@ impl ProtocolConfig { max_num_transferred_move_object_ids_system_tx: Some(2048 * 16), max_event_emit_size: Some(250 * 1024), max_move_vector_len: Some(256 * 1024), + max_type_to_layout_nodes: None, max_back_edges_per_function: Some(10_000), max_back_edges_per_module: Some(10_000), @@ -2743,7 +2748,9 @@ impl ProtocolConfig { .consensus_distributed_vote_scoring_strategy = true; } } - 59 => {} + 59 => { + cfg.max_type_to_layout_nodes = Some(512); + } // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_59.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_59.snap index e180e29416382..0ee1891b5084b 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_59.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_59.snap @@ -137,6 +137,7 @@ obj_access_cost_read_per_byte: 15 obj_access_cost_mutate_per_byte: 40 obj_access_cost_delete_per_byte: 40 obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 gas_model_version: 8 obj_data_cost_refundable: 100 obj_metadata_cost_non_refundable: 50 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_59.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_59.snap index e180e29416382..0ee1891b5084b 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_59.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_59.snap @@ -137,6 +137,7 @@ obj_access_cost_read_per_byte: 15 obj_access_cost_mutate_per_byte: 40 obj_access_cost_delete_per_byte: 40 obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 gas_model_version: 8 obj_data_cost_refundable: 100 obj_metadata_cost_non_refundable: 50 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_59.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_59.snap index 8f11e5ae9775b..c43e3be6a80dc 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_59.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_59.snap @@ -143,6 +143,7 @@ obj_access_cost_read_per_byte: 15 obj_access_cost_mutate_per_byte: 40 obj_access_cost_delete_per_byte: 40 obj_access_cost_verify_per_byte: 200 +max_type_to_layout_nodes: 512 gas_model_version: 8 obj_data_cost_refundable: 100 obj_metadata_cost_non_refundable: 50 diff --git a/crates/sui-types/src/balance.rs b/crates/sui-types/src/balance.rs index 85c19dbaa8424..0f5094b91af5f 100644 --- a/crates/sui-types/src/balance.rs +++ b/crates/sui-types/src/balance.rs @@ -78,10 +78,10 @@ impl Balance { pub fn layout(type_param: TypeTag) -> MoveStructLayout { MoveStructLayout { type_: Self::type_(type_param), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("value").to_owned(), MoveTypeLayout::U64, - )], + )]), } } } diff --git a/crates/sui-types/src/coin.rs b/crates/sui-types/src/coin.rs index c03a93e47d910..d1f19defc6cc3 100644 --- a/crates/sui-types/src/coin.rs +++ b/crates/sui-types/src/coin.rs @@ -96,16 +96,16 @@ impl Coin { pub fn layout(type_param: TypeTag) -> MoveStructLayout { MoveStructLayout { type_: Self::type_(type_param.clone()), - fields: vec![ + fields: Box::new(vec![ MoveFieldLayout::new( ident_str!("id").to_owned(), - MoveTypeLayout::Struct(UID::layout()), + MoveTypeLayout::Struct(Box::new(UID::layout())), ), MoveFieldLayout::new( ident_str!("balance").to_owned(), - MoveTypeLayout::Struct(Balance::layout(type_param)), + MoveTypeLayout::Struct(Box::new(Balance::layout(type_param))), ), - ], + ]), } } diff --git a/crates/sui-types/src/id.rs b/crates/sui-types/src/id.rs index a4410f1f1c3a3..ffb6314465145 100644 --- a/crates/sui-types/src/id.rs +++ b/crates/sui-types/src/id.rs @@ -61,10 +61,10 @@ impl UID { pub fn layout() -> MoveStructLayout { MoveStructLayout { type_: Self::type_(), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("id").to_owned(), - MoveTypeLayout::Struct(ID::layout()), - )], + MoveTypeLayout::Struct(Box::new(ID::layout())), + )]), } } } @@ -86,10 +86,10 @@ impl ID { pub fn layout() -> MoveStructLayout { MoveStructLayout { type_: Self::type_(), - fields: vec![MoveFieldLayout::new( + fields: Box::new(vec![MoveFieldLayout::new( ident_str!("bytes").to_owned(), MoveTypeLayout::Address, - )], + )]), } } } diff --git a/crates/sui-types/src/layout_resolver.rs b/crates/sui-types/src/layout_resolver.rs index 372e0fd86eb14..cc551ed5aa540 100644 --- a/crates/sui-types/src/layout_resolver.rs +++ b/crates/sui-types/src/layout_resolver.rs @@ -38,7 +38,7 @@ pub fn get_layout_from_struct_tag( pub fn into_struct_layout(layout: A::MoveDatatypeLayout) -> Result { match layout { - A::MoveDatatypeLayout::Struct(s) => Ok(s), + A::MoveDatatypeLayout::Struct(s) => Ok(*s), A::MoveDatatypeLayout::Enum(e) => Err(SuiError::ObjectSerializationError { error: format!("Expected struct layout but got an enum {e:?}"), }), diff --git a/crates/sui-types/src/object.rs b/crates/sui-types/src/object.rs index 97f0f7a7e2c7b..2bc5294cfd904 100644 --- a/crates/sui-types/src/object.rs +++ b/crates/sui-types/src/object.rs @@ -314,7 +314,7 @@ impl MoveObject { } })?; match layout { - MoveTypeLayout::Struct(l) => Ok(l), + MoveTypeLayout::Struct(l) => Ok(*l), _ => unreachable!( "We called build_with_types on Struct type, should get a struct layout" ), diff --git a/crates/sui-types/src/object/balance_traversal.rs b/crates/sui-types/src/object/balance_traversal.rs index 81b055d62caa5..cbbcc49236d74 100644 --- a/crates/sui-types/src/object/balance_traversal.rs +++ b/crates/sui-types/src/object/balance_traversal.rs @@ -255,7 +255,7 @@ mod tests { layout_( &format!("0x2::coin::Coin<{tag}>"), vec![ - ("id", A::MoveTypeLayout::Struct(UID::layout())), + ("id", A::MoveTypeLayout::Struct(Box::new(UID::layout()))), ("balance", bal_t(tag)), ], ) @@ -285,7 +285,10 @@ mod tests { .map(|(name, layout)| A::MoveFieldLayout::new(Identifier::new(name).unwrap(), layout)) .collect(); - A::MoveTypeLayout::Struct(A::MoveStructLayout { type_, fields }) + A::MoveTypeLayout::Struct(Box::new(A::MoveStructLayout { + type_, + fields: Box::new(fields), + })) } /// BCS encode Move value. diff --git a/crates/sui-types/src/object/bounded_visitor.rs b/crates/sui-types/src/object/bounded_visitor.rs index c4f654a01cc94..54efcbccf7407 100644 --- a/crates/sui-types/src/object/bounded_visitor.rs +++ b/crates/sui-types/src/object/bounded_visitor.rs @@ -205,7 +205,7 @@ impl Visitor for BoundedVisitor { let tag = driver.struct_layout().type_.clone().into(); self.debit_type_size(&tag)?; - for field in &driver.struct_layout().fields { + for field in driver.struct_layout().fields.iter() { self.debit(field.name.len())?; } @@ -469,7 +469,10 @@ mod tests { .map(|(name, layout)| A::MoveFieldLayout::new(Identifier::new(name).unwrap(), layout)) .collect(); - A::MoveTypeLayout::Struct(A::MoveStructLayout { type_, fields }) + A::MoveTypeLayout::Struct(Box::new(A::MoveStructLayout { + type_, + fields: Box::new(fields), + })) } /// BCS encode Move value. diff --git a/external-crates/move/crates/move-bytecode-utils/src/layout.rs b/external-crates/move/crates/move-bytecode-utils/src/layout.rs index dde1d9245335f..a4fe222b28d63 100644 --- a/external-crates/move/crates/move-bytecode-utils/src/layout.rs +++ b/external-crates/move/crates/move-bytecode-utils/src/layout.rs @@ -226,8 +226,8 @@ impl<'a, T: GetModule> SerdeLayoutBuilder<'a, T> { (None, Some(enum_def)) => { Container::Enum(Enum::new(declaring_module.borrow(), enum_def).1) } - (Some(_), Some(_)) => panic!("Found both struct and enum with name {}", name), - (None, None) => panic!( + (Some(_), Some(_)) => bail!("Found both struct and enum with name {}", name), + (None, None) => bail!( "Could not find datatype named {} in module {}", name, declaring_module.borrow().name() @@ -287,7 +287,7 @@ impl<'a, T: GetModule> SerdeLayoutBuilder<'a, T> { if old_datatype.clone() != self.generate_serde_container(def, type_arguments, depth)? { - panic!( + bail!( "Name conflict: multiple structs with name {}, but different addresses", type_key ) @@ -587,7 +587,10 @@ impl DatatypeLayoutBuilder { .zip(layouts) .map(|(name, layout)| A::MoveFieldLayout::new(name, layout)) .collect(); - Ok(A::MoveStructLayout { type_, fields }) + Ok(A::MoveStructLayout { + type_, + fields: Box::new(fields), + }) } } } @@ -608,7 +611,7 @@ impl DatatypeLayoutBuilder { module.borrow().find_struct_def_by_name(name), module.borrow().find_enum_def_by_name(name), ) { - (Some(struct_def), None) => Ok(A::MoveDatatypeLayout::Struct( + (Some(struct_def), None) => Ok(A::MoveDatatypeLayout::Struct(Box::new( Self::build_from_struct_definition( module.borrow(), struct_def, @@ -616,8 +619,8 @@ impl DatatypeLayoutBuilder { resolver, depth, )?, - )), - (None, Some(enum_def)) => Ok(A::MoveDatatypeLayout::Enum( + ))), + (None, Some(enum_def)) => Ok(A::MoveDatatypeLayout::Enum(Box::new( Self::build_from_enum_definition( module.borrow(), enum_def, @@ -625,9 +628,9 @@ impl DatatypeLayoutBuilder { resolver, depth, )?, - )), - (Some(_), Some(_)) => panic!("Found both struct and enum with name {}", name), - (None, None) => panic!( + ))), + (Some(_), Some(_)) => bail!("Found both struct and enum with name {}", name), + (None, None) => bail!( "Could not find struct/enum named {} in module {}", name, module.borrow().name() @@ -645,13 +648,13 @@ impl DatatypeLayoutBuilder { check_depth!(depth); if let Some(def) = m.find_struct_def(s) { // declared internally - Ok(A::MoveDatatypeLayout::Struct( + Ok(A::MoveDatatypeLayout::Struct(Box::new( Self::build_from_struct_definition(m, def, type_arguments, resolver, depth)?, - )) + ))) } else if let Some(def) = m.find_enum_def(s) { - Ok(A::MoveDatatypeLayout::Enum( + Ok(A::MoveDatatypeLayout::Enum(Box::new( Self::build_from_enum_definition(m, def, type_arguments, resolver, depth)?, - )) + ))) } else { let handle = m.datatype_handle_at(s); let name = m.identifier_at(handle.name); diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp index f32b667741b9f..27d53946a009e 100644 --- a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp @@ -1,11 +1,15 @@ -processed 4 tasks +processed 5 tasks -task 2, line 34: +task 2, line 43: //# run 0x42::m::x1 return values: |0|{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -task 3, line 36: +task 3, line 45: //# run 0x42::m::x3 +return values: |1|{ 0 } + +task 4, line 47: +//# run 0x42::m::x4 Error: Function execution failed with VMError: { major_status: VERIFICATION_ERROR, sub_status: None, diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move index 97813d424a567..121896afebc9d 100644 --- a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move @@ -21,6 +21,11 @@ public enum X3 { U64(u64), } +public enum X4 { + X2(X3, X3, X3), + U64(u64), +} + entry fun x1(): X1 { X1::Big(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) } @@ -29,8 +34,14 @@ entry fun x3(): X3 { X3::U64(0) } +entry fun x4(): X4 { + X4::U64(0) +} + } //# run 0x42::m::x1 //# run 0x42::m::x3 + +//# run 0x42::m::x4 diff --git a/external-crates/move/crates/move-core-types/src/annotated_value.rs b/external-crates/move/crates/move-core-types/src/annotated_value.rs index 602c4ee050408..b3624fc398741 100644 --- a/external-crates/move/crates/move-core-types/src/annotated_value.rs +++ b/external-crates/move/crates/move-core-types/src/annotated_value.rs @@ -87,7 +87,7 @@ impl MoveFieldLayout { pub struct MoveStructLayout { /// An decorated representation with both types and human-readable field names pub type_: StructTag, - pub fields: Vec, + pub fields: Box>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -98,8 +98,8 @@ pub struct MoveEnumLayout { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MoveDatatypeLayout { - Struct(MoveStructLayout), - Enum(MoveEnumLayout), + Struct(Box), + Enum(Box), } impl MoveDatatypeLayout { @@ -126,7 +126,7 @@ pub enum MoveTypeLayout { #[serde(rename(serialize = "vector", deserialize = "vector"))] Vector(Box), #[serde(rename(serialize = "struct", deserialize = "struct"))] - Struct(MoveStructLayout), + Struct(Box), #[serde(rename(serialize = "signer", deserialize = "signer"))] Signer, @@ -138,7 +138,7 @@ pub enum MoveTypeLayout { #[serde(rename(serialize = "u256", deserialize = "u256"))] U256, #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(MoveEnumLayout), + Enum(Box), } impl MoveValue { @@ -298,7 +298,10 @@ impl MoveVariant { impl MoveStructLayout { pub fn new(type_: StructTag, fields: Vec) -> Self { - Self { type_, fields } + Self { + type_, + fields: Box::new(fields), + } } pub fn into_fields(self) -> Vec { @@ -585,7 +588,7 @@ impl fmt::Display for MoveStructLayout { write!(f, "struct ")?; write!(f, "{} ", self.type_)?; let mut map = f.debug_map(); - for field in &self.fields { + for field in &*self.fields { map.entry(&DD(&field.name), &DD(&field.layout)); } map.finish() @@ -633,8 +636,8 @@ impl From<&MoveTypeLayout> for TypeTag { let inner_type = &**v; TypeTag::Vector(Box::new(inner_type.into())) } - MoveTypeLayout::Struct(v) => TypeTag::Struct(Box::new(v.into())), - MoveTypeLayout::Enum(e) => TypeTag::Struct(Box::new(e.into())), + MoveTypeLayout::Struct(v) => TypeTag::Struct(Box::new(v.as_ref().into())), + MoveTypeLayout::Enum(e) => TypeTag::Struct(Box::new(e.as_ref().into())), } } } diff --git a/external-crates/move/crates/move-core-types/src/runtime_value.rs b/external-crates/move/crates/move-core-types/src/runtime_value.rs index ddeec601cd801..2c089b3ed8f80 100644 --- a/external-crates/move/crates/move-core-types/src/runtime_value.rs +++ b/external-crates/move/crates/move-core-types/src/runtime_value.rs @@ -50,15 +50,15 @@ pub enum MoveValue { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveStructLayout(pub Vec); +pub struct MoveStructLayout(pub Box>); #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveEnumLayout(pub Vec>); +pub struct MoveEnumLayout(pub Box>>); #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MoveDatatypeLayout { - Struct(MoveStructLayout), - Enum(MoveEnumLayout), + Struct(Box), + Enum(Box), } impl MoveDatatypeLayout { @@ -86,7 +86,7 @@ pub enum MoveTypeLayout { #[serde(rename(serialize = "vector", deserialize = "vector"))] Vector(Box), #[serde(rename(serialize = "struct", deserialize = "struct"))] - Struct(MoveStructLayout), + Struct(Box), #[serde(rename(serialize = "signer", deserialize = "signer"))] Signer, @@ -98,7 +98,7 @@ pub enum MoveTypeLayout { #[serde(rename(serialize = "u256", deserialize = "u256"))] U256, #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(MoveEnumLayout), + Enum(Box), } impl MoveValue { @@ -192,7 +192,7 @@ impl MoveStruct { type_: type_.clone(), fields: vals .into_iter() - .zip(fields) + .zip(fields.iter()) .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) .collect(), } @@ -228,7 +228,7 @@ impl MoveVariant { tag, fields: fields .into_iter() - .zip(v_layout) + .zip(v_layout.iter()) .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) .collect(), variant_name: v_name.clone(), @@ -246,7 +246,7 @@ impl MoveVariant { impl MoveStructLayout { pub fn new(types: Vec) -> Self { - Self(types) + Self(Box::new(types)) } pub fn fields(&self) -> &[MoveTypeLayout] { @@ -254,7 +254,7 @@ impl MoveStructLayout { } pub fn into_fields(self) -> Vec { - self.0 + *self.0 } } diff --git a/external-crates/move/crates/move-core-types/src/unit_tests/value_test.rs b/external-crates/move/crates/move-core-types/src/unit_tests/value_test.rs index ffa09a0b063e2..ccaaa35e1ad56 100644 --- a/external-crates/move/crates/move-core-types/src/unit_tests/value_test.rs +++ b/external-crates/move/crates/move-core-types/src/unit_tests/value_test.rs @@ -11,6 +11,12 @@ use crate::{ }; use serde_json::json; +#[test] +fn check_layout_size() { + assert_eq!(std::mem::size_of::(), 16); + assert_eq!(std::mem::size_of::(), 16); +} + #[test] fn struct_deserialization() { let struct_type = StructTag { @@ -37,17 +43,21 @@ fn struct_deserialization() { let struct_type_layout = A::MoveStructLayout { type_: struct_type.clone(), - fields: vec![ - A::MoveFieldLayout::new(ident_str!("f").to_owned(), A::MoveTypeLayout::U64), - A::MoveFieldLayout::new(ident_str!("g").to_owned(), A::MoveTypeLayout::Bool), - ] - .into_iter() - .collect(), + fields: Box::new( + vec![ + A::MoveFieldLayout::new(ident_str!("f").to_owned(), A::MoveTypeLayout::U64), + A::MoveFieldLayout::new(ident_str!("g").to_owned(), A::MoveTypeLayout::Bool), + ] + .into_iter() + .collect(), + ), }; - let deser_typed_value = - A::MoveValue::simple_deserialize(&ser, &A::MoveTypeLayout::Struct(struct_type_layout)) - .unwrap(); + let deser_typed_value = A::MoveValue::simple_deserialize( + &ser, + &A::MoveTypeLayout::Struct(Box::new(struct_type_layout)), + ) + .unwrap(); let typed_value = A::MoveStruct::new(struct_type, field_values); assert_eq!( @@ -96,8 +106,8 @@ fn enum_deserialization() { R::MoveTypeLayout::Bool, R::MoveTypeLayout::U8, ]; - let enum_layout = R::MoveEnumLayout(vec![variant_layout1, variant_layout2]); - R::MoveTypeLayout::Enum(enum_layout) + let enum_layout = R::MoveEnumLayout(Box::new(vec![variant_layout1, variant_layout2])); + R::MoveTypeLayout::Enum(Box::new(enum_layout)) }; // test each deserialization scheme @@ -156,9 +166,11 @@ fn enum_deserialization() { json!([1, [8, false, 0]]) ); - let deser_typed_value = - A::MoveValue::simple_deserialize(&ser, &A::MoveTypeLayout::Enum(enum_type_layout.clone())) - .unwrap(); + let deser_typed_value = A::MoveValue::simple_deserialize( + &ser, + &A::MoveTypeLayout::Enum(Box::new(enum_type_layout.clone())), + ) + .unwrap(); let typed_value = A::MoveVariant { type_: enum_type.clone(), variant_name: ident_str!("Variant1").to_owned(), @@ -182,9 +194,11 @@ fn enum_deserialization() { let ser1 = R::MoveValue::Variant(runtime_value.clone()) .simple_serialize() .unwrap(); - let deser1_typed_value = - A::MoveValue::simple_deserialize(&ser1, &A::MoveTypeLayout::Enum(enum_type_layout)) - .unwrap(); + let deser1_typed_value = A::MoveValue::simple_deserialize( + &ser1, + &A::MoveTypeLayout::Enum(Box::new(enum_type_layout)), + ) + .unwrap(); let typed_value = A::MoveVariant { type_: enum_type, variant_name: ident_str!("Variant2").to_owned(), @@ -220,10 +234,10 @@ fn enum_deserialization_vec_option_runtime_layout_equiv() { let vec_ser = vec_option.simple_serialize().unwrap(); let enum_ser = enum_option.simple_serialize().unwrap(); - let enum_layout = R::MoveTypeLayout::Enum(R::MoveEnumLayout(vec![ + let enum_layout = R::MoveTypeLayout::Enum(Box::new(R::MoveEnumLayout(Box::new(vec![ vec![], vec![R::MoveTypeLayout::U64], - ])); + ])))); let vec_layout = R::MoveTypeLayout::Vector(Box::new(R::MoveTypeLayout::U64)); diff --git a/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs b/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs index 3edbb4e4c20ae..2daee77142270 100644 --- a/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs +++ b/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs @@ -689,7 +689,10 @@ fn struct_layout_(rep: &str, fields: Vec<(&str, MoveTypeLayout)>) -> MoveTypeLay .map(|(name, layout)| MoveFieldLayout::new(Identifier::new(name).unwrap(), layout)) .collect(); - MoveTypeLayout::Struct(MoveStructLayout { type_, fields }) + MoveTypeLayout::Struct(Box::new(MoveStructLayout { + type_, + fields: Box::new(fields), + })) } /// Create a variant value for test purposes. @@ -723,7 +726,7 @@ fn enum_layout_(rep: &str, variants: Vec<(&str, Vec<(&str, MoveTypeLayout)>)>) - }) .collect(); - MoveTypeLayout::Enum(MoveEnumLayout { type_, variants }) + MoveTypeLayout::Enum(Box::new(MoveEnumLayout { type_, variants })) } /// BCS encode Move value. diff --git a/external-crates/move/crates/move-vm-config/src/runtime.rs b/external-crates/move/crates/move-vm-config/src/runtime.rs index d51578a3d208d..a3c763e760ed3 100644 --- a/external-crates/move/crates/move-vm-config/src/runtime.rs +++ b/external-crates/move/crates/move-vm-config/src/runtime.rs @@ -36,6 +36,9 @@ pub struct VMConfig { // Whether value serialization errors when generating type layouts should be rethrown or // converted to a different error. pub rethrow_serialization_type_layout_errors: bool, + /// Maximal nodes which are allowed when converting to layout. This includes the types of + /// fields for struct types. + pub max_type_to_layout_nodes: Option, } impl Default for VMConfig { @@ -50,6 +53,7 @@ impl Default for VMConfig { error_execution_state: true, binary_config: BinaryConfig::with_extraneous_bytes_check(false), rethrow_serialization_type_layout_errors: false, + max_type_to_layout_nodes: Some(512), } } } diff --git a/external-crates/move/crates/move-vm-runtime/src/loader.rs b/external-crates/move/crates/move-vm-runtime/src/loader.rs index dd23015d2ab64..b0c1a24dee64d 100644 --- a/external-crates/move/crates/move-vm-runtime/src/loader.rs +++ b/external-crates/move/crates/move-vm-runtime/src/loader.rs @@ -2353,11 +2353,9 @@ impl TypeCache { /// Maximal depth of a value in terms of type depth. pub const VALUE_DEPTH_MAX: u64 = 128; -/// Maximal nodes which are allowed when converting to layout. This includes the types of +/// [Historical] Maximal nodes which are allowed when converting to layout. This includes the types of /// fields for struct types. -/// Maximal nodes which are allowed when converting to layout. This includes the types of -/// fields for datatypes. -const MAX_TYPE_TO_LAYOUT_NODES: u64 = 256; +const HISTORICAL_MAX_TYPE_TO_LAYOUT_NODES: u64 = 256; /// Maximal nodes which are all allowed when instantiating a generic type. This does not include /// field types of datatypes. @@ -2526,7 +2524,7 @@ impl Loader { .collect::>>()?; variant_layouts.push(field_layouts); } - R::MoveDatatypeLayout::Enum(R::MoveEnumLayout(variant_layouts)) + R::MoveDatatypeLayout::Enum(Box::new(R::MoveEnumLayout(Box::new(variant_layouts)))) } Datatype::Struct(ref sinfo) => { let field_tys = sinfo @@ -2539,7 +2537,7 @@ impl Loader { .map(|ty| self.type_to_type_layout_impl(ty, count, depth + 1)) .collect::>>()?; - R::MoveDatatypeLayout::Struct(R::MoveStructLayout::new(field_layouts)) + R::MoveDatatypeLayout::Struct(Box::new(R::MoveStructLayout::new(field_layouts))) } }; @@ -2564,14 +2562,19 @@ impl Loader { count: &mut u64, depth: u64, ) -> PartialVMResult { - if *count > MAX_TYPE_TO_LAYOUT_NODES { + if *count + > self + .vm_config() + .max_type_to_layout_nodes + .unwrap_or(HISTORICAL_MAX_TYPE_TO_LAYOUT_NODES) + { return Err(PartialVMError::new(StatusCode::TOO_MANY_TYPE_NODES)); } if depth > VALUE_DEPTH_MAX { return Err(PartialVMError::new(StatusCode::VM_MAX_VALUE_DEPTH_REACHED)); } *count += 1; - Ok(match ty { + let ty = match ty { Type::Bool => R::MoveTypeLayout::Bool, Type::U8 => R::MoveTypeLayout::U8, Type::U16 => R::MoveTypeLayout::U16, @@ -2598,7 +2601,8 @@ impl Loader { .with_message(format!("no type layout for {:?}", ty)), ); } - }) + }; + Ok(ty) } fn datatype_gidx_to_fully_annotated_layout( @@ -2651,10 +2655,10 @@ impl Loader { field_layouts, ); } - A::MoveDatatypeLayout::Enum(A::MoveEnumLayout { + A::MoveDatatypeLayout::Enum(Box::new(A::MoveEnumLayout { type_: struct_tag.clone(), variants: variant_layouts, - }) + })) } Datatype::Struct(struct_type) => { if struct_type.fields.len() != struct_type.field_names.len() { @@ -2676,7 +2680,10 @@ impl Loader { Ok(A::MoveFieldLayout::new(n.clone(), l)) }) .collect::>>()?; - A::MoveDatatypeLayout::Struct(A::MoveStructLayout::new(struct_tag, field_layouts)) + A::MoveDatatypeLayout::Struct(Box::new(A::MoveStructLayout::new( + struct_tag, + field_layouts, + ))) } }; @@ -2701,7 +2708,12 @@ impl Loader { count: &mut u64, depth: u64, ) -> PartialVMResult { - if *count > MAX_TYPE_TO_LAYOUT_NODES { + if *count + > self + .vm_config() + .max_type_to_layout_nodes + .unwrap_or(HISTORICAL_MAX_TYPE_TO_LAYOUT_NODES) + { return Err(PartialVMError::new(StatusCode::TOO_MANY_TYPE_NODES)); } if depth > VALUE_DEPTH_MAX { diff --git a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs index aab6897fdc244..19e79444fcf7c 100644 --- a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs +++ b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs @@ -3306,7 +3306,7 @@ impl<'a, 'b> serde::Serialize for AnnotatedValue<'a, 'b, MoveTypeLayout, ValueIm (MoveTypeLayout::Struct(struct_layout), ValueImpl::Container(Container::Struct(r))) => { (AnnotatedValue { - layout: struct_layout, + layout: &**struct_layout, val: &*r.borrow(), }) .serialize(serializer) @@ -3314,7 +3314,7 @@ impl<'a, 'b> serde::Serialize for AnnotatedValue<'a, 'b, MoveTypeLayout, ValueIm (MoveTypeLayout::Enum(enum_layout), ValueImpl::Container(Container::Variant(r))) => { (AnnotatedValue { - layout: enum_layout, + layout: &**enum_layout, val: &*r.borrow(), }) .serialize(serializer) @@ -3483,12 +3483,12 @@ impl<'d> serde::de::DeserializeSeed<'d> for SeedWrapper<&MoveTypeLayout> { L::Signer => AccountAddress::deserialize(deserializer).map(Value::signer), L::Struct(struct_layout) => Ok(SeedWrapper { - layout: struct_layout, + layout: &**struct_layout, } .deserialize(deserializer)?), L::Enum(enum_layout) => Ok(SeedWrapper { - layout: enum_layout, + layout: &**enum_layout, } .deserialize(deserializer)?), @@ -4138,10 +4138,11 @@ impl ValueImpl { (L::Bool, ValueImpl::Bool(x)) => MoveValue::Bool(*x), (L::Address, ValueImpl::Address(x)) => MoveValue::Address(*x), - (L::Enum(MoveEnumLayout(variants)), ValueImpl::Container(Container::Variant(r))) => { + (L::Enum(enum_layout), ValueImpl::Container(Container::Variant(r))) => { + let MoveEnumLayout(variants) = &**enum_layout; let (tag, values) = &*r.borrow(); let tag = *tag; - let field_layouts = &variants[tag as usize]; + let field_layouts = &variants.as_slice()[tag as usize]; let mut fields = vec![]; for (v, field_layout) in values.iter().zip(field_layouts) { fields.push(v.as_move_value(field_layout)); diff --git a/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/loader.rs b/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/loader.rs index 294f7e108cf8a..bb189d673c964 100644 --- a/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/loader.rs +++ b/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/loader.rs @@ -2186,17 +2186,14 @@ impl Loader { Type::Vector(ty) => R::MoveTypeLayout::Vector(Box::new( self.type_to_type_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => R::MoveTypeLayout::Struct(self.struct_gidx_to_type_layout( - *gidx, - &[], - count, - depth, - )?), + Type::Datatype(gidx) => R::MoveTypeLayout::Struct(Box::new( + self.struct_gidx_to_type_layout(*gidx, &[], count, depth)?, + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - R::MoveTypeLayout::Struct( + R::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_type_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( @@ -2289,14 +2286,14 @@ impl Loader { Type::Vector(ty) => A::MoveTypeLayout::Vector(Box::new( self.type_to_fully_annotated_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => A::MoveTypeLayout::Struct( + Type::Datatype(gidx) => A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, &[], count, depth)?, - ), + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - A::MoveTypeLayout::Struct( + A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( diff --git a/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/loader.rs b/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/loader.rs index ee65647069e9e..7744987a8a955 100644 --- a/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/loader.rs +++ b/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/loader.rs @@ -2188,17 +2188,14 @@ impl Loader { Type::Vector(ty) => R::MoveTypeLayout::Vector(Box::new( self.type_to_type_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => R::MoveTypeLayout::Struct(self.struct_gidx_to_type_layout( - *gidx, - &[], - count, - depth, - )?), + Type::Datatype(gidx) => R::MoveTypeLayout::Struct(Box::new( + self.struct_gidx_to_type_layout(*gidx, &[], count, depth)?, + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - R::MoveTypeLayout::Struct( + R::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_type_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( @@ -2291,14 +2288,14 @@ impl Loader { Type::Vector(ty) => A::MoveTypeLayout::Vector(Box::new( self.type_to_fully_annotated_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => A::MoveTypeLayout::Struct( + Type::Datatype(gidx) => A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, &[], count, depth)?, - ), + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - A::MoveTypeLayout::Struct( + A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( diff --git a/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/loader.rs b/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/loader.rs index 3d0f98b4f6247..65117a1c3c426 100644 --- a/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/loader.rs +++ b/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/loader.rs @@ -2157,17 +2157,14 @@ impl Loader { Type::Vector(ty) => R::MoveTypeLayout::Vector(Box::new( self.type_to_type_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => R::MoveTypeLayout::Struct(self.struct_gidx_to_type_layout( - *gidx, - &[], - count, - depth, - )?), + Type::Datatype(gidx) => R::MoveTypeLayout::Struct(Box::new( + self.struct_gidx_to_type_layout(*gidx, &[], count, depth)?, + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - R::MoveTypeLayout::Struct( + R::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_type_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( @@ -2260,14 +2257,14 @@ impl Loader { Type::Vector(ty) => A::MoveTypeLayout::Vector(Box::new( self.type_to_fully_annotated_layout_impl(ty, count, depth + 1)?, )), - Type::Datatype(gidx) => A::MoveTypeLayout::Struct( + Type::Datatype(gidx) => A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, &[], count, depth)?, - ), + )), Type::DatatypeInstantiation(struct_inst) => { let (gidx, ty_args) = &**struct_inst; - A::MoveTypeLayout::Struct( + A::MoveTypeLayout::Struct(Box::new( self.struct_gidx_to_fully_annotated_layout(*gidx, ty_args, count, depth)?, - ) + )) } Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( diff --git a/sui-execution/latest/sui-adapter/src/adapter.rs b/sui-execution/latest/sui-adapter/src/adapter.rs index a752f280a7840..c1856234639c7 100644 --- a/sui-execution/latest/sui-adapter/src/adapter.rs +++ b/sui-execution/latest/sui-adapter/src/adapter.rs @@ -72,6 +72,7 @@ mod checked { binary_config: to_binary_config(protocol_config), rethrow_serialization_type_layout_errors: protocol_config .rethrow_serialization_type_layout_errors(), + max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v0/sui-adapter/src/adapter.rs b/sui-execution/v0/sui-adapter/src/adapter.rs index 1733184068fe6..d95c13b547eb3 100644 --- a/sui-execution/v0/sui-adapter/src/adapter.rs +++ b/sui-execution/v0/sui-adapter/src/adapter.rs @@ -74,6 +74,7 @@ mod checked { binary_config: to_binary_config(protocol_config), rethrow_serialization_type_layout_errors: protocol_config .rethrow_serialization_type_layout_errors(), + max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v1/sui-adapter/src/adapter.rs b/sui-execution/v1/sui-adapter/src/adapter.rs index dfcb584a05e70..8b72d30244853 100644 --- a/sui-execution/v1/sui-adapter/src/adapter.rs +++ b/sui-execution/v1/sui-adapter/src/adapter.rs @@ -74,6 +74,7 @@ mod checked { binary_config: to_binary_config(protocol_config), rethrow_serialization_type_layout_errors: protocol_config .rethrow_serialization_type_layout_errors(), + max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v2/sui-adapter/src/adapter.rs b/sui-execution/v2/sui-adapter/src/adapter.rs index 483f0eca59928..687494b9d57af 100644 --- a/sui-execution/v2/sui-adapter/src/adapter.rs +++ b/sui-execution/v2/sui-adapter/src/adapter.rs @@ -72,6 +72,7 @@ mod checked { binary_config: to_binary_config(protocol_config), rethrow_serialization_type_layout_errors: protocol_config .rethrow_serialization_type_layout_errors(), + max_type_to_layout_nodes: protocol_config.max_type_to_layout_nodes_as_option(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation)