Add externally managed contract executable type #1969
Replies: 4 comments 12 replies
|
Thanks for posting this. I think another alternative that might be suggested is a direct ability for a contract to 'inherit' the executable of another contract directly. However, I think the proposed approach has several benefits compared to the direct inheritance approach:
Some small design notes:
|
|
I liked the approach, and I think it addresses a fair problem. The only potential problem I could see is, there is no guarantee whether the updated code will be compatible with the proxy contract's storage. If the upgrades were to be done manually, we could enforce the migration path easier. However, in this approach, I think the updates can happen, only if they do not require migration? Because, I couldn't see a way to migrate all the contracts, without calling them one by one (which is the exact thing this proposal is trying to avoid). Although I'm pointing out a problem, I unfortunately don't have a good solution to it. This problem was the first thing came to my mind when I read the proposal. I'm hoping that raising this concern can ultimately be helpful for this proposal, and may spark further discussions on how to find a good solution to it (if it is possible). Having said that, I don't think the problem I've described should be a blocker for this proposal. The value this proposal will bring, will be bigger than this problem, so I think overall, it is still good to go! |
|
On the decision to have
I'd like to push back on this, because the ref provides information that's valuable independently of whether the caller can resolve it. When a large set of instances share a single owner-managed executable, the owner contract and/or the tag can be a signal for "membership", info that can be very useful to route or group calls. I see the point about the backward compatibility issue if adding a new variant to the |
|
Something observing during the SDK implementation in stellar/rs-soroban-sdk#2020, is that the significant behaviour difference introduced into To improve the UX of the APIs around executable, maybe we should consider expanding stellar-protocol/core/cap-0085.md Line 288 in 8091ab6 I don't have a specific use case for this, other than improving the UX. So I don't expect this to move forward, but noting here that we have the assymetry. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Problem
The factory pattern is commonly used in smart contract protocols to deploy many contract instances that share the same WASM (a "fleet" of contracts). When that shared WASM needs to be upgraded, the admin must submit a separate upgrade transaction for every instance. This is fine for small fleets, but for large ones it is expensive and creates a window during the rollout where some instances run the old WASM and some run the new.
Because some upgrades carry security fixes, or are not backwards-compatible with the previous version, there is incentive for the upgrade to apply atomically across the entire fleet.
Ecosystems that manage reusable code via proxies, such as Ethereum, achieve this with the beacon proxy pattern.
Stellar has no equivalent today. Each contract instance defines its own WASM hash, and we intentionally do not expose the proxy primitives (
fallback()ordelegatecall) that beacon implementations rely on. This discussion proposes fixing this by extending the contract executable definition to support references to an updatable WASM hash.Proposal
We would like to extend the contract instance executable definition to include an external reference variant:
A contract can manage WASM entries via a new ScVal ledger key type
ScVal::LedgerKeyWasmRef(Symbol)and a new host function that validateswasm_hashexists as a code entry:A contract can switch its executable to an external reference or update it's external reference via a new host function:
A factory contract can deploy contracts with an externally referenced WASM with the new host functions:
Diagrams
Legend:
Resolving an ExternalRef Contract during a contract invocation
sequenceDiagram actor U as Caller participant H as Host participant L as Ledger U->>H: invoke Follower.transfer(...) H->>L: read Follower instance L-->>H: executable = ExternalRef{ Manager, "token" } H->>L: read reserved entry (Manager, WasmRef("token")) L-->>H: wasm_hash H->>L: read ContractCode(wasm_hash) L-->>H: wasm code Note over H: instantiate VM, run transfer(...) H-->>U: result of transfer(...)Manager upgrading a WASM it controls
sequenceDiagram actor Op as Admin participant M as Manager participant H as Host participant L as Ledger Op->>M: upgrade("token", new_hash) M->>M: governance checks (require_auth(admin), timelock, ...) M->>H: set_contract_wasm_ref("token", new_hash) H->>H: assert ContractCode(new_hash) exists H->>L: write (Manager, WasmRef("token")) = new_hash H->>H: emit wasm_ref_updated(Manager, "token", old_hash, new_hash) H-->>M: ok Note over L: every Follower with ExternalRef{ Manager, "token" }<br/>resolves to new_hash on its next callFactory deploying a new follower contract
sequenceDiagram actor U as Deployer participant Fa as Factory participant H as Host participant L as Ledger U->>Fa: deploy(salt) Fa->>H: create_contract_with_external_ref(deployer, Manager, "token", salt) H->>H: derive Follower address from (deployer, salt) H->>L: read (Manager, WasmRef("token")) L-->>H: wasm_hash H->>H: assert ContractCode(wasm_hash) exists H->>L: write Follower instance, executable = ExternalRef{ Manager, "token" } H-->>Fa: Follower address Fa-->>U: Follower address Note over H,L: the _and_constructor variant also loads the code<br/>and runs the Follower's constructor against itAlternatives
ExternalRef { contract: Address, key: Val }. This is the simplest protocol change, but potentially the easiest to go wrong as no additional host functions are added that can validate things like "this entry holds a deployed WASM hash".Questions
extend_current_contract_instance_and_code_ttl, what gets extended?All reactions