Skip to content

Commit

Permalink
Allow passing the setting for non-root auth to the simulation. (#1503)
Browse files Browse the repository at this point in the history
### What

Allow passing the setting for non-root auth to the simulation.

This is unfortunately a breaking change and thus it needs to be guarded
by unstable-next-api feature.

### Why

This allows for more control over the simulation logic - while the
default is sufficient 99% of the time, there is still 1% of the cases
where non-root auth should be allowed.

### Known limitations

N/A
  • Loading branch information
dmkozh authored Jan 6, 2025
1 parent f6f187d commit 5a28b69
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 37 deletions.
61 changes: 54 additions & 7 deletions soroban-env-host/src/e2e_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,25 @@ fn clear_signature(auth_entry: &mut SorobanAuthorizationEntry) {
}
}

#[cfg(any(test, feature = "recording_mode"))]
#[cfg(not(feature = "unstable-next-api"))]
/// Defines the authorization mode for the `invoke_host_function_in_recording_mode`.
///
/// When `None`, recording authorization with disabled non-root authorization will be used.
/// When `Some()`, enforcing auth will be used with the provided entries.
pub type RecordingInvocationAuthMode = Option<Vec<SorobanAuthorizationEntry>>;

#[cfg(all(any(test, feature = "recording_mode"), feature = "unstable-next-api"))]
/// Defines the authorization mode for the `invoke_host_function_in_recording_mode`.
pub enum RecordingInvocationAuthMode {
/// Use enforcing auth and pass the signed authorization entries to be used.
Enforcing(Vec<SorobanAuthorizationEntry>),
/// Use recording auth and determine whether non-root authorization is
/// disabled (i.e. non-root auth is not allowed when `true` is passed to
/// the enum).
Recording(bool),
}

