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

Document of the Genesis Contract #3517

Open
wants to merge 2 commits into
base: docs
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
2 changes: 1 addition & 1 deletion docs-sphinx/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Welcome to AElf's official documentation!
Smart Contract API <reference/smart-contract-api/index>
Acs Introduction <reference/acs/index>
Command-line Interface <reference/cli/index>

Smart Contract <reference/smart-contract/index>

.. toctree::
:caption: Resources
Expand Down
232 changes: 232 additions & 0 deletions docs-sphinx/reference/smart-contract/genesis-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# Genesis Contract

## Overview

Genesis Contract, also known as the Zero Contract, is mainly used to deploy and maintain smart contracts running on the aelf blockchain.

This contract will be deployed first when aelf blockchain launched so that it can be used to deploy other smart contracts.

To achieve this purpose, the Genesis Contract implements the following methods defined in [acs0](https://docs.aelf.io/en/latest/reference/acs/acs0.html):

```protobuf
service ACS0 {
// Deploy a system smart contract on chain and return the address of the system contract deployed.
rpc DeploySystemSmartContract (SystemContractDeploymentInput) returns (aelf.Address) {
}

// Deploy a smart contract on chain and return the address of the contract deployed.
rpc DeploySmartContract (ContractDeploymentInput) returns (aelf.Address) {
}

// Update a smart contract on chain.
rpc UpdateSmartContract (ContractUpdateInput) returns (aelf.Address) {
}

// and others.
// ...
}
```

Therefore, developers can deploy (and update) their own smart contracts by interacting with the Genesis Contract.

In this article, we will discuss:

zhifenglee-aelf marked this conversation as resolved.
Show resolved Hide resolved
- States related to smart contracts
- Implementation of deploy and update contracts
- How the contract will be loaded to the smart contract execution environment
- The current process of deploying and updating aelf smart contracts
- Other details of the Genesis Contract

## States related to smart contracts

zhifenglee-aelf marked this conversation as resolved.
Show resolved Hide resolved
On aelf, each contract has the ability to read and write the general ledgers through a State
defined by a protobuf message.

The Genesis Contract uses `SmartContractRegistration` and `ContractInfo` states
to store contract information in the general ledger of the aelf blockchain.

The data structure of `SmartContractRegistration` is defined in `aelf/core.proto`:

```C#
message SmartContractRegistration {
// The category of contract code(0: C#).
sint32 category = 1;
// The byte array of the contract code.
bytes code = 2;
// The hash of the contract code.
Hash code_hash = 3;
// Whether it is a system contract.
bool is_system_contract = 4;
// The version of the current contract.
int32 version = 5;
// The version of the contract.
string contract_version = 6;
// The address of the current contract.
Address contract_address = 7;
// Indicates if the contract is the user contract.
bool is_user_contract = 8;
}
```
Smart Contract code is stored in the `code` field.

We use the `State.SmartContractRegistrations` to access stored `SmartContractRegistration` entities, the key is the `code_hash` of one contract.

```C#
public MappedState<Hash, SmartContractRegistration> SmartContractRegistrations { get; set; }
```

The `SmartContractRegistration` entity can be fetched by the hash value of the contract code.
It is only written once when deploying the contract.

The data structure that corresponds one-to-one with contracts is called `ContractInfo`.
Structure `ContractInfo` is defined in [acs0](https://docs.aelf.io/en/latest/reference/acs/acs0.html).

```C#
message ContractInfo
{
// The serial number of the contract.
int64 serial_number = 1;
// The author of the contract is the person who deployed the contract.
aelf.Address author = 2;
// The category of contract code(0: C#).
sint32 category = 3;
// The hash of the contract code.
aelf.Hash code_hash = 4;
// Whether it is a system contract.
bool is_system_contract = 5;
// The version of the current contract.
int32 version = 6;
string contract_version = 7;
// Indicates if the contract is the user contract.
bool is_user_contract = 8;
}
```

We use the MappedState to store related instances.

```C#
public MappedState<Address, ContractInfo> ContractInfos { get; set; }
```

From the `code_hash` field of `ContractInfo`, it is not difficult to guess:

1. When trying to retrieve the contract code, the `code_hash` of ContractInfo is first read, and then the contract code itself is read from the `State.SmartContractRegistrations` mapped state.
2. Upgrading a contract on aelf is replacing the `code_hash` of `ContractInfo`.

## Deploy and update contracts

To deploy a smart contract to aelf, developers need to interact with the `DeploySmartContract` or `DeployUserSmartContract` defined by [acs0](https://docs.aelf.io/en/latest/reference/acs/acs0.html) and implemented by the Genesis Contract.
The differences between these two methods will be explained later.

When executing the deployment method, the contract code will be stored in the StateDb through the structure we mentioned before: `SmartContractRegistration`.
More specifically, it is the `code` field.

If developer's smart contract is written by C#, the `category` should be `0`.
The execution environment will select which runtime to load the contract code into based on the `category` field.

And the `code_hash` is a unique identifier for the contract code.
For C# smart contract, the code hash is calculated by the Genesis Contract during deployment.

After the contract code is saved in StateDb, another field is used to store the relevant information of the contract.
The structure is also mentioned before: `ContractInfo`.
There is a `code_hash` field defined in this structure, make it possible to use `GetContractInfo` method to get the contract information of provided contract address,
then use `GetSmartContractRegistrationByCodeHash` method to get contract code via contract code hash.
In addition, the contract code can also be obtained through method `GetSmartContractRegistrationByAddress`.

As for updating the contract code, the contract information (`ContractInfo`) can be directly modified through method `UpdateSmartContract`,
then aelf smart contract execution environment can obtain the new contract code via new contract hash from the Genesis Contract in the future.

## Execution of contract code

Although the execution process of the contract is unrelated to the Genesis Contract.
Developers may be concerned about how their contract code will be consumed in the future after deployment.
Therefore, here are some brief explanations.

![Contract Execution](images/contract-execution.png)

As shown in the above figure, assuming that the contract code has been stored in the Genesis Contract.
When a caller tries to call a method of the contract, within the aelf node, the corresponding `SmartContractRegistration` will be obtained from the Genesis Contract, the contract code will be extracted, encapsulated as an Executive type, for the contract execution environment to call.
After completing the call, return the transaction result to the caller.

Upgrading the contract will change the `SmartContractRegistration` obtained during the above process, so it is feasible to upgrade the deployed contract in aelf.

## Calculation of contract address

The contract address is calculated through a field that increases with the number of contract deployments.

```C#
public Int64State ContractSerialNumber { get; set; }
```

Its calculation process is located in the `DeploySmartContract` method:

```C#
var contractAddress = AddressHelper.BuildContractAddress(Context.ChainId, serialNumber);
```

- The contract address of each chain of aelf is different.
- The contract address is not related to the contract code, but only to the order in which it is deployed on this chain.
- Therefore, when testing newly written contracts in `aelf-boilerplate` or `aelf-developer-tools`, the new contract always has a fixed address.

After the 1.6.0 version, Salt is added to the imported parameter of the deployment/upgrade contract. The contract address is calculated by using the Deployer address of the deployment account and the hash value Salt.

```C#
var contractAddress = AddressHelper.ComputeContractAddress(deployer, salt);
```

- Deploying contracts with the same account and using the same Salt can make the contract address of each chain of aelf the same.

## Contract deployment and update process

Deploy contract with audit that
you must wait for the Parliament members review and manually approve your contract
before it can enter the actual deployment process.

Deploy contract without audit can directly deploy your contract,
but the cost is that your contract must inherit from acs12,
which is a standard for pre-defining how contract fees should be charged when executing your contract methods.

### Deploy contract with audit
zhifenglee-aelf marked this conversation as resolved.
Show resolved Hide resolved

![Deploy Contract With Audit](images/deploy-contract-with-audit.png)

The current pipeline starts with Propose, which generates a parliamentary proposal.
When more than 2/3 of the BPs agree to deploy/update, a new proposal is released to request code inspection.
Finally, after the code audit has passed, the real contract deployment/upgrade will be achieved through the proposal of releasing the code inspection.

### Deploy contract without audit

![Deploy Contract Without Audit](images/deploy-contract-without-audit.png)

Developers send deployment/update user contract transactions, generate a parliamentary CodeCheck proposal, and when more than 2/3 of the BPs conduct code checks and pass, achieve real contract deployment/upgrade through the proposal of automatically releasing code checks.

### Contract deployment and upgrade new version number

When upgrading a contract, check the contract version information
- If the contract version is less than or equal to the original contract version, the upgrade contract transaction fails
The old version of the contract only has a version number after being upgraded.
- If the version number is increasing, the upgrade contract transaction is successful.

In the updateSmartContract method, increase the version number judgment:

```C#
var contractInfo = Context.UpdateSmartContract(contractAddress, reg, null, info.ContractVersion);
Assert(contractInfo.IsSubsequentVersion,
$"The version to be deployed is lower than the effective version({info.ContractVersion}), please correct the version number.");
```

## Contract error message

`DeployUserSmartContract` method:
- No permission. Trying to deploy a smart contract to an aelf private sidechain, and the transaction sender is not in the allowlist.
- contract code has already been deployed before. Contract code deployed.
- Already proposed. Duplicate deployment request.

`UpdateUserSmartContract` method:
- No permission. The transaction sender is not the contract author.
- Code is not changed. The contract code has not changed since deployment or the previous update.
- The version to be deployed is lower than the effective version({currentVersion}), please correct the version number. The updated contract version number is too low.
- Already proposed. Duplicate deployment request.

## Usage
Check the `deploy` command of [aelf-command](https://docs.aelf.io/en/latest/reference/cli/methods.html#deploy-deploy-a-smart-contract).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docs-sphinx/reference/smart-contract/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Smart Contract
==============

.. toctree::

Genesis Contract <genesis-contract>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@startuml
autonumber

Caller -> TransactionExecutionService: Transaction
TransactionExecutionService -> SmartContractExecutiveService: Call Execute method
SmartContractExecutiveService -> GenesisContract: Call GetSmartContractRegistrationByAddress method
GenesisContract --> SmartContractExecutiveService: SmartContractRegistration instance
SmartContractExecutiveService --> TransactionExecutionService: Executive instance
TransactionExecutionService --> Caller: Transaction Result

@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@startuml

autonumber

participant "User" as U
participant "Chain A MultiToken Contract" as A
participant "Chain B MultiToken Contract" as B

U -> A: CrossChainTransfer
A --> U:
U -> B: CrossChainReceiveToken

@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@startuml

autonumber

Developer -> GenesisContract: ProposeNewContract / ProposeUpdateContract
GenesisContract -> ParliamentContract: CreateProposalBySystemContract
ParliamentContract -> ParliamentContract: CreateNewProposal \n(GenesisContract.ProposeContractCodeCheck)
ParliamentContract -> BlockProducer: ProposalCreated event
BlockProducer -> ParliamentContract: Approve >= 2/3 \n(ContractZero.ProposeContractCodeCheck)
ParliamentContract -> Developer: ReceiptCreated event
Developer -> GenesisContract: ReleaseApprovedContract
GenesisContract -> ParliamentContract: Release
ParliamentContract -> ParliamentContract: CreateProposalBySystemContract
ParliamentContract -> ParliamentContract: CreateNewProposal \n(ContractZero.DeploySmartContract \n/ContractZero.UpdateSmartContract)
ParliamentContract -> BlockProducer: ProposalCreated event
BlockProducer -> ParliamentContract: Approve \n(Automatically by aelf node)
ParliamentContract -> Developer: ReceiptCreated event
Developer -> GenesisContract: ReleaseCodeCheckedContract
GenesisContract -> ParliamentContract: Release
ParliamentContract -> GenesisContract: DeploySmartContract \n/UpdateSmartContract


@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@startuml

autonumber

Developer -> GenesisContract: DeployUserSmartContract \n/UpdateUserSmartContract
GenesisContract -> ParliamentContract: CreateProposalBySystemContract
ParliamentContract -> ParliamentContract: CreateNewProposal \n(ContractZero.PerformDeployUserSmartContract \n/ContractZero.PerformUpdateUserSmartContract)
ParliamentContract -> BlockProducer: ProposalCreated event
BlockProducer -> ParliamentContract: Approve \n(Automatically by aelf node)

BlockProducer -> GenesisContract: Approve>=2/3 \nReleaseApprovedUserSamrtContract \n(Automatically by aelf node)
GenesisContract -> ParliamentContract: Release
ParliamentContract -> GenesisContract: PerformDeployUserSmartContract \n/PerformUpdateUserSmartContract


@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@startuml
start
:GetConsensusCommand;
if (MinerList contains provided pubkey?) then (false)
:Nothing;
stop
else (true)
if (Timeslot for this BP already passed?) then (false)
if (Has this BP produced enough tiny blocks?) then (false)
:TinyBlock;
stop
else (true)
:NextRound;
stop
endif
else (true)
:NextRound;
stop
endif

@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@startuml

autonumber

participant "SideChain CrossChain Contract" as SC
participant "SideChain" as S
participant "MainChain" as M
participant "MainChain CrossChain Contract" as MC

S -> S: RequestChainInitializationDataAsync
S -> M: RequestChainInitializationDataFromParentChain
M -> MC: GetChainInitializationData
MC -> S: GetInitializeMethodList
S -> SC: Initialize

@enduml
Loading