-
Notifications
You must be signed in to change notification settings - Fork 470
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? |
@@ -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
?
@@ -3948,6 +3948,10 @@ | |||
"fix-signers": { | |||
"description": "If true, signers for transactions that are missing signatures will be fixed during evaluation.", | |||
"type": "boolean" | |||
}, | |||
"populate-resource-arrays": { |
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'd prefer we say "populate-resources" here. I don't think we need to mention the datatype in the name. (This will also lead to some smaller diffs below).
ExtraOpcodeBudget uint64 `codec:"extra-opcode-budget,omitempty"` | ||
ExecTraceConfig simulation.ExecTraceConfig `codec:"exec-trace-config,omitempty"` | ||
FixSigners bool `codec:"fix-signers,omitempty"` | ||
PopulateResourceArrays bool `codec:"populate-resource-arrays,omitempty"` |
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.
Here's an example of where "PopulateResources" will make for a smaller diff.
ExtraOpcodeBudget: request.ExtraOpcodeBudget, | ||
TraceConfig: request.ExecTraceConfig, | ||
FixSigners: request.FixSigners, | ||
PopulateResourceArrays: request.PopulateResourceArrays, |
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.
Another potentially smaller diff.
// The static fields are resource arrays that were given in the txn group and thus cannot be removed | ||
// The assumption is that these are prefilled because of one of the following reaons: | ||
// - This transaction has already been signed | ||
// - One of the foreign arrays is accessed on-chain |
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.
Reads funny. Do you mean this?
// - One of the foreign arrays is accessed on-chain | |
// - One of the foreign resources is accessed on-chain |
return nil | ||
} | ||
} | ||
return fmt.Errorf("no room for holding") |
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.
More info usually better:
return fmt.Errorf("no room for holding") | |
return fmt.Errorf("no room for holding %s : %d", addr, aid) |
Lots of other similar.
} | ||
|
||
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{}{} |
} | ||
|
||
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{}{} |
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".
for asset := range r.assets { | ||
assets = append(assets, asset) | ||
} | ||
for asset := range r.staticAssets { | ||
assets = append(assets, asset) | ||
} |
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.
These can use the ...
form of append
.
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
@@ -4265,6 +4269,10 @@ | |||
}, | |||
"unnamed-resources-accessed": { | |||
"$ref": "#/definitions/SimulateUnnamedResourcesAccessed" | |||
}, | |||
"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.", |
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.
"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.", | |
"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 more resource arrays given than transaction in the group, which means more app call transactions would be needed for extra resources.", |
}, | ||
"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.
@@ -605,3 +605,488 @@ func (p *resourcePolicy) AvailableBox(app basics.AppIndex, name string, operatio | |||
} | |||
return p.tracker.addBox(app, name, readSize, *p.initialBoxSurplusReadBudget, p.ep.Proto.BytesPerBoxReference) | |||
} | |||
|
|||
// txnResources tracks the resources being added to a tranasction during resource population |
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.
// txnResources tracks the resources being added to a tranasction during resource population | |
// txnResources tracks the resources being added to a transaction during resource population |
// txnResources tracks the resources being added to a tranasction during resource population | ||
type txnResources struct { | ||
// The static fields are resource arrays that were given in the txn group and thus cannot be removed | ||
// The assumption is that these are prefilled because of one of the following reaons: |
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.
// The assumption is that these are prefilled because of one of the following reaons: | |
// The assumption is that these are prefilled because of one of the following reasons: |
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.
FailureMessage *string `codec:"failure-message,omitempty"` | ||
UnnamedResourcesAccessed *model.SimulateUnnamedResourcesAccessed `codec:"unnamed-resources-accessed,omitempty"` | ||
Txns []PreEncodedSimulateTxnResult `codec:"txn-results"` | ||
PopulatedResourceArrays map[int]simulation.PopulatedResourceArrays `code:"populated-resource-arrays,omitempty"` |
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.
PopulatedResourceArrays map[int]simulation.PopulatedResourceArrays `code:"populated-resource-arrays,omitempty"` | |
PopulatedResourceArrays map[int]simulation.PopulatedResourceArrays `codec:"populated-resource-arrays,omitempty"` |
}, | ||
}, | ||
} | ||
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).
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