Skip to content
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

OEP11: shard smart contract template proposal #54

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions OEPS/OEP-11_en-US.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<pre>
OEP: 11
Title: Ontology X-Shard Smart Contract Specification
Author: qiluge <[email protected]>
Type: Standard
Status: Draft
Created: 2019-06-11
</pre>

==Abstract==

In the sharding network, the traditional smart contract can not achieve cross-shards invocation, which will not give full play to the advantages of shard. Therefore, it is necessary to supplement the original smart contract in order to adapt to the new environment. These supplements are mainly embodied in allowing smart contracts to run across shards and contract migration across shards.

There are two meanings of the cross-shards running of the contract. One is that the contract can run on multiple shards. The other is that the contract can call other contracts across shard. There is a precondition for cross-shards running, that is, the contract across the shard must be between the father and son shard cluster, can be between father and son, brothers and relatives, but not between grandchildren, uncles and cousins shard.

Contract cross-shards migration refers to the contract migration between shards, which transfers all the states of the contract from one shard to another. The limitation of this migration is that it can only migrate between child shards that have the same parent shard.

To implement cross-shards running and migration of contracts, it is necessary to supplement existing smart contracts, including adding meta data to identify contract status and adding cross-shards communication API. These are the contents of OEP-11.

==Motivation==

In order to enable smart contracts to communicate across shards in an ontology sharding network.

==Specification==

===Contract meta data===

====Definition====

<pre>
type MetaDataCode struct {
OntVersion uint64
Contract Address
Owner Address
AllShard bool
IsFrozen bool
ShardId uint64

InvokedContract []Address
}
</pre>

The metadata of the contract describes the status of the contract, and the meanings of each field are as follows:

<pre>
OntVersion: version of metadata;
Contract: contract address;
Owner: contract owner;
AllShard: bool, identify contract running mode, multi shard or single shard;
IsFrozen: bool, identify the contract is frozen or not, if frozen, the contract will not be invoked; this field is used when the contract migrates across shards;
ShardId: integer, identify single shard contract running shard;
InvokedContract: address array, all contract can be x-shard call by self.
</pre>

====Initialize Meta Data====

The contract code and the contract metadata must be on the same shard, which means that the contract is deployed on which shard and the contract metadata is on which shard. However, contract metadata is not automatically generated with contract deployment, that is, after contract deployment, the corresponding metadata is empty.

This is designed to be compatible with existing old contracts on Ontology network because they have no metadata. When a contract does not have corresponding metadata, it can only run on deployed shards and cannot communicate across shards, which means that it cannot call contracts on other shards or be accessed by contracts on other shards. Once the contract is deployed, metadata can be initialized for the contract at any time, but this can only be achieved by calling the neovm runtime API <code>InitMetaData</code> provided by ontology in this contract.

<pre>
def InitMetaData(owner, allShard, isfrozen, shardId, invokedContracts):
"""
:param owner: address, contract owner
:param allShard: bool, running multi shard or not
:param isfrozen: bool, freeze contract or not
:param shardId: integer, running shard id
:param invokedContracts: address array, all contract can be x-shard call by self
"""
</pre>

The combination of different values of <code>AllShard</code> and <code>ShardId</code> identifies the running mode of the contract. When <code>AllShard</code> is true, the contract runs in multi-shard mode, otherwise the contract runs in single-shard mode; <code>ShardId</code> must be the Id of the current shard where the contract deployed or the Id of the sub-shard of the current shard. In the single-shard mode, when the state of the contract has been modified, that is, when the contract has stored data on the ontology chain, <code>ShardId</code> can only be the current shard Id. This design is designed to support cross-shards migration of contracts, because contracts can only migrate between sub-shards with the same parent shard, so if the contract runs for a period of time and records the data, and then initializes the metadata which the <code>ShardId</code> is the id of the sub-shard of the current shard, the contract will not be able to migrate between shards. Because the data generated by the contract is stored on the current shard. On the other hand, when the status of the contract has been modified and metadata is initialized, the running mode of the contract can be set to multi-shard, that is, <code>AllShard</code> of metadata is set to true. Because the contract running in multi-shard mode does not need to support contract migration, it only need to support invoked between a parent shard and all child shards.

So the correct approach is that each contract should define a <code>init</code> method to initialize some states, the initialization process should contain <code>InitMetaData</code> and initialization of other contract states. It should be noted that <code>InitMetaData</code> is best before the initialization of other contract states. When the contract is deployed, you should call the <code>init</code> method to initialize and generate the metadata firstly. After that, invoke other function. For the old contract of the existing ontology, since the deployed contract does not contain the <code>InitMetaData</code> call interface, the metadata cannot be initialized directly. In this case, if you want the contract to run on multiple shards, you can only upgrade the contract first, and add the <code>InitMetaData</code> call interface to the new contract. After the contract upgrade is complete, call <code>InitMetaData</code> to generate metadata. If the existing contract does not leave a contract migrate interface, the contract can only continue to run on the ontology main chain in single-shard mode.

