-
Notifications
You must be signed in to change notification settings - Fork 473
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
simulate: resource population #6015
base: master
Are you sure you want to change the base?
Conversation
466fd50
to
5ba0a9a
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6015 +/- ##
==========================================
+ Coverage 55.88% 55.97% +0.09%
==========================================
Files 482 482
Lines 68571 68835 +264
==========================================
+ Hits 38320 38531 +211
- Misses 27646 27680 +34
- Partials 2605 2624 +19 ☔ View full report in Codecov by Sentry. |
In 99abd2f I have added resource population to the simulate API but for some reason this struct is not being properly encoded in the response type PopulatedResourceArrays struct {
Accounts []basics.Address `codec:"accounts"`
Assets []basics.AssetIndex `codec:"assets"`
Apps []basics.AppIndex `codec:"apps"`
Boxes []logic.BoxRef `codec:"boxes"`
}
@jasonpaulos Any idea where the data is getting lost? In general, what is the best way to debug these algod API tests? |
daemon/algod/api/server/v2/utils.go
Outdated
@@ -576,6 +576,7 @@ func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult) PreEncodedS | |||
AppBudgetAdded: omitEmpty(txnGroupResult.AppBudgetAdded), | |||
AppBudgetConsumed: omitEmpty(txnGroupResult.AppBudgetConsumed), | |||
UnnamedResourcesAccessed: convertUnnamedResourcesAccessed(txnGroupResult.UnnamedResourcesAccessed), | |||
PopulatedResourceArrays: txnGroupResult.PopulatedResourceArrays, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably needs omitEmpty
?
} | ||
|
||
func (r *txnResources) addApp(aid basics.AppIndex) { | ||
r.apps[aid] = struct{}{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to track that this adds availability of the app account too.
r.apps[aid] = struct{}{} | |
r.apps[aid] = struct{}{} | |
r.accounts[aid.Address()] = struct{}{} |
ledger/simulation/resources.go
Outdated
} | ||
|
||
for _, app := range txn.ForeignApps { | ||
p.txnResources[groupIndex].staticApps[app] = struct{}{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
App account becomes accessible too.
p.txnResources[groupIndex].staticApps[app] = struct{}{} | |
p.txnResources[groupIndex].staticApps[app] = struct{}{} | |
p.txnResources[groupIndex].staticAccounts[app.Address()] = struct{}{} |
ledger/simulation/resources.go
Outdated
return hasField || hasStatic || hasRef | ||
} | ||
|
||
func (r *txnResources) addAccount(addr basics.Address) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the style of needing to call hasRoom()
and then invoking add*
unconditionally. Could the add*
methods return error
instead?
This would require you have two forms of some add*
methods, or an extra argument that means "add this if you can do so by adding only one reference".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know what you think of 2ea34f2. Rather than having an additional argument the call sites short circuit if it returns nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally submitting my review that's been in progress for a while. Note there's some overlap with what @jannotti has already posted
daemon/algod/api/algod.oas2.json
Outdated
}, | ||
"populated-resource-arrays": { | ||
"description": "Present if populate-resource-arrays is true in the request. In that case, it will be a map of transaction index in the group to populated resource arrays. There may be moure resource arrays given than transaction in the group, which means more app call transactions would be needed for extra resources.", | ||
"type": "object" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless there is a very compelling reason, I would recommend against using a map with non-string keys in the response object. It makes JSON encoding much more difficult.
I'd instead suggest an array, or (my preference) adding this as a property to individual SimulateTransactionResult
objects, with another array here for any extra txns.
ledger/simulation/resources.go
Outdated
staticAssets map[basics.AssetIndex]struct{} | ||
staticApps map[basics.AppIndex]struct{} | ||
staticAccounts map[basics.Address]struct{} | ||
staticBoxes []logic.BoxRef |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI you can use logic.BoxRef
as a map key
accounts: make(map[basics.Address]struct{}), | ||
boxes: []logic.BoxRef{}, | ||
maxTotalRefs: consensusParams.MaxAppTotalTxnReferences, | ||
maxAccounts: consensusParams.MaxAppTxnAccounts, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you choosing to ignore the individual limits on assets, apps, and boxes because they happen to be the same as the total ref limit? I'd rather not have this code rely on that always being true
} | ||
|
||
// PopulatedResourceArrays is a struct that contains all the populated arrays for a txn | ||
type PopulatedResourceArrays struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with the other structs in this package, I would prefer this struct not have codec tags and be directly exposed through the REST API. Instead a new model should be added to the oas2 spec and then daemon/algod/api/server/v2/utils.go
can convert between this simulation.PopulatedResourceArrays
and the generated model struct.
}, | ||
}, | ||
} | ||
a.Equal(expectedResult, resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: the test failure that you're seeing. This is what the server sends back (decoded from the msgpack response):
"unnamed-resources-accessed": {
"app-locals": [
{
"account": "7WCFW7UO7NA3NTQWDPKMFT4NNTOU4A7M5IF5VGPB3OCH6TMLD4X6QX453M",
"app": 1005
}
],
"asset-holdings": [
{
"account": "7WCFW7UO7NA3NTQWDPKMFT4NNTOU4A7M5IF5VGPB3OCH6TMLD4X6QX453M",
"asset": 1002
}
],
"boxes": [
{
"app": 1006,
"name:b64": "QQ=="
}
],
"extra-box-refs": 1
}
I don't think there's anything going wrong with the encoding or e2e aspect, but rather a logic error inside the simulate package when multiple different resource types are unspecified. I noticed that the test you're comparing against, TestPopulateResources
, is much simpler and only tests accounts. I suggest replicating this in a unit test and debugging further there. Generally speaking the most complex test cases and corner cases should be unit tests in simulation_eval_test.go
, then all the e2e test needs to do is make sure all inputs get picked up properly and all outputs are present as expected in the response (to catch encoding errors or fields being dropped).
Co-authored-by: Jason Paulos <[email protected]>
Co-authored-by: Jason Paulos <[email protected]> Co-authored-by: John Jannotti <[email protected]>
In 292a9b9 I updated the API model to avoid using If I print the raw response I see this:
So for some reason // PreEncodedSimulateTxnResult mirrors model.SimulateTransactionResult
type PreEncodedSimulateTxnResult struct {
Txn PreEncodedTxInfo `codec:"txn-result"`
AppBudgetConsumed *uint64 `codec:"app-budget-consumed,omitempty"`
LogicSigBudgetConsumed *uint64 `codec:"logic-sig-budget-consumed,omitempty"`
TransactionTrace *model.SimulationTransactionExecTrace `codec:"exec-trace,omitempty"`
UnnamedResourcesAccessed *model.SimulateUnnamedResourcesAccessed `codec:"unnamed-resources-accessed,omitempty"`
FixedSigner *string `codec:"fixed-signer,omitempty"`
PopulatedResourceArrays *model.ResourceArrays `codec:"populated-resource-arrays,omitempty"`
} Any ideas on what might be happening here? |
It almost seems like the |
At a glance it seems to me like you might have a bug somewhere where you're assigning Where are you printing the raw response? |
Summary
When a user calls simulate with
UnnamedResources
enabled, simulate should suggest to the user how they can populate the resource arrays in their transactions to properly send the transaction group to the network.Test Plan
/simulate
endpoint with ResourcePopulator functionalityledger/simulation/resources.go
coverage