diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 2e22cbc7a..1553ecf59 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -260,16 +260,22 @@ func entryKeyToTTLEntryKey(key xdr.LedgerKey) (xdr.LedgerKey, error) { } func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKeyAndEntry, error) { - encodedKeys := make([]string, len(keys), 2*len(keys)) - encodedKeyToKey := make(map[string]xdr.LedgerKey, len(keys)) - encodedKeyToEncodedTTLLedgerKey := make(map[string]string, len(keys)) - for _, k := range keys { + encodedKeys := make([]string, 0, 2*len(keys)) + type keyToEncoded struct { + key xdr.LedgerKey + encodedKey string + encodedTTLKey *string + } + keysToEncoded := make([]keyToEncoded, len(keys)) + for i, k := range keys { + k2 := k + keysToEncoded[i].key = k2 encodedKey, err := encodeLedgerKey(l.buffer, k) if err != nil { return nil, err } + keysToEncoded[i].encodedKey = encodedKey encodedKeys = append(encodedKeys, encodedKey) - encodedKeyToKey[encodedKey] = k if !hasTTLKey(k) { continue } @@ -281,7 +287,7 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKey if err != nil { return nil, err } - encodedKeyToEncodedTTLLedgerKey[encodedKey] = encodedTTLKey + keysToEncoded[i].encodedTTLKey = &encodedTTLKey encodedKeys = append(encodedKeys, encodedTTLKey) } @@ -290,9 +296,9 @@ func (l *ledgerEntryReadTx) GetLedgerEntries(keys ...xdr.LedgerKey) ([]LedgerKey return nil, err } - result := make([]LedgerKeyAndEntry, 0, len(rawResult)) - for encodedKey, key := range encodedKeyToKey { - encodedEntry, ok := rawResult[encodedKey] + result := make([]LedgerKeyAndEntry, 0, len(keys)) + for _, k2e := range keysToEncoded { + encodedEntry, ok := rawResult[k2e.encodedKey] if !ok { continue } @@ -300,22 +306,21 @@ 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") } - encodedExpKey, has := encodedKeyToEncodedTTLLedgerKey[encodedKey] - if !has { - result = append(result, LedgerKeyAndEntry{key, entry, nil}) + if k2e.encodedTTLKey == nil { + result = append(result, LedgerKeyAndEntry{k2e.key, entry, nil}) continue } - encodedExpEntry, ok := rawResult[encodedExpKey] + encodedTTLEntry, ok := rawResult[*k2e.encodedTTLKey] if !ok { // missing ttl key. This should not happen. return nil, errors.New("missing ttl key entry") } - var expEntry xdr.LedgerEntry - if err := xdr.SafeUnmarshal([]byte(encodedExpEntry), &expEntry); err != nil { + var ttlEntry xdr.LedgerEntry + if err := xdr.SafeUnmarshal([]byte(encodedTTLEntry), &ttlEntry); err != nil { return nil, errors.Wrap(err, "cannot decode TTL ledger entry from DB") } - liveUntilSeq := uint32(expEntry.Data.Ttl.LiveUntilLedgerSeq) - result = append(result, LedgerKeyAndEntry{key, entry, &liveUntilSeq}) + liveUntilSeq := uint32(ttlEntry.Data.Ttl.LiveUntilLedgerSeq) + result = append(result, LedgerKeyAndEntry{k2e.key, entry, &liveUntilSeq}) } return result, nil diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index b7c9c326b..0b444ce1c 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -108,8 +108,18 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn } } - for i, ledgerKeyAndEntry := range ledgerKeysAndEntries { - ledgerXDR, err := xdr.MarshalBase64(ledgerKeyAndEntry.Entry.Data) + for _, ledgerKeyAndEntry := range ledgerKeysAndEntries { + keyXDR, err := xdr.MarshalBase64(ledgerKeyAndEntry.Key) + if err != nil { + logger.WithError(err).WithField("request", request). + Infof("could not serialize ledger key %v", ledgerKeyAndEntry.Key) + return GetLedgerEntriesResponse{}, &jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Sprintf("could not serialize ledger key %v", ledgerKeyAndEntry.Key), + } + } + + entryXDR, err := xdr.MarshalBase64(ledgerKeyAndEntry.Entry.Data) if err != nil { logger.WithError(err).WithField("request", request). Infof("could not serialize ledger entry data for ledger entry %v", ledgerKeyAndEntry.Entry) @@ -120,8 +130,8 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn } ledgerEntryResults = append(ledgerEntryResults, LedgerEntryResult{ - Key: request.Keys[i], - XDR: ledgerXDR, + Key: keyXDR, + XDR: entryXDR, LastModifiedLedger: int64(ledgerKeyAndEntry.Entry.LastModifiedLedgerSeq), LiveUntilLedgerSeq: ledgerKeyAndEntry.LiveUntilLedgerSeq, }) 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 74e6dce30..728f4d46b 100644 --- a/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go +++ b/cmd/soroban-rpc/internal/test/get_ledger_entries_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" "github.com/stellar/go/keypair" - proto "github.com/stellar/go/protocols/stellarcore" "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" @@ -80,8 +79,9 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { ch := jhttp.NewChannel(test.sorobanRPCURL(), nil) client := jrpc2.NewClient(ch, nil) - kp := keypair.Root(StandaloneNetworkPassphrase) - account := txnbuild.NewSimpleAccount(kp.Address(), 0) + sourceAccount := keypair.Root(StandaloneNetworkPassphrase) + address := sourceAccount.Address() + account := txnbuild.NewSimpleAccount(address, 0) contractBinary := getHelloWorldContract(t) params := preflightTransactionParams(t, client, txnbuild.TransactionParams{ @@ -96,35 +96,40 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { }, }) tx, err := txnbuild.NewTransaction(params) - require.NoError(t, err) - tx, err = tx.Sign(StandaloneNetworkPassphrase, kp) - require.NoError(t, err) - b64, err := tx.Base64() - require.NoError(t, err) + assert.NoError(t, err) + sendSuccessfulTransaction(t, client, sourceAccount, tx) - sendTxRequest := methods.SendTransactionRequest{Transaction: b64} - var sendTxResponse methods.SendTransactionResponse - err = client.CallResult(context.Background(), "sendTransaction", sendTxRequest, &sendTxResponse) - require.NoError(t, err) - require.Equal(t, proto.TXStatusPending, sendTxResponse.Status) + params = preflightTransactionParams(t, client, txnbuild.TransactionParams{ + SourceAccount: &account, + IncrementSequenceNum: true, + Operations: []txnbuild.Operation{ + createCreateContractOperation(address, contractBinary), + }, + BaseFee: txnbuild.MinBaseFee, + Preconditions: txnbuild.Preconditions{ + TimeBounds: txnbuild.NewInfiniteTimeout(), + }, + }) + tx, err = txnbuild.NewTransaction(params) + assert.NoError(t, err) + sendSuccessfulTransaction(t, client, sourceAccount, tx) - txStatusResponse := getTransaction(t, client, sendTxResponse.Hash) - require.Equal(t, methods.TransactionStatusSuccess, txStatusResponse.Status) + contractID := getContractID(t, address, testSalt, StandaloneNetworkPassphrase) contractHash := sha256.Sum256(contractBinary) - contractKeyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ + contractCodeKeyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ Type: xdr.LedgerEntryTypeContractCode, ContractCode: &xdr.LedgerKeyContractCode{ Hash: contractHash, }, }) - require.NoError(t, err) // Doesn't exist. - sourceAccount := keypair.Root(StandaloneNetworkPassphrase).Address() - contractID := getContractID(t, sourceAccount, testSalt, StandaloneNetworkPassphrase) + notFoundKeyB64, err := xdr.MarshalBase64(getCounterLedgerKey(contractID)) + require.NoError(t, err) + contractIDHash := xdr.Hash(contractID) - notFoundKeyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ + contractInstanceKeyB64, err := xdr.MarshalBase64(xdr.LedgerKey{ Type: xdr.LedgerEntryTypeContractData, ContractData: &xdr.LedgerKeyContractData{ Contract: xdr.ScAddress{ @@ -139,9 +144,7 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { }) require.NoError(t, err) - var keys []string - keys = append(keys, contractKeyB64) - keys = append(keys, notFoundKeyB64) + keys := []string{contractCodeKeyB64, notFoundKeyB64, contractInstanceKeyB64} request := methods.GetLedgerEntriesRequest{ Keys: keys, } @@ -149,11 +152,28 @@ func TestGetLedgerEntriesSucceeds(t *testing.T) { var result methods.GetLedgerEntriesResponse err = client.CallResult(context.Background(), "getLedgerEntries", request, &result) require.NoError(t, err) - require.Equal(t, 1, len(result.Entries)) + require.Equal(t, 2, len(result.Entries)) require.Greater(t, result.LatestLedger, int64(0)) + require.Greater(t, result.Entries[0].LastModifiedLedger, int64(0)) + require.LessOrEqual(t, result.Entries[0].LastModifiedLedger, result.LatestLedger) + require.NotNil(t, result.Entries[0].LiveUntilLedgerSeq) + require.Greater(t, *result.Entries[0].LiveUntilLedgerSeq, uint32(result.LatestLedger)) + require.Equal(t, contractCodeKeyB64, result.Entries[0].Key) var firstEntry xdr.LedgerEntryData require.NoError(t, xdr.SafeUnmarshalBase64(result.Entries[0].XDR, &firstEntry)) + require.Equal(t, xdr.LedgerEntryTypeContractCode, firstEntry.Type) require.Equal(t, contractBinary, firstEntry.MustContractCode().Code) - require.Equal(t, contractKeyB64, result.Entries[0].Key) + + require.Greater(t, result.Entries[1].LastModifiedLedger, int64(0)) + require.LessOrEqual(t, result.Entries[1].LastModifiedLedger, result.LatestLedger) + require.NotNil(t, result.Entries[1].LiveUntilLedgerSeq) + require.Greater(t, *result.Entries[1].LiveUntilLedgerSeq, uint32(result.LatestLedger)) + require.Equal(t, contractInstanceKeyB64, result.Entries[1].Key) + var secondEntry xdr.LedgerEntryData + require.NoError(t, xdr.SafeUnmarshalBase64(result.Entries[1].XDR, &secondEntry)) + require.Equal(t, xdr.LedgerEntryTypeContractData, secondEntry.Type) + require.True(t, secondEntry.MustContractData().Key.Equals(xdr.ScVal{ + Type: xdr.ScValTypeScvLedgerKeyContractInstance, + })) }