====How to Change Meta Data====

When the contract needs to be migrated, the contract needs to be frozen first. At this time, we need to modify the contract metadata to freeze the contract. However, the <code>InitMetaData</code> API can only be used to initialize metadata and cannot modify existing metadata. Once the contract is frozen via <code>InitMetaData</code>, the contract can no longer be called, this means that you can't call any contract api to unfreeze contract.

In order to solve this drawback, the ontology should add a transaction type that is specifically used to change the metadata of the contract. This new transaction type is called <code>ChangeMetaData</code>, and its <code>TxType</code> is <code>0xd2</code>, and its payload is [[#Definition | <code>MetaDataCode</code>]].

For this transaction, in order to prevent arbitrarily modifying the metadata of the contract, authentication is necessary, only the <code>Owner</code> in the metadata is allowed to modify this metadata, which is also the role of adding the owner field in the metadata. At the same time, this transaction also limits the content of the metadata that can be modified. Only the <code>Owner</code>, <code>IsFrozen</code>, <code>InvokedContract</code> fields can be modified. That is, you can only change the <code>Owner</code> of the contract, freeze or unfreeze the contract, and update the contract that the contract can be called across the shards.

====Meta Data Constraints====

In order to ensure the normal running of contracts between shards, when setting metadata, it need to meet certain constraints, as follows:

1. <code>ShardId</code> can only be the Id of the contracted deployment shard or the Id of the sub-shard of the deployed shard.

2. In addition to allowing contracts to across shards call themselves, the chain of cross-shards invoke of contracts can not be looped. For example, there is contract B in contract A <code>InvokedContracts</code> and contract B <code>InvokedContracts</code> can not contain A, but can contain B itself.

===X-Shard Call Interface===

In the sharding network, ontology provides two interfaces for cross-shards communication: <code>NotifyRemoteShard</code> for asynchronous and <code>InvokeRemoteShard</code> for synchronous calls. Because the communication process in cross-shards calls depends on network transmission, the parameters transmitted during cross-shards calls should be serialized into byte arrays.

====Notify Remote Shard====

<pre>
def NotifyRemoteShard(shardId, contract, fee, method, args):
"""
:param shardId: integer, target shard Id
:param contract: address, target contract
:param fee: integer, use gas
:param method: string, method name
:param args: byte array of serialize args
:return:
"""
</pre>

Use to cross-shards asynchronous calls, caller can not get the result of callee, nor know when the callee is executed on the target shard, nor know whether the callee is successful or failed.

====Invoke Remote Shard====

<pre>
def InvokeRemoteShard(shardId, contract, method, args):
"""
:param shardId: integer, target shard Id
:param contract: address, target contract
:param fee: integer, use gas
:param method: string, method name
:param args: byte array of serialize args
:return:
"""
</pre>

Used for cross-shards synchronous invocation, caller can fetch the result of callee. It can be considered as a delayed cross-contract call. Because the result of the call is returned by network transmission, the result of callee should be byte array after serialization. In order to ensure the atomization of synchronous invocation, the contract involved in the whole cross-shards invocation process will be locked until the invocation is completed. The locked contract does not allow any form of invocation, including non-cross-shards invocation, which means that the contract involved will not be accessible throughout the cross-shards synchronous invocation period. This kind of lock is called smart contract lock. The existence of smart contract lock protects the atomicity of synchronous across shards invocation, but reduces the execution efficiency of smart contract. At the same time, in order not to affect and protect the contracts already existing on the ontology main chain, the ontology main chain is not allowed to participate in cross-shards synchronous calls, whether as a caller or a callee.

====Handling Fee for X-Shard Call====

In the case of asynchronous invocation, the x-shard call fee has deducted from account of original payer at callee shard. This means that the original payer must hold a certain amount of ONG on the target shard. At the same time, the original payer can control the maximum number of gas used in this cross-shard asynchronous call through the <code>NotifyRemoteShard</code> API <code>fee</code> parameter. On the other hand, gasPrice for this asynchronous call is determined by the target shard. Therefore, the maximum handling fee paid by the original payer for this cross-shard asynchronous call is <code>fee</code> parameter multiplied by gasPrice of the target shard. When the call ends, the fee is deducted.

In the case of synchronous invocation, the invocation fee is deducted from account of original payer at the current shard. It should be noted that when send a transaction with a cross-shard call, the gasPrice of the transaction must be greater than or equal to the gasPrice at all the invoked shards, otherwise the corresponding cross-shard call will fail.

===Implementation===
====Example implementations are available at====

OEP-11 Python Template: [[https://github.com/qiluge/ontology-xshard-contract/tree/master/xshardcall | Python Template]]
Loading