Skip to content

Commit

Permalink
CAP-0047 - remove ContractCodeEntry and outdated LedgerHeader flags (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sisuresh committed May 31, 2022
1 parent bfd8826 commit 2351beb
Showing 1 changed file with 59 additions and 121 deletions.
180 changes: 59 additions & 121 deletions core/cap-0047.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,52 +34,26 @@ This CAP is aligned with the following Stellar Network Goals:
create highly usable products

## Abstract
Users need a way to manage smart contracts on the network. This CAP allows the
user to upload WASM code into a LedgerEntry, update the contract code, and lock
all updates to the contract code going forward if desired. This CAP also lets
the validators initially roll out smart contracts access to a subset of accounts
using an allow list, and also gives them the ability to turn off all smart
contract functionality if some unexpected behavior is found in the protocol.
Users need a way to manage smart contracts on the network. This CAP allows users
to upload WASM code into a LedgerEntry. The host functions in
[CAP-0053](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0053.md#host-function-additions)
allow for contract updates and deletion. This CAP also lets the validators turn
off all smart contract functionality if some unexpected behavior is found in the
protocol.

## Specification

### XDR

```diff mddiffcheck.base=ce981331000128a1c145ec364fbd83d3dd4be5ed
diff --git a/src/xdr/Stellar-ledger-entries.x b/src/xdr/Stellar-ledger-entries.x
index 3eb578f16..b1a74cfaa 100644
index 3eb578f16..8909d003c 100644
--- a/src/xdr/Stellar-ledger-entries.x
+++ b/src/xdr/Stellar-ledger-entries.x
@@ -491,6 +491,61 @@ struct LiquidityPoolEntry
@@ -491,6 +491,35 @@ struct LiquidityPoolEntry
body;
};

+//TODO:256000 is a placeholder. Still need to figure out if this is the maximum
+// or the upper bound for a maximum set by the validators
+typedef opaque WASMCode<256000>;
+
+enum ContractCodeType {
+ CONTRACT_CODE_WASM = 0
+};
+
+union ContractBody switch (ContractCodeType type)
+{
+ case CONTRACT_CODE_WASM:
+ WASMCode wasm;
+};
+
+struct ContractCodeEntry {
+ union switch (int v)
+ {
+ case 0:
+ void;
+ }
+ ext;
+
+ Hash contractID;
+ ContractBody body;
+};
+
+enum ConfigSettingType
+{
+ CONFIG_TYPE_UINT32 = 1
Expand Down Expand Up @@ -112,26 +86,19 @@ index 3eb578f16..b1a74cfaa 100644
struct LedgerEntryExtensionV1
{
SponsorshipDescriptor sponsoringID;
@@ -521,6 +576,10 @@ struct LedgerEntry
@@ -521,6 +550,8 @@ struct LedgerEntry
ClaimableBalanceEntry claimableBalance;
case LIQUIDITY_POOL:
LiquidityPoolEntry liquidityPool;
+ case CONTRACT_CODE:
+ ContractCodeEntry contractCode;
+ case CONFIG:
+ ConfigurationEntry globalContractConfig;
}
data;

@@ -575,6 +634,16 @@ case LIQUIDITY_POOL:
@@ -575,6 +606,11 @@ case LIQUIDITY_POOL:
{
PoolID liquidityPoolID;
} liquidityPool;
+case CONTRACT_CODE:
+ struct
+ {
+ Hash contractID;
+ } contractCode;
+case CONFIG:
+ struct
+ {
Expand All @@ -140,7 +107,7 @@ index 3eb578f16..b1a74cfaa 100644
};

// list of all envelope types used in the application
@@ -589,6 +658,9 @@ enum EnvelopeType
@@ -589,6 +625,9 @@ enum EnvelopeType
ENVELOPE_TYPE_SCPVALUE = 4,
ENVELOPE_TYPE_TX_FEE_BUMP = 5,
ENVELOPE_TYPE_OP_ID = 6,
Expand All @@ -152,15 +119,15 @@ index 3eb578f16..b1a74cfaa 100644
};
}
diff --git a/src/xdr/Stellar-ledger.x b/src/xdr/Stellar-ledger.x
index 84b84cbf7..1ccff93b9 100644
index 84b84cbf7..97ffc5979 100644
--- a/src/xdr/Stellar-ledger.x
+++ b/src/xdr/Stellar-ledger.x
@@ -47,13 +47,17 @@ struct StellarValue
@@ -47,13 +47,15 @@ struct StellarValue
ext;
};

-const MASK_LEDGER_HEADER_FLAGS = 0x7;
+const MASK_LEDGER_HEADER_FLAGS = 0x7F;
+const MASK_LEDGER_HEADER_FLAGS = 0x1F;

enum LedgerHeaderFlags
{
Expand All @@ -169,13 +136,11 @@ index 84b84cbf7..1ccff93b9 100644
- DISABLE_LIQUIDITY_POOL_WITHDRAWAL_FLAG = 0x4
+ DISABLE_LIQUIDITY_POOL_WITHDRAWAL_FLAG = 0x4,
+ DISABLE_CONTRACT_CREATE = 0x8,
+ DISABLE_CONTRACT_UPDATE = 0x10,
+ DISABLE_CONTRACT_REMOVE = 0x20,
+ DISABLE_CONTRACT_INVOKE = 0x40
+ DISABLE_CONTRACT_INVOKE = 0x10
};

struct LedgerHeaderExtensionV1
@@ -122,7 +126,8 @@ enum LedgerUpgradeType
@@ -122,7 +124,8 @@ enum LedgerUpgradeType
LEDGER_UPGRADE_BASE_FEE = 2,
LEDGER_UPGRADE_MAX_TX_SET_SIZE = 3,
LEDGER_UPGRADE_BASE_RESERVE = 4,
Expand All @@ -185,7 +150,7 @@ index 84b84cbf7..1ccff93b9 100644
};

union LedgerUpgrade switch (LedgerUpgradeType type)
@@ -137,6 +142,12 @@ case LEDGER_UPGRADE_BASE_RESERVE:
@@ -137,6 +140,12 @@ case LEDGER_UPGRADE_BASE_RESERVE:
uint32 newBaseReserve; // update baseReserve
case LEDGER_UPGRADE_FLAGS:
uint32 newFlags; // update flags
Expand All @@ -199,7 +164,7 @@ index 84b84cbf7..1ccff93b9 100644

/* Entries used to define the bucket list */
diff --git a/src/xdr/Stellar-transaction.x b/src/xdr/Stellar-transaction.x
index 23918226d..14ecbd0de 100644
index 23918226d..3ed9625bc 100644
--- a/src/xdr/Stellar-transaction.x
+++ b/src/xdr/Stellar-transaction.x
@@ -545,6 +545,18 @@ case ENVELOPE_TYPE_POOL_REVOKE_OP_ID:
Expand All @@ -221,7 +186,7 @@ index 23918226d..14ecbd0de 100644
};

enum MemoType
@@ -733,6 +745,40 @@ struct FeeBumpTransactionEnvelope
@@ -733,6 +745,41 @@ struct FeeBumpTransactionEnvelope
DecoratedSignature signatures<20>;
};

Expand All @@ -239,8 +204,9 @@ index 23918226d..14ecbd0de 100644
+ // used to derive the contractID
+ uint256 salt;
+
+ // contract code to be written into a ContractCodeEntry
+ ContractBody body;
+ //TODO:256000 is a placeholder. Still need to figure out if this is the maximum
+ // or the upper bound for a maximum set by the validators
+ opaque contractCode<256000>;
+
+ union switch (int v)
+ {
Expand All @@ -262,7 +228,7 @@ index 23918226d..14ecbd0de 100644
/* A TransactionEnvelope wraps a transaction with signatures. */
union TransactionEnvelope switch (EnvelopeType type)
{
@@ -742,6 +788,8 @@ case ENVELOPE_TYPE_TX:
@@ -742,6 +789,8 @@ case ENVELOPE_TYPE_TX:
TransactionV1Envelope v1;
case ENVELOPE_TYPE_TX_FEE_BUMP:
FeeBumpTransactionEnvelope feeBump;
Expand All @@ -271,7 +237,7 @@ index 23918226d..14ecbd0de 100644
};

struct TransactionSignaturePayload
@@ -754,6 +802,8 @@ struct TransactionSignaturePayload
@@ -754,6 +803,8 @@ struct TransactionSignaturePayload
Transaction tx;
case ENVELOPE_TYPE_TX_FEE_BUMP:
FeeBumpTransaction feeBump;
Expand All @@ -285,28 +251,15 @@ index 23918226d..14ecbd0de 100644
## Semantics

### CreateContractTransaction
`CreateContractTransaction` will create a new `ContractCodeEntry`, and assign it
a unique contractID. The contractID will be the SHA-256 hash of a
`HashIDPreimage`, with type `ENVELOPE_TYPE_CONTRACT_ID`, which contains the
source account that created the contract, and a salt provided by the user. The
transaction will fail if a contract with the calculated contractID already exists.

TODO: Reserve requirement for `ContractCodeEntry`?

### Host function to remove contracts
We will provide a host function to allow for the removal of contracts.
`remove_contract` will remove the `ContractCodeEntry` that corresponds to the
contractID of the current executing contract. The call to `remove_contract` will
result in a positive termination condition. (TODO: Is this possible?)
```rust
fn remove_contract() -> bool;
```
`CreateContractTransaction` will create a new `ContractDataEntry`
(https://github.com/stellar/stellar-protocol/blob/master/core/cap-0053.md) with
a `SCV_STATIC` key type, and `SCS_LEDGER_KEY_CONTRACT_CODE_WASM` key value. The
contractID will be the SHA-256 hash of a `HashIDPreimage`, with type
`ENVELOPE_TYPE_CONTRACT_ID`, which contains the source account that created the
contract, and a salt provided by the user. The transaction will fail if a
contract with the calculated contractID already exists.

TODO: What's the incentive for users to remove contracts (this depends on the
fee model)? This might not be worth it for the initial version if the incentives
don't make sense.
TODO:What about contract data?
TODO:What if contract is recreated?
TODO: Reserve requirement for `ContractDataEntry`?

### Host function to create contracts
Factory contracts are quite popular already on other networks, so this CAP adds
Expand Down Expand Up @@ -335,14 +288,13 @@ validators cannot add or remove configuration settings using `LedgerUpgrades`.
New configurations need to be added during the protocol upgrade.

### Validator override
This proposal adds four new `LedgerHeader` flags that can disable the create,
update, remove, 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.
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.

## Design Rationale

Expand All @@ -353,26 +305,20 @@ more general solution than baking it into the protocol. The downside is this is
more error prone and will take more space since the same logic will be
implemented multiple times.

### ContractCodeEntry has no account associated with it
The account that creates the `ContractCodeEntry` is not tied to it in
any way. This allows for contract management and authorization to be handled in
the contract using whichever custom mechanism the contract creator chooses.

This also means that any changes to the contract metadata must be done in the
contract through host functions. This isn't ideal, since some of these features
may not be available when the contracts are initally written. For example, we
are considering leaving the metadata out of the initial version, or even
introducing a fast routing scheme to the metadata mentioned at the bottom of
this CAP in the future. An alternative to this is to add an "admin" account to
the `ContractCodeEntry`, which can be used along with another transaction to
update the metadata.

### Contracts are immutable on launch
Contract writers can use proxy contracts to upgrade contract logic, so mutable
contract code is not neccessary. Keeping contracts immutable will keep the
protocol implementation simpler so we can get a working product out faster. We
can always add this functionality later. Note that contracts written before
contract mutability is introduced will always be immutable.
### ContractDataEntry has no account associated with it
The account that creates the `ContractDataEntry` that contains the contract code
is not tied to it in any way. This allows for contract management and
authorization to be handled in the contract using whichever custom mechanism the
contract creator chooses.

### Contracts can be updated and deleted
The contract code is stored in a `ContractDataEntry`, so contracts can update or
delete that entry. If the contract code is updated or deleted, the host function
should terminate contract invocation to prevent the contract from doing
something potentialy dangerous like writing new data after deleting a contract.

TODO: Contract ID's don't have a seqnum or a nonce, so they can be recreated.
This could be an issue if the deleted contract still has data on the ledger.

### Malicious contracts
The validators do not have a mechanism to ban specific contracts. Any kind of
Expand Down Expand Up @@ -407,20 +353,13 @@ block specific contracts.

## Extensions

### Mutable contracts
We can add host functions that allow contracts to rewrite their own
`ContractCodeEntry`, which will allow for more flexiblity like upgradable
contracts without proxies. If mutable contracts are introduced, the host
function to write the updated `ContractCodeEntry` should be the last thing to
happen in the contract. This would ideally be enforced with a positive
termination condition in WASM.

### Contract metadata
A ContractMetadata union can accompany the WASM contract, which would specify
the functions available in the contract, along with the return and argument
types for each function. The host function to call into the WASM contract should
first validate the contract call against the metadata. If the contract call does
not match one of the functions specified in the metadata, then an error will be
A ContractMetadata union can accompany the WASM contract as another
`ContractDataEntry` with a new `SCV_STATIC` key type, which would specify the
functions available in the contract, along with the return and argument types
for each function. The host function to call into the WASM contract should first
validate the contract call against the metadata. If the contract call does not
match one of the functions specified in the metadata, then an error will be
thrown.

Users can also load the ContractMetadata to see what the interface looks like.
Expand Down Expand Up @@ -465,7 +404,6 @@ case METADATA_TYPE_V0:
FunctionSignature interface<10>;
};

//CreateContractTransaction and ContractCodeEntry should be extended to add on ContractMetadata
```
### Contract metadata can include a router along with the raw WASM
- This has a couple advantages -
Expand Down

0 comments on commit 2351beb

Please sign in to comment.