Giter Club home page Giter Club logo

Comments (17)

nujabes403 avatar nujabes403 commented on August 31, 2024 2

@bowenwang1996
What if the shared contract deployer want to upgrade the existing contract?
Do they need to pay again?

(ex: 300kb -> 310kb)

scenario 1: Pay 30 NEAR + Pay 31 Near = Total 61 NEAR

scenario 2: Pay 30 NEAR + Pay 1 Near = Total 31 NEAR

from neps.

bowenwang1996 avatar bowenwang1996 commented on August 31, 2024 2

@ilblackdragon the main problem here with upgrades based on some sort of label other than contract hash is that it requires a nontrivial change to the state format and the runtime implementation. Basically, instead of storing contract hash in account state, we now need to store an account id and resolve it to a specific contract at runtime. This requires introducing another account version, which is doable but requires extra efforts. I think it is probably beneficial to implement the proposal without contract upgradability in the first version. If there is a strong demand from contract developers for the upgradability feature, it can be implemented on top of the first version.

from neps.

bowenwang1996 avatar bowenwang1996 commented on August 31, 2024 1

@nujabes403 this design does not allow one to upgrade permanently deployed contract, so you need to deploy a new contract. To implement upgradability is not trivial because of two reasons: 1) currently the contract is identified by its hash and that is how a regular account can deploy a permanent contract on their account. If there is a need for upgradability this needs to be changed. 2) There needs to be a permission management system that specifies who can upgrade the contract. Because the contract is not deployed on any specific account, it becomes very messy.

What do you think is a contract that needs to be deployed on many user accounts and also need to be upgraded frequently?

from neps.

nujabes403 avatar nujabes403 commented on August 31, 2024 1

DeployPermanentContractAction

@bowenwang1996

A commonly used method in EVM-based chains is:
The Proxy Contract calls the Logic Contract using delegateCall, where state storage is done through the Logic Contract in the Proxy Contract, and the Logic Contract can be replaced at any time.

This method is widely adopted by EVM-based developers. If NEAR also supports this, developers could develop more easily.

For example, if we declare a NEP 141 contract with custom logic as a Permanent Contract, and allow all deployed NEP 141 tokens to point to �the contract, when we want to change the Logic of all deployed contracts, we only need to modify the Logic Contract declared as a Permanent Contract. This would provide great convenience for developers.

In this regard, if upgradeability of permanent contracts is supported, it would be very powerful.

from neps.

ewiner avatar ewiner commented on August 31, 2024 1

I think we should make the global contract semantics as similar as possible to existing contracts. For instance, AFAIK this proposal would be the first place we have a concept of a separate 'deployer' account used to refer to a contract, but I don't think that's necessary.

So here's my mental model for how this feature could work:

  • Today, each account either has contract code or doesn't have contract code.
  • After this feature, there's four possibilities for each account, listed here along with the action to make each one happen:
  1. No contract code
  2. Local contract (DeployContractAction(code))
  3. Global contract (DeployPermanentContractAction(code))
  4. Global contract copy (DeployExistingContractAction(global_contract_account_id | code_hash))

To upgrade a global contract (which takes effect for all account-referenced copies ASAP), you can call DeployPermanentContractAction again using an FAK or from within the existing global contract code. If you're self-upgrading from the global contract code, that means any copies would come with a vestigial and possibly dangerous self-upgrade mechanism. So that upgrade function should be written so it only works on the main global contract.

Similarly, even if the global contract is meant to be just a template/factory that gets copied to other accounts, it might still have a working #[init] method, so that method might be written so it only works on copies.

Other transition rules:

  1. You can't convert from Global to Local, i.e. if an account has called DeployExistingContractAction, it cannot call DeployContractAction.
  2. Can you go from Global to Global Copy, or from Global Copy to Global or Local? Maybe. I can think of some use cases for those capabilities, but they're not vital so it wouldn't be worth complicating the implementation.

from neps.

ewiner avatar ewiner commented on August 31, 2024 1

I'm trying to understand the motivation. Is it this one:

Using global contracts deployed by third parties is unsafe because they can upgrade them at any time. Instead, you should first copy the contract and then reference the copy for your contracts. Is this correct?

