Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support resource group #4374

Merged
merged 11 commits into from
Jan 23, 2025
4 changes: 2 additions & 2 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2099,8 +2099,8 @@ impl ChainReader for BlockChain {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, &struct_tag)?
}
DataPath::ResourceGroup(_) => {
bail!("ResourceGroup is not supported in get_transaction_proof")
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, &struct_tag)
}
};
Some(statedb.get_with_proof(&state_key)?)
Expand Down
4 changes: 3 additions & 1 deletion cmd/starcoin/src/state/get_proof_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ impl CommandAction for GetProofCommand {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, &struct_tag)?
}
DataPath::ResourceGroup(_) => Err(anyhow::anyhow!("ResourceGroup is not supported."))?,
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, &struct_tag)
}
};

let (proof, result) = if opt.raw {
Expand Down
37 changes: 35 additions & 2 deletions executor/tests/script_function_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use starcoin_vm_types::transaction::{
use starcoin_vm_types::vm_status::KeptVMStatus;
use std::ops::Sub;
use test_helper::executor::{
compile_ir_script, compile_modules_with_address, compile_script, execute_and_apply,
prepare_genesis,
association_execute_should_success, compile_ir_script, compile_modules_with_address,
compile_script, execute_and_apply, prepare_genesis,
};
use test_helper::txn::create_account_txn_sent_as_association;

Expand Down Expand Up @@ -463,3 +463,36 @@ fn test_transaction_arg_verify() -> Result<()> {
);
Ok(())
}

// this test aims to check if resource group works well
#[stest::test]
fn test_object_metadata() -> Result<()> {
let (chain_state, net) = prepare_genesis();

let script = prepare_script(
SyntaxChoice::Source,
r#"
script{
use std::option;
use std::string;
use starcoin_std::debug;
use starcoin_framework::object;
use starcoin_framework::coin;
use starcoin_framework::starcoin_coin::STC;

fun test_metadata(_account: &signer) {
debug::print(&string::utf8(b"test_metadata | entered"));
let metadata = coin::paired_metadata<STC>();
assert!(option::is_some(&metadata), 10000);
let metdata_obj = option::destroy_some(metadata);
assert!(object::is_object(object::object_address(&metdata_obj)), 10001);
debug::print(&string::utf8(b"test_metadata | exited"));
}
}
"#,
)?;
let payload = TransactionPayload::Script(Script::new(script, vec![], vec![]));
let output = association_execute_should_success(&net, &chain_state, payload)?;
assert_eq!(KeptVMStatus::Executed, output.status().status().unwrap());
Ok(())
}
Binary file modified genesis/generated/barnard/genesis
Binary file not shown.
Binary file modified genesis/generated/halley/genesis
Binary file not shown.
Binary file modified genesis/generated/main/genesis
Binary file not shown.
Binary file modified genesis/generated/proxima/genesis
Binary file not shown.
Binary file modified genesis/generated/vega/genesis
Binary file not shown.
19 changes: 14 additions & 5 deletions state/statedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct AccountStateObject {
// refactor AccountStateObject to a readonly object.
code_tree: Mutex<Option<StateTree<ModuleName>>>,
resource_tree: Mutex<StateTree<StructTag>>,
// todo(simon) add resource_group_tree
store: Arc<dyn StateNodeStore>,
}

Expand Down Expand Up @@ -112,7 +113,7 @@ impl AccountStateObject {
.transpose()?
.flatten()),
DataPath::Resource(struct_tag) => self.resource_tree.lock().get(struct_tag),
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => self.resource_tree.lock().get(struct_tag),
}
}

Expand All @@ -131,7 +132,9 @@ impl AccountStateObject {
.transpose()?
.unwrap_or((None, SparseMerkleProof::new(None, vec![])))),
DataPath::Resource(struct_tag) => self.resource_tree.lock().get_with_proof(struct_tag),
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
self.resource_tree.lock().get_with_proof(struct_tag)
}
}
}

Expand All @@ -151,7 +154,9 @@ impl AccountStateObject {
DataPath::Resource(struct_tag) => {
self.resource_tree.lock().put(struct_tag, value);
}
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
self.resource_tree.lock().put(struct_tag, value);
}
}
}

