Creation of a Side Chain
Side chains can be created in the aelf ecosystem to enable scalability. This section introduces the process in detail.
Side Chain Creation API#
Anyone can request the creation of a side chain in the aelf ecosystem. The proposer/creator of a new side chain needs to request the creation through the cross-chain contract on the main chain. The request contains fields that determine the type of side chain to be created.
API for Proposing Side Chain Creation#
The fields in the SideChainCreationRequest determine the type of side chain that is created. For more details, follow RequestSideChainCreation in the Crosschain contract documentation.
Upon creating a new proposal for the side chain, the ProposalCreated event containing the proposal ID will be fired. A parliament organization, specified since the chain's launch, will approve this proposal within 24 hours (refer to the Parliament contract documentation for details). The proposer can release the side chain creation request with the proposal ID once it can be released. Refer to ReleaseSideChainCreation in the Crosschain contract documentation.
Once the side chain is created, the SideChainCreatedEvent containing the chain ID will be fired.
The side chain node can be launched once it is created on the main chain. Ensure the side chain ID from the creation result is configured correctly before launching the side chain node. Make sure the cross-chain communication context is correctly set, as the side chain node will request main chain node for chain initialization data. For more details, check the side chain node running tutorial.
Side Chain Types#
Two types of side chains currently exist: exclusive and shared. An exclusive side chain allows developers to choose the transaction fee model and set the transaction fee price. Only the creator of an exclusive side chain can propose deploying a new contract.
Paying for Side Chain#
Indexing Fee#
The indexing fee is paid for side chain indexing. You can specify the indexing fee price and prepayments amount when requesting side chain creation. The cross-chain contract charges prepayments once the side chain is created and pays the miner who indexes the side chain block every time.
Resource Fee#
Developers of an exclusive side chain pay producers for running it by paying CPU, RAM, DISK, and NET resource tokens. This model is called charge-by-time. The amount the side chain creator must share with the producers is set after the chain's creation. The exclusive side chain is priced according to the time used. The unit price of the fee is determined through negotiation between the production node and the developer.
Simple Demo for Side Chain Creation Request#
When a user (usually a developer) feels the need to create a new side chain on aelf, they must call the cross-chain contract and request a side chain creation. After the request, parliament organization members will either approve or reject the creation. If the request is approved, the developer must then release the proposal.
Step-by-Step Code Snippets#
We'll use the aelf-js-sdk to create a new side chain. The full script will be provided at the end.
This creation of a side chain (logical, on-chain creation) is done in four steps:
Set-Up#
To test the creation process, you will need a producer node running and the following:
Here is the initialization code:
1const AElf = require('aelf-sdk');2const Wallet = AElf.wallet;34const { sha256 } = AElf.utils;56// set the private key of the block producer.7// REPLACE8const defaultPrivateKey = 'e119487fea0658badc42f089fbaa56de23d8c0e8d999c5f76ac12ad8ae897d76';9const defaultPrivateKeyAddress = 'HEtBQStfqu53cHVC3PxJU6iGP3RGxiNUfQGvAPTjfrF3ZWH3U';1011// load the wallet associated with your block producer's account.12const wallet = Wallet.getWalletByPrivateKey(defaultPrivateKey);1314// API link to the node15// REPLACE16const aelf = new AElf(new AElf.providers.HttpProvider('http://127.0.0.1:1234'));1718// names of the contracts that will be used.19const tokenContractName = 'AElf.ContractNames.Token';20const parliamentContractName = 'AElf.ContractNames.Parliament';21const crossChainContractName = 'AElf.ContractNames.CrossChain';2223...2425const createSideChain = async () => {26// check the chain status to make sure the node is running27const chainStatus = await aelf.chain.getChainStatus({sync: true});28const genesisContract = await aelf.chain.contractAt(chainStatus.GenesisContractAddress, wallet)29.catch((err) => {30console.log(err);31});3233// get the addresses of the contracts that we'll need to call34const tokenContractAddress = await genesisContract.GetContractAddressByName.call(sha256(tokenContractName));35const parliamentContractAddress = await genesisContract.GetContractAddressByName.call(sha256(parliamentContractName));36const crossChainContractAddress = await genesisContract.GetContractAddressByName.call(sha256(crossChainContractName));3738// build the aelf-sdk contract instance objects39const parliamentContract = await aelf.chain.contractAt(parliamentContractAddress, wallet);40const tokenContract = await aelf.chain.contractAt(tokenContractAddress, wallet);41const crossChainContract = await aelf.chain.contractAt(crossChainContractAddress, wallet);4243...44}
When running the script, the createSideChain function will be executed and will run through the full process of creating the side chain.
Creation of the Side Chain#
Set the Allowance
First, the developer must approve some ELF tokens for use by the cross-chain contract.
1var setAllowance = async function (tokenContract, crossChainContractAddress) {2// set some allowance to the cross-chain contract3const approvalResult = await tokenContract.Approve({4symbol: "ELF",5spender: crossChainContractAddress,6amount: 20000,7});89let approveTransactionResult = await pollMining(approvalResult.TransactionId);10};
Creation Request
To request a side chain creation, the developer must call RequestSideChainCreation on the cross-chain contract. This creates a proposal with the Parliament contract. After calling this method, a ProposalCreated log will be created containing the ProposalId.
1rpc RequestSideChainCreation(SideChainCreationRequest) returns (google.protobuf.Empty){}23message SideChainCreationRequest {4int64 indexing_price = 1; // The cross chain indexing price.5int64 locked_token_amount = 2; // Initial locked balance for a new side chain.6bool is_privilege_preserved = 3; // Creator privilege boolean flag.7SideChainTokenCreationRequest side_chain_token_creation_request = 4; // Side chain token information.8repeated SideChainTokenInitialIssue side_chain_token_initial_issue_list = 5; // A list of accounts and amounts that will be issued when the chain starts.9map<string, int32> initial_resource_amount = 6; // The initial rent resources.10}1112message SideChainTokenCreationRequest{13string side_chain_token_symbol = 1; // Token symbol of the side chain to be created.14string side_chain_token_name = 2; // Token name of the side chain to be created.15int64 side_chain_token_total_supply = 3; // Token total supply of the side chain to be created.16int32 side_chain_token_decimals = 4; // Token decimals of the side chain to be created.17}1819message SideChainTokenInitialIssue{20aelf.Address address = 1; // The account that will be issued.21int64 amount = 2; // The amount that will be issued.22}
In order for the creation request to succeed, some assertions must pass:
1var sideChainCreationRequest = async function (crossChainContract) {2// call the cross-chain contract to request the creation3const creationRequestResult =4await crossChainContract.RequestSideChainCreation({5indexing_price: 1,6locked_token_amount: 20000,7is_privilege_preserved: true,8side_chain_token_creation_request: {9side_chain_token_symbol: "MEGA",10side_chain_token_name: "MEGA",11side_chain_token_total_supply: 100000000,12side_chain_token_decimals: 8,13},14side_chain_token_initial_issue_list: [15{16address: defaultPrivateKeyAddress,17amount: 10000000,18},19],20initial_resource_amount: {21CPU: 100,22RAM: 100,23DISK: 100,24NET: 100,25},26});2728let sideChainProposalResult = await pollMining(29creationRequestResult.TransactionId30);31let logs = parseLogs(sideChainProposalResult.Logs);32let proposalId = logs.ProposalId;33};
The cross-chain contract emits an event containing the ProposalId. This is needed for the last step.
Approve the Proposal
This is where the parliament organization members approve the proposal:
1var proposalApproveTx = await parliamentContract.Approve(deserializedLogs[0].proposalId);23await pollMining(proposalApproveTx.TransactionId);
Note: when calling Approve it will be the Sender of the transaction that approves. Here the script is set to use the key of one parliament organization member, see full script at the end.
Release the Proposal
This part of the script releases the proposal:
1var releaseResult = await crossChainContract.ReleaseSideChainCreation({2proposalId: deserializedLogs[0].proposalId3});45let releaseTxResult = await pollMining(releaseResult.TransactionId);67// Parse the logs to get the chain id.8let sideChainCreationEvent = crossChainContract.deserializeLog(releaseTxResult.Logs, 'SideChainCreatedEvent');
This is the last step involved in creating a side chain, after this the chain id of the new side chain is accessible in the SideChainCreatedEvent event log.
Complete Script#
This script demonstrates the essential steps to create a side chain in the aelf ecosystem. The developer must approve some ELF tokens, request the side chain creation, get approval from the parliament organization, and finally release the proposal to create the side chain. Ensure to set the proper configurations and values as per your blockchain environment.
1const AElf = require("aelf-sdk");2const Wallet = AElf.wallet;34const { sha256 } = AElf.utils;56// set the private key of the block producer7const defaultPrivateKey =8"e119487fea0658badc42f089fbaa56de23d8c0e8d999c5f76ac12ad8ae897d76";9const defaultPrivateKeyAddress =10"HEtBQStfqu53cHVC3PxJU6iGP3RGxiNUfQGvAPTjfrF3ZWH3U";1112const wallet = Wallet.getWalletByPrivateKey(defaultPrivateKey);1314// link to the node15const aelf = new AElf(new AElf.providers.HttpProvider("http://127.0.0.1:8000"));1617if (!aelf.isConnected()) {18console.log("Could not connect to the node.");19}2021const tokenContractName = "AElf.ContractNames.Token";22const parliamentContractName = "AElf.ContractNames.Parliament";23const crossChainContractName = "AElf.ContractNames.CrossChain";2425var pollMining = async function (transactionId) {26console.log(`>> Waiting for ${transactionId} the transaction to be mined.`);2728for (i = 0; i < 10; i++) {29const currentResult = await aelf.chain.getTxResult(transactionId);30// console.log('transaction status: ' + currentResult.Status);3132if (currentResult.Status === "MINED") return currentResult;3334await new Promise((resolve) => setTimeout(resolve, 2000)).catch(35function () {36console.log("Promise Rejected");37}38);39}40};4142var setAllowance = async function (tokenContract, crossChainContractAddress) {43console.log("\n>>>> Setting allowance for the cross-chain contract.");4445// set some allowance to the cross-chain contract46const approvalResult = await tokenContract.Approve({47symbol: "ELF",48spender: crossChainContractAddress,49amount: 20000,50});5152await pollMining(approvalResult.TransactionId);53};5455var checkAllowance = async function (tokenContract, owner, spender) {56console.log("\n>>>> Checking the cross-chain contract's allowance");5758const checkAllowanceTx = await tokenContract.GetAllowance.call({59symbol: "ELF",60owner: owner,61spender: spender,62});6364console.log(65`>> allowance to the cross-chain contract: ${checkAllowanceTx.allowance} ${checkAllowanceTx.symbol}`66);67};6869const createSideChain = async () => {70// get the status of the chain in order to get the genesis contract address71console.log("Starting side chain creation script\n");7273const chainStatus = await aelf.chain.getChainStatus({ sync: true });74const genesisContract = await aelf.chain75.contractAt(chainStatus.GenesisContractAddress, wallet)76.catch((err) => {77console.log(err);78});7980// get the addresses of the contracts that we'll need to call81const tokenContractAddress =82await genesisContract.GetContractAddressByName.call(83sha256(tokenContractName)84);85const parliamentContractAddress =86await genesisContract.GetContractAddressByName.call(87sha256(parliamentContractName)88);89const crossChainContractAddress =90await genesisContract.GetContractAddressByName.call(91sha256(crossChainContractName)92);9394// build the aelf-sdk contract object95const parliamentContract = await aelf.chain.contractAt(96parliamentContractAddress,97wallet98);99const tokenContract = await aelf.chain.contractAt(100tokenContractAddress,101wallet102);103const crossChainContract = await aelf.chain.contractAt(104crossChainContractAddress,105wallet106);107108// 1. set and check the allowance, spender is the cross-chain contract109await setAllowance(tokenContract, crossChainContractAddress);110await checkAllowance(111tokenContract,112defaultPrivateKeyAddress,113crossChainContractAddress114);115116// 2. request the creation of the side chain with the cross=chain contract117console.log("\n>>>> Requesting the side chain creation.");118const sideChainCreationRequestTx =119await crossChainContract.RequestSideChainCreation({120indexingPrice: 1,121lockedTokenAmount: "20000",122isPrivilegePreserved: true,123sideChainTokenCreationRequest: {124sideChainTokenDecimals: 8,125sideChainTokenName: "SCATokenName",126sideChainTokenSymbol: "SCA",127sideChainTokenTotalSupply: "100000000000000000",128},129sideChainTokenInitialIssueList: [130{131address: "28Y8JA1i2cN6oHvdv7EraXJr9a1gY6D1PpJXw9QtRMRwKcBQMK",132amount: "1000000000000000",133},134],135initialResourceAmount: { CPU: 2, RAM: 4, DISK: 512, NET: 1024 },136});137138let sideChainCreationRequestTxResult = await pollMining(139sideChainCreationRequestTx.TransactionId140);141142// deserialize the log to get the proposal's ID.143let deserializedLogs = parliamentContract.deserializeLog(144sideChainCreationRequestTxResult.Logs,145"ProposalCreated"146);147console.log(148`>> side chain creation request proposal id ${JSON.stringify(149deserializedLogs[0].proposalId150)}`151);152153// 3. Approve the proposal154console.log("\n>>>> Approving the proposal.");155156var proposalApproveTx = await parliamentContract.Approve(157deserializedLogs[0].proposalId158);159await pollMining(proposalApproveTx.TransactionId);160161// 3. Release the side chain162console.log("\n>>>> Release the side chain.");163164var releaseResult = await crossChainContract.ReleaseSideChainCreation({165proposalId: deserializedLogs[0].proposalId,166});167168let releaseTxResult = await pollMining(releaseResult.TransactionId);169170// Parse the logs to get the chain id.171let sideChainCreationEvent = crossChainContract.deserializeLog(172releaseTxResult.Logs,173"SideChainCreatedEvent"174);175console.log("Chain chain created : ");176console.log(sideChainCreationEvent);177};178179createSideChain().then(() => {180console.log("Done.");181});
Note: Replace the placeholders in the script with actual values and logic for your use case.
Next, we can move on to Running a Side Chain.
Edited on: 17 July 2024 03:24:14 GMT+0