-
Notifications
You must be signed in to change notification settings - Fork 304
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
Refresh lifecycle CAP #1373
Refresh lifecycle CAP #1373
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,16 +28,13 @@ Users need a way to manage smart contracts on the network. This CAP allows users | |
to deploy the smart contracts to the network and specifies the supported | ||
contract code kinds. | ||
|
||
This CAP also lets the validators turn off all smart contract functionality if | ||
some unexpected behavior is found in the protocol. | ||
|
||
## Specification | ||
|
||
### XDR | ||
|
||
See the XDR diffs in the Soroban overview CAP, specifically those referring to | ||
`HOST_FUNCTION_TYPE_CREATE_CONTRACT` and | ||
`HOST_FUNCTION_TYPE_INSTALL_CONTRACT_CODE`. | ||
`HOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM`. | ||
|
||
## Semantics | ||
|
||
|
@@ -60,61 +57,60 @@ This CAP defines two possible kinds of contract sources: | |
- Built-in contract: this is a 'source' compiled into host directly that has a | ||
protocol-defined interface and behavior. | ||
|
||
#### Contract executable | ||
|
||
The Contract executable contains a pointer to the WASM source or a tag of a built-in | ||
contract. | ||
|
||
#### Contract instance | ||
|
||
Contract instance can be thought of as an instance of the contract 'class'. | ||
Contract instance consists of: | ||
|
||
- Identifier: SHA-256 hash of a pre-image payload | ||
- Source reference: a pointer to the WASM source or a tag of a built-in | ||
contract | ||
- A `ContractExecutable`. | ||
- An optional `SCMap*` for users to store data | ||
that should be tied to the lifetime of the instance (TODO: link to state | ||
expiration CAP). | ||
|
||
A contract instance may own an arbitrary amount of ledger entries attributed to | ||
its identifier. Contracts that share the same source in no way may influence | ||
each other; from the perspective of a contract invoker there is no difference | ||
between calling the contracts with the same or different source references | ||
(besides the possible contract-defined behavior differences). | ||
|
||
### Contract identifier preimage types | ||
|
||
This CAP defines the following supported contract identifier preimage kinds. The | ||
use cases for every identifier type are described in the following sections. | ||
Every preimage is a part of the `HashIDPreimage` union and has a unique tag | ||
associated with it in order to ensure that there are no collisions with other | ||
hashes in the protocol. | ||
|
||
- `ENVELOPE_TYPE_CONTRACT_ID_FROM_ED25519`: built from an `ed25519` public key | ||
and the user-specified `uint256` salt. | ||
- `ENVELOPE_TYPE_CONTRACT_ID_FROM_SOURCE_ACCOUNT`: built from a Stellar account | ||
identifier and the user-specified `uint256` salt. | ||
- `ENVELOPE_TYPE_CONTRACT_ID_FROM_ASSET`: built from a Stellar `Asset` | ||
### Contract identifier preimage type | ||
|
||
`ENVELOPE_TYPE_CONTRACT_ID` is the `HashIDPreimage` type used for contract | ||
identifiers. This unique tag is what ensures that there are no collisions with | ||
other hashes in the protocol. It contains the `Hash` of the `networkID` to | ||
ensure that every network has unique set of contract identifiers, along with a | ||
`ContractIDPreimage`, which is a union that supports both | ||
`CONTRACT_ID_PREIMAGE_FROM_ADDRESS` and `CONTRACT_ID_PREIMAGE_FROM_ASSET`. | ||
- `CONTRACT_ID_PREIMAGE_FROM_ADDRESS`: built from an an `SCAddress` and the | ||
user-specified `uint256` salt. | ||
- `CONTRACT_ID_PREIMAGE_FROM_ASSET`: built from a Stellar `Asset` | ||
structure. | ||
- `ENVELOPE_TYPE_CONTRACT_ID_FROM_CONTRACT`: built from another contract | ||
identifier and contract-specified `uint256` salt. | ||
|
||
Every preimage must also include a `networkID` (a hash of the network | ||
passphrase) which ensures that every network has unique set of contract | ||
identifiers. | ||
|
||
### Installing WASM sources using `InvokeHostFunctionOp` | ||
|
||
WASM contract sources can be installed to the network without instantiating a | ||
WASM contract sources can be uploaded to the network without instantiating a | ||
contract via `InvokeHostFunctionOp`(defined in [CAP-0046-04](./cap-0046-04.md)) | ||
with `HOST_FUNCTION_TYPE_INSTALL_CONTRACT_CODE` host function type. | ||
with `HOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM` host function type in | ||
`hostFunction`. | ||
|
||
This function accepts `InstallContractCodeArgs` struct that contains the WASM | ||
This function accepts `opaque wasm<>` that contains the WASM | ||
contract code. | ||
|
||
Installed contracts are stored in `ContractCodeEntry` ledger entries. These | ||
entries are keyed by the hash of `InstallContractCodeArgs` used to install | ||
Uploaded contracts are stored in `ContractCodeEntry` ledger entries. These | ||
entries are keyed by the hash of the WASM used to upload | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Could you please replace 'WASM' with 'Wasm' everywhere in the doc? WASM is not an official spelling, see https://webassembly.org/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
them. | ||
|
||
Contract installation host function will compute the hash of | ||
`InstallContractCodeArgs` and check if such a contract code already exists. If | ||
The contract upload host function will compute the hash of | ||
the WASM and check if such a contract code already exists. If | ||
the entry exists, the operation will immediately succeed. If it doesn't, the | ||
new `ContractCodeEntry` will be created. | ||
|
||
Host does not perform any validation on the installed contract code, besides | ||
Core does not perform any validation on the installed contract code, besides | ||
checking its size. | ||
|
||
#### Max contract size setting | ||
|
@@ -124,72 +120,34 @@ The maximum WASM contract size will be introduced as a `ConfigSettingEntry`(see | |
|
||
It is set during the protocol version upgrade using a new `ConfigSettingEntry`, | ||
with `configSettingID` == `CONFIG_SETTING_CONTRACT_MAX_SIZE_BYTES`, and | ||
`contractMaxSizeBytes` == `16384`. The valid values for | ||
`contractMaxSizeBytes` are[0, 256000] (inclusive). | ||
|
||
#### No contract-driven WASM installation | ||
|
||
This CAP does not specify a way to install WASM sources from within a | ||
contract. | ||
|
||
This is done to encourage efficient code reuse and deduplication: if the | ||
contract was allowed to install the WASM code, then we'd need to store it | ||
twice (in the installer contract and in the source entry). | ||
`contractMaxSizeBytes` == `65536`. The valid values for | ||
dmkozh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`contractMaxSizeBytes` are [5000, UINT32_MAX] (inclusive). | ||
|
||
### Instantiating contracts using `InvokeHostFunctionOp` | ||
|
||
Contracts can be instantiated via `InvokeHostFunctionOp` with | ||
`HOST_FUNCTION_TYPE_CREATE_CONTRACT` host function type. | ||
|
||
The function accepts `CreateContractArgs` struct that defines the input for | ||
building the contract identifier preimage (`contractID` field) and the contract | ||
source reference (`source` field). | ||
|
||
All the preimage types besides `ENVELOPE_TYPE_CONTRACT_ID_FROM_CONTRACT` can be | ||
built from the `contractID` field. | ||
building the contract identifier preimage (`contractIDPreimage` field) and the | ||
dmkozh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
contract executable reference (`executable` field). | ||
|
||
The source and identifier arguments are normally independent of each other with | ||
an exception: identifiers that are built from `CONTRACT_ID_FROM_ASSET` may only | ||
be used in conjunction with built-in token contract source. This handles the | ||
special case of instantiating token contracts corresponding to the classic | ||
Stellar assets (see more details in [CAP-0046-06](./CAP-0046-06.md)). | ||
The executable and identifier arguments are normally independent of each other | ||
with an exception: identifiers that are built from | ||
`CONTRACT_ID_PREIMAGE_FROM_ASSET` may only be used in conjunction with built-in | ||
token contract source. This handles the special case of instantiating token | ||
contracts corresponding to the classic Stellar assets (see more details in | ||
[CAP-0046-06](./CAP-0046-06.md)). | ||
|
||
The host builds the actual contract identifier by computing SHA-256 of the | ||
`HashIDPreimage` corresponding to the `contractID`. If the contract identifier | ||
already exists, the operation fails. | ||
|
||
If the identifier is new, the host will a new `ContractDataEntry` from | ||
[CAP-0046-05](./CAP-0046-05.md) with a `SCV_STATIC` key type, and | ||
`SCS_LEDGER_KEY_CONTRACT_CODE` key value. The value of the entry is | ||
`SCContractCode` that either refers to the WASM code entry or to a built-in | ||
contract (according to the value of `source` field in `CreateContractArgs`). | ||
|
||
#### ED25519-based contract identifiers | ||
|
||
Building a `ENVELOPE_TYPE_CONTRACT_ID_FROM_ED25519` preimage based on a public | ||
ED25519 key has an a additional signature verification step as to make sure | ||
that this key has authorized creating a contract on their behalf. | ||
`HashIDPreimage` corresponding to the `contractIDPreimage`. If the contract | ||
identifier already exists, the operation fails. | ||
|
||
The owner of the key must sign SHA-256 hash of `HashIDPreimage` of type | ||
`ENVELOPE_TYPE_CREATE_CONTRACT_ARGS`, that includes the network id, `salt`, and | ||
`CreateContractSource` args that must match the respective args of | ||
`HOST_FUNCTION_TYPE_CREATE_CONTRACT` invocation. | ||
|
||
#### Installation fused with instantiation | ||
|
||
One of the possible types of `CreateContractSource` is | ||
`CONTRACT_SOURCE_INSTALLED`, that accepts `InstallContractCodeArgs`. This is a | ||
convenience argument that allows to install the code and instantiate a contract | ||
using that code in a single operation. | ||
|
||
The installation implementation is exactly the same as for the case when | ||
`HOST_FUNCTION_TYPE_INSTALL_CONTRACT_CODE` function is called. The contract | ||
will be instantiated with WASM code reference source type that points to the | ||
hash of the newly installed contract. | ||
|
||
As mentioned in the installation section, if the contract code already exists in | ||
the ledger, the operation will still succeed, but no code entry will be | ||
created. | ||
If the identifier is new, the host will create a new Persistent | ||
`ContractDataEntry` from [CAP-0046-05](./CAP-0046-05.md) with a | ||
`SCV_LEDGER_KEY_CONTRACT_INSTANCE` key value. The value of the entry is | ||
`ScContractInstance` that either refers to the WASM code entry or to a built-in | ||
contract (according to the value of the `executable` field in `CreateContractArgs`). | ||
|
||
### Instantiating a contract from a contract | ||
|
||
|
@@ -199,29 +157,34 @@ functionality to support them. | |
The following host functions are provided to instantiate contracts: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...to instantiate contracts and upload Wasm: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
```rust | ||
// Instantiates a contract with the source referring to the provided wasm_hash. | ||
fn create_contract_from_contract(wasm_hash: Object /* 32-bytes array */, | ||
salt: Object /* 32-bytes array */) -> Object /* 32-bytes array */ | ||
// Instantiates a contract with the source referring to the built-in token. | ||
fn create_token_from_contract(salt: Object /* 32-bytes array */) -> Object /* 32-bytes array */ | ||
// Uploads the WASM. Returns the SHA-256 hash of the WASM code. | ||
fn upload_wasm(wasm: Bytes) -> Bytes | ||
|
||
// Creates a WASM instance using the deployer, SHA-256 hash of the WASM, and a user specified salt. | ||
// Returns the Address of the newly created contract. | ||
fn create_contract(deployer: Address, wasm_hash: Bytes, salt:Bytes) -> Address | ||
|
||
// Creates a Stellar Asset Contract for the XDR serialized asset passed in. Returns the Address | ||
// for the newly created contract. | ||
fn create_asset_contract(serialized_asset: Bytes) -> Address | ||
``` | ||
|
||
All of these functions return the identifier of the newly created contract. | ||
The contractIDs for the contracts created with `create_contract` and | ||
`create_asset_contract` are derived from `CONTRACT_ID_PREIMAGE_FROM_ADDRESS` and | ||
`CONTRACT_ID_PREIMAGE_FROM_ASSET` respectively. | ||
dmkozh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The identifier of the created contract is generated by hashing the | ||
`HashIDPreimage` with type `ENVELOPE_TYPE_CONTRACT_ID_FROM_CONTRACT` with the | ||
salt provided by the host function call. | ||
### Updating a contracts code | ||
|
||
### Validator override | ||
We also provide a host function that allows contract instances to update the | ||
WASM executable by first uploading the new code, and then calling | ||
`update_current_contract_wasm` with the hash of the newly uploaded WASM.The | ||
update happens only after the current contract invocation has successfully | ||
finished, so this can be safely called in the middle of a function. | ||
|
||
This proposal adds two new `LedgerHeader` flags that can disable the create and | ||
invoke contract operations using upgrades. The validators can use this | ||
mechanism in case unexpected behaviour is seen. We also considered adding a | ||
mechanism for validators to opt accounts into smart contracts to allow for | ||
a "soft" launch, but the implementation changes to get this to work are not | ||
simple. The validators have the `LedgerHeader` overrides to fall back on, so | ||
it's not clear that the complexity of adding a "soft" launch mechanism is worth | ||
it. | ||
```rust | ||
// Updated the current contracts WASM executable. | ||
fn update_current_contract_wasm(wasm_hash: Bytes) | ||
``` | ||
|
||
## Design Rationale | ||
|
||
|
@@ -247,12 +210,12 @@ can install contract sources to the ledger and then anyone can use them. This | |
encourages sharing the contract code and allows contracts that use it to be | ||
sure that their implementation can't unexpectedly change. | ||
|
||
### Contracts cannot be updated and deleted | ||
### Contracts cannot be deleted, and can only be updated through the update_current_contract_wasm host function | ||
|
||
The contract code reference is stored in a `ContractDataEntry`, but contract | ||
code cannot be updated or deleted in the initial version. The host functions in | ||
[CAP-0046-05](./CAP-0046-05#host-function-additions) to update or delete | ||
`ContractDataEntry` should trap if they are used on contract code. | ||
The contract code reference is stored in a `ContractDataEntry`, but the host | ||
functions in [CAP-0046-05](./CAP-0046-05#host-function-additions) to set, | ||
update, or delete `ContractDataEntry` should trap if they are used on contract | ||
code. | ||
|
||
### Malicious contracts | ||
|
||
|
@@ -285,25 +248,6 @@ The security concerns from CAP-0046 | |
apply here as well. | ||
|
||
In addition to those concerns, this CAP does not provide validators with much | ||
control over contracts on the network. The only mechanism they have is blocking | ||
all contract creations and invocations, which should only be used in drastic | ||
situations. This CAP does not define a way for validators to block specific | ||
contracts. | ||
|
||
## Potential Future Work | ||
|
||
### Mutable contracts support | ||
|
||
While the contracts are immutable in this CAP, it's already possible to make | ||
them 'mutable' via proxy contracts (for example, contract A forwards its | ||
method calls to contract B and ID of contract B is stored in the data of | ||
the contract A). | ||
|
||
To further support mutation via the proxy pattern we could do the following: | ||
|
||
- Allow contract to modify its `SCS_LEDGER_KEY_CONTRACT_CODE` entry (as the | ||
modification can only happen from within the contract, this would need to be | ||
implemented in the first installed version of the contract) | ||
- Allow specifying a contract ID as the contract's source reference, so that the | ||
contract would be guaranteed to have exactly the same implementation as the | ||
referred contract without any additional code. | ||
control over contracts on the network. The only mechanism they have is disabling | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there are more mechanisms now, as it's possible to limit resources on multiple dimensions (e.g. disable large writes or constrain CPU etc.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed this section since it no longer applies and probably belongs in a different CAP. |
||
all Soroban transactions, which should only be used in drastic situations. This | ||
CAP does not define a way for validators to block specific contracts. |
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.
nit:
Uploading
for consistency with op name?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.
Done