Skip to content
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

chore: additional changes to propose_update #729

Merged
merged 13 commits into from
Jul 25, 2024

Conversation

ChaoticTempest
Copy link
Member

@ChaoticTempest ChaoticTempest commented Jul 24, 2024

Follow up to #714

  • An attached deposit is now required on propose_update where the uploaded config or contract bytes is used to determine how much. This amount currently cannot be withdrawn, so it'll be trapped there until we make more changes to the contract. For us, this just means that there's a cost of about 8N per contract update to mainnet and not more than 0.1N for config updates
  • Configs are now dynamic so we can make any arbitrary config update without changing the contract code at all
  • ProposedUpdates now just uses one hashmap to save us the hassle of fetching both the votes and updates at the same time
  • migrate function will be invoked after a deploy contract
  • test case where deployed contract has a paniced/failed migrate function which effectively rolls back to the previous state. In this case the update fails and is evicted from being voted upon again

@ChaoticTempest ChaoticTempest force-pushed the phuong/chore/additional-changes-to-update branch from 82fd209 to 2bd66dd Compare July 24, 2024 22:06
Copy link
Collaborator

@volovyks volovyks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, we will need to better test it. @ppca @ailisp I'm merging this to unblock other PRs, but please, take a look and comment.

@@ -0,0 +1,18 @@
use near_sdk::{env, near, PanicOnDefault};

// use crate::FailureContractExt;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead code

let execution = contract
.call("propose_update")
.args_json(serde_json::json!({
"contract": vec![1, 2, 3],
"code": vec![1, 2, 3],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is called test_propose_update_config but we are proposing a new code.


dbg!(&execution);
let state: mpc_contract::ProtocolContractState = execution.json().unwrap();
dbg!(state);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check the execution status here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, json().unwrap() does it

// Try calling into state and see if it works after the contract updates with an invalid
// contract. It will fail in `migrate` so a state rollback on the contract code should have
// happened.
let execution = accounts[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the end goal in tests for update logic should be:

  • To make sure that the user-facing API was not changed, make a sign, pk, and other calls. It should prevent us from accidentally breaking the API
  • To make sure that the upgrade logic is still functioning after the update. It will allow us to fix it with new updates no matter what. The flow can be: deploy the contract, upgrade it, and upgrade it again to see if the upgrade logic is functioning.

// contract. It will fail in `migrate` so a state rollback on the contract code should have
// happened.
let execution = accounts[0]
.call(contract.id(), "state")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not exist on that contract. Is this intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the state rollback, the FailureContract does not get deployed, so we end up back at our original contract which has the state function

else {
return Err(MpcContractError::VoteError(VoteError::UpdateNotFound));
let Some(_promise) = self.proposed_updates().do_update(&id, UPDATE_CONFIG_GAS) else {
return Err(MpcContractError::from(VoteError::UpdateNotFound));
};

Ok(true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

theoretically do_update can fail, but maybe convenient bool result is more important

#[private]
#[init(ignore_state)]
#[handle_result]
pub fn migrate() -> Result<Self, MpcContractError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must keep the last deployed version somewhere to ensure migrations are successful.

Copy link
Member Author

@ChaoticTempest ChaoticTempest Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think we should have a list of structs when we're making these breaking changes. We're supposed to be using the versioned contracts for this, but it hasn't been necessary since we haven't released yet

@@ -772,12 +794,27 @@ impl VersionedMpcContract {
}
}

fn threshold(&self) -> Result<usize, VoteError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VoteError looks strange here :/ We may need to reorg the error structure

votes: HashMap<UpdateId, HashSet<AccountId>>,
next_id: u64,
entries: HashMap<UpdateId, UpdateEntry>,
id: UpdateId,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't next_id a better name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just an id generator. Calling it next_id meant that it could be sequential when it just generates you an arbitrary id

Update::Contract(code) => promise = promise.deploy_contract(code),
Update::Contract(code) => {
// deploy contract then do a `migrate` call to migrate state.
promise = promise.deploy_contract(code).function_call(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work as a batched transaction? Will deployment fail if the function call fails?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, this ends up being a batch call where calling into migrate will roll back the whole entire promise with the deployed contract

@volovyks volovyks merged commit fa6fa9b into develop Jul 25, 2024
4 checks passed
@volovyks volovyks deleted the phuong/chore/additional-changes-to-update branch July 25, 2024 11:40
Copy link

Terraform Feature Environment Destroy (dev-729)

Terraform Initialization ⚙️success

Terraform Destroy success

Show Destroy Plan


No changes. No objects need to be destroyed.

Either you have not created any objects yet or the existing objects were
already deleted outside of Terraform.

Destroy complete! Resources: 0 destroyed.

Pusher: @volovyks, Action: pull_request, Working Directory: ``, Workflow: Terraform Feature Env (Destroy)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants