Skip to content

Commit

Permalink
Adding combinatorial proptests to test runtime access specifiers.
Browse files Browse the repository at this point in the history
  • Loading branch information
wrwg committed Oct 20, 2023
1 parent 3562af0 commit e530af7
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 8 deletions.
4 changes: 3 additions & 1 deletion crates/aptos-admin-service/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
use crate::server::utils::reply_with_status;
use aptos_config::config::NodeConfig;
use aptos_logger::info;
#[cfg(target_os = "linux")]
use hyper::Method;
use hyper::{
service::{make_service_fn, service_fn},
Body, Method, Request, Response, Server, StatusCode,
Body, Request, Response, Server, StatusCode,
};
use std::{
convert::Infallible,
Expand Down
4 changes: 2 additions & 2 deletions third_party/move/move-binary-format/src/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl CompiledScript {
bytecode_version: Option<u32>,
binary: &mut Vec<u8>,
) -> Result<()> {
let version = bytecode_version.unwrap_or(VERSION_DEFAULT);
let version = bytecode_version.unwrap_or(VERSION_MAX);
validate_version(version)?;
let mut binary_data = BinaryData::from(binary.clone());
let mut ser = ScriptSerializer::new(version);
Expand Down Expand Up @@ -243,7 +243,7 @@ impl CompiledModule {
bytecode_version: Option<u32>,
binary: &mut Vec<u8>,
) -> Result<()> {
let version = bytecode_version.unwrap_or(VERSION_DEFAULT);
let version = bytecode_version.unwrap_or(VERSION_MAX);
validate_version(version)?;
let mut binary_data = BinaryData::from(binary.clone());
let mut ser = ModuleSerializer::new(version);
Expand Down
1 change: 1 addition & 0 deletions third_party/move/move-vm/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ move-binary-format = { path = "../../move-binary-format" }
move-core-types = { path = "../../move-core/types" }

[dev-dependencies]
move-binary-format = { path = "../../move-binary-format", features = ["fuzzing"] }
proptest = "1.0.0"

[features]
Expand Down
2 changes: 2 additions & 0 deletions third_party/move/move-vm/types/src/loaded_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
//! This module contains the loaded definition of code data used in runtime.

pub mod runtime_access_specifier;
#[cfg(test)]
mod runtime_access_specifiers_prop_tests;
pub mod runtime_types;
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -436,7 +435,7 @@ impl AddressSpecifier {
use AddressSpecifier::*;
match (self, other) {
(Any, _) => true,
(_, Any) => true,
(_, Any) => false,
(Literal(a1), Literal(a2)) => a1 == a2,
(_, _) => {
// Eval should be specialized away when subsumes is called.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::loaded_data::{
runtime_access_specifier::{
AccessInstance, AccessSpecifier, AccessSpecifierClause, AddressSpecifier, ResourceSpecifier,
},
runtime_types::{StructIdentifier, Type},
};
use move_binary_format::file_format::AccessKind;
use move_core_types::{
account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId,
};
use proptest::{collection::vec, prelude::*};

proptest! {
#![proptest_config(ProptestConfig{cases: 5000, verbose: 1, ..ProptestConfig::default()})]

/// Test in all combinations the semi-lattice properties of the join and subsumes.
/// Together with testing basic membership, this gives extensive coverage of the operators.
#[test]
fn access_specifier_semi_lattice_properties(
access in access_instance_strategy(),
s1 in access_specifier_strategy(4, 3),
s2 in access_specifier_strategy(4, 3)
) {
//eprint!(".");
if s1.enables(&access) && s2.enables(&access) {
assert!(s1.join(&s2).enables(&access))
} else {
assert!(!s1.join(&s2).enables(&access))
}
if s1.subsumes(&s2).unwrap_or_default() && s2.enables(&access) {
assert!(s1.enables(&access))
}
}

/// Test membership, by constructing all combinations of specifiers derivable from a given
/// instance.
#[test]
fn access_specifier_enables((access, clause) in access_to_matching_specifier_clause(access_instance_strategy()) ) {
//eprint!(".");
let clauses = vec![clause];
let incl = AccessSpecifier::Constraint(clauses.clone(), vec![]);
let incl_excl = AccessSpecifier::Constraint(clauses.clone(), clauses.clone());
let excl = AccessSpecifier::Constraint(vec![], clauses.clone());
assert!(incl.enables(&access));
assert!(incl.join(&incl).enables(&access));
assert!(!incl_excl.enables(&access));
assert!(!excl.enables(&access));
}
}

fn access_instance_strategy() -> impl Strategy<Value = AccessInstance> {
(
any::<AccessKind>(),
struct_id_strategy(),
type_args_strategy(),
address_strategy(),
)
.prop_map(|(kind, resource, instance, address)| AccessInstance {
kind,
resource,
instance,
address,
})
}
fn access_specifier_strategy(
incl_size: usize,
excl_size: usize,
) -> impl Strategy<Value = AccessSpecifier> {
prop_oneof![
Just(AccessSpecifier::Any),
(
vec(access_specifier_clause_strategy(), 0..incl_size),
vec(access_specifier_clause_strategy(), 0..excl_size),
)
.prop_map(|(incls, excls)| AccessSpecifier::Constraint(incls, excls))
]
}

fn access_specifier_clause_strategy() -> impl Strategy<Value = AccessSpecifierClause> {
(
any::<AccessKind>(),
resource_specifier_strategy(),
address_specifier_strategy(),
)
.prop_map(|(kind, resource, address)| AccessSpecifierClause {
kind,
resource,
address,
})
}

fn resource_specifier_strategy() -> impl Strategy<Value = ResourceSpecifier> {
prop_oneof![
Just(ResourceSpecifier::Any),
address_strategy().prop_map(ResourceSpecifier::DeclaredAtAddress),
module_id_strategy().prop_map(ResourceSpecifier::DeclaredInModule),
struct_id_strategy().prop_map(ResourceSpecifier::Resource),
(struct_id_strategy(), type_args_strategy())
.prop_map(|(s, ts)| ResourceSpecifier::ResourceInstantiation(s, ts)),
]
}

fn address_specifier_strategy() -> impl Strategy<Value = AddressSpecifier> {
prop_oneof![
Just(AddressSpecifier::Any),
address_strategy().prop_map(AddressSpecifier::Literal) // Skip Eval as it is not appearing subsumes and join
]
}

fn type_args_strategy() -> impl Strategy<Value = Vec<Type>> {
prop_oneof![
Just(vec![]),
Just(vec![Type::U8]),
Just(vec![Type::U16, Type::U32])
]
}

fn struct_id_strategy() -> impl Strategy<Value = StructIdentifier> {
(module_id_strategy(), identifier_strategy())
.prop_map(|(module, name)| StructIdentifier { module, name })
}

fn module_id_strategy() -> impl Strategy<Value = ModuleId> {
(address_strategy(), identifier_strategy()).prop_map(|(a, i)| ModuleId::new(a, i))
}

fn identifier_strategy() -> impl Strategy<Value = Identifier> {
"[a-b]{1}[c-d]{1}".prop_map(|s| Identifier::new(s).unwrap())
}

fn address_strategy() -> impl Strategy<Value = AccountAddress> {
prop_oneof![
Just(AccountAddress::from_str_strict("0x1").unwrap()),
Just(AccountAddress::from_str_strict("0x2").unwrap()),
Just(AccountAddress::from_str_strict("0x3").unwrap())
]
}

/// Map a strategy of instances to matching access specifier clauses.
fn access_to_matching_specifier_clause(
instances: impl Strategy<Value = AccessInstance>,
) -> impl Strategy<Value = (AccessInstance, AccessSpecifierClause)> {
instances.prop_flat_map(|inst| {
(
Just(inst.kind),
resource_to_matching_specifier(Just((inst.resource.clone(), inst.instance.clone()))),
address_to_matching_specifier(Just(inst.address)),
)
.prop_map(move |(kind, resource, address)| {
(inst.clone(), AccessSpecifierClause {
kind,
resource,
address,
})
})
})
}

/// Map a strategy of resources to a strategy of specifiers which match them.
fn resource_to_matching_specifier(
resources: impl Strategy<Value = (StructIdentifier, Vec<Type>)>,
) -> impl Strategy<Value = ResourceSpecifier> {
resources.prop_flat_map(|(s, ts)| {
prop_oneof![
Just(ResourceSpecifier::Any),
Just(ResourceSpecifier::DeclaredAtAddress(s.module.address)),
Just(ResourceSpecifier::DeclaredInModule(s.module.clone())),
Just(ResourceSpecifier::Resource(s.clone())),
Just(ResourceSpecifier::ResourceInstantiation(s, ts))
]
})
}

/// Map a strategy of addresses to a strategy of specifiers which match them.
fn address_to_matching_specifier(
addresses: impl Strategy<Value = AccountAddress>,
) -> impl Strategy<Value = AddressSpecifier> {
addresses.prop_flat_map(|a| {
prop_oneof![
Just(AddressSpecifier::Any),
Just(AddressSpecifier::Literal(a))
]
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ name = "Package1"
version = "0.0.0"

[addresses]
std = "0x1"
Std = "0x1"
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ name = "Package1"
version = "0.0.0"

[addresses]
std = "0x1"
Std = "0x1"
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ name = "Package1"
version = "0.0.0"

[addresses]
std = "0x1"
Std = "0x1"

0 comments on commit e530af7

Please sign in to comment.