Kind of. But yes, the issue here is who is allowed to upgrade the contract on your behalf. Consider a situation where multisigcreator.near writes and deploys a really nice multisig fund management global contract by calling DeployPermanentContractAction, and you (appdev.near) would like to set up a copy of that contract for each of your app's users. Your options are:

  1. Most restrictive/safest: DeployExistingContractAction(code_hash) (or in your proposal, DeployExistingContractAction(multisigcreator.near/1)) . Similar to normal contracts, only the account itself can replace its contract, by calling DeployExistingContractAction again or calling DeployContractAction. The downside of this approach is that if you need to fix a contract bug, that could be thousands and thousands of upgrades to do across each of your users' accounts.
  2. Least restrictive/easiest: Call DeployExistingContractAction(multisigcreator.near) (or multisigcreator.near/latest from your proposal). If multisigcreator.near upgrades the global contract, all your users will automatically receive the upgrade. But it also means that multisigcreator.near and not you (appdev.near) is in control of those upgrades.
  3. Better option: Have appdev.near call DeployPermanentContractAction(code), which will be cheap because it'll detect that the same code is already a deployed global contract from when multisigcreator.near deployed it. Then your accounts use DeployExistingContractAction(appdev.near). That way, you leverage the nice multisig global contract, but you are in control of the upgrades for your users and can do that upgrade across your userbase in just one transaction.

I think you should be able to move from any state to any other state. In case a critical issue is found on the global contract and you have FAK for your account, you should be able to redeploy a new contract code.

@mfornet You can always deploy a new global contract to upgrade your previous global contract. But if you try to convert from global to local, then any copies on different accounts that were pointing to the latest copy of your global contract would break.

from neps.

akhi3030 avatar akhi3030 commented on August 31, 2024

I like this mechanism. It is fairly straightforward and seems to be a good MVP.

Processing DeployPermanentContractAction seems to require computing a new permanent_contracts_root, and distributing the difference from the previous root to all the validators. Will our existing implementations on how distributing state witnesses will be able to handle these types of distributions as well?

A potential future direction to consider could be that we do not eagerly download all permanent contracts to all shards / validators. Instead, a validator only fetches the permanent contract when DeployExistingContractAction is called on an account for a contract that has not been deployed to any other account on that shard before. In order to do this, we would also potentially have to define some kind of "home" shard / validators that are guaranteed to contain the permanent contract.

from neps.

mfornet avatar mfornet commented on August 31, 2024

Related discussion about a more general approach to a global key-value storage: #93

from neps.

bowenwang1996 avatar bowenwang1996 commented on August 31, 2024

Will our existing implementations on how distributing state witnesses will be able to handle these types of distributions as well?

No it will not. The current mechanism does not distribute contract code to all validators, but only to validators assigned to that shard.

A potential future direction to consider could be that we do not eagerly download all permanent contracts to all shards / validators. Instead, a validator only fetches the permanent contract when DeployExistingContractAction is called on an account for a contract that has not been deployed to any other account on that shard before. In order to do this, we would also potentially have to define some kind of "home" shard / validators that are guaranteed to contain the permanent contract.

Complexities like "home shard" are what this design aim to simplify. If you want a smart contract to be domiciled on some shard, then you inevitably have to either complicate the state structure or deal with questions such as what if the last account with this contract deployed is deleted. The beauty of this design is that it does not make any change to the state representation and therefore avoids a number of complexities.

from neps.

tifrel avatar tifrel commented on August 31, 2024

This is an awesome addition! I would also want to know how upgrades are handled (and if they are possible at all under this mechanism), and if smart contracts stay upgradable, then how are storage migrations handled?

from neps.

ilblackdragon avatar ilblackdragon commented on August 31, 2024

The proposal above describes a global contract code that doesn't have any label which means there is no permissions and upgradability that have been enjoyed by NEAR smart contracts.

