Skip to content

Commit

Permalink
Cherrypick #12969 (#12970)
Browse files Browse the repository at this point in the history
* [resource-viewer] Add metering to resource viewer

* bump node version
  • Loading branch information
runtian-zhou committed Apr 22, 2024
1 parent 9caaaa3 commit a1cda42
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

11 changes: 8 additions & 3 deletions api/types/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use move_core_types::{
resolver::ModuleResolver,
value::{MoveStructLayout, MoveTypeLayout},
};
use move_resource_viewer::MoveValueAnnotator;
use move_resource_viewer::{Limiter, MoveValueAnnotator};
use serde_json::Value;
use std::{
convert::{TryFrom, TryInto},
Expand Down Expand Up @@ -81,8 +81,13 @@ impl<'a, R: ModuleResolver + ?Sized> MoveConverter<'a, R> {
&self,
data: impl Iterator<Item = (StructTag, &'b [u8])>,
) -> Result<Vec<MoveResource>> {
data.map(|(typ, bytes)| self.try_into_resource(&typ, bytes))
.collect()
let mut limiter = Limiter::default();
data.map(|(typ, bytes)| {
self.inner
.view_resource_with_limit(&typ, bytes, &mut limiter)?
.try_into()
})
.collect()
}

pub fn try_into_resource(&self, typ: &StructTag, bytes: &'_ [u8]) -> Result<MoveResource> {
Expand Down
2 changes: 1 addition & 1 deletion aptos-node/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "aptos-node"
description = "Aptos node"
version = "1.11.0"
version = "1.11.2"

# Workspace inherited keys
authors = { workspace = true }
Expand Down
84 changes: 70 additions & 14 deletions third_party/move/tools/move-resource-viewer/src/fat_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//! Loaded representation for runtime types.

use crate::limit::Limiter;
use move_binary_format::{
errors::{PartialVMError, PartialVMResult},
file_format::AbilitySet,
Expand Down Expand Up @@ -72,7 +73,37 @@ pub(crate) enum FatType {
}

impl FatStructType {
pub fn subst(&self, ty_args: &[FatType]) -> PartialVMResult<FatStructType> {
fn clone_with_limit(&self, limit: &mut Limiter) -> PartialVMResult<Self> {
limit.charge(std::mem::size_of::<AccountAddress>())?;
limit.charge(self.module.as_bytes().len())?;
limit.charge(self.name.as_bytes().len())?;

Ok(Self {
address: self.address,
module: self.module.clone(),
name: self.name.clone(),
abilities: self.abilities,
ty_args: self
.ty_args
.iter()
.map(|ty| ty.clone_with_limit(limit))
.collect::<PartialVMResult<_>>()?,
layout: self
.layout
.iter()
.map(|ty| ty.clone_with_limit(limit))
.collect::<PartialVMResult<_>>()?,
})
}

pub fn subst(
&self,
ty_args: &[FatType],
limiter: &mut Limiter,
) -> PartialVMResult<FatStructType> {
limiter.charge(std::mem::size_of::<AccountAddress>())?;
limiter.charge(self.module.as_bytes().len())?;
limiter.charge(self.name.as_bytes().len())?;
Ok(Self {
address: self.address,
module: self.module.clone(),
Expand All @@ -81,22 +112,27 @@ impl FatStructType {
ty_args: self
.ty_args
.iter()
.map(|ty| ty.subst(ty_args))
.map(|ty| ty.subst(ty_args, limiter))
.collect::<PartialVMResult<_>>()?,
layout: self
.layout
.iter()
.map(|ty| ty.subst(ty_args))
.map(|ty| ty.subst(ty_args, limiter))
.collect::<PartialVMResult<_>>()?,
})
}

pub fn struct_tag(&self) -> PartialVMResult<StructTag> {
pub fn struct_tag(&self, limiter: &mut Limiter) -> PartialVMResult<StructTag> {
let ty_args = self
.ty_args
.iter()
.map(|ty| ty.type_tag())
.map(|ty| ty.type_tag(limiter))
.collect::<PartialVMResult<Vec<_>>>()?;

limiter.charge(std::mem::size_of::<AccountAddress>())?;
limiter.charge(self.module.as_bytes().len())?;
limiter.charge(self.name.as_bytes().len())?;

Ok(StructTag {
address: self.address,
module: self.module.clone(),
Expand All @@ -107,12 +143,32 @@ impl FatStructType {
}

impl FatType {
pub fn subst(&self, ty_args: &[FatType]) -> PartialVMResult<FatType> {
fn clone_with_limit(&self, limit: &mut Limiter) -> PartialVMResult<Self> {
use FatType::*;
Ok(match self {
TyParam(idx) => TyParam(*idx),
Bool => Bool,
U8 => U8,
U16 => U16,
U32 => U32,
U64 => U64,
U128 => U128,
U256 => U256,
Address => Address,
Signer => Signer,
Vector(ty) => Vector(Box::new(ty.clone_with_limit(limit)?)),
Reference(ty) => Reference(Box::new(ty.clone_with_limit(limit)?)),
MutableReference(ty) => MutableReference(Box::new(ty.clone_with_limit(limit)?)),
Struct(struct_ty) => Struct(Box::new(struct_ty.clone_with_limit(limit)?)),
})
}

pub fn subst(&self, ty_args: &[FatType], limit: &mut Limiter) -> PartialVMResult<FatType> {
use FatType::*;

let res = match self {
TyParam(idx) => match ty_args.get(*idx) {
Some(ty) => ty.clone(),
Some(ty) => ty.clone_with_limit(limit)?,
None => {
return Err(
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
Expand All @@ -134,17 +190,17 @@ impl FatType {
U256 => U256,
Address => Address,
Signer => Signer,
Vector(ty) => Vector(Box::new(ty.subst(ty_args)?)),
Reference(ty) => Reference(Box::new(ty.subst(ty_args)?)),
MutableReference(ty) => MutableReference(Box::new(ty.subst(ty_args)?)),
Vector(ty) => Vector(Box::new(ty.subst(ty_args, limit)?)),
Reference(ty) => Reference(Box::new(ty.subst(ty_args, limit)?)),
MutableReference(ty) => MutableReference(Box::new(ty.subst(ty_args, limit)?)),

Struct(struct_ty) => Struct(Box::new(struct_ty.subst(ty_args)?)),
Struct(struct_ty) => Struct(Box::new(struct_ty.subst(ty_args, limit)?)),
};

Ok(res)
}

pub fn type_tag(&self) -> PartialVMResult<TypeTag> {
pub fn type_tag(&self, limit: &mut Limiter) -> PartialVMResult<TypeTag> {
use FatType::*;

let res = match self {
Expand All @@ -157,8 +213,8 @@ impl FatType {
U256 => TypeTag::U256,
Address => TypeTag::Address,
Signer => TypeTag::Signer,
Vector(ty) => TypeTag::Vector(Box::new(ty.type_tag()?)),
Struct(struct_ty) => TypeTag::Struct(Box::new(struct_ty.struct_tag()?)),
Vector(ty) => TypeTag::Vector(Box::new(ty.type_tag(limit)?)),
Struct(struct_ty) => TypeTag::Struct(Box::new(struct_ty.struct_tag(limit)?)),

Reference(_) | MutableReference(_) | TyParam(_) => {
return Err(
Expand Down
55 changes: 42 additions & 13 deletions third_party/move/tools/move-resource-viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
resolver::Resolver,
};
use anyhow::{anyhow, Result};
pub use limit::Limiter;
use move_binary_format::{
errors::{Location, PartialVMError},
file_format::{Ability, AbilitySet},
Expand All @@ -30,6 +31,7 @@ use std::{
};

mod fat_type;
mod limit;
mod module_cache;
mod resolver;

Expand Down Expand Up @@ -119,6 +121,7 @@ impl<'a, T: ModuleResolver + ?Sized> MoveValueAnnotator<'a, T> {
ty_args: &[TypeTag],
args: &[Vec<u8>],
) -> Result<Vec<AnnotatedMoveValue>> {
let mut limit = Limiter::default();
let types: Vec<FatType> = self
.cache
.resolve_function_arguments(module, function)?
Expand Down Expand Up @@ -154,18 +157,29 @@ impl<'a, T: ModuleResolver + ?Sized> MoveValueAnnotator<'a, T> {
.iter()
.enumerate()
.map(|(i, ty)| {
ty.subst(&ty_args)
ty.subst(&ty_args, &mut limit)
.map_err(anyhow::Error::from)
.and_then(|fat_type| self.view_value_by_fat_type(&fat_type, &args[i]))
.and_then(|fat_type| {
self.view_value_by_fat_type(&fat_type, &args[i], &mut limit)
})
})
.collect::<Result<Vec<AnnotatedMoveValue>>>()
}

pub fn view_resource(&self, tag: &StructTag, blob: &[u8]) -> Result<AnnotatedMoveStruct> {
self.view_resource_with_limit(tag, blob, &mut Limiter::default())
}

pub fn view_resource_with_limit(
&self,
tag: &StructTag,
blob: &[u8],
limit: &mut Limiter,
) -> Result<AnnotatedMoveStruct> {
let ty = self.cache.resolve_struct(tag)?;
let struct_def = (&ty).try_into().map_err(into_vm_status)?;
let move_struct = MoveStruct::simple_deserialize(blob, &struct_def)?;
self.annotate_struct(&move_struct, &ty)
self.annotate_struct(&move_struct, &ty, limit)
}

pub fn move_struct_fields(
Expand All @@ -187,28 +201,38 @@ impl<'a, T: ModuleResolver + ?Sized> MoveValueAnnotator<'a, T> {
}

pub fn view_value(&self, ty_tag: &TypeTag, blob: &[u8]) -> Result<AnnotatedMoveValue> {
let ty = self.cache.resolve_type(ty_tag)?;
self.view_value_by_fat_type(&ty, blob)
let mut limit = Limiter::default();
let ty = self.cache.resolve_type_impl(ty_tag, &mut limit)?;
self.view_value_by_fat_type(&ty, blob, &mut limit)
}

fn view_value_by_fat_type(&self, ty: &FatType, blob: &[u8]) -> Result<AnnotatedMoveValue> {
fn view_value_by_fat_type(
&self,
ty: &FatType,
blob: &[u8],
limit: &mut Limiter,
) -> Result<AnnotatedMoveValue> {
let layout = ty.try_into().map_err(into_vm_status)?;
let move_value = MoveValue::simple_deserialize(blob, &layout)?;
self.annotate_value(&move_value, ty)
self.annotate_value(&move_value, ty, limit)
}

fn annotate_struct(
&self,
move_struct: &MoveStruct,
ty: &FatStructType,
limit: &mut Limiter,
) -> Result<AnnotatedMoveStruct> {
let struct_tag = ty
.struct_tag()
.struct_tag(limit)
.map_err(|e| e.finish(Location::Undefined).into_vm_status())?;
let field_names = self.cache.get_field_names(ty)?;
for names in field_names.iter() {
limit.charge(names.as_bytes().len())?;
}
let mut annotated_fields = vec![];
for (ty, v) in ty.layout.iter().zip(move_struct.fields().iter()) {
annotated_fields.push(self.annotate_value(v, ty)?);
annotated_fields.push(self.annotate_value(v, ty, limit)?);
}
Ok(AnnotatedMoveStruct {
abilities: ty.abilities.0,
Expand All @@ -217,7 +241,12 @@ impl<'a, T: ModuleResolver + ?Sized> MoveValueAnnotator<'a, T> {
})
}

fn annotate_value(&self, value: &MoveValue, ty: &FatType) -> Result<AnnotatedMoveValue> {
fn annotate_value(
&self,
value: &MoveValue,
ty: &FatType,
limit: &mut Limiter,
) -> Result<AnnotatedMoveValue> {
Ok(match (value, ty) {
(MoveValue::Bool(b), FatType::Bool) => AnnotatedMoveValue::Bool(*b),
(MoveValue::U8(i), FatType::U8) => AnnotatedMoveValue::U8(*i),
Expand All @@ -237,14 +266,14 @@ impl<'a, T: ModuleResolver + ?Sized> MoveValueAnnotator<'a, T> {
.collect::<Result<_>>()?,
),
_ => AnnotatedMoveValue::Vector(
ty.type_tag().unwrap(),
ty.type_tag(limit).unwrap(),
a.iter()
.map(|v| self.annotate_value(v, ty.as_ref()))
.map(|v| self.annotate_value(v, ty.as_ref(), limit))
.collect::<Result<_>>()?,
),
},
(MoveValue::Struct(s), FatType::Struct(ty)) => {
AnnotatedMoveValue::Struct(self.annotate_struct(s, ty.as_ref())?)
AnnotatedMoveValue::Struct(self.annotate_struct(s, ty.as_ref(), limit)?)
},
(MoveValue::U8(_), _)
| (MoveValue::U64(_), _)
Expand Down
27 changes: 27 additions & 0 deletions third_party/move/tools/move-resource-viewer/src/limit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

use move_binary_format::errors::{PartialVMError, PartialVMResult};
use move_core_types::vm_status::StatusCode;

// Default limit set to 100mb per query.
const DEFAULT_LIMIT: usize = 100_000_000;

pub struct Limiter(usize);

impl Limiter {
pub fn charge(&mut self, cost: usize) -> PartialVMResult<()> {
if self.0 < cost {
return Err(PartialVMError::new(StatusCode::ABORTED)
.with_message("Query exceeds size limit".to_string()));
}
self.0 -= cost;
Ok(())
}
}

impl Default for Limiter {
fn default() -> Self {
Limiter(DEFAULT_LIMIT)
}
}

0 comments on commit a1cda42

Please sign in to comment.