Эх сурвалжийг харах

DEP 0002: Smart Contract Composability

x 2 жил өмнө
parent
commit
12bc7c74d9
2 өөрчлөгдсөн 117 нэмэгдсэн , 1 устгасан
  1. 2 1
      doc/src/SUMMARY.md
  2. 115 0
      doc/src/dep/0002.md

+ 2 - 1
doc/src/SUMMARY.md

@@ -43,7 +43,7 @@
   - [P2P Network](arch/p2p-network.md)
   - [Services](arch/services.md)
   - [Smart Contracts](arch/sc/sc.md)
-  - [Transaction lifetime](arch/sc/tx-lifetime.md)
+    - [Transaction lifetime](arch/sc/tx-lifetime.md)
 - [zkas](zkas/index.md)
   - [Bincode](zkas/bincode.md)
   - [zkVM](zkas/zkvm.md)
@@ -69,6 +69,7 @@
 # DEP
 
 - [DEP 0001: Version Message Info](dep/0001.md)
+- [DEP 0002: Smart Contract Composability](dep/0002.md)
 
 # Specs
 

+ 115 - 0
doc/src/dep/0002.md

@@ -0,0 +1,115 @@
+# DEP 0002: Smart Contract Composability
+
+```
+status: draft
+```
+
+## Current Situation
+
+When creating the DAO, we needed to invent the concept of protocol owned liquidity in DarkFi.
+Without this, in order to have on chain DAO treasuries, the DarkFi blockchain would have to
+recognize funds held by both the money and DAO contracts as valid. This introduces a security
+risk if there is an error in the DAO contracts.
+
+Additionally it means that liquidity could only be held by the DAO contract, and any liquidity
+held by other contracts would have to be recognized by the consensus as valid. This would
+restrict the protocols that could work with on chain liquidity to a small hardcoded subset
+due to security.
+
+Motivated by the desire to enable protocol owned liquidity, we created the concept in
+`money::transfer()` of the `spend_hook`.
+
+Firstly a quick recap of how `money::transfer()` works. During the mint phase of creating
+coins, we construct a coin `C = hash(…, spend_hook, user_data)`. The `…` contains coin data such
+as value, token ID and other attributes. During the burn phase we produce a deterministic
+unlinkable nullifier.
+
+This is a more general ZK concept of committing to several attributes, and then later either
+full on revealing them or more specifically applying constraints to the attributes.
+To enable protocol owned liquidity, we introduced the coin attributes `spend_hook` and
+`user_data`, motivated by these desires:
+
+* Generalize protocol-owned liquidity enabling any third party to write contracts that
+  own liquidity.
+* Stronger security model for on chain liquidity by only depending on the money contract
+  when composed with contracts like the DAO.
+
+When a coin is spent, the `spend_hook` is revealed publicly. The `money::transfer()` call
+enforces that the subsequent contract called in the tx matches the `spend_hook`.
+In our example, `spend_hook = DAO`, and then our tx will have two calls:
+`[money::transfer(), DAO::exec()]`. When spending a coin where the `spend_hook = DAO`,
+then `money::transfer()` will check the next contract in the tx will match the `spend_hook`.
+
+Now you might ask some questions:
+
+* Here we are listing `DAO`, but actually we need a stricter check that the call is `DAO::exec()`
+  and not some other DAO method call.
+* We need to enforce which DAO we are operating on.
+
+This is where the `user_data` is used. We can commit to several things including which function
+is called in the contract. Since in the DAO, only `DAO::exec()` can be composed, we just sidestep
+this and enforce that when a tx has two calls, then the DAO one must be `DAO::exec()`. We then
+use the `user_data` to store the DAO bulla.
+
+## Motivation: Limitations of Current Approach
+
+The current approach enables contracts to own liquidity, which is how we can have DAO on chain
+treasuries. We have the ability for contracts to directly call other contracts. However this
+calling mechanism is static.
+
+We desire now to generalize the DAO calling mechanism, so any contract could be called.
+Currently `DAO::exec()` deserializes the `money::transfer()` calldata, and then enforces its
+checks on it inside wasm. These checks are hardcoded.
+
+It would be very useful if instead this data or code were to be dynamic. Therefore a DAO
+proposal could be called, not to call `money::transfer()` but instead to call another contract.
+This system would then be generic and usable with other contracts, such as an algorithmic
+streaming contract making calls on a ZK NFT.
+
+## Proposal: Introspective Params
+
+The current `ContractCall` struct looks like:
+
+```
+pub struct ContractCall {
+    /// ID of the contract invoked
+    pub contract_id: ContractId,
+    /// Call data passed to the contract
+    pub data: Vec<u8>,
+}
+```
+
+We propose to change the `data` field to this:
+
+```
+pub struct ContractCall {
+    /// ID of the contract invoked
+    pub contract_id: ContractId,
+    /// Named call data passed to the contract
+    pub data: HashMap<String, Vec<u8>>,
+}
+```
+
+This way contracts can query each other's calldata in a dynamic compatible way.
+Some part of the params for a contract may be specific to that contract. Another part
+might be generic, which shares the same struct with multiple other contracts.
+This enables contracts to query an interface from another contract's calldata,
+deserialize that data and work with it, without having to hardcode a dependency,
+e.g `DAO::exec()` hardcoding a dependency on `money::transfer()` params.
+
+## Note on Auth Modules
+
+An alternative approach is introducing the concept of auth modules. So for example, with
+the DAO, a user could deploy their own contract on chain with specific logic, then
+make a proposal to execute that contract. We could also supply our own auth module with
+hardcoded branching support for several common contract types.
+
+However while this may be desirable in some cases where complex logic in DAO proposals
+are required, it presents several downsides:
+
+* The supplied auth module will hardcode support for a few contract types and not be
+  properly generic.
+* User deployed contracts could be expensive and error prone.
+* For efficiency the DAO would probably end up hardcoding support for several contract
+  types directly, as well as other composable contracts.
+