Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

soroban-rpc: Allow Budget Instruction Leeway to be Configured by Clients #1131

Merged
merged 10 commits into from
Dec 15, 2023
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
# resolution options, using npm release or a gh ref:
#
# option #1, set the version of stellar-sdk based on a npm release version
SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION: 11.0.1
SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION: 11.1.0
# option #2, set the version of stellar-sdk used as a ref to a gh repo if
# a value is set on SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO, it takes
# precedence over any SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION
Expand Down
4 changes: 3 additions & 1 deletion cmd/soroban-cli/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,9 +693,11 @@ soroban config identity fund {address} --helper-url <url>"#
) -> Result<SimulateTransactionResponse, Error> {
tracing::trace!("Simulating:\n{tx:#?}");
let base64_tx = tx.to_xdr_base64(Limits::none())?;
let mut builder = ObjectParams::new();
builder.insert("transaction", base64_tx)?;
let response: SimulateTransactionResponse = self
.client()?
.request("simulateTransaction", rpc_params![base64_tx])
.request("simulateTransaction", builder)
.await?;
tracing::trace!("Simulation response:\n {response:#?}");
match response.error {
Expand Down
19 changes: 16 additions & 3 deletions cmd/soroban-rpc/internal/methods/simulate_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
)

type SimulateTransactionRequest struct {
Transaction string `json:"transaction"`
Transaction string `json:"transaction"`
ResourceConfig *preflight.ResourceConfig `json:"resourceConfig,omitempty"`
}

type SimulateTransactionCost struct {
Expand Down Expand Up @@ -46,7 +47,7 @@ type SimulateTransactionResponse struct {
}

type PreflightGetter interface {
GetPreflight(ctx context.Context, readTx db.LedgerEntryReadTx, bucketListSize uint64, sourceAccount xdr.AccountId, opBody xdr.OperationBody, footprint xdr.LedgerFootprint) (preflight.Preflight, error)
GetPreflight(ctx context.Context, params preflight.PreflightGetterParameters) (preflight.Preflight, error)
}

// NewSimulateTransactionHandler returns a json rpc handler to run preflight simulations
Expand Down Expand Up @@ -113,7 +114,19 @@ func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.Ledge
}
}

