Skip to content

Commit

Permalink
[API] Add tests for view function and txn simulation filters (#11796)
Browse files Browse the repository at this point in the history
  • Loading branch information
banool authored Jan 26, 2024
1 parent c01adcf commit d8127d6
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"message": "Transaction not allowed by simulation filter",
"error_code": "invalid_input",
"vm_error_code": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"message": "Transaction not allowed by simulation filter",
"error_code": "invalid_input",
"vm_error_code": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[["100000"],{"message":"Function 0000000000000000000000000000000000000000000000000000000000000001::coin::decimals is not allowed","error_code":"invalid_input","vm_error_code":null}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"message":"Function 0000000000000000000000000000000000000000000000000000000000000001::coin::balance is not allowed","error_code":"invalid_input","vm_error_code":null},[8]]
64 changes: 64 additions & 0 deletions api/src/tests/transactions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use aptos_crypto::{
use aptos_sdk::types::LocalAccount;
use aptos_types::{
account_address::AccountAddress,
account_config::aptos_test_root_address,
transaction::{
authenticator::{AuthenticationKey, TransactionAuthenticator},
EntryFunction, Script, SignedTransaction,
Expand Down Expand Up @@ -1407,6 +1408,69 @@ async fn test_simulation_failure_error_message() {
.contains("Division by zero"));
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_simulation_filter_deny() {
let mut node_config = NodeConfig::default();

// Blocklist the balance function.
let mut filter = node_config.api.simulation_filter.clone();
filter = filter.add_deny_all();
node_config.api.simulation_filter = filter;

let mut context = new_test_context_with_config(current_function_name!(), node_config);

let admin0 = context.root_account().await;

let resp = context.simulate_transaction(&admin0, json!({
"type": "script_payload",
"code": {
"bytecode": "a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
},
"type_arguments": [],
"arguments": [],
}), 403).await;

context.check_golden_output(resp);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_simulation_filter_allow_sender() {
let mut node_config = NodeConfig::default();

// Allow the root sender only.
let mut filter = node_config.api.simulation_filter.clone();
filter = filter.add_allow_sender(aptos_test_root_address());
filter = filter.add_deny_all();
node_config.api.simulation_filter = filter;

let mut context = new_test_context_with_config(current_function_name!(), node_config);

let admin0 = context.root_account().await;
let other_account = context.create_account().await;

context.simulate_transaction(&admin0, json!({
"type": "script_payload",
"code": {
"bytecode": "a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
},
"type_arguments": [],
"arguments": [],
}), 200).await;

let resp = context.simulate_transaction(&other_account, json!({
"type": "script_payload",
"code": {
"bytecode": "a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
},
"type_arguments": [],
"arguments": [],
}), 403).await;

// It was difficult to prune when using a vec of responses so we just put the
// rejection response in the goldens.
context.check_golden_output(resp);
}

fn gen_string(len: u64) -> String {
let mut rng = thread_rng();
std::iter::repeat(())
Expand Down
103 changes: 93 additions & 10 deletions api/src/tests/view_function.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use super::new_test_context;
use super::{new_test_context, new_test_context_with_config};
use aptos_api_test_context::current_function_name;
use aptos_cached_packages::aptos_stdlib;
use serde_json::json;
use aptos_config::config::{NodeConfig, ViewFilter, ViewFunctionId};
use aptos_types::account_address::AccountAddress;
use serde_json::{json, Value};
use std::str::FromStr;

fn build_coin_balance_request(address: &AccountAddress) -> Value {
json!({
"function":"0x1::coin::balance",
"arguments": vec![address.to_string()],
"type_arguments": vec!["0x1::aptos_coin::AptosCoin"],
})
}

fn build_coin_decimals_request() -> Value {
let arguments: Vec<String> = Vec::new();
json!({
"function":"0x1::coin::decimals",
"arguments": arguments,
"type_arguments": vec!["0x1::aptos_coin::AptosCoin"],
})
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_simple_view() {
Expand All @@ -17,19 +37,82 @@ async fn test_simple_view() {
context.commit_block(&vec![txn1, txn2]).await;

let resp = context
.post(
"/view",
json!({
"function":"0x1::coin::balance",
"arguments": vec![owner.address().to_string()],
"type_arguments": vec!["0x1::aptos_coin::AptosCoin"],
}),
)
.post("/view", build_coin_balance_request(&owner.address()))
.await;

context.check_golden_output_no_prune(resp);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_view_allowlist() {
let mut node_config = NodeConfig::default();

// Allowlist only the balance function.
node_config.api.view_filter = ViewFilter::Allowlist(vec![ViewFunctionId {
address: AccountAddress::from_str("0x1").unwrap(),
module: "coin".to_string(),
function_name: "balance".to_string(),
}]);

let mut context = new_test_context_with_config(current_function_name!(), node_config);

let creator = &mut context.gen_account();
let owner = &mut context.gen_account();
let txn1 = context.mint_user_account(creator).await;
let txn2 = context.account_transfer(creator, owner, 100_000);

context.commit_block(&vec![txn1, txn2]).await;

// See that an allowed function works.
let resp1 = context
.expect_status_code(200)
.post("/view", build_coin_balance_request(&owner.address()))
.await;

// See that a non-allowed function is rejected.
let resp2 = context
.expect_status_code(403)
.post("/view", build_coin_decimals_request())
.await;

context.check_golden_output_no_prune(json!(vec![resp1, resp2]));
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_view_blocklist() {
let mut node_config = NodeConfig::default();

// Blocklist the balance function.
node_config.api.view_filter = ViewFilter::Blocklist(vec![ViewFunctionId {
address: AccountAddress::from_str("0x1").unwrap(),
module: "coin".to_string(),
function_name: "balance".to_string(),
}]);

let mut context = new_test_context_with_config(current_function_name!(), node_config);

let creator = &mut context.gen_account();
let owner = &mut context.gen_account();
let txn1 = context.mint_user_account(creator).await;
let txn2 = context.account_transfer(creator, owner, 100_000);

context.commit_block(&vec![txn1, txn2]).await;

// See that a blocked function is rejected.
let resp1 = context
.expect_status_code(403)
.post("/view", build_coin_balance_request(&owner.address()))
.await;

// See that a non-blocked function is allowed.
let resp2 = context
.expect_status_code(200)
.post("/view", build_coin_decimals_request())
.await;

context.check_golden_output_no_prune(json!(vec![resp1, resp2]));
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_simple_view_invalid() {
let mut context = new_test_context(current_function_name!());
Expand Down

0 comments on commit d8127d6

Please sign in to comment.