Expand Down Expand Up @@ -617,7 +622,9 @@ impl ChainStateWriter for ChainStateDB {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, struct_tag)?
}
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, struct_tag)
}
}
};
self.apply_write_set(
Expand All @@ -634,7 +641,9 @@ impl ChainStateWriter for ChainStateDB {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, struct_tag)?
}
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, struct_tag)
}
}
};
self.apply_write_set(
Expand Down
8 changes: 6 additions & 2 deletions vm/e2e-tests/src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ impl ChainStateWriter for FakeDataStore {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, struct_tag)?
}
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, struct_tag)
}
}
};
self.set(state_key, value);
Expand All @@ -142,7 +144,9 @@ impl ChainStateWriter for FakeDataStore {
DataPath::Resource(struct_tag) => {
StateKey::resource(&access_path.address, struct_tag)?
}
DataPath::ResourceGroup(_) => unimplemented!(),
DataPath::ResourceGroup(struct_tag) => {
StateKey::resource_group(&access_path.address, struct_tag)
}
}
};
self.remove(&state_key);
Expand Down
8 changes: 6 additions & 2 deletions vm/types/src/access_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ impl DataPath {
pub fn is_resource(&self) -> bool {
matches!(self, Self::Resource(_))
}
pub fn is_resource_group(&self) -> bool {
matches!(self, Self::ResourceGroup(_))
}
// todo(simon): handle ResourceGroup
pub fn as_struct_tag(&self) -> Option<&StructTag> {
match self {
Self::Resource(struct_tag) => Some(struct_tag),
Expand Down Expand Up @@ -365,7 +369,7 @@ impl fmt::Display for DataPath {
write!(f, "{}/{}", storage_index, module_name)
}
DataPath::ResourceGroup(struct_tag) => {
write!(f, "ResourceGroup({})", struct_tag)
write!(f, "{}/{}", storage_index, struct_tag)
}
}
}
Expand All @@ -384,7 +388,7 @@ impl FromStr for AccessPath {
let data_path = match data_type {
DataType::CODE => Self::code_data_path(Identifier::new(parts[2])?),
DataType::RESOURCE => Self::resource_data_path(parse_struct_tag(parts[2])?),
DataType::ResourceGroup => Self::resource_data_path(parse_struct_tag(parts[2])?),
DataType::ResourceGroup => Self::resource_group_data_path(parse_struct_tag(parts[2])?),
};
Ok(Self::new(address, data_path))
}
Expand Down
1 change: 1 addition & 0 deletions vm/types/src/state_store/state_key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl StateKey {
Ok(myself)
}

// todo: remove Result
pub fn resource(address: &AccountAddress, struct_tag: &StructTag) -> Result<Self> {
Ok(Self(REGISTRY.resource(struct_tag, address).get_or_add(
struct_tag,
Expand Down
9 changes: 5 additions & 4 deletions vm/vm-runtime-types/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,15 @@ pub trait TResourceGroupView {
/// the parallel execution setting, as a wrong value will be (later) caught by validation.
/// Thus, R/W conflicts are avoided, as long as the estimates are correct (e.g. updating
/// struct members of a fixed size).
fn resource_group_size(&self, group_key: &Self::GroupKey) -> anyhow::Result<ResourceGroupSize>;
fn resource_group_size(&self, group_key: &Self::GroupKey)
-> PartialVMResult<ResourceGroupSize>;

fn get_resource_from_group(
&self,
group_key: &Self::GroupKey,
resource_tag: &Self::ResourceTag,
maybe_layout: Option<&Self::Layout>,
) -> anyhow::Result<Option<Bytes>>;
) -> PartialVMResult<Option<Bytes>>;

/// Needed for charging storage fees for a resource group write, as that requires knowing
/// the size of the resource group AFTER the changeset of the transaction is applied (while
Expand All @@ -107,7 +108,7 @@ pub trait TResourceGroupView {
&self,
group_key: &Self::GroupKey,
resource_tag: &Self::ResourceTag,
) -> anyhow::Result<usize> {
) -> PartialVMResult<usize> {
Ok(self
.get_resource_from_group(group_key, resource_tag, None)?
.map_or(0, |bytes| bytes.len()))
Expand All @@ -127,7 +128,7 @@ pub trait TResourceGroupView {
&self,
group_key: &Self::GroupKey,
resource_tag: &Self::ResourceTag,
) -> anyhow::Result<bool> {
) -> PartialVMResult<bool> {
self.get_resource_from_group(group_key, resource_tag, None)
.map(|maybe_bytes| maybe_bytes.is_some())
}
Expand Down
54 changes: 34 additions & 20 deletions vm/vm-runtime-types/src/resource_group_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
use crate::resolver::{
size_u32_as_uleb128, ResourceGroupSize, ResourceGroupView, TResourceGroupView, TResourceView,
};
use anyhow::Error;
use bytes::Bytes;
use move_core_types::vm_status::StatusCode;
use move_core_types::{language_storage::StructTag, value::MoveTypeLayout};
use serde::Serialize;
use starcoin_vm_types::errors::{PartialVMError, PartialVMResult};
use starcoin_vm_types::state_store::state_key::StateKey;
use std::{
cell::RefCell,
Expand Down Expand Up @@ -46,20 +47,24 @@ impl GroupSizeKind {
pub fn group_tagged_resource_size<T: Serialize + Clone + Debug>(
tag: &T,
value_byte_len: usize,
) -> anyhow::Result<u64> {
Ok((bcs::serialized_size(&tag)? + value_byte_len + size_u32_as_uleb128(value_byte_len)) as u64)
) -> PartialVMResult<u64> {
Ok((bcs::serialized_size(&tag).map_err(|e| {
PartialVMError::new(StatusCode::VALUE_SERIALIZATION_ERROR).with_message(format!(
"Tag serialization error for tag {:?}: {:?}",
tag, e
))
})? + value_byte_len
+ size_u32_as_uleb128(value_byte_len)) as u64)
}

/// Utility method to compute the size of the group as GroupSizeKind::AsSum.
pub fn group_size_as_sum<T: Serialize + Clone + Debug>(
mut group: impl Iterator<Item = (T, usize)>,
) -> anyhow::Result<ResourceGroupSize> {
let (count, len) = group
.try_fold((0, 0), |(count, len), (tag, value_byte_len)| {
let delta = group_tagged_resource_size(&tag, value_byte_len)?;
Ok((count + 1, len + delta))
})
.map_err(|_: Error| anyhow::Error::msg("Resource group member tag serialization error"))?;
) -> PartialVMResult<ResourceGroupSize> {
let (count, len) = group.try_fold((0, 0), |(count, len), (tag, value_byte_len)| {
let delta = group_tagged_resource_size(&tag, value_byte_len)?;
Ok::<(usize, u64), PartialVMError>((count + 1, len + delta))
})?;

Ok(ResourceGroupSize::Combined {
num_tagged_resources: count,
Expand Down Expand Up @@ -157,18 +162,24 @@ impl<'r> ResourceGroupAdapter<'r> {

// Ensures that the resource group at state_key is cached in self.group_cache. Ok(true)
// means the resource was already cached, while Ok(false) means it just got cached.
fn load_to_cache(&self, group_key: &StateKey) -> anyhow::Result<bool> {
fn load_to_cache(&self, group_key: &StateKey) -> PartialVMResult<bool> {
let already_cached = self.group_cache.borrow().contains_key(group_key);
if already_cached {
return Ok(true);
}

let group_data = self.resource_view.get_resource_bytes(group_key, None)?;
let (group_data, blob_len): (BTreeMap<StructTag, Bytes>, u64) = group_data.map_or_else(
|| Ok::<_, Error>((BTreeMap::new(), 0)),
|| Ok::<_, PartialVMError>((BTreeMap::new(), 0)),
|group_data_blob| {
let group_data = bcs::from_bytes(&group_data_blob)
.map_err(|_| anyhow::Error::msg("Resource group deserialization error"))?;
let group_data = bcs::from_bytes(&group_data_blob).map_err(|e| {
PartialVMError::new(StatusCode::UNEXPECTED_DESERIALIZATION_ERROR).with_message(
format!(
"Failed to deserialize the resource group at {:? }: {:?}",
group_key, e
),
)
})?;
Ok((group_data, group_data_blob.len() as u64))
},
)?;
Expand Down Expand Up @@ -199,7 +210,10 @@ impl TResourceGroupView for ResourceGroupAdapter<'_> {
self.group_size_kind == GroupSizeKind::AsSum
}

fn resource_group_size(&self, group_key: &Self::GroupKey) -> anyhow::Result<ResourceGroupSize> {
fn resource_group_size(
&self,
group_key: &Self::GroupKey,
) -> PartialVMResult<ResourceGroupSize> {
if self.group_size_kind == GroupSizeKind::None {
return Ok(ResourceGroupSize::zero_concrete());
}
Expand All @@ -222,7 +236,7 @@ impl TResourceGroupView for ResourceGroupAdapter<'_> {
group_key: &Self::GroupKey,
resource_tag: &Self::ResourceTag,
maybe_layout: Option<&MoveTypeLayout>,
) -> anyhow::Result<Option<Bytes>> {
) -> PartialVMResult<Option<Bytes>> {
if let Some(group_view) = self.maybe_resource_group_view {
return group_view.get_resource_from_group(group_key, resource_tag, maybe_layout);
}
Expand Down Expand Up @@ -388,7 +402,7 @@ mod tests {
fn resource_group_size(
&self,
group_key: &Self::GroupKey,
) -> anyhow::Result<ResourceGroupSize> {
) -> PartialVMResult<ResourceGroupSize> {
Ok(self
.group
.get(group_key)
Expand All @@ -401,7 +415,7 @@ mod tests {
group_key: &Self::GroupKey,
resource_tag: &Self::ResourceTag,
_maybe_layout: Option<&Self::Layout>,
) -> anyhow::Result<Option<Bytes>> {
) -> PartialVMResult<Option<Bytes>> {
Ok(self
.group
.get(group_key)
Expand All @@ -412,15 +426,15 @@ mod tests {
&self,
_group_key: &Self::GroupKey,
_resource_tag: &Self::ResourceTag,
) -> anyhow::Result<usize> {
) -> PartialVMResult<usize> {
unimplemented!("Currently resolved by ResourceGroupAdapter");
}

fn resource_exists_in_group(
&self,
_group_key: &Self::GroupKey,
_resource_tag: &Self::ResourceTag,
) -> anyhow::Result<bool> {
) -> PartialVMResult<bool> {
unimplemented!("Currently resolved by ResourceGroupAdapter");
}

Expand Down
Loading
Loading