result, err := getter.GetPreflight(ctx, readTx, bucketListSize, sourceAccount, op.Body, footprint)
resource_config := preflight.DefaultResourceConfig()
if request.ResourceConfig != nil {
resource_config = *request.ResourceConfig
}
params := preflight.PreflightGetterParameters{
LedgerEntryReadTx: readTx,
BucketListSize: bucketListSize,
SourceAccount: sourceAccount,
OperationBody: op.Body,
Footprint: footprint,
ResourceConfig: resource_config,
}
result, err := getter.GetPreflight(ctx, params)
if err != nil {
return SimulateTransactionResponse{
Error: err.Error(),
Expand Down
17 changes: 9 additions & 8 deletions cmd/soroban-rpc/internal/preflight/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,26 +139,27 @@ func (m *metricsLedgerEntryWrapper) GetLedgerEntries(keys ...xdr.LedgerKey) ([]d
return entries, err
}

func (pwp *PreflightWorkerPool) GetPreflight(ctx context.Context, readTx db.LedgerEntryReadTx, bucketListSize uint64, sourceAccount xdr.AccountId, opBody xdr.OperationBody, footprint xdr.LedgerFootprint) (Preflight, error) {
func (pwp *PreflightWorkerPool) GetPreflight(ctx context.Context, params PreflightGetterParameters) (Preflight, error) {
if pwp.isClosed.Load() {
return Preflight{}, errors.New("preflight worker pool is closed")
}
wrappedTx := metricsLedgerEntryWrapper{
LedgerEntryReadTx: readTx,
LedgerEntryReadTx: params.LedgerEntryReadTx,
}
params := PreflightParameters{
preflightParams := PreflightParameters{
Logger: pwp.logger,
SourceAccount: sourceAccount,
OpBody: opBody,
SourceAccount: params.SourceAccount,
OpBody: params.OperationBody,
NetworkPassphrase: pwp.networkPassphrase,
LedgerEntryReadTx: &wrappedTx,
BucketListSize: bucketListSize,
Footprint: footprint,
BucketListSize: params.BucketListSize,
Footprint: params.Footprint,
ResourceConfig: params.ResourceConfig,
EnableDebug: pwp.enableDebug,
}
resultC := make(chan workerResult)
select {
case pwp.requestChan <- workerRequest{ctx, params, resultC}:
case pwp.requestChan <- workerRequest{ctx, preflightParams, resultC}:
result := <-resultC
if wrappedTx.ledgerEntriesFetched > 0 {
status := "ok"
Expand Down
28 changes: 28 additions & 0 deletions cmd/soroban-rpc/internal/preflight/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type snapshotSourceHandle struct {
logger *log.Entry
}

const (
defaultInstructionLeeway uint64 = 3000000
)

// SnapshotSourceGet takes a LedgerKey XDR in base64 string and returns its matching LedgerEntry XDR in base64 string
// It's used by the Rust preflight code to obtain ledger entries.
//
Expand Down Expand Up @@ -71,6 +75,25 @@ func FreeGoXDR(xdr C.xdr_t) {
C.free(unsafe.Pointer(xdr.xdr))
}

type ResourceConfig struct {
InstructionLeeway uint64 `json:"instructionLeeway"`
}

func DefaultResourceConfig() ResourceConfig {
return ResourceConfig{
InstructionLeeway: defaultInstructionLeeway,
}
}

type PreflightGetterParameters struct {
LedgerEntryReadTx db.LedgerEntryReadTx
BucketListSize uint64
SourceAccount xdr.AccountId
OperationBody xdr.OperationBody
Footprint xdr.LedgerFootprint
ResourceConfig ResourceConfig
}

type PreflightParameters struct {
Logger *log.Entry
SourceAccount xdr.AccountId
Expand All @@ -79,6 +102,7 @@ type PreflightParameters struct {
NetworkPassphrase string
LedgerEntryReadTx db.LedgerEntryReadTx
BucketListSize uint64
ResourceConfig ResourceConfig
EnableDebug bool
}

Expand Down Expand Up @@ -216,12 +240,16 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro

handle := cgo.NewHandle(snapshotSourceHandle{params.LedgerEntryReadTx, params.Logger})
defer handle.Delete()
resourceConfig := C.resource_config_t{
instruction_leeway: C.uint64_t(params.ResourceConfig.InstructionLeeway),
}
res := C.preflight_invoke_hf_op(
C.uintptr_t(handle),
C.uint64_t(params.BucketListSize),
invokeHostFunctionCXDR,
sourceAccountCXDR,
li,
resourceConfig,
C.bool(params.EnableDebug),
)
FreeGoXDR(invokeHostFunctionCXDR)
Expand Down
5 changes: 5 additions & 0 deletions cmd/soroban-rpc/lib/preflight.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ typedef struct xdr_vector_t {
size_t len;
} xdr_vector_t;

typedef struct resource_config_t {
uint64_t instruction_leeway; // Allow this many extra instructions when budgeting
} resource_config_t;

typedef struct preflight_result_t {
char *error; // Error string in case of error, otherwise null
xdr_vector_t auth; // array of SorobanAuthorizationEntries
Expand All @@ -43,6 +47,7 @@ preflight_result_t *preflight_invoke_hf_op(uintptr_t handle, // Go Handle to for
const xdr_t invoke_hf_op, // InvokeHostFunctionOp XDR
const xdr_t source_account, // AccountId XDR
const ledger_info_t ledger_info,
const resource_config_t resource_config,
bool enable_debug);

preflight_result_t *preflight_footprint_ttl_op(uintptr_t handle, // Go Handle to forward to SnapshotSourceGet
Expand Down
16 changes: 12 additions & 4 deletions cmd/soroban-rpc/lib/preflight/src/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,28 @@ use state_ttl::{get_restored_ledger_sequence, TTLLedgerEntry};
use std::cmp::max;
use std::convert::{TryFrom, TryInto};

use crate::CResourceConfig;

#[allow(clippy::too_many_arguments)]
pub(crate) fn compute_host_function_transaction_data_and_min_fee(
op: &InvokeHostFunctionOp,
pre_storage: &LedgerStorage,
post_storage: &Storage,
budget: &Budget,
resource_config: CResourceConfig,
events: &[DiagnosticEvent],
invocation_result: &ScVal,
bucket_list_size: u64,
current_ledger_seq: u32,
) -> Result<(SorobanTransactionData, i64)> {
let ledger_changes = get_ledger_changes(budget, post_storage, pre_storage, TtlEntryMap::new())?;
let soroban_resources =
calculate_host_function_soroban_resources(&ledger_changes, &post_storage.footprint, budget)
.context("cannot compute host function resources")?;
let soroban_resources = calculate_host_function_soroban_resources(
&ledger_changes,
&post_storage.footprint,
budget,
resource_config,
)
.context("cannot compute host function resources")?;

let contract_events_size =
calculate_contract_events_size_bytes(events).context("cannot calculate events size")?;
Expand Down Expand Up @@ -128,6 +135,7 @@ fn calculate_host_function_soroban_resources(
ledger_changes: &[LedgerEntryChange],
footprint: &Footprint,
budget: &Budget,
resource_config: CResourceConfig,
) -> Result<SorobanResources> {
let ledger_footprint = storage_footprint_to_ledger_footprint(footprint)
.context("cannot convert storage footprint to ledger footprint")?;
Expand All @@ -143,7 +151,7 @@ fn calculate_host_function_soroban_resources(
.get_cpu_insns_consumed()
.context("cannot get instructions consumed")?;
let instructions = max(
budget_instructions + 3000000,
budget_instructions + resource_config.instruction_leeway,
budget_instructions * 120 / 100,
);
Ok(SorobanResources {
Expand Down
10 changes: 10 additions & 0 deletions cmd/soroban-rpc/lib/preflight/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ fn get_default_c_xdr_vector() -> CXDRVector {
}
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct CResourceConfig {
pub instruction_leeway: u64,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct CPreflightResult {
Expand Down Expand Up @@ -133,6 +139,7 @@ pub extern "C" fn preflight_invoke_hf_op(
invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64
source_account: CXDR, // AccountId XDR in base64
ledger_info: CLedgerInfo,
resource_config: CResourceConfig,
enable_debug: bool,
) -> *mut CPreflightResult {
catch_preflight_panic(Box::new(move || {
Expand All @@ -142,6 +149,7 @@ pub extern "C" fn preflight_invoke_hf_op(
invoke_hf_op,
source_account,
ledger_info,
resource_config,
enable_debug,
)
}))
Expand All @@ -153,6 +161,7 @@ fn preflight_invoke_hf_op_or_maybe_panic(
invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64
source_account: CXDR, // AccountId XDR in base64
ledger_info: CLedgerInfo,
resource_config: CResourceConfig,
enable_debug: bool,
) -> Result<CPreflightResult> {
let invoke_hf_op =
Expand All @@ -166,6 +175,7 @@ fn preflight_invoke_hf_op_or_maybe_panic(
invoke_hf_op,
source_account,
LedgerInfo::from(ledger_info),
resource_config,
enable_debug,
)?;
Ok(result.into())
Expand Down
4 changes: 4 additions & 0 deletions cmd/soroban-rpc/lib/preflight/src/preflight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use std::convert::{TryFrom, TryInto};
use std::iter::FromIterator;
use std::rc::Rc;

use crate::CResourceConfig;

pub(crate) struct RestorePreamble {
pub(crate) transaction_data: SorobanTransactionData,
pub(crate) min_fee: i64,
Expand All @@ -40,6 +42,7 @@ pub(crate) fn preflight_invoke_hf_op(
invoke_hf_op: InvokeHostFunctionOp,
source_account: AccountId,
ledger_info: LedgerInfo,
resource_config: CResourceConfig,
enable_debug: bool,
) -> Result<PreflightResult> {
let ledger_storage_rc = Rc::new(ledger_storage);
Expand Down Expand Up @@ -117,6 +120,7 @@ pub(crate) fn preflight_invoke_hf_op(
&ledger_storage_rc,
&storage,
&budget,
resource_config,
&diagnostic_events,
&result,
bucket_list_size,
Expand Down
Loading