From 522ce05c96b8f98e649b02ba932e032f0c4c41e0 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 31 Aug 2023 22:35:30 +0200 Subject: [PATCH 01/35] Bump Rust and Go dependencies --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 20 ++++++++++---------- go.mod | 2 +- go.sum | 1 + 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acf4eadaf..af31dfd1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2564,7 +2564,7 @@ dependencies = [ [[package]] name = "soroban-env-common" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "arbitrary", "crate-git-revision 0.0.6", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "soroban-env-guest" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "soroban-env-common", "static_assertions", @@ -2590,7 +2590,7 @@ dependencies = [ [[package]] name = "soroban-env-host" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "backtrace", "curve25519-dalek 4.0.0", @@ -2616,7 +2616,7 @@ dependencies = [ [[package]] name = "soroban-env-macros" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "itertools", "proc-macro2", @@ -2634,7 +2634,7 @@ version = "0.9.4" [[package]] name = "soroban-ledger-snapshot" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "serde", "serde_json", @@ -2646,7 +2646,7 @@ dependencies = [ [[package]] name = "soroban-native-sdk-macros" version = "0.0.17" -source = "git+https://github.com/stellar/rs-soroban-env?rev=ee3896617f5eeb06d6346e2fbf70cb44d0823b27#ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +source = "git+https://github.com/stellar/rs-soroban-env?rev=eb5a9ba053a7b64a8eff605db625525378f7bea0#eb5a9ba053a7b64a8eff605db625525378f7bea0" dependencies = [ "itertools", "proc-macro2", @@ -2657,7 +2657,7 @@ dependencies = [ [[package]] name = "soroban-sdk" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "arbitrary", "bytes-lit", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -2693,7 +2693,7 @@ dependencies = [ [[package]] name = "soroban-spec" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "base64 0.13.1", "stellar-xdr", @@ -2718,7 +2718,7 @@ dependencies = [ [[package]] name = "soroban-spec-rust" version = "0.9.2" -source = "git+https://github.com/sisuresh/rs-soroban-sdk?rev=e15e552974a1070fc48027384bdee9ae9f539943#e15e552974a1070fc48027384bdee9ae9f539943" +source = "git+https://github.com/stellar/rs-soroban-sdk?rev=18b8fa1a358aa84afd196e2f6d44942798c8f335#18b8fa1a358aa84afd196e2f6d44942798c8f335" dependencies = [ "prettyplease", "proc-macro2", @@ -2854,7 +2854,7 @@ dependencies = [ [[package]] name = "stellar-xdr" version = "0.0.17" -source = "git+https://github.com/stellar/rs-stellar-xdr?rev=e2a9cbf72d94941de1bde6ba34a38e1f49328567#e2a9cbf72d94941de1bde6ba34a38e1f49328567" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=39904e09941046dab61e6e35fc89e31bf2dea1cd#39904e09941046dab61e6e35fc89e31bf2dea1cd" dependencies = [ "arbitrary", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index 876a93f21..0c9bb5419 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,18 +16,18 @@ version = "0.9.4" [workspace.dependencies.soroban-env-host] version = "0.0.17" git = "https://github.com/stellar/rs-soroban-env" -rev = "ee3896617f5eeb06d6346e2fbf70cb44d0823b27" +rev = "eb5a9ba053a7b64a8eff605db625525378f7bea0" [workspace.dependencies.soroban-spec] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" # path = "../rs-soroban-sdk/soroban-spec" [workspace.dependencies.soroban-spec-rust] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" # path = "../rs-soroban-sdk/soroban-spec-rust" [workspace.dependencies.soroban-spec-json] @@ -44,13 +44,13 @@ path = "./cmd/crates/soroban-spec-tools" [workspace.dependencies.soroban-sdk] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" [workspace.dependencies.soroban-ledger-snapshot] version = "0.9.1" -git = "https://github.com/sisuresh/rs-soroban-sdk" -rev = "e15e552974a1070fc48027384bdee9ae9f539943" +git = "https://github.com/stellar/rs-soroban-sdk" +rev = "18b8fa1a358aa84afd196e2f6d44942798c8f335" [workspace.dependencies.soroban-cli] version = "0.9.4" @@ -59,7 +59,7 @@ path = "cmd/soroban-cli" [workspace.dependencies.stellar-xdr] version = "0.0.17" git = "https://github.com/stellar/rs-stellar-xdr" -rev = "e2a9cbf72d94941de1bde6ba34a38e1f49328567" +rev = "39904e09941046dab61e6e35fc89e31bf2dea1cd" default-features = false [workspace.dependencies] diff --git a/go.mod b/go.mod index 4012ee58f..96c0fd60a 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,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-20230829095205-a541e69acb5f + github.com/stellar/go v0.0.0-20230831173646-edbaa5613179 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.12.0 ) diff --git a/go.sum b/go.sum index 34e369b28..4b1e1b3a9 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,7 @@ github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21 h1:OqDQXwl/IvYX4WNJJSQX github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f h1:bNIzaQjSYAOAkbAK9g2zY4dykWEltK5LCuEe4uEoygc= github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= +github.com/stellar/go v0.0.0-20230831173646-edbaa5613179/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 333f364b7bae791826d5ae49711ec6b53b7b536c Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 31 Aug 2023 23:12:59 +0200 Subject: [PATCH 02/35] Start migrating soroban-rpc --- cmd/soroban-rpc/internal/db/ledgerentry.go | 70 +--- .../internal/db/ledgerentry_test.go | 375 ++---------------- .../internal/ingest/ledgerentry.go | 21 +- .../methods/get_latest_ledger_test.go | 2 +- .../internal/methods/get_ledger_entries.go | 2 +- .../internal/methods/get_ledger_entry.go | 2 +- cmd/soroban-rpc/internal/preflight/pool.go | 4 +- .../internal/preflight/preflight.go | 7 +- .../internal/preflight/preflight_test.go | 32 +- .../internal/test/get_ledger_entries_test.go | 4 +- .../internal/test/get_ledger_entry_test.go | 3 +- .../test/simulate_transaction_test.go | 17 +- cmd/soroban-rpc/lib/preflight.h | 3 +- cmd/soroban-rpc/lib/preflight/src/fees.rs | 18 +- .../lib/preflight/src/ledger_storage.rs | 157 +++++--- cmd/soroban-rpc/lib/preflight/src/lib.rs | 4 +- .../lib/preflight/src/state_expiration.rs | 51 +-- go.sum | 5 +- 18 files changed, 209 insertions(+), 568 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 6363e2518..8bb6a68cb 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -3,7 +3,6 @@ package db import ( "context" "database/sql" - "encoding/base64" "fmt" sq "github.com/Masterminds/squirrel" @@ -30,12 +29,11 @@ type LedgerKeyAndEntry struct { type LedgerEntryReadTx interface { GetLatestLedgerSequence() (uint32, error) - GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) + GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) Done() error } type LedgerEntryWriter interface { - ExtendLedgerEntry(key xdr.LedgerKey, expirationLedgerSeq xdr.Uint32) error UpsertLedgerEntry(entry xdr.LedgerEntry) error DeleteLedgerEntry(key xdr.LedgerKey) error } @@ -49,52 +47,6 @@ type ledgerEntryWriter struct { maxBatchSize int } -func (l ledgerEntryWriter) ExtendLedgerEntry(key xdr.LedgerKey, expirationLedgerSeq xdr.Uint32) error { - // TODO: How do we figure out the current expiration? We might need to read - // from the DB, but in the case of creating a new entry and immediately - // extending it, or extending multiple times in the same ledger, the - // expirationLedgerSeq might be buffered but not flushed yet. - if key.Type != xdr.LedgerEntryTypeContractCode && key.Type != xdr.LedgerEntryTypeContractData { - panic("ExtendLedgerEntry can only be used for contract code and data") - } - - encodedKey, err := encodeLedgerKey(l.buffer, key) - if err != nil { - return err - } - - var entry xdr.LedgerEntry - // See if we have a pending (unflushed) update for this key - queued := l.keyToEntryBatch[encodedKey] - if queued != nil { - entry = *queued - } else { - var existing string - // Nothing in the flush buffer. Load the entry from the db - err = sq.StatementBuilder.RunWith(l.stmtCache).Select("entry").From(ledgerEntriesTableName).Where(sq.Eq{"key": encodedKey}).QueryRow().Scan(&existing) - if err == sql.ErrNoRows { - return fmt.Errorf("no entry for key %q in table %q", base64.StdEncoding.EncodeToString([]byte(encodedKey)), ledgerEntriesTableName) - } else if err != nil { - return err - } - // Unmarshal the existing entry - if err := xdr.SafeUnmarshal([]byte(existing), &entry); err != nil { - return err - } - } - - // Update the expiration - switch entry.Data.Type { - case xdr.LedgerEntryTypeContractData: - entry.Data.ContractData.ExpirationLedgerSeq = expirationLedgerSeq - case xdr.LedgerEntryTypeContractCode: - entry.Data.ContractCode.ExpirationLedgerSeq = expirationLedgerSeq - } - - // Marshal the entry back and stage it - return l.UpsertLedgerEntry(entry) -} - func (l ledgerEntryWriter) UpsertLedgerEntry(entry xdr.LedgerEntry) error { // We can do a little extra validation to ensure the entry and key match, // because the key can be derived from the entry. @@ -264,8 +216,8 @@ func (l *ledgerEntryReadTx) getRawLedgerEntries(keys ...string) (map[string]stri return result, nil } -func GetLedgerEntry(tx LedgerEntryReadTx, includeExpired bool, key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) { - keyEntries, err := tx.GetLedgerEntries(includeExpired, key) +func GetLedgerEntry(tx LedgerEntryReadTx, key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) { + keyEntries, err := tx.GetLedgerEntries(key) if err != nil { return false, xdr.LedgerEntry{}, err } @@ -280,7 +232,7 @@ func GetLedgerEntry(tx LedgerEntryReadTx, includeExpired bool, key xdr.LedgerKey } } -func (l *ledgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { +func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { encodedKeys := make([]string, len(keys)) encodedKeyToKey := make(map[string]xdr.LedgerKey, len(keys)) for i, k := range keys { @@ -307,20 +259,6 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.Le if err := xdr.SafeUnmarshal([]byte(encodedEntry), &entry); err != nil { return nil, errors.Wrap(err, "cannot decode ledger entry from DB") } - if !includeExpired { - // Disallow access to entries that have expired. Expiration excludes the - // "current" ledger, which we are building. - if expirationLedgerSeq, ok := entry.Data.ExpirationLedgerSeq(); ok { - latestClosedLedger, err := l.GetLatestLedgerSequence() - if err != nil { - return nil, err - } - currentLedger := latestClosedLedger + 1 - if expirationLedgerSeq < xdr.Uint32(currentLedger) { - continue - } - } - } result = append(result, LedgerKeyAndEntry{key, entry}) } diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index e237a1786..9089ebbf1 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -3,7 +3,6 @@ package db import ( "context" "fmt" - "math" "math/rand" "path" "sync" @@ -31,7 +30,7 @@ func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bo return false, xdr.LedgerEntry{}, 0, err } - present, entry, err := GetLedgerEntry(tx, false, key) + present, entry, err := GetLedgerEntry(tx, key) if err != nil { return false, xdr.LedgerEntry{}, 0, err } @@ -68,16 +67,10 @@ func TestGoldenPath(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: 100, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -89,7 +82,7 @@ func TestGoldenPath(t *testing.T) { assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, obtainedEntry.Data.Type, xdr.LedgerEntryTypeContractData) assert.Equal(t, xdr.Hash{0xca, 0xfe}, *obtainedEntry.Data.ContractData.Contract.ContractId) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) obtainedLedgerSequence, err = NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) assert.NoError(t, err) @@ -100,7 +93,7 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, err) writer = tx.LedgerEntryWriter() eight := xdr.Uint32(8) - entry.Data.ContractData.Body.Data.Val.U32 = &eight + entry.Data.ContractData.Val.U32 = &eight assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -110,7 +103,7 @@ func TestGoldenPath(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, eight, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, eight, *obtainedEntry.Data.ContractData.Val.U32) // Do another round, deleting the ledger entry tx, err = NewReadWriter(db, 150, 15).NewTx(context.Background()) @@ -152,16 +145,10 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: 100, } key, _ := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.DeleteLedgerEntry(key)) @@ -179,253 +166,6 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { assert.Equal(t, ledgerSequence, obtainedLedgerSequence) } -func TestExtendEntry(t *testing.T) { - db := NewTestDB(t) - - // Simulate a ledger which creates a ledger entry, then extends it. - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 24, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(uint32(23))) - - // Extend the entry's expiration - tx, err = NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer = tx.LedgerEntryWriter() - assert.NoError(t, writer.ExtendLedgerEntry(key, 32)) - ledgerSequence := uint32(24) - assert.NoError(t, tx.Commit(ledgerSequence)) - - // Make sure that the ledger number was submitted - obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.NoError(t, err) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - - // And that the entry was updated - present, resultEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.True(t, present) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, xdr.Uint32(32), resultEntry.Data.ContractData.ExpirationLedgerSeq) -} - -func TestCreateAndImmediatelyExtendEntry(t *testing.T) { - db := NewTestDB(t) - - // Simulate a ledger which creates a ledger entry, then extends it. - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 24, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - - // Immediately Extend the entry's expiration - assert.NoError(t, writer.ExtendLedgerEntry(key, 32)) - - // Commit everything at once - ledgerSequence := uint32(24) - assert.NoError(t, tx.Commit(ledgerSequence)) - - // Check that the entry was updated - present, resultEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.True(t, present) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - - // Check that the extension was applied - assert.Equal(t, xdr.Uint32(32), resultEntry.Data.ContractData.ExpirationLedgerSeq) -} - -func TestExtendNonExistentLedgerEntry(t *testing.T) { - db := NewTestDB(t) - - four := xdr.Uint32(4) - key := xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeContractData, - ContractData: &xdr.LedgerKeyContractData{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, - }, - } - - // Try to extend the entry's expiration - tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - err = writer.ExtendLedgerEntry(key, 32) - assert.ErrorContains(t, err, "no entry for key") -} - -func TestGetLedgerEntryHidesExpiredContractDataEntries(t *testing.T) { - db := NewTestDB(t) - - // Check that we get an empty DB error - _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.Equal(t, ErrEmptyDB, err) - - // Start filling the DB with a single entry (enforce flushing right away) - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, - }, - ExpirationLedgerSeq: 23, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(20)) - - for _, c := range []struct { - ledgerSequence uint32 - expected bool - }{ - {21, true}, - {22, true}, - {23, false}, - {24, false}, - } { - // ffwd to the ledger sequence - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - // Close the ledger N - assert.NoError(t, tx.Commit(c.ledgerSequence)) - - // Now, ledger N is our latestClosedLedger, so any preflights should act as - // though it is currently ledger N+1 - - // Try to read the entry back, and check it disappears when expected - present, _, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.Equal(t, c.ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, c.expected, present, "ledger sequence %d", c.ledgerSequence) - } -} - -func TestGetLedgerEntryHidesExpiredContractCodeEntries(t *testing.T) { - db := NewTestDB(t) - - // Check that we get an empty DB error - _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.Equal(t, ErrEmptyDB, err) - - // Start filling the DB with a single entry (enforce flushing right away) - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - source := []byte("some code") - code := xdr.ContractCodeEntry{ - Hash: xdr.Hash{0xca, 0xfe}, - Body: xdr.ContractCodeEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Code: &source, - }, - ExpirationLedgerSeq: 23, - } - key, entry := getContractCodeLedgerEntry(t, code) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - assert.NoError(t, tx.Commit(20)) - - for _, c := range []struct { - ledgerSequence uint32 - expected bool - }{ - {21, true}, - {22, true}, - {23, false}, - {24, false}, - } { - // ffwd to the ledger sequence - tx, err := NewReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - // Close the ledger N - assert.NoError(t, tx.Commit(c.ledgerSequence)) - - // Now, ledger N is our latestClosedLedger, so any preflights should act as - // though it is currently ledger N+1 - - // Try to read the entry back, and check it disappears when expected - present, _, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.Equal(t, c.ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, c.expected, present, "ledger sequence %d", c.ledgerSequence) - } -} - func getContractDataLedgerEntry(t require.TestingT, data xdr.ContractDataEntry) (xdr.LedgerKey, xdr.LedgerEntry) { entry := xdr.LedgerEntry{ LastModifiedLedgerSeq: 1, @@ -436,22 +176,7 @@ func getContractDataLedgerEntry(t require.TestingT, data xdr.ContractDataEntry) Ext: xdr.LedgerEntryExt{}, } var key xdr.LedgerKey - err := key.SetContractData(data.Contract, data.Key, data.Durability, data.Body.BodyType) - require.NoError(t, err) - return key, entry -} - -func getContractCodeLedgerEntry(t require.TestingT, code xdr.ContractCodeEntry) (xdr.LedgerKey, xdr.LedgerEntry) { - entry := xdr.LedgerEntry{ - LastModifiedLedgerSeq: 1, - Data: xdr.LedgerEntryData{ - Type: xdr.LedgerEntryTypeContractCode, - ContractCode: &code, - }, - Ext: xdr.LedgerEntryExt{}, - } - var key xdr.LedgerKey - err := key.SetContractCode(code.Hash) + err := key.SetContractData(data.Contract, data.Key, data.Durability) require.NoError(t, err) return key, entry } @@ -481,17 +206,10 @@ func TestReadTxsDuringWriteTx(t *testing.T) { Type: xdr.ScValTypeScvU32, U32: &four, }, - Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -504,14 +222,14 @@ func TestReadTxsDuringWriteTx(t *testing.T) { _, err = readTx1.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx1, false, key) + present, _, err := GetLedgerEntry(readTx1, key) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx1.Done()) _, err = readTx2.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err = GetLedgerEntry(readTx2, false, key) + present, _, err = GetLedgerEntry(readTx2, key) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx2.Done()) @@ -527,7 +245,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) } // Make sure that a write transaction can happen while multiple read transactions are ongoing, @@ -566,16 +284,10 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { U32: &four, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) @@ -588,7 +300,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, false, key) + present, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -600,7 +312,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, false, key) + present, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -614,7 +326,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { assert.NoError(t, readTx.Done()) @@ -643,16 +355,10 @@ func TestConcurrentReadersAndWriter(t *testing.T) { U32: &val, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &val, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &val, }, - ExpirationLedgerSeq: math.MaxUint32, } } rw := NewReadWriter(db, 10, 15) @@ -685,7 +391,6 @@ func TestConcurrentReadersAndWriter(t *testing.T) { U32: &val, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, } for { @@ -703,7 +408,7 @@ func TestConcurrentReadersAndWriter(t *testing.T) { // All entries should be found once the first write commit is done assert.True(t, found) logMessageCh <- fmt.Sprintf("reader %d: for ledger %d", keyVal, ledger) - assert.Equal(t, xdr.Uint32(keyVal), *ledgerEntry.Data.ContractData.Body.Data.Val.U32) + assert.Equal(t, xdr.Uint32(keyVal), *ledgerEntry.Data.ContractData.Val.U32) } time.Sleep(time.Duration(rand.Int31n(30)) * time.Millisecond) } @@ -749,16 +454,10 @@ func benchmarkLedgerEntry(b *testing.B, cached bool, includeExpired bool) { U32: &keyUint32, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &keyUint32, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &keyUint32, }, - ExpirationLedgerSeq: math.MaxUint32, } key, entry := getContractDataLedgerEntry(b, data) tx, err := NewReadWriter(db, 150, 15).NewTx(context.Background()) @@ -780,7 +479,7 @@ func benchmarkLedgerEntry(b *testing.B, cached bool, includeExpired bool) { assert.NoError(b, err) for i := 0; i < numQueriesPerOp; i++ { b.StartTimer() - found, _, err := GetLedgerEntry(readTx, includeExpired, key) + found, _, err := GetLedgerEntry(readTx, key) b.StopTimer() assert.NoError(b, err) assert.True(b, found) @@ -808,16 +507,10 @@ func BenchmarkLedgerUpdate(b *testing.B) { U32: &keyUint32, }, Durability: xdr.ContractDataDurabilityPersistent, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &keyUint32, - }, - }, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &keyUint32, }, - ExpirationLedgerSeq: math.MaxUint32, } _, entry := getContractDataLedgerEntry(b, data) const numEntriesPerOp = 3500 diff --git a/cmd/soroban-rpc/internal/ingest/ledgerentry.go b/cmd/soroban-rpc/internal/ingest/ledgerentry.go index b10abed69..cc00b884b 100644 --- a/cmd/soroban-rpc/internal/ingest/ledgerentry.go +++ b/cmd/soroban-rpc/internal/ingest/ledgerentry.go @@ -54,25 +54,6 @@ func ingestLedgerEntryChange(writer db.LedgerEntryWriter, change ingest.Change) } return writer.DeleteLedgerEntry(ledgerKey) } else { - ledgerKey, err := xdr.GetLedgerKeyFromData(change.Post.Data) - if err != nil { - return err - } - if isExtension, expirationLedgerSeq := ledgerEntryIsExtension(ledgerKey, change.Post); isExtension { - return writer.ExtendLedgerEntry(ledgerKey, expirationLedgerSeq) - } else { - return writer.UpsertLedgerEntry(*change.Post) - } - } -} - -func ledgerEntryIsExtension(ledgerKey xdr.LedgerKey, entry *xdr.LedgerEntry) (bool, xdr.Uint32) { - switch ledgerKey.Type { - case xdr.LedgerEntryTypeContractCode: - return entry.Data.ContractCode.Body.BodyType == xdr.ContractEntryBodyTypeExpirationExtension, entry.Data.ContractCode.ExpirationLedgerSeq - case xdr.LedgerEntryTypeContractData: - return entry.Data.ContractData.Body.BodyType == xdr.ContractEntryBodyTypeExpirationExtension, entry.Data.ContractData.ExpirationLedgerSeq - default: - return false, 0 + return writer.UpsertLedgerEntry(*change.Post) } } diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index 1beb15b0a..124ec6a33 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -42,7 +42,7 @@ func (entryReaderTx ConstantLedgerEntryReaderTx) GetLatestLedgerSequence() (uint return expectedLatestLedgerSequence, nil } -func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { return nil, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index f4d14d223..b7474e323 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -78,7 +78,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn } ledgerEntryResults := make([]LedgerEntryResult, 0, len(ledgerKeys)) - ledgerKeysAndEntries, err := tx.GetLedgerEntries(false, ledgerKeys...) + ledgerKeysAndEntries, err := tx.GetLedgerEntries(ledgerKeys...) if err != nil { logger.WithError(err).WithField("request", request). Info("could not obtain ledger entryies from storage") diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index c44fd3f64..4ab908a68 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -61,7 +61,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr } } - present, ledgerEntry, err := db.GetLedgerEntry(tx, false, key) + present, ledgerEntry, err := db.GetLedgerEntry(tx, key) if err != nil { logger.WithError(err).WithField("request", request). Info("could not obtain ledger entry from storage") diff --git a/cmd/soroban-rpc/internal/preflight/pool.go b/cmd/soroban-rpc/internal/preflight/pool.go index e23754c16..ff591ddda 100644 --- a/cmd/soroban-rpc/internal/preflight/pool.go +++ b/cmd/soroban-rpc/internal/preflight/pool.go @@ -127,9 +127,9 @@ type metricsLedgerEntryWrapper struct { ledgerEntriesFetched uint32 } -func (m *metricsLedgerEntryWrapper) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (m *metricsLedgerEntryWrapper) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { startTime := time.Now() - entries, err := m.LedgerEntryReadTx.GetLedgerEntries(includeExpired, keys...) + entries, err := m.LedgerEntryReadTx.GetLedgerEntries(keys...) atomic.AddUint64(&m.totalDurationMs, uint64(time.Since(startTime).Milliseconds())) atomic.AddUint32(&m.ledgerEntriesFetched, uint32(len(keys))) return entries, err diff --git a/cmd/soroban-rpc/internal/preflight/preflight.go b/cmd/soroban-rpc/internal/preflight/preflight.go index d7414415c..029a6b7da 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight.go +++ b/cmd/soroban-rpc/internal/preflight/preflight.go @@ -39,14 +39,14 @@ type snapshotSourceHandle struct { // It's used by the Rust preflight code to obtain ledger entries. // //export SnapshotSourceGet -func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey C.xdr_t, includeExpired C.int) C.xdr_t { +func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey C.xdr_t) C.xdr_t { h := cgo.Handle(handle).Value().(snapshotSourceHandle) ledgerKeyXDR := GoXDR(cLedgerKey) var ledgerKey xdr.LedgerKey if err := xdr.SafeUnmarshal(ledgerKeyXDR, &ledgerKey); err != nil { panic(err) } - present, entry, err := db.GetLedgerEntry(h.readTx, includeExpired != 0, ledgerKey) + present, entry, err := db.GetLedgerEntry(h.readTx, ledgerKey) if err != nil { h.logger.WithError(err).Error("SnapshotSourceGet(): GetLedgerEntry() failed") return C.xdr_t{} @@ -181,7 +181,7 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro } sourceAccountCXDR := CXDR(sourceAccountXDR) - hasConfig, stateExpirationConfig, err := db.GetLedgerEntry(params.LedgerEntryReadTx, false, xdr.LedgerKey{ + hasConfig, stateExpirationConfig, err := db.GetLedgerEntry(params.LedgerEntryReadTx, xdr.LedgerKey{ Type: xdr.LedgerEntryTypeConfigSetting, ConfigSetting: &xdr.LedgerKeyConfigSetting{ ConfigSettingId: xdr.ConfigSettingIdConfigSettingStateExpiration, @@ -210,7 +210,6 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro min_temp_entry_expiration: C.uint(stateExpiration.MinTempEntryExpiration), min_persistent_entry_expiration: C.uint(stateExpiration.MinPersistentEntryExpiration), max_entry_expiration: C.uint(stateExpiration.MaxEntryExpiration), - auto_bump_ledgers: C.uint(stateExpiration.AutoBumpLedgers), } handle := cgo.NewHandle(snapshotSourceHandle{params.LedgerEntryReadTx, params.Logger}) diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index 9d4ea3c26..b7cb8045f 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -44,22 +44,15 @@ var mockLedgerEntries = []xdr.LedgerEntry{ Key: xdr.ScVal{ Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, - Durability: xdr.ContractDataDurabilityPersistent, - ExpirationLedgerSeq: 100000, - Body: xdr.ContractDataEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Data: &xdr.ContractDataEntryData{ - Flags: 0, - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvContractInstance, - Instance: &xdr.ScContractInstance{ - Executable: xdr.ContractExecutable{ - Type: xdr.ContractExecutableTypeContractExecutableWasm, - WasmHash: &mockContractHash, - }, - Storage: nil, - }, + Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvContractInstance, + Instance: &xdr.ScContractInstance{ + Executable: xdr.ContractExecutable{ + Type: xdr.ContractExecutableTypeContractExecutableWasm, + WasmHash: &mockContractHash, }, + Storage: nil, }, }, }, @@ -71,11 +64,7 @@ var mockLedgerEntries = []xdr.LedgerEntry{ Type: xdr.LedgerEntryTypeContractCode, ContractCode: &xdr.ContractCodeEntry{ Hash: mockContractHash, - Body: xdr.ContractCodeEntryBody{ - BodyType: xdr.ContractEntryBodyTypeDataEntry, - Code: &helloWorldContract, - }, - ExpirationLedgerSeq: 20000, + Code: helloWorldContract, }, }, }, @@ -169,7 +158,6 @@ var mockLedgerEntries = []xdr.LedgerEntry{ MaxEntryExpiration: 100, MinTempEntryExpiration: 100, MinPersistentEntryExpiration: 100, - AutoBumpLedgers: 100, PersistentRentRateDenominator: 100, TempRentRateDenominator: 100, MaxEntriesToExpire: 100, @@ -216,7 +204,7 @@ var helloWorldContract = func() []byte { type inMemoryLedgerEntryReadTx map[string]xdr.LedgerEntry -func (m inMemoryLedgerEntryReadTx) GetLedgerEntries(includeExpired bool, keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { +func (m inMemoryLedgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]db.LedgerKeyAndEntry, error) { result := make([]db.LedgerKeyAndEntry, 0, len(keys)) for _, key := range keys { serializedKey, err := key.MarshalBinaryBase64() diff --git a/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go b/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go index cf0d3d97d..d8154b3db 100644 --- a/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go +++ b/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go @@ -38,7 +38,6 @@ func TestGetLedgerEntriesNotFound(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -135,7 +134,6 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -155,6 +153,6 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { var firstEntry xdr.LedgerEntryData require.NoError(t, xdr.SafeUnmarshalBase64(result.Entries[0].XDR, &firstEntry)) - require.Equal(t, testContract, *firstEntry.MustContractCode().Body.Code) + require.Equal(t, testContract, firstEntry.MustContractCode().Code) require.Equal(t, contractKeyB64, result.Entries[0].Key) } diff --git a/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go b/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go index 85a3b76d7..4b8e17572 100644 --- a/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go +++ b/cmd/soroban-rpc/internal/test/get_ledger_entry_test.go @@ -37,7 +37,6 @@ func TestGetLedgerEntryNotFound(t *testing.T) { Type: xdr.ScValTypeScvLedgerKeyContractInstance, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, }) require.NoError(t, err) @@ -111,5 +110,5 @@ func TestGetLedgerEntrySucceeds(t *testing.T) { assert.GreaterOrEqual(t, result.LatestLedger, result.LastModifiedLedger) var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(result.XDR, &entry)) - assert.Equal(t, testContract, *entry.MustContractCode().Body.Code) + assert.Equal(t, testContract, entry.MustContractCode().Code) } diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index f73c66702..99fdaacfe 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -233,8 +233,7 @@ func TestSimulateTransactionSucceeds(t *testing.T) { { Type: xdr.LedgerEntryTypeContractCode, ContractCode: &xdr.LedgerKeyContractCode{ - Hash: xdr.Hash(testContractId), - BodyType: xdr.ContractEntryBodyTypeDataEntry, + Hash: xdr.Hash(testContractId), }, }, }, @@ -691,7 +690,6 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { Sym: &counterSym, }, Durability: xdr.ContractDataDurabilityPersistent, - BodyType: xdr.ContractEntryBodyTypeDataEntry, }, } keyB64, err := xdr.MarshalBase64(key) @@ -704,8 +702,9 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - initialExpirationSeq, ok := entry.ExpirationLedgerSeq() - assert.True(t, ok) + // TODO: fix this test + // initialExpirationSeq, ok := entry.ExpirationLedgerSeq() + // assert.True(t, ok) params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ SourceAccount: &account, @@ -737,10 +736,10 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - newExpirationSeq, ok := entry.ExpirationLedgerSeq() - assert.True(t, ok) + // newExpirationSeq, ok := entry.ExpirationLedgerSeq() + // assert.True(t, ok) - assert.Greater(t, newExpirationSeq, initialExpirationSeq) + // assert.Greater(t, newExpirationSeq, initialExpirationSeq) // Wait until it expires waitForExpiration := func() { @@ -753,7 +752,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { break } assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - t.Log("waiting for ledger entry to expire at ledger", entry.MustContractData().ExpirationLedgerSeq) + // t.Log("waiting for ledger entry to expire at ledger", entry.MustContractData().ExpirationLedgerSeq) time.Sleep(time.Second) } require.True(t, expired) diff --git a/cmd/soroban-rpc/lib/preflight.h b/cmd/soroban-rpc/lib/preflight.h index ad958cd92..fc01d9f45 100644 --- a/cmd/soroban-rpc/lib/preflight.h +++ b/cmd/soroban-rpc/lib/preflight.h @@ -12,7 +12,6 @@ typedef struct ledger_info_t { uint32_t min_temp_entry_expiration; uint32_t min_persistent_entry_expiration; uint32_t max_entry_expiration; - uint32_t auto_bump_ledgers; } ledger_info_t; typedef struct xdr_t { @@ -52,7 +51,7 @@ preflight_result_t *preflight_footprint_expiration_op(uintptr_t handle, // Go // LedgerKey XDR to LedgerEntry XDR -extern xdr_t SnapshotSourceGet(uintptr_t handle, xdr_t ledger_key, int include_expired); +extern xdr_t SnapshotSourceGet(uintptr_t handle, xdr_t ledger_key); void free_preflight_result(preflight_result_t *result); diff --git a/cmd/soroban-rpc/lib/preflight/src/fees.rs b/cmd/soroban-rpc/lib/preflight/src/fees.rs index b016ba5a8..211e467ea 100644 --- a/cmd/soroban-rpc/lib/preflight/src/fees.rs +++ b/cmd/soroban-rpc/lib/preflight/src/fees.rs @@ -361,12 +361,13 @@ fn compute_bump_footprint_rent_changes( let mut rent_changes: Vec = Vec::with_capacity(footprint.read_only.len()); for key in (&footprint).read_only.as_vec() { - let unmodified_entry = ledger_storage + let unmodified_entry_and_expiration = ledger_storage .get(key, false) .with_context(|| format!("cannot find bump footprint ledger entry with key {key:?}"))?; - let size = (key.to_xdr()?.len() + unmodified_entry.to_xdr()?.len()) as u32; - let expirable_entry: Box = - (&unmodified_entry).try_into().map_err(|e: String| { + let size = (key.to_xdr()?.len() + unmodified_entry_and_expiration.0.to_xdr()?.len()) as u32; + let expirable_entry: Box = (&unmodified_entry_and_expiration) + .try_into() + .map_err(|e: String| { Error::msg(e.clone()).context("incorrect ledger entry type in footprint") })?; let new_expiration_ledger = current_ledger_seq + ledgers_to_expire; @@ -452,12 +453,13 @@ fn compute_restore_footprint_rent_changes( let mut rent_changes: Vec = Vec::with_capacity(footprint.read_write.len()); for key in footprint.read_write.as_vec() { - let unmodified_entry = ledger_storage.get(key, true).with_context(|| { + let unmodified_entry_and_expiration = ledger_storage.get(key, true).with_context(|| { format!("cannot find restore footprint ledger entry with key {key:?}") })?; - let size = (key.to_xdr()?.len() + unmodified_entry.to_xdr()?.len()) as u32; - let expirable_entry: Box = - (&unmodified_entry).try_into().map_err(|e: String| { + let size = (key.to_xdr()?.len() + unmodified_entry_and_expiration.0.to_xdr()?.len()) as u32; + let expirable_entry: Box = (&unmodified_entry_and_expiration) + .try_into() + .map_err(|e: String| { Error::msg(e.clone()).context("incorrect ledger entry type in footprint") })?; ensure!( diff --git a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs index 4159dda2e..378794352 100644 --- a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs +++ b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs @@ -1,11 +1,13 @@ +use sha2::Digest; use soroban_env_host::storage::SnapshotSource; use soroban_env_host::xdr::ContractDataDurability::Persistent; use soroban_env_host::xdr::{ - ConfigSettingEntry, ConfigSettingId, Error as XdrError, LedgerEntry, LedgerEntryData, - LedgerKey, LedgerKeyConfigSetting, ReadXdr, ScError, ScErrorCode, WriteXdr, + ConfigSettingEntry, ConfigSettingId, Error as XdrError, ExpirationEntry, Hash, LedgerEntry, + LedgerEntryData, LedgerKey, LedgerKeyConfigSetting, LedgerKeyExpiration, ReadXdr, ScError, + ScErrorCode, WriteXdr, }; use soroban_env_host::HostError; -use state_expiration::{restore_ledger_entry, ExpirableLedgerEntry}; +use state_expiration::{get_restored_ledger_sequence, has_expired, ExpirableLedgerEntry}; use std::cell::RefCell; use std::collections::HashSet; use std::convert::TryInto; @@ -19,11 +21,7 @@ extern "C" { // Free Strings returned from Go functions fn FreeGoXDR(xdr: CXDR); // LedgerKey XDR in base64 string to LedgerEntry XDR in base64 string - fn SnapshotSourceGet( - handle: libc::uintptr_t, - ledger_key: CXDR, - include_expired: libc::c_int, - ) -> CXDR; + fn SnapshotSourceGet(handle: libc::uintptr_t, ledger_key: CXDR) -> CXDR; } #[derive(thiserror::Error, Debug)] @@ -38,6 +36,8 @@ pub(crate) enum Error { Utf8Error(#[from] Utf8Error), #[error("unexpected config ledger entry for setting_id {setting_id}")] UnexpectedConfigLedgerEntry { setting_id: String }, + #[error("unexpected ledger entry type ({ledger_entry_type}) for expiration ledger key")] + UnexpectedLedgerEntryTypeForExpirationKey { ledger_entry_type: String }, } impl From for HostError { @@ -51,54 +51,54 @@ impl From for HostError { } struct EntryRestoreTracker { - current_ledger_seq: u32, min_persistent_entry_expiration: u32, // RefCell is needed to mutate the hashset inside SnapshotSource::get(), which is an immutable method ledger_keys_requiring_restore: RefCell>, } impl EntryRestoreTracker { - pub(crate) fn track_and_restore(&self, key: &LedgerKey, entry: &mut LedgerEntry) { - if self.track(key, entry) { - restore_ledger_entry( - entry, - self.current_ledger_seq, - self.min_persistent_entry_expiration, - ); - } - } - - pub(crate) fn track(&self, key: &LedgerKey, entry: &LedgerEntry) -> bool { - let expirable_entry: Box = match entry.try_into() { + // Tracks ledger entries which need to be restored and returns its expiration as it was restored + pub(crate) fn track_and_restore( + &self, + current_ledger_sequence: u32, + key: &LedgerKey, + entry_and_expiration: &(LedgerEntry, Option), + ) -> Option { + let expirable_entry: Box = match entry_and_expiration.try_into() { Ok(e) => e, Err(_) => { // Nothing to track, the entry isn't expirable - return false; + return None; } }; if expirable_entry.durability() != Persistent - || !expirable_entry.has_expired(self.current_ledger_seq) + || !expirable_entry.has_expired(current_ledger_sequence) { // Nothing to track, the entry isn't persistent (and thus not restorable) or // it hasn't expired - return false; + return Some(expirable_entry.expiration_ledger_seq()); } self.ledger_keys_requiring_restore .borrow_mut() .insert(key.clone()); - true + Some(get_restored_ledger_sequence( + current_ledger_sequence, + self.min_persistent_entry_expiration, + )) } } pub(crate) struct LedgerStorage { golang_handle: libc::uintptr_t, + current_ledger_sequence: u32, restore_tracker: Option, } impl LedgerStorage { - pub(crate) fn new(golang_handle: libc::uintptr_t) -> Self { + pub(crate) fn new(golang_handle: libc::uintptr_t, current_ledger_sequence: u32) -> Self { LedgerStorage { golang_handle, + current_ledger_sequence, restore_tracker: None, } } @@ -110,6 +110,7 @@ impl LedgerStorage { // First, we initialize it without the tracker, to get the minimum restore ledger from the network let mut ledger_storage = LedgerStorage { golang_handle, + current_ledger_sequence, restore_tracker: None, }; let setting_id = ConfigSettingId::StateExpiration; @@ -122,27 +123,19 @@ impl LedgerStorage { }; // Now that we have the state expiration config, we can build the tracker ledger_storage.restore_tracker = Some(EntryRestoreTracker { - current_ledger_seq: current_ledger_sequence, ledger_keys_requiring_restore: RefCell::new(HashSet::new()), min_persistent_entry_expiration: state_expiration.min_persistent_entry_expiration, }); Ok(ledger_storage) } - pub(crate) fn get(&self, key: &LedgerKey, include_expired: bool) -> Result { - let xdr = self.get_xdr(key, include_expired)?; - let entry = LedgerEntry::from_xdr(xdr)?; - Ok(entry) - } - - pub(crate) fn get_xdr(&self, key: &LedgerKey, include_expired: bool) -> Result, Error> { - let mut key_xdr = key.to_xdr()?; + // Get the XDR, regardless of expiration + fn get_xdr_internal(&self, key_xdr: &mut Vec) -> Result, Error> { let key_c_xdr = CXDR { xdr: key_xdr.as_mut_ptr(), len: key_xdr.len(), }; - let res = - unsafe { SnapshotSourceGet(self.golang_handle, key_c_xdr, include_expired.into()) }; + let res = unsafe { SnapshotSourceGet(self.golang_handle, key_c_xdr) }; if res.xdr.is_null() { return Err(Error::NotFound); } @@ -151,6 +144,56 @@ impl LedgerStorage { Ok(v) } + pub(crate) fn get( + &self, + key: &LedgerKey, + include_expired: bool, + ) -> Result<(LedgerEntry, Option), Error> { + let mut key_xdr = key.to_xdr()?; + let xdr = self.get_xdr_internal(&mut key_xdr)?; + + let expiration_seq = match key { + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { + let key_hash: [u8; 32] = sha2::Sha256::digest(key_xdr).into(); + let expiration_key = LedgerKey::Expiration(LedgerKeyExpiration { + key_hash: Hash(key_hash), + }); + let mut expiration_key_xdr = expiration_key.to_xdr()?; + let expiration_entry_xdr = self.get_xdr_internal(&mut expiration_key_xdr)?; + let expiration_entry = LedgerEntry::from_xdr(expiration_entry_xdr)?; + if let LedgerEntryData::Expiration(ExpirationEntry { + expiration_ledger_seq: expiration_ledger_seq, + .. + }) = expiration_entry.data + { + Some(expiration_ledger_seq) + } else { + return Err(Error::UnexpectedLedgerEntryTypeForExpirationKey { + ledger_entry_type: expiration_entry.data.name().to_string(), + }); + } + } + _ => None, + }; + + if !include_expired + && expiration_seq.is_some() + && has_expired(expiration_seq.unwrap(), self.current_ledger_sequence) + { + return Err(Error::NotFound); + } + + let entry = LedgerEntry::from_xdr(xdr)?; + Ok((entry, expiration_seq)) + } + + pub(crate) fn get_xdr(&self, key: &LedgerKey, include_expired: bool) -> Result, Error> { + // TODO: this can be optimized since for entry types other than ContractCode/ContractData, + // they don't need to be deserialized and serialized again + let (entry, _) = self.get(key, include_expired)?; + Ok(entry.to_xdr()?) + } + pub(crate) fn get_configuration_setting( &self, setting_id: ConfigSettingId, @@ -159,10 +202,13 @@ impl LedgerStorage { config_setting_id: setting_id, }); match self.get(&key, false)? { - LedgerEntry { - data: LedgerEntryData::ConfigSetting(cs), - .. - } => Ok(cs), + ( + LedgerEntry { + data: LedgerEntryData::ConfigSetting(cs), + .. + }, + _, + ) => Ok(cs), _ => Err(Error::UnexpectedConfigLedgerEntry { setting_id: setting_id.name().to_string(), }), @@ -178,25 +224,28 @@ impl LedgerStorage { } impl SnapshotSource for LedgerStorage { - fn get(&self, key: &Rc) -> Result, HostError> { - let mut entry = ::get(self, key, self.restore_tracker.is_some())?; + fn get(&self, key: &Rc) -> Result<(Rc, Option), HostError> { + let mut entry_and_expiration = + ::get(self, key, self.restore_tracker.is_some())?; if let Some(ref tracker) = self.restore_tracker { - // If the entry expired, we modify it to make it seem like it was restored - tracker.track_and_restore(key, &mut entry); + // If the entry expired, we modify the expiration to make it seem like it was restored + entry_and_expiration.1 = + tracker.track_and_restore(self.current_ledger_sequence, key, &entry_and_expiration); } - Ok(entry.into()) + Ok((entry_and_expiration.0.into(), entry_and_expiration.1)) } fn has(&self, key: &Rc) -> Result { - let entry = match ::get(self, key, self.restore_tracker.is_some()) { - Err(e) => match e { - Error::NotFound => return Ok(false), - _ => return Err(HostError::from(e)), - }, - Ok(le) => le, - }; + let entry_and_expiration = + match ::get(self, key, self.restore_tracker.is_some()) { + Err(e) => match e { + Error::NotFound => return Ok(false), + _ => return Err(HostError::from(e)), + }, + Ok(le) => le, + }; if let Some(ref tracker) = self.restore_tracker { - tracker.track(key, &entry); + _ = tracker.track_and_restore(self.current_ledger_sequence, key, &entry_and_expiration); } Ok(true) } diff --git a/cmd/soroban-rpc/lib/preflight/src/lib.rs b/cmd/soroban-rpc/lib/preflight/src/lib.rs index 5f913f044..4df90fb64 100644 --- a/cmd/soroban-rpc/lib/preflight/src/lib.rs +++ b/cmd/soroban-rpc/lib/preflight/src/lib.rs @@ -33,7 +33,6 @@ pub struct CLedgerInfo { pub min_temp_entry_expiration: u32, pub min_persistent_entry_expiration: u32, pub max_entry_expiration: u32, - pub autobump_ledgers: u32, } impl From for LedgerInfo { @@ -48,7 +47,6 @@ impl From for LedgerInfo { min_temp_entry_expiration: c.min_temp_entry_expiration, min_persistent_entry_expiration: c.min_persistent_entry_expiration, max_entry_expiration: c.max_entry_expiration, - autobump_ledgers: c.autobump_ledgers, } } } @@ -196,7 +194,7 @@ fn preflight_footprint_expiration_op_or_maybe_panic( ) -> Result { let op_body = OperationBody::from_xdr(from_c_xdr(op_body)).unwrap(); let footprint = LedgerFootprint::from_xdr(from_c_xdr(footprint)).unwrap(); - let ledger_storage = &LedgerStorage::new(handle); + let ledger_storage = &LedgerStorage::new(handle, current_ledger_seq); let result = preflight::preflight_footprint_expiration_op( ledger_storage, bucket_list_size, diff --git a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs index 9efd330c8..f7690a39b 100644 --- a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs +++ b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs @@ -8,62 +8,63 @@ pub(crate) trait ExpirableLedgerEntry { fn durability(&self) -> ContractDataDurability; fn expiration_ledger_seq(&self) -> u32; fn has_expired(&self, current_ledger_seq: u32) -> bool { - current_ledger_seq > self.expiration_ledger_seq() + has_expired(current_ledger_seq, self.expiration_ledger_seq()) } } -impl ExpirableLedgerEntry for &ContractCodeEntry { +impl ExpirableLedgerEntry for (&ContractCodeEntry, u32) { fn durability(&self) -> ContractDataDurability { Persistent } fn expiration_ledger_seq(&self) -> u32 { - self.expiration_ledger_seq + self.1 } } -impl ExpirableLedgerEntry for &ContractDataEntry { +impl ExpirableLedgerEntry for (&ContractDataEntry, u32) { fn durability(&self) -> ContractDataDurability { - self.durability + self.0.durability } fn expiration_ledger_seq(&self) -> u32 { - self.expiration_ledger_seq + self.1 } } -impl<'a> TryInto> for &'a LedgerEntry { +// Convert a ledger entry and its expiration into an ExpirableLedgerEntry +impl<'a> TryInto> for &'a (LedgerEntry, Option) { type Error = String; fn try_into(self) -> Result, Self::Error> { - match &self.data { - LedgerEntryData::ContractData(d) => Ok(Box::new(d)), - LedgerEntryData::ContractCode(c) => Ok(Box::new(c)), + match (&self.0.data, self.1) { + (LedgerEntryData::ContractData(d), Some(expiration_seq)) => { + Ok(Box::new((d, expiration_seq))) + } + (LedgerEntryData::ContractCode(c), Some(expiration_seq)) => { + Ok(Box::new((c, expiration_seq))) + } + (LedgerEntryData::ContractData(_) | LedgerEntryData::ContractCode(_), _) => { + Err(format!( + "missing expiration for expirable ledger entry ({})", + self.0.data.name() + )) + } _ => Err(format!( "ledger entry type ({}) is not expirable", - self.data.name() + self.0.data.name() )), } } } +pub(crate) fn has_expired(expiration_ledger_seq: u32, current_ledger_seq: u32) -> bool { + current_ledger_seq > expiration_ledger_seq +} + pub(crate) fn get_restored_ledger_sequence( current_ledger_seq: u32, min_persistent_entry_expiration: u32, ) -> u32 { return current_ledger_seq + min_persistent_entry_expiration - 1; } - -pub(crate) fn restore_ledger_entry( - ledger_entry: &mut LedgerEntry, - current_ledger_seq: u32, - min_persistent_entry_expiration: u32, -) { - let new_ledger_seq = - get_restored_ledger_sequence(current_ledger_seq, min_persistent_entry_expiration); - match &mut ledger_entry.data { - LedgerEntryData::ContractData(d) => d.expiration_ledger_seq = new_ledger_seq, - LedgerEntryData::ContractCode(c) => c.expiration_ledger_seq = new_ledger_seq, - _ => (), - } -} diff --git a/go.sum b/go.sum index 4b1e1b3a9..535997212 100644 --- a/go.sum +++ b/go.sum @@ -186,10 +186,7 @@ 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.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21 h1:OqDQXwl/IvYX4WNJJSQXxbArjjeCcoHgfnaZPndbc58= -github.com/stellar/go v0.0.0-20230825090709-76e07a4dbd21/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= -github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f h1:bNIzaQjSYAOAkbAK9g2zY4dykWEltK5LCuEe4uEoygc= -github.com/stellar/go v0.0.0-20230829095205-a541e69acb5f/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= +github.com/stellar/go v0.0.0-20230831173646-edbaa5613179 h1:3IbKtLXZzQsP0IvQrbToHsSYr7idl9PWenG08iTfyAI= github.com/stellar/go v0.0.0-20230831173646-edbaa5613179/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= From f3ddedbaef0e3fea9a90e8916f55f92312cdbf60 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 01:42:48 +0200 Subject: [PATCH 03/35] Fix get_ledger_changes() invocation --- cmd/soroban-rpc/lib/preflight/src/fees.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-rpc/lib/preflight/src/fees.rs b/cmd/soroban-rpc/lib/preflight/src/fees.rs index 211e467ea..79fd3b286 100644 --- a/cmd/soroban-rpc/lib/preflight/src/fees.rs +++ b/cmd/soroban-rpc/lib/preflight/src/fees.rs @@ -1,7 +1,9 @@ use anyhow::{bail, ensure, Context, Error, Result}; use ledger_storage::LedgerStorage; use soroban_env_host::budget::Budget; -use soroban_env_host::e2e_invoke::{extract_rent_changes, get_ledger_changes, LedgerEntryChange}; +use soroban_env_host::e2e_invoke::{ + extract_rent_changes, get_ledger_changes, ExpirationEntryMap, LedgerEntryChange, +}; use soroban_env_host::fees::{ compute_rent_fee, compute_transaction_resource_fee, compute_write_fee_per_1kb, FeeConfiguration, LedgerEntryRentChange, RentFeeConfiguration, TransactionResources, @@ -31,7 +33,10 @@ pub(crate) fn compute_host_function_transaction_data_and_min_fee( bucket_list_size: u64, current_ledger_seq: u32, ) -> Result<(SorobanTransactionData, i64)> { - let ledger_changes = get_ledger_changes(budget, post_storage, pre_storage)?; + // TODO: is this OK? + let init_expiration_entries = ExpirationEntryMap::new(); + let ledger_changes = + get_ledger_changes(budget, post_storage, pre_storage, init_expiration_entries)?; let soroban_resources = calculate_host_function_soroban_resources(&ledger_changes, &post_storage.footprint, budget) .context("cannot compute host function resources")?; From 13aae97b8f00d0fe0bde42d69158e6bbde70491f Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 31 Aug 2023 21:42:54 -0700 Subject: [PATCH 04/35] wip migrating the soroban-cli (incomplete!) --- cmd/soroban-cli/src/commands/contract/bump.rs | 47 +++++----- .../src/commands/contract/fetch.rs | 23 ++--- .../src/commands/contract/invoke.rs | 5 +- cmd/soroban-cli/src/commands/contract/read.rs | 23 +---- .../src/commands/contract/restore.rs | 33 +++---- cmd/soroban-cli/src/commands/events.rs | 1 - .../src/commands/lab/token/wrap.rs | 12 +-- cmd/soroban-cli/src/rpc/mod.rs | 26 ++---- cmd/soroban-cli/src/utils.rs | 88 ++++++++----------- cmd/soroban-cli/src/wasm.rs | 3 +- 10 files changed, 95 insertions(+), 166 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/bump.rs b/cmd/soroban-cli/src/commands/contract/bump.rs index 6ded03d6a..3cc86a4ad 100644 --- a/cmd/soroban-cli/src/commands/contract/bump.rs +++ b/cmd/soroban-cli/src/commands/contract/bump.rs @@ -6,12 +6,12 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - BumpFootprintExpirationOp, ContractCodeEntry, ContractDataEntry, ContractEntryBodyType, - Error as XdrError, ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, - SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, - TransactionMeta, TransactionMetaV3, Uint256, + BumpFootprintExpirationOp, ContractCodeEntry, ContractDataEntry, Error as XdrError, + ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, + LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, + OperationBody, Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, + SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, + TransactionMetaV3, Uint256, }; use stellar_strkey::DecodeError; @@ -200,17 +200,11 @@ impl Cmd { LedgerEntryChange::State(_), LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { - expiration_ledger_seq, - .. - }), + LedgerEntryData::ContractData(ContractDataEntry { .. }) + | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), .. }), - ) => Ok(*expiration_ledger_seq), + ) => Ok(0), // TODO: How to get expiration ledger now? _ => Err(Error::LedgerEntryNotFound), } } @@ -229,16 +223,21 @@ impl Cmd { .iter() .map(|(k, v)| { let new_k = k.as_ref().clone(); - let new_v = v.as_ref().clone(); + let new_v = v.0.as_ref().clone(); + let new_e = v.1; ( Box::new(new_k.clone()), - Box::new(if needle == new_k { - let (new_v, new_expiration) = bump_entry(&new_v, self.ledgers_to_expire); - expiration_ledger_seq = Some(new_expiration); - new_v - } else { - new_v - }), + ( + Box::new(if needle == new_k { + let (new_v, new_expiration) = + bump_entry(&new_v, self.ledgers_to_expire); + expiration_ledger_seq = Some(new_expiration); + new_v + } else { + new_v + }), + new_e, + ), ) }) .collect::>(); @@ -273,7 +272,6 @@ impl Cmd { utils::contract_id_from_str(wasm_hash) .map_err(|e| Error::CannotParseContractId(wasm_hash.clone(), e))?, ), - body_type: ContractEntryBodyType::DataEntry, })); } else { ScVal::LedgerKeyContractInstance @@ -283,7 +281,6 @@ impl Cmd { Ok(LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(Hash(contract_id)), durability: self.durability.into(), - body_type: ContractEntryBodyType::DataEntry, key, })) } diff --git a/cmd/soroban-cli/src/commands/contract/fetch.rs b/cmd/soroban-cli/src/commands/contract/fetch.rs index 7508daf8c..a1284123e 100644 --- a/cmd/soroban-cli/src/commands/contract/fetch.rs +++ b/cmd/soroban-cli/src/commands/contract/fetch.rs @@ -10,8 +10,7 @@ use soroban_env_host::{ budget::Budget, storage::Storage, xdr::{ - self, ContractCodeEntry, ContractCodeEntryBody, ContractDataDurability, ContractDataEntry, - ContractDataEntryBody, ContractDataEntryData, ContractEntryBodyType, ContractExecutable, + self, ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractExecutable, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, ScAddress, ScContractInstance, ScVal, }, @@ -171,38 +170,26 @@ pub fn get_contract_wasm_from_storage( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }); match storage.get(&key.into(), &Budget::default()) { Ok(rc) => match rc.as_ref() { xdr::LedgerEntry { data: LedgerEntryData::ContractData(ContractDataEntry { - body: - ContractDataEntryBody::DataEntry(ContractDataEntryData { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), + val: ScVal::ContractInstance(ScContractInstance { executable, .. }), .. }), .. } => match executable { ContractExecutable::Wasm(hash) => { if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }) - .into(), + &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }) + .into(), &Budget::default(), ) { match rc.as_ref() { xdr::LedgerEntry { - data: - LedgerEntryData::ContractCode(ContractCodeEntry { - body: ContractCodeEntryBody::DataEntry(code), - .. - }), + data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), .. } => Ok(code.to_vec()), _ => Err(FromWasmError::NotFound), diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 38a4887b1..cf49570b5 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -354,7 +354,10 @@ impl Cmd { { state.ledger_entries.push(( Box::new(source_account_ledger_key), - Box::new(default_account_ledger_entry(source_account.clone())), + ( + Box::new(default_account_ledger_entry(source_account.clone())), + None, // TODO: Should this be no expiry, or a value? + ), )); } diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index eb074c8ee..3526b3c65 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -7,8 +7,7 @@ use std::{ use clap::{command, Parser, ValueEnum}; use soroban_env_host::{ xdr::{ - self, ContractDataDurability, ContractDataEntry, ContractDataEntryBody, - ContractDataEntryData, ContractEntryBodyType, Error as XdrError, Hash, LedgerEntryData, + self, ContractDataDurability, ContractDataEntry, Error as XdrError, Hash, LedgerEntryData, LedgerKey, LedgerKeyContractData, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, WriteXdr, }, HostError, @@ -163,7 +162,6 @@ impl Cmd { contract: ScAddress::Contract(Hash(contract_id)), key: key.clone(), durability: (*durability).into(), - body_type: ContractEntryBodyType::DataEntry, }) }) .collect::>(); @@ -202,7 +200,7 @@ impl Cmd { Ok(ledger_entries .iter() - .map(|(k, v)| (k.as_ref().clone(), v.as_ref().clone())) + .map(|(k, v)| (k.as_ref().clone(), (v.0.as_ref().clone(), v.1))) .filter(|(k, _v)| { if let LedgerKey::ContractData(LedgerKeyContractData { contract: c, .. }) = k { if c == &contract { @@ -211,14 +209,6 @@ impl Cmd { } false }) - .filter(|(k, _v)| { - if let LedgerKey::ContractData(LedgerKeyContractData { body_type, .. }) = k { - if body_type == &ContractEntryBodyType::DataEntry { - return true; - } - } - false - }) .filter(|(k, _v)| { if key.is_none() { return true; @@ -241,7 +231,7 @@ impl Cmd { } false }) - .map(|(k, v)| (k, v.data)) + .map(|(k, v)| (k, v.0.data)) .collect::>()) } @@ -249,12 +239,7 @@ impl Cmd { let entries = raw_entries .iter() .filter_map(|(_k, data)| { - if let LedgerEntryData::ContractData(ContractDataEntry { - key, - body: ContractDataEntryBody::DataEntry(ContractDataEntryData { val, .. }), - .. - }) = &data - { + if let LedgerEntryData::ContractData(ContractDataEntry { key, val, .. }) = &data { Some((key.clone(), val.clone())) } else { None diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index a5d3cb0df..0b8ed6c8d 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -6,10 +6,10 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractEntryBodyType, - Error as XdrError, ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, OperationMeta, Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, + ContractCodeEntry, ContractDataDurability, ContractDataEntry, Error as XdrError, + ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, + LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, + OperationBody, OperationMeta, Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, Uint256, }; @@ -133,7 +133,6 @@ impl Cmd { utils::contract_id_from_str(wasm_hash) .map_err(|e| Error::CannotParseContractId(wasm_hash.clone(), e))?, ), - body_type: ContractEntryBodyType::DataEntry, })] } else { let contract_id = self.contract_id()?; @@ -250,7 +249,6 @@ impl Cmd { LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(Hash(contract_id)), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, key: key.clone(), }) }) @@ -258,33 +256,22 @@ impl Cmd { } } +// TODO: Replace Option<()> with bool. fn parse_operations(ops: &[OperationMeta]) -> Option { ops.get(0).and_then(|op| { op.changes.iter().find_map(|entry| match entry { LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { - expiration_ledger_seq, - .. - }), + LedgerEntryData::ContractData(ContractDataEntry { .. }) + | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), .. }) | LedgerEntryChange::Created(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { - expiration_ledger_seq, - .. - }) - | LedgerEntryData::ContractCode(ContractCodeEntry { - expiration_ledger_seq, - .. - }), + LedgerEntryData::ContractData(ContractDataEntry { .. }) + | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), .. - }) => Some(*expiration_ledger_seq), + }) => Some(0), // TODO: How to get expiration now? _ => None, }) }) diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs index 16a097334..ad9055e2d 100644 --- a/cmd/soroban-cli/src/commands/events.rs +++ b/cmd/soroban-cli/src/commands/events.rs @@ -344,7 +344,6 @@ mod tests { max_entry_expiration: 6, min_persistent_entry_expiration: 7, min_temp_entry_expiration: 8, - autobump_ledgers: 16, }; events_file.commit(&events, &ledger_info, &temp).unwrap(); diff --git a/cmd/soroban-cli/src/commands/lab/token/wrap.rs b/cmd/soroban-cli/src/commands/lab/token/wrap.rs index 963413671..9d52bd43b 100644 --- a/cmd/soroban-cli/src/commands/lab/token/wrap.rs +++ b/cmd/soroban-cli/src/commands/lab/token/wrap.rs @@ -3,11 +3,10 @@ use soroban_env_host::{ budget::Budget, storage::Storage, xdr::{ - Asset, ContractDataDurability, ContractEntryBodyType, ContractExecutable, - ContractIdPreimage, CreateContractArgs, Error as XdrError, Hash, HostFunction, - InvokeHostFunctionOp, LedgerKey::ContractData, LedgerKeyContractData, Memo, MuxedAccount, - Operation, OperationBody, Preconditions, ScAddress, ScVal, SequenceNumber, Transaction, - TransactionExt, Uint256, VecM, + Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, + Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, + LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, Preconditions, + ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, Uint256, VecM, }, Host, HostError, }; @@ -162,7 +161,6 @@ fn build_wrap_token_tx( contract: contract.clone(), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }), ContractData(LedgerKeyContractData { contract: contract.clone(), @@ -170,7 +168,6 @@ fn build_wrap_token_tx( vec![ScVal::Symbol("Metadata".try_into().unwrap())].try_into()?, )), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }), ]; if asset != &Asset::Native { @@ -180,7 +177,6 @@ fn build_wrap_token_tx( vec![ScVal::Symbol("Admin".try_into().unwrap())].try_into()?, )), durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, })); } diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index b70b566d5..91aac783c 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -718,7 +718,6 @@ soroban config identity fund {address} --helper-url "# contract: xdr::ScAddress::Contract(xdr::Hash(*contract_id)), key: xdr::ScVal::LedgerKeyContractInstance, durability: xdr::ContractDataDurability::Persistent, - body_type: xdr::ContractEntryBodyType::DataEntry, }); let contract_ref = self.get_ledger_entries(&[contract_key]).await?; let entries = contract_ref.entries.unwrap_or_default(); @@ -736,13 +735,9 @@ soroban config identity fund {address} --helper-url "# pub async fn get_remote_wasm(&self, contract_id: &[u8; 32]) -> Result, Error> { match self.get_contract_data(contract_id).await? { xdr::ContractDataEntry { - body: - xdr::ContractDataEntryBody::DataEntry(xdr::ContractDataEntryData { - val: - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::Wasm(hash), - .. - }), + val: + xdr::ScVal::ContractInstance(xdr::ScContractInstance { + executable: xdr::ContractExecutable::Wasm(hash), .. }), .. @@ -752,10 +747,7 @@ soroban config identity fund {address} --helper-url "# } pub async fn get_remote_wasm_from_hash(&self, hash: xdr::Hash) -> Result, Error> { - let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { - hash: hash.clone(), - body_type: xdr::ContractEntryBodyType::DataEntry, - }); + let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { hash: hash.clone() }); let contract_data = self.get_ledger_entries(&[code_key]).await?; let entries = contract_data.entries.unwrap_or_default(); if entries.is_empty() { @@ -766,10 +758,7 @@ soroban config identity fund {address} --helper-url "# } let contract_data_entry = &entries[0]; match LedgerEntryData::from_xdr_base64(&contract_data_entry.xdr)? { - LedgerEntryData::ContractCode(xdr::ContractCodeEntry { - body: xdr::ContractCodeEntryBody::DataEntry(code), - .. - }) => Ok(code.into()), + LedgerEntryData::ContractCode(xdr::ContractCodeEntry { code, .. }) => Ok(code.into()), scval => Err(Error::UnexpectedContractCodeDataType(scval)), } } @@ -779,10 +768,7 @@ soroban config identity fund {address} --helper-url "# contract_id: &[u8; 32], ) -> Result, Error> { let contract_data = self.get_contract_data(contract_id).await?; - let xdr::ContractDataEntryBody::DataEntry(data) = contract_data.body else { - return Err(Error::Xdr(XdrError::Invalid)); - }; - match data.val { + match contract_data.val { xdr::ScVal::ContractInstance(xdr::ScContractInstance { executable: xdr::ContractExecutable::Wasm(hash), .. diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 3792c63d7..75862aa20 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -8,9 +8,8 @@ use soroban_env_host::{ budget::Budget, storage::{AccessType, Footprint, Storage}, xdr::{ - AccountEntry, AccountEntryExt, AccountId, Asset, ContractCodeEntry, ContractCodeEntryBody, - ContractDataDurability, ContractDataEntry, ContractDataEntryBody, ContractDataEntryData, - ContractEntryBodyType, ContractExecutable, ContractIdPreimage, DecoratedSignature, + AccountEntry, AccountEntryExt, AccountId, Asset, ContractCodeEntry, ContractDataDurability, + ContractDataEntry, ContractExecutable, ContractIdPreimage, DecoratedSignature, Error as XdrError, ExtensionPoint, Hash, HashIdPreimage, HashIdPreimageContractId, LedgerEntry, LedgerEntryData, LedgerEntryExt, LedgerFootprint, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, ScAddress, ScContractInstance, ScSpecEntry, @@ -64,23 +63,19 @@ pub fn ledger_snapshot_read_or_default( /// /// Might return an error pub fn add_contract_code_to_ledger_entries( - entries: &mut Vec<(Box, Box)>, + entries: &mut Vec<(Box, (Box, Option))>, contract: Vec, min_persistent_entry_expiration: u32, ) -> Result { // Install the code let hash = contract_hash(contract.as_slice())?; - let code_key = LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }); + let code_key = LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }); let code_entry = LedgerEntry { last_modified_ledger_seq: 0, data: LedgerEntryData::ContractCode(ContractCodeEntry { ext: ExtensionPoint::V0, hash: hash.clone(), - body: ContractCodeEntryBody::DataEntry(contract.try_into()?), - expiration_ledger_seq: min_persistent_entry_expiration, + code: contract.try_into()?, }), ext: LedgerEntryExt::V0, }; @@ -90,12 +85,15 @@ pub fn add_contract_code_to_ledger_entries( return Ok(hash); } } - entries.push((Box::new(code_key), Box::new(code_entry))); + entries.push(( + Box::new(code_key), + (Box::new(code_entry), Some(min_persistent_entry_expiration)), + )); Ok(hash) } pub fn add_contract_to_ledger_entries( - entries: &mut Vec<(Box, Box)>, + entries: &mut Vec<(Box, (Box, Option))>, contract_id: [u8; 32], wasm_hash: [u8; 32], min_persistent_entry_expiration: u32, @@ -105,7 +103,6 @@ pub fn add_contract_to_ledger_entries( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body_type: ContractEntryBodyType::DataEntry, }); let contract_entry = LedgerEntry { @@ -114,14 +111,11 @@ pub fn add_contract_to_ledger_entries( contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, - body: ContractDataEntryBody::DataEntry(ContractDataEntryData { - flags: 0, - val: ScVal::ContractInstance(ScContractInstance { - executable: ContractExecutable::Wasm(Hash(wasm_hash)), - storage: None, - }), + val: ScVal::ContractInstance(ScContractInstance { + executable: ContractExecutable::Wasm(Hash(wasm_hash)), + storage: None, }), - expiration_ledger_seq: min_persistent_entry_expiration, + ext: ExtensionPoint::V0, }), ext: LedgerEntryExt::V0, }; @@ -131,7 +125,13 @@ pub fn add_contract_to_ledger_entries( return; } } - entries.push((Box::new(contract_key), Box::new(contract_entry))); + entries.push(( + Box::new(contract_key), + ( + Box::new(contract_entry), + Some(min_persistent_entry_expiration), + ), + )); } pub fn bump_ledger_entry_expirations( @@ -141,7 +141,8 @@ pub fn bump_ledger_entry_expirations( for (k, e) in &mut *entries { if let Some(min_expiration) = lookup.get(k.as_ref()) { if let LedgerEntryData::ContractData(entry) = &mut e.data { - entry.expiration_ledger_seq = *min_expiration; + // TODO: How to update the expiration entry? + // entry.expiration_ledger_seq = *min_expiration; } } } @@ -207,7 +208,6 @@ pub fn get_contract_spec_from_storage( let key = LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, - body_type: ContractEntryBodyType::DataEntry, durability: ContractDataDurability::Persistent, }); match storage.get(&key.into(), &Budget::default()) { @@ -215,48 +215,38 @@ pub fn get_contract_spec_from_storage( LedgerEntry { data: LedgerEntryData::ContractData(ContractDataEntry { - body: - ContractDataEntryBody::DataEntry(ContractDataEntryData { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), - expiration_ledger_seq, + val: ScVal::ContractInstance(ScContractInstance { executable, .. }), .. }), .. } => match executable { ContractExecutable::Token => { - if expiration_ledger_seq <= current_ledger_seq { - return Err(FromWasmError::NotFound); - } + // TODO: How to identify that entry is expired now? + // if expiration_ledger_seq <= current_ledger_seq { + // return Err(FromWasmError::NotFound); + // } let res = soroban_spec::read::parse_raw(&token::StellarAssetSpec::spec_xdr()); res.map_err(FromWasmError::Parse) } ContractExecutable::Wasm(hash) => { - if expiration_ledger_seq <= current_ledger_seq { - return Err(FromWasmError::NotFound); - } + // TODO: How to identify that entry is expired now? + // if expiration_ledger_seq <= current_ledger_seq { + // return Err(FromWasmError::NotFound); + // } if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { - hash: hash.clone(), - body_type: ContractEntryBodyType::DataEntry, - }) - .into(), + &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }) + .into(), &Budget::default(), ) { match rc.as_ref() { LedgerEntry { - data: - LedgerEntryData::ContractCode(ContractCodeEntry { - body: ContractCodeEntryBody::DataEntry(code), - expiration_ledger_seq, - .. - }), + data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), .. } => { - if expiration_ledger_seq <= current_ledger_seq { - return Err(FromWasmError::NotFound); - } + // TODO: How to identify that entry is expired now? + // if expiration_ledger_seq <= current_ledger_seq { + // return Err(FromWasmError::NotFound); + // } soroban_spec::read::from_wasm(code.as_vec()) } _ => Err(FromWasmError::NotFound), diff --git a/cmd/soroban-cli/src/wasm.rs b/cmd/soroban-cli/src/wasm.rs index 428a68e93..fce44c7c5 100644 --- a/cmd/soroban-cli/src/wasm.rs +++ b/cmd/soroban-cli/src/wasm.rs @@ -1,5 +1,5 @@ use clap::arg; -use soroban_env_host::xdr::{self, ContractEntryBodyType, LedgerKey, LedgerKeyContractCode}; +use soroban_env_host::xdr::{self, LedgerKey, LedgerKeyContractCode}; use std::{ fs, io, path::{Path, PathBuf}, @@ -77,7 +77,6 @@ impl TryInto for Args { fn try_into(self) -> Result { Ok(LedgerKey::ContractCode(LedgerKeyContractCode { hash: utils::contract_hash(&self.read()?)?, - body_type: ContractEntryBodyType::DataEntry, })) } } From 43f45c2902c1b1da462ebaed286e3f9b0068d87c Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 02:16:24 +0200 Subject: [PATCH 05/35] Appease rust --- cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs index 378794352..cd5ecb038 100644 --- a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs +++ b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs @@ -162,7 +162,7 @@ impl LedgerStorage { let expiration_entry_xdr = self.get_xdr_internal(&mut expiration_key_xdr)?; let expiration_entry = LedgerEntry::from_xdr(expiration_entry_xdr)?; if let LedgerEntryData::Expiration(ExpirationEntry { - expiration_ledger_seq: expiration_ledger_seq, + expiration_ledger_seq, .. }) = expiration_entry.data { From 3b76940a336e05ebfc1b8b8986afad61e19d0c60 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 02:18:20 +0200 Subject: [PATCH 06/35] Bump core dependencies --- .github/workflows/soroban-rpc.yml | 2 +- cmd/soroban-rpc/internal/test/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index 9022e877e..8ee0898d2 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -110,7 +110,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.13.1-1458.431e4e324.focal~soroban~settings~override + PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.13.1-1462.22b9bb384.focal~vnext runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 diff --git a/cmd/soroban-rpc/internal/test/docker-compose.yml b/cmd/soroban-rpc/internal/test/docker-compose.yml index 27d275916..d983857d9 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/unsafe-stellar-core-next:19.13.1-1458.431e4e324.focal-soroban-settings-override} + image: ${CORE_IMAGE:-stellar/unsafe-stellar-core-next:19.13.1-1462.22b9bb384.focal-vnext} depends_on: - core-postgres restart: on-failure From 7f8024a0f717ee3cb398b3cd01dd37799972916d Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 02:29:53 +0200 Subject: [PATCH 07/35] Bump Go dependencies again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 96c0fd60a..c6773f33e 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,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-20230831173646-edbaa5613179 + github.com/stellar/go v0.0.0-20230901002747-400590c5cce4 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.12.0 ) diff --git a/go.sum b/go.sum index 535997212..0af0d6452 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,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.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stellar/go v0.0.0-20230831173646-edbaa5613179 h1:3IbKtLXZzQsP0IvQrbToHsSYr7idl9PWenG08iTfyAI= -github.com/stellar/go v0.0.0-20230831173646-edbaa5613179/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= +github.com/stellar/go v0.0.0-20230901002747-400590c5cce4 h1:SUkKaeW+G+KQ/LJwkN+li/ws/aSpJVsyZW1uhJHPY4k= +github.com/stellar/go v0.0.0-20230901002747-400590c5cce4/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8= github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 6e515b894bf5722965766e89cf1b094b984f4d0d Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 02:42:35 +0200 Subject: [PATCH 08/35] Fix test comparisons --- .../internal/test/simulate_transaction_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 99fdaacfe..5ae780791 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -238,9 +238,9 @@ func TestSimulateTransactionSucceeds(t *testing.T) { }, }, }, - Instructions: 77283, - ReadBytes: 40, - WriteBytes: 112, + Instructions: 79653, + ReadBytes: 36, + WriteBytes: 100, }, RefundableFee: 20045, } @@ -487,7 +487,7 @@ func TestSimulateInvokeContractTransactionSucceeds(t *testing.T) { require.Contains(t, metrics, "soroban_rpc_json_rpc_request_duration_seconds_count{endpoint=\"simulateTransaction\",status=\"ok\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"db\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"all\"} 3") - require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 55") + require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 93") } func TestSimulateTransactionError(t *testing.T) { From 27810264863eaab9d9918b6436cc174402eee14f Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 03:03:51 +0200 Subject: [PATCH 09/35] Fix BumpAndRestoreFootprint test --- .../test/simulate_transaction_test.go | 39 +++++++++++++------ .../lib/preflight/src/state_expiration.rs | 2 +- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 5ae780791..af71911c5 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -675,7 +675,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) sendSuccessfulTransaction(t, client, sourceAccount, tx) - // get the counter ledger entry + // get the counter ledger entry expiration contractIDHash := xdr.Hash(contractID) counterSym := xdr.ScSymbol("COUNTER") key := xdr.LedgerKey{ @@ -692,7 +692,18 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { Durability: xdr.ContractDataDurabilityPersistent, }, } - keyB64, err := xdr.MarshalBase64(key) + + binKey, err := key.MarshalBinary() + assert.NoError(t, err) + + expirationKey := xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, + } + + keyB64, err := xdr.MarshalBase64(expirationKey) require.NoError(t, err) getLedgerEntryrequest := methods.GetLedgerEntryRequest{ Key: keyB64, @@ -702,10 +713,11 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - // TODO: fix this test - // initialExpirationSeq, ok := entry.ExpirationLedgerSeq() - // assert.True(t, ok) + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + initialExpirationSeq := entry.Expiration.ExpirationLedgerSeq + + // bump the initial expiration params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ SourceAccount: &account, IncrementSequenceNum: true, @@ -736,23 +748,26 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - // newExpirationSeq, ok := entry.ExpirationLedgerSeq() - // assert.True(t, ok) - - // assert.Greater(t, newExpirationSeq, initialExpirationSeq) + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + newExpirationSeq := entry.Expiration.ExpirationLedgerSeq + assert.Greater(t, newExpirationSeq, initialExpirationSeq) // Wait until it expires waitForExpiration := func() { expired := false for i := 0; i < 50; i++ { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) - if err != nil { + assert.NoError(t, err) + assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) + assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger + currentLedger := getLedgerEntryResult.LatestLedger + 1 + if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq { expired = true t.Logf("ledger entry expired") break } - assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - // t.Log("waiting for ledger entry to expire at ledger", entry.MustContractData().ExpirationLedgerSeq) + t.Log("waiting for ledger entry to expire at ledger", entry.Expiration.ExpirationLedgerSeq) time.Sleep(time.Second) } require.True(t, expired) diff --git a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs index f7690a39b..d0c9bbff1 100644 --- a/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs +++ b/cmd/soroban-rpc/lib/preflight/src/state_expiration.rs @@ -8,7 +8,7 @@ pub(crate) trait ExpirableLedgerEntry { fn durability(&self) -> ContractDataDurability; fn expiration_ledger_seq(&self) -> u32; fn has_expired(&self, current_ledger_seq: u32) -> bool { - has_expired(current_ledger_seq, self.expiration_ledger_seq()) + has_expired(self.expiration_ledger_seq(), current_ledger_seq) } } From f5a980368e5ccb3f639bb982ff417962618e632c Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 03:32:57 +0200 Subject: [PATCH 10/35] Improve restore test a bit further --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index af71911c5..5d363bbf5 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -190,6 +190,8 @@ func preflightTransactionParamsLocally(t *testing.T, params txnbuild.Transaction func preflightTransactionParams(t *testing.T, client *jrpc2.Client, params txnbuild.TransactionParams) txnbuild.TransactionParams { response := simulateTransactionFromTxParams(t, client, params) + // The preamble should be zero except for the special restore case + assert.NotZero(t, response.RestorePreamble) return preflightTransactionParamsLocally(t, params, response) } From 1a58165cbe4fdaa6c780205ebafad15bad0f5d0d Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 03:36:39 +0200 Subject: [PATCH 11/35] Adjust tests further --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 5d363bbf5..9c1a19c16 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -489,7 +489,7 @@ func TestSimulateInvokeContractTransactionSucceeds(t *testing.T) { require.Contains(t, metrics, "soroban_rpc_json_rpc_request_duration_seconds_count{endpoint=\"simulateTransaction\",status=\"ok\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"db\"} 3") require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_get_duration_seconds_count{status=\"ok\",type=\"all\"} 3") - require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 93") + require.Contains(t, metrics, "soroban_rpc_preflight_pool_request_ledger_entries_fetched_sum 67") } func TestSimulateTransactionError(t *testing.T) { From eb90f6ba21fdc7c1a6581ff2a89d0f8e87859843 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 03:40:24 +0200 Subject: [PATCH 12/35] Fix typo --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 9c1a19c16..97cc48a8e 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -191,7 +191,7 @@ func preflightTransactionParamsLocally(t *testing.T, params txnbuild.Transaction func preflightTransactionParams(t *testing.T, client *jrpc2.Client, params txnbuild.TransactionParams) txnbuild.TransactionParams { response := simulateTransactionFromTxParams(t, client, params) // The preamble should be zero except for the special restore case - assert.NotZero(t, response.RestorePreamble) + assert.Zero(t, response.RestorePreamble) return preflightTransactionParamsLocally(t, params, response) } From f1cd7c6fdcba707d8b10bbf086bbae9586bbaaca Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 04:21:44 +0200 Subject: [PATCH 13/35] Fix GetPreflight test --- .../internal/preflight/preflight_test.go | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index b7cb8045f..b3215f49d 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -2,6 +2,7 @@ package preflight import ( "context" + "crypto/sha256" "os" "path" "runtime" @@ -31,7 +32,7 @@ var contractCostParams = func() *xdr.ContractCostParams { return &result }() -var mockLedgerEntries = []xdr.LedgerEntry{ +var mockLedgerEntriesWithoutExpirations = []xdr.LedgerEntry{ { LastModifiedLedgerSeq: 1, Data: xdr.LedgerEntryData{ @@ -191,6 +192,38 @@ var mockLedgerEntries = []xdr.LedgerEntry{ }, } +// Adds expiration entries to mockLedgerEntriesWithoutExpirations +var mockLedgerEntries = func() []xdr.LedgerEntry { + result := make([]xdr.LedgerEntry, 0, len(mockLedgerEntriesWithoutExpirations)) + for _, entry := range mockLedgerEntriesWithoutExpirations { + result = append(result, entry) + + if entry.Data.Type == xdr.LedgerEntryTypeContractData || entry.Data.Type == xdr.LedgerEntryTypeContractCode { + key, err := entry.LedgerKey() + if err != nil { + panic(err) + } + bin, err := key.MarshalBinary() + if err != nil { + panic(err) + } + expirationEntry := xdr.LedgerEntry{ + LastModifiedLedgerSeq: entry.LastModifiedLedgerSeq, + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.ExpirationEntry{ + KeyHash: sha256.Sum256(bin), + // Make sure it doesn't expire + ExpirationLedgerSeq: 1000, + }, + }, + } + result = append(result, expirationEntry) + } + } + return result +}() + var helloWorldContract = func() []byte { _, filename, _, _ := runtime.Caller(0) testDirName := path.Dir(filename) @@ -324,8 +357,9 @@ func getPreflightParameters(t testing.TB, dbConfig *preflightParametersDBConfig) func TestGetPreflight(t *testing.T) { // in-memory params := getPreflightParameters(t, nil) - _, err := GetPreflight(context.Background(), params) + result, err := GetPreflight(context.Background(), params) require.NoError(t, err) + require.Empty(t, result.Error) require.NoError(t, params.LedgerEntryReadTx.Done()) // using a restarted db with caching and @@ -335,8 +369,9 @@ func TestGetPreflight(t *testing.T) { disableCache: false, } params = getPreflightParameters(t, dbConfig) - _, err = GetPreflight(context.Background(), params) + result, err = GetPreflight(context.Background(), params) require.NoError(t, err) + require.Empty(t, result.Error) require.NoError(t, params.LedgerEntryReadTx.Done()) require.NoError(t, dbConfig.dbInstance.Close()) } @@ -364,9 +399,10 @@ func benchmark(b *testing.B, config benchmarkConfig) { for i := 0; i < b.N; i++ { params := getPreflightParameters(b, dbConfig) b.StartTimer() - _, err := GetPreflight(context.Background(), params) + result, err := GetPreflight(context.Background(), params) b.StopTimer() require.NoError(b, err) + require.Empty(b, result.Error) require.NoError(b, params.LedgerEntryReadTx.Done()) } if dbConfig != nil { From 0e414da63c3a3a63d1e0d354ffb148083de16cee Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 12:13:15 +0200 Subject: [PATCH 14/35] Get CLI to compile and fix most TODOs --- cmd/soroban-cli/src/commands/contract/bump.rs | 50 ++++++++----------- .../src/commands/contract/invoke.rs | 7 +-- .../src/commands/contract/restore.rs | 27 +++++----- cmd/soroban-cli/src/utils.rs | 22 ++++---- 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/bump.rs b/cmd/soroban-cli/src/commands/contract/bump.rs index 3cc86a4ad..593d096be 100644 --- a/cmd/soroban-cli/src/commands/contract/bump.rs +++ b/cmd/soroban-cli/src/commands/contract/bump.rs @@ -6,12 +6,12 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - BumpFootprintExpirationOp, ContractCodeEntry, ContractDataEntry, Error as XdrError, - ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, - LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, - OperationBody, Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, - SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, - TransactionMetaV3, Uint256, + BumpFootprintExpirationOp, Error as XdrError, ExpirationEntry, ExtensionPoint, Hash, + LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, LedgerKey, + LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, + Preconditions, ReadXdr, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, SorobanResources, + SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, + Uint256, }; use stellar_strkey::DecodeError; @@ -195,16 +195,19 @@ impl Cmd { return Err(Error::LedgerEntryNotFound); } + // TODO: double-check that this match is correct match (&operations[0].changes[0], &operations[0].changes[1]) { ( LedgerEntryChange::State(_), LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { .. }) - | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), + LedgerEntryData::Expiration(ExpirationEntry { + expiration_ledger_seq, + .. + }), .. }), - ) => Ok(0), // TODO: How to get expiration ledger now? + ) => Ok(*expiration_ledger_seq), // TODO: How to get expiration ledger now? _ => Err(Error::LedgerEntryNotFound), } } @@ -228,15 +231,15 @@ impl Cmd { ( Box::new(new_k.clone()), ( - Box::new(if needle == new_k { - let (new_v, new_expiration) = - bump_entry(&new_v, self.ledgers_to_expire); - expiration_ledger_seq = Some(new_expiration); - new_v + Box::new(new_v), + if needle == new_k { + // It must have an expiration since it's a contract data entry + let old_expiration = v.1.unwrap(); + expiration_ledger_seq = Some(old_expiration + self.ledgers_to_expire); + expiration_ledger_seq } else { - new_v - }), - new_e, + new_e + }, ), ) }) @@ -286,19 +289,6 @@ impl Cmd { } } -fn bump_entry(v: &LedgerEntry, ledgers_to_expire: u32) -> (LedgerEntry, u32) { - let mut new_v = v.clone(); - let mut new_expiration_ledger_seq = 0; - if let LedgerEntryData::ContractData(ref mut data) = new_v.data { - data.expiration_ledger_seq += ledgers_to_expire; - new_expiration_ledger_seq = data.expiration_ledger_seq; - } else if let LedgerEntryData::ContractCode(ref mut code) = new_v.data { - code.expiration_ledger_seq += ledgers_to_expire; - new_expiration_ledger_seq = code.expiration_ledger_seq; - } - (new_v, new_expiration_ledger_seq) -} - fn contract_id(s: &str) -> Result<[u8; 32], Error> { utils::contract_id_from_str(s).map_err(|e| Error::CannotParseContractId(s.to_string(), e)) } diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index cf49570b5..8b65eb46d 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -9,7 +9,7 @@ use std::{fmt::Debug, fs, io, rc::Rc}; use clap::{arg, command, value_parser, Parser}; use ed25519_dalek::Keypair; use heck::ToKebabCase; -use soroban_env_host::e2e_invoke::get_ledger_changes; +use soroban_env_host::e2e_invoke::{get_ledger_changes, ExpirationEntryMap}; use soroban_env_host::xdr::ReadXdr; use soroban_env_host::{ budget::Budget, @@ -356,7 +356,7 @@ impl Cmd { Box::new(source_account_ledger_key), ( Box::new(default_account_ledger_entry(source_account.clone())), - None, // TODO: Should this be no expiry, or a value? + None, ), )); } @@ -423,7 +423,8 @@ impl Cmd { crate::log::host_events(&events.0); log_events(footprint, &[contract_auth.try_into()?], &[], Some(&budget)); - let ledger_changes = get_ledger_changes(&budget, &storage, &state)?; + let ledger_changes = + get_ledger_changes(&budget, &storage, &state, ExpirationEntryMap::new())?; let mut expiration_ledger_bumps: HashMap = HashMap::new(); for ledger_entry_change in ledger_changes { if let Some(exp_change) = ledger_entry_change.expiration_change { diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 0b8ed6c8d..ed703454d 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -6,12 +6,12 @@ use std::{ use clap::{command, Parser}; use soroban_env_host::xdr::{ - ContractCodeEntry, ContractDataDurability, ContractDataEntry, Error as XdrError, - ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, - LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Memo, MuxedAccount, Operation, - OperationBody, OperationMeta, Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, - ScSpecTypeDef, ScVal, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, - TransactionExt, TransactionMeta, TransactionMetaV3, Uint256, + ContractDataDurability, Error as XdrError, ExpirationEntry, ExtensionPoint, Hash, LedgerEntry, + LedgerEntryChange, LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyContractCode, + LedgerKeyContractData, Memo, MuxedAccount, Operation, OperationBody, OperationMeta, + Preconditions, ReadXdr, RestoreFootprintOp, ScAddress, ScSpecTypeDef, ScVal, SequenceNumber, + SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, + TransactionMetaV3, Uint256, }; use stellar_strkey::DecodeError; @@ -256,22 +256,25 @@ impl Cmd { } } -// TODO: Replace Option<()> with bool. fn parse_operations(ops: &[OperationMeta]) -> Option { ops.get(0).and_then(|op| { op.changes.iter().find_map(|entry| match entry { LedgerEntryChange::Updated(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { .. }) - | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), + LedgerEntryData::Expiration(ExpirationEntry { + expiration_ledger_seq, + .. + }), .. }) | LedgerEntryChange::Created(LedgerEntry { data: - LedgerEntryData::ContractData(ContractDataEntry { .. }) - | LedgerEntryData::ContractCode(ContractCodeEntry { .. }), + LedgerEntryData::Expiration(ExpirationEntry { + expiration_ledger_seq, + .. + }), .. - }) => Some(0), // TODO: How to get expiration now? + }) => Some(*expiration_ledger_seq), _ => None, }) }) diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 75862aa20..be68d5516 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -81,7 +81,7 @@ pub fn add_contract_code_to_ledger_entries( }; for (k, e) in &mut *entries { if **k == code_key { - **e = code_entry; + *e = (Box::new(code_entry), Some(min_persistent_entry_expiration)); return Ok(hash); } } @@ -121,7 +121,10 @@ pub fn add_contract_to_ledger_entries( }; for (k, e) in &mut *entries { if **k == contract_key { - **e = contract_entry; + *e = ( + Box::new(contract_entry), + Some(min_persistent_entry_expiration), + ); return; } } @@ -135,15 +138,12 @@ pub fn add_contract_to_ledger_entries( } pub fn bump_ledger_entry_expirations( - entries: &mut [(Box, Box)], + entries: &mut Vec<(Box, (Box, Option))>, lookup: &HashMap, ) { - for (k, e) in &mut *entries { + for (k, (_, expiration)) in &mut *entries { if let Some(min_expiration) = lookup.get(k.as_ref()) { - if let LedgerEntryData::ContractData(entry) = &mut e.data { - // TODO: How to update the expiration entry? - // entry.expiration_ledger_seq = *min_expiration; - } + *expiration = Some(*min_expiration); } } } @@ -202,7 +202,7 @@ pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strke /// Might return an error pub fn get_contract_spec_from_storage( storage: &mut Storage, - current_ledger_seq: &u32, + _current_ledger_seq: &u32, contract_id: [u8; 32], ) -> Result, FromWasmError> { let key = LedgerKey::ContractData(LedgerKeyContractData { @@ -221,7 +221,9 @@ pub fn get_contract_spec_from_storage( .. } => match executable { ContractExecutable::Token => { - // TODO: How to identify that entry is expired now? + // TODO: How to identify that an entry is expired now? + // I(fons) don't think that the storage contains expiration entries and I don't + // think we can get it from the state in all case (e.g. Token contracts) // if expiration_ledger_seq <= current_ledger_seq { // return Err(FromWasmError::NotFound); // } From ed9fb10be4cb03949fb514cb28efd9b4e07e0d6a Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 12:51:55 +0200 Subject: [PATCH 15/35] run cargo md-gen --- docs/soroban-cli-full-docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index f528ccb35..ea17a2183 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -833,7 +833,7 @@ Decode XDR * `--type ` — XDR type to decode to - Possible values: `Value`, `ScpBallot`, `ScpStatementType`, `ScpNomination`, `ScpStatement`, `ScpStatementPledges`, `ScpStatementPrepare`, `ScpStatementConfirm`, `ScpStatementExternalize`, `ScpEnvelope`, `ScpQuorumSet`, `ConfigSettingContractExecutionLanesV0`, `ConfigSettingContractComputeV0`, `ConfigSettingContractLedgerCostV0`, `ConfigSettingContractHistoricalDataV0`, `ConfigSettingContractEventsV0`, `ConfigSettingContractBandwidthV0`, `ContractCostType`, `ContractCostParamEntry`, `StateExpirationSettings`, `EvictionIterator`, `ContractCostParams`, `ConfigSettingId`, `ConfigSettingEntry`, `ScEnvMetaKind`, `ScEnvMetaEntry`, `ScMetaV0`, `ScMetaKind`, `ScMetaEntry`, `ScSpecType`, `ScSpecTypeOption`, `ScSpecTypeResult`, `ScSpecTypeVec`, `ScSpecTypeMap`, `ScSpecTypeTuple`, `ScSpecTypeBytesN`, `ScSpecTypeUdt`, `ScSpecTypeDef`, `ScSpecUdtStructFieldV0`, `ScSpecUdtStructV0`, `ScSpecUdtUnionCaseVoidV0`, `ScSpecUdtUnionCaseTupleV0`, `ScSpecUdtUnionCaseV0Kind`, `ScSpecUdtUnionCaseV0`, `ScSpecUdtUnionV0`, `ScSpecUdtEnumCaseV0`, `ScSpecUdtEnumV0`, `ScSpecUdtErrorEnumCaseV0`, `ScSpecUdtErrorEnumV0`, `ScSpecFunctionInputV0`, `ScSpecFunctionV0`, `ScSpecEntryKind`, `ScSpecEntry`, `ScValType`, `ScErrorType`, `ScErrorCode`, `ScError`, `UInt128Parts`, `Int128Parts`, `UInt256Parts`, `Int256Parts`, `ContractExecutableType`, `ContractExecutable`, `ScAddressType`, `ScAddress`, `ScVec`, `ScMap`, `ScBytes`, `ScString`, `ScSymbol`, `ScNonceKey`, `ScContractInstance`, `ScVal`, `ScMapEntry`, `StoredTransactionSet`, `PersistedScpStateV0`, `PersistedScpStateV1`, `PersistedScpState`, `Thresholds`, `String32`, `String64`, `SequenceNumber`, `DataValue`, `PoolId`, `AssetCode4`, `AssetCode12`, `AssetType`, `AssetCode`, `AlphaNum4`, `AlphaNum12`, `Asset`, `Price`, `Liabilities`, `ThresholdIndexes`, `LedgerEntryType`, `Signer`, `AccountFlags`, `SponsorshipDescriptor`, `AccountEntryExtensionV3`, `AccountEntryExtensionV2`, `AccountEntryExtensionV2Ext`, `AccountEntryExtensionV1`, `AccountEntryExtensionV1Ext`, `AccountEntry`, `AccountEntryExt`, `TrustLineFlags`, `LiquidityPoolType`, `TrustLineAsset`, `TrustLineEntryExtensionV2`, `TrustLineEntryExtensionV2Ext`, `TrustLineEntry`, `TrustLineEntryExt`, `TrustLineEntryV1`, `TrustLineEntryV1Ext`, `OfferEntryFlags`, `OfferEntry`, `OfferEntryExt`, `DataEntry`, `DataEntryExt`, `ClaimPredicateType`, `ClaimPredicate`, `ClaimantType`, `Claimant`, `ClaimantV0`, `ClaimableBalanceIdType`, `ClaimableBalanceId`, `ClaimableBalanceFlags`, `ClaimableBalanceEntryExtensionV1`, `ClaimableBalanceEntryExtensionV1Ext`, `ClaimableBalanceEntry`, `ClaimableBalanceEntryExt`, `LiquidityPoolConstantProductParameters`, `LiquidityPoolEntry`, `LiquidityPoolEntryBody`, `LiquidityPoolEntryConstantProduct`, `ContractEntryBodyType`, `ContractDataFlags`, `ContractDataDurability`, `ContractDataEntry`, `ContractDataEntryBody`, `ContractDataEntryData`, `ContractCodeEntry`, `ContractCodeEntryBody`, `LedgerEntryExtensionV1`, `LedgerEntryExtensionV1Ext`, `LedgerEntry`, `LedgerEntryData`, `LedgerEntryExt`, `LedgerKey`, `LedgerKeyAccount`, `LedgerKeyTrustLine`, `LedgerKeyOffer`, `LedgerKeyData`, `LedgerKeyClaimableBalance`, `LedgerKeyLiquidityPool`, `LedgerKeyContractData`, `LedgerKeyContractCode`, `LedgerKeyConfigSetting`, `EnvelopeType`, `UpgradeType`, `StellarValueType`, `LedgerCloseValueSignature`, `StellarValue`, `StellarValueExt`, `LedgerHeaderFlags`, `LedgerHeaderExtensionV1`, `LedgerHeaderExtensionV1Ext`, `LedgerHeader`, `LedgerHeaderExt`, `LedgerUpgradeType`, `ConfigUpgradeSetKey`, `LedgerUpgrade`, `ConfigUpgradeSet`, `BucketEntryType`, `BucketMetadata`, `BucketMetadataExt`, `BucketEntry`, `TxSetComponentType`, `TxSetComponent`, `TxSetComponentTxsMaybeDiscountedFee`, `TransactionPhase`, `TransactionSet`, `TransactionSetV1`, `GeneralizedTransactionSet`, `TransactionResultPair`, `TransactionResultSet`, `TransactionHistoryEntry`, `TransactionHistoryEntryExt`, `TransactionHistoryResultEntry`, `TransactionHistoryResultEntryExt`, `LedgerHeaderHistoryEntry`, `LedgerHeaderHistoryEntryExt`, `LedgerScpMessages`, `ScpHistoryEntryV0`, `ScpHistoryEntry`, `LedgerEntryChangeType`, `LedgerEntryChange`, `LedgerEntryChanges`, `OperationMeta`, `TransactionMetaV1`, `TransactionMetaV2`, `ContractEventType`, `ContractEvent`, `ContractEventBody`, `ContractEventV0`, `DiagnosticEvent`, `SorobanTransactionMeta`, `TransactionMetaV3`, `InvokeHostFunctionSuccessPreImage`, `TransactionMeta`, `TransactionResultMeta`, `UpgradeEntryMeta`, `LedgerCloseMetaV0`, `LedgerCloseMetaV1`, `LedgerCloseMetaV2`, `LedgerCloseMeta`, `ErrorCode`, `SError`, `SendMore`, `SendMoreExtended`, `AuthCert`, `Hello`, `Auth`, `IpAddrType`, `PeerAddress`, `PeerAddressIp`, `MessageType`, `DontHave`, `SurveyMessageCommandType`, `SurveyMessageResponseType`, `SurveyRequestMessage`, `SignedSurveyRequestMessage`, `EncryptedBody`, `SurveyResponseMessage`, `SignedSurveyResponseMessage`, `PeerStats`, `PeerStatList`, `TopologyResponseBodyV0`, `TopologyResponseBodyV1`, `SurveyResponseBody`, `TxAdvertVector`, `FloodAdvert`, `TxDemandVector`, `FloodDemand`, `StellarMessage`, `AuthenticatedMessage`, `AuthenticatedMessageV0`, `LiquidityPoolParameters`, `MuxedAccount`, `MuxedAccountMed25519`, `DecoratedSignature`, `OperationType`, `CreateAccountOp`, `PaymentOp`, `PathPaymentStrictReceiveOp`, `PathPaymentStrictSendOp`, `ManageSellOfferOp`, `ManageBuyOfferOp`, `CreatePassiveSellOfferOp`, `SetOptionsOp`, `ChangeTrustAsset`, `ChangeTrustOp`, `AllowTrustOp`, `ManageDataOp`, `BumpSequenceOp`, `CreateClaimableBalanceOp`, `ClaimClaimableBalanceOp`, `BeginSponsoringFutureReservesOp`, `RevokeSponsorshipType`, `RevokeSponsorshipOp`, `RevokeSponsorshipOpSigner`, `ClawbackOp`, `ClawbackClaimableBalanceOp`, `SetTrustLineFlagsOp`, `LiquidityPoolDepositOp`, `LiquidityPoolWithdrawOp`, `HostFunctionType`, `ContractIdPreimageType`, `ContractIdPreimage`, `ContractIdPreimageFromAddress`, `CreateContractArgs`, `InvokeContractArgs`, `HostFunction`, `SorobanAuthorizedFunctionType`, `SorobanAuthorizedFunction`, `SorobanAuthorizedInvocation`, `SorobanAddressCredentials`, `SorobanCredentialsType`, `SorobanCredentials`, `SorobanAuthorizationEntry`, `InvokeHostFunctionOp`, `BumpFootprintExpirationOp`, `RestoreFootprintOp`, `Operation`, `OperationBody`, `HashIdPreimage`, `HashIdPreimageOperationId`, `HashIdPreimageRevokeId`, `HashIdPreimageContractId`, `HashIdPreimageSorobanAuthorization`, `MemoType`, `Memo`, `TimeBounds`, `LedgerBounds`, `PreconditionsV2`, `PreconditionType`, `Preconditions`, `LedgerFootprint`, `SorobanResources`, `SorobanTransactionData`, `TransactionV0`, `TransactionV0Ext`, `TransactionV0Envelope`, `Transaction`, `TransactionExt`, `TransactionV1Envelope`, `FeeBumpTransaction`, `FeeBumpTransactionInnerTx`, `FeeBumpTransactionExt`, `FeeBumpTransactionEnvelope`, `TransactionEnvelope`, `TransactionSignaturePayload`, `TransactionSignaturePayloadTaggedTransaction`, `ClaimAtomType`, `ClaimOfferAtomV0`, `ClaimOfferAtom`, `ClaimLiquidityAtom`, `ClaimAtom`, `CreateAccountResultCode`, `CreateAccountResult`, `PaymentResultCode`, `PaymentResult`, `PathPaymentStrictReceiveResultCode`, `SimplePaymentResult`, `PathPaymentStrictReceiveResult`, `PathPaymentStrictReceiveResultSuccess`, `PathPaymentStrictSendResultCode`, `PathPaymentStrictSendResult`, `PathPaymentStrictSendResultSuccess`, `ManageSellOfferResultCode`, `ManageOfferEffect`, `ManageOfferSuccessResult`, `ManageOfferSuccessResultOffer`, `ManageSellOfferResult`, `ManageBuyOfferResultCode`, `ManageBuyOfferResult`, `SetOptionsResultCode`, `SetOptionsResult`, `ChangeTrustResultCode`, `ChangeTrustResult`, `AllowTrustResultCode`, `AllowTrustResult`, `AccountMergeResultCode`, `AccountMergeResult`, `InflationResultCode`, `InflationPayout`, `InflationResult`, `ManageDataResultCode`, `ManageDataResult`, `BumpSequenceResultCode`, `BumpSequenceResult`, `CreateClaimableBalanceResultCode`, `CreateClaimableBalanceResult`, `ClaimClaimableBalanceResultCode`, `ClaimClaimableBalanceResult`, `BeginSponsoringFutureReservesResultCode`, `BeginSponsoringFutureReservesResult`, `EndSponsoringFutureReservesResultCode`, `EndSponsoringFutureReservesResult`, `RevokeSponsorshipResultCode`, `RevokeSponsorshipResult`, `ClawbackResultCode`, `ClawbackResult`, `ClawbackClaimableBalanceResultCode`, `ClawbackClaimableBalanceResult`, `SetTrustLineFlagsResultCode`, `SetTrustLineFlagsResult`, `LiquidityPoolDepositResultCode`, `LiquidityPoolDepositResult`, `LiquidityPoolWithdrawResultCode`, `LiquidityPoolWithdrawResult`, `InvokeHostFunctionResultCode`, `InvokeHostFunctionResult`, `BumpFootprintExpirationResultCode`, `BumpFootprintExpirationResult`, `RestoreFootprintResultCode`, `RestoreFootprintResult`, `OperationResultCode`, `OperationResult`, `OperationResultTr`, `TransactionResultCode`, `InnerTransactionResult`, `InnerTransactionResultResult`, `InnerTransactionResultExt`, `InnerTransactionResultPair`, `TransactionResult`, `TransactionResultResult`, `TransactionResultExt`, `Hash`, `Uint256`, `Uint32`, `Int32`, `Uint64`, `Int64`, `TimePoint`, `Duration`, `ExtensionPoint`, `CryptoKeyType`, `PublicKeyType`, `SignerKeyType`, `PublicKey`, `SignerKey`, `SignerKeyEd25519SignedPayload`, `Signature`, `SignatureHint`, `NodeId`, `AccountId`, `Curve25519Secret`, `Curve25519Public`, `HmacSha256Key`, `HmacSha256Mac` + Possible values: `Value`, `ScpBallot`, `ScpStatementType`, `ScpNomination`, `ScpStatement`, `ScpStatementPledges`, `ScpStatementPrepare`, `ScpStatementConfirm`, `ScpStatementExternalize`, `ScpEnvelope`, `ScpQuorumSet`, `ConfigSettingContractExecutionLanesV0`, `ConfigSettingContractComputeV0`, `ConfigSettingContractLedgerCostV0`, `ConfigSettingContractHistoricalDataV0`, `ConfigSettingContractEventsV0`, `ConfigSettingContractBandwidthV0`, `ContractCostType`, `ContractCostParamEntry`, `StateExpirationSettings`, `EvictionIterator`, `ContractCostParams`, `ConfigSettingId`, `ConfigSettingEntry`, `ScEnvMetaKind`, `ScEnvMetaEntry`, `ScMetaV0`, `ScMetaKind`, `ScMetaEntry`, `ScSpecType`, `ScSpecTypeOption`, `ScSpecTypeResult`, `ScSpecTypeVec`, `ScSpecTypeMap`, `ScSpecTypeTuple`, `ScSpecTypeBytesN`, `ScSpecTypeUdt`, `ScSpecTypeDef`, `ScSpecUdtStructFieldV0`, `ScSpecUdtStructV0`, `ScSpecUdtUnionCaseVoidV0`, `ScSpecUdtUnionCaseTupleV0`, `ScSpecUdtUnionCaseV0Kind`, `ScSpecUdtUnionCaseV0`, `ScSpecUdtUnionV0`, `ScSpecUdtEnumCaseV0`, `ScSpecUdtEnumV0`, `ScSpecUdtErrorEnumCaseV0`, `ScSpecUdtErrorEnumV0`, `ScSpecFunctionInputV0`, `ScSpecFunctionV0`, `ScSpecEntryKind`, `ScSpecEntry`, `ScValType`, `ScErrorType`, `ScErrorCode`, `ScError`, `UInt128Parts`, `Int128Parts`, `UInt256Parts`, `Int256Parts`, `ContractExecutableType`, `ContractExecutable`, `ScAddressType`, `ScAddress`, `ScVec`, `ScMap`, `ScBytes`, `ScString`, `ScSymbol`, `ScNonceKey`, `ScContractInstance`, `ScVal`, `ScMapEntry`, `StoredTransactionSet`, `PersistedScpStateV0`, `PersistedScpStateV1`, `PersistedScpState`, `Thresholds`, `String32`, `String64`, `SequenceNumber`, `DataValue`, `PoolId`, `AssetCode4`, `AssetCode12`, `AssetType`, `AssetCode`, `AlphaNum4`, `AlphaNum12`, `Asset`, `Price`, `Liabilities`, `ThresholdIndexes`, `LedgerEntryType`, `Signer`, `AccountFlags`, `SponsorshipDescriptor`, `AccountEntryExtensionV3`, `AccountEntryExtensionV2`, `AccountEntryExtensionV2Ext`, `AccountEntryExtensionV1`, `AccountEntryExtensionV1Ext`, `AccountEntry`, `AccountEntryExt`, `TrustLineFlags`, `LiquidityPoolType`, `TrustLineAsset`, `TrustLineEntryExtensionV2`, `TrustLineEntryExtensionV2Ext`, `TrustLineEntry`, `TrustLineEntryExt`, `TrustLineEntryV1`, `TrustLineEntryV1Ext`, `OfferEntryFlags`, `OfferEntry`, `OfferEntryExt`, `DataEntry`, `DataEntryExt`, `ClaimPredicateType`, `ClaimPredicate`, `ClaimantType`, `Claimant`, `ClaimantV0`, `ClaimableBalanceIdType`, `ClaimableBalanceId`, `ClaimableBalanceFlags`, `ClaimableBalanceEntryExtensionV1`, `ClaimableBalanceEntryExtensionV1Ext`, `ClaimableBalanceEntry`, `ClaimableBalanceEntryExt`, `LiquidityPoolConstantProductParameters`, `LiquidityPoolEntry`, `LiquidityPoolEntryBody`, `LiquidityPoolEntryConstantProduct`, `ContractDataDurability`, `ContractDataEntry`, `ContractCodeEntry`, `ExpirationEntry`, `LedgerEntryExtensionV1`, `LedgerEntryExtensionV1Ext`, `LedgerEntry`, `LedgerEntryData`, `LedgerEntryExt`, `LedgerKey`, `LedgerKeyAccount`, `LedgerKeyTrustLine`, `LedgerKeyOffer`, `LedgerKeyData`, `LedgerKeyClaimableBalance`, `LedgerKeyLiquidityPool`, `LedgerKeyContractData`, `LedgerKeyContractCode`, `LedgerKeyConfigSetting`, `LedgerKeyExpiration`, `EnvelopeType`, `UpgradeType`, `StellarValueType`, `LedgerCloseValueSignature`, `StellarValue`, `StellarValueExt`, `LedgerHeaderFlags`, `LedgerHeaderExtensionV1`, `LedgerHeaderExtensionV1Ext`, `LedgerHeader`, `LedgerHeaderExt`, `LedgerUpgradeType`, `ConfigUpgradeSetKey`, `LedgerUpgrade`, `ConfigUpgradeSet`, `BucketEntryType`, `BucketMetadata`, `BucketMetadataExt`, `BucketEntry`, `TxSetComponentType`, `TxSetComponent`, `TxSetComponentTxsMaybeDiscountedFee`, `TransactionPhase`, `TransactionSet`, `TransactionSetV1`, `GeneralizedTransactionSet`, `TransactionResultPair`, `TransactionResultSet`, `TransactionHistoryEntry`, `TransactionHistoryEntryExt`, `TransactionHistoryResultEntry`, `TransactionHistoryResultEntryExt`, `LedgerHeaderHistoryEntry`, `LedgerHeaderHistoryEntryExt`, `LedgerScpMessages`, `ScpHistoryEntryV0`, `ScpHistoryEntry`, `LedgerEntryChangeType`, `LedgerEntryChange`, `LedgerEntryChanges`, `OperationMeta`, `TransactionMetaV1`, `TransactionMetaV2`, `ContractEventType`, `ContractEvent`, `ContractEventBody`, `ContractEventV0`, `DiagnosticEvent`, `SorobanTransactionMeta`, `TransactionMetaV3`, `InvokeHostFunctionSuccessPreImage`, `TransactionMeta`, `TransactionResultMeta`, `UpgradeEntryMeta`, `LedgerCloseMetaV0`, `LedgerCloseMetaV1`, `LedgerCloseMetaV2`, `LedgerCloseMeta`, `ErrorCode`, `SError`, `SendMore`, `SendMoreExtended`, `AuthCert`, `Hello`, `Auth`, `IpAddrType`, `PeerAddress`, `PeerAddressIp`, `MessageType`, `DontHave`, `SurveyMessageCommandType`, `SurveyMessageResponseType`, `SurveyRequestMessage`, `SignedSurveyRequestMessage`, `EncryptedBody`, `SurveyResponseMessage`, `SignedSurveyResponseMessage`, `PeerStats`, `PeerStatList`, `TopologyResponseBodyV0`, `TopologyResponseBodyV1`, `SurveyResponseBody`, `TxAdvertVector`, `FloodAdvert`, `TxDemandVector`, `FloodDemand`, `StellarMessage`, `AuthenticatedMessage`, `AuthenticatedMessageV0`, `LiquidityPoolParameters`, `MuxedAccount`, `MuxedAccountMed25519`, `DecoratedSignature`, `OperationType`, `CreateAccountOp`, `PaymentOp`, `PathPaymentStrictReceiveOp`, `PathPaymentStrictSendOp`, `ManageSellOfferOp`, `ManageBuyOfferOp`, `CreatePassiveSellOfferOp`, `SetOptionsOp`, `ChangeTrustAsset`, `ChangeTrustOp`, `AllowTrustOp`, `ManageDataOp`, `BumpSequenceOp`, `CreateClaimableBalanceOp`, `ClaimClaimableBalanceOp`, `BeginSponsoringFutureReservesOp`, `RevokeSponsorshipType`, `RevokeSponsorshipOp`, `RevokeSponsorshipOpSigner`, `ClawbackOp`, `ClawbackClaimableBalanceOp`, `SetTrustLineFlagsOp`, `LiquidityPoolDepositOp`, `LiquidityPoolWithdrawOp`, `HostFunctionType`, `ContractIdPreimageType`, `ContractIdPreimage`, `ContractIdPreimageFromAddress`, `CreateContractArgs`, `InvokeContractArgs`, `HostFunction`, `SorobanAuthorizedFunctionType`, `SorobanAuthorizedFunction`, `SorobanAuthorizedInvocation`, `SorobanAddressCredentials`, `SorobanCredentialsType`, `SorobanCredentials`, `SorobanAuthorizationEntry`, `InvokeHostFunctionOp`, `BumpFootprintExpirationOp`, `RestoreFootprintOp`, `Operation`, `OperationBody`, `HashIdPreimage`, `HashIdPreimageOperationId`, `HashIdPreimageRevokeId`, `HashIdPreimageContractId`, `HashIdPreimageSorobanAuthorization`, `MemoType`, `Memo`, `TimeBounds`, `LedgerBounds`, `PreconditionsV2`, `PreconditionType`, `Preconditions`, `LedgerFootprint`, `SorobanResources`, `SorobanTransactionData`, `TransactionV0`, `TransactionV0Ext`, `TransactionV0Envelope`, `Transaction`, `TransactionExt`, `TransactionV1Envelope`, `FeeBumpTransaction`, `FeeBumpTransactionInnerTx`, `FeeBumpTransactionExt`, `FeeBumpTransactionEnvelope`, `TransactionEnvelope`, `TransactionSignaturePayload`, `TransactionSignaturePayloadTaggedTransaction`, `ClaimAtomType`, `ClaimOfferAtomV0`, `ClaimOfferAtom`, `ClaimLiquidityAtom`, `ClaimAtom`, `CreateAccountResultCode`, `CreateAccountResult`, `PaymentResultCode`, `PaymentResult`, `PathPaymentStrictReceiveResultCode`, `SimplePaymentResult`, `PathPaymentStrictReceiveResult`, `PathPaymentStrictReceiveResultSuccess`, `PathPaymentStrictSendResultCode`, `PathPaymentStrictSendResult`, `PathPaymentStrictSendResultSuccess`, `ManageSellOfferResultCode`, `ManageOfferEffect`, `ManageOfferSuccessResult`, `ManageOfferSuccessResultOffer`, `ManageSellOfferResult`, `ManageBuyOfferResultCode`, `ManageBuyOfferResult`, `SetOptionsResultCode`, `SetOptionsResult`, `ChangeTrustResultCode`, `ChangeTrustResult`, `AllowTrustResultCode`, `AllowTrustResult`, `AccountMergeResultCode`, `AccountMergeResult`, `InflationResultCode`, `InflationPayout`, `InflationResult`, `ManageDataResultCode`, `ManageDataResult`, `BumpSequenceResultCode`, `BumpSequenceResult`, `CreateClaimableBalanceResultCode`, `CreateClaimableBalanceResult`, `ClaimClaimableBalanceResultCode`, `ClaimClaimableBalanceResult`, `BeginSponsoringFutureReservesResultCode`, `BeginSponsoringFutureReservesResult`, `EndSponsoringFutureReservesResultCode`, `EndSponsoringFutureReservesResult`, `RevokeSponsorshipResultCode`, `RevokeSponsorshipResult`, `ClawbackResultCode`, `ClawbackResult`, `ClawbackClaimableBalanceResultCode`, `ClawbackClaimableBalanceResult`, `SetTrustLineFlagsResultCode`, `SetTrustLineFlagsResult`, `LiquidityPoolDepositResultCode`, `LiquidityPoolDepositResult`, `LiquidityPoolWithdrawResultCode`, `LiquidityPoolWithdrawResult`, `InvokeHostFunctionResultCode`, `InvokeHostFunctionResult`, `BumpFootprintExpirationResultCode`, `BumpFootprintExpirationResult`, `RestoreFootprintResultCode`, `RestoreFootprintResult`, `OperationResultCode`, `OperationResult`, `OperationResultTr`, `TransactionResultCode`, `InnerTransactionResult`, `InnerTransactionResultResult`, `InnerTransactionResultExt`, `InnerTransactionResultPair`, `TransactionResult`, `TransactionResultResult`, `TransactionResultExt`, `Hash`, `Uint256`, `Uint32`, `Int32`, `Uint64`, `Int64`, `TimePoint`, `Duration`, `ExtensionPoint`, `CryptoKeyType`, `PublicKeyType`, `SignerKeyType`, `PublicKey`, `SignerKey`, `SignerKeyEd25519SignedPayload`, `Signature`, `SignatureHint`, `NodeId`, `AccountId`, `Curve25519Secret`, `Curve25519Public`, `HmacSha256Key`, `HmacSha256Mac` * `--xdr ` — XDR (base64 encoded) to decode * `--output ` — Type of output From 44bc32326fb4a696bbd8aca8c8df71ff5ddf2627 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 12:59:21 +0200 Subject: [PATCH 16/35] Appease clippy --- cmd/soroban-cli/src/utils.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index be68d5516..eeab67884 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -59,11 +59,13 @@ pub fn ledger_snapshot_read_or_default( } } +type LedgerSnapshotEntries = Vec<(Box, (Box, Option))>; + /// # Errors /// /// Might return an error pub fn add_contract_code_to_ledger_entries( - entries: &mut Vec<(Box, (Box, Option))>, + entries: &mut LedgerSnapshotEntries, contract: Vec, min_persistent_entry_expiration: u32, ) -> Result { @@ -93,7 +95,7 @@ pub fn add_contract_code_to_ledger_entries( } pub fn add_contract_to_ledger_entries( - entries: &mut Vec<(Box, (Box, Option))>, + entries: &mut LedgerSnapshotEntries, contract_id: [u8; 32], wasm_hash: [u8; 32], min_persistent_entry_expiration: u32, @@ -138,7 +140,7 @@ pub fn add_contract_to_ledger_entries( } pub fn bump_ledger_entry_expirations( - entries: &mut Vec<(Box, (Box, Option))>, + entries: &mut LedgerSnapshotEntries, lookup: &HashMap, ) { for (k, (_, expiration)) in &mut *entries { From f6210f518b69de7e97383e5792ff111d262c4e6c Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 13:40:20 +0200 Subject: [PATCH 17/35] Add comment about expiration entry efficiency --- cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs index cd5ecb038..2ac3862b8 100644 --- a/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs +++ b/cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs @@ -153,6 +153,8 @@ impl LedgerStorage { let xdr = self.get_xdr_internal(&mut key_xdr)?; let expiration_seq = match key { + // TODO: it would probably be more efficient to do all of this in the Go side + // (e.g. it would allow us to query multiple entries at once) LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { let key_hash: [u8; 32] = sha2::Sha256::digest(key_xdr).into(); let expiration_key = LedgerKey::Expiration(LedgerKeyExpiration { From ff1a045929aac738e16f31ce7963104cb1c81941 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 14:45:43 +0200 Subject: [PATCH 18/35] Address review feedback --- cmd/soroban-cli/src/commands/contract/bump.rs | 2 +- cmd/soroban-cli/src/commands/contract/read.rs | 2 +- cmd/soroban-cli/src/utils.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/bump.rs b/cmd/soroban-cli/src/commands/contract/bump.rs index 593d096be..ed07041ce 100644 --- a/cmd/soroban-cli/src/commands/contract/bump.rs +++ b/cmd/soroban-cli/src/commands/contract/bump.rs @@ -207,7 +207,7 @@ impl Cmd { }), .. }), - ) => Ok(*expiration_ledger_seq), // TODO: How to get expiration ledger now? + ) => Ok(*expiration_ledger_seq), _ => Err(Error::LedgerEntryNotFound), } } diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index 3526b3c65..8c00244f7 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -231,7 +231,7 @@ impl Cmd { } false }) - .map(|(k, v)| (k, v.0.data)) + .map(|(k, (v, _))| (k, v.data)) .collect::>()) } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index eeab67884..5bd6bfe76 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -225,7 +225,7 @@ pub fn get_contract_spec_from_storage( ContractExecutable::Token => { // TODO: How to identify that an entry is expired now? // I(fons) don't think that the storage contains expiration entries and I don't - // think we can get it from the state in all case (e.g. Token contracts) + // think we can get it from the state in all cases (e.g. Token contracts) // if expiration_ledger_seq <= current_ledger_seq { // return Err(FromWasmError::NotFound); // } From 315b6b1433c50a6f756a61a62acdecb2c71fc929 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Fri, 1 Sep 2023 15:26:58 +0200 Subject: [PATCH 19/35] Take a stab at extracting the spec from the state --- .../soroban-test/tests/it/invoke_sandbox.rs | 24 ++++ .../src/commands/contract/invoke.rs | 4 +- cmd/soroban-cli/src/utils.rs | 111 ++++++++++-------- 3 files changed, 85 insertions(+), 54 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs b/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs index 821e2bf2f..0b1977caa 100644 --- a/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs +++ b/cmd/crates/soroban-test/tests/it/invoke_sandbox.rs @@ -184,6 +184,19 @@ fn invoke_auth() { .assert() .stdout(format!("\"{DEFAULT_PUB_KEY}\"\n")) .success(); + + // Invoke it again without providing the contract, to exercise the deployment + sandbox + .new_assert_cmd("contract") + .arg("invoke") + .arg("--id=1") + .arg("--") + .arg("auth") + .arg(&format!("--addr={DEFAULT_PUB_KEY}")) + .arg("--world=world") + .assert() + .stdout(format!("\"{DEFAULT_PUB_KEY}\"\n")) + .success(); } #[tokio::test] @@ -290,6 +303,17 @@ fn invoke_with_source(sandbox: &TestEnv, source: &str) { "--world=world", ]); assert_eq!(cmd.unwrap(), "[\"Hello\",\"world\"]"); + + // Invoke it again without providing the wasm + let cmd = sandbox.invoke(&[ + "--source-account", + source, + "--id=1", + "--", + "hello", + "--world=world", + ]); + assert_eq!(cmd.unwrap(), "[\"Hello\",\"world\"]"); } #[test] diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 8b65eb46d..885be5e46 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -362,11 +362,11 @@ impl Cmd { } let snap = Rc::new(state.clone()); - let mut storage = Storage::with_recording_footprint(snap); + let storage = Storage::with_recording_footprint(snap); let spec_entries = if let Some(spec) = self.spec_entries()? { spec } else { - utils::get_contract_spec_from_storage(&mut storage, &state.sequence_number, contract_id) + utils::get_contract_spec_from_state(&state, contract_id) .map_err(Error::CannotParseContractSpec)? }; let budget = Budget::default(); diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 5bd6bfe76..ee5337ecd 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -5,8 +5,7 @@ use ed25519_dalek::Signer; use sha2::{Digest, Sha256}; use soroban_env_host::{ - budget::Budget, - storage::{AccessType, Footprint, Storage}, + storage::{AccessType, Footprint}, xdr::{ AccountEntry, AccountEntryExt, AccountId, Asset, ContractCodeEntry, ContractDataDurability, ContractDataEntry, ContractExecutable, ContractIdPreimage, DecoratedSignature, @@ -199,68 +198,76 @@ pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strke .map_err(|_| stellar_strkey::DecodeError::Invalid) } +fn get_entry_from_spanshot( + key: &LedgerKey, + entries: &LedgerSnapshotEntries, +) -> Option<(Box, Option)> { + for (k, result) in entries { + if *key == **k { + return Some((*result).clone()); + } + } + None +} + /// # Errors /// /// Might return an error -pub fn get_contract_spec_from_storage( - storage: &mut Storage, - _current_ledger_seq: &u32, +pub fn get_contract_spec_from_state( + state: &LedgerSnapshot, contract_id: [u8; 32], ) -> Result, FromWasmError> { + let current_ledger_seq = state.sequence_number; let key = LedgerKey::ContractData(LedgerKeyContractData { contract: ScAddress::Contract(contract_id.into()), key: ScVal::LedgerKeyContractInstance, durability: ContractDataDurability::Persistent, }); - match storage.get(&key.into(), &Budget::default()) { - Ok(rc) => match rc.as_ref() { - LedgerEntry { - data: - LedgerEntryData::ContractData(ContractDataEntry { - val: ScVal::ContractInstance(ScContractInstance { executable, .. }), - .. - }), - .. - } => match executable { - ContractExecutable::Token => { - // TODO: How to identify that an entry is expired now? - // I(fons) don't think that the storage contains expiration entries and I don't - // think we can get it from the state in all cases (e.g. Token contracts) - // if expiration_ledger_seq <= current_ledger_seq { - // return Err(FromWasmError::NotFound); - // } - let res = soroban_spec::read::parse_raw(&token::StellarAssetSpec::spec_xdr()); - res.map_err(FromWasmError::Parse) + let (entry, expiration_ledger_seq) = match get_entry_from_spanshot(&key, &state.ledger_entries) + { + // It's a contract data entry, so it should have an expiration if present + Some((entry, expiration)) => (entry, expiration.unwrap()), + None => return Err(FromWasmError::NotFound), + }; + if expiration_ledger_seq <= current_ledger_seq { + return Err(FromWasmError::NotFound); + } + + match *entry { + LedgerEntry { + data: + LedgerEntryData::ContractData(ContractDataEntry { + val: ScVal::ContractInstance(ScContractInstance { executable, .. }), + .. + }), + .. + } => match executable { + ContractExecutable::Token => { + // TODO/FIXME: I don't think it will work for token contracts, since we don't store them in the state? + let res = soroban_spec::read::parse_raw(&token::StellarAssetSpec::spec_xdr()); + res.map_err(FromWasmError::Parse) + } + ContractExecutable::Wasm(hash) => { + // It's a contract code entry, so it should have an expiration if present + let (entry, expiration_ledger_seq) = match get_entry_from_spanshot( + &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }), + &state.ledger_entries, + ) { + // It's a contract data entry, so it should have an expiration if present + Some((entry, expiration)) => (entry, expiration.unwrap()), + None => return Err(FromWasmError::NotFound), + }; + if expiration_ledger_seq <= current_ledger_seq { + return Err(FromWasmError::NotFound); } - ContractExecutable::Wasm(hash) => { - // TODO: How to identify that entry is expired now? - // if expiration_ledger_seq <= current_ledger_seq { - // return Err(FromWasmError::NotFound); - // } - if let Ok(rc) = storage.get( - &LedgerKey::ContractCode(LedgerKeyContractCode { hash: hash.clone() }) - .into(), - &Budget::default(), - ) { - match rc.as_ref() { - LedgerEntry { - data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), - .. - } => { - // TODO: How to identify that entry is expired now? - // if expiration_ledger_seq <= current_ledger_seq { - // return Err(FromWasmError::NotFound); - // } - soroban_spec::read::from_wasm(code.as_vec()) - } - _ => Err(FromWasmError::NotFound), - } - } else { - Err(FromWasmError::NotFound) - } + match *entry { + LedgerEntry { + data: LedgerEntryData::ContractCode(ContractCodeEntry { code, .. }), + .. + } => soroban_spec::read::from_wasm(code.as_vec()), + _ => Err(FromWasmError::NotFound), } - }, - _ => Err(FromWasmError::NotFound), + } }, _ => Err(FromWasmError::NotFound), } From d95def2cd1f5977730825c08020f1512b0c252f8 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:34:36 -0400 Subject: [PATCH 20/35] update --- cmd/soroban-rpc/internal/db/ledgerentry.go | 79 +++++++++++++++--- .../internal/db/ledgerentry_test.go | 80 ++++++++++++++----- .../internal/methods/get_ledger_entries.go | 3 + .../internal/methods/get_ledger_entry.go | 5 +- .../internal/preflight/preflight.go | 8 +- 5 files changed, 139 insertions(+), 36 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 8bb6a68cb..dfe3d1dfd 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -2,6 +2,7 @@ package db import ( "context" + "crypto/sha256" "database/sql" "fmt" @@ -23,8 +24,9 @@ type LedgerEntryReader interface { } type LedgerKeyAndEntry struct { - Key xdr.LedgerKey - Entry xdr.LedgerEntry + Key xdr.LedgerKey + Entry xdr.LedgerEntry + ExpirationLedgerSeq *uint32 // optional expiration ledger seq, whne applicable. } type LedgerEntryReadTx interface { @@ -216,32 +218,71 @@ func (l *ledgerEntryReadTx) getRawLedgerEntries(keys ...string) (map[string]stri return result, nil } -func GetLedgerEntry(tx LedgerEntryReadTx, key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) { +func GetLedgerEntry(tx LedgerEntryReadTx, key xdr.LedgerKey) (bool, xdr.LedgerEntry, *uint32, error) { keyEntries, err := tx.GetLedgerEntries(key) if err != nil { - return false, xdr.LedgerEntry{}, err + return false, xdr.LedgerEntry{}, nil, err } switch len(keyEntries) { case 0: - return false, xdr.LedgerEntry{}, nil + return false, xdr.LedgerEntry{}, nil, nil case 1: // expected length - return true, keyEntries[0].Entry, nil + return true, keyEntries[0].Entry, keyEntries[0].ExpirationLedgerSeq, nil default: - return false, xdr.LedgerEntry{}, fmt.Errorf("multiple entries (%d) for key %v", len(keyEntries), key) + return false, xdr.LedgerEntry{}, nil, fmt.Errorf("multiple entries (%d) for key %v", len(keyEntries), key) } } +// isExpirableKey check to see if the key type is expected to be accompanied by a LedgerExpirationEntry +func isExpirableKey(key xdr.LedgerKey) bool { + switch key.Type { + case xdr.LedgerEntryTypeContractData: + return true + case xdr.LedgerEntryTypeContractCode: + return true + default: + } + return false +} + +func entryKeyToExpirationEntryKey(key xdr.LedgerKey) (xdr.LedgerKey, error) { + buf, err := key.MarshalBinary() + if err != nil { + return xdr.LedgerKey{}, err + } + var expirationEntry xdr.LedgerKey + err = expirationEntry.SetExpiration(xdr.Hash(sha256.Sum256(buf))) + if err != nil { + return xdr.LedgerKey{}, err + } + return expirationEntry, nil +} + func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { - encodedKeys := make([]string, len(keys)) + encodedKeys := make([]string, len(keys), 2*len(keys)) encodedKeyToKey := make(map[string]xdr.LedgerKey, len(keys)) - for i, k := range keys { + encodedKeyToEncodedExpirationLedgerKey := make(map[string]string, len(keys)) + for _, k := range keys { encodedKey, err := encodeLedgerKey(l.buffer, k) if err != nil { return nil, err } - encodedKeys[i] = encodedKey + encodedKeys = append(encodedKeys, encodedKey) encodedKeyToKey[encodedKey] = k + if !isExpirableKey(k) { + continue + } + expirationEntryKey, err := entryKeyToExpirationEntryKey(k) + if err != nil { + return nil, err + } + encodedExpirationKey, err := encodeLedgerKey(l.buffer, expirationEntryKey) + if err != nil { + return nil, err + } + encodedKeyToEncodedExpirationLedgerKey[encodedKey] = encodedExpirationKey + encodedKeys = append(encodedKeys, encodedExpirationKey) } rawResult, err := l.getRawLedgerEntries(encodedKeys...) @@ -259,7 +300,23 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKey if err := xdr.SafeUnmarshal([]byte(encodedEntry), &entry); err != nil { return nil, errors.Wrap(err, "cannot decode ledger entry from DB") } - result = append(result, LedgerKeyAndEntry{key, entry}) + encodedExpKey, has := encodedKeyToEncodedExpirationLedgerKey[encodedKey] + if !has { + result = append(result, LedgerKeyAndEntry{key, entry, nil}) + continue + } + encodedExpEntry, ok := rawResult[encodedExpKey] + if !ok { + // missing expiration key. this should no happen. + return nil, errors.New("missing expiration key entry") + } + var expEntry xdr.LedgerEntry + if err := xdr.SafeUnmarshal([]byte(encodedExpEntry), &expEntry); err != nil { + return nil, errors.Wrap(err, "cannot decode expiration ledger entry from DB") + } + expSeq := uint32(expEntry.Data.Expiration.ExpirationLedgerSeq) + result = append(result, LedgerKeyAndEntry{key, entry, &expSeq}) + } return result, nil diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index 9089ebbf1..49aabaee5 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -15,10 +15,10 @@ import ( "github.com/stellar/go/xdr" ) -func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bool, xdr.LedgerEntry, uint32, error) { +func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bool, xdr.LedgerEntry, uint32, *uint32, error) { tx, err := NewLedgerEntryReader(db).NewTx(context.Background()) if err != nil { - return false, xdr.LedgerEntry{}, 0, err + return false, xdr.LedgerEntry{}, 0, nil, err } var doneErr error defer func() { @@ -27,21 +27,21 @@ func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bo latestSeq, err := tx.GetLatestLedgerSequence() if err != nil { - return false, xdr.LedgerEntry{}, 0, err + return false, xdr.LedgerEntry{}, 0, nil, err } - present, entry, err := GetLedgerEntry(tx, key) + present, entry, expSeq, err := GetLedgerEntry(tx, key) if err != nil { - return false, xdr.LedgerEntry{}, 0, err + return false, xdr.LedgerEntry{}, 0, nil, err } - return present, entry, latestSeq, doneErr + return present, entry, latestSeq, expSeq, doneErr } -func getLedgerEntryAndLatestLedgerSequence(t require.TestingT, db *DB, key xdr.LedgerKey) (bool, xdr.LedgerEntry, uint32) { - present, entry, latestSeq, err := getLedgerEntryAndLatestLedgerSequenceWithErr(db, key) +func getLedgerEntryAndLatestLedgerSequence(t require.TestingT, db *DB, key xdr.LedgerKey) (bool, xdr.LedgerEntry, uint32, *uint32) { + present, entry, latestSeq, expSeq, err := getLedgerEntryAndLatestLedgerSequenceWithErr(db, key) require.NoError(t, err) - return present, entry, latestSeq + return present, entry, latestSeq, expSeq } func TestGoldenPath(t *testing.T) { @@ -74,12 +74,20 @@ func TestGoldenPath(t *testing.T) { } key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) + + expLedgerKey, err := entryKeyToExpirationEntryKey(key) + assert.NoError(t, err) + expLegerEntry := getExpirationLedgerEntry(expLedgerKey) + assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) + ledgerSequence := uint32(23) assert.NoError(t, tx.Commit(ledgerSequence)) - present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) + require.NotNil(t, expSeq) + assert.Equal(t, uint32(expLegerEntry.Data.Expiration.ExpirationLedgerSeq), *expSeq) assert.Equal(t, obtainedEntry.Data.Type, xdr.LedgerEntryTypeContractData) assert.Equal(t, xdr.Hash{0xca, 0xfe}, *obtainedEntry.Data.ContractData.Contract.ContractId) assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) @@ -100,8 +108,9 @@ func TestGoldenPath(t *testing.T) { ledgerSequence = uint32(24) assert.NoError(t, tx.Commit(ledgerSequence)) - present, obtainedEntry, obtainedLedgerSequence = getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, obtainedEntry, obtainedLedgerSequence, expSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) + require.NotNil(t, expSeq) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, eight, *obtainedEntry.Data.ContractData.Val.U32) @@ -115,7 +124,7 @@ func TestGoldenPath(t *testing.T) { ledgerSequence = uint32(25) assert.NoError(t, tx.Commit(ledgerSequence)) - present, _, obtainedLedgerSequence = getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, _, obtainedLedgerSequence, expSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.False(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) @@ -161,8 +170,9 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { assert.Equal(t, ledgerSequence, obtainedLedgerSequence) // And that the entry doesn't exist - present, _, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, _, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.False(t, present) + require.Nil(t, expSeq) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) } @@ -181,6 +191,16 @@ func getContractDataLedgerEntry(t require.TestingT, data xdr.ContractDataEntry) return key, entry } +func getExpirationLedgerEntry(key xdr.LedgerKey) xdr.LedgerEntry { + var expLegerEntry xdr.LedgerEntry + expLegerEntry.Data.Expiration = &xdr.ExpirationEntry{ + KeyHash: key.Expiration.KeyHash, + ExpirationLedgerSeq: 100, + } + expLegerEntry.Data.Type = key.Type + return expLegerEntry +} + // Make sure that (multiple, simultaneous) read transactions can happen while a write-transaction is ongoing, // and write is only visible once the transaction is committed func TestReadTxsDuringWriteTx(t *testing.T) { @@ -214,6 +234,11 @@ func TestReadTxsDuringWriteTx(t *testing.T) { key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) + expLedgerKey, err := entryKeyToExpirationEntryKey(key) + assert.NoError(t, err) + expLegerEntry := getExpirationLedgerEntry(expLedgerKey) + assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) + // Before committing the changes, make sure multiple concurrent transactions can query the DB readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) assert.NoError(t, err) @@ -222,14 +247,15 @@ func TestReadTxsDuringWriteTx(t *testing.T) { _, err = readTx1.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx1, key) + present, _, expSeq, err := GetLedgerEntry(readTx1, key) + require.Nil(t, expSeq) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx1.Done()) _, err = readTx2.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err = GetLedgerEntry(readTx2, key) + present, _, expSeq, err = GetLedgerEntry(readTx2, key) assert.NoError(t, err) assert.False(t, present) assert.NoError(t, readTx2.Done()) @@ -242,7 +268,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { assert.NoError(t, err) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) @@ -292,6 +318,11 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { key, entry := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.UpsertLedgerEntry(entry)) + expLedgerKey, err := entryKeyToExpirationEntryKey(key) + assert.NoError(t, err) + expLegerEntry := getExpirationLedgerEntry(expLedgerKey) + assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) + // Third read transaction, after the first insert has happened in the write transaction readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) assert.NoError(t, err) @@ -300,7 +331,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, key) + present, _, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -312,7 +343,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { _, err = readTx.GetLatestLedgerSequence() assert.Equal(t, ErrEmptyDB, err) - present, _, err := GetLedgerEntry(readTx, key) + present, _, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) } @@ -323,8 +354,9 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { assert.NoError(t, err) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - present, obtainedEntry, obtainedLedgerSequence := getLedgerEntryAndLatestLedgerSequence(t, db, key) + present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) + require.NotNil(t, expSeq) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) @@ -367,8 +399,12 @@ func TestConcurrentReadersAndWriter(t *testing.T) { assert.NoError(t, err) writer := tx.LedgerEntryWriter() for i := 0; i < 200; i++ { - _, entry := getContractDataLedgerEntry(t, data(i)) + key, entry := getContractDataLedgerEntry(t, data(i)) assert.NoError(t, writer.UpsertLedgerEntry(entry)) + expLedgerKey, err := entryKeyToExpirationEntryKey(key) + assert.NoError(t, err) + expLegerEntry := getExpirationLedgerEntry(expLedgerKey) + assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) } assert.NoError(t, tx.Commit(ledgerSequence)) logMessageCh <- fmt.Sprintf("Wrote ledger %d", ledgerSequence) @@ -399,7 +435,7 @@ func TestConcurrentReadersAndWriter(t *testing.T) { return default: } - found, ledgerEntry, ledger, err := getLedgerEntryAndLatestLedgerSequenceWithErr(db, key) + found, ledgerEntry, ledger, _, err := getLedgerEntryAndLatestLedgerSequenceWithErr(db, key) if err != nil { if err != ErrEmptyDB { t.Fatalf("reader %d failed with error %v\n", keyVal, err) @@ -479,7 +515,7 @@ func benchmarkLedgerEntry(b *testing.B, cached bool, includeExpired bool) { assert.NoError(b, err) for i := 0; i < numQueriesPerOp; i++ { b.StartTimer() - found, _, err := GetLedgerEntry(readTx, key) + found, _, _, err := GetLedgerEntry(readTx, key) b.StopTimer() assert.NoError(b, err) assert.True(b, found) diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index b7474e323..61c78166d 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -24,6 +24,8 @@ type LedgerEntryResult struct { XDR string `json:"xdr"` // Last modified ledger for this entry. LastModifiedLedger int64 `json:"lastModifiedLedgerSeq,string"` + // The expiration ledger, available for entries that have expiration ledgers. + ExpirationLedger *uint32 `json:"expirationLedgerSeq,string,omitempty"` } type GetLedgerEntriesResponse struct { @@ -103,6 +105,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn Key: request.Keys[i], XDR: ledgerXDR, LastModifiedLedger: int64(ledgerKeyAndEntry.Entry.LastModifiedLedgerSeq), + ExpirationLedger: ledgerKeyAndEntry.ExpirationLedgerSeq, }) } diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index 4ab908a68..bd0245eb8 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -25,6 +25,8 @@ type GetLedgerEntryResponse struct { XDR string `json:"xdr"` LastModifiedLedger int64 `json:"lastModifiedLedgerSeq,string"` LatestLedger int64 `json:"latestLedger,string"` + // The expiration ledger, available for entries that have expiration ledgers. + ExpirationLedger *uint32 `json:"expirationLedgerSeq,string,omitempty"` } // NewGetLedgerEntryHandler returns a json rpc handler to retrieve the specified ledger entry from stellar core @@ -61,7 +63,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr } } - present, ledgerEntry, err := db.GetLedgerEntry(tx, key) + present, ledgerEntry, ledgerExpirationSeq, err := db.GetLedgerEntry(tx, key) if err != nil { logger.WithError(err).WithField("request", request). Info("could not obtain ledger entry from storage") @@ -81,6 +83,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr response := GetLedgerEntryResponse{ LastModifiedLedger: int64(ledgerEntry.LastModifiedLedgerSeq), LatestLedger: int64(latestLedger), + ExpirationLedger: ledgerExpirationSeq, } if response.XDR, err = xdr.MarshalBase64(ledgerEntry.Data); err != nil { logger.WithError(err).WithField("request", request). diff --git a/cmd/soroban-rpc/internal/preflight/preflight.go b/cmd/soroban-rpc/internal/preflight/preflight.go index 029a6b7da..dc36810c8 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight.go +++ b/cmd/soroban-rpc/internal/preflight/preflight.go @@ -46,7 +46,8 @@ func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey C.xdr_t) C.xdr_t { if err := xdr.SafeUnmarshal(ledgerKeyXDR, &ledgerKey); err != nil { panic(err) } - present, entry, err := db.GetLedgerEntry(h.readTx, ledgerKey) + // TODO : the expiration sequence here is being ignored for now; it should be passed downstream. + present, entry, _, err := db.GetLedgerEntry(h.readTx, ledgerKey) if err != nil { h.logger.WithError(err).Error("SnapshotSourceGet(): GetLedgerEntry() failed") return C.xdr_t{} @@ -181,7 +182,7 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro } sourceAccountCXDR := CXDR(sourceAccountXDR) - hasConfig, stateExpirationConfig, err := db.GetLedgerEntry(params.LedgerEntryReadTx, xdr.LedgerKey{ + hasConfig, stateExpirationConfig, expSeq, err := db.GetLedgerEntry(params.LedgerEntryReadTx, xdr.LedgerKey{ Type: xdr.LedgerEntryTypeConfigSetting, ConfigSetting: &xdr.LedgerKeyConfigSetting{ ConfigSettingId: xdr.ConfigSettingIdConfigSettingStateExpiration, @@ -190,6 +191,9 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro if err != nil { return Preflight{}, err } + if expSeq != nil { + return Preflight{}, errors.New("configuration setting are not expected to be expiring, yet, an expiration ledger sequence was found for ledger entry") + } if !hasConfig { return Preflight{}, errors.New("state expiration config setting missing in ledger storage") } From ac85d7e1c432332e85ee72129c68356d3b11eb31 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:57:35 -0400 Subject: [PATCH 21/35] linter --- cmd/soroban-rpc/internal/db/ledgerentry.go | 1 - cmd/soroban-rpc/internal/db/ledgerentry_test.go | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index dfe3d1dfd..37ca0d7e9 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -316,7 +316,6 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKey } expSeq := uint32(expEntry.Data.Expiration.ExpirationLedgerSeq) result = append(result, LedgerKeyAndEntry{key, entry, &expSeq}) - } return result, nil diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index 49aabaee5..5da7791ef 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -126,6 +126,7 @@ func TestGoldenPath(t *testing.T) { present, _, obtainedLedgerSequence, expSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.False(t, present) + assert.Nil(t, expSeq) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) obtainedLedgerSequence, err = NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) @@ -258,6 +259,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { present, _, expSeq, err = GetLedgerEntry(readTx2, key) assert.NoError(t, err) assert.False(t, present) + assert.Nil(t, expSeq) assert.NoError(t, readTx2.Done()) // Finish the write transaction and check that the results are present @@ -272,6 +274,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { assert.True(t, present) assert.Equal(t, ledgerSequence, obtainedLedgerSequence) assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) + assert.NotNil(t, expSeq) } // Make sure that a write transaction can happen while multiple read transactions are ongoing, From a441688c74c91985e254f26e68cca71c470d07dd Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 5 Sep 2023 12:09:30 -0400 Subject: [PATCH 22/35] limit get_ledger_entries to expiration ledger entries. --- cmd/soroban-rpc/internal/methods/get_ledger_entries.go | 8 ++++++++ cmd/soroban-rpc/internal/methods/get_ledger_entry.go | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index 61c78166d..cb5e1280d 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -57,6 +57,14 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn Message: fmt.Sprintf("cannot unmarshal key value %s at index %d", requestKey, i), } } + if ledgerKey.Type == xdr.LedgerEntryTypeExpiration { + logger.WithField("request", request). + Infof("could not provide ledger expiration entry %s at index %d from getLedgerEntries request", requestKey, i) + return GetLedgerEntriesResponse{}, &jrpc2.Error{ + Code: jrpc2.InvalidParams, + Message: "ledger expiration entries cannot be queried directly", + } + } ledgerKeys = append(ledgerKeys, ledgerKey) } diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index bd0245eb8..6a427ddad 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -44,6 +44,13 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr } } + if key.Type == xdr.LedgerEntryTypeExpiration { + return GetLedgerEntryResponse{}, &jrpc2.Error{ + Code: jrpc2.InvalidParams, + Message: "ledger expiration entries cannot be queried directly", + } + } + tx, err := ledgerEntryReader.NewTx(ctx) if err != nil { return GetLedgerEntryResponse{}, &jrpc2.Error{ From 42ccee9539b0190d47831ad10651500d52ed61b8 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 5 Sep 2023 12:20:24 -0400 Subject: [PATCH 23/35] fix cli serialization --- cmd/soroban-cli/src/rpc/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index 0e9796482..23fb7ead9 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -144,6 +144,12 @@ pub struct LedgerEntryResult { pub xdr: String, #[serde(rename = "lastModifiedLedgerSeq")] pub last_modified_ledger: String, + #[serde( + rename = "expirationLedgerSeq", + skip_serializing_if = "Option::is_none", + default + )] + pub expiration_ledger_seq: Option, } #[derive(serde::Deserialize, serde::Serialize, Debug)] From ad8d4ab0cdd9d4efeff627f26265687a5f027fea Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:28:05 -0400 Subject: [PATCH 24/35] update test --- .../internal/test/simulate_transaction_test.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index cc6d4ed77..543f38191 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -695,17 +695,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { }, } - binKey, err := key.MarshalBinary() - assert.NoError(t, err) - - expirationKey := xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeExpiration, - Expiration: &xdr.LedgerKeyExpiration{ - KeyHash: sha256.Sum256(binKey), - }, - } - - keyB64, err := xdr.MarshalBase64(expirationKey) + keyB64, err := xdr.MarshalBase64(key) require.NoError(t, err) getLedgerEntryrequest := methods.GetLedgerEntryRequest{ Key: keyB64, @@ -713,11 +703,13 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { var getLedgerEntryResult methods.GetLedgerEntryResponse err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) + var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) - initialExpirationSeq := entry.Expiration.ExpirationLedgerSeq + require.NotNil(t, getLedgerEntryResult.ExpirationLedger) + + initialExpirationSeq := *getLedgerEntryResult.ExpirationLedger // bump the initial expiration params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ From bd3e64196809272b40a3ac496f8fb15a50eb0811 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:42:36 -0400 Subject: [PATCH 25/35] further fix.. --- .../internal/test/simulate_transaction_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 543f38191..4182f8a6f 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -743,7 +743,8 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) - newExpirationSeq := entry.Expiration.ExpirationLedgerSeq + require.NotNil(t, getLedgerEntryResult.ExpirationLedger) + newExpirationSeq := *getLedgerEntryResult.ExpirationLedger assert.Greater(t, newExpirationSeq, initialExpirationSeq) // Wait until it expires @@ -756,12 +757,13 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger currentLedger := getLedgerEntryResult.LatestLedger + 1 - if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq { + require.NotNil(t, getLedgerEntryResult.ExpirationLedger) + if uint32(currentLedger) > *getLedgerEntryResult.ExpirationLedger { expired = true t.Logf("ledger entry expired") break } - t.Log("waiting for ledger entry to expire at ledger", entry.Expiration.ExpirationLedgerSeq) + t.Log("waiting for ledger entry to expire at ledger", *getLedgerEntryResult.ExpirationLedger) time.Sleep(time.Second) } require.True(t, expired) From 1928ea8c558885af5919d5be53d756549738b8bc Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:59:02 -0400 Subject: [PATCH 26/35] further fix. --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 4182f8a6f..fa8788de5 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -754,7 +754,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + assert.Equal(t, xdr.LedgerEntryTypeContractData, entry.Type) // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger currentLedger := getLedgerEntryResult.LatestLedger + 1 require.NotNil(t, getLedgerEntryResult.ExpirationLedger) From b72120180f2c85406b036428c739e863c251d07b Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:16:19 -0400 Subject: [PATCH 27/35] further fix --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index fa8788de5..9d0d4a839 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -706,7 +706,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + assert.Equal(t, xdr.LedgerEntryTypeContractData, entry.Type) require.NotNil(t, getLedgerEntryResult.ExpirationLedger) initialExpirationSeq := *getLedgerEntryResult.ExpirationLedger @@ -742,7 +742,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) assert.NoError(t, err) assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + assert.Equal(t, xdr.LedgerEntryTypeContractData, entry.Type) require.NotNil(t, getLedgerEntryResult.ExpirationLedger) newExpirationSeq := *getLedgerEntryResult.ExpirationLedger assert.Greater(t, newExpirationSeq, initialExpirationSeq) From ec013b7af749b145eea8c4b58594d23120ed4536 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:46:16 -0400 Subject: [PATCH 28/35] update test --- cmd/soroban-rpc/internal/test/cli_test.go | 12 ++++++++++++ .../test/simulate_transaction_test.go | 19 +++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index bb3a789ac..f8c76ac48 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -216,6 +216,18 @@ func TestCLIRestore(t *testing.T) { assert.Equal(t, fmt.Sprintf("New expiration ledger: %d", newExpirationSeq), restoreOutput) } +func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { + assert.True(t, key.Type == xdr.LedgerEntryTypeContractCode || key.Type == xdr.LedgerEntryTypeContractData) + binKey, err := key.MarshalBinary() + assert.NoError(t, err) + return xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, + } +} + func getExpirationKeyForCounterLedgerEntry(t *testing.T, strkeyContractID string) xdr.LedgerKey { return getExpirationKey(t, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) } diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index a79ed1135..0cf258e8d 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -730,7 +730,6 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { // get the counter ledger entry expiration key := getCounterLedgerKey(contractID) - expirationKey := getExpirationKey(t, key) keyB64, err := xdr.MarshalBase64(key) require.NoError(t, err) @@ -835,7 +834,7 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { sendSuccessfulTransaction(t, client, sourceAccount, tx) // Wait for expiration again and check the pre-restore field when trying to exec the contract again - waitForLedgerEntryToExpire(t, client, expirationKey) + waitForLedgerEntryToExpire(t, client, key) simulationResult := simulateTransactionFromTxParams(t, client, invokeIncPresistentEntryParams) require.NotNil(t, simulationResult.RestorePreamble) @@ -869,18 +868,6 @@ func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) { sendSuccessfulTransaction(t, client, sourceAccount, tx) } -func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { - assert.True(t, key.Type == xdr.LedgerEntryTypeContractCode || key.Type == xdr.LedgerEntryTypeContractData) - binKey, err := key.MarshalBinary() - assert.NoError(t, err) - return xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeExpiration, - Expiration: &xdr.LedgerKeyExpiration{ - KeyHash: sha256.Sum256(binKey), - }, - } -} - func getCounterLedgerKey(contractID [32]byte) xdr.LedgerKey { contractIDHash := xdr.Hash(contractID) counterSym := xdr.ScSymbol("COUNTER") @@ -917,8 +904,8 @@ func getExpirationForLedgerEntry(t *testing.T, client *jrpc2.Client, expirationL return entry.Expiration.ExpirationLedgerSeq } -func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, expirationKey xdr.LedgerKey) { - keyB64, err := xdr.MarshalBase64(expirationKey) +func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, ledgerKey xdr.LedgerKey) { + keyB64, err := xdr.MarshalBase64(ledgerKey) require.NoError(t, err) getLedgerEntryrequest := methods.GetLedgerEntryRequest{ Key: keyB64, From babcb57ae6c3ee7506dcbf724f986f4896c6bf4d Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:30:07 -0400 Subject: [PATCH 29/35] fix typo --- cmd/soroban-rpc/internal/db/ledgerentry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 37ca0d7e9..87fbd75dd 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -26,7 +26,7 @@ type LedgerEntryReader interface { type LedgerKeyAndEntry struct { Key xdr.LedgerKey Entry xdr.LedgerEntry - ExpirationLedgerSeq *uint32 // optional expiration ledger seq, whne applicable. + ExpirationLedgerSeq *uint32 // optional expiration ledger seq, when applicable. } type LedgerEntryReadTx interface { From b4838f8168e8837ee8c057d80d05c14b948e4442 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 12 Oct 2023 13:24:23 -0400 Subject: [PATCH 30/35] fix: update CLI and tests for new getLedgerEntries (#1021) * fix: add latest_modified_ledger to FullLedgerEntry Also simplifies parsing FullLedgerEntry * fix: CLI test for contract read * fix: update tests to use getLedgerEntries * fix: nit --- .../soroban-test/tests/it/hello_world.rs | 4 +- cmd/soroban-cli/src/commands/contract/bump.rs | 2 +- cmd/soroban-cli/src/commands/contract/read.rs | 33 ++++---- cmd/soroban-cli/src/rpc/mod.rs | 82 +++++++++---------- cmd/soroban-rpc/internal/test/cli_test.go | 2 +- .../test/simulate_transaction_test.go | 20 +++-- 6 files changed, 74 insertions(+), 69 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/hello_world.rs b/cmd/crates/soroban-test/tests/it/hello_world.rs index 30ee8c907..a87aff892 100644 --- a/cmd/crates/soroban-test/tests/it/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/hello_world.rs @@ -310,7 +310,7 @@ fn contract_data_read() { .arg("--durability=persistent") .assert() .success() - .stdout("COUNTER,1,4096\n"); + .stdout("COUNTER,1,4,4096\n"); sandbox .new_assert_cmd("contract") @@ -331,7 +331,7 @@ fn contract_data_read() { .arg("--durability=persistent") .assert() .success() - .stdout("COUNTER,2,4096\n"); + .stdout("COUNTER,2,4,4096\n"); } #[test] diff --git a/cmd/soroban-cli/src/commands/contract/bump.rs b/cmd/soroban-cli/src/commands/contract/bump.rs index c1e24c8fc..06a86cf92 100644 --- a/cmd/soroban-cli/src/commands/contract/bump.rs +++ b/cmd/soroban-cli/src/commands/contract/bump.rs @@ -175,7 +175,7 @@ impl Cmd { if operations[0].changes.is_empty() { let entry = client.get_full_ledger_entries(&keys).await?; - let expire = entry.entries[0].expiration.expiration_ledger_seq; + let expire = entry.entries[0].expiration_ledger_seq; if entry.latest_ledger + i64::from(ledgers_to_expire) < i64::from(expire) { return Ok(expire); } diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index c0f40291c..4c47f8887 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -1,15 +1,13 @@ use std::{ - convert::Into, fmt::Debug, io::{self, stdout}, }; use clap::{command, Parser, ValueEnum}; -use sha2::{Digest, Sha256}; use soroban_env_host::{ xdr::{ - ContractDataEntry, Error as XdrError, ExpirationEntry, Hash, LedgerEntryData, LedgerKey, - LedgerKeyContractData, ScVal, WriteXdr, + ContractDataEntry, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractData, + ScVal, WriteXdr, }, HostError, }; @@ -113,7 +111,7 @@ impl Cmd { fn run_in_sandbox(&self) -> Result { let state = self.config.get_state()?; let ledger_entries = &state.ledger_entries; - + let latest_ledger = u32::try_from(state.ledger_entries.len()).unwrap(); let keys = self.key.parse_keys()?; let entries = ledger_entries .iter() @@ -121,10 +119,8 @@ impl Cmd { .filter(|(k, _v)| keys.contains(k)) .map(|(key, (v, expiration))| { Ok(FullLedgerEntry { - expiration: ExpirationEntry { - key_hash: Hash(Sha256::digest(key.to_xdr()?).into()), - expiration_ledger_seq: expiration.unwrap_or_default(), - }, + expiration_ledger_seq: expiration.unwrap_or_default(), + last_modified_ledger: latest_ledger, key, val: v.data, }) @@ -145,7 +141,8 @@ impl Cmd { for FullLedgerEntry { key, val, - expiration, + expiration_ledger_seq, + last_modified_ledger, } in &entries.entries { let ( @@ -155,8 +152,6 @@ impl Cmd { else { return Err(Error::OnlyDataAllowed); }; - let expiration = expiration.expiration_ledger_seq; - let output = match self.output { Output::String => [ soroban_spec_tools::to_string(key).map_err(|e| Error::CannotPrintResult { @@ -167,7 +162,8 @@ impl Cmd { result: val.clone(), error: e, })?, - expiration.to_string(), + last_modified_ledger.to_string(), + expiration_ledger_seq.to_string(), ], Output::Json => [ serde_json::to_string_pretty(&key).map_err(|error| { @@ -182,7 +178,13 @@ impl Cmd { error, } })?, - serde_json::to_string_pretty(&expiration).map_err(|error| { + serde_json::to_string_pretty(&last_modified_ledger).map_err(|error| { + Error::CannotPrintJsonResult { + result: val.clone(), + error, + } + })?, + serde_json::to_string_pretty(&expiration_ledger_seq).map_err(|error| { Error::CannotPrintJsonResult { result: val.clone(), error, @@ -192,7 +194,8 @@ impl Cmd { Output::Xdr => [ key.to_xdr_base64()?, val.to_xdr_base64()?, - expiration.to_xdr_base64()?, + last_modified_ledger.to_xdr_base64()?, + expiration_ledger_seq.to_xdr_base64()?, ], }; out.write_record(output) diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index 897152619..dbced68d7 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -3,14 +3,16 @@ use itertools::Itertools; use jsonrpsee_core::params::ObjectParams; use jsonrpsee_core::{self, client::ClientT, rpc_params}; use jsonrpsee_http_client::{HeaderMap, HttpClient, HttpClientBuilder}; -use serde_aux::prelude::{deserialize_default_from_null, deserialize_number_from_string}; -use sha2::{Digest, Sha256}; +use serde_aux::prelude::{ + deserialize_default_from_null, deserialize_number_from_string, + deserialize_option_number_from_string, +}; use soroban_env_host::xdr::{ self, AccountEntry, AccountId, ContractDataEntry, DiagnosticEvent, Error as XdrError, - ExpirationEntry, LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, - LedgerKeyExpiration, PublicKey, ReadXdr, SequenceNumber, SorobanAuthorizationEntry, - SorobanResources, Transaction, TransactionEnvelope, TransactionMeta, TransactionMetaV3, - TransactionResult, TransactionV1Envelope, Uint256, VecM, WriteXdr, + LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, PublicKey, ReadXdr, + SequenceNumber, SorobanAuthorizationEntry, SorobanResources, Transaction, TransactionEnvelope, + TransactionMeta, TransactionMetaV3, TransactionResult, TransactionV1Envelope, Uint256, VecM, + WriteXdr, }; use soroban_env_host::xdr::{DepthLimitedRead, SorobanAuthorizedFunction}; use soroban_sdk::token; @@ -96,8 +98,8 @@ pub enum Error { SpecBase64(#[from] soroban_spec::read::ParseSpecBase64Error), #[error("Fee was too large {0}")] LargeFee(u64), - #[error("Failed to parse LedgerEntryData")] - FailedParseLedgerEntryData, + #[error("Failed to parse LedgerEntryData\nkey:{0:?}\nvalue:{1:?}\nexpiration:{2:?}")] + FailedParseLedgerEntryData(LedgerKey, LedgerEntryData, LedgerEntryData), } #[derive(serde::Deserialize, serde::Serialize, Debug)] @@ -146,14 +148,18 @@ pub struct GetTransactionResponse { pub struct LedgerEntryResult { pub key: String, pub xdr: String, - #[serde(rename = "lastModifiedLedgerSeq")] - pub last_modified_ledger: String, + #[serde( + rename = "lastModifiedLedgerSeq", + deserialize_with = "deserialize_number_from_string" + )] + pub last_modified_ledger: u32, #[serde( rename = "expirationLedgerSeq", skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_option_number_from_string", default )] - pub expiration_ledger_seq: Option, + pub expiration_ledger_seq: Option, } #[derive(serde::Deserialize, serde::Serialize, Debug)] @@ -433,7 +439,8 @@ pub enum EventStart { pub struct FullLedgerEntry { pub key: LedgerKey, pub val: LedgerEntryData, - pub expiration: ExpirationEntry, + pub last_modified_ledger: u32, + pub expiration_ledger_seq: u32, } #[derive(Debug)] @@ -501,10 +508,10 @@ impl Client { pub async fn verify_network_passphrase(&self, expected: Option<&str>) -> Result { let server = self.get_network().await?.passphrase; - if expected != Some(&server) { - if let Some(expected_val) = expected { + if let Some(expected) = expected { + if expected != server { return Err(Error::InvalidNetworkPassphrase { - expected: expected_val.to_string(), + expected: expected.to_string(), server, }); } @@ -775,35 +782,35 @@ soroban config identity fund {address} --helper-url "# ) -> Result { let keys = ledger_keys .iter() - .map(|key| Ok(into_keys(key.clone())?.into_iter())) - .flatten_ok() - .collect::, Error>>()?; - tracing::trace!("{keys:#?}"); + .filter(|key| !matches!(key, LedgerKey::Expiration(_))) + .map(Clone::clone) + .collect::>(); + tracing::trace!("keys: {keys:#?}"); let GetLedgerEntriesResponse { entries, latest_ledger, } = self.get_ledger_entries(&keys).await?; - tracing::trace!(?entries); + tracing::trace!("raw: {entries:#?}"); let entries = entries - .as_deref() .unwrap_or_default() .iter() - .tuple_windows() - .map(|(key_res, entry_res)| { - let expiration = LedgerEntryData::from_xdr_base64(&entry_res.xdr)?; - if let LedgerEntryData::Expiration(expiration) = expiration { - let key = LedgerKey::from_xdr_base64(&key_res.key)?; - let val = LedgerEntryData::from_xdr_base64(&key_res.xdr)?; + .map( + |LedgerEntryResult { + key, + xdr, + last_modified_ledger, + expiration_ledger_seq, + }| { Ok(FullLedgerEntry { - key, - val, - expiration, + key: LedgerKey::from_xdr_base64(key)?, + val: LedgerEntryData::from_xdr_base64(xdr)?, + expiration_ledger_seq: expiration_ledger_seq.unwrap_or_default(), + last_modified_ledger: *last_modified_ledger, }) - } else { - Err(Error::FailedParseLedgerEntryData) - } - }) + }, + ) .collect::, Error>>()?; + tracing::trace!("parsed: {entries:#?}"); Ok(FullLedgerEntries { entries, latest_ledger, @@ -960,13 +967,6 @@ pub fn parse_cursor(c: &str) -> Result<(u64, i32), Error> { Ok((toid_part, start_index)) } -fn into_keys(key: LedgerKey) -> Result<[LedgerKey; 2], Error> { - let expiration = LedgerKey::Expiration(LedgerKeyExpiration { - key_hash: xdr::Hash(Sha256::digest(key.to_xdr()?).into()), - }); - Ok([key, expiration]) -} - #[cfg(test)] mod tests { use super::*; diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index f8c76ac48..f4a73d3e8 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -229,7 +229,7 @@ func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { } func getExpirationKeyForCounterLedgerEntry(t *testing.T, strkeyContractID string) xdr.LedgerKey { - return getExpirationKey(t, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) + return getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) } func parseContractStrKey(t *testing.T, strkeyContractID string) [32]byte { diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 0cf258e8d..34049e379 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -907,25 +907,27 @@ func getExpirationForLedgerEntry(t *testing.T, client *jrpc2.Client, expirationL func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, ledgerKey xdr.LedgerKey) { keyB64, err := xdr.MarshalBase64(ledgerKey) require.NoError(t, err) - getLedgerEntryrequest := methods.GetLedgerEntryRequest{ - Key: keyB64, + request := methods.GetLedgerEntriesRequest{ + Keys: []string{keyB64}, } expired := false for i := 0; i < 50; i++ { - var getLedgerEntryResult methods.GetLedgerEntryResponse + var result methods.GetLedgerEntriesResponse var entry xdr.LedgerEntryData - err := client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) + err := client.CallResult(context.Background(), "getLedgerEntries", request, &result) assert.NoError(t, err) - assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + assert.NotEmpty(t, result.Entries) + assert.NoError(t, xdr.SafeUnmarshalBase64(result.Entries[0].XDR, &entry)) + assert.NotEqual(t, xdr.LedgerEntryTypeExpiration, entry.Type) + expirationLedgerSeq := xdr.Uint32(*result.Entries[0].ExpirationLedger) // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger - currentLedger := getLedgerEntryResult.LatestLedger + 1 - if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq { + currentLedger := result.LatestLedger + 1 + if xdr.Uint32(currentLedger) > expirationLedgerSeq { expired = true t.Logf("ledger entry expired") break } - t.Log("waiting for ledger entry to expire at ledger", entry.Expiration.ExpirationLedgerSeq) + t.Log("waiting for ledger entry to expire at ledger", expirationLedgerSeq) time.Sleep(time.Second) } require.True(t, expired) From caccfb3c8f764d980899509841dc45a6f74b3e83 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Oct 2023 13:24:54 -0400 Subject: [PATCH 31/35] step --- cmd/soroban-rpc/internal/test/simulate_transaction_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 0cf258e8d..9fe2b5a1e 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -915,11 +915,12 @@ func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, ledgerKey xd var getLedgerEntryResult methods.GetLedgerEntryResponse var entry xdr.LedgerEntryData err := client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) - assert.NoError(t, err) - assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) + require.NoError(t, err) + require.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) + require.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) // See https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#expiration-ledger currentLedger := getLedgerEntryResult.LatestLedger + 1 + require.NotNil(t, entry.Expiration) if xdr.Uint32(currentLedger) > entry.Expiration.ExpirationLedgerSeq { expired = true t.Logf("ledger entry expired") From 1c47ba8beb931af632f4e2236e4f9fee49868d08 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:23:47 -0400 Subject: [PATCH 32/35] update --- cmd/soroban-rpc/internal/test/cli_test.go | 14 +++++--------- .../internal/test/simulate_transaction_test.go | 5 +++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index f4a73d3e8..bfad4e499 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -117,7 +117,7 @@ func TestCLIRestorePreamble(t *testing.T) { // This ensures that the CLI restores the entry (using the RestorePreamble in the simulateTransaction response) ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - waitForLedgerEntryToExpire(t, client, getExpirationKeyForCounterLedgerEntry(t, strkeyContractID)) + waitForLedgerEntryToExpire(t, client, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) count = runSuccessfulCLICmd(t, fmt.Sprintf("contract invoke --id %s -- inc", strkeyContractID)) require.Equal(t, "3", count) @@ -132,7 +132,7 @@ func TestCLIBump(t *testing.T) { ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - expirationKey := getExpirationKeyForCounterLedgerEntry(t, strkeyContractID) + expirationKey := getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) initialExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey) bumpOutput := runSuccessfulCLICmd( @@ -156,7 +156,7 @@ func TestCLIBumpTooLow(t *testing.T) { ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - expirationKey := getExpirationKeyForCounterLedgerEntry(t, strkeyContractID) + expirationKey := getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) initialExpirationSeq := parseInt(t, getExpirationForLedgerEntry(t, client, expirationKey).GoString()) bumpOutput := bump(t, strkeyContractID, "400", "--key COUNTER ") @@ -178,7 +178,7 @@ func TestCLIBumpTooHigh(t *testing.T) { ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - expirationKey := getExpirationKeyForCounterLedgerEntry(t, strkeyContractID) + expirationKey := getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) initialExpirationSeq := parseInt(t, getExpirationForLedgerEntry(t, client, expirationKey).GoString()) bumpOutput := bump(t, strkeyContractID, "100000000", "--key COUNTER ") @@ -197,7 +197,7 @@ func TestCLIRestore(t *testing.T) { ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - expirationKey := getExpirationKeyForCounterLedgerEntry(t, strkeyContractID) + expirationKey := getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) initialExpirationSeq := getExpirationForLedgerEntry(t, client, expirationKey) // Wait for the counter ledger entry to expire and successfully invoke the `inc` contract function again // This ensures that the CLI restores the entry (using the RestorePreamble in the simulateTransaction response) @@ -228,10 +228,6 @@ func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { } } -func getExpirationKeyForCounterLedgerEntry(t *testing.T, strkeyContractID string) xdr.LedgerKey { - return getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)) -} - func parseContractStrKey(t *testing.T, strkeyContractID string) [32]byte { contractIDBytes := strkey.MustDecode(strkey.VersionByteContract, strkeyContractID) var contractID [32]byte diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 4c62fa8b2..a2c7117d5 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -900,8 +900,9 @@ func getExpirationForLedgerEntry(t *testing.T, client *jrpc2.Client, expirationL var entry xdr.LedgerEntryData assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) - assert.Equal(t, xdr.LedgerEntryTypeExpiration, entry.Type) - return entry.Expiration.ExpirationLedgerSeq + require.Contains(t, []xdr.LedgerEntryType{xdr.LedgerEntryTypeContractCode, xdr.LedgerEntryTypeContractData}, entry.Type) + require.NotNil(t, getLedgerEntryResult.ExpirationLedger) + return xdr.Uint32(*getLedgerEntryResult.ExpirationLedger) } func waitForLedgerEntryToExpire(t *testing.T, client *jrpc2.Client, ledgerKey xdr.LedgerKey) { From 29f0ae7c6ea149eff8a481c205e6f71ab59772ff Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Oct 2023 16:14:28 -0400 Subject: [PATCH 33/35] fix tests --- cmd/soroban-rpc/internal/test/cli_test.go | 14 +------------- .../internal/test/simulate_transaction_test.go | 4 ++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index bfad4e499..26928bc06 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -211,23 +211,11 @@ func TestCLIRestore(t *testing.T) { ), ) - newExpirationSeq := getExpirationForLedgerEntry(t, client, getExpirationKey(t, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID)))) + newExpirationSeq := getExpirationForLedgerEntry(t, client, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) assert.Greater(t, newExpirationSeq, initialExpirationSeq) assert.Equal(t, fmt.Sprintf("New expiration ledger: %d", newExpirationSeq), restoreOutput) } -func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { - assert.True(t, key.Type == xdr.LedgerEntryTypeContractCode || key.Type == xdr.LedgerEntryTypeContractData) - binKey, err := key.MarshalBinary() - assert.NoError(t, err) - return xdr.LedgerKey{ - Type: xdr.LedgerEntryTypeExpiration, - Expiration: &xdr.LedgerKeyExpiration{ - KeyHash: sha256.Sum256(binKey), - }, - } -} - func parseContractStrKey(t *testing.T, strkeyContractID string) [32]byte { contractIDBytes := strkey.MustDecode(strkey.VersionByteContract, strkeyContractID) var contractID [32]byte diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index a2c7117d5..3e0f62ba1 100644 --- a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go @@ -896,9 +896,9 @@ func getExpirationForLedgerEntry(t *testing.T, client *jrpc2.Client, expirationL } var getLedgerEntryResult methods.GetLedgerEntryResponse err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &getLedgerEntryResult) - assert.NoError(t, err) + require.NoError(t, err) var entry xdr.LedgerEntryData - assert.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) + require.NoError(t, xdr.SafeUnmarshalBase64(getLedgerEntryResult.XDR, &entry)) require.Contains(t, []xdr.LedgerEntryType{xdr.LedgerEntryTypeContractCode, xdr.LedgerEntryTypeContractData}, entry.Type) require.NotNil(t, getLedgerEntryResult.ExpirationLedger) From e095f8391d5df96a042839f35a86596c0f99b57d Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Oct 2023 16:48:46 -0400 Subject: [PATCH 34/35] updating --- .../internal/test/simulate_transaction_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-rpc/internal/test/simulate_transaction_test.go b/cmd/soroban-rpc/internal/test/simulate_transaction_test.go index 3e0f62ba1..e1c02b8e4 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: 6070660, + Instructions: 6062311, ReadBytes: 48, - WriteBytes: 7060, + WriteBytes: 7048, }, RefundableFee: 20056, } @@ -243,7 +243,11 @@ func TestSimulateTransactionSucceeds(t *testing.T) { var transactionData xdr.SorobanTransactionData err := xdr.SafeUnmarshalBase64(result.TransactionData, &transactionData) assert.NoError(t, err) - assert.Equal(t, expectedTransactionData, transactionData) + assert.Equal(t, expectedTransactionData.Resources.Footprint, transactionData.Resources.Footprint) + assert.InDelta(t, uint32(expectedTransactionData.Resources.Instructions), uint32(transactionData.Resources.Instructions), 100000) + assert.InDelta(t, uint32(expectedTransactionData.Resources.ReadBytes), uint32(transactionData.Resources.ReadBytes), 10) + assert.InDelta(t, uint32(expectedTransactionData.Resources.WriteBytes), uint32(transactionData.Resources.WriteBytes), 100) + assert.InDelta(t, int64(expectedTransactionData.RefundableFee), int64(transactionData.RefundableFee), 1000) // Then decode and check the result xdr, separately so we get a decent diff if it fails. assert.Len(t, result.Results, 1) @@ -1154,8 +1158,8 @@ func TestSimulateSystemEvent(t *testing.T) { err = xdr.SafeUnmarshalBase64(response.TransactionData, &transactionData) require.NoError(t, err) - assert.Equal(t, xdr.Int64(45), transactionData.RefundableFee) - assert.Equal(t, xdr.Uint32(7260), transactionData.Resources.ReadBytes) - assert.Equal(t, xdr.Uint32(104), transactionData.Resources.WriteBytes) + assert.InDelta(t, 7260, uint32(transactionData.Resources.ReadBytes), 200) + assert.InDelta(t, 45, int64(transactionData.RefundableFee), 10) + assert.InDelta(t, 104, uint32(transactionData.Resources.WriteBytes), 15) require.GreaterOrEqual(t, len(response.Events), 3) } From 9c44d71b1685b42683bee6121b6ef7247895af09 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:50:00 -0400 Subject: [PATCH 35/35] update per code review --- .../internal/methods/get_ledger_entries.go | 4 ++- .../internal/methods/get_ledger_entry.go | 2 +- cmd/soroban-rpc/internal/test/cli_test.go | 33 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index 626f7b00e..00c3ad22a 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -14,6 +14,8 @@ import ( "github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/db" ) +var ErrLedgerExpirationEntriesCannotBeQueriedDirectly = "ledger expiration entries cannot be queried directly" + type GetLedgerEntriesRequest struct { Keys []string `json:"keys"` } @@ -71,7 +73,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn Infof("could not provide ledger expiration entry %s at index %d from getLedgerEntries request", requestKey, i) return GetLedgerEntriesResponse{}, &jrpc2.Error{ Code: jrpc2.InvalidParams, - Message: "ledger expiration entries cannot be queried directly", + Message: ErrLedgerExpirationEntriesCannotBeQueriedDirectly, } } ledgerKeys = append(ledgerKeys, ledgerKey) diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index 659f2ed89..1da93f9dd 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -55,7 +55,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr if key.Type == xdr.LedgerEntryTypeExpiration { return GetLedgerEntryResponse{}, &jrpc2.Error{ Code: jrpc2.InvalidParams, - Message: "ledger expiration entries cannot be queried directly", + Message: ErrLedgerExpirationEntriesCannotBeQueriedDirectly, } } diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index 26928bc06..1634df6c3 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -1,6 +1,7 @@ package test import ( + "context" "crypto/sha256" "encoding/hex" "fmt" @@ -16,6 +17,7 @@ import ( "github.com/stellar/go/strkey" "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" + "github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/methods" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/icmd" @@ -214,6 +216,37 @@ func TestCLIRestore(t *testing.T) { newExpirationSeq := getExpirationForLedgerEntry(t, client, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) assert.Greater(t, newExpirationSeq, initialExpirationSeq) assert.Equal(t, fmt.Sprintf("New expiration ledger: %d", newExpirationSeq), restoreOutput) + + // test to see that we get an error when requesting the expiration ledger entry explicitly. + ledgerExpirationEntry := getExpirationKey(t, getCounterLedgerKey(parseContractStrKey(t, strkeyContractID))) + ledgerExpirationEntryB64, err := xdr.MarshalBase64(ledgerExpirationEntry) + require.NoError(t, err) + var getLedgerEntryResult methods.GetLedgerEntryResponse + err = client.CallResult(context.Background(), "getLedgerEntry", methods.GetLedgerEntryRequest{ + Key: ledgerExpirationEntryB64, + }, &getLedgerEntryResult) + require.Error(t, err) + require.Contains(t, err.Error(), methods.ErrLedgerExpirationEntriesCannotBeQueriedDirectly) + + // repeat with getLedgerEntries + var getLedgerEntriesResult methods.GetLedgerEntriesResponse + err = client.CallResult(context.Background(), "getLedgerEntries", methods.GetLedgerEntriesRequest{ + Keys: []string{ledgerExpirationEntryB64}, + }, &getLedgerEntriesResult) + require.Error(t, err) + require.Contains(t, err.Error(), methods.ErrLedgerExpirationEntriesCannotBeQueriedDirectly) +} + +func getExpirationKey(t *testing.T, key xdr.LedgerKey) xdr.LedgerKey { + assert.True(t, key.Type == xdr.LedgerEntryTypeContractCode || key.Type == xdr.LedgerEntryTypeContractData) + binKey, err := key.MarshalBinary() + assert.NoError(t, err) + return xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeExpiration, + Expiration: &xdr.LedgerKeyExpiration{ + KeyHash: sha256.Sum256(binKey), + }, + } } func parseContractStrKey(t *testing.T, strkeyContractID string) [32]byte {