-
Notifications
You must be signed in to change notification settings - Fork 19
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
contract: implement sign fn with yield/resume #628
Conversation
eb8f529
to
31c3f76
Compare
31c3f76
to
fd69972
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.
Nice!
@@ -150,7 +184,7 @@ impl VersionedMpcContract { | |||
/// we ask for a small deposit for each signature request. | |||
/// The fee changes based on how busy the network is. | |||
#[payable] | |||
pub fn sign(&mut self, request: SignRequest) -> Promise { | |||
pub fn sign(&mut self, request: SignRequest) { |
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.
Is it better to still return Promise? Without -> Promise
, env::promise_return(yield_promise);
will return yield_promise
, but if yield_promise
executes to fail, the transaction (of calling sign
) is still success.
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.
yeah,I noticed during my load tests with this code that the sign() tx result will show success until signature request timed out
, which I think happens when it's past the number of blocks timeout yield/resume allows and still no result.
I tried load testing again, so the explorer will always show the status of tx as success, one example tx: one example tx: https://testnet.nearblocks.io/txns/BgkgGo3Xt4iWk9FdGeiJfSYtdQnVexzQS8DsnpaDUBqT?tab=execution.
But if we request status of transaction with this code
let outcome_view = ctx
.jsonrpc_client
.call(RpcTransactionStatusRequest {
transaction_info: TransactionInfo::TransactionId {
tx_hash,
sender_account_id: ctx.nodes.ctx().mpc_contract.id().clone(),
},
wait_until: near_primitives::views::TxExecutionStatus::Final,
})
.await?;
let Some(outcome) = outcome_view.final_execution_outcome else {
anyhow::bail!("final execution outcome not available");
};
let outcome = outcome.into_outcome();
let FinalExecutionStatus::SuccessValue(payload) = outcome.status else {
anyhow::bail!("tx finished unsuccessfully: {:?}", outcome.status);
};
as we did in integration tests, if the sign_on_finish
fn did panic, this will show outcome.status as Failure(ActionError(ActionError { index: Some(0), kind: FunctionCallError(ExecutionError("Smart contract panicked: signature request timed out")) }))
.
So I guess if yield_promise executes to fail, which will result in sign_on_finish
fn do env::panic_str(), then we'd be able to find out the transaction status is failure, although not through explorer.
Nice Implementation! I noticed in doc that:
Not sure if this need to be considered, if so combine the previous recursive call approach with yield can extend the limit to 400, 600, ... blocks with a bit more gas |
@ailisp that can be a good idea if we will create an infinite or long queue of signature requests. At the moment we are using different strategy where we allow max 8 simultaneous requests and return signature fast (<200blocks). We may change this strategy in the future. |
@ailisp What Serhii said about limiting request count at the same time is right. I've personally load tested the contract in this code without the |
eb666e1
to
5c9bbcb
Compare
5c9bbcb
to
d083e32
Compare
d083e32
to
f2613ff
Compare
tx finished unsuccessfully: Failure(ActionError(ActionError { index: Some(0), kind: FunctionCallError(ExecutionError("Smart contract panicked: Signature for this payload already requested")) })) |
…onded to return Error types instead of anyhow
Turns out it's because the Also refactored |
Initial testing of this, I found that if the test fails and the signature is not provided in time at all, we just get "Smart contract panicked: Signature for this payload already requested". Which does not seem correct for an error message if it the MPC network never responded. If you need the test code, I'll make a separate PR.
|
Seems like if we submit the same request for a signature after it has already completed the first, it will error out with |
Seems like also getting Take a look at all the testing I added in branch |
Hey @ChaoticTempest I've checked that code out and tried it. I've tried local unit tests myself too, but I did it by returning String as a signature. When I tested locally, I found out after many attempts that I needed to add in some wait time between sending async The I tried to reproduce the failed cases you mentioned:
|
Did not have a chance to locally test it, but the code looks good to me overall. Nice work! |
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.
It's weird that we require the sleep between the sign and respond, but we can resolve it later in a future PR
"clear_state_on_finish", | ||
&serde_json::to_vec(&(index,)).unwrap(), | ||
CLEAR_STATE_ON_FINISH_CALL_GAS, | ||
GasWeight(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.
Is gas weight supposed to be zero here. The docs mention the default is GasWeight(1)
. Tested and changed it to 1, but doesn't seem to effect much
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.
Confirming with Saketh
d79de76
to
9493b27
Compare
Terraform Feature Environment Destroy (dev-628)Terraform Initialization ⚙️
|
in this new design, we don't store the SignatureResponse in contract state. We still 1) won't allow repeated request if it was already requested and not completed; 2) only allow 8 requests concurrently
Note this new sign fn will return nothing, but you could still use it as if it returns Promise in any cross contract calls.