From 9837a5d21d5e38dc38a516093bfe2cde8e07fff0 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 2 Nov 2023 17:20:12 +0100 Subject: [PATCH 1/9] rpc: preflight: enable debug by default now that there is a debug budget (#1064) --- .github/workflows/soroban-rpc.yml | 2 +- Cargo.lock | 43 ++++++++++--------- Cargo.toml | 10 ++--- cmd/soroban-rpc/internal/config/options.go | 2 +- .../internal/test/docker-compose.yml | 2 +- cmd/soroban-rpc/lib/preflight/Cargo.toml | 2 +- 6 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index 5b1c0b04b..8ec1b35c5 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -112,7 +112,7 @@ jobs: env: SOROBAN_RPC_INTEGRATION_TESTS_ENABLED: true SOROBAN_RPC_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core - PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.14.1-1529.fcbbad4ce.focal + PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.14.1-1547.f2d06fbce.focal steps: - uses: actions/checkout@v3 with: diff --git a/Cargo.lock b/Cargo.lock index 0c6f50d4f..beaf4e359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2452,6 +2452,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "soroban-builtin-sdk-macros" +version = "20.0.0-rc2" +source = "git+https://github.com/stellar/rs-soroban-env?rev=2674d867d7c6aa4212abab05ff30e5804ff1db90#2674d867d7c6aa4212abab05ff30e5804ff1db90" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "soroban-cli" version = "20.0.0-rc4" @@ -2518,7 +2529,7 @@ dependencies = [ [[package]] name = "soroban-env-common" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=91f44778389490ad863d61a8a90ac9875ba6d8fd#91f44778389490ad863d61a8a90ac9875ba6d8fd" +source = "git+https://github.com/stellar/rs-soroban-env?rev=2674d867d7c6aa4212abab05ff30e5804ff1db90#2674d867d7c6aa4212abab05ff30e5804ff1db90" dependencies = [ "arbitrary", "crate-git-revision 0.0.6", @@ -2535,7 +2546,7 @@ dependencies = [ [[package]] name = "soroban-env-guest" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=91f44778389490ad863d61a8a90ac9875ba6d8fd#91f44778389490ad863d61a8a90ac9875ba6d8fd" +source = "git+https://github.com/stellar/rs-soroban-env?rev=2674d867d7c6aa4212abab05ff30e5804ff1db90#2674d867d7c6aa4212abab05ff30e5804ff1db90" dependencies = [ "soroban-env-common", "static_assertions", @@ -2544,9 +2555,10 @@ dependencies = [ [[package]] name = "soroban-env-host" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=91f44778389490ad863d61a8a90ac9875ba6d8fd#91f44778389490ad863d61a8a90ac9875ba6d8fd" +source = "git+https://github.com/stellar/rs-soroban-env?rev=2674d867d7c6aa4212abab05ff30e5804ff1db90#2674d867d7c6aa4212abab05ff30e5804ff1db90" dependencies = [ "backtrace", + "curve25519-dalek 4.1.1", "ed25519-dalek 2.0.0", "getrandom", "k256", @@ -2557,8 +2569,8 @@ dependencies = [ "rand_chacha", "sha2 0.10.7", "sha3", + "soroban-builtin-sdk-macros", "soroban-env-common", - "soroban-native-sdk-macros", "soroban-wasmi", "static_assertions", "stellar-strkey 0.0.7 (git+https://github.com/stellar/rs-stellar-strkey?rev=e6ba45c60c16de28c7522586b80ed0150157df73)", @@ -2567,7 +2579,7 @@ dependencies = [ [[package]] name = "soroban-env-macros" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=91f44778389490ad863d61a8a90ac9875ba6d8fd#91f44778389490ad863d61a8a90ac9875ba6d8fd" +source = "git+https://github.com/stellar/rs-soroban-env?rev=2674d867d7c6aa4212abab05ff30e5804ff1db90#2674d867d7c6aa4212abab05ff30e5804ff1db90" dependencies = [ "itertools 0.10.5", "proc-macro2", @@ -2585,7 +2597,7 @@ version = "20.0.0-rc4" [[package]] name = "soroban-ledger-snapshot" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=729ed3ac5fe8600a3245d5816eadd3c95ab2eb54#729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=fb422beae0d4944dc0e83559a8940b31f5ebd89d#fb422beae0d4944dc0e83559a8940b31f5ebd89d" dependencies = [ "serde", "serde_json", @@ -2594,21 +2606,10 @@ dependencies = [ "thiserror", ] -[[package]] -name = "soroban-native-sdk-macros" -version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-env?rev=91f44778389490ad863d61a8a90ac9875ba6d8fd#91f44778389490ad863d61a8a90ac9875ba6d8fd" -dependencies = [ - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 2.0.37", -] - [[package]] name = "soroban-sdk" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=729ed3ac5fe8600a3245d5816eadd3c95ab2eb54#729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=fb422beae0d4944dc0e83559a8940b31f5ebd89d#fb422beae0d4944dc0e83559a8940b31f5ebd89d" dependencies = [ "arbitrary", "bytes-lit", @@ -2625,7 +2626,7 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=729ed3ac5fe8600a3245d5816eadd3c95ab2eb54#729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=fb422beae0d4944dc0e83559a8940b31f5ebd89d#fb422beae0d4944dc0e83559a8940b31f5ebd89d" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -2644,7 +2645,7 @@ dependencies = [ [[package]] name = "soroban-spec" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=729ed3ac5fe8600a3245d5816eadd3c95ab2eb54#729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=fb422beae0d4944dc0e83559a8940b31f5ebd89d#fb422beae0d4944dc0e83559a8940b31f5ebd89d" dependencies = [ "base64 0.13.1", "stellar-xdr", @@ -2669,7 +2670,7 @@ dependencies = [ [[package]] name = "soroban-spec-rust" version = "20.0.0-rc2" -source = "git+https://github.com/stellar/rs-soroban-sdk?rev=729ed3ac5fe8600a3245d5816eadd3c95ab2eb54#729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=fb422beae0d4944dc0e83559a8940b31f5ebd89d#fb422beae0d4944dc0e83559a8940b31f5ebd89d" dependencies = [ "prettyplease", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 7ccadcac0..a36ec9fd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,18 @@ version = "20.0.0-rc4" [workspace.dependencies.soroban-env-host] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-env" -rev = "91f44778389490ad863d61a8a90ac9875ba6d8fd" +rev = "2674d867d7c6aa4212abab05ff30e5804ff1db90" [workspace.dependencies.soroban-spec] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +rev = "fb422beae0d4944dc0e83559a8940b31f5ebd89d" # path = "../rs-soroban-sdk/soroban-spec" [workspace.dependencies.soroban-spec-rust] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +rev = "fb422beae0d4944dc0e83559a8940b31f5ebd89d" # path = "../rs-soroban-sdk/soroban-spec-rust" [workspace.dependencies.soroban-spec-json] @@ -45,12 +45,12 @@ path = "./cmd/crates/soroban-spec-tools" [workspace.dependencies.soroban-sdk] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +rev = "fb422beae0d4944dc0e83559a8940b31f5ebd89d" [workspace.dependencies.soroban-ledger-snapshot] version = "20.0.0-rc2" git = "https://github.com/stellar/rs-soroban-sdk" -rev = "729ed3ac5fe8600a3245d5816eadd3c95ab2eb54" +rev = "fb422beae0d4944dc0e83559a8940b31f5ebd89d" [workspace.dependencies.soroban-cli] version = "20.0.0-rc4" diff --git a/cmd/soroban-rpc/internal/config/options.go b/cmd/soroban-rpc/internal/config/options.go index 14f2bd79b..c3b4f2eda 100644 --- a/cmd/soroban-rpc/internal/config/options.go +++ b/cmd/soroban-rpc/internal/config/options.go @@ -278,7 +278,7 @@ func (cfg *Config) options() ConfigOptions { Name: "preflight-enable-debug", Usage: "Enable debug information in preflighting (provides more detailed errors). It should not be enabled in production deployments.", ConfigKey: &cfg.PreflightEnableDebug, - DefaultValue: false, + DefaultValue: true, }, { TomlKey: strutils.KebabToConstantCase("request-backlog-global-queue-limit"), diff --git a/cmd/soroban-rpc/internal/test/docker-compose.yml b/cmd/soroban-rpc/internal/test/docker-compose.yml index bac17e52e..3e8af4bfe 100644 --- a/cmd/soroban-rpc/internal/test/docker-compose.yml +++ b/cmd/soroban-rpc/internal/test/docker-compose.yml @@ -15,7 +15,7 @@ services: # Note: Please keep the image pinned to an immutable tag matching the Captive Core version. # This avoids implicit updates which break compatibility between # the Core container and captive core. - image: ${CORE_IMAGE:-2opremio/stellar-core:19.14.1-1529.fcbbad4ce.focal} + image: ${CORE_IMAGE:-stellar/stellar-core:19.14.1-1547.f2d06fbce.focal} depends_on: - core-postgres restart: on-failure diff --git a/cmd/soroban-rpc/lib/preflight/Cargo.toml b/cmd/soroban-rpc/lib/preflight/Cargo.toml index 415221779..6fb710f72 100644 --- a/cmd/soroban-rpc/lib/preflight/Cargo.toml +++ b/cmd/soroban-rpc/lib/preflight/Cargo.toml @@ -12,5 +12,5 @@ base64 = { workspace = true } thiserror = { workspace = true } libc = "0.2.147" sha2 = { workspace = true } -soroban-env-host = { workspace = true } +soroban-env-host = { workspace = true, features = ["recording_auth"]} rand = "0.8.5" From d83a514a127f4f54f18ccd60c8408bb724c46fe6 Mon Sep 17 00:00:00 2001 From: George Date: Fri, 3 Nov 2023 14:59:20 -0700 Subject: [PATCH 2/9] Upgrade E2E tests to work with latest `stellar-sdk` (#1051) --- .github/workflows/e2e.yml | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 09ab2070c..31fc28d1e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest-4-cores env: # the gh tag of system-test repo version to run - SYSTEM_TEST_GIT_REF: 352cc1d4ea96af8cfb2c2b7335c3b51da000f889 + SYSTEM_TEST_GIT_REF: master # the soroban tools source code to compile and run from system test # refers to checked out source of current git hub ref context @@ -32,16 +32,16 @@ jobs: # test runtime environment, tests invoke rustc/cargo SYSTEM_TEST_RUST_TOOLCHAIN_VERSION: stable - # set the version of js-soroban-client to use, need to choose one of either + # set the version of js-stellar-sdk to use, need to choose one of either # resolution options, using npm release or a gh ref: # - # option #1, set the version of soroban-js-client based on a npm release version - SYSTEM_TEST_JS_SOROBAN_CLIENT_NPM_VERSION: 1.0.0-beta.2 - # option #2, set the version of soroban-js-client used as a ref to a gh repo - # if a value is set on SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO, it takes precedence - # over any SYSTEM_TEST_JS_SOROBAN_CLIENT_NPM_VERSION - SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO: - SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REF: + # option #1, set the version of stellar-sdk based on a npm release version + SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION: v11.0.0-beta.6 + # 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 + SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO: + SYSTEM_TEST_JS_STELLAR_SDK_GH_REF: # system test will build quickstart image internally to use for running the service stack # configured in standalone network mode(core, rpc) @@ -64,25 +64,25 @@ jobs: name: checkout soroban-tools with: path: soroban-tools - - if: ${{ env.SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO != ''}} - name: prepare local js-soroban-client + - if: ${{ env.SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO != ''}} + name: prepare local js-stellar-sdk run: | - rm -rf $GITHUB_WORKSPACE/system-test/js-soroban-client; - - if: ${{ env.SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO != ''}} + rm -rf $GITHUB_WORKSPACE/system-test/js-stellar-sdk; + - if: ${{ env.SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO != ''}} uses: actions/checkout@v3 with: - repository: ${{ env.SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO }} - ref: ${{ env.SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REF }} - path: system-test/js-soroban-client + repository: ${{ env.SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO }} + ref: ${{ env.SYSTEM_TEST_JS_STELLAR_SDK_GH_REF }} + path: system-test/js-stellar-sdk - uses: stellar/actions/rust-cache@main - name: Build system test with component versions run: | cd $GITHUB_WORKSPACE/system-test - if [ -z "$SYSTEM_TEST_JS_SOROBAN_CLIENT_GH_REPO" ]; then \ - JS_SOROBAN_CLIENT_REF="$SYSTEM_TEST_JS_SOROBAN_CLIENT_NPM_VERSION"; \ + if [ -z "$SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO" ]; then \ + JS_STELLAR_SDK_REF="$SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION"; \ else \ - JS_SOROBAN_CLIENT_REF="file:/home/tester/js-soroban-client"; \ - fi + JS_STELLAR_SDK_REF="file:/home/tester/js-stellar-sdk"; \ + fi make \ CORE_GIT_REF=$SYSTEM_TEST_CORE_GIT_REF \ CORE_COMPILE_CONFIGURE_FLAGS="$SYSTEM_TEST_CORE_COMPILE_CONFIGURE_FLAGS" \ @@ -91,7 +91,7 @@ jobs: SOROBAN_CLI_GIT_REF=$SYSTEM_TEST_SOROBAN_TOOLS_REF \ RUST_TOOLCHAIN_VERSION=$SYSTEM_TEST_RUST_TOOLCHAIN_VERSION \ QUICKSTART_GIT_REF=$SYSTEM_TEST_QUICKSTART_GIT_REF \ - JS_SOROBAN_CLIENT_NPM_VERSION=$JS_SOROBAN_CLIENT_REF \ + JS_STELLAR_SDK_NPM_VERSION=$JS_STELLAR_SDK_REF \ build - name: Run system test scenarios run: | From 87f5a98feb65a1d26a5c9ce22808a0a4d1ee8a50 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:58:01 -0500 Subject: [PATCH 3/9] optimize in-memory transaction store (#1063) --- .../internal/ingest/service_test.go | 22 ++- .../internal/methods/get_transaction.go | 25 +-- .../internal/methods/get_transaction_test.go | 40 ++--- .../internal/transactions/transactions.go | 28 ++- .../transactions/transactions_test.go | 164 ++++++++++++++++-- 5 files changed, 215 insertions(+), 64 deletions(-) diff --git a/cmd/soroban-rpc/internal/ingest/service_test.go b/cmd/soroban-rpc/internal/ingest/service_test.go index 4993d326f..c2e4def09 100644 --- a/cmd/soroban-rpc/internal/ingest/service_test.go +++ b/cmd/soroban-rpc/internal/ingest/service_test.go @@ -111,6 +111,7 @@ func TestIngestion(t *testing.T) { Type: xdr.ScAddressTypeScAddressTypeContract, ContractId: &contractID, } + xdrTrue := true operationChanges := xdr.LedgerEntryChanges{ { Type: xdr.LedgerEntryChangeTypeLedgerEntryState, @@ -125,6 +126,10 @@ func TestIngestion(t *testing.T) { Sym: &persistentKey, }, Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvBool, + B: &xdrTrue, + }, }, }, }, @@ -145,6 +150,10 @@ func TestIngestion(t *testing.T) { Sym: &persistentKey, }, Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvBool, + B: &xdrTrue, + }, }, }, }, @@ -161,6 +170,10 @@ func TestIngestion(t *testing.T) { Sym: &persistentKey, }, Durability: xdr.ContractDataDurabilityTemporary, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvBool, + B: &xdrTrue, + }, }, }, } @@ -202,7 +215,14 @@ func TestIngestion(t *testing.T) { }, TxProcessing: []xdr.TransactionResultMeta{ { - Result: xdr.TransactionResultPair{TransactionHash: firstTxHash}, + Result: xdr.TransactionResultPair{ + TransactionHash: firstTxHash, + Result: xdr.TransactionResult{ + Result: xdr.TransactionResultResult{ + Results: &[]xdr.OperationResult{}, + }, + }, + }, FeeProcessing: xdr.LedgerEntryChanges{}, TxApplyProcessing: xdr.TransactionMeta{ V: 3, diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index a93457d79..257d435f3 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -2,6 +2,7 @@ package methods import ( "context" + "encoding/base64" "encoding/hex" "fmt" @@ -99,25 +100,11 @@ func GetTransaction(getter transactionGetter, request GetTransactionRequest) (Ge response.FeeBump = tx.FeeBump response.Ledger = int64(tx.Ledger.Sequence) response.LedgerCloseTime = tx.Ledger.CloseTime - if response.ResultXdr, err = xdr.MarshalBase64(tx.Result); err != nil { - return GetTransactionResponse{}, &jrpc2.Error{ - Code: jrpc2.InternalError, - Message: err.Error(), - } - } - if response.EnvelopeXdr, err = xdr.MarshalBase64(tx.Envelope); err != nil { - return GetTransactionResponse{}, &jrpc2.Error{ - Code: jrpc2.InternalError, - Message: err.Error(), - } - } - if response.ResultMetaXdr, err = xdr.MarshalBase64(tx.Meta); err != nil { - return GetTransactionResponse{}, &jrpc2.Error{ - Code: jrpc2.InternalError, - Message: err.Error(), - } - } - if tx.Result.Successful() { + + response.ResultXdr = base64.StdEncoding.EncodeToString(tx.Result) + response.EnvelopeXdr = base64.StdEncoding.EncodeToString(tx.Envelope) + response.ResultMetaXdr = base64.StdEncoding.EncodeToString(tx.Meta) + if tx.Successful { response.Status = TransactionStatusSuccess } else { response.Status = TransactionStatusFailed diff --git a/cmd/soroban-rpc/internal/methods/get_transaction_test.go b/cmd/soroban-rpc/internal/methods/get_transaction_test.go index 1b30cb762..85847f00c 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction_test.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/stellar/go/xdr" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stellar/go/network" @@ -115,33 +115,33 @@ func txEnvelope(acctSeq uint32) xdr.TransactionEnvelope { func TestGetTransaction(t *testing.T) { store := transactions.NewMemoryStore(interfaces.MakeNoOpDeamon(), "passphrase", 100) _, err := GetTransaction(store, GetTransactionRequest{"ab"}) - assert.EqualError(t, err, "[-32602] unexpected hash length (2)") + require.EqualError(t, err, "[-32602] unexpected hash length (2)") _, err = GetTransaction(store, GetTransactionRequest{"foo "}) - assert.EqualError(t, err, "[-32602] incorrect hash: encoding/hex: invalid byte: U+006F 'o'") + require.EqualError(t, err, "[-32602] incorrect hash: encoding/hex: invalid byte: U+006F 'o'") hash := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" tx, err := GetTransaction(store, GetTransactionRequest{hash}) - assert.NoError(t, err) - assert.Equal(t, GetTransactionResponse{ + require.NoError(t, err) + require.Equal(t, GetTransactionResponse{ Status: TransactionStatusNotFound, }, tx) meta := txMeta(1, true) err = store.IngestTransactions(meta) - assert.NoError(t, err) + require.NoError(t, err) xdrHash := txHash(1) hash = hex.EncodeToString(xdrHash[:]) tx, err = GetTransaction(store, GetTransactionRequest{hash}) - assert.NoError(t, err) + require.NoError(t, err) expectedTxResult, err := xdr.MarshalBase64(meta.V1.TxProcessing[0].Result.Result) - assert.NoError(t, err) + require.NoError(t, err) expectedEnvelope, err := xdr.MarshalBase64(txEnvelope(1)) - assert.NoError(t, err) + require.NoError(t, err) expectedTxMeta, err := xdr.MarshalBase64(meta.V1.TxProcessing[0].TxApplyProcessing) - assert.NoError(t, err) - assert.Equal(t, GetTransactionResponse{ + require.NoError(t, err) + require.Equal(t, GetTransactionResponse{ Status: TransactionStatusSuccess, LatestLedger: 101, LatestLedgerCloseTime: 2625, @@ -159,12 +159,12 @@ func TestGetTransaction(t *testing.T) { // ingest another (failed) transaction meta = txMeta(2, false) err = store.IngestTransactions(meta) - assert.NoError(t, err) + require.NoError(t, err) // the first transaction should still be there tx, err = GetTransaction(store, GetTransactionRequest{hash}) - assert.NoError(t, err) - assert.Equal(t, GetTransactionResponse{ + require.NoError(t, err) + require.Equal(t, GetTransactionResponse{ Status: TransactionStatusSuccess, LatestLedger: 102, LatestLedgerCloseTime: 2650, @@ -184,16 +184,16 @@ func TestGetTransaction(t *testing.T) { hash = hex.EncodeToString(xdrHash[:]) expectedTxResult, err = xdr.MarshalBase64(meta.V1.TxProcessing[0].Result.Result) - assert.NoError(t, err) + require.NoError(t, err) expectedEnvelope, err = xdr.MarshalBase64(txEnvelope(2)) - assert.NoError(t, err) + require.NoError(t, err) expectedTxMeta, err = xdr.MarshalBase64(meta.V1.TxProcessing[0].TxApplyProcessing) - assert.NoError(t, err) + require.NoError(t, err) tx, err = GetTransaction(store, GetTransactionRequest{hash}) - assert.NoError(t, err) - assert.NoError(t, err) - assert.Equal(t, GetTransactionResponse{ + require.NoError(t, err) + require.NoError(t, err) + require.Equal(t, GetTransactionResponse{ Status: TransactionStatusFailed, LatestLedger: 102, LatestLedgerCloseTime: 2650, diff --git a/cmd/soroban-rpc/internal/transactions/transactions.go b/cmd/soroban-rpc/internal/transactions/transactions.go index 1c63ce399..8d58a035a 100644 --- a/cmd/soroban-rpc/internal/transactions/transactions.go +++ b/cmd/soroban-rpc/internal/transactions/transactions.go @@ -14,10 +14,11 @@ import ( type transaction struct { bucket *ledgerbucketwindow.LedgerBucket[[]xdr.Hash] - result xdr.TransactionResult - meta xdr.TransactionMeta - envelope xdr.TransactionEnvelope + result []byte // encoded XDR of xdr.TransactionResult + meta []byte // encoded XDR of xdr.TransactionMeta + envelope []byte // encoded XDR of xdr.TransactionEnvelope feeBump bool + successful bool applicationOrder int32 } @@ -92,11 +93,18 @@ func (m *MemoryStore) IngestTransactions(ledgerCloseMeta xdr.LedgerCloseMeta) er } transactions[i] = transaction{ bucket: &bucket, - result: tx.Result.Result, - meta: tx.UnsafeMeta, - envelope: tx.Envelope, feeBump: tx.Envelope.IsFeeBump(), applicationOrder: int32(tx.Index), + successful: tx.Result.Result.Successful(), + } + if transactions[i].result, err = tx.Result.Result.MarshalBinary(); err != nil { + return err + } + if transactions[i].meta, err = tx.UnsafeMeta.MarshalBinary(); err != nil { + return err + } + if transactions[i].envelope, err = tx.Envelope.MarshalBinary(); err != nil { + return err } if transactions[i].feeBump { innerHash := tx.Result.InnerHash() @@ -135,11 +143,12 @@ type LedgerInfo struct { } type Transaction struct { - Result xdr.TransactionResult - Meta xdr.TransactionMeta - Envelope xdr.TransactionEnvelope + Result []byte // XDR encoded xdr.TransactionResult + Meta []byte // XDR encoded xdr.TransactionMeta + Envelope []byte // XDR encoded xdr.TransactionEnvelope FeeBump bool ApplicationOrder int32 + Successful bool Ledger LedgerInfo } @@ -191,6 +200,7 @@ func (m *MemoryStore) GetTransaction(hash xdr.Hash) (Transaction, bool, StoreRan Meta: internalTx.meta, Envelope: internalTx.envelope, FeeBump: internalTx.feeBump, + Successful: internalTx.successful, ApplicationOrder: internalTx.applicationOrder, Ledger: LedgerInfo{ Sequence: internalTx.bucket.LedgerSeq, diff --git a/cmd/soroban-rpc/internal/transactions/transactions_test.go b/cmd/soroban-rpc/internal/transactions/transactions_test.go index 64fabff1f..d32a62c6d 100644 --- a/cmd/soroban-rpc/internal/transactions/transactions_test.go +++ b/cmd/soroban-rpc/internal/transactions/transactions_test.go @@ -1,7 +1,12 @@ package transactions import ( + "encoding/hex" + "fmt" + "math" + "runtime" "testing" + "time" "github.com/stellar/go/network" "github.com/stellar/go/xdr" @@ -10,19 +15,24 @@ import ( "github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/daemon/interfaces" ) -func expectedTransaction(ledger uint32, feeBump bool) Transaction { - return Transaction{ - Result: transactionResult(ledger, feeBump), - Meta: xdr.TransactionMeta{ - V: 3, - Operations: &[]xdr.OperationMeta{}, - V3: &xdr.TransactionMetaV3{}, - }, - Envelope: txEnvelope(ledger, feeBump), +func expectedTransaction(t *testing.T, ledger uint32, feeBump bool) Transaction { + tx := Transaction{ FeeBump: feeBump, ApplicationOrder: 1, Ledger: expectedLedgerInfo(ledger), } + var err error + tx.Result, err = transactionResult(ledger, feeBump).MarshalBinary() + require.NoError(t, err) + tx.Meta, err = xdr.TransactionMeta{ + V: 3, + Operations: &[]xdr.OperationMeta{}, + V3: &xdr.TransactionMetaV3{}, + }.MarshalBinary() + require.NoError(t, err) + tx.Envelope, err = txEnvelope(ledger, feeBump).MarshalBinary() + require.NoError(t, err) + return tx } func expectedLedgerInfo(ledgerSequence uint32) LedgerInfo { @@ -81,13 +91,72 @@ func transactionResult(ledgerSequence uint32, feeBump bool) xdr.TransactionResul func txMeta(ledgerSequence uint32, feeBump bool) xdr.LedgerCloseMeta { envelope := txEnvelope(ledgerSequence, feeBump) - + persistentKey := xdr.ScSymbol("TEMPVAL") + contractIDBytes, _ := hex.DecodeString("df06d62447fd25da07c0135eed7557e5a5497ee7d15b7fe345bd47e191d8f577") + var contractID xdr.Hash + copy(contractID[:], contractIDBytes) + contractAddress := xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &contractID, + } + xdrTrue := true + operationChanges := xdr.LedgerEntryChanges{ + { + Type: xdr.LedgerEntryChangeTypeLedgerEntryState, + State: &xdr.LedgerEntry{ + LastModifiedLedgerSeq: xdr.Uint32(ledgerSequence - 1), + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeContractData, + ContractData: &xdr.ContractDataEntry{ + Contract: contractAddress, + Key: xdr.ScVal{ + Type: xdr.ScValTypeScvSymbol, + Sym: &persistentKey, + }, + Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvBool, + B: &xdrTrue, + }, + }, + }, + }, + }, + { + Type: xdr.LedgerEntryChangeTypeLedgerEntryUpdated, + Updated: &xdr.LedgerEntry{ + LastModifiedLedgerSeq: xdr.Uint32(ledgerSequence - 1), + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeContractData, + ContractData: &xdr.ContractDataEntry{ + Contract: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &contractID, + }, + Key: xdr.ScVal{ + Type: xdr.ScValTypeScvSymbol, + Sym: &persistentKey, + }, + Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvBool, + B: &xdrTrue, + }, + }, + }, + }, + }, + } txProcessing := []xdr.TransactionResultMeta{ { TxApplyProcessing: xdr.TransactionMeta{ - V: 3, - Operations: &[]xdr.OperationMeta{}, - V3: &xdr.TransactionMetaV3{}, + V: 3, + Operations: &[]xdr.OperationMeta{ + { + Changes: operationChanges, + }, + }, + V3: &xdr.TransactionMetaV3{}, }, Result: xdr.TransactionResultPair{ TransactionHash: txHash(ledgerSequence, feeBump), @@ -173,12 +242,12 @@ func txEnvelope(ledgerSequence uint32, feeBump bool) xdr.TransactionEnvelope { func requirePresent(t *testing.T, store *MemoryStore, feeBump bool, ledgerSequence, firstSequence, lastSequence uint32) { tx, ok, storeRange := store.GetTransaction(txHash(ledgerSequence, false)) require.True(t, ok) - require.Equal(t, expectedTransaction(ledgerSequence, feeBump), tx) + require.Equal(t, expectedTransaction(t, ledgerSequence, feeBump), tx) require.Equal(t, expectedStoreRange(firstSequence, lastSequence), storeRange) if feeBump { tx, ok, storeRange = store.GetTransaction(txHash(ledgerSequence, true)) require.True(t, ok) - require.Equal(t, expectedTransaction(ledgerSequence, feeBump), tx) + require.Equal(t, expectedTransaction(t, ledgerSequence, feeBump), tx) require.Equal(t, expectedStoreRange(firstSequence, lastSequence), storeRange) } } @@ -241,3 +310,68 @@ func TestIngestTransactions(t *testing.T) { require.Equal(t, uint32(3), store.transactionsByLedger.Len()) require.Len(t, store.transactions, 3) } + +func stableHeapInUse() int64 { + var ( + m = runtime.MemStats{} + prevInUse uint64 + prevNumGC uint32 + ) + + for { + runtime.GC() + + // Sleeping to allow GC to run a few times and collect all temporary data. + time.Sleep(100 * time.Millisecond) + + runtime.ReadMemStats(&m) + + // Considering heap stable if recent cycle collected less than 10KB. + if prevNumGC != 0 && m.NumGC > prevNumGC && math.Abs(float64(m.HeapInuse-prevInUse)) < 10*1024 { + break + } + + prevInUse = m.HeapInuse + prevNumGC = m.NumGC + } + + return int64(m.HeapInuse) +} + +func byteCountBinary(b int64) string { + const unit = 1024 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) +} + +func BenchmarkIngestTransactionsMemory(b *testing.B) { + roundsNumber := uint32(b.N * 100000) + // Use a small retention window to test eviction + store := NewMemoryStore(interfaces.MakeNoOpDeamon(), "passphrase", roundsNumber) + + heapSizeBefore := stableHeapInUse() + + for i := uint32(0); i < roundsNumber; i++ { + // Insert ledger i + require.NoError(b, store.IngestTransactions(txMeta(i, false))) + } + heapSizeAfter := stableHeapInUse() + b.ReportMetric(float64(heapSizeAfter), "bytes/100k_transactions") + b.Logf("Memory consumption for %d transactions %v", roundsNumber, byteCountBinary(heapSizeAfter-heapSizeBefore)) + + // we want to generate 500*20000 transactions total, to cover the expected daily amount of transactions. + projectedTransactionCount := int64(500 * 20000) + projectedMemoryUtiliztion := (heapSizeAfter - heapSizeBefore) * projectedTransactionCount / int64(roundsNumber) + b.Logf("Projected memory consumption for %d transactions %v", projectedTransactionCount, byteCountBinary(projectedMemoryUtiliztion)) + b.ReportMetric(float64(projectedMemoryUtiliztion), "bytes/10M_transactions") + + // add another call to store to prevent the GC from collecting. + store.GetTransaction(xdr.Hash{}) +} From 6bb1c12bc3ade831a42ddb722bdf14017af9f6be Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 6 Nov 2023 13:36:47 -0500 Subject: [PATCH 4/9] fix: print help if custom CLI is empty & replace unwrap where prossible (#1068) --- .../src/commands/contract/bindings/typescript.rs | 10 ++++++++-- cmd/soroban-cli/src/commands/contract/deploy.rs | 8 +++----- cmd/soroban-cli/src/commands/contract/invoke.rs | 16 +++++++++++----- cmd/soroban-cli/src/commands/plugin.rs | 4 +++- cmd/soroban-cli/src/utils.rs | 4 +++- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs index c0a780153..1c39cf2ae 100644 --- a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs +++ b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs @@ -61,13 +61,17 @@ pub enum Error { Fetch(#[from] fetch::Error), #[error(transparent)] Spec(#[from] contract_spec::Error), + #[error(transparent)] + Wasm(#[from] wasm::Error), + #[error("Failed to get file name from path: {0:?}")] + FailedToGetFileName(PathBuf), } impl Cmd { pub async fn run(&self) -> Result<(), Error> { let spec = if let Some(wasm) = &self.wasm { let wasm: wasm::Args = wasm.into(); - wasm.parse().unwrap().spec + wasm.parse()?.spec } else { let fetch = contract::fetch::Cmd { contract_id: self.contract_id.clone(), @@ -100,7 +104,9 @@ impl Cmd { .ok() .unwrap_or_else(Network::futurenet); let absolute_path = self.output_dir.canonicalize()?; - let file_name = absolute_path.file_name().unwrap(); + let file_name = absolute_path + .file_name() + .ok_or_else(|| Error::FailedToGetFileName(absolute_path.clone()))?; let contract_name = &file_name .to_str() .ok_or_else(|| Error::NotUtf8(file_name.to_os_string()))?; diff --git a/cmd/soroban-cli/src/commands/contract/deploy.rs b/cmd/soroban-cli/src/commands/contract/deploy.rs index 5ef18a833..5467b992a 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy.rs @@ -85,6 +85,8 @@ pub enum Error { Config(#[from] config::Error), #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), + #[error(transparent)] + Infallible(#[from] std::convert::Infallible), } impl Cmd { @@ -204,11 +206,7 @@ fn get_contract_id( contract_id_preimage: ContractIdPreimage, network_passphrase: &str, ) -> Result { - let network_id = Hash( - Sha256::digest(network_passphrase.as_bytes()) - .try_into() - .unwrap(), - ); + let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).try_into()?); let preimage = HashIdPreimage::ContractId(HashIdPreimageContractId { network_id, contract_id_preimage, diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 59c9bf440..2d4c77c61 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -167,8 +167,12 @@ impl Cmd { cmd = cmd.subcommand(build_custom_cmd(&name.to_string_lossy(), &spec)?); } cmd.build(); + let long_help = cmd.render_long_help(); let mut matches_ = cmd.get_matches_from(&self.slop); - let (function, matches_) = &matches_.remove_subcommand().unwrap(); + let Some((function, matches_)) = &matches_.remove_subcommand() else { + println!("{long_help}"); + std::process::exit(1); + }; let func = spec.find_function(function)?; // create parsed_args in same order as the inputs to func @@ -177,7 +181,7 @@ impl Cmd { .inputs .iter() .map(|i| { - let name = i.name.to_string().unwrap(); + let name = i.name.to_string()?; if let Some(mut val) = matches_.get_raw(&name) { let mut s = val.next().unwrap().to_string_lossy().to_string(); if matches!(i.type_, ScSpecTypeDef::Address) { @@ -205,7 +209,10 @@ impl Cmd { &std::fs::read(arg_path) .map_err(|_| Error::MissingFileArg(arg_path.clone()))?, ) - .unwrap()) + .map_err(|()| Error::CannotParseArg { + arg: name.clone(), + error: soroban_spec_tools::Error::Unknown, + })?) } else { let file_contents = std::fs::read_to_string(arg_path) .map_err(|_| Error::MissingFileArg(arg_path.clone()))?; @@ -414,7 +421,6 @@ fn build_custom_cmd(name: &str, spec: &Spec) -> Result { if kebab_name != name { cmd = cmd.alias(kebab_name); } - let func = spec.find_function(name).unwrap(); let doc: &'static str = Box::leak(func.doc.to_string_lossy().into_boxed_str()); let long_doc: &'static str = Box::leak(arg_file_help(doc).into_boxed_str()); @@ -428,7 +434,7 @@ fn build_custom_cmd(name: &str, spec: &Spec) -> Result { .alias(name.to_kebab_case()) .num_args(1) .value_parser(clap::builder::NonEmptyStringValueParser::new()) - .long_help(spec.doc(name, type_).unwrap()); + .long_help(spec.doc(name, type_)?); file_arg = file_arg .long(&file_arg_name) diff --git a/cmd/soroban-cli/src/commands/plugin.rs b/cmd/soroban-cli/src/commands/plugin.rs index e847dd043..27c191f08 100644 --- a/cmd/soroban-cli/src/commands/plugin.rs +++ b/cmd/soroban-cli/src/commands/plugin.rs @@ -19,6 +19,8 @@ pub enum Error { ExecutableNotFound(String, String), #[error(transparent)] Which(#[from] which::Error), + #[error(transparent)] + Regex(#[from] regex::Error), } const SUBCOMMAND_TOLERANCE: f64 = 0.75; @@ -82,7 +84,7 @@ pub fn list() -> Result, Error> { } else { r"^soroban-.*" }; - let re = regex::Regex::new(re_str).unwrap(); + let re = regex::Regex::new(re_str)?; Ok(which::which_re(re)? .filter_map(|b| { let s = b.file_name()?.to_str()?; diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index fcc3964c9..27faee733 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -131,6 +131,8 @@ pub mod parsing { CannotParseAccountId { account_id: String }, #[error("cannot parse asset: {asset}")] CannotParseAsset { asset: String }, + #[error(transparent)] + Regex(#[from] regex::Error), } pub fn parse_asset(str: &str) -> Result { @@ -145,7 +147,7 @@ pub mod parsing { } let code = split[0]; let issuer = split[1]; - let re = Regex::new("^[[:alnum:]]{1,12}$").unwrap(); + let re = Regex::new("^[[:alnum:]]{1,12}$")?; if !re.is_match(code) { return Err(Error::InvalidAssetCode { asset: str.to_string(), From e357ccb4edf8d3323de4cc243cdec578d1591ed2 Mon Sep 17 00:00:00 2001 From: Elizabeth <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:49:04 -0500 Subject: [PATCH 5/9] Fix/typescript bindings (#1066) * Use the default freight-api export * Update soroban-client to 1.0.0-beta.3 * Update error handling in invoke to conform to soroban-client beta.3 --- .../src/project_template/package.json | 2 +- .../src/project_template/src/invoke.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/package.json b/cmd/crates/soroban-spec-typescript/src/project_template/package.json index 168e94258..d75b89c74 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/package.json +++ b/cmd/crates/soroban-spec-typescript/src/project_template/package.json @@ -4,7 +4,7 @@ "dependencies": { "@stellar/freighter-api": "1.5.1", "buffer": "6.0.3", - "soroban-client": "1.0.0-beta.2" + "soroban-client": "1.0.0-beta.3" }, "scripts": { "build": "node ./scripts/build.mjs" diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts b/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts index d69166ce1..27a85c0ac 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts +++ b/cmd/crates/soroban-spec-typescript/src/project_template/src/invoke.ts @@ -17,6 +17,7 @@ import type { export type Tx = Transaction, Operation[]>; +export class SendFailedError extends Error { } /** * Get account details from the Soroban network for the publicKey currently * selected in Freighter. If not connected to Freighter, return null. @@ -82,7 +83,7 @@ export async function invoke({ contractId, wallet, }: InvokeArgs): Promise { - wallet = wallet ?? (await import("@stellar/freighter-api")); + wallet = wallet ?? (await import("@stellar/freighter-api")).default; let parse = parseResultXdr; const server = new SorobanClient.Server(rpcUrl, { allowHttp: rpcUrl.startsWith("http://"), @@ -162,7 +163,11 @@ export async function invoke({ if ("returnValue" in raw) return parse(raw.returnValue!); // otherwise, it returned the result of `sendTransaction` - if ("errorResultXdr" in raw) return parse(raw.errorResultXdr!); + if ("errorResult" in raw) { + throw new SendFailedError( + `errorResult.result(): ${JSON.stringify(raw.errorResult?.result())}` + ) + } // if neither of these are present, something went wrong console.error("Don't know how to parse result! Returning full RPC response."); From 9259e081de643463192a395e4be6760e22f24f31 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 7 Nov 2023 09:55:34 -0500 Subject: [PATCH 6/9] feat(CLI): Assembled Transaction that handle preparing transaction post simulation (#1049) * feat(CLI): Transaction Handler that handles the state transitions * fix: assembly transaction after authorizing & improve txn error output * feat: Handler->Assembled - simplify to assembled txn with simulation --- cmd/soroban-cli/src/rpc/mod.rs | 215 +++++++++--------- .../src/rpc/{transaction.rs => txn.rs} | 211 ++++++++++++++--- 2 files changed, 287 insertions(+), 139 deletions(-) rename cmd/soroban-cli/src/rpc/{transaction.rs => txn.rs} (71%) diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index e8fc3285c..d9fe541a3 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -7,14 +7,14 @@ use serde_aux::prelude::{ deserialize_default_from_null, deserialize_number_from_string, deserialize_option_number_from_string, }; +use soroban_env_host::xdr::DepthLimitedRead; use soroban_env_host::xdr::{ self, AccountEntry, AccountId, ContractDataEntry, DiagnosticEvent, Error as XdrError, LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, PublicKey, ReadXdr, - SequenceNumber, SorobanAuthorizationEntry, SorobanResources, Transaction, TransactionEnvelope, - TransactionMeta, TransactionMetaV3, TransactionResult, TransactionV1Envelope, Uint256, VecM, + SorobanAuthorizationEntry, SorobanResources, SorobanTransactionData, Transaction, + TransactionEnvelope, TransactionMeta, TransactionMetaV3, TransactionResult, Uint256, VecM, WriteXdr, }; -use soroban_env_host::xdr::{DepthLimitedRead, SorobanAuthorizedFunction}; use soroban_sdk::token; use std::{ fmt::Display, @@ -25,10 +25,9 @@ use termcolor::{Color, ColorChoice, StandardStream, WriteColor}; use termcolor_output::colored; use tokio::time::sleep; -use crate::utils::{self, contract_spec}; +use crate::utils::contract_spec; -mod transaction; -use transaction::{assemble, build_restore_txn, sign_soroban_authorizations}; +mod txn; const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); @@ -98,6 +97,8 @@ pub enum Error { SpecBase64(#[from] soroban_spec::read::ParseSpecBase64Error), #[error("Fee was too large {0}")] LargeFee(u64), + #[error("Cannot authorize raw transactions")] + CannotAuthorizeRawTransaction, } #[derive(serde::Deserialize, serde::Serialize, Debug)] @@ -123,7 +124,7 @@ pub struct SendTransactionResponse { } #[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct GetTransactionResponse { +pub struct GetTransactionResponseRaw { pub status: String, #[serde( rename = "envelopeXdr", @@ -142,6 +143,33 @@ pub struct GetTransactionResponse { // TODO: add ledger info and application order } +#[derive(serde::Deserialize, serde::Serialize, Debug)] +pub struct GetTransactionResponse { + pub status: String, + pub envelope: Option, + pub result: Option, + pub result_meta: Option, +} + +impl TryInto for GetTransactionResponseRaw { + type Error = xdr::Error; + + fn try_into(self) -> Result { + Ok(GetTransactionResponse { + status: self.status, + envelope: self + .envelope_xdr + .map(ReadXdr::from_xdr_base64) + .transpose()?, + result: self.result_xdr.map(ReadXdr::from_xdr_base64).transpose()?, + result_meta: self + .result_meta_xdr + .map(ReadXdr::from_xdr_base64) + .transpose()?, + }) + } +} + #[derive(serde::Deserialize, serde::Serialize, Debug)] pub struct LedgerEntryResult { pub key: String, @@ -212,12 +240,18 @@ pub struct Cost { } #[derive(serde::Deserialize, serde::Serialize, Debug)] -pub struct SimulateHostFunctionResult { +pub struct SimulateHostFunctionResultRaw { #[serde(deserialize_with = "deserialize_default_from_null")] pub auth: Vec, pub xdr: String, } +#[derive(Debug)] +pub struct SimulateHostFunctionResult { + pub auth: Vec, + pub xdr: xdr::ScVal, +} + #[derive(serde::Deserialize, serde::Serialize, Debug, Default)] pub struct SimulateTransactionResponse { #[serde( @@ -229,7 +263,7 @@ pub struct SimulateTransactionResponse { #[serde(default)] pub cost: Cost, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub results: Vec, + pub results: Vec, #[serde(rename = "transactionData", default)] pub transaction_data: String, #[serde( @@ -253,6 +287,37 @@ pub struct SimulateTransactionResponse { pub error: Option, } +impl SimulateTransactionResponse { + pub fn results(&self) -> Result, Error> { + self.results + .iter() + .map(|r| { + Ok(SimulateHostFunctionResult { + auth: r + .auth + .iter() + .map(|a| Ok(SorobanAuthorizationEntry::from_xdr_base64(a)?)) + .collect::>()?, + xdr: xdr::ScVal::from_xdr_base64(&r.xdr)?, + }) + }) + .collect() + } + + pub fn events(&self) -> Result, Error> { + self.events + .iter() + .map(|e| Ok(DiagnosticEvent::from_xdr_base64(e)?)) + .collect() + } + + pub fn transaction_data(&self) -> Result { + Ok(SorobanTransactionData::from_xdr_base64( + &self.transaction_data, + )?) + } +} + #[derive(serde::Deserialize, serde::Serialize, Debug, Default)] pub struct RestorePreamble { #[serde(rename = "transactionData")] @@ -563,7 +628,7 @@ soroban config identity fund {address} --helper-url "# tx: &TransactionEnvelope, ) -> Result<(TransactionResult, TransactionMeta, Vec), Error> { let client = self.client()?; - tracing::trace!(?tx); + tracing::trace!("Sending:\n{tx:#?}"); let SendTransactionResponse { hash, error_result_xdr, @@ -572,7 +637,9 @@ soroban config identity fund {address} --helper-url "# } = client .request("sendTransaction", rpc_params![tx.to_xdr_base64()?]) .await - .map_err(|err| Error::TransactionSubmissionFailed(format!("{err:#?}")))?; + .map_err(|err| { + Error::TransactionSubmissionFailed(format!("No status yet:\n {err:#?}")) + })?; if status == "ERROR" { let error = error_result_xdr @@ -585,7 +652,7 @@ soroban config identity fund {address} --helper-url "# .map_err(|_| Error::InvalidResponse) }) .map(|r| r.result); - tracing::error!(?error); + tracing::error!("TXN failed:\n {error:#?}"); return Err(Error::TransactionSubmissionFailed(format!("{:#?}", error?))); } // even if status == "success" we need to query the transaction status in order to get the result @@ -593,27 +660,27 @@ soroban config identity fund {address} --helper-url "# // Poll the transaction status let start = Instant::now(); loop { - let response = self.get_transaction(&hash).await?; + let response: GetTransactionResponse = self.get_transaction(&hash).await?.try_into()?; match response.status.as_str() { "SUCCESS" => { // TODO: the caller should probably be printing this - tracing::trace!(?response); - let result = TransactionResult::from_xdr_base64( - response.result_xdr.clone().ok_or(Error::MissingResult)?, - )?; - let meta = TransactionMeta::from_xdr_base64( - response - .result_meta_xdr - .clone() - .ok_or(Error::MissingResult)?, - )?; + tracing::trace!("{response:#?}"); + let GetTransactionResponse { + result, + result_meta, + .. + } = response; + let meta = result_meta.ok_or(Error::MissingResult)?; let events = extract_events(&meta); - return Ok((result, meta, events)); + return Ok((result.ok_or(Error::MissingResult)?, meta, events)); } "FAILED" => { - tracing::error!(?response); + tracing::error!("{response:#?}"); // TODO: provide a more elaborate error - return Err(Error::TransactionSubmissionFailed(format!("{response:#?}"))); + return Err(Error::TransactionSubmissionFailed(format!( + "{:#?}", + response.result + ))); } "NOT_FOUND" => (), _ => { @@ -633,13 +700,13 @@ soroban config identity fund {address} --helper-url "# &self, tx: &TransactionEnvelope, ) -> Result { - tracing::trace!(?tx); + tracing::trace!("Simulating:\n{tx:#?}"); let base64_tx = tx.to_xdr_base64()?; let response: SimulateTransactionResponse = self .client()? .request("simulateTransaction", rpc_params![base64_tx]) .await?; - tracing::trace!(?response); + tracing::trace!("Simulation response:\n {response:#?}"); match response.error { None => Ok(response), Some(e) => { @@ -649,31 +716,6 @@ soroban config identity fund {address} --helper-url "# } } - // Simulate a transaction, then assemble the result of the simulation into the envelope, so it - // is ready for sending to the network. - pub async fn prepare_transaction( - &self, - tx: &Transaction, - ) -> Result<(Transaction, Option, Vec), Error> { - tracing::trace!(?tx); - let sim_response = self - .simulate_transaction(&TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: VecM::default(), - })) - .await?; - let events = sim_response - .events - .iter() - .map(DiagnosticEvent::from_xdr_base64) - .collect::, _>>()?; - Ok(( - assemble(tx, &sim_response)?, - sim_response.restore_preamble, - events, - )) - } - pub async fn prepare_and_send_transaction( &self, tx_without_preflight: &Transaction, @@ -683,68 +725,19 @@ soroban config identity fund {address} --helper-url "# log_events: Option, log_resources: Option, ) -> Result<(TransactionResult, TransactionMeta, Vec), Error> { - let GetLatestLedgerResponse { sequence, .. } = self.get_latest_ledger().await?; - let (mut unsigned_tx, restore_preamble, events) = - self.prepare_transaction(tx_without_preflight).await?; - if let Some(restore) = restore_preamble { - // Build and submit the restore transaction - self.send_transaction(&utils::sign_transaction( - source_key, - &build_restore_txn(&unsigned_tx, &restore)?, - network_passphrase, - )?) + let txn = txn::Assembled::new(tx_without_preflight, self).await?; + let seq_num = txn.sim_res().latest_ledger + 60; //5 min; + let authorized = txn + .handle_restore(self, source_key, network_passphrase) + .await? + .authorize(self, source_key, signers, seq_num, network_passphrase) .await?; - // Increment the original txn's seq_num so it doesn't conflict - unsigned_tx.seq_num = SequenceNumber(unsigned_tx.seq_num.0 + 1); - } - let (part_signed_tx, signed_auth_entries) = sign_soroban_authorizations( - &unsigned_tx, - source_key, - signers, - sequence + 60, // ~5 minutes of ledgers - network_passphrase, - )?; - let (fee_ready_txn, events) = if signed_auth_entries.is_empty() - || (signed_auth_entries.len() == 1 - && matches!( - signed_auth_entries[0].root_invocation.function, - SorobanAuthorizedFunction::CreateContractHostFn(_) - )) { - (part_signed_tx, events) - } else { - // re-simulate to calculate the new fees - let (tx, _, events) = self.prepare_transaction(&part_signed_tx).await?; - (tx, events) - }; - - // Try logging stuff if requested - if let Transaction { - ext: xdr::TransactionExt::V1(xdr::SorobanTransactionData { resources, .. }), - .. - } = fee_ready_txn.clone() - { - if let Some(log) = log_events { - if let xdr::Operation { - body: - xdr::OperationBody::InvokeHostFunction(xdr::InvokeHostFunctionOp { - auth, .. - }), - .. - } = &fee_ready_txn.operations[0] - { - log(&resources.footprint, &[auth.clone()], &events); - } - } - if let Some(log) = log_resources { - log(&resources); - } - } - - let tx = utils::sign_transaction(source_key, &fee_ready_txn, network_passphrase)?; + authorized.log(log_events, log_resources)?; + let tx = authorized.sign(source_key, network_passphrase)?; self.send_transaction(&tx).await } - pub async fn get_transaction(&self, tx_id: &str) -> Result { + pub async fn get_transaction(&self, tx_id: &str) -> Result { Ok(self .client()? .request("getTransaction", rpc_params![tx_id]) diff --git a/cmd/soroban-cli/src/rpc/transaction.rs b/cmd/soroban-cli/src/rpc/txn.rs similarity index 71% rename from cmd/soroban-cli/src/rpc/transaction.rs rename to cmd/soroban-cli/src/rpc/txn.rs index bdd3dfc68..32cda0633 100644 --- a/cmd/soroban-cli/src/rpc/transaction.rs +++ b/cmd/soroban-cli/src/rpc/txn.rs @@ -1,14 +1,161 @@ use ed25519_dalek::Signer; use sha2::{Digest, Sha256}; use soroban_env_host::xdr::{ - AccountId, ExtensionPoint, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, Memo, - Operation, OperationBody, Preconditions, PublicKey, ReadXdr, RestoreFootprintOp, ScAddress, - ScMap, ScSymbol, ScVal, SorobanAddressCredentials, SorobanAuthorizationEntry, - SorobanCredentials, SorobanTransactionData, Transaction, TransactionExt, Uint256, VecM, - WriteXdr, + self, AccountId, DecoratedSignature, ExtensionPoint, Hash, HashIdPreimage, + HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Memo, Operation, OperationBody, + Preconditions, PublicKey, ReadXdr, RestoreFootprintOp, ScAddress, ScMap, ScSymbol, ScVal, + Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, + SorobanAuthorizedFunction, SorobanCredentials, SorobanResources, SorobanTransactionData, + Transaction, TransactionEnvelope, TransactionExt, TransactionSignaturePayload, + TransactionSignaturePayloadTaggedTransaction, TransactionV1Envelope, Uint256, VecM, WriteXdr, }; -use crate::rpc::{Error, RestorePreamble, SimulateTransactionResponse}; +use crate::rpc::{Client, Error, RestorePreamble, SimulateTransactionResponse}; + +use super::{LogEvents, LogResources}; + +pub struct Assembled { + txn: Transaction, + sim_res: SimulateTransactionResponse, +} + +impl Assembled { + pub async fn new(txn: &Transaction, client: &Client) -> Result { + let sim_res = Self::simulate(txn, client).await?; + let txn = assemble(txn, &sim_res)?; + Ok(Self { txn, sim_res }) + } + + pub fn hash(&self, network_passphrase: &str) -> Result<[u8; 32], xdr::Error> { + let signature_payload = TransactionSignaturePayload { + network_id: Hash(Sha256::digest(network_passphrase).into()), + tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(self.txn.clone()), + }; + Ok(Sha256::digest(signature_payload.to_xdr()?).into()) + } + + pub fn sign( + self, + key: &ed25519_dalek::SigningKey, + network_passphrase: &str, + ) -> Result { + let tx = self.txn(); + let tx_hash = self.hash(network_passphrase)?; + let tx_signature = key.sign(&tx_hash); + + let decorated_signature = DecoratedSignature { + hint: SignatureHint(key.verifying_key().to_bytes()[28..].try_into()?), + signature: Signature(tx_signature.to_bytes().try_into()?), + }; + + Ok(TransactionEnvelope::Tx(TransactionV1Envelope { + tx: tx.clone(), + signatures: vec![decorated_signature].try_into()?, + })) + } + + pub async fn simulate( + tx: &Transaction, + client: &Client, + ) -> Result { + client + .simulate_transaction(&TransactionEnvelope::Tx(TransactionV1Envelope { + tx: tx.clone(), + signatures: VecM::default(), + })) + .await + } + + pub async fn handle_restore( + self, + client: &Client, + source_key: &ed25519_dalek::SigningKey, + network_passphrase: &str, + ) -> Result { + if let Some(restore_preamble) = &self.sim_res.restore_preamble { + // Build and submit the restore transaction + client + .send_transaction( + &Assembled::new(&restore(self.txn(), restore_preamble)?, client) + .await? + .sign(source_key, network_passphrase)?, + ) + .await?; + Ok(self.bump_seq_num()) + } else { + Ok(self) + } + } + + pub fn txn(&self) -> &Transaction { + &self.txn + } + + pub fn sim_res(&self) -> &SimulateTransactionResponse { + &self.sim_res + } + + pub async fn authorize( + self, + client: &Client, + source_key: &ed25519_dalek::SigningKey, + signers: &[ed25519_dalek::SigningKey], + seq_num: u32, + network_passphrase: &str, + ) -> Result { + if let Some(txn) = sign_soroban_authorizations( + self.txn(), + source_key, + signers, + seq_num, + network_passphrase, + )? { + Self::new(&txn, client).await + } else { + Ok(self) + } + } + + pub fn bump_seq_num(mut self) -> Self { + self.txn.seq_num.0 += 1; + self + } + + pub fn auth(&self) -> VecM { + self.txn + .operations + .get(0) + .and_then(|op| match op.body { + OperationBody::InvokeHostFunction(ref body) => (matches!( + body.auth.get(0).map(|x| &x.root_invocation.function), + Some(&SorobanAuthorizedFunction::ContractFn(_)) + )) + .then_some(body.auth.clone()), + _ => None, + }) + .unwrap_or_default() + } + + pub fn log( + &self, + log_events: Option, + log_resources: Option, + ) -> Result<(), Error> { + if let TransactionExt::V1(SorobanTransactionData { + resources: resources @ SorobanResources { footprint, .. }, + .. + }) = &self.txn.ext + { + if let Some(log) = log_resources { + log(resources); + } + if let Some(log) = log_events { + log(footprint, &[self.auth()], &self.sim_res.events()?); + }; + } + Ok(()) + } +} // Apply the result of a simulateTransaction onto a transaction envelope, preparing it for // submission to the network. @@ -28,7 +175,7 @@ pub fn assemble( }); } - let transaction_data = SorobanTransactionData::from_xdr_base64(&simulation.transaction_data)?; + let transaction_data = simulation.transaction_data()?; let mut op = tx.operations[0].clone(); if let OperationBody::InvokeHostFunction(ref mut body) = &mut op.body { @@ -74,23 +221,34 @@ pub fn assemble( // Use the given source_key and signers, to sign all SorobanAuthorizationEntry's in the given // transaction. If unable to sign, return an error. -pub fn sign_soroban_authorizations( +fn sign_soroban_authorizations( raw: &Transaction, source_key: &ed25519_dalek::SigningKey, signers: &[ed25519_dalek::SigningKey], signature_expiration_ledger: u32, network_passphrase: &str, -) -> Result<(Transaction, Vec), Error> { +) -> Result, Error> { let mut tx = raw.clone(); + let mut op = match tx.operations.as_slice() { + [op @ Operation { + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { auth, .. }), + .. + }] if matches!( + auth.get(0).map(|x| &x.root_invocation.function), + Some(&SorobanAuthorizedFunction::ContractFn(_)) + ) => + { + op.clone() + } + _ => return Ok(None), + }; - if tx.operations.len() != 1 { - // This must not be an invokeHostFunction operation, so nothing to do - return Ok((tx, Vec::new())); - } - - let mut op = tx.operations[0].clone(); - let OperationBody::InvokeHostFunction(ref mut body) = &mut op.body else { - return Ok((tx, Vec::new())); + let Operation { + body: OperationBody::InvokeHostFunction(ref mut body), + .. + } = op + else { + return Ok(None); }; let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); @@ -100,6 +258,7 @@ pub fn sign_soroban_authorizations( let signed_auths = body .auth + .as_slice() .iter() .map(|raw_auth| { let mut auth = raw_auth.clone(); @@ -153,12 +312,12 @@ pub fn sign_soroban_authorizations( }) .collect::, Error>>()?; - body.auth = signed_auths.clone().try_into()?; + body.auth = signed_auths.try_into()?; tx.operations = vec![op].try_into()?; - Ok((tx, signed_auths)) + Ok(Some(tx)) } -pub fn sign_soroban_authorization_entry( +fn sign_soroban_authorization_entry( raw: &SorobanAuthorizationEntry, signer: &ed25519_dalek::SigningKey, signature_expiration_ledger: u32, @@ -218,12 +377,8 @@ pub fn sign_soroban_authorization_entry( Ok(auth) } -pub fn build_restore_txn( - parent: &Transaction, - restore: &RestorePreamble, -) -> Result { - let transaction_data = - SorobanTransactionData::from_xdr_base64(restore.transaction_data.clone())?; +pub fn restore(parent: &Transaction, restore: &RestorePreamble) -> Result { + let transaction_data = SorobanTransactionData::from_xdr_base64(&restore.transaction_data)?; let fee = u32::try_from(restore.min_resource_fee) .map_err(|_| Error::LargeFee(restore.min_resource_fee))?; Ok(Transaction { @@ -251,7 +406,7 @@ pub fn build_restore_txn( mod tests { use super::*; - use super::super::SimulateHostFunctionResult; + use super::super::SimulateHostFunctionResultRaw; use soroban_env_host::xdr::{ self, AccountId, ChangeTrustAsset, ChangeTrustOp, ExtensionPoint, Hash, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerFootprint, Memo, MuxedAccount, Operation, @@ -303,7 +458,7 @@ mod tests { SimulateTransactionResponse { min_resource_fee: 115, latest_ledger: 3, - results: vec![SimulateHostFunctionResult { + results: vec![SimulateHostFunctionResultRaw { auth: vec![fn_auth.to_xdr_base64().unwrap()], xdr: ScVal::U32(0).to_xdr_base64().unwrap(), }], From 5398c062a1aebea19595071360c0d99dc72ebe85 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 8 Nov 2023 12:20:12 -0500 Subject: [PATCH 7/9] chore: add burn test (#1071) --- .../soroban-test/tests/it/integration.rs | 1 + .../soroban-test/tests/it/integration/wrap.rs | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 cmd/crates/soroban-test/tests/it/integration/wrap.rs diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index d196ce07b..4e92b931a 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -2,3 +2,4 @@ mod custom_types; mod dotenv; mod hello_world; mod util; +mod wrap; diff --git a/cmd/crates/soroban-test/tests/it/integration/wrap.rs b/cmd/crates/soroban-test/tests/it/integration/wrap.rs new file mode 100644 index 000000000..f1a453e7a --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/wrap.rs @@ -0,0 +1,79 @@ +use soroban_cli::CommandParser; +use soroban_cli::{ + commands::{ + config::{self}, + lab::token::wrap, + }, + utils::contract_id_hash_from_asset, +}; +use soroban_test::TestEnv; + +use super::util::network_passphrase; + +#[tokio::test] +#[ignore] +async fn burn() { + let sandbox = &TestEnv::default(); + let address = config::identity::address::Cmd::parse("--hd-path=0") + .unwrap() + .public_key() + .unwrap(); + let asset = format!("native:{address}"); + wrap_cmd(&asset).run().await.unwrap(); + let asset = soroban_cli::utils::parsing::parse_asset(&asset).unwrap(); + let hash = contract_id_hash_from_asset(&asset, &network_passphrase().unwrap()).unwrap(); + let id = stellar_strkey::Contract(hash.0).to_string(); + assert_eq!( + "CAMTHSPKXZJIRTUXQP5QWJIFH3XIDMKLFAWVQOFOXPTKAW5GKV37ZC4N", + id + ); + assert_eq!( + "true", + sandbox + .invoke(&[ + "--id", + &id, + "--", + "authorized", + "--id", + &address.to_string() + ]) + .await + .unwrap() + ); + assert_eq!( + "\"9223372036854775807\"", + sandbox + .invoke(&["--id", &id, "--", "balance", "--id", &address.to_string()]) + .await + .unwrap(), + ); + + println!( + "{}", + sandbox + .invoke(&[ + "--id", + &id, + "--", + "burn", + "--id", + &address.to_string(), + "--amount=100" + ]) + .await + .unwrap() + ); + + assert_eq!( + "\"9223372036854775707\"", + sandbox + .invoke(&["--id", &id, "--", "balance", "--id", &address.to_string()]) + .await + .unwrap(), + ); +} + +fn wrap_cmd(asset: &str) -> wrap::Cmd { + wrap::Cmd::parse_arg_vec(&[&format!("--asset={asset}")]).unwrap() +} From 733ce062532c45ff96e690f135f6c1193a6d14d3 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 14 Nov 2023 23:05:00 +0100 Subject: [PATCH 8/9] Bump core version (#1076) --- .github/workflows/soroban-rpc.yml | 2 +- .../internal/test/docker-compose.yml | 2 +- .../test/simulate_transaction_test.go | 2 +- .../internal/test/transaction_test.go | 16 +++++++++++- cmd/soroban-rpc/lib/preflight/src/fees.rs | 25 +++---------------- go.mod | 2 +- go.sum | 4 +-- 7 files changed, 25 insertions(+), 28 deletions(-) diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index 8ec1b35c5..bbd1c647c 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -112,7 +112,7 @@ jobs: env: SOROBAN_RPC_INTEGRATION_TESTS_ENABLED: true SOROBAN_RPC_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core - PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.14.1-1547.f2d06fbce.focal + PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.14.1-1553.f4c4e2fca.focal steps: - uses: actions/checkout@v3 with: diff --git a/cmd/soroban-rpc/internal/test/docker-compose.yml b/cmd/soroban-rpc/internal/test/docker-compose.yml index 3e8af4bfe..39e3f7ed6 100644 --- a/cmd/soroban-rpc/internal/test/docker-compose.yml +++ b/cmd/soroban-rpc/internal/test/docker-compose.yml @@ -15,7 +15,7 @@ services: # Note: Please keep the image pinned to an immutable tag matching the Captive Core version. # This avoids implicit updates which break compatibility between # the Core container and captive core. - image: ${CORE_IMAGE:-stellar/stellar-core:19.14.1-1547.f2d06fbce.focal} + image: ${CORE_IMAGE:-stellar/stellar-core:19.14.1-1553.f4c4e2fca.focal} depends_on: - core-postgres restart: on-failure diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 04ac395ea..b47c609f0 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -233,7 +233,7 @@ func TestSimulateTransactionSucceeds(t *testing.T) { }, }, Instructions: 6062311, - ReadBytes: 48, + ReadBytes: 0, WriteBytes: 7048, }, ResourceFee: 130498, diff --git a/cmd/soroban-rpc/internal/test/transaction_test.go b/cmd/soroban-rpc/internal/test/transaction_test.go index ff8473420..e370d7d19 100644 --- a/cmd/soroban-rpc/internal/test/transaction_test.go +++ b/cmd/soroban-rpc/internal/test/transaction_test.go @@ -259,7 +259,21 @@ func sendSuccessfulTransaction(t *testing.T, client *jrpc2.Client, kp *keypair.F var txMeta xdr.TransactionMeta err = xdr.SafeUnmarshalBase64(response.ResultMetaXdr, &txMeta) assert.NoError(t, err) - fmt.Printf("meta: %#v\n", txMeta) + if txMeta.V == 3 && txMeta.V3.SorobanMeta != nil { + if len(txMeta.V3.SorobanMeta.Events) > 0 { + fmt.Println("Contract events:") + for i, e := range txMeta.V3.SorobanMeta.Events { + fmt.Printf(" %d: %s\n", i, e) + } + } + + if len(txMeta.V3.SorobanMeta.DiagnosticEvents) > 0 { + fmt.Println("Diagnostic events:") + for i, d := range txMeta.V3.SorobanMeta.DiagnosticEvents { + fmt.Printf(" %d: %s\n", i, d) + } + } + } } require.NotNil(t, response.ResultXdr) diff --git a/cmd/soroban-rpc/lib/preflight/src/fees.rs b/cmd/soroban-rpc/lib/preflight/src/fees.rs index 20e002b3d..e32ab3e9b 100644 --- a/cmd/soroban-rpc/lib/preflight/src/fees.rs +++ b/cmd/soroban-rpc/lib/preflight/src/fees.rs @@ -7,7 +7,7 @@ use soroban_env_host::e2e_invoke::{ use soroban_env_host::fees::{ compute_rent_fee, compute_transaction_resource_fee, compute_write_fee_per_1kb, FeeConfiguration, LedgerEntryRentChange, RentFeeConfiguration, TransactionResources, - WriteFeeConfiguration, TTL_ENTRY_SIZE, + WriteFeeConfiguration, }; use soroban_env_host::storage::{AccessType, Footprint, Storage}; use soroban_env_host::xdr; @@ -131,10 +131,7 @@ fn calculate_host_function_soroban_resources( ) -> Result { let ledger_footprint = storage_footprint_to_ledger_footprint(footprint) .context("cannot convert storage footprint to ledger footprint")?; - let read_bytes: u32 = ledger_changes - .iter() - .map(|c| c.old_entry_size_bytes + c.ttl_change.as_ref().map_or(0, |_| TTL_ENTRY_SIZE)) - .sum(); + let read_bytes: u32 = ledger_changes.iter().map(|c| c.old_entry_size_bytes).sum(); let write_bytes: u32 = ledger_changes .iter() @@ -224,17 +221,6 @@ fn get_fee_configurations( Ok((fee_configuration, rent_fee_configuration)) } -// Calculate the implicit TTLEntry bytes that will be read for TTLLedgerEntries -fn calculate_ttl_entry_bytes(ledger_entries: &[LedgerKey]) -> u32 { - ledger_entries - .iter() - .map(|lk| match lk { - LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => TTL_ENTRY_SIZE, - _ => 0, - }) - .sum() -} - #[allow(clippy::cast_possible_truncation)] fn calculate_unmodified_ledger_entry_bytes( ledger_entries: &[LedgerKey], @@ -322,8 +308,6 @@ pub(crate) fn compute_extend_footprint_ttl_transaction_data_and_min_fee( ) .context("cannot compute extend rent changes")?; - let ttl_bytes: u32 = calculate_ttl_entry_bytes(footprint.read_only.as_vec()); - let unmodified_entry_bytes = calculate_unmodified_ledger_entry_bytes( footprint.read_only.as_slice(), ledger_storage, @@ -334,7 +318,7 @@ pub(crate) fn compute_extend_footprint_ttl_transaction_data_and_min_fee( let soroban_resources = SorobanResources { footprint, instructions: 0, - read_bytes: unmodified_entry_bytes + ttl_bytes, + read_bytes: unmodified_entry_bytes, write_bytes: 0, }; let transaction_size_bytes = estimate_max_transaction_size_for_operation( @@ -420,7 +404,6 @@ pub(crate) fn compute_restore_footprint_transaction_data_and_min_fee( ) .context("cannot compute restore rent changes")?; - let ttl_bytes: u32 = calculate_ttl_entry_bytes(footprint.read_write.as_vec()); let write_bytes = calculate_unmodified_ledger_entry_bytes( footprint.read_write.as_vec(), ledger_storage, @@ -430,7 +413,7 @@ pub(crate) fn compute_restore_footprint_transaction_data_and_min_fee( let soroban_resources = SorobanResources { footprint, instructions: 0, - read_bytes: write_bytes + ttl_bytes, + read_bytes: write_bytes, write_bytes, }; let transaction_size_bytes = estimate_max_transaction_size_for_operation( diff --git a/go.mod b/go.mod index fd4fca5f1..f929c0e33 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stellar/go v0.0.0-20231016174715-c7d3a47ee7a2 + github.com/stellar/go v0.0.0-20231114175958-eb2984b58392 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.13.0 gotest.tools/v3 v3.5.0 diff --git a/go.sum b/go.sum index f0d20235f..7ab9b159e 100644 --- a/go.sum +++ b/go.sum @@ -333,8 +333,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= -github.com/stellar/go v0.0.0-20231016174715-c7d3a47ee7a2 h1:IOOHd1yrwmK0wiAuNDmoHUPTucO0oGkkKa3CE1pgn2E= -github.com/stellar/go v0.0.0-20231016174715-c7d3a47ee7a2/go.mod h1:g78pyZyDFnKMJUaBIXxH7xyQ7PdDrvrJTFCxdGMMb3c= +github.com/stellar/go v0.0.0-20231114175958-eb2984b58392 h1:sYxHgLDT3z6cJrWuf0O9Fbs/E2UNGh3PPoOlM8DJ2vk= +github.com/stellar/go v0.0.0-20231114175958-eb2984b58392/go.mod h1:g78pyZyDFnKMJUaBIXxH7xyQ7PdDrvrJTFCxdGMMb3c= github.com/stellar/go-xdr v0.0.0-20230919160922-6c7b68458206 h1:UFuvvpbWL8+jqO1QmKYWSVhiMp4MRiIFd8/zQlUINH0= github.com/stellar/go-xdr v0.0.0-20230919160922-6c7b68458206/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From c560bddc58dda6f3bd9646d65e1653077efd2c98 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 21 Nov 2023 00:32:42 +0100 Subject: [PATCH 9/9] cli: Appease new clippy version (#1094) --- cmd/crates/soroban-spec-typescript/src/lib.rs | 6 +++--- cmd/crates/soroban-test/tests/it/arg_parsing.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/crates/soroban-spec-typescript/src/lib.rs b/cmd/crates/soroban-spec-typescript/src/lib.rs index 7dff04756..5f7fcdc21 100644 --- a/cmd/crates/soroban-spec-typescript/src/lib.rs +++ b/cmd/crates/soroban-spec-typescript/src/lib.rs @@ -192,12 +192,12 @@ pub fn entry_to_ts(entry: &Entry) -> String { output = format!(r#"return {output};"#); }; let parse_result_xdr = if return_type == "void" { - r#"parseResultXdr: () => {}"#.to_owned() + r"parseResultXdr: () => {}".to_owned() } else { format!( - r#"parseResultXdr: (xdr): {return_type} => {{ + r"parseResultXdr: (xdr): {return_type} => {{ {output} - }}"# + }}" ) }; let js_name = jsify_name(name); diff --git a/cmd/crates/soroban-test/tests/it/arg_parsing.rs b/cmd/crates/soroban-test/tests/it/arg_parsing.rs index fd685b5a9..c245fd8c9 100644 --- a/cmd/crates/soroban-test/tests/it/arg_parsing.rs +++ b/cmd/crates/soroban-test/tests/it/arg_parsing.rs @@ -90,7 +90,7 @@ fn parse_i256() { #[test] fn parse_bytes() { - let b = from_string_primitive(r#"beefface"#, &ScSpecTypeDef::Bytes).unwrap(); + let b = from_string_primitive(r"beefface", &ScSpecTypeDef::Bytes).unwrap(); assert_eq!( b, ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap())) @@ -100,7 +100,7 @@ fn parse_bytes() { #[test] fn parse_bytes_when_hex_is_all_numbers() { - let b = from_string_primitive(r#"4554"#, &ScSpecTypeDef::Bytes).unwrap(); + let b = from_string_primitive(r"4554", &ScSpecTypeDef::Bytes).unwrap(); assert_eq!( b, ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap())) @@ -111,7 +111,7 @@ fn parse_bytes_when_hex_is_all_numbers() { #[test] fn parse_bytesn() { let b = from_string_primitive( - r#"beefface"#, + r"beefface", &ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 4 }), ) .unwrap(); @@ -124,8 +124,8 @@ fn parse_bytesn() { #[test] fn parse_bytesn_when_hex_is_all_numbers() { - let b = from_string_primitive(r#"4554"#, &ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 2 })) - .unwrap(); + let b = + from_string_primitive(r"4554", &ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 2 })).unwrap(); assert_eq!( b, ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))