-
Notifications
You must be signed in to change notification settings - Fork 332
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
feat: [EXC-1768] Add system API to get costs of management canister calls. #3584
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: Leo Eichhorn <[email protected]>
Co-authored-by: Leo Eichhorn <[email protected]>
let cost = self | ||
.sandbox_safe_system_state | ||
.cycles_account_manager | ||
.xnet_call_total_fee((method_name_size + payload_size).into(), execution_mode); |
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.
If the caller provides u64::MAX
for both arguments, this sum will overflow and the call will return a wrong cost. However, the method name of an actual call is limited to 10'000 bytes. So the input u64::MAX
for method_name_size
is "wrong", so I would accept this behaviour.
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.
But in this case, method_name_size = 9999
and payload_size = u64::MAX - 1
are valid inputs, and there will be an overflow. So I will suggest adding a check and explaining it in the doc comment of system API.
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 question: is ic0_cost_call
is used only to determine the cost of xnet
calls?
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.
Actually, payload_size = u64::MAX - 1
is not a valid input, they only go up to 2MB.
While the limits may change, the are very unlikely to ever multiply to even close to u64::MAX
.
I would argue that giving a bad result for bad inputs is inevitable, and it does not make it more correct if we e.g. saturate: The result would still be wrong.
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 xnet
prefix is used in the cycles account manager for all inter-canister calls.
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 believe that we should make consistent behavior of ic0_call
and ic0_cost_call
. Taking that into account in the solution you proposed, if the user calls ic0_cost_call
with payload_size 3MB, it will receive the response with some cost, if, after that, the user calls ic0_call
with a payload of 3MB call will fail because it is above the payload limit. What is your opinion on that scenario?
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 the cost endpoint should only care about the potential cost, not about other things like limits or whether e.g. the payload is malformed or anything like that. I mean, ic0.call_perform
can also fail because of many other reasons, we don't want to reflect them in the cost endpoint. Imagine e.g. an empty method name. That would give a legal cost, but is not a legal argument to call_perform
.
In other words, I propose:
- If
call_perform
would fail because we don't have enough cycles, thencost_call
should help detecting that - If
call_perform
would fail for any other reason (e.g. too large inputs), that has nothing to do with the cost, socost_call
will be agnostic about it.
If we want to return an error code for the cost_call
endpoint, we have to specify it (or extend the one from ic0.call_perform
), and I don't think it's worth specifying "your inputs to cost calculation were too large".
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.
If the payload is malformed that is not information that we have at the moment of calling ic0_cost_call
so we cannot know what the payload will be hence we should not do anything. But we know payload_size
in the moment of calling ic0_cost_call
so with that information if the payload_size
is greater than the limit will most certainly tell us that the call will not succeed, hence in my opinion that function that should predict the cost of such call(ic0_cost_call
) returns some cost when we know that such call will not succeed.
Regarding the empty method name, I also wanted to mention that the check for that should be added for the same reason as payload_size
, we know a call with such a method name will fail so it does not make sense that function that should predict the cost of such call(ic0_cost_call
) returns some cost when in the moment of calling it we know such call will not succeed.
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.
How about we meet on zoom to discuss this?
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.
IMO this would not separate concerns properly and leak error states/-handling of call_perform
into cost_call
.
Regarding the second paragraph: E.g. I think it's not wrong to say that e.g. cost_call(0, 0) = 0
, instead of cost_call(0, 0) = error
(or some base cost instead of 0, but that's not the point). It's just about the potential cost, nothing else should matter.
Ok, we can discuss it afk.
let cost = self | ||
.sandbox_safe_system_state | ||
.cycles_account_manager | ||
.xnet_call_total_fee((method_name_size + payload_size).into(), execution_mode); |
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.
But in this case, method_name_size = 9999
and payload_size = u64::MAX - 1
are valid inputs, and there will be an overflow. So I will suggest adding a check and explaining it in the doc comment of system API.
let cost = self | ||
.sandbox_safe_system_state | ||
.cycles_account_manager | ||
.xnet_call_total_fee((method_name_size + payload_size).into(), execution_mode); |
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 question: is ic0_cost_call
is used only to determine the cost of xnet
calls?
Co-authored-by: Dragoljub Djuric <[email protected]>
This PR introduces system API endpoints that allow the caller to predict the cost of certain calls:
Design Doc
Spec PR