forked from aptos-labs/aptos-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[randomness] Add entry function annotation (aptos-labs#12134)
Adds `#[randomness]` annotation which should be used to mark entry functions which use randomness. For example ``` #[randomness] entry fun foo() { let _ = randomness::u64_integer(); } ``` The attribute has the following semantics: 1. It can only be used for entry functions. Using it on other type of functions is not allowed. 2. It can be used on entry functions even if they don't use randomness. 3. If an entry function doesn't have an annotation, but uses randomness, the randomness call fails at runtime.
- Loading branch information
1 parent
2e8eedc
commit 0c7a0ae
Showing
28 changed files
with
508 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright © Aptos Foundation | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::move_vm_ext::{AptosMoveResolver, SessionExt}; | ||
use aptos_types::transaction::EntryFunction; | ||
use move_binary_format::errors::VMResult; | ||
|
||
/// Returns true if function has an attribute that it uses randomness. | ||
pub(crate) fn has_randomness_attribute( | ||
resolver: &impl AptosMoveResolver, | ||
session: &mut SessionExt, | ||
entry_fn: &EntryFunction, | ||
) -> VMResult<bool> { | ||
let module = session | ||
.get_move_vm() | ||
.load_module(entry_fn.module(), resolver)?; | ||
let metadata = aptos_framework::get_metadata_from_compiled_module(&module); | ||
if let Some(metadata) = metadata { | ||
Ok(metadata | ||
.fun_attributes | ||
.get(entry_fn.function().as_str()) | ||
.map(|attrs| attrs.iter().any(|attr| attr.is_randomness())) | ||
.unwrap_or(false)) | ||
} else { | ||
Ok(false) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
aptos-move/e2e-move-tests/src/tests/randomness.data/invalid_pack_non_entry/Move.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "randomness_invalid_test_non_entry" | ||
version = "0.0.0" | ||
|
||
[dependencies] | ||
AptosFramework = { local = "../../../../../framework/aptos-framework" } | ||
AptosStdlib = { local = "../../../../../framework/aptos-stdlib" } |
6 changes: 6 additions & 0 deletions
6
...move/e2e-move-tests/src/tests/randomness.data/invalid_pack_non_entry/sources/invalid.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module 0x1::invalid { | ||
#[randomness] | ||
public entry fun foo() { | ||
// Do nothing. | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
aptos-move/e2e-move-tests/src/tests/randomness.data/invalid_pack_public_entry/Move.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "randomness_invalid_test_public_entry" | ||
version = "0.0.0" | ||
|
||
[dependencies] | ||
AptosFramework = { local = "../../../../../framework/aptos-framework" } | ||
AptosStdlib = { local = "../../../../../framework/aptos-stdlib" } |
6 changes: 6 additions & 0 deletions
6
...e/e2e-move-tests/src/tests/randomness.data/invalid_pack_public_entry/sources/invalid.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module 0x1::invalid { | ||
#[randomness] | ||
public fun foo() { | ||
// Do nothing. | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
aptos-move/e2e-move-tests/src/tests/randomness.data/pack/Move.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "randomness_test" | ||
version = "0.0.0" | ||
|
||
[dependencies] | ||
AptosFramework = { local = "../../../../../framework/aptos-framework" } | ||
AptosStdlib = { local = "../../../../../framework/aptos-stdlib" } |
21 changes: 21 additions & 0 deletions
21
aptos-move/e2e-move-tests/src/tests/randomness.data/pack/sources/test.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module 0x1::test { | ||
use aptos_framework::randomness; | ||
|
||
entry fun ok_if_not_annotated_and_not_using_randomness() { | ||
// Do nothing. | ||
} | ||
|
||
#[randomness] | ||
entry fun ok_if_annotated_and_not_using_randomness() { | ||
// Do nothing. | ||
} | ||
|
||
#[randomness] | ||
entry fun ok_if_annotated_and_using_randomness() { | ||
let _ = randomness::u64_integer(); | ||
} | ||
|
||
entry fun fail_if_not_annotated_and_using_randomness() { | ||
let _ = randomness::u64_integer(); | ||
} | ||
} |
168 changes: 168 additions & 0 deletions
168
aptos-move/e2e-move-tests/src/tests/randomness_test_and_abort.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright © Aptos Foundation | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::{assert_abort, assert_success, build_package, tests::common, MoveHarness}; | ||
use aptos_framework::BuiltPackage; | ||
use aptos_language_e2e_tests::account::{Account, TransactionBuilder}; | ||
use aptos_types::{ | ||
account_address::AccountAddress, | ||
on_chain_config::OnChainConfig, | ||
randomness::PerBlockRandomness, | ||
transaction::{ExecutionStatus, Script, TransactionStatus}, | ||
}; | ||
use claims::assert_ok; | ||
use move_core_types::{ident_str, language_storage::ModuleId, vm_status::AbortLocation}; | ||
|
||
// Error codes from randomness module. | ||
const E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT: u64 = 1; | ||
|
||
#[test] | ||
fn test_and_abort_defense_is_sound_and_correct() { | ||
let mut h = MoveHarness::new(); | ||
|
||
// These scripts call a public entry function and a public function. The randomness API will reject both calls. | ||
for dir in [ | ||
"randomness_unsafe_public_entry.data/pack", | ||
"randomness_unsafe_public.data/pack", | ||
] { | ||
println!("Testing {dir}"); | ||
// This will redeploy the package, so backwards compatibility must be maintained in these directories. | ||
let (_, package) = | ||
deploy_code(AccountAddress::ONE, dir, &mut h).expect("building package must succeed"); | ||
|
||
let status = run_script(&mut h, &package); | ||
assert_abort!(status, E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT); | ||
} | ||
|
||
// The randomness module is initialized, but the randomness seed is not set. | ||
set_randomness_seed(&mut h); | ||
|
||
// This is a safe call that the randomness API should allow through. | ||
let status = run_entry_func( | ||
&mut h, | ||
"0xa11ce", | ||
"0x1::some_randapp::safe_private_entry_call", | ||
); | ||
assert_success!(status); | ||
|
||
// This is a safe call that the randomness API should allow through. | ||
// (I suppose that, since TXNs with private entry function payloads are okay, increasing the | ||
// visibility to public(friend) should not create any problems.) | ||
let status = run_entry_func( | ||
&mut h, | ||
"0xa11ce", | ||
"0x1::some_randapp::safe_friend_entry_call", | ||
); | ||
assert_success!(status); | ||
} | ||
|
||
#[test] | ||
fn test_only_private_entry_function_can_be_annotated() { | ||
// Make sure building a package fails. | ||
let mut h = MoveHarness::new(); | ||
assert!(deploy_code( | ||
AccountAddress::ONE, | ||
"randomness.data/invalid_pack_non_entry", | ||
&mut h | ||
) | ||
.is_err()); | ||
assert!(deploy_code( | ||
AccountAddress::ONE, | ||
"randomness.data/invalid_pack_public_entry", | ||
&mut h | ||
) | ||
.is_err()); | ||
} | ||
|
||
#[test] | ||
fn test_unbiasable_annotation() { | ||
let mut h = MoveHarness::new(); | ||
deploy_code(AccountAddress::ONE, "randomness.data/pack", &mut h) | ||
.expect("building package must succeed"); | ||
set_randomness_seed(&mut h); | ||
|
||
let should_succeed = [ | ||
"0x1::test::ok_if_not_annotated_and_not_using_randomness", | ||
"0x1::test::ok_if_annotated_and_not_using_randomness", | ||
"0x1::test::ok_if_annotated_and_using_randomness", | ||
]; | ||
for entry_func in should_succeed { | ||
let status = run_entry_func(&mut h, "0xa11ce", entry_func); | ||
assert_success!(status); | ||
} | ||
|
||
// Non-annotated functions which use randomness fail at runtime. | ||
let entry_func = "0x1::test::fail_if_not_annotated_and_using_randomness"; | ||
let status = run_entry_func(&mut h, "0xa11ce", entry_func); | ||
let status = assert_ok!(status.as_kept_status()); | ||
|
||
if let ExecutionStatus::MoveAbort { | ||
location, | ||
code, | ||
info: _, | ||
} = status | ||
{ | ||
assert_eq!( | ||
location, | ||
AbortLocation::Module(ModuleId::new( | ||
AccountAddress::ONE, | ||
ident_str!("randomness").to_owned() | ||
)) | ||
); | ||
assert_eq!(code, E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT); | ||
} else { | ||
unreachable!("Non-annotated entry call function should result in Move abort") | ||
} | ||
} | ||
|
||
fn set_randomness_seed(h: &mut MoveHarness) { | ||
let fx = h.aptos_framework_account(); | ||
let mut pbr = h | ||
.read_resource::<PerBlockRandomness>(fx.address(), PerBlockRandomness::struct_tag()) | ||
.unwrap(); | ||
assert!(pbr.seed.is_none()); | ||
|
||
pbr.seed = Some((0..32).map(|_| 0u8).collect::<Vec<u8>>()); | ||
assert_eq!(pbr.seed.as_ref().unwrap().len(), 32); | ||
h.set_resource(*fx.address(), PerBlockRandomness::struct_tag(), &pbr); | ||
} | ||
|
||
fn deploy_code( | ||
addr: AccountAddress, | ||
code_path: &str, | ||
harness: &mut MoveHarness, | ||
) -> anyhow::Result<(Account, BuiltPackage)> { | ||
let account = harness.new_account_at(addr); | ||
|
||
let package = build_package( | ||
common::test_dir_path(code_path), | ||
aptos_framework::BuildOptions::default(), | ||
)?; | ||
|
||
let txn = harness.create_publish_built_package(&account, &package, |_| {}); | ||
|
||
assert_success!(harness.run(txn)); | ||
Ok((account, package)) | ||
} | ||
|
||
fn run_script(h: &mut MoveHarness, package: &BuiltPackage) -> TransactionStatus { | ||
let alice = h.new_account_at(AccountAddress::from_hex_literal("0xa11ce").unwrap()); | ||
let scripts = package.extract_script_code(); | ||
let code = scripts[0].clone(); | ||
|
||
let txn = TransactionBuilder::new(alice.clone()) | ||
.script(Script::new(code, vec![], vec![])) | ||
.sequence_number(10) | ||
.max_gas_amount(1_000_000) | ||
.gas_unit_price(1) | ||
.sign(); | ||
|
||
h.run(txn) | ||
} | ||
|
||
fn run_entry_func(h: &mut MoveHarness, signer: &str, name: &str) -> TransactionStatus { | ||
let alice = h.new_account_at(AccountAddress::from_hex_literal(signer).unwrap()); | ||
|
||
println!("Running entry function '{name}'"); | ||
h.run_entry_function(&alice, str::parse(name).unwrap(), vec![], vec![]) | ||
} |
Oops, something went wrong.