/// Invokes a host function within a fresh host instance in 'recording' mode.
///
/// The purpose of recording mode is to measure the resources necessary for
Expand All @@ -503,10 +522,11 @@ fn clear_signature(auth_entry: &mut SorobanAuthorizationEntry) {
/// - Footprint - this is based on the ledger entries accessed.
/// - Read/write bytes - this is based on the sizes of ledger entries read
/// from the provided `ledger_snapshot`
/// - Authorization payloads - when the input `auth_entries` is `None`, Host
/// - Authorization mode - when the input `auth_mode` is `None`, Host
/// switches to recording auth mode and fills the recorded data in the output.
/// When `auth_entries` is not `None`, the authorization is performed in
/// enforcing mode and `auth_entries` are passed through to the output.
/// When `auth_mode` is not `None`, the authorization is performed in
/// enforcing mode and entries from `auth_mode` are passed through to the
/// output.
/// - Instructions - this simply measures the instructions measured by the
/// provided `budget`. While this function makes the best effort to emulate
/// the work performed by `invoke_host_function`, the measured value might
Expand All @@ -532,26 +552,40 @@ pub fn invoke_host_function_in_recording_mode(
enable_diagnostics: bool,
host_fn: &HostFunction,
source_account: &AccountId,
auth_entries: Option<Vec<SorobanAuthorizationEntry>>,
auth_mode: RecordingInvocationAuthMode,
ledger_info: LedgerInfo,
ledger_snapshot: Rc<dyn SnapshotSource>,
base_prng_seed: [u8; 32],
diagnostic_events: &mut Vec<DiagnosticEvent>,
) -> Result<InvokeHostFunctionRecordingModeResult, HostError> {
let storage = Storage::with_recording_footprint(ledger_snapshot.clone());
let host = Host::with_storage_and_budget(storage, budget.clone());
let is_recording_auth = auth_entries.is_none();
#[cfg(not(feature = "unstable-next-api"))]
let is_recording_auth = auth_mode.is_none();
#[cfg(feature = "unstable-next-api")]
let is_recording_auth = matches!(auth_mode, RecordingInvocationAuthMode::Recording(_));
let ledger_seq = ledger_info.sequence_number;
let host_function = host.xdr_roundtrip(host_fn)?;
let source_account: AccountId = host.xdr_roundtrip(source_account)?;
host.set_source_account(source_account)?;
host.set_ledger_info(ledger_info)?;
host.set_base_prng_seed(base_prng_seed)?;
if let Some(auth_entries) = &auth_entries {

#[cfg(not(feature = "unstable-next-api"))]
if let Some(auth_entries) = &auth_mode {
host.set_authorization_entries(auth_entries.clone())?;
} else {
host.switch_to_recording_auth(true)?;
}
#[cfg(feature = "unstable-next-api")]
match &auth_mode {
RecordingInvocationAuthMode::Enforcing(auth_entries) => {
host.set_authorization_entries(auth_entries.clone())?;
}
RecordingInvocationAuthMode::Recording(disable_non_root_auth) => {
host.switch_to_recording_auth(*disable_non_root_auth)?;
}
}

if enable_diagnostics {
host.set_diagnostic_level(DiagnosticLevel::Debug)?;
Expand All @@ -564,7 +598,9 @@ pub fn invoke_host_function_in_recording_mode(
contract_events_and_return_value_size = contract_events_and_return_value_size
.saturating_add(encoded_result_sc_val.len() as u32);
}
let mut output_auth = if let Some(auth_entries) = auth_entries {

#[cfg(not(feature = "unstable-next-api"))]
let mut output_auth = if let Some(auth_entries) = auth_mode {
auth_entries
} else {
let recorded_auth = host.get_recorded_auth_payloads()?;
Expand All @@ -573,6 +609,17 @@ pub fn invoke_host_function_in_recording_mode(
.map(|a| a.into_auth_entry_with_emulated_signature())
.collect::<Result<Vec<SorobanAuthorizationEntry>, HostError>>()?
};
#[cfg(feature = "unstable-next-api")]
let mut output_auth = if let RecordingInvocationAuthMode::Enforcing(auth_entries) = auth_mode {
auth_entries
} else {
let recorded_auth = host.get_recorded_auth_payloads()?;
recorded_auth
.into_iter()
.map(|a| a.into_auth_entry_with_emulated_signature())
.collect::<Result<Vec<SorobanAuthorizationEntry>, HostError>>()?
};

let encoded_auth_entries = output_auth
.iter()
.map(|e| host.to_xdr_non_metered(e))
Expand Down
51 changes: 37 additions & 14 deletions soroban-env-host/src/test/e2e_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::builtin_contracts::testutils::AccountContractSigner;
use crate::e2e_invoke::RecordingInvocationAuthMode;
use crate::e2e_testutils::{account_entry, bytes_sc_val, upload_wasm_host_fn};
use crate::testutils::simple_account_sign_fn;
use crate::{
Expand Down Expand Up @@ -191,6 +192,28 @@ impl LedgerEntryChangeHelper {
}
}

// NB: this is a temporary helper function that we should remove and embed
// RecordingInvocationAuthMode into code during the `unstable-next-api` cleanup
// when switching to v23.
fn recording_auth_mode() -> RecordingInvocationAuthMode {
#[cfg(not(feature = "unstable-next-api"))]
return None;
#[cfg(feature = "unstable-next-api")]
return RecordingInvocationAuthMode::Recording(true);
}

// NB: this is a temporary helper function that we should remove and embed
// RecordingInvocationAuthMode into code during the `unstable-next-api` cleanup
// when switching to v23.
fn enforcing_auth_mode(
auth_entries: Vec<SorobanAuthorizationEntry>,
) -> RecordingInvocationAuthMode {
#[cfg(not(feature = "unstable-next-api"))]
return Some(auth_entries);
#[cfg(feature = "unstable-next-api")]
return RecordingInvocationAuthMode::Enforcing(auth_entries);
}

struct InvokeHostFunctionHelperResult {
invoke_result: Result<ScVal, HostError>,
ledger_changes: Vec<LedgerEntryChangeHelper>,
Expand Down Expand Up @@ -294,7 +317,7 @@ fn invoke_host_function_recording_helper(
enable_diagnostics: bool,
host_fn: &HostFunction,
source_account: &AccountId,
auth_entries: Option<Vec<SorobanAuthorizationEntry>>,
auth_mode: RecordingInvocationAuthMode,
ledger_info: &LedgerInfo,
ledger_entries_with_ttl: Vec<(LedgerEntry, Option<u32>)>,
prng_seed: &[u8; 32],
Expand All @@ -311,7 +334,7 @@ fn invoke_host_function_recording_helper(
enable_diagnostics,
host_fn,
source_account,
auth_entries,
auth_mode,
ledger_info.clone(),
snapshot,
*prng_seed,
Expand Down Expand Up @@ -342,7 +365,7 @@ fn invoke_host_function_using_simulation_with_signers(
enable_diagnostics,
host_fn,
source_account,
None,
recording_auth_mode(),
ledger_info,
ledger_entries_with_ttl.clone(),
prng_seed,
Expand All @@ -360,7 +383,7 @@ fn invoke_host_function_using_simulation_with_signers(
enable_diagnostics,
host_fn,
source_account,
Some(signed_auth.clone()),
enforcing_auth_mode(signed_auth.clone()),
ledger_info,
ledger_entries_with_ttl.clone(),
prng_seed,
Expand Down Expand Up @@ -557,7 +580,7 @@ fn test_run_out_of_budget_before_calling_host_in_recording_mode() {
true,
&upload_wasm_host_fn(ADD_I32),
&get_account_id([0; 32]),
None,
recording_auth_mode(),
&default_ledger_info(),
vec![],
&prng_seed(),
Expand Down Expand Up @@ -646,7 +669,7 @@ fn test_wasm_upload_success_in_recording_mode() {
false,
&upload_wasm_host_fn(ADD_I32),
&get_account_id([123; 32]),
None,
recording_auth_mode(),
&ledger_info,
vec![],
&prng_seed(),
Expand Down Expand Up @@ -699,7 +722,7 @@ fn test_wasm_upload_failure_in_recording_mode() {
true,
&upload_wasm_host_fn(&[0_u8; 1000]),
&get_account_id([123; 32]),
None,
recording_auth_mode(),
&ledger_info,
vec![],
&prng_seed(),
Expand Down Expand Up @@ -737,7 +760,7 @@ fn test_unsupported_wasm_upload_failure_in_recording_mode() {
true,
&upload_wasm_host_fn(ADD_F32),
&get_account_id([123; 32]),
None,
recording_auth_mode(),
&ledger_info,
vec![],
&prng_seed(),
Expand Down Expand Up @@ -1149,7 +1172,7 @@ fn test_create_contract_success_in_recording_mode() {
true,
&cd.host_fn,
&cd.deployer,
None,
recording_auth_mode(),
&ledger_info,
vec![(
cd.wasm_entry.clone(),
Expand Down Expand Up @@ -1235,7 +1258,7 @@ fn test_create_contract_success_in_recording_mode_with_custom_account() {
true,
&cd.host_fn,
&cd.deployer,
None,
recording_auth_mode(),
&ledger_info,
vec![
(
Expand Down Expand Up @@ -1351,7 +1374,7 @@ fn test_create_contract_success_in_recording_mode_with_enforced_auth() {
true,
&cd.host_fn,
&cd.deployer,
Some(vec![cd.auth_entry.clone()]),
enforcing_auth_mode(vec![cd.auth_entry.clone()]),
&ledger_info,
vec![(
cd.wasm_entry.clone(),
Expand Down Expand Up @@ -1767,7 +1790,7 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() {
true,
&host_fn,
&cd.deployer,
None,
recording_auth_mode(),
&ledger_info,
vec![
(
Expand Down Expand Up @@ -1842,7 +1865,7 @@ fn test_invoke_contract_with_storage_ops_success_in_recording_mode() {
true,
&extend_host_fn,
&cd.deployer,
None,
recording_auth_mode(),
&ledger_info,
vec![
(
Expand Down Expand Up @@ -1945,7 +1968,7 @@ fn test_invoke_contract_with_storage_ops_success_using_simulation() {
true,
&extend_host_fn,
&cd.deployer,
None,
recording_auth_mode(),
&ledger_info,
vec![
(
Expand Down
5 changes: 3 additions & 2 deletions soroban-simulation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ publish = true

[features]
testutils = ["soroban-env-host/testutils"]
unstable-next-api = ["soroban-env-host/unstable-next-api"]

[dependencies]
anyhow = { version = "1.0.75", features = [] }
thiserror = "1.0.40"
soroban-env-host = { workspace = true, features = ["recording_mode", "unstable-next-api"]}
soroban-env-host = { workspace = true, features = ["recording_mode"]}
static_assertions = "1.1.0"
rand = "0.8.5"

[dev-dependencies]
soroban-env-host = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]}
soroban-env-host = { workspace = true, features = ["recording_mode", "testutils"]}
soroban-test-wasms = { package = "soroban-test-wasms", path = "../soroban-test-wasms" }
pretty_assertions = "1.4"
tap = "1.0.1"
Expand Down
13 changes: 7 additions & 6 deletions soroban-simulation/src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::snapshot_source::{
use anyhow::Result;
use soroban_env_host::{
e2e_invoke::invoke_host_function_in_recording_mode,
e2e_invoke::LedgerEntryChange,
e2e_invoke::{LedgerEntryChange, RecordingInvocationAuthMode},
storage::SnapshotSource,
xdr::{
AccountId, ContractEvent, DiagnosticEvent, HostFunction, InvokeHostFunctionOp, LedgerKey,
Expand Down Expand Up @@ -100,9 +100,10 @@ pub struct RestoreOpSimulationResult {
/// relevant payload parts.
///
/// The operation is defined by the host function itself (`host_fn`)
/// and optionally signed `auth_entries`. In case if `auth_entries` are
/// omitted, the simulation will use recording authorization mode and
/// return non-signed recorded authorization entries.
/// and `auth_mode`. In case if `auth_mode` is `None`, the simulation will
/// use recording authorization mode and return non-signed recorded
/// authorization entries. Otherwise, the signed entries will be used for
/// authorization and authentication enforcement.
///
/// The rest of parameters define the ledger state (`snapshot_source`,
/// `network_config`, `ledger_info`), simulation adjustment
Expand All @@ -122,7 +123,7 @@ pub fn simulate_invoke_host_function_op(
adjustment_config: &SimulationAdjustmentConfig,
ledger_info: &LedgerInfo,
host_fn: HostFunction,
auth_entries: Option<Vec<SorobanAuthorizationEntry>>,
auth_mode: RecordingInvocationAuthMode,
source_account: &AccountId,
base_prng_seed: [u8; 32],
enable_diagnostics: bool,
Expand All @@ -135,7 +136,7 @@ pub fn simulate_invoke_host_function_op(
enable_diagnostics,
&host_fn,
source_account,
auth_entries,
auth_mode,
ledger_info.clone(),
snapshot_source.clone(),
base_prng_seed,
Expand Down
Loading

0 comments on commit 5a28b69

Please sign in to comment.