diff --git a/docs-sphinx/index.rst b/docs-sphinx/index.rst index 19d912b311..2da8da1ca1 100644 --- a/docs-sphinx/index.rst +++ b/docs-sphinx/index.rst @@ -55,7 +55,7 @@ Welcome to AElf's official documentation! Smart Contract API Acs Introduction Command-line Interface - + Smart Contract .. toctree:: :caption: Resources diff --git a/docs-sphinx/reference/smart-contract/genesis-contract.md b/docs-sphinx/reference/smart-contract/genesis-contract.md new file mode 100644 index 0000000000..16651bde2c --- /dev/null +++ b/docs-sphinx/reference/smart-contract/genesis-contract.md @@ -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: + +- 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 + +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 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 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 + +![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). \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/images/contract-execution.png b/docs-sphinx/reference/smart-contract/images/contract-execution.png new file mode 100644 index 0000000000..5369c99fe7 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/contract-execution.png differ diff --git a/docs-sphinx/reference/smart-contract/images/cross-chain-class-diagram.png b/docs-sphinx/reference/smart-contract/images/cross-chain-class-diagram.png new file mode 100644 index 0000000000..1f6fdd2847 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/cross-chain-class-diagram.png differ diff --git a/docs-sphinx/reference/smart-contract/images/cross-chain-token-transfer.png b/docs-sphinx/reference/smart-contract/images/cross-chain-token-transfer.png new file mode 100644 index 0000000000..ee0e45f1b2 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/cross-chain-token-transfer.png differ diff --git a/docs-sphinx/reference/smart-contract/images/deploy-contract-with-audit.png b/docs-sphinx/reference/smart-contract/images/deploy-contract-with-audit.png new file mode 100644 index 0000000000..1092aef66c Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/deploy-contract-with-audit.png differ diff --git a/docs-sphinx/reference/smart-contract/images/deploy-contract-without-audit.png b/docs-sphinx/reference/smart-contract/images/deploy-contract-without-audit.png new file mode 100644 index 0000000000..084762681f Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/deploy-contract-without-audit.png differ diff --git a/docs-sphinx/reference/smart-contract/images/get-consensus-behaiviour.png b/docs-sphinx/reference/smart-contract/images/get-consensus-behaiviour.png new file mode 100644 index 0000000000..88276020c1 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/get-consensus-behaiviour.png differ diff --git a/docs-sphinx/reference/smart-contract/images/initialize-side-chain.png b/docs-sphinx/reference/smart-contract/images/initialize-side-chain.png new file mode 100644 index 0000000000..d1f7b5ed94 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/initialize-side-chain.png differ diff --git a/docs-sphinx/reference/smart-contract/images/lib-calculation.png b/docs-sphinx/reference/smart-contract/images/lib-calculation.png new file mode 100644 index 0000000000..8e03c7c4bb Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/lib-calculation.png differ diff --git a/docs-sphinx/reference/smart-contract/images/verify-parent-chain-tx.png b/docs-sphinx/reference/smart-contract/images/verify-parent-chain-tx.png new file mode 100644 index 0000000000..274ebbcb05 Binary files /dev/null and b/docs-sphinx/reference/smart-contract/images/verify-parent-chain-tx.png differ diff --git a/docs-sphinx/reference/smart-contract/index.rst b/docs-sphinx/reference/smart-contract/index.rst new file mode 100644 index 0000000000..fd4363cee8 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/index.rst @@ -0,0 +1,6 @@ +Smart Contract +============== + +.. toctree:: + + Genesis Contract \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/plantuml/contract-execution.puml b/docs-sphinx/reference/smart-contract/plantuml/contract-execution.puml new file mode 100644 index 0000000000..905a37a5d7 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/contract-execution.puml @@ -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 \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/plantuml/cross-chain-token-transfer.puml b/docs-sphinx/reference/smart-contract/plantuml/cross-chain-token-transfer.puml new file mode 100644 index 0000000000..4586ba42e3 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/cross-chain-token-transfer.puml @@ -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 \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-with-audit.puml b/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-with-audit.puml new file mode 100644 index 0000000000..8ea27e8931 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-with-audit.puml @@ -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 \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-without-audit.puml b/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-without-audit.puml new file mode 100644 index 0000000000..edc7808497 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/deploy-contract-without-audit.puml @@ -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 \ No newline at end of file diff --git a/docs-sphinx/reference/smart-contract/plantuml/get-consensus-behaiviour.puml b/docs-sphinx/reference/smart-contract/plantuml/get-consensus-behaiviour.puml new file mode 100644 index 0000000000..3547443fc6 --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/get-consensus-behaiviour.puml @@ -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 diff --git a/docs-sphinx/reference/smart-contract/plantuml/initialize-side-chain.puml b/docs-sphinx/reference/smart-contract/plantuml/initialize-side-chain.puml new file mode 100644 index 0000000000..b7919daffa --- /dev/null +++ b/docs-sphinx/reference/smart-contract/plantuml/initialize-side-chain.puml @@ -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 \ No newline at end of file