Skip to content

Commit

Permalink
soroban-rpc: Add restore footprint test
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed Jul 10, 2023
1 parent 83f824c commit 2987904
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 43 deletions.
21 changes: 11 additions & 10 deletions cmd/soroban-rpc/internal/db/ledgerentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type LedgerEntryReader interface {

type LedgerEntryReadTx interface {
GetLatestLedgerSequence() (uint32, error)
GetLedgerEntry(key xdr.LedgerKey) (bool, xdr.LedgerEntry, error)
GetLedgerEntry(key xdr.LedgerKey, includeExpired bool) (bool, xdr.LedgerEntry, error)
Done() error
}

Expand Down Expand Up @@ -181,7 +181,7 @@ func (l *ledgerEntryReadTx) GetLatestLedgerSequence() (uint32, error) {
return latestLedgerSeq, err
}

func (l *ledgerEntryReadTx) GetLedgerEntry(key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) {
func (l *ledgerEntryReadTx) GetLedgerEntry(key xdr.LedgerKey, includeExpired bool) (bool, xdr.LedgerEntry, error) {
encodedKey, err := encodeLedgerKey(l.buffer, key)
if err != nil {
return false, xdr.LedgerEntry{}, err
Expand All @@ -208,14 +208,15 @@ func (l *ledgerEntryReadTx) GetLedgerEntry(key xdr.LedgerKey) (bool, xdr.LedgerE

// Disallow access to entries that have expired. Expiration excludes the
// "current" ledger, which we are building.
// TODO: Support allowing access, but recording for simulateTransaction
if expirationLedgerSeq, ok := result.Data.ExpirationLedgerSeq(); ok {
latestClosedLedger, err := l.GetLatestLedgerSequence()
if err != nil {
return false, xdr.LedgerEntry{}, err
}
if expirationLedgerSeq <= xdr.Uint32(latestClosedLedger) {
return false, xdr.LedgerEntry{}, nil
if !includeExpired {
if expirationLedgerSeq, ok := result.Data.ExpirationLedgerSeq(); ok {
latestClosedLedger, err := l.GetLatestLedgerSequence()
if err != nil {
return false, xdr.LedgerEntry{}, err
}
if expirationLedgerSeq <= xdr.Uint32(latestClosedLedger) {
return false, xdr.LedgerEntry{}, nil
}
}
}

Expand Down
10 changes: 5 additions & 5 deletions cmd/soroban-rpc/internal/db/ledgerentry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func getLedgerEntryAndLatestLedgerSequenceWithErr(db db.SessionInterface, key xd
return false, xdr.LedgerEntry{}, 0, err
}

present, entry, err := tx.GetLedgerEntry(key)
present, entry, err := tx.GetLedgerEntry(key, false)
if err != nil {
return false, xdr.LedgerEntry{}, 0, err
}
Expand Down Expand Up @@ -497,14 +497,14 @@ func TestReadTxsDuringWriteTx(t *testing.T) {

_, err = readTx1.GetLatestLedgerSequence()
assert.Equal(t, ErrEmptyDB, err)
present, _, err := readTx1.GetLedgerEntry(key)
present, _, err := readTx1.GetLedgerEntry(key, false)
assert.NoError(t, err)
assert.False(t, present)
assert.NoError(t, readTx1.Done())

_, err = readTx2.GetLatestLedgerSequence()
assert.Equal(t, ErrEmptyDB, err)
present, _, err = readTx2.GetLedgerEntry(key)
present, _, err = readTx2.GetLedgerEntry(key, false)
assert.NoError(t, err)
assert.False(t, present)
assert.NoError(t, readTx2.Done())
Expand Down Expand Up @@ -581,7 +581,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) {
for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} {
_, err = readTx.GetLatestLedgerSequence()
assert.Equal(t, ErrEmptyDB, err)
present, _, err := readTx.GetLedgerEntry(key)
present, _, err := readTx.GetLedgerEntry(key, false)
assert.NoError(t, err)
assert.False(t, present)
}
Expand All @@ -593,7 +593,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) {
for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} {
_, err = readTx.GetLatestLedgerSequence()
assert.Equal(t, ErrEmptyDB, err)
present, _, err := readTx.GetLedgerEntry(key)
present, _, err := readTx.GetLedgerEntry(key, false)
assert.NoError(t, err)
assert.False(t, present)
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (

"github.com/creachadair/jrpc2"
"github.com/stellar/go/xdr"
"github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/db"
"github.com/stretchr/testify/assert"

"github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/db"
)

const (
Expand Down Expand Up @@ -37,7 +38,7 @@ func (entryReaderTx ConstantLedgerEntryReaderTx) GetLatestLedgerSequence() (uint
return expectedLatestLedgerSequence, nil
}

func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntry(key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) {
func (entryReaderTx ConstantLedgerEntryReaderTx) GetLedgerEntry(key xdr.LedgerKey, includeExpired bool) (bool, xdr.LedgerEntry, error) {
return false, xdr.LedgerEntry{}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/soroban-rpc/internal/methods/get_ledger_entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn

var ledgerEntryResults []LedgerEntryResult
for i, ledgerKey := range ledgerKeys {
present, ledgerEntry, err := tx.GetLedgerEntry(ledgerKey)
present, ledgerEntry, err := tx.GetLedgerEntry(ledgerKey, false)
if err != nil {
logger.WithError(err).WithField("request", request).
Infof("could not obtain ledger entry %v at index %d from storage", ledgerKey, i)
Expand Down
2 changes: 1 addition & 1 deletion cmd/soroban-rpc/internal/methods/get_ledger_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr
}
}

present, ledgerEntry, err := tx.GetLedgerEntry(key)
present, ledgerEntry, err := tx.GetLedgerEntry(key, false)
if err != nil {
logger.WithError(err).WithField("request", request).
Info("could not obtain ledger entry from storage")
Expand Down
4 changes: 2 additions & 2 deletions cmd/soroban-rpc/internal/preflight/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ type metricsLedgerEntryWrapper struct {
ledgerEntriesFetched uint32
}

func (m *metricsLedgerEntryWrapper) GetLedgerEntry(key xdr.LedgerKey) (bool, xdr.LedgerEntry, error) {
func (m *metricsLedgerEntryWrapper) GetLedgerEntry(key xdr.LedgerKey, includeExpired bool) (bool, xdr.LedgerEntry, error) {
startTime := time.Now()
ok, entry, err := m.LedgerEntryReadTx.GetLedgerEntry(key)
ok, entry, err := m.LedgerEntryReadTx.GetLedgerEntry(key, includeExpired)
atomic.AddUint64(&m.totalDurationMs, uint64(time.Since(startTime).Milliseconds()))
atomic.AddUint32(&m.ledgerEntriesFetched, 1)
return ok, entry, err
Expand Down
8 changes: 4 additions & 4 deletions cmd/soroban-rpc/internal/preflight/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.char) *C.char {
func SnapshotSourceGet(handle C.uintptr_t, cLedgerKey *C.char, includeExpired C.int) *C.char {
h := cgo.Handle(handle).Value().(snapshotSourceHandle)
ledgerKeyB64 := C.GoString(cLedgerKey)
var ledgerKey xdr.LedgerKey
if err := xdr.SafeUnmarshalBase64(ledgerKeyB64, &ledgerKey); err != nil {
panic(err)
}
present, entry, err := h.readTx.GetLedgerEntry(ledgerKey)
present, entry, err := h.readTx.GetLedgerEntry(ledgerKey, includeExpired != 0)
if err != nil {
h.logger.WithError(err).Error("SnapshotSourceGet(): GetLedgerEntry() failed")
return nil
Expand All @@ -72,7 +72,7 @@ func SnapshotSourceHas(handle C.uintptr_t, cLedgerKey *C.char) C.int {
if err := xdr.SafeUnmarshalBase64(ledgerKeyB64, &ledgerKey); err != nil {
panic(err)
}
present, _, err := h.readTx.GetLedgerEntry(ledgerKey)
present, _, err := h.readTx.GetLedgerEntry(ledgerKey, false)
if err != nil {
h.logger.WithError(err).Error("SnapshotSourceHas(): GetLedgerEntry() failed")
return 0
Expand Down Expand Up @@ -184,7 +184,7 @@ func getInvokeHostFunctionPreflight(params PreflightParameters) (Preflight, erro
ConfigSetting: &xdr.LedgerKeyConfigSetting{
ConfigSettingId: xdr.ConfigSettingIdConfigSettingStateExpiration,
},
})
}, false)
if err != nil {
return Preflight{}, err
}
Expand Down
40 changes: 38 additions & 2 deletions cmd/soroban-rpc/internal/test/simulate_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path"
"runtime"
"testing"
"time"

"github.com/creachadair/jrpc2"
"github.com/creachadair/jrpc2/jhttp"
Expand Down Expand Up @@ -638,7 +639,7 @@ func TestSimulateTransactionUnmarshalError(t *testing.T) {
)
}

func TestSimulateTransactionBumpFootprint(t *testing.T) {
func TestSimulateTransactionBumpAndRestoreFootprint(t *testing.T) {
test := NewTest(t)

ch := jhttp.NewChannel(test.sorobanRPCURL(), nil)
Expand Down Expand Up @@ -736,7 +737,7 @@ func TestSimulateTransactionBumpFootprint(t *testing.T) {
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
&txnbuild.BumpFootprintExpiration{
LedgersToExpire: 100,
LedgersToExpire: 17,
Ext: xdr.TransactionExt{
V: 1,
SorobanData: &xdr.SorobanTransactionData{
Expand Down Expand Up @@ -766,4 +767,39 @@ func TestSimulateTransactionBumpFootprint(t *testing.T) {

assert.Greater(t, newExpirationSeq, initialExpirationSeq)

// Wait until it expires
for i := 0; i < 30; i++ {
err = client.CallResult(context.Background(), "getLedgerEntry", getLedgerEntryrequest, &result)
if err != nil {
break
}
time.Sleep(time.Second)
}

// and restore it
params = preflightTransactionParams(t, client, txnbuild.TransactionParams{
SourceAccount: &account,
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
&txnbuild.RestoreFootprint{
Ext: xdr.TransactionExt{
V: 1,
SorobanData: &xdr.SorobanTransactionData{
Resources: xdr.SorobanResources{
Footprint: xdr.LedgerFootprint{
ReadWrite: []xdr.LedgerKey{key},
},
},
},
},
},
},
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
})
tx, err = txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)
}
2 changes: 1 addition & 1 deletion cmd/soroban-rpc/lib/preflight.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ CPreflightResult *preflight_footprint_expiration_op(uintptr_t handle, // Go Hand
const char *footprint); // LedgerFootprint XDR in base64

// LedgerKey XDR in base64 string to LedgerEntry XDR in base64 string
extern char *SnapshotSourceGet(uintptr_t handle, char *ledger_key);
extern char *SnapshotSourceGet(uintptr_t handle, char *ledger_key, int include_expired);

// LedgerKey XDR in base64 string to bool
extern int SnapshotSourceHas(uintptr_t handle, char *ledger_key);
Expand Down
23 changes: 15 additions & 8 deletions cmd/soroban-rpc/lib/preflight/src/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ fn calculate_host_function_soroban_resources(
metadataSize = readBytes(footprint.readWrite) + writeBytes + eventsSize
*/
let original_write_ledger_entry_bytes =
calculate_unmodified_ledger_entry_bytes(fp.read_write.as_vec(), snapshot_source)?;
calculate_unmodified_ledger_entry_bytes(fp.read_write.as_vec(), snapshot_source, false)?;
let read_bytes =
calculate_unmodified_ledger_entry_bytes(fp.read_only.as_vec(), snapshot_source)?
calculate_unmodified_ledger_entry_bytes(fp.read_only.as_vec(), snapshot_source, false)?
+ original_write_ledger_entry_bytes;
let write_bytes =
calculate_modified_read_write_ledger_entry_bytes(&storage.footprint, &storage.map, budget)?;
Expand Down Expand Up @@ -150,7 +150,7 @@ fn get_configuration_setting(
let key = LedgerKey::ConfigSetting(LedgerKeyConfigSetting {
config_setting_id: setting_id,
});
match ledger_storage.get(&key)? {
match ledger_storage.get(&key, false)? {
LedgerEntry {
data: LedgerEntryData::ConfigSetting(cs),
..
Expand Down Expand Up @@ -241,11 +241,12 @@ fn calculate_modified_read_write_ledger_entry_bytes(
fn calculate_unmodified_ledger_entry_bytes(
ledger_entries: &Vec<LedgerKey>,
snapshot_source: &ledger_storage::LedgerStorage,
include_expired: bool,
) -> Result<u32, Box<dyn error::Error>> {
let mut res: u32 = 0;
for lk in ledger_entries {
res += u32::try_from(lk.to_xdr()?.len())?;
match snapshot_source.get_xdr(lk) {
match snapshot_source.get_xdr(lk, include_expired) {
Ok(entry_bytes) => {
res += u32::try_from(entry_bytes.len())?;
}
Expand Down Expand Up @@ -295,8 +296,11 @@ pub(crate) fn compute_bump_footprint_exp_transaction_data_and_min_fee(
ledgers_to_expire: u32,
snapshot_source: &ledger_storage::LedgerStorage,
) -> Result<(SorobanTransactionData, i64), Box<dyn error::Error>> {
let read_bytes =
calculate_unmodified_ledger_entry_bytes(footprint.read_only.as_vec(), snapshot_source)?;
let read_bytes = calculate_unmodified_ledger_entry_bytes(
footprint.read_only.as_vec(),
snapshot_source,
false,
)?;
let soroban_resources = SorobanResources {
footprint,
instructions: 0,
Expand Down Expand Up @@ -334,8 +338,11 @@ pub(crate) fn compute_restore_footprint_transaction_data_and_min_fee(
footprint: LedgerFootprint,
snapshot_source: &ledger_storage::LedgerStorage,
) -> Result<(SorobanTransactionData, i64), Box<dyn error::Error>> {
let write_bytes =
calculate_unmodified_ledger_entry_bytes(footprint.read_write.as_vec(), snapshot_source)?;
let write_bytes = calculate_unmodified_ledger_entry_bytes(
footprint.read_write.as_vec(),
snapshot_source,
true,
)?;
let soroban_resources = SorobanResources {
footprint,
instructions: 0,
Expand Down
21 changes: 14 additions & 7 deletions cmd/soroban-rpc/lib/preflight/src/ledger_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern "C" {
fn SnapshotSourceGet(
handle: libc::uintptr_t,
ledger_key: *const libc::c_char,
include_expired: libc::c_int,
) -> *const libc::c_char;
// TODO: this function is unnecessary, we can just look for null in SnapshotSourceGet
// LedgerKey XDR in base64 string to bool
Expand Down Expand Up @@ -62,10 +63,16 @@ pub(crate) struct LedgerStorage {
}

impl LedgerStorage {
fn get_xdr_base64(&self, key: &LedgerKey) -> Result<String, Error> {
fn get_xdr_base64(&self, key: &LedgerKey, include_expired: bool) -> Result<String, Error> {
let key_xdr = key.to_xdr_base64()?;
let key_cstr = CString::new(key_xdr)?;
let res = unsafe { SnapshotSourceGet(self.golang_handle, key_cstr.as_ptr()) };
let res = unsafe {
SnapshotSourceGet(
self.golang_handle,
key_cstr.as_ptr(),
include_expired.into(),
)
};
if res.is_null() {
return Err(Error::NotFound);
}
Expand All @@ -79,21 +86,21 @@ impl LedgerStorage {
Ok(str)
}

pub fn get(&self, key: &LedgerKey) -> Result<LedgerEntry, Error> {
let base64_str = self.get_xdr_base64(key)?;
pub fn get(&self, key: &LedgerKey, include_expired: bool) -> Result<LedgerEntry, Error> {
let base64_str = self.get_xdr_base64(key, include_expired)?;
let entry = LedgerEntry::from_xdr_base64(base64_str)?;
Ok(entry)
}

pub fn get_xdr(&self, key: &LedgerKey) -> Result<Vec<u8>, Error> {
let base64_str = self.get_xdr_base64(key)?;
pub fn get_xdr(&self, key: &LedgerKey, include_expired: bool) -> Result<Vec<u8>, Error> {
let base64_str = self.get_xdr_base64(key, include_expired)?;
Ok(base64::decode(base64_str)?)
}
}

impl SnapshotSource for LedgerStorage {
fn get(&self, key: &Rc<LedgerKey>) -> Result<Rc<LedgerEntry>, HostError> {
let entry = self.get(key).map_err(|e| Error::to_host_error(&e))?;
let entry = self.get(key, false).map_err(|e| Error::to_host_error(&e))?;
Ok(entry.into())
}

Expand Down

0 comments on commit 2987904

Please sign in to comment.