diff --git a/README.md b/README.md index 03f244d..7afd1f4 100644 --- a/README.md +++ b/README.md @@ -90,21 +90,25 @@ Two abstractions will be promoted in `Kuai`: `Data storage` and `Data manipulati #### Abstraction of data storage -There'll be three basic data models used in `Kuai` based DApps: `Data`, `Code`, `MultiCoin Token` +There'll be three basic data models used in `Kuai` based DApps: `Store`, `Contract`, `Token` -##### Data +##### Store -The basic function of cells in CKB is storing data. But data are meaningless without schema, so the first basic model introduced by `Kuai` is named `Data` which is used to transform plain data into a structured data with schema. +The basic function of cells in CKB is storing data. But data are meaningless without schema, so the first basic model introduced by `Kuai` is named `Store` which is used to transform plain data into a structured data with schema, similar to [ORM](https://www.wikiwand.com/en/Object-relational_mapping) -Note that a `Data` model could be a group of cells matching the same pattern and working as an entity so it could be regarded as a virutal DApp. Say we have 2 DApps in `School Roster Data` models, each of them consists of many `Student Data` models. And we are going to build a `Sports Meeting` DApp, a new `Sports Meeting Data` model could be created and consists of partial `Student Data` from School A and B respectively, it should work as same as a `School Roster` DApp. +Note that a `Store` model could be a group of cells matching the same `pattern` and working as an entity so it could be regarded as a virutal DApp. Say we have 2 DApps in `School Roster Store` models, each of them consists of many `Student Store` models. And we are going to build a `Sports Meeting` DApp, a new `Sports Meeting Store` model could be created and consists of partial `Student Store` from School A and B respectively, it should work as same as a `School Roster` DApp. -##### Code +Sidenote: +For JavaScript developers, the `Repository` concept in [TypeORM](https://github.com/typeorm/typeorm/blob/master/docs/working-with-repository.md) is quite similar, or to say, `Store` is a simplified `Repository` in TypeORM. `Store` has its own schema and accepts a pattern to load data from CKB, just as `Repository` accepts an `Entity` and builds a query string to load data from database. + -`Code` model is extended from `Data` model and used for scripts. A `Code` model has not only attributes, but also methods. In other words, it's not only read-/writable, but also executable. `Code` model's data/state could be updated by some rules rather than a simple `set` method inherited from the `Data` model. +##### Contract -##### MultiCoin Token +`Contract` model is extended from `Store` model and used for scripts. A `Contract` model has not only attributes, but also methods. In other words, it's not only read-/writable, but also executable. `Contract` model's data/state could be updated by some rules rather than a simple `set` method inherited from the `Store` model. -Token is a general use case that should have ability of transferring between DApps, so the third model to introduce is `MultiCoin Token`, which is specialized from `Code` model with specific attributes and methods. +##### Token + +Token is a general use case that should have ability of transferring between DApps, so the third model to introduce is `Token`, which is specialized from `Contract` model with specific attributes and methods. Especially, a `Token` model is a collection of tokens, similar to [ERC 1155](https://eips.ethereum.org/EIPS/eip-1155) that represents multiple token types in one model. #### Abstraction of data manipulation @@ -112,31 +116,31 @@ DApps can read states from each other freely because data/states are arranged un DApps can also communicate with each other freely if data manipulation has been abstracted. -##### Data +##### Store -A `Data` model should have 7 basic interfaces +A `Store` model should have 7 basic interfaces -- New(pattern): create an `Data` model binding to specified cells, as a DApp located by the pattern, ps: it's a virtual DApp rather than an explicit DApp, but because cells are following the same rule, they can work together as an entity. +- New(pattern): create an `Store` model binding to specified cells, as a DApp located by the pattern, ps: it's a virtual DApp rather than an explicit DApp, but because cells are following the same rule, they can work together as an entity. - Destroy(): remove state and turn the cells into empty -- Duplicate(): create a `Data` model from a instance +- Duplicate(): create a `Store` model from an instance - Sync(blockNumber?): will load and update data from the chain at latest(or specific block) global state - Get(path): will read value of specified path - Set(path, value): will set given value at specified path - Delete: (path): remove key/value at specified path -##### Code +##### Contract -A `Code` model should have 5 more interfaces than `Data` model +A `Contract` model should have 5 more interfaces than `Store` model -- Deploy(): deploy the script on the chain -- Extend(code): extends from an existing `Code` model for overriding -- Upgrade(): upgrade the script on the chain -- Run(method: string, params: Array): call a method of the script to update its state -- Link(abi: Array): instantiate a script SDK from ABI +- Deploy(): deploy the contract on the chain +- Extend(contract): extends from an existing `Contract` model for overriding +- Upgrade(): upgrade the contract on the chain +- Run(method: string, params: Array): call a method of the contract to update its state +- Link(interfaces: Array): instantiate a contract SDK from interfaces. mainly used to encode a human-readable method call into a message transferred between `Contract` models. -##### MultiCoin Token +##### Token -A generally used token must have some methods based on `Code` +A generally used token must have some methods based on `Contract` - Metadata(tokenId, metadata?): a setter/getter to the metadata of a specific token id including `name`, `symbol`, `tokenURI`, `totalSupply`) - Mint(tokenId, amount): Mint a token @@ -145,12 +149,19 @@ A generally used token must have some methods based on `Code` - Redeem(address, dapp, tokenId, amount): unstake from a dapp and burn a staking token - Lock(address, amount, tokenId, locktime): lock tokens, for cases like pre-sale, bounty of a team - Unlock(address, tokenId, amount): unlock tokens -- Combine(token): combine two `MultiCoin Token` models into one +- Combine(token): combine two `Token` models into one - GetBalance(address, tokenId): get token balance of a given address Notice, all actions will be grouped and finalized together, viz. `mint`/`send` multiple token id will be finalized with a single transaction +Sidenote: +The relations between `Store`, `Contract` and `Token` could be analogized to JavaScript Built-in Objects [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) and [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) +`Store` is similar to `Map` in JavaScript which is an iterable collection of key-value pairs and has basic methods such sa `get`, `has`, `set`, `forEach`, `keys`, `values`, `entries`. Its only usage is to store structured data. +`Contract` extends `Store` to have the ability to retrieve/alter its internal data by specific interfaces rather than simple `get/set` methods. For interoperability, `Contract` exposes a uniform approach to update its state, named `run(methodSignature, argumentList)` which is similar to `Reflect.apply(fn, thisArg, argumentList)` so the trigger could be broadcasted via message in the model tree, we will talk about it later. +`Token` is specialized from `Contract` and can be thought of as `Date` in JavaScript which has its own specific attributes and methods `now`, `setHour`. `Token` would have methods like `mint`, `burn`, `transfer`, etc. `Date` and `Token` are used so widely that they have their own seats in Built-in Objects. More advanced models will be added along with the evolving ecosystem. + + #### Reactive Lazy Evaluation As mentioned above @@ -160,11 +171,11 @@ As mentioned above All actions/manipulations adopted on a model will be cached and evaluated at the end of the pipeline. ```javascript -const model = new Data() +const model = new Store() model.action_1() model.action_2() // ... -modeol.action_n() +model.action_n() /** * get a structure with initial state and actions @@ -192,17 +203,29 @@ model.finalize() Lazy evaluation is beneficial to the following points: -1. state of the model could be traversed for debugging -2. actions could be revoked easily to find the best path of state transition. -3. inspector(or other dependencies) could be injected to enhance development +1. State of the model could be traversed for debugging +2. Actions could be revoked easily to find the best path of state transition. +3. Inspector(or other dependencies) could be injected to enhance development #### Model Tree -There would be multiple levels of `Data` models constructing a model tree. +The keyword **model tree** was mentioned in the sidenote above of analogizing `Contract` to `Reflect`. -Every cell on CKB could be treated as a DApp because every cell has its own state and script while a group of cells using the same script should also be treated as a DApp because they adopt the same logic on a broader state. +Every cell of CKB could be treated as a minimal DApp because every cell has its own state and script, but usually a group of cells using the same script will work together as a real DApp because it adopts the same logic on a broader state. Thus a DApp running on CKB could be represented as a 2-level tree which has cells as leaves and shared scripts as nodes. + +```mermaid +flowchart BT +cell_0 --> script_a +cell_1 --> script_a +cell_2 --> script_a +cell_3 --> script_b +cell_4 --> script_b +cell_5 --> script_b +script_a --> DApp +script_b --> DApp +``` -Besides, if a DApp_A wants to interact with another DApp_B, stateof DApp_B would be a part of DApp_A's state so the models would be like +Similarly, a DApp could be destructed into a multi-level **model tree**. And if a **DApp_A** wants to interact with another **DApp_B**, state of **DApp_B** would be a part of **DApp_A**'s so the **model tree** of **DApp_A** would be like ```mermaid flowchart BT @@ -212,12 +235,42 @@ cell_a_1_model --> dapp_a_sub_model cell_a_n_model --> dapp_a_sub_model dapp_a_sub_model --> dapp_a_model -cell_b_0_model --> dapp_b_model -cell_b_1_model --> dapp_b_model -cell_b_n_model --> dapp_b_model +dapp_b_0_sub_model --> dapp_b_model +dapp_b_1_sub_model --> dapp_b_model +dapp_b_n_sub_model --> dapp_b_model dapp_b_model --> dapp_a_model ``` +With a set of standardized interfaces, the detail of DApp_B's model tree could be obscure to DApp_A. + +This idea is from [actor model](https://www.oreilly.com/library/view/applied-akka-patterns/9781491934876/ch01.html) so the structure of an application is similar + +![actor model](https://camo.githubusercontent.com/0aec56b15a4dcccf39723b43503b32e3b3a3a364ef0c3f760c9fc6a0e0ae77f3/68747470733a2f2f7777772e7a61727469732e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f696d6167652d31332d373638783237352e706e672e77656270) + +Actor model is a programming paradigm for concurrent computation, states will not be stored in a single point, but distributed to various actors so computation could be performed in many actors. + +Actor model follows several fundamental rules +- All computation is performed within an actor +- Actors can communicate only through messages +- In response to a message, an actor can: + - Change its state or behavior + - Send messages to other actors + - Create a finite number of child actors + +From the perspective of the model tree and CKB's cell model, states/cells will not be stored in a single `Store` model, but distributed to a bulk of `Store` models and can be updated in parallel via messages. If a model handles a message, it would +- Update model's state +- Proxy the message to another model +- Create a new model to fetch more state/cells and handle the message + +States/cells are arranged isolatedly into different pieces and can only be changed by messages, the updates are sequenced and data conflicts are avoided naturally. + +One more interesting point is that model tree could be server-agnostic. As mentioned above, if **DApp_A** relies on **DApp_B**, e.g. **Swap DApp** relies on **Token DApp**, model tree of **DApp_B** will be a part of **DApp_A**'s model tree, illustrated in the diagram above, and **DApp_A** have to rebuild the model tree of **DApp_B** to interact with it. In actor model, **DApp_B**'s model tree could be rebuilt locally or remotely because the interactions are transferred by messages, thus **DApp_A** could request the server of **DApp_B** to output `Store` by a specific `action`. + +Take a concrete example, there's a **Swap DApp** to swap **Token A** and **Token B**. Now **user** wants to swap **X token a** with **Y token b** from **Swap DApp** +1. **Swap DApp** requests **Token A DApp** to take an action **transfer X from swap_pool to user** and return a `Store of Token A` +2. **Swap DApp** requests **Token B DApp** to take an action **transfer Y from user to swap_pool** and return a `Store of Token B`. +3. **Swap DApp** combines `Store of Token A` and `Store of Token B` to generate a transaction for user to confirm the swap. + ### Conventions `Kuai-convention` is a set of conventions for the implementation reference of `Kuai-runtime` and the usage guide of `Kuai-runtime`, such as: @@ -317,3 +370,7 @@ Last but not least, as an open source project, `Kuai` would be introduced to dev - M6 - Use the project to deliver a simple .bit dapp +## Promising Features +- Working with contracts purely by JavaScript/TypeScript instead of Rust/C; +- Supporting [open transactions](https://en.bitcoin.it/wiki/Open_Transactions) for further use cases; +- Adding [DSL](https://www.wikiwand.com/en/Domain-specific_language) for facilication of development.