diff --git a/sui_core/Cargo.toml b/sui_core/Cargo.toml index bc8f6e30ac3d6..ffe2f3c11eccf 100644 --- a/sui_core/Cargo.toml +++ b/sui_core/Cargo.toml @@ -28,6 +28,7 @@ sui-network = { path = "../network_utils" } sui-types = { path = "../sui_types" } move-binary-format = { git = "https://github.com/diem/move", rev="1e5e36cb1fede8073c8688886a0943bb5bfe40d5" } +move-bytecode-utils = { git = "https://github.com/diem/move", rev="1e5e36cb1fede8073c8688886a0943bb5bfe40d5" } move-core-types = { git = "https://github.com/diem/move", rev="1e5e36cb1fede8073c8688886a0943bb5bfe40d5", features=["address20"] } move-package = { git = "https://github.com/diem/move", rev="1e5e36cb1fede8073c8688886a0943bb5bfe40d5" } move-vm-runtime = { git = "https://github.com/diem/move", rev="1e5e36cb1fede8073c8688886a0943bb5bfe40d5" } diff --git a/sui_core/src/authority.rs b/sui_core/src/authority.rs index cf48e14f631fc..b41bf41538520 100644 --- a/sui_core/src/authority.rs +++ b/sui_core/src/authority.rs @@ -1,6 +1,7 @@ // Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: Apache-2.0 +use move_bytecode_utils::module_cache::ModuleCache; use move_core_types::{ account_address::AccountAddress, language_storage::{ModuleId, StructTag}, @@ -439,8 +440,19 @@ impl AuthorityState { } else { self.get_order_lock(&object.to_object_reference()).await? }; + let layout = match request.request_layout { + Some(format) => { + let resolver = ModuleCache::new(&self); + object.get_layout(format, &resolver)? + } + None => None, + }; - Some(ObjectResponse { object, lock }) + Some(ObjectResponse { + object, + lock, + layout, + }) } Err(e) => return Err(e), _ => None, @@ -630,3 +642,11 @@ impl AuthorityState { Ok(filtered) } } + +impl ModuleResolver for AuthorityState { + type Error = SuiError; + + fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> { + self._database.get_module(module_id) + } +} diff --git a/sui_core/src/authority/authority_store.rs b/sui_core/src/authority/authority_store.rs index 7f9fa8be8f58c..cc3451868aaf1 100644 --- a/sui_core/src/authority/authority_store.rs +++ b/sui_core/src/authority/authority_store.rs @@ -451,3 +451,22 @@ impl AuthorityStore { })) } } + +impl ModuleResolver for AuthorityStore { + type Error = SuiError; + + fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> { + match self.get_object(module_id.address())? { + Some(o) => match &o.data { + Data::Package(c) => Ok(c + .get(module_id.name().as_str()) + .cloned() + .map(|m| m.into_vec())), + _ => Err(SuiError::BadObjectType { + error: "Expected module object".to_string(), + }), + }, + None => Ok(None), + } + } +} diff --git a/sui_core/src/authority_aggregator.rs b/sui_core/src/authority_aggregator.rs index 9ea6b2c6ab407..ee22f84b4afb1 100644 --- a/sui_core/src/authority_aggregator.rs +++ b/sui_core/src/authority_aggregator.rs @@ -427,11 +427,17 @@ where // None if the object was deleted at this authority // // NOTE: here we could also be gathering the locked orders to see if we could make a cert. - let (object_option, signed_order_option) = - if let Some(ObjectResponse { object, lock }) = object_and_lock { - (Some(object), lock) + // TODO: pass along layout_option so the client can store it + let (object_option, signed_order_option, _layout_option) = + if let Some(ObjectResponse { + object, + lock, + layout, + }) = object_and_lock + { + (Some(object), lock, layout) } else { - (None, None) + (None, None, None) }; // Update the map with the information from this authority @@ -1072,6 +1078,8 @@ where let request = ObjectInfoRequest { object_id, request_sequence_number: None, + // TODO: allow caller to specify layout + request_layout: None, }; // For now assume all authorities. Assume they're all honest diff --git a/sui_types/src/messages.rs b/sui_types/src/messages.rs index 2ee0cb3b8397a..ac8f0e3745706 100644 --- a/sui_types/src/messages.rs +++ b/sui_types/src/messages.rs @@ -1,7 +1,7 @@ // Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: Apache-2.0 -use crate::object::{Object, OBJECT_START_VERSION}; +use crate::object::{Object, ObjectFormatOptions, OBJECT_START_VERSION}; use super::{base_types::*, committee::Committee, error::*, event::Event}; @@ -10,7 +10,7 @@ use super::{base_types::*, committee::Committee, error::*, event::Event}; mod messages_tests; use move_binary_format::{access::ModuleAccess, CompiledModule}; -use move_core_types::{identifier::Identifier, language_storage::TypeTag}; +use move_core_types::{identifier::Identifier, language_storage::TypeTag, value::MoveStructLayout}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeSet, HashSet}, @@ -124,6 +124,8 @@ pub struct ObjectInfoRequest { pub object_id: ObjectID, /// The version of the object for which the parent certificate is sought. pub request_sequence_number: Option<SequenceNumber>, + /// If true, the request will return the layout of the object in the given format + pub request_layout: Option<ObjectFormatOptions>, } impl From<ObjectRef> for ObjectInfoRequest { @@ -131,6 +133,7 @@ impl From<ObjectRef> for ObjectInfoRequest { ObjectInfoRequest { object_id: object_ref.0, request_sequence_number: Some(object_ref.1), + request_layout: None, } } } @@ -140,6 +143,7 @@ impl From<ObjectID> for ObjectInfoRequest { ObjectInfoRequest { object_id, request_sequence_number: None, + request_layout: None, } } } @@ -157,6 +161,9 @@ pub struct ObjectResponse { /// Order the object is locked on in this authority. /// None if the object is not currently locked by this authority. pub lock: Option<SignedOrder>, + /// Schema of the Move value inside this object. + /// None if the object is a Move package, or the request did not ask for the layout + pub layout: Option<MoveStructLayout>, } /// This message provides information about the latest object and its lock diff --git a/sui_types/src/object.rs b/sui_types/src/object.rs index b5820fe119e19..3f06c68d4b69e 100644 --- a/sui_types/src/object.rs +++ b/sui_types/src/object.rs @@ -47,7 +47,8 @@ const ID_END_INDEX: usize = AccountAddress::LENGTH; /// Index marking the end of the object's version + the beginning of type-specific data const VERSION_END_INDEX: usize = ID_END_INDEX + 8; -/// Different schemes for converting a Move object to JSON +/// Different schemes for converting a Move value into a structured representation +#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] pub struct ObjectFormatOptions { /// If true, include the type of each object as well as its fields; e.g.: /// `{ "fields": { "f": 20, "g": { "fields" { "h": true }, "type": "0x0::MyModule::MyNestedType" }, "type": "0x0::MyModule::MyType" }` @@ -400,6 +401,20 @@ impl Object { } } + /// Get a `MoveStructLayout` for `self`. + /// The `resolver` value must contain the module that declares `self.type_` and the (transitive) + /// dependencies of `self.type_` in order for this to succeed. Failure will result in an `ObjectSerializationError` + pub fn get_layout( + &self, + format: ObjectFormatOptions, + resolver: &impl GetModule, + ) -> Result<Option<MoveStructLayout>, SuiError> { + match &self.data { + Data::Move(m) => Ok(Some(m.get_layout(format, resolver)?)), + Data::Package(_) => Ok(None), + } + } + /// Convert `self` to the JSON representation dictated by `format`. /// If `self` is a Move value, the `resolver` value must contain the module that declares `self.type_` and the (transitive) /// dependencies of `self.type_` in order for this to succeed. Failure will result in an `ObjectSerializationError` @@ -465,3 +480,11 @@ impl Display for Object { ) } } + +impl Default for ObjectFormatOptions { + fn default() -> Self { + ObjectFormatOptions { + include_types: true, + } + } +} diff --git a/sui_types/src/unit_tests/serialize_tests.rs b/sui_types/src/unit_tests/serialize_tests.rs index 420c3a855b440..b36ee349fbe3e 100644 --- a/sui_types/src/unit_tests/serialize_tests.rs +++ b/sui_types/src/unit_tests/serialize_tests.rs @@ -51,10 +51,12 @@ fn test_info_request() { let req1 = ObjectInfoRequest { object_id: dbg_object_id(0x20), request_sequence_number: None, + request_layout: None, }; let req2 = ObjectInfoRequest { object_id: dbg_object_id(0x20), request_sequence_number: Some(SequenceNumber::from(129)), + request_layout: None, }; let buf1 = serialize_object_info_request(&req1); @@ -244,6 +246,7 @@ fn test_info_response() { object_and_lock: Some(ObjectResponse { object: object.clone(), lock: Some(vote), + layout: None, }), parent_certificate: None, requested_object_reference: Some(object.to_object_reference()),