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

Potentially required contract updates due to increased deployment cost #71

Open
jakmeier opened this issue Mar 9, 2022 · 2 comments
Open

Comments

@jakmeier
Copy link

jakmeier commented Mar 9, 2022

Deployment costs per byte of code are going to be increased by a factor of ~5 with this change to NEAR protocol: near/nearcore#6397
I believe this does not break any of your code but I want to raise awareness so that you can double-check before this is deployed.

Probably this will be included with protocol version 53, which would be released on testnet by March 23 and on mainnet by April 20.

Why am I even contacting you? To find everyone affected by this change, I listed all the indirect deployments from the past that would fail with the new cost, if all gas attachments stay the same. One deployment from v2.ref-finance.near and one from v2.ref-farming.near showed up in this list. Meaning that the mechanism used to deploy them in the past might no longer work after the new release.

I am guessing ref-exchange is deployed on v2.ref-finance.near and ref-farming on v2.ref-farming.near, right? Both seem to follow a similar pattern for upgrades. Here is the code I found for ref-farming.

/// Gas for calling migration call.
pub const GAS_FOR_MIGRATE_CALL: Gas = 10_000_000_000_000;
/// Self upgrade and call migrate, optimizes gas by not loading into memory the code.
/// Takes as input non serialized set of bytes of the code.
#[no_mangle]
pub extern "C" fn upgrade() {
env::setup_panic_hook();
env::set_blockchain_interface(Box::new(near_blockchain::NearBlockchain {}));
let contract: Contract = env::state_read().expect("ERR_CONTRACT_IS_NOT_INITIALIZED");
contract.assert_owner();
let current_id = env::current_account_id().into_bytes();
let method_name = "migrate".as_bytes().to_vec();
unsafe {
BLOCKCHAIN_INTERFACE.with(|b| {
// Load input into register 0.
b.borrow()
.as_ref()
.expect(BLOCKCHAIN_INTERFACE_NOT_SET_ERR)
.input(0);
let promise_id = b
.borrow()
.as_ref()
.expect(BLOCKCHAIN_INTERFACE_NOT_SET_ERR)
.promise_batch_create(current_id.len() as _, current_id.as_ptr() as _);
b.borrow()
.as_ref()
.expect(BLOCKCHAIN_INTERFACE_NOT_SET_ERR)
.promise_batch_action_deploy_contract(promise_id, u64::MAX as _, 0);
let attached_gas = env::prepaid_gas() - env::used_gas() - GAS_FOR_MIGRATE_CALL;
b.borrow()
.as_ref()
.expect(BLOCKCHAIN_INTERFACE_NOT_SET_ERR)
.promise_batch_action_function_call(
promise_id,
method_name.len() as _,
method_name.as_ptr() as _,
0 as _,
0 as _,
0 as _,
attached_gas,
);
});
}
}
}

First of all, props to you for let attached_gas = env::prepaid_gas() - env::used_gas() - GAS_FOR_MIGRATE_CALL;. Using this dynamic gas value for the following function call means that you are probably fine with the upgrade even without any changes. (Other contracts use hard-coded values and they are breaking with the increased cost.)

What I want to tell you is that the cost to deploy ref_farming_release.wasm, or any contract of size 416KB, will go from ~6.04TGas to ~30.07Tgas. (That is pure deployment cost, without function call costs or storage costs.) After a quick scan over your code, I see nothing that would break because of this increase. But please review this for yourself.

@fercargo
Copy link

fercargo commented Apr 13, 2022

@jakmeier, question, at line 86, env::used_gas() already includes the gas required for deployment on line 82?
but line 82 seems to be a promise preparation, not the actual deployment

@jakmeier
Copy link
Author

Line 82 calls promise_batch_action_deploy_contract, which asks the runtime to create a promise for deployment. You are right, it is not executed, yet.
However, the protocol follows the idea that whenever possible, gas costs are charged upfront. So what happens is, calling promise_batch_action_deploy_contract burns the gas to create the promise and uses all gas necessary to execute the promise. (The difference between burning and using is only important if something fails and therefore the promise never executes. In that case, used but not burned gas is refunded.) Calls to env::used_gas() will give you the updated value, after deducting promise creation and execution fees.
This is possible since we know how large the contract is, we know exactly how much executing the deployment promise will cost. For function call promises, the exact cost to execute is not known before, thus we have to guess how much it will use and manually attach gas that will be used later. But even for function call promises, there is a fixed amount that is charged upfront.

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

No branches or pull requests

2 participants