One option to extend proposal with:

  • DeployPermanentContractAction gets executed from specific account based on permissions of that account
  • This generates a message that is sent to all validators, that includes the pair (<account id>, <contract hash>)
  • Any validator when receiving such message, validate that it is received from the shard that has <account id> and updates the global smart contract table they maintain with this pair. If there was an existing item in the registry under <account id>, it gets replaced.
  • For shared smart contracts, we can have then two options: use <contract hash> or use <account id> via DeployExistingContractAction
  • For sharded smart contracts, one can refer to <account id> which resolves to the current <contract hash>

from neps.

ilblackdragon avatar ilblackdragon commented on August 31, 2024

Given this will be the same method that will power both shared and sharded smart contracts, I think making sure that you have the account_id management from the start will be important. For sharded smart contracts this will make a massive difference in DevX and adoption.

There is a pretty simple way to implement support this without any changes to state/contract runtime: the table with shared contracts is keyed by hash(account_id) and DeployExistingContractAction uses hash(account_id).

from neps.

bowenwang1996 avatar bowenwang1996 commented on August 31, 2024

There is a pretty simple way to implement support this without any changes to state/contract runtime: the table with shared contracts is keyed by hash(account_id) and DeployExistingContractAction uses hash(account_id).

I agree that it is simple. However, it does not offer the option to not have the contract be controlled by a remote account. In comparison, if you specify a contract hash, you know for sure that this will be the contract deployed on your account and only you have the permission to modify it.

from neps.

ilblackdragon avatar ilblackdragon commented on August 31, 2024

from neps.

gagdiez avatar gagdiez commented on August 31, 2024

I agree with @bowenwang1996 that simplicity is a benefit here. As a developer, I can check the code deployed, link it to my account, and know that it will never change (which removed any chance of new bugs being introduced in the future without my explicit consent)

If I need to update my contract in the future there are 3 options:

  1. My contract was not locked - I simply updated to the new address
  2. My contract was locked and I have an update function - I use it to update to the new address
  3. My contract was locked and with no update function - I didn't want my contract to be updatable on the first place

I would expect most contracts to not update frequently.

When I spoke with developers about this need, they were more worried about storage cost than upgradeability

With that being said, if this is a common pattern in Ethereum, we might add another layer of friction on their onboarding journey

from neps.

bowenwang1996 avatar bowenwang1996 commented on August 31, 2024

Adding notes from a discussion with @ewiner @alexauroradev @ilblackdragon:

  • "Permanent contracts" is not necessarily the best name. Global contracts are more intuitive
  • We want to have the ability of "copying" a globally deployed contract. The idea is that someone may want to "fork" an already deployed global contract and have the ability to upgrade it for those who deploy from the account that deploys the fork without affecting anyone who deploys from the original account. As an example, if "root.near" deploys a global multisig contract, "as.near" may want to fork the deployment and other people can deploy by referring to "as.near". "as.near" and "root.near" can independently update the aforementioned global contract. From a technical perspective, what this means is that a DeployPermanentContractAction, when processed, will burn tokens only if the contract to be deployed has not been deployed before. Otherwise it will only charge gas and there will be no storage cost associated.

from neps.

mfornet avatar mfornet commented on August 31, 2024

We want to have the ability of "copying" a globally deployed contract. The idea is that someone may want to "fork" an already deployed global contract and have the ability to upgrade it for those who deploy from the account that deploys the fork without affecting anyone who deploys from the original account.

I'm trying to understand the motivation. Is it this one:

Using global contracts deployed by third parties is unsafe because they can upgrade them at any time. Instead, you should first copy the contract and then reference the copy for your contracts. Is this correct?

An alternative to copy:
Every global contract deployed to an account is there forever indexed by a number. "Upgrading" the contract means pushing a new contract to the list (previous ones stay there).

Another contract can indicate that its code is either:

  • none
  • local (as.near has some local contract)
  • global-pinned: root.near/42 (updating root.near won't affect as.near)
  • global-with-upgrades: root.near/latest (as.near will use the latest deploy from root.near)

@ewiner

You can't convert from Global to Local, i.e. if an account has called DeployExistingContractAction, it cannot call DeployContractAction.

I think you should be able to move from any state to any other state. In case a critical issue is found on the global contract and you have FAK for your account, you should be able to redeploy a new contract code.

from neps.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.