From 8bdbbeb3e49bbdc605ae42bf24f0c2ae28af70b8 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:58:51 -0700 Subject: [PATCH] Set base prng to zero in preflight and invoke (#975) Co-authored-by: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> --- Cargo.lock | 1 + .../test-wasms/hello_world/src/lib.rs | 4 + .../soroban-test/tests/it/contract_sandbox.rs | 26 ++++ .../src/commands/contract/invoke.rs | 1 + .../test/simulate_transaction_test.go | 115 +++++++++++++++++- cmd/soroban-rpc/lib/preflight/Cargo.toml | 2 +- .../lib/preflight/src/preflight.rs | 2 + 7 files changed, 148 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e070e8d9..967dcfa66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1862,6 +1862,7 @@ dependencies = [ "anyhow", "base64 0.21.4", "libc", + "rand 0.8.5", "sha2 0.10.7", "soroban-env-host", "thiserror", diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs index 7bdf62194..1f1964505 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs @@ -42,6 +42,10 @@ impl Contract { count } + pub fn prng_u64_in_range(env: Env, low: u64, high: u64) -> u64 { + env.prng().u64_in_range(low..=high) + } + #[allow(unused_variables)] pub fn multi_word_cmd(env: Env, contract_owner: String) {} /// Logs a string with `hello ` in front. diff --git a/cmd/crates/soroban-test/tests/it/contract_sandbox.rs b/cmd/crates/soroban-test/tests/it/contract_sandbox.rs index 931d5e9e5..df0088b3c 100644 --- a/cmd/crates/soroban-test/tests/it/contract_sandbox.rs +++ b/cmd/crates/soroban-test/tests/it/contract_sandbox.rs @@ -465,3 +465,29 @@ fn build() { .assert() .success(); } + +#[test] +fn invoke_prng_u64_in_range_test() { + let sandbox = TestEnv::default(); + let res = sandbox + .new_assert_cmd("contract") + .arg("deploy") + .arg("--wasm") + .arg(HELLO_WORLD.path()) + .assert() + .success(); + let stdout = String::from_utf8(res.get_output().stdout.clone()).unwrap(); + let id = stdout.trim_end(); + println!("{id}"); + sandbox + .new_assert_cmd("contract") + .arg("invoke") + .arg("--id") + .arg(id) + .arg("--") + .arg("prng_u64_in_range") + .arg("--low=0") + .arg("--high=100") + .assert() + .success(); +} diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 11855b7af..e80c91884 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -382,6 +382,7 @@ impl Cmd { let h = Host::with_storage_and_budget(storage, budget); h.switch_to_recording_auth(true)?; h.set_source_account(source_account)?; + h.set_base_prng_seed(rand::Rng::gen(&mut rand::thread_rng()))?; let mut ledger_info = state.ledger_info(); ledger_info.sequence_number += 1; diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 29198e954..df21d6b65 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -232,9 +232,9 @@ func TestSimulateTransactionSucceeds(t *testing.T) { }, }, }, - Instructions: 5007615, + Instructions: 5733936, ReadBytes: 48, - WriteBytes: 5532, + WriteBytes: 6576, }, RefundableFee: 20056, } @@ -904,3 +904,114 @@ func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, expirationKe } require.True(t, expired) } + +func TestSimulateInvokePrng_u64_in_range(t *testing.T) { + test := NewTest(t) + + ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) + client := jrpc2.NewClient(ch, nil) + + sourceAccount := keypair.Root(StandaloneNetworkPassphrase) + address := sourceAccount.Address() + account := txnbuild.NewSimpleAccount(address, 0) + + helloWorldContract := getHelloWorldContract(t) + + params := preflightTransactionParams(t, client, txnbuild.TransactionParams{ + SourceAccount: &account, + IncrementSequenceNum: true, + Operations: []txnbuild.Operation{ + createInstallContractCodeOperation(account.AccountID, helloWorldContract), + }, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{ + TimeBounds: txnbuild.NewInfiniteTimeout(), + }, + }) + + tx, err := txnbuild.NewTransaction(params) + require.NoError(t, err) + sendSuccessfulTransaction(t, client, sourceAccount, tx) + + params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ + SourceAccount: &account, + IncrementSequenceNum: true, + Operations: []txnbuild.Operation{ + createCreateContractOperation(address, helloWorldContract), + }, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{ + TimeBounds: txnbuild.NewInfiniteTimeout(), + }, + }) + + tx, err = txnbuild.NewTransaction(params) + require.NoError(t, err) + sendSuccessfulTransaction(t, client, sourceAccount, tx) + + contractID := getContractID(t, address, testSalt, StandaloneNetworkPassphrase) + authAddrArg := "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H" + tx, err = txnbuild.NewTransaction(txnbuild.TransactionParams{ + SourceAccount: &account, + IncrementSequenceNum: true, + Operations: []txnbuild.Operation{ + &txnbuild.CreateAccount{ + Destination: authAddrArg, + Amount: "100000", + SourceAccount: address, + }, + }, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{ + TimeBounds: txnbuild.NewInfiniteTimeout(), + }, + }) + require.NoError(t, err) + sendSuccessfulTransaction(t, client, sourceAccount, tx) + low := xdr.Uint64(1500) + high := xdr.Uint64(10000) + params = txnbuild.TransactionParams{ + SourceAccount: &account, + IncrementSequenceNum: false, + Operations: []txnbuild.Operation{ + createInvokeHostOperation( + address, + contractID, + "prng_u64_in_range", + xdr.ScVal{ + Type: xdr.ScValTypeScvU64, + U64: &low, + }, + xdr.ScVal{ + Type: xdr.ScValTypeScvU64, + U64: &high, + }, + ), + }, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{ + TimeBounds: txnbuild.NewInfiniteTimeout(), + }, + } + tx, err = txnbuild.NewTransaction(params) + + require.NoError(t, err) + + txB64, err := tx.Base64() + require.NoError(t, err) + + request := methods.SimulateTransactionRequest{Transaction: txB64} + var response methods.SimulateTransactionResponse + err = client.CallResult(context.Background(), "simulateTransaction", request, &response) + require.NoError(t, err) + require.Empty(t, response.Error) + + // check the result + require.Len(t, response.Results, 1) + var obtainedResult xdr.ScVal + err = xdr.SafeUnmarshalBase64(response.Results[0].XDR, &obtainedResult) + require.NoError(t, err) + require.Equal(t, xdr.ScValTypeScvU64, obtainedResult.Type) + require.LessOrEqual(t, uint64(*obtainedResult.U64), uint64(high)) + require.GreaterOrEqual(t, uint64(*obtainedResult.U64), uint64(low)) +} diff --git a/cmd/soroban-rpc/lib/preflight/Cargo.toml b/cmd/soroban-rpc/lib/preflight/Cargo.toml index 1c9f15fcf..a49cc8a13 100644 --- a/cmd/soroban-rpc/lib/preflight/Cargo.toml +++ b/cmd/soroban-rpc/lib/preflight/Cargo.toml @@ -13,4 +13,4 @@ thiserror = { workspace = true } libc = "0.2.147" sha2 = { workspace = true } soroban-env-host = { workspace = true } - +rand = "0.8.5" diff --git a/cmd/soroban-rpc/lib/preflight/src/preflight.rs b/cmd/soroban-rpc/lib/preflight/src/preflight.rs index d9f7d787f..6071f977f 100644 --- a/cmd/soroban-rpc/lib/preflight/src/preflight.rs +++ b/cmd/soroban-rpc/lib/preflight/src/preflight.rs @@ -52,6 +52,8 @@ pub(crate) fn preflight_invoke_hf_op( .context("cannot set debug diagnostic level")?; host.set_ledger_info(ledger_info.clone()) .context("cannot set ledger info")?; + host.set_base_prng_seed(rand::Rng::gen(&mut rand::thread_rng())) + .context("cannot set base prng seed")?; // We make an assumption here: // - if a transaction doesn't include any soroban authorization entries the client either