Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

soroban-rpc: simulateTransaction: automatically detect ledger entries which require restoring #865

Merged
merged 5 commits into from
Aug 18, 2023

Conversation

2opremio
Copy link
Contributor

@2opremio 2opremio commented Aug 16, 2023

This change incorporates two new fields to simulateTransaction's Response:

PreRestoreTransactionData string                       `json:"preRestoreTransactionData,omitempty"` // SorobanTransactionData XDR in base64
PreRestoreMinResourceFee  int64                        `json:"preRestoreMinResourceFee,omitempty"`

When preflighting an InvokeHostFunction operation, soroban-rpc will now detect ledger entries which require restoring prior to the invocation for it to succeed.

If any such entries are detected, the simulateTransaction response will provide the Soroban Transaction Data and minimum resource fee needed to restore those entries.

The user is then now expected to:

  1. Submit a RestoreFootprint operation using the new fields above, if present.
  2. Submit the InvokeHostFunction operation normally (i.e. using the pre-existing simulateTransaction response fields)

Closes #803

… which require restoring

This change incorporates two new fields to the simulateTransactionResponse:

```
PreRestoreTransactionData string                       `json:"preRestoreTransactionData,omitempty"` // SorobanTransactionData XDR in base64
PreRestoreMinResourceFee  int64                        `json:"preRestoreMinResourceFee,omitempty"`
```

When preflighting an InvokeHostFunction operation, soroban-rpc will
now detect ledger entries which require restoring prior to the
invocation for it to succeed.

If any such entries are detected, the simulateTransaction response
will provide the Soroban Transaction Data and minimum resource fee
needed to restore those entries.

The user is then now expected to:
1. Submit a RestoreFootprint operation using the new fields above, if present.
2. Submit the InvokeHostFunction operation normally.
@2opremio
Copy link
Contributor Author

@dmkozh does something like state_expiration.rs already exist? I figure you need something like that upstream.

Otherwise, it may make sense to place it in the env library if you find it appropriate.

Comment on lines 787 to 820
// Wait for expiration again and check the pre-restore field when trying to exec the contract again
waitForExpiration()

simulationResult := simulateTransactionFromTxParams(t, client, invokeIncPresistentEntryParams)
assert.NotZero(t, simulationResult.PreRestoreTransactionData)
assert.NotZero(t, simulationResult.PreRestoreMinResourceFee)

params = preflightTransactionParamsLocally(t,
txnbuild.TransactionParams{
SourceAccount: &account,
IncrementSequenceNum: true,
Operations: []txnbuild.Operation{
&txnbuild.RestoreFootprint{},
},
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
},
methods.SimulateTransactionResponse{
TransactionData: simulationResult.PreRestoreTransactionData,
MinResourceFee: simulationResult.PreRestoreMinResourceFee,
},
)
tx, err = txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)

// Finally, we should be able to send the inc host function invocation now that we
// have pre-restored the entries
params = preflightTransactionParamsLocally(t, invokeIncPresistentEntryParams, simulationResult)
tx, err = txnbuild.NewTransaction(params)
assert.NoError(t, err)
sendSuccessfulTransaction(t, client, sourceAccount, tx)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Shaptic here's an example of how to use it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This means the flow is something like:

result = simulateTransaction(invoke)
if result.PreRestoreTransactionData != undefined: 
    submitTransaction(restore, transactionData=result.PreRestoreTransactionData)
    result = simulateTransaction(invoke)
submitTransaction(invoke, transactionData=result.transactionData)

Correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, assuming you somehow also use the minfee too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, you don't need the second simulation

result = simulateTransaction(invoke)
if result.PreRestoreTransactionData != undefined: 
    submitTransaction(restore, transactionData=result.PreRestoreTransactionData)
submitTransaction(invoke, transactionData=result.transactionData)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Shaptic did you see my udpate above? You don't need to re-run the transaction simulation

@tsachiherman
Copy link
Contributor

tsachiherman commented Aug 17, 2023

To me, PreRestore sounds misleading, since it's not something you would do before the restore operation. it's the information for the restore operation. I would use the preamble/prelude prefix for these two fields.
@Shaptic any thoughts about the naming and/or implementation ?

@2opremio
Copy link
Contributor Author

@willemneal we should probably use this in the cli once merged

@2opremio
Copy link
Contributor Author

@Shaptic you have an example of how to use this at

To me, PreRestore sounds misleading, since it's not something you would do before the restore operation. it's the information for the restore operation. I would use the preamble/prelude prefix for these two fields.

No strong opinions here.

@tsachiherman
Copy link
Contributor

thinking about that further, I think that what I'd like to see if I were in @Shaptic 's seat is the following:

RestorePreamble  restorePreamble   `json:"restorePeramble,omitempty"` // optional restore preamble

where the restorePreamble is defined as

type restorePreamble struct {
  TransactionData string           `json:"transactionData"` // non-optional since the struct itself is optional; base64 encoded.
  MinResourceFee  int64            `json:"minResourceFee"` // non-optional since the struct itself is optional.
}

@Shaptic
Copy link
Contributor

Shaptic commented Aug 17, 2023

@tsachiherman from a client perspective, having one omittable structure is better than two, so yes, I agree - I like encapsulating it into a structure like this. Of course we could have said "zero or both of these are present," but "zero or one" is much better!

Copy link
Contributor

@Shaptic Shaptic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you rock 🎸

this LGTM as-is but I’d prefer the schema Tsachi outlined

@2opremio
Copy link
Contributor Author

this LGTM as-is but I’d prefer the schema Tsachi outlined

sure

@2opremio 2opremio enabled auto-merge (squash) August 18, 2023 23:21
@2opremio 2opremio merged commit 08de7d5 into stellar:main Aug 18, 2023
19 of 20 checks passed
@2opremio 2opremio deleted the detect-required-restores branch August 18, 2023 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

soroban-rpc: Transaction simulation should assist in determining state expiration
3 participants