-
Notifications
You must be signed in to change notification settings - Fork 679
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
Implement #1641 rpc call public functions as read-only - take 2 #2527
Implement #1641 rpc call public functions as read-only - take 2 #2527
Conversation
Thanks for sending this @psq! We'll try to get this included in 2.0.12. |
}, | ||
), | ||
Ok(Some(Err(e))) => match e { | ||
Unchecked(CheckErrors::CostBalanceExceeded(actual_cost, _)) |
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 just want to clarify the "path" you're expecting execute_contract
to follow to get to this branch of the match statement (where we get the CostBalanceExceeded
error).
To me, it seems you want execute_contract
to fail at its call to runtime_cost
, which will check that the execution costs do not exceed those set by the limits the environment has set. However, the limit is equal to options.read_only_call_limit
(as set in line 1250 of this file), and the value for this struct (type ExecutionCost
) ultimately comes from ConnectionOptions
, which is specified in the Config
passed to initialize a new node. So, it seems like there is not a guarantee that write_count
or write_length
would be 0, meaning the call to runtime_cost
in execute_contract
may be okay even if there is a write.
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 also had a question about this. I could actually see this going either way:
- This API endpoint explicitly forbids any writes, and causes a
CostBalanceExceeded
error the moment the code attempts to wrote. - This API endpoint explicitly permits writes, but never materializes them. This could be used to allow clients to simulate what would happen if they called a function that performed a write, even though the write never commits.
I can see value in both approaches.
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.
as described in #1641
Make it so that the RPC endpoint used for calling read-only functions can be used to call any functions. Make it so that any mutations are discarded
this makes it possible to call any function, and get a result if no write occurred. Furthermore, a read-only function that makes a contrac-call?
is flagged as non read only, even if it is calling other read-only functions and the call-read
endpoint will fail. So this only allow calling functions that are so basic it is not so useful in practice, and it prevents composing contracts. You have to use define-public
instead, but this is not callable via the call-read
endpoint.
So this extends the call-read
endpoint to try the function, and return a result if no writes occurred, or a failure if any writes did occur.
So, @jcnelson, this is definitely the former.
@pavitthrap I'm not sure I understand how the node configuration would come into the picture. Either write_count
is 0 after the execution and that's a read-only call, or it will fail because it was not read-only.
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 @pavitthrap's concern (correct me if I'm wrong) is that options.read_only_call_limit
may have a non-zero values, since this ExecutionCost
instance is instantiated from the node's config file, and you're allowed to specify non-zero write sizes. I think the intention for even permitting this at all is that one day, there would be an interface that lets you call read/write contract-calls such that the writes never materialize (or even hit disk).
I think the solution here would be to constract an ExecutionCost
from options.read_only_call_limit
and explicitly set the write_length
and write_count
fields to 0.
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.
oh, @pavitthrap comment makes sense now. I wasn't expecting these values to be other that 0, but to be safe, I'll set them to 0.
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.
addressed in 57b29c8
@@ -1456,7 +1456,7 @@ impl PeerNetwork { | |||
}; | |||
|
|||
debug!( | |||
"{:?}: Send GetPoxInv to {:?} for {} rewad cycles starting at {} ({})", | |||
"{:?}: Send GetPoxInv to {:?} for {} reward cycles starting at {} ({})", |
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.
Lol wee kant spel
(ok (var-set x val))) | ||
"; | ||
|
||
const MAIN_CONTRACT: &'static str = " |
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.
Could you change the variables here to be more descriptive of what we are trying to test? (ex: READ_ONLY_MAIN_CONTRACT
)
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.
will do
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.
addressed in 1e2ae7a
}) | ||
.map_err(|_| { | ||
ClarityRuntimeError::from(InterpreterError::CostContractLoadFailure) | ||
})?; | ||
|
||
clarity_tx.with_readonly_clarity_env(mainnet, sender.clone(), cost_track, |env| { | ||
env.execute_contract(&contract_identifier, function.as_str(), &args, true) | ||
env.execute_contract(&contract_identifier, function.as_str(), &args, false) |
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.
Could you add a comment here explaining why we are setting this to false here (something about how we are using the limits imposed by the cost tracker, which is owned by the environment, to detect any writes).
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.
sure, will do.
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.
addressed in 1e2ae7a
57b29c8
to
1e2ae7a
Compare
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.
LGTM! Thanks @psq!
Just a reminder: please don't merge until 2.0.11 is released. |
thank you both for the review and the back and forth, which made this PR better than what it started as. |
thanks for the PR @psq would be great if this can land in 2.0.12 as this blocks arkadiko from dynamically versioning contracts 👀 |
swapr also relies heavily on this, but would run my own node with this PR deployed if necessary to extract data from contract, but let Connect send transactions through the regular API node (but would rather not have to do this, of course). Not available yet on mainnet, just on my own mocknet. Happy to share if 2.0.12 gets delayed for any reason. |
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.
@psq -- these changes all look good to me. Can you resolve the conflicts in integrations
and make sure that the test passes?
@kantai sure, will rebase |
17a889b
to
74d3595
Compare
@kantai rebased and all green |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
second attempt in fixing #1641 to allow calling any function as read-only. the call will fail if any write happened during the call.
For example, this allows calling public functions that call use
contract-call?
, which can not be used in a read-only function, even if the called contract function itself is read-only.