├── .gitbook.yaml ├── .github └── workflows │ ├── cli-tests.yml │ ├── compile.yml │ └── scripts │ ├── compile.sh │ ├── get-latest-header.sh │ └── insert-keys.sh ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── assembly ├── frame │ ├── executive.ts │ ├── index.ts │ ├── models │ │ └── storage-entry.ts │ └── system.ts ├── generated │ └── .gitignore ├── index.ts ├── pallets │ ├── aura │ │ └── aura.ts │ ├── balances │ │ └── balances.ts │ ├── grandpa │ │ └── grandpa.ts │ ├── index.ts │ ├── timestamp │ │ └── timestamp.ts │ └── transaction-payment │ │ ├── payment.ts │ │ └── transaction-payment.ts ├── runtime │ ├── api │ │ ├── aura-api.ts │ │ ├── block-builder.ts │ │ ├── core.ts │ │ ├── grandpa-api.ts │ │ ├── others.ts │ │ └── parachain-host.ts │ ├── index.ts │ └── runtime.ts └── tsconfig.json ├── cli ├── package-lock.json ├── package.json ├── src │ ├── commands │ │ ├── compile │ │ │ ├── README.md │ │ │ ├── codegen │ │ │ │ └── index.js │ │ │ ├── compile.ts │ │ │ ├── dispatcher │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── metadata │ │ │ │ ├── index.js │ │ │ │ ├── metadata-json.js │ │ │ │ ├── metadata-ts.js │ │ │ │ ├── module-metadata.js │ │ │ │ └── types.json │ │ ├── index.ts │ │ ├── init │ │ │ └── init.ts │ │ └── spec │ │ │ ├── build-spec.ts │ │ │ └── builder │ │ │ ├── assembly │ │ │ ├── index.ts │ │ │ └── tsconfig.json │ │ │ ├── customSpec-template.json │ │ │ ├── index.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── genesis-builder.js │ │ │ ├── modules │ │ │ │ ├── aura.js │ │ │ │ ├── balances.js │ │ │ │ ├── grandpa.js │ │ │ │ └── system.js │ │ │ ├── utils.js │ │ │ └── wasm-loader.js │ │ │ └── test │ │ │ ├── build-spec.js │ │ │ ├── json-files │ │ │ ├── customSpec-code.json │ │ │ ├── customSpec-noAura.json │ │ │ ├── customSpec-noBalances.json │ │ │ ├── customSpec-noCode.json │ │ │ ├── customSpec-noGenesis.json │ │ │ ├── customSpec-noRuntime.json │ │ │ ├── customSpec.json │ │ │ └── customspec-noGrandpa.json │ │ │ └── utils │ │ │ ├── mocked-constants.json │ │ │ ├── utils.js │ │ │ └── wasm-exmpl │ ├── constants.ts │ ├── index.ts │ └── utils.ts ├── test │ ├── compile.js │ ├── init.js │ ├── spec.js │ └── utils │ │ ├── constants.js │ │ ├── subsembly.js │ │ └── wasm-exmpl └── tsconfig.json ├── docs ├── .gitbook │ └── assets │ │ ├── components_diagram.png │ │ ├── image (1).png │ │ ├── image (2).png │ │ ├── image (3).png │ │ ├── image (4).png │ │ ├── image (5).png │ │ ├── image (6).png │ │ ├── image (7).png │ │ ├── image.png │ │ ├── logo.svg │ │ ├── screenshot-2021-03-23-at-15.36.51.png │ │ ├── screenshot-2021-03-23-at-16.58.28.png │ │ ├── screenshot-2021-03-23-at-17.32.55.png │ │ ├── screenshot-2021-03-23-at-17.40.28.png │ │ ├── screenshot-2021-03-31-at-16.27.13.png │ │ ├── screenshot-2021-03-31-at-18.00.40.png │ │ ├── screenshot-2021-03-31-at-18.18.54.png │ │ ├── screenshot-2021-04-01-at-16.47.08.png │ │ ├── screenshot-2021-04-01-at-16.50.47.png │ │ ├── screenshot-2021-04-02-at-1.44.09.png │ │ ├── screenshot-2021-04-02-at-1.44.48.png │ │ ├── screenshot-2021-04-02-at-1.45.59.png │ │ ├── screenshot-2021-04-02-at-1.47.21.png │ │ ├── screenshot-2021-04-02-at-17.56.26.png │ │ ├── screenshot-2021-04-02-at-17.59.57.png │ │ ├── screenshot-2021-04-02-at-18.01.17.png │ │ ├── screenshot-2021-04-02-at-18.03.02.png │ │ └── web3_badge_black.png ├── README.md ├── SUMMARY.md ├── advanced │ ├── as-scale-codec.md │ ├── subsembly-core.md │ └── untitled │ │ ├── README.md │ │ └── dispatcher.md ├── development │ ├── chain-specification.md │ ├── development.md │ ├── runtime-compilation.md │ └── runtime-running.md ├── getting-started │ ├── cli │ │ ├── README.md │ │ ├── init.md │ │ ├── subsembly-cli.md │ │ ├── subsembly-compile.md │ │ └── subsembly-spec.md │ ├── overview.md │ └── substrate-essentials.md ├── guides │ ├── create-a-new-module │ │ ├── README.md │ │ ├── launch-the-node.md │ │ ├── nicks-module.md │ │ └── polkadotjs.md │ ├── create-your-first-subsembly-runtime │ │ ├── README.md │ │ ├── polkadotjs.md │ │ ├── set-up.md │ │ └── start-the-node.md │ └── start-your-network │ │ ├── README.md │ │ ├── generate-authority-keys.md │ │ ├── launch-a-network.md │ │ └── launch-the-third-node.md ├── index.md └── modules │ ├── core │ ├── README.md │ ├── crypto.md │ ├── executive.md │ ├── log.md │ └── untitled.md │ └── pre-built │ ├── README.md │ ├── aura.md │ ├── balances.md │ ├── timestamp.md │ └── transactionpayment.md ├── images ├── components_diagram.png ├── logo.svg └── web3_badge_black.png ├── package-lock.json ├── package.json └── yarn.lock /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | 3 | structure: 4 | readme: README.md 5 | summary: SUMMARY.md 6 | 7 | redirects: 8 | previous/page: new-folder/page.md -------------------------------------------------------------------------------- /.github/workflows/cli-tests.yml: -------------------------------------------------------------------------------- 1 | name: Subsembly CLI tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-node@v2 11 | with: 12 | node-version: 14.x 13 | - run: npm install -g yarn && yarn install 14 | - run: cd cli && npm install && npm run build && sudo npm link 15 | - run: cd cli && npm run test -------------------------------------------------------------------------------- /.github/workflows/compile.yml: -------------------------------------------------------------------------------- 1 | name: Smoke Tests 2 | 3 | on: [push] 4 | jobs: 5 | compile-run-node: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Install jq and curl 10 | run: sudo apt-get update && sudo apt-get install -y jq curl make 11 | - name: Compile runtime 12 | run: bash .github/workflows/scripts/compile.sh 13 | - name: Run the node and insert Aura keys 14 | run: | 15 | make run-node \ 16 | NAME=node01 \ 17 | PORT=30333 \ 18 | WS-PORT=9944 \ 19 | RPC-PORT=9933 \ 20 | spec=raw-chain-spec.json detached=1 \ 21 | && bash .github/workflows/scripts/insert-keys.sh \ 22 | && docker restart node01 23 | - name: Sleep for 10 seconds 24 | uses: jakejarvis/wait-action@master 25 | with: 26 | time: '10s' 27 | - name: Get latest header 28 | id: check-header 29 | run: | 30 | docker start node01 \ 31 | && echo "::set-output name=tx-output::$(bash .github/workflows/scripts/get-latest-header.sh)\n" 32 | - name: | 33 | Check the latest header 34 | If it contains number":"0x0", 35 | it means no block was produced except the genesis 36 | env: 37 | TX_RESULT: ${{steps.check-header.outputs.tx-output}} 38 | run: | 39 | if [[ $TX_RESULT == *"number":"0x0"* ]]; then \ 40 | exit 1; \ 41 | else \ 42 | exit 0; \ 43 | fi \ 44 | -------------------------------------------------------------------------------- /.github/workflows/scripts/compile.sh: -------------------------------------------------------------------------------- 1 | sudo npm install -g yarn 2 | yarn install 3 | npm run build 4 | node ./cli/dist/src/index.js spec --to=./chain-spec.json 5 | node ./cli/dist/src/index.js spec --src=./chain-spec.json 6 | sudo npm uninstall -g yarn -------------------------------------------------------------------------------- /.github/workflows/scripts/get-latest-header.sh: -------------------------------------------------------------------------------- 1 | lsof -n | grep LISTEN > /dev/null 2 | curl --location --request POST 'localhost:9933' \ 3 | --header 'Content-Type: application/json' \ 4 | --data-raw '{ 5 | "jsonrpc": "2.0", 6 | "method": "chain_getHeader", 7 | "params": [], 8 | "id": 1 9 | }' -------------------------------------------------------------------------------- /.github/workflows/scripts/insert-keys.sh: -------------------------------------------------------------------------------- 1 | lsof -n | grep LISTEN > /dev/null 2 | curl --location --request POST '0.0.0.0:9933' \ 3 | --header 'Content-Type: application/json' \ 4 | --data-raw '{ 5 | "jsonrpc": "2.0", 6 | "method": "author_insertKey", 7 | "params": ["aura","clip organ olive upper oak void inject side suit toilet stick narrow","0x9effc1668ca381c242885516ec9fa2b19c67b6684c02a8a3237b6862e5c8cd7e"], 8 | "id": 1 9 | }' 10 | lsof -n | grep LISTEN > /dev/null 11 | curl --location --request POST '0.0.0.0:9933' \ 12 | --header 'Content-Type: application/json' \ 13 | --data-raw '{ 14 | "jsonrpc": "2.0", 15 | "method": "author_insertKey", 16 | "params": ["gran","clip organ olive upper oak void inject side suit toilet stick narrow","0xb48004c6e1625282313b07d1c9950935e86894a2e4f21fb1ffee9854d180c781"], 17 | "id": 1 18 | }' 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | wasm-code 4 | # Logs 5 | *.log 6 | yarn-error.log 7 | metadata.json 8 | metadata.ts 9 | dispatcher.ts 10 | dist 11 | cli/test/generated -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: run-node 2 | 3 | # Name of our Docker image 4 | DOCKER_IMAGE=limechain/as-substrate:latest 5 | # Name of our Docker container 6 | DOCKER_CONTAINER=as-substrate-node 7 | 8 | # Run Docker container in a detached mode 9 | run-node-demo: 10 | @echo "Running the container in detached mode" 11 | ifdef detached 12 | @docker run -p 9933:9933 -p 9944:9944 -p 30333:30333 -v "$(CURDIR)/${spec}":/customSpecRaw.json -d $(DOCKER_IMAGE) 13 | else 14 | @docker run -p 9933:9933 -p 9944:9944 -p 30333:30333 -v "$(CURDIR)/${spec}":/customSpecRaw.json $(DOCKER_IMAGE) 15 | endif 16 | 17 | run-node: 18 | ifdef help 19 | @docker run -it limechain/as-substrate:node-v1 --help 20 | else ifeq ($(detached), 1) 21 | @docker run -p ${RPC-PORT}:${RPC-PORT} -p ${WS-PORT}:${WS-PORT} -p ${PORT}:${PORT} -v "$(CURDIR)/${spec}":/raw-chain-spec.json -v /tmp:/tmp --name ${NAME} -d limechain/as-substrate:grandpa --base-path /tmp/${NAME} --port ${PORT} --ws-port ${WS-PORT} --rpc-port ${RPC-PORT} --execution Wasm --offchain-worker Never --validator --name=${NAME} ${OTHER} 22 | else 23 | @docker run -p ${RPC-PORT}:${RPC-PORT} -p ${WS-PORT}:${WS-PORT} -p ${PORT}:${PORT} -v "$(CURDIR)/${spec}":/raw-chain-spec.json -v /tmp:/tmp limechain/as-substrate:grandpa --base-path /tmp/${NAME} --port ${PORT} --ws-port ${WS-PORT} --rpc-port ${RPC-PORT} --execution Wasm --offchain-worker Never --validator --name=${NAME} ${OTHER} 24 | endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 | 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![Smoke Tests](https://github.com/LimeChain/subsembly/workflows/Smoke%20Tests/badge.svg) 8 | ![Subsembly CLI Tests](https://github.com/LimeChain/subsembly/workflows/Subsembly%20CLI%20tests/badge.svg) 9 | 10 |
11 | 12 | ## Subsembly 13 | 14 | ### What is it? 15 | 16 | **`Subsembly`** is a framework used for designing and implementing Substrate runtimes from scratch. **`Subsembly`** utilises Substrate Core to build runtimes in a non-native language of Substrate. With **`Subsembly`** you can build Substrate runtimes rapidly, with a varying degrees of technical freedom. 17 | 18 | ### How does it work? 19 | 20 | **`Substrate`** runtimes can be designed in any other language than Rust, provided that the language targets **`WebAssembly`**. This is where **`AssemblyScript`** and **`Subsembly`** come into play. 21 | 22 | ### Documentation 23 | 24 | For more info refer to our documentation: https://subsembly.gitbook.io/subsembly 25 | 26 | ### Funding and Support 27 | 28 | **`Subsembly`** is funded by [**Web3 Foundation**](https://web3.foundation) **Grants** and developed, maintained by [**LimeChain**](https://limechain.tech). 29 | -------------------------------------------------------------------------------- /assembly/frame/executive.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray, Hash } from 'as-scale-codec'; 2 | import { 3 | Crypto, InherentData, 4 | Log, ResponseCodes, SignatureTypes, TransactionSource, Utils 5 | } from 'subsembly-core'; 6 | import { Dispatcher } from '../generated/dispatcher'; 7 | import { Aura, Timestamp } from '../pallets'; 8 | import { 9 | BlockNumber, BlockType, HeaderType, 10 | Inherent, RuntimeConfig, SignatureType, UncheckedExtrinsic 11 | } from '../runtime/runtime'; 12 | import { System, SystemEvents, SystemStorageEntries } from './system'; 13 | 14 | /** 15 | * @description Acts as the orchestration layer for the runtime. 16 | * It dispatches incoming extrinsic calls to the respective pallets in the runtime. 17 | */ 18 | export namespace Executive { 19 | /** 20 | * @description Calls the System function initializeBlock() 21 | * @param header Header instance 22 | */ 23 | export function initializeBlock(header: HeaderType): void { 24 | System._initialize(header); 25 | } 26 | 27 | /** 28 | * @description Performs necessary checks for Block execution 29 | * @param block Block instance 30 | */ 31 | export function initialChecks(block: BlockType): void { 32 | let header = block.getHeader(); 33 | let n: BlockNumber = header.getNumber(); 34 | // check that parentHash is valid 35 | const previousBlock: BlockNumber = instantiate(n.unwrap() - 1); 36 | const parentHash: Hash = SystemStorageEntries.BlockHash().get(previousBlock); 37 | 38 | if (n == instantiate(0) && parentHash != header.getParentHash()) { 39 | Log.error("Initial checks: Parent hash should be valid."); 40 | throw new Error("Executive: Initial checks for block execution failed"); 41 | } 42 | } 43 | 44 | /** 45 | * @description Final checks before including block in the chain 46 | * @param header 47 | */ 48 | export function finalChecks(header: HeaderType): void { 49 | System._computeExtrinsicsRoot(); 50 | let newHeader = System._finalize(); 51 | let storageRoot = newHeader.getStateRoot(); 52 | if (header.getStateRoot() != storageRoot) { 53 | Log.error("Storage root must match that calculated"); 54 | throw new Error("Executive: Final checks for block execution failed"); 55 | } 56 | } 57 | 58 | /** 59 | * @description Actually execute all transactions for Block 60 | * @param block Block instance 61 | */ 62 | export function executeBlock(block: BlockType): void { 63 | Executive.initializeBlock(block.getHeader()); 64 | Executive.initialChecks(block); 65 | 66 | Executive.executeExtrinsicsWithBookKeeping(block.getExtrinsics()); 67 | Executive.finalChecks(block.getHeader()); 68 | } 69 | /** 70 | * @description Finalize the block - it is up the caller to ensure that all header fields are valid 71 | * except state-root. 72 | */ 73 | export function finalizeBlock(): HeaderType { 74 | System._noteFinishedExtrinsics(); 75 | System._computeExtrinsicsRoot(); 76 | return System._finalize(); 77 | } 78 | /** 79 | * @description creates inherents from internal modules 80 | * @param data inherents 81 | */ 82 | export function createExtrinsics(data: InherentData): u8[] { 83 | const timestamp: Inherent = Timestamp._createInherent(data); 84 | const aura = Aura._createInherent(data); 85 | return System.ALL_MODULES.concat(timestamp.toU8a()).concat(aura); 86 | } 87 | 88 | /** 89 | * @description Apply Extrinsics 90 | * @param ext extrinsic 91 | */ 92 | export function applyExtrinsic(ext: UncheckedExtrinsic): u8[] { 93 | const encodedLen = ext.encodedLength(); 94 | const result = Executive.applyExtrinsicWithLen(ext, encodedLen); 95 | // if applying extrinsic succeeded, notify System about it 96 | if (Utils.areArraysEqual(result, ResponseCodes.SUCCESS)) { 97 | System._noteAppliedExtrinsic(ext); 98 | } 99 | else { 100 | System._depositEvent(SystemEvents.ExtrinsicFailure, []); 101 | } 102 | return result; 103 | } 104 | 105 | /** 106 | * @description Orchestrating function for applying extrinsic 107 | * @param ext extrinsic 108 | * @param encodedLen length 109 | * @param encoded encoded extrinsic 110 | */ 111 | export function applyExtrinsicWithLen(ext: UncheckedExtrinsic, encodedLen: u32): u8[] { 112 | return Dispatcher.dispatch(ext); 113 | } 114 | 115 | /** 116 | * @description Execute given extrinsics and take care of post-extrinsics book-keeping 117 | * @param extrinsics byte array of extrinsics 118 | */ 119 | export function executeExtrinsicsWithBookKeeping(extrinsics: UncheckedExtrinsic[]): void { 120 | for (let i = 0; i < extrinsics.length; i++) { 121 | Executive.applyExtrinsic(extrinsics[i]); 122 | } 123 | System._noteFinishedExtrinsics(); 124 | } 125 | 126 | /** 127 | * @description Initial transaction validation 128 | * @param source source of the transaction (external, inblock, etc.) 129 | * @param utx transaction 130 | */ 131 | export function validateTransaction(utx: UncheckedExtrinsic): u8[] { 132 | if(utx.isSigned()){ 133 | const extSignature = utx.signature; 134 | const from = extSignature.signer; 135 | const signedExt = extSignature.signedExtension; 136 | 137 | const nonce = SystemStorageEntries.Account().get(from).nonce; 138 | if (nonce && nonce.unwrap() > signedExt.nonce.unwrap()) { 139 | Log.error("Validation error: Nonce value is less than or equal to the latest nonce"); 140 | return ResponseCodes.NONCE_TOO_LOW; 141 | } 142 | 143 | const blockNumber = SystemStorageEntries.Number().get(); 144 | const blockHash = SystemStorageEntries.BlockHash().get(instantiate(blockNumber.unwrap() - 2)); 145 | const genesisHash = SystemStorageEntries.BlockHash().get(instantiate(0)); 146 | const specVersion = RuntimeConfig.runtimeVersion().specVersion; 147 | const transactionVersion = RuntimeConfig.runtimeVersion().transactionVersion; 148 | const payload = utx.createPayload(blockHash, genesisHash, specVersion, transactionVersion); 149 | 150 | if (!Crypto.verifySignature(extSignature.signature, payload, from, SignatureTypes.sr25519)) { 151 | Log.error("Validation error: Invalid signature"); 152 | return ResponseCodes.INVALID_SIGNATURE; 153 | } 154 | return utx.validate(TransactionSource.External).toU8a(); 155 | } 156 | return utx.validateUnsigned().toU8a(); 157 | } 158 | 159 | /** 160 | * @description module hook 161 | */ 162 | export function onFinalize(): void { 163 | Log.info("onInitialize() called"); 164 | } 165 | } -------------------------------------------------------------------------------- /assembly/frame/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./executive"; 2 | export * from "./models/storage-entry"; 3 | export * from "./system"; 4 | -------------------------------------------------------------------------------- /assembly/frame/models/storage-entry.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray, BytesReader, Codec } from "as-scale-codec"; 2 | import { Storage, Utils } from "subsembly-core"; 3 | 4 | /** 5 | * @description Represents an entry in the Storage 6 | */ 7 | export class StorageEntry{ 8 | /** 9 | * Module prefix 10 | */ 11 | private prefix: string; 12 | 13 | /** 14 | * Key name 15 | */ 16 | private key: string; 17 | 18 | constructor(prefix: string, key: string ){ 19 | this.prefix = prefix; 20 | this.key = key; 21 | } 22 | 23 | /** 24 | * @description Returns value mapped to the key, if the value exists in storage 25 | * If not, returns new instance of the value. 26 | * @param suffix suffix to append in the end of key (f.e, for storing balance of account, accountId is appended) 27 | */ 28 | get(suffix: Codec | null = null): T{ 29 | const value = Storage.get(Utils.getHashedKey(this.prefix, this.key, suffix)); 30 | if(value.isSome()){ 31 | return BytesReader.decodeInto((value.unwrap()).unwrap()); 32 | } 33 | return instantiate(); 34 | } 35 | /** 36 | * @description Set value in the storage 37 | * @param value 38 | * @param append suffix to append in the end of key (f.e, for storing balance of account, accountId is appended) 39 | */ 40 | set(value: T, suffix: Codec | null = null): void{ 41 | const key = Utils.getHashedKey(this.prefix, this.key, suffix); 42 | Storage.set(key, value.toU8a()); 43 | } 44 | 45 | /** 46 | * @description Clears the entry from storage 47 | * @param suffix suffix to append in the end of key (f.e, for storing balance of account, accountId is appended) 48 | */ 49 | clear(suffix: Codec | null = null): void{ 50 | const key = Utils.getHashedKey(this.prefix, this.key, suffix); 51 | Storage.clear(key); 52 | } 53 | 54 | /** 55 | * @description Retrieves value and gets rid of the entry in the storage 56 | * @param suffix suffix to append in the end of key (f.e, for storing balance of account, accountId is appended) 57 | */ 58 | take(suffix: Codec | null = null): T{ 59 | const value = Storage.take(Utils.getHashedKey(this.prefix, this.key, suffix)); 60 | return value.length ? BytesReader.decodeInto(value) : instantiate(); 61 | } 62 | } -------------------------------------------------------------------------------- /assembly/generated/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | # Folder contains automatically generated files 5 | !.gitignore -------------------------------------------------------------------------------- /assembly/index.ts: -------------------------------------------------------------------------------- 1 | // The entry file of your WebAssembly module. 2 | export const __heap_base = 0; 3 | 4 | export * from "./runtime/api/aura-api"; 5 | export * from "./runtime/api/block-builder"; 6 | export * from "./runtime/api/core"; 7 | export * from "./runtime/api/grandpa-api"; 8 | export * from "./runtime/api/others"; 9 | -------------------------------------------------------------------------------- /assembly/pallets/aura/aura.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray, BytesReader } from 'as-scale-codec'; 2 | import { InherentData, Storage, Utils } from "subsembly-core"; 3 | import { Moment, TimestampConfig } from '../../runtime/runtime'; 4 | 5 | /** 6 | * Storage entries for Aura module 7 | */ 8 | export namespace AuraStorageEntries {}; 9 | 10 | /** 11 | * @description Aura provides a slot-based block authoring mechanism. 12 | * In Aura a known set of authorities take turns producing blocks. 13 | */ 14 | export class Aura { 15 | public static readonly INHERENT_IDENTIFIER: string = "auraslot"; 16 | 17 | /** 18 | * @description Calls the TimeStamp module and returns configured min period. 19 | */ 20 | static _getSlotDuration(): Moment { 21 | return TimestampConfig.minimumPeriod(); 22 | } 23 | 24 | /** 25 | * @description Reads from the Storage the authorities that 26 | * were set on genesis, creates a vector of AccountIds and return it 27 | */ 28 | static _getAuthorities(): u8[] { 29 | const authorities = Storage.get(Utils.getHashedKey("Aura", "Authorities", null)); 30 | return authorities.isSome() ? (authorities.unwrap()).unwrap() : []; 31 | } 32 | /** 33 | * @description Sets the list of AccountIds to the storage 34 | * @param auths SCALE encoded list of authorities 35 | */ 36 | static _setAuthorities(auths: u8[]): void { 37 | Storage.set(Utils.getHashedKey("Aura", "Authorities", null), auths); 38 | } 39 | 40 | /** 41 | * @description Creates Aura inherent 42 | * @param data Inherent data 43 | */ 44 | static _createInherent(data: InherentData): u8[] { 45 | return []; 46 | } 47 | 48 | /** 49 | * @description Verify the validity of the inherent using the timestamp. 50 | * @param t new value for the timestamp inherent data 51 | * @param data inherent data to extract aura inherent data from 52 | */ 53 | static _checkInherent(t: Moment, data: InherentData): bool { 54 | const auraSlot = Aura._extractAuraInherentData(data); 55 | const timestampBasedSlot: Moment = instantiate(t.unwrap() / Aura._getSlotDuration().unwrap()); 56 | if (timestampBasedSlot == auraSlot) { 57 | return true; 58 | } 59 | else { 60 | return false; 61 | } 62 | } 63 | /** 64 | * @description Gets timestamp inherent data 65 | * @param inhData 66 | */ 67 | static _extractAuraInherentData(inhData: InherentData): Moment { 68 | const value = inhData.getData().get(Aura.INHERENT_IDENTIFIER); 69 | return BytesReader.decodeInto(value.unwrap()); 70 | } 71 | } -------------------------------------------------------------------------------- /assembly/pallets/balances/balances.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray, BytesReader, CompactInt } from 'as-scale-codec'; 2 | import { 3 | AccountData, 4 | AccountId, 5 | ExistenceRequirement, 6 | 7 | ResponseCodes, 8 | Storage, Utils, WithdrawReasons 9 | } from 'subsembly-core'; 10 | import { StorageEntry, SystemStorageEntries } from '../../frame'; 11 | import { AccountIdType, Balance, BalancesConfig, NonceType } from '../../runtime'; 12 | 13 | enum EventTypes { 14 | Transfer = 0, 15 | BalanceSet = 1 16 | } 17 | 18 | export namespace BalancesStorageEntries{ 19 | /** 20 | * @description Stores information about accountId 21 | * @storage_map AccountId 22 | */ 23 | export function Account(): StorageEntry>{ 24 | return new StorageEntry>("Balances", "Account"); 25 | } 26 | } 27 | /** 28 | * @description The Balances Module. 29 | * Used for account balance manipulation such as: 30 | * - Getting and setting free/reserved balances 31 | */ 32 | export class Balances { 33 | static readonly BALANCESET: u8[] = [2, 0]; 34 | static readonly TRANSFER: u8[] = [2, 1]; 35 | /** 36 | * @description Sets the balances of a given AccountId 37 | * Alters the Free balance and Reserved balances in Storage. 38 | */ 39 | static _setBalance(accountId: AccountIdType, freeBalance: Balance, reservedBalance: Balance): u8[] { 40 | // this is the minimum balance an account may have 41 | if(BalancesConfig.existentialDeposit().unwrap() > freeBalance.unwrap()) { 42 | return ResponseCodes.VALIDITY_ERROR; 43 | } 44 | const accData = BalancesStorageEntries.Account().get(accountId); 45 | accData.setFree(freeBalance); 46 | accData.setReserved(reservedBalance); 47 | BalancesStorageEntries.Account().set(accData, accountId); 48 | Balances._depositEvent(EventTypes.BalanceSet, accountId.toU8a().concat(freeBalance.toU8a())); 49 | return ResponseCodes.SUCCESS; 50 | } 51 | 52 | /** 53 | * @description Dummy transfer call 54 | * @param dest 55 | * @param value 56 | * @returns 57 | */ 58 | static transfer(dest: AccountId, value: Balance): u8[] { 59 | return []; 60 | } 61 | 62 | /** 63 | * @description Transfer the given value from source to destination 64 | * @param source source account 65 | * @param dest dest account 66 | * @param value value of the transfer 67 | */ 68 | static _applyTransfer(source: AccountIdType, dest: AccountIdType, value: Balance): u8[] { 69 | const senderBalance = BalancesStorageEntries.Account().get(source); 70 | const receiverBalance = BalancesStorageEntries.Account().get(dest); 71 | 72 | if(senderBalance.getFree().unwrap() < value.unwrap()) { 73 | return ResponseCodes.INSUFFICIENT_BALANCE; 74 | } 75 | //@ts-ignore 76 | const newFreeBalance = senderBalance.getFree().unwrap() - value.unwrap(); 77 | // Make sure new balances is higher than ExistentialDeposit 78 | if(newFreeBalance < BalancesConfig.existentialDeposit().unwrap()) { 79 | return ResponseCodes.VALIDITY_ERROR; 80 | } 81 | 82 | //@ts-ignore 83 | senderBalance.setFree(instantiate(newFreeBalance)); 84 | //@ts-ignore 85 | receiverBalance.setFree(instantiate(receiverBalance.getFree().unwrap() + value.unwrap())); 86 | 87 | BalancesStorageEntries.Account().set(senderBalance, source); 88 | BalancesStorageEntries.Account().set(receiverBalance, dest); 89 | 90 | // Increment nonce and update balances in System 91 | const senderInfo = SystemStorageEntries.Account().get(source); 92 | const receiverInfo = SystemStorageEntries.Account().get(dest); 93 | 94 | senderInfo.setData(senderBalance); 95 | receiverInfo.setData(receiverBalance); 96 | 97 | senderInfo.setNonce(instantiate(senderInfo.nonce.unwrap() + 1)); 98 | SystemStorageEntries.Account().set(senderInfo, source); 99 | SystemStorageEntries.Account().set(receiverInfo, dest); 100 | Balances._depositEvent(EventTypes.Transfer, source.toU8a().concat(dest.toU8a()).concat(value.toU8a())); 101 | 102 | return ResponseCodes.SUCCESS; 103 | } 104 | 105 | /** 106 | * @description Append event to the vector of events in the storage 107 | * @param name Type of the event 108 | * @param args Argumenst for the event 109 | */ 110 | static _depositEvent(type: i32, args: u8[]): void { 111 | switch(type) { 112 | case EventTypes.Transfer: { 113 | Balances._addEventInStorage(this.TRANSFER, args); 114 | } 115 | case EventTypes.BalanceSet: { 116 | Balances._addEventInStorage(this.BALANCESET, args); 117 | } 118 | default: 119 | return ; 120 | } 121 | } 122 | 123 | /** 124 | * @description Validate transfer before executing the extrinsic 125 | * @param who 126 | * @param amount 127 | */ 128 | static _validateTransaction(who: AccountIdType, amount: Balance): u8[] { 129 | const currentBalance = BalancesStorageEntries.Account().get(who); 130 | if(BalancesConfig.existentialDeposit().unwrap() > currentBalance.getFree().unwrap()) { 131 | return ResponseCodes.VALIDITY_ERROR; 132 | } 133 | //@ts-ignore 134 | const newBalance = currentBalance.getFree().unwrap() - amount.unwrap(); 135 | if(newBalance < amount.unwrap()) { 136 | return ResponseCodes.INSUFFICIENT_BALANCE; 137 | } 138 | 139 | return ResponseCodes.SUCCESS; 140 | } 141 | 142 | /** 143 | * @description Withdraw fee for transaction inclusion, or for some other reason 144 | * @param who 145 | * @param fee 146 | * @param reason 147 | * @param existenceRequirement 148 | */ 149 | static _withdraw(who: AccountIdType, fee: Balance, reason: WithdrawReasons, existenceRequirement: ExistenceRequirement): void { 150 | if(reason == WithdrawReasons.TRANSACTION_PAYMENT) { 151 | const balance = BalancesStorageEntries.Account().get(who).getFree(); 152 | if(Utils.areArraysEqual(ResponseCodes.SUCCESS, this._validateTransaction(who, fee))) { 153 | BalancesStorageEntries.Account().set(instantiate(balance.unwrap() - fee.unwrap()), who); 154 | return ; 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * @description Adds RawEvent to vector of events in the storage 161 | * @param event Raw event to append in the storage 162 | */ 163 | static _addEventInStorage(event: u8[], args: u8[]): void { 164 | /** 165 | * TO-DO: Make event deposit dynamic 166 | * Currently it's hard-coded for two types of events: 167 | * ExtrinsicSuccess and ExtrinsicFailed 168 | */ 169 | 170 | const events = Storage.get(Utils.getHashedKey("Balances", "Events", null)); 171 | const eventsRaw: u8[] = events.isSome() ? (events.unwrap()).unwrap() : [0]; 172 | const bytesReader = new BytesReader(eventsRaw); 173 | const len = bytesReader.readInto().unwrap(); 174 | 175 | const newEvents = new CompactInt(len + 1).toU8a() 176 | .concat(bytesReader.getLeftoverBytes()) 177 | .concat(event) 178 | .concat(args); 179 | 180 | Storage.set(Utils.getHashedKey("Balances", "Events", null), newEvents); 181 | } 182 | } -------------------------------------------------------------------------------- /assembly/pallets/grandpa/grandpa.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray } from "as-scale-codec"; 2 | import { Storage, Utils } from "subsembly-core"; 3 | 4 | /** 5 | * @descritption Storage entries for Grandpa 6 | */ 7 | export namespace GrandpaStorageEntries { 8 | } 9 | 10 | /** 11 | * @description Class representing GRANDPA module 12 | */ 13 | export class Grandpa { 14 | public static readonly GRANDPA_AUTHORITIES: string = ":grandpa_authorities"; 15 | 16 | /** 17 | * @description Get GRANDPA authorities from the storage 18 | * concatenated list of Grandpa authorities 19 | */ 20 | static _authorities(): u8[] { 21 | const entry = Storage.get(Utils.stringsToBytes([this.GRANDPA_AUTHORITIES], false)); 22 | const auths = entry.isSome() ? (entry.unwrap()).unwrap() : [0]; 23 | // first byte is option byte 24 | return auths; 25 | } 26 | 27 | /** 28 | * @description Set GRANDPA authorities 29 | * @param auths list of Grandpa authorities 30 | */ 31 | static _setAuthorities(auths: u8[]): void { 32 | Storage.set(Utils.stringsToBytes([this.GRANDPA_AUTHORITIES], false), auths); 33 | } 34 | } -------------------------------------------------------------------------------- /assembly/pallets/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./aura/aura"; 2 | export * from "./balances/balances"; 3 | export * from "./grandpa/grandpa"; 4 | export * from "./timestamp/timestamp"; 5 | export * from "./transaction-payment/transaction-payment"; 6 | -------------------------------------------------------------------------------- /assembly/pallets/timestamp/timestamp.ts: -------------------------------------------------------------------------------- 1 | import { Bool, ByteArray, BytesReader } from 'as-scale-codec'; 2 | import { Call, InherentData, Log, ResponseCodes } from 'subsembly-core'; 3 | import { StorageEntry } from '../../frame'; 4 | import { Pallets, TimestampCalls } from '../../generated/dispatcher'; 5 | import { Inherent, Moment, TimestampConfig } from '../../runtime/runtime'; 6 | 7 | /** 8 | * @description Storage entries for Timestamp 9 | */ 10 | export namespace TimestampStorageEntries{ 11 | /** 12 | * Current set Timestamp value 13 | */ 14 | export function Now(): StorageEntry{ 15 | return new StorageEntry("Timestamp", "Now"); 16 | }; 17 | 18 | /** 19 | * Checks if Timestamp was updated for current slot 20 | */ 21 | export function DidUpdate(): StorageEntry{ 22 | return new StorageEntry("Timestamp", "DidUpdate"); 23 | }; 24 | }; 25 | 26 | /** 27 | * @description The Timestamp pallet provides functionality to get and set the on-chain time. 28 | */ 29 | export class Timestamp { 30 | public static readonly INHERENT_IDENTIFIER: string = "timstap0"; 31 | /** 32 | * @description Toggles the current value of didUpdate 33 | */ 34 | static _toggleUpdate(): void { 35 | const value = TimestampStorageEntries.DidUpdate().get(); 36 | TimestampStorageEntries.DidUpdate().set(new Bool(!(value.unwrap()))); 37 | } 38 | 39 | /** 40 | * @description Sets the current time. When setting the new time, 41 | * it must be greater than the last one (set into storage) with at least a MinimumPeriod 42 | * @param now timestamp number 43 | */ 44 | static set(now: Moment): u8[] { 45 | const didUpdate = TimestampStorageEntries.DidUpdate().get(); 46 | if (didUpdate.unwrap()) { 47 | Log.error('Validation error: Timestamp must be updated only once in the block'); 48 | return this._tooFrequentResponseCode(); 49 | } 50 | let minValue = TimestampStorageEntries.Now().get().unwrap() + TimestampConfig.minimumPeriod().unwrap(); 51 | if (now.unwrap() < minValue) { 52 | Log.error('Validation error: Timestamp must increment by at least between sequential blocks'); 53 | return this._timeframeTooLowResponceCode(); 54 | } 55 | 56 | TimestampStorageEntries.DidUpdate().set(new Bool(true)); 57 | TimestampStorageEntries.Now().set(now); 58 | Timestamp._toggleUpdate(); 59 | return ResponseCodes.SUCCESS; 60 | } 61 | 62 | /** 63 | * @description Creates timestamp inherent data 64 | * @param data inherent data to extract timestamp from 65 | */ 66 | static _createInherent(data: InherentData): Inherent { 67 | const timestampData: Moment = BytesReader.decodeInto(this._extractInherentData(data).unwrap()); 68 | let nextTime = timestampData; 69 | 70 | const now = TimestampStorageEntries.Now().get(); 71 | if (now.unwrap()) { 72 | let nextTimeValue = timestampData.unwrap() > now.unwrap() + TimestampConfig.minimumPeriod().unwrap() 73 | ? timestampData.unwrap() : now.unwrap() + TimestampConfig.minimumPeriod().unwrap(); 74 | 75 | nextTime = instantiate(nextTimeValue); 76 | } 77 | const call = new Call([Pallets.Timestamp, TimestampCalls.set], nextTime.toU8a()); 78 | return instantiate(call); 79 | } 80 | 81 | /** 82 | * @description Checks if the new value can be set as inherent data 83 | * @param t new value of the timestamp inherent data 84 | * @param data inherent data to extract timestamp from 85 | */ 86 | static _checkInherent(t: Moment, data: InherentData): bool { 87 | const MAX_TIMESTAMP_DRIFT_MILLS: Moment = instantiate(30 * 1000); 88 | const timestampData: Moment = BytesReader.decodeInto(this._extractInherentData(data).unwrap()); 89 | const minimum: Moment = instantiate(TimestampStorageEntries.Now().get().unwrap() + TimestampConfig.minimumPeriod().unwrap()); 90 | if (t.unwrap() > timestampData.unwrap() + MAX_TIMESTAMP_DRIFT_MILLS.unwrap()) { 91 | return false; 92 | } 93 | else if (t.unwrap() < minimum.unwrap()) { 94 | return false; 95 | } 96 | else { 97 | return true; 98 | } 99 | } 100 | 101 | /** 102 | * @description Construct too frequent response error 103 | */ 104 | static _tooFrequentResponseCode(): u8[] { 105 | return ResponseCodes.dispatchError(Pallets.Timestamp, 1); 106 | } 107 | 108 | /** 109 | * @description Construct too low response error 110 | */ 111 | static _timeframeTooLowResponceCode(): u8[] { 112 | return ResponseCodes.dispatchError(Pallets.Timestamp, 2); 113 | } 114 | 115 | /** 116 | * @description Gets timestamp inherent data 117 | * @param inhData inherentData instance provided 118 | */ 119 | static _extractInherentData(inhData: InherentData): ByteArray { 120 | return inhData.getData().get(Timestamp.INHERENT_IDENTIFIER); 121 | } 122 | } -------------------------------------------------------------------------------- /assembly/pallets/transaction-payment/payment.ts: -------------------------------------------------------------------------------- 1 | import { DispatchInfo, ExistenceRequirement, WithdrawReasons } from "subsembly-core"; 2 | import { AccountIdType, Balance, Weight } from "../../runtime/runtime"; 3 | import { Balances } from "../balances/balances"; 4 | 5 | /** 6 | * @description Handle withdrawing, refunding and depositing of transaction fees. 7 | */ 8 | export class Payment { 9 | /** 10 | * @description Withdraw transaction fees from the corresponding account 11 | * @param who 12 | * @param dispatchInfo 13 | * @param fee 14 | * @param tip 15 | */ 16 | static withdrawFee(who: AccountIdType, _dispatchInfo: DispatchInfo, fee: Balance, tip: Balance): void { 17 | if(fee.eq(instantiate(0))) { 18 | return ; 19 | } 20 | const withdrawReason = tip.eq(instantiate(0)) 21 | ? WithdrawReasons.TRANSACTION_PAYMENT 22 | : WithdrawReasons.TRANSACTION_PAYMENT | WithdrawReasons.TIP; 23 | Balances._withdraw(who, fee, withdrawReason, ExistenceRequirement.KeepAlive); 24 | } 25 | } -------------------------------------------------------------------------------- /assembly/pallets/transaction-payment/transaction-payment.ts: -------------------------------------------------------------------------------- 1 | import { u128 } from 'as-bignum'; 2 | import { UInt128, UInt32 } from 'as-scale-codec'; 3 | import { DispatchClass, DispatchInfo, Pays, PostDispatchInfo, RuntimeDispatchInfo, WeightToFeeCoefficient, WeightToFeePolynomial } from 'subsembly-core'; 4 | import { StorageEntry } from '../../frame'; 5 | import { AccountIdType, Balance, Multiplier, SystemConfig, UncheckedExtrinsic, Weight } from '../../runtime/runtime'; 6 | import { Payment } from './payment'; 7 | 8 | /** 9 | * Storage entries for TransactionPayment module 10 | */ 11 | export namespace TransactionPaymentStorageEntries { 12 | /** 13 | * @description Multiplier value for the next fee 14 | */ 15 | export function NextFeeMultiplier(): StorageEntry { 16 | return new StorageEntry("TransactionPayment", "Multiplier"); 17 | } 18 | 19 | /** 20 | * @description Fee for each byte 21 | */ 22 | export function TransactionByteFee(): StorageEntry { 23 | return new StorageEntry("TransactionPayment", "ByteFee"); 24 | } 25 | } 26 | 27 | export class TransactionPayment { 28 | /** 29 | * @description Compute the final fee value for a particular transaction. 30 | * 31 | * The final fee is composed of: 32 | * - `base_fee`: This is the minimum amount a user pays for a transaction. It is declared 33 | * as a base _weight_ in the runtime and converted to a fee using `_WeightToFee`. 34 | * - `len_fee`: The length fee, the amount paid for the encoded length (in bytes) of the 35 | * transaction. 36 | * - `weight_fee`: This amount is computed based on the weight of the transaction. Weight 37 | * accounts for the execution time of a transaction. 38 | * - `targeted_fee_adjustment`: This is a multiplier that can tune the final fee based on 39 | * the congestion of the network. 40 | * - (Optional) `tip`: If included in the transaction, the tip will be added on top. Only 41 | * signed transactions can have a tip. 42 | * The base fee and adjusted weight and length fees constitute the _inclusion fee,_ which is 43 | * the minimum fee for a transaction to be included in a block. 44 | * ```ignore 45 | * inclusion_fee = base_fee + len_fee + [targeted_fee_adjustment * weight_fee]; 46 | * final_fee = inclusion_fee + tip; 47 | * ``` 48 | * @param len byte length of the extrinsic 49 | * @param info information about dispatch 50 | * @param tip tip to be included 51 | */ 52 | static _computeFee(len: UInt32, info: DispatchInfo, tip: Balance): Balance { 53 | return this._computeFeeRaw(len, info.weight, tip, info.paysFee); 54 | }; 55 | 56 | /** 57 | * @description Compute the actual post dispatch fee for a particular transaction. 58 | * 59 | * Identical to `compute_fee` with the only difference that the post dispatch corrected 60 | * weight is used for the weight fee calculation. 61 | * @param len byte length of the extrinsic 62 | * @param info information about dispatch 63 | * @param postInfo information about what happens after dispatch 64 | * @param tip tip to be included 65 | */ 66 | static _computeActualFee(len: UInt32, info: DispatchInfo, postInfo: PostDispatchInfo, tip: Balance): Balance { 67 | return this._computeFeeRaw(len, postInfo.calcActualWeight(info), tip, postInfo.paysFee); 68 | }; 69 | 70 | /** 71 | * @description Actual computation of transaction fees 72 | * @param len byte length of the transaction 73 | * @param weight weight of transaction 74 | * @param tip tip to be included 75 | * @param paysFee simple boolean indicating whether initiator pays transaction fees 76 | */ 77 | static _computeFeeRaw(len: UInt32, weight: Weight, tip: Balance, paysFee: Pays): Balance { 78 | if (paysFee == Pays.Yes) { 79 | let length: Balance = instantiate(u128.fromU32(len.unwrap())); 80 | let perByte: Balance = TransactionPaymentStorageEntries.TransactionByteFee().get(); 81 | if (perByte.unwrap() == u128.Zero) { 82 | perByte = instantiate(u128.One); 83 | } 84 | 85 | // length fee. not adjusted 86 | let fixedLenFee = length.unwrap() * perByte.unwrap(); 87 | 88 | // the adjustable part of the fee 89 | let unadjustedWeightFee = this._weightToFee(weight); 90 | let multiplier = instantiate(u128.fromU64(TransactionPaymentStorageEntries.NextFeeMultiplier().get().unwrap())); 91 | 92 | // final adjusted part of the fee 93 | const adjustedWeightFee = multiplier.unwrap() * unadjustedWeightFee.unwrap(); 94 | let baseFee = this._weightToFee(SystemConfig.ExtrinsicBaseWeight()); 95 | 96 | let basedFee = baseFee.unwrap() + fixedLenFee + adjustedWeightFee + tip.unwrap(); 97 | return instantiate(basedFee); 98 | } 99 | else { 100 | return tip; 101 | } 102 | }; 103 | 104 | /** 105 | * @description Convert passed weight to balance 106 | * @param weight 107 | */ 108 | static _weightToFee(weight: Weight): Balance { 109 | // cap the weight to the maximum defined in runtime, otherwise it will be the 110 | // `Bounded` maximum of its data type, which is not desired. 111 | let cappedWeight = weight.unwrap() < SystemConfig.MaximumBlockWeight().unwrap() ? weight : SystemConfig.MaximumBlockWeight(); 112 | let formula = new WeightToFeePolynomial([ 113 | new WeightToFeeCoefficient(1, 0, false, 1) 114 | ]); 115 | return formula.calc(cappedWeight); 116 | }; 117 | 118 | /** 119 | * @description Withdraw transaction fees from the account 120 | * @param who 121 | * @param tip 122 | * @param info 123 | * @param len 124 | */ 125 | static _withdrawFee(who: AccountIdType, tip: Balance, info: DispatchInfo, len: UInt32): void { 126 | const fee = this._computeFee(len, info, tip); 127 | Payment.withdrawFee(who, info, fee, tip); 128 | }; 129 | 130 | /** 131 | * @description Query the data that we know about the fee of a given call. 132 | * NOTE: Because of the bug in UInt128 of as-scale-codec, we are currently returning static partial fee of 1; 133 | * @param ext 134 | * @param len 135 | */ 136 | static _queryInfo(ext: UncheckedExtrinsic, len: UInt32): RuntimeDispatchInfo { 137 | const dispatchInfo = new DispatchInfo(instantiate(ext.encodedLength()), Pays.Yes, DispatchClass.Normal); 138 | const partialFee = this._computeFee(len, dispatchInfo, instantiate(u128.Zero)); 139 | return new RuntimeDispatchInfo(dispatchInfo.weight, dispatchInfo.klass, partialFee); 140 | } 141 | } -------------------------------------------------------------------------------- /assembly/runtime/api/aura-api.ts: -------------------------------------------------------------------------------- 1 | import { Serialiser } from "subsembly-core"; 2 | import { Aura } from "../../pallets"; 3 | 4 | /** 5 | * @description Runtime API entries for Aura consensus 6 | */ 7 | 8 | /** 9 | * @description Get slot duration 10 | * @param data - i32 pointer to the start of the arguments passed 11 | * @param len - i32 length (in bytes) of the arguments passed 12 | */ 13 | export function AuraApi_slot_duration(data: i32, len: i32): u64 { 14 | return Serialiser.serialiseResult(Aura._getSlotDuration().toU8a()); 15 | } 16 | 17 | /** 18 | * @description Get list of Aura authorities 19 | * @param data - i32 pointer to the start of the arguments passed 20 | * @param len - i32 length (in bytes) of the arguments passed 21 | */ 22 | export function AuraApi_authorities(data: i32, len: i32): u64 { 23 | return Serialiser.serialiseResult(Aura._getAuthorities()); 24 | } 25 | -------------------------------------------------------------------------------- /assembly/runtime/api/block-builder.ts: -------------------------------------------------------------------------------- 1 | import { Bool, ByteArray, BytesReader } from 'as-scale-codec'; 2 | import { InherentData, Serialiser } from 'subsembly-core'; 3 | import { Executive } from '../../frame/executive'; 4 | import { UncheckedExtrinsic } from '../runtime'; 5 | 6 | /** 7 | * @description Runtime API entries used in block building process 8 | */ 9 | 10 | /** 11 | * @description On success returns array of zero length, on failure returns Dispatch error or Apply error (tbd) 12 | * @param data - i32 pointer to the start of the arguments passed 13 | * @param len - i32 length (in bytes) of the arguments passed 14 | */ 15 | export function BlockBuilder_apply_extrinsic(data: i32, len: i32): u64 { 16 | const input = Serialiser.deserialiseInput(data, len); 17 | const ext = BytesReader.decodeInto(input); 18 | const result = Executive.applyExtrinsic(ext); 19 | return Serialiser.serialiseResult(result); 20 | } 21 | 22 | /** 23 | * @description On success, returns an array of inherents 24 | * @param data i32 pointer to the start of the argument passed 25 | * @param len i32 length (in bytes) of the arguments passed 26 | */ 27 | 28 | export function BlockBuilder_inherent_extrinsics(data: i32, len: i32): u64 { 29 | const input = Serialiser.deserialiseInput(data, len); 30 | const inherent = BytesReader.decodeInto>(input); 31 | const inherents = Executive.createExtrinsics(inherent); 32 | return Serialiser.serialiseResult(inherents); 33 | } 34 | 35 | /** 36 | * @description Upon succesfull validation of Block's fields, appends the block to the chain 37 | * @param data i32 pointer to the start of the argument passed 38 | * @param len i32 length (in bytes) of the arguments passed 39 | */ 40 | 41 | export function BlockBuilder_finalize_block(data: i32, len: i32): u64 { 42 | const header = Executive.finalizeBlock(); 43 | return Serialiser.serialiseResult(header.toU8a()); 44 | } 45 | 46 | /** 47 | * @description Validates fields of the InherentData and sends back okay or error message with failed InherentDatas 48 | * @param data i32 pointer to the start of the argument passed 49 | * @param len i32 length (in bytes) of the arguments passed 50 | */ 51 | export function BlockBuilder_check_inherents(data: i32, len: i32): u64 { 52 | return Serialiser.serialiseResult((new Bool(true)).toU8a()); 53 | } 54 | 55 | /** 56 | * @description Generates random seed, returns Block object 57 | * @param data i32 pointer to the start of the argument passed 58 | * @param len i32 length (in bytes) of the arguments passed 59 | */ 60 | 61 | export function BlockBuilder_random_seed(data: i32, len: i32): u64 { 62 | return Serialiser.serialiseResult([]); 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /assembly/runtime/api/core.ts: -------------------------------------------------------------------------------- 1 | import { Bool, BytesReader } from "as-scale-codec"; 2 | import { Serialiser } from "subsembly-core"; 3 | import { Executive } from '../../frame/executive'; 4 | import { BlockType, HeaderType, RuntimeConfig } from '../runtime'; 5 | 6 | /** 7 | * @description Returns the version data encoded in ABI format as per the specification 8 | * @param data - i32 pointer to the start of the arguments passed 9 | * @param len - i32 length (in bytes) of the arguments passed 10 | */ 11 | export function Core_version(data: i32, len: i32): u64 { 12 | const version = RuntimeConfig.runtimeVersion(); 13 | return Serialiser.serialiseResult(version.toU8a()); 14 | } 15 | 16 | /** 17 | * @description Executes a full block by executing all exctrinsics included in it and update state accordingly. 18 | * @param data - i32 pointer to the start of the arguments passed 19 | * @param len - i32 length (in bytes) of the arguments passed 20 | */ 21 | export function Core_execute_block(data: i32, len: i32): u64 { 22 | const input = Serialiser.deserialiseInput(data, len); 23 | const block = BytesReader.decodeInto(input); 24 | Executive.executeBlock(block); 25 | return Serialiser.serialiseResult((new Bool(true)).toU8a()); // return true, if block execution succeds 26 | } 27 | 28 | /** 29 | * @description Initializes the Block instance from the passed argument 30 | * @param data - i32 pointer to the start of the arguments passed 31 | * @param len - i32 length ( in bytes ) of the arguments passed 32 | */ 33 | 34 | export function Core_initialize_block(data: i32, len: i32): u64 { 35 | const input = Serialiser.deserialiseInput(data, len); 36 | const header = BytesReader.decodeInto(input); 37 | Executive.initializeBlock(header); 38 | return Serialiser.serialiseResult([]); 39 | } 40 | -------------------------------------------------------------------------------- /assembly/runtime/api/grandpa-api.ts: -------------------------------------------------------------------------------- 1 | import { Serialiser } from 'subsembly-core'; 2 | import { Grandpa } from '../../pallets/grandpa/grandpa'; 3 | 4 | /** 5 | * @description Attempt to extract a pending set-change signal from a digest. 6 | * @param data i32 pointer to the start of the argument passed 7 | * @param len i32 length (in bytes) of the arguments passed 8 | */ 9 | export function GrandpaApi_grandpa_pending_change(data: i32, len: i32): u64 { 10 | return Serialiser.serialiseResult([]); 11 | } 12 | 13 | /** 14 | * @description Attempt to extract a forced set-change signal from a digest. 15 | * @param data i32 pointer to the start of the argument passed 16 | * @param len i32 length (in bytes) of the arguments passed 17 | */ 18 | export function GrandpaApi_grandpa_forced_change(data: i32, len: i32): u64 { 19 | return Serialiser.serialiseResult([]); 20 | } 21 | 22 | /** 23 | * @description Get the current set of authorities, along with their respective weights. 24 | * @param data i32 pointer to the start of the argument passed 25 | * @param len i32 length (in bytes) of the arguments passed 26 | */ 27 | export function GrandpaApi_grandpa_authorities(data: i32, len: i32): u64 { 28 | const authorities = Grandpa._authorities(); 29 | return Serialiser.serialiseResult(authorities.slice(1)); 30 | } 31 | -------------------------------------------------------------------------------- /assembly/runtime/api/others.ts: -------------------------------------------------------------------------------- 1 | import { BytesReader, CompactInt, UInt32 } from "as-scale-codec"; 2 | import { Serialiser } from "subsembly-core"; 3 | import { Executive, SystemStorageEntries } from "../../frame"; 4 | import { Metadata } from "../../generated/metadata"; 5 | import { TransactionPayment } from "../../pallets"; 6 | import { AccountIdType, UncheckedExtrinsic } from "../runtime"; 7 | 8 | /** 9 | * @description Babe configuration 10 | * @param data i32 pointer to the start of the argument passed 11 | * @param len i32 length (in bytes) of the arguments passed 12 | */ 13 | export function BabeApi_configuration(data: i32, len: i32): u64 { 14 | return Serialiser.serialiseResult([]); 15 | } 16 | 17 | /** 18 | * @description Generate session keys 19 | * @param data i32 pointer to the start of the argument passed 20 | * @param len i32 length (in bytes) of the arguments passed 21 | */ 22 | export function SessionKeys_generate_session_keys(data: i32, len: i32): u64 { 23 | return Serialiser.serialiseResult([]); 24 | } 25 | 26 | /** 27 | * @description Receives encoded byte array of Extrinsic appended to the source of transaction 28 | * Returns ValidTransaction or TransactionError code 29 | * @param data i32 pointer to the start of the argument passed 30 | * @param len i32 length (in bytes) of the arguments passed 31 | */ 32 | export function TaggedTransactionQueue_validate_transaction(data: i32, len: i32): u64 { 33 | let input = Serialiser.deserialiseInput(data, len); 34 | const uxt = BytesReader.decodeInto(input); 35 | const result = Executive.validateTransaction(uxt); 36 | return Serialiser.serialiseResult(result); 37 | } 38 | 39 | /** 40 | * @description Retrieves offchain worker 41 | * @param data i32 pointer to the start of the argument passed 42 | * @param len i32 length (in bytes) of the arguments passed 43 | */ 44 | export function OffchainWorkerApi_offchain_worker(data: i32, len: i32): u64 { 45 | return Serialiser.serialiseResult([]); 46 | } 47 | 48 | /** 49 | * @description Get metadata of the runtime 50 | * @param data i32 pointer to the start of the argument passed 51 | * @param len i32 length (in bytes) of the arguments passed 52 | */ 53 | export function Metadata_metadata(data: i32, len: i32): u64 { 54 | const encodedLen = new CompactInt(Metadata.metadata().length); 55 | return Serialiser.serialiseResult(encodedLen.toU8a().concat(Metadata.metadata())); 56 | } 57 | 58 | /** 59 | * @description Get the latest nonce for the account 60 | * @param data i32 pointer to the start of the argument passed 61 | * @param len i32 length (in bytes) of the arguments passed 62 | */ 63 | export function AccountNonceApi_account_nonce(data: i32, len: i32): u64 { 64 | const input = Serialiser.deserialiseInput(data, len); 65 | const who = BytesReader.decodeInto(input); 66 | const nonce = SystemStorageEntries.Account().get(who).nonce; 67 | return Serialiser.serialiseResult(nonce.toU8a()); 68 | } 69 | 70 | /** 71 | * @description Return dispatch info with partial transaction fees for the Extrinsic 72 | * @param data 73 | * @param len 74 | */ 75 | export function TransactionPaymentApi_query_info(data: i32, len: i32): u64 { 76 | const input = Serialiser.deserialiseInput(data, len); 77 | const bytesReader = new BytesReader(input); 78 | const ext = bytesReader.readInto(); 79 | const queryInfo = TransactionPayment._queryInfo(ext, new UInt32(ext.encodedLength())); 80 | return Serialiser.serialiseResult(queryInfo.toU8a()); 81 | } -------------------------------------------------------------------------------- /assembly/runtime/api/parachain-host.ts: -------------------------------------------------------------------------------- 1 | import { Serialiser } from "subsembly-core"; 2 | 3 | /** 4 | * @description The API for querying the state of parachains on-chain. 5 | * Current state of the API is mocked and returns either empty array or True 6 | */ 7 | 8 | /** 9 | * Get the current validators 10 | * @param data i32 pointer to the start of the argument passed 11 | * @param len i32 length (in bytes) of the arguments passed 12 | */ 13 | export function ParachainHost_validators(data: i32, len: i32): u64 { 14 | return Serialiser.serialiseResult([]); 15 | } 16 | 17 | /** 18 | * Get the current duty roster 19 | * @param data i32 pointer to the start of the argument passed 20 | * @param len i32 length (in bytes) of the arguments passed 21 | */ 22 | export function ParachainHost_duty_roster(data: i32, len: i32): u64 { 23 | return Serialiser.serialiseResult([]); 24 | } 25 | 26 | /** 27 | * Get the currently active parachains 28 | * @param data i32 pointer to the start of the argument passed 29 | * @param len i32 length (in bytes) of the arguments passed 30 | */ 31 | 32 | export function ParachainHost_active_parachains(data: i32, len: i32): u64 { 33 | return Serialiser.serialiseResult([]); 34 | } 35 | 36 | /** 37 | * Get the global validation schedule that all parachains should be validated under. 38 | * @param data i32 pointer to the start of the argument passed 39 | * @param len i32 length (in bytes) of the arguments passed 40 | */ 41 | export function ParachainHost_parachain_status(data: i32, len: i32): u64 { 42 | return Serialiser.serialiseResult([]); 43 | } 44 | 45 | /** 46 | * Get the given parachain's head code blob. 47 | * Takes id of the parachain and returns ValidationCode instance 48 | * @param data i32 pointer to the start of the argument passed 49 | * @param len i32 length (in bytes) of the arguments passed 50 | */ 51 | export function ParachainHost_parachain_code(data: i32, len: i32): u64 { 52 | let input = Serialiser.deserialiseInput(data, len); 53 | return Serialiser.serialiseResult([]); 54 | } 55 | 56 | /** 57 | * 58 | * @param data i32 pointer to the start of the argument passed 59 | * @param len i32 length (in bytes) of the arguments passed 60 | */ 61 | export function ParachainHost_ingress(data: i32, len: i32): u64 { 62 | let input = Serialiser.deserialiseInput(data, len); 63 | return Serialiser.serialiseResult([]); 64 | } -------------------------------------------------------------------------------- /assembly/runtime/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./runtime"; 2 | -------------------------------------------------------------------------------- /assembly/runtime/runtime.ts: -------------------------------------------------------------------------------- 1 | import { u128 } from "as-bignum"; 2 | import { ByteArray, CompactInt, Hash, UInt128, UInt32, UInt64 } from "as-scale-codec"; 3 | import { 4 | AccountData, AccountId, AccountInfo, Block, DigestItem, ExtrinsicData, 5 | GenericExtrinsic, Header, RuntimeVersion, Signature, SignedTransaction, SupportedAPIs 6 | } from "subsembly-core"; 7 | 8 | export type HashType = Hash; 9 | export type Moment = UInt64; 10 | export type NonceType = UInt32; 11 | export type ExtrinsicIndexType = UInt32; 12 | export type AmountType = Balance; 13 | export type SignedTransactionType = SignedTransaction; 14 | export type BlockNumber = CompactInt; 15 | export type AccountIdType = AccountId; 16 | export type SignatureType = Signature; 17 | export type DigestItemType = DigestItem; 18 | export type Balance = UInt128; 19 | export type HeaderType = Header; 20 | export type BlockType = Block; 21 | export type Inherent = UncheckedExtrinsic; 22 | export type ExtrinsicDataType = ExtrinsicData; 23 | export type Multiplier = UInt64; 24 | export type Weight = UInt64; 25 | export type UncheckedExtrinsic = GenericExtrinsic; 26 | export type AccountDataType = AccountData; 27 | export type RefCount = UInt32; 28 | export type AccountInfoType = AccountInfo; 29 | /** 30 | * Note: Originally Events are stored as a vector of RawEvents, 31 | * since we don't have support for vector of Codec types (i.e Codec[]), 32 | * we are using opaque bytes to store events 33 | */ 34 | export type VecEvent = ByteArray; 35 | /** 36 | * @description Constants for runtime 37 | */ 38 | export class RuntimeConfig { 39 | /** 40 | * @description Instanciates new RuntimeVersion Configration 41 | */ 42 | static runtimeVersion(): RuntimeVersion { 43 | const SPEC_NAME: string = "node-template"; 44 | const IMPL_NAME: string = "AssemblyScript" 45 | const AUTHORING_VERSION: u32 = 1; 46 | const SPEC_VERSION: u32 = 1; 47 | const IMPL_VERSION: u32 = 1; 48 | 49 | const APIS_VEC: SupportedAPIs = new SupportedAPIs(); 50 | APIS_VEC.addAPI([1, 1, 1, 1, 1, 1, 1, 1], 10); 51 | 52 | const TRANSACTION_VERSION: u32 = 1; 53 | 54 | return new RuntimeVersion( 55 | SPEC_NAME, 56 | IMPL_NAME, 57 | AUTHORING_VERSION, 58 | SPEC_VERSION, 59 | IMPL_VERSION, 60 | APIS_VEC, 61 | TRANSACTION_VERSION 62 | ); 63 | }; 64 | } 65 | 66 | /** 67 | * System config 68 | */ 69 | export class SystemConfig{ 70 | static readonly WEIGHT_PER_SECOND: u64 = 1_000_000_000_000; 71 | static readonly WEIGHT_PER_MILLIS: u64 = SystemConfig.WEIGHT_PER_SECOND / 1_000; 72 | static readonly WEIGHT_PER_MICROS: u64 = SystemConfig.WEIGHT_PER_MILLIS / 1_000; 73 | /** 74 | * @description Number of block hashes to store in the storage, pruning starts with the oldest block 75 | */ 76 | static BlockHashCount(): BlockNumber { 77 | return instantiate(1000); 78 | } 79 | 80 | /** 81 | * @description The maximum length of a block (in bytes). 82 | */ 83 | static MaximumBlockLength(): UInt32 { 84 | return new UInt32(5000); 85 | } 86 | 87 | /** 88 | * @description The maximum weight of a block. 89 | */ 90 | static MaximumBlockWeight(): Weight { 91 | return instantiate(2 * this.WEIGHT_PER_SECOND); 92 | } 93 | 94 | /** 95 | * @description The base weight of an Extrinsic in the block, independent of the of extrinsic being executed. 96 | */ 97 | static ExtrinsicBaseWeight(): Weight { 98 | return instantiate(125 * this.WEIGHT_PER_MICROS); 99 | } 100 | 101 | /** 102 | * @description Importing a block with 0 txs takes ~5 ms 103 | */ 104 | static BlockExecutionWeight(): Weight { 105 | return instantiate(5 * this.WEIGHT_PER_MILLIS); 106 | } 107 | } 108 | 109 | /** 110 | * @description Types and constants used in Timestamp pallet 111 | */ 112 | export class TimestampConfig { 113 | /** 114 | * @description Minimum period between timestamps 115 | */ 116 | static minimumPeriod(): Moment { 117 | return instantiate(5000); 118 | } 119 | } 120 | /** 121 | * @description Constants used in Balances module 122 | */ 123 | export class BalancesConfig { 124 | /** 125 | * @description Existential deposit 126 | */ 127 | static existentialDeposit(): Balance { 128 | return instantiate(u128.fromU32(1000)); 129 | } 130 | } -------------------------------------------------------------------------------- /assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } -------------------------------------------------------------------------------- /cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subsembly", 3 | "version": "1.0.7", 4 | "description": "CLI tool for Subsembly", 5 | "main": "dist/src/index.js", 6 | "scripts": { 7 | "test": "npx mocha 'test/**/*.js' --timeout 10000 && cd ./src/commands/spec/builder && npm run test", 8 | "dev": "ts-node src/index.ts", 9 | "build-spec": "npm install --prefix ./src/commands/spec/builder && npm run asbuild --prefix ./src/commands/spec/builder", 10 | "copy-wasm": "mkdir -p ./dist/src/commands/spec/builder/build/ && cp ./src/commands/spec/builder/build/build-spec.wasm ./dist/src/commands/spec/builder/build/", 11 | "build": "npm run build-spec && ./node_modules/.bin/tsc -p . && npm run copy-wasm" 12 | }, 13 | "bin": { 14 | "subsembly": "dist/src/index.js" 15 | }, 16 | "files": [ 17 | "dist", 18 | "../README.md" 19 | ], 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/LimeChain/subsembly" 23 | }, 24 | "keywords": [ 25 | "subsembly", 26 | "substrate", 27 | "polkadot", 28 | "assemblyscript" 29 | ], 30 | "author": "Dastanbek Samatov ", 31 | "license": "Apache-2.0", 32 | "bugs": { 33 | "url": "https://github.com/LimeChain/subsembly/issues" 34 | }, 35 | "homepage": "https://github.com/LimeChain/subsembly#readme", 36 | "devDependencies": { 37 | "@types/adm-zip": "^0.4.33", 38 | "@types/fs-extra": "^9.0.7", 39 | "@types/node": "^14.14.22", 40 | "@types/yargs": "^15.0.12", 41 | "mocha": "^8.3.0", 42 | "ts-node": "^9.1.1", 43 | "chai": "^4.3.0", 44 | "chai-as-promised": "^7.1.1" 45 | }, 46 | "dependencies": { 47 | "@assemblyscript/loader": "^0.17.1", 48 | "@polkadot/api": "3.9.1", 49 | "@polkadot/keyring": "5.6.2", 50 | "@polkadot/metadata": "3.9.1", 51 | "@polkadot/types": "3.9.1", 52 | "@polkadot/util": "5.6.2", 53 | "@polkadot/util-crypto": "5.6.2", 54 | "adm-zip": "^0.5.1", 55 | "axios": "^0.21.1", 56 | "fs-extra": "^9.1.0", 57 | "typescript": "^4.1.3", 58 | "yargs": "^16.2.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cli/src/commands/compile/README.md: -------------------------------------------------------------------------------- 1 | ## Metadata and Dispatcher 2 | 3 | ### Metadata 4 | 5 | A tool used for generating metadata of the Subsembly runtime. It reads from files inside `pallets` directory and from `System` module. 6 | 7 | It consists of a tool that generates and hex encodes the metadata of the runtime. Under the hood, it reads all pallets, gets declared calls, types, storage entries, events and generate a metadata of the runtime. Then it generates a new `metadata.ts` file inside `frame` folder, that contains a function that returns the metadata of the runtime. 8 | 9 | In order to generate metadata, certain rules are enforced for the pallets and runtime: 10 | 11 | - Pallet constants are defined in `runtime.ts` file 12 | - Each pallets constants are defined in a class with a name format `{pallet_name}{config}`, for example, `TimestampConfig` 13 | - Each constant is a static function 14 | - Each pallet should be represented by a class 15 | - Pallet calls are defined as static functions and functions that are denoted with '_' prefix are not exposed outside of runtime, i.e not included in module calls 16 | - Storage entries for the pallet are represented by a namespace called `StorageEntries` 17 | - Each Storage entry is a function 18 | 19 | The command for generating metadata is included in runtime build command. In order to debug metadata file, you can use `--json or -j` flag to generate json file of the metadata. 20 | 21 | ### Usage 22 | 23 | #### Install dependencies 24 | 1. `yarn install` 25 | 26 | #### Generate metadata json file 27 | 2. `yarn run metadata - --debug` 28 | 29 | #### Hex encode metadata ts file 30 | 3. `yarn run metadata` 31 | 32 | ### Dispatcher 33 | 34 | A tool used for generating `Dispatcher` function that receiving incoming extrinsics, decodes bytes to arguments and calls the corresponding pallet calls. Function returns a `ResponseCode` to indicate whether extrinsic was succesfully applied or not. 35 | 36 | It uses previously generated metadata json file to parse pallet calls, therefore, before generating dispatcher class, it's always good to generate the latest metadata of your runtime. 37 | 38 | ### Usage 39 | 40 | #### Pre-requisite 41 | 42 | 1. Generate the latest metadata as a `json` file. 43 | 44 | #### Install dependencies 45 | 1. `yarn install` 46 | 47 | #### Run dispatcher code generation 48 | 2. `yarn run dispatcher` 49 | -------------------------------------------------------------------------------- /cli/src/commands/compile/codegen/index.js: -------------------------------------------------------------------------------- 1 | const INDENTATION = " ".repeat(4); 2 | 3 | const polkadotTypes = require("../metadata/types.json"); 4 | 5 | function _reverseObject(obj) { 6 | return Object.keys(obj).reduce((r, k) => 7 | Object.assign(r, { [obj[k]]: k }, {}) 8 | ) 9 | } 10 | 11 | const subsemblyTypes = _reverseObject(polkadotTypes); 12 | 13 | class Import { 14 | constructor(from, types) { 15 | this.from = from; 16 | this.types = types; 17 | } 18 | 19 | toString() { 20 | return `import { ${this.types.map(type => type.toString()).join(', ')} } from "${this.from}";`; 21 | } 22 | } 23 | 24 | class ReturnType { 25 | constructor(value, indent) { 26 | this.value = value; 27 | this.indent = indent; 28 | } 29 | 30 | toString() { 31 | return `${INDENTATION.repeat(this.indent)}return ${this.value.toString()}`; 32 | } 33 | } 34 | 35 | /** 36 | * Class for variables 37 | */ 38 | class Variable { 39 | constructor (name, value, type, constant){ 40 | this.name = name; 41 | this.value = value; 42 | this.type = type; 43 | this.constant = constant; 44 | }; 45 | 46 | toString(){ 47 | return `${this.constant ? 'const' : 'let'} ${this.name}: ${this.type} = ${this.value.toString()}` 48 | }; 49 | } 50 | 51 | /** 52 | * Class that represents parameter 53 | */ 54 | class Parameter { 55 | constructor(name, type){ 56 | this.name = name; 57 | this.type = type; 58 | } 59 | 60 | toString(){ 61 | return `${this.name}: ${this.type.toString()}`; 62 | } 63 | } 64 | 65 | /** 66 | * Class that represents method 67 | */ 68 | class Method { 69 | constructor(name, parameters, returnType, body, isExport){ 70 | this.name = name; 71 | this.parameters = parameters; 72 | this.returnType = returnType; 73 | this.body = body; 74 | this.isExport = isExport; 75 | } 76 | 77 | toString(){ 78 | return `${this.isExport ? 'export ' : ''}function ${this.name}(${this.parameters.map(param => param.toString()).join(',')}): ${this.returnType.toString()} { 79 | ${this.body} 80 | }` 81 | } 82 | } 83 | 84 | /** 85 | * Class that represents Namespace type in AS 86 | */ 87 | class Namespace { 88 | constructor(name, methods, isExport){ 89 | this.name = name; 90 | this.methods = methods; 91 | this.isExport = isExport; 92 | } 93 | 94 | toString(){ 95 | return `${this.isExport ? 'export ' : ''}namespace ${this.name} { 96 | ${this.methods.map(method => method.toString()).join('\n')} 97 | }`; 98 | } 99 | 100 | addMethod(method){ 101 | this.methods.push(method); 102 | } 103 | } 104 | 105 | /** 106 | * Class that represents Enum in AS 107 | */ 108 | class EnumType { 109 | constructor(name, members, isExport) { 110 | this.name = name; 111 | this.members = members; 112 | this.isExport = isExport; 113 | } 114 | 115 | toString() { 116 | return ` 117 | ${this.isExport ? 'export ' : ''}enum ${this.name} { 118 | ${this.members.map(([name, value]) => `${INDENTATION}${name} = ${value}`).join(', \n') 119 | } 120 | }` 121 | }; 122 | 123 | toSwitchCase() { 124 | return switchCase(`${this.name.toLowerCase()}Type`, this.members.map(([name, value]) => [name, returnType(value).toString()])).toString(); 125 | } 126 | 127 | addMember(member) { 128 | this.members.push(member); 129 | } 130 | } 131 | 132 | class SwitchCase { 133 | constructor (type, members, indent, defaultReturn){ 134 | this.type = type; 135 | this.members = members; 136 | this.indent = indent; 137 | this.defaultReturn = defaultReturn; 138 | } 139 | 140 | closingBrace(indent){ 141 | return `${INDENTATION.repeat(indent)}}` 142 | } 143 | 144 | toString() { 145 | return `switch(${this.type}) { 146 | ${this.members.map(([name, value]) => { 147 | return `${INDENTATION.repeat(this.indent)}case ${name}: { 148 | ${INDENTATION.repeat(this.indent + 1)}${value.toString()}\n${this.closingBrace(this.indent)}`;}).join('\n')} 149 | ${INDENTATION.repeat(this.indent)}default: { 150 | ${INDENTATION.repeat(this.indent + 1)}${this.defaultReturn.toString()} 151 | ${this.closingBrace(this.indent)} 152 | ${this.closingBrace(this.indent - 1)}`; 153 | } 154 | } 155 | 156 | /** 157 | * Array value 158 | */ 159 | class ArrayType { 160 | constructor(value){ 161 | this.value = value; 162 | } 163 | 164 | toString(){ 165 | return `[${this.value.toString()}];`; 166 | } 167 | } 168 | 169 | class Call { 170 | constructor(module, call, args, indent) { 171 | this.module = module; 172 | this.call = call; 173 | this.args = args; 174 | this.indent = indent; 175 | } 176 | 177 | toString() { 178 | return `${INDENTATION.repeat(this.indent)}${this.module}.${this.call}(${this.args.map(arg => arg).join(', ')});` 179 | } 180 | } 181 | 182 | class BytesReader { 183 | constructor(bytes, index = 0, args, indent){ 184 | this.bytes = bytes; 185 | this.index = index; 186 | this.args = args; 187 | this.indent = indent; 188 | } 189 | 190 | toString() { 191 | return this.args.length ? `let bytesReader = new BytesReader(${this.bytes}); 192 | ${this.args.map((arg) => `${INDENTATION.repeat(this.indent)}const ${arg.name} = bytesReader.readInto<${subsemblyTypes[arg.type]}>();`).join('\n')}` : ``; 193 | } 194 | } 195 | 196 | const namespace = (name, methods, isExport) => new Namespace(name, methods, isExport); 197 | const method = (name, params, returnType, body, isExport) => new Method(name, params, returnType, body, isExport); 198 | const param = (name, type) => new Parameter(name, type); 199 | const variable = (name, value, type, constant) => new Variable(name, value, type, constant); 200 | const arrayType = (value) => new ArrayType(value); 201 | const eNum = (name, members, isExport) => new EnumType(name, members, isExport); 202 | const call = (module, call, args, indent) => new Call(module, call, args, indent); 203 | const bytesReader = (bytes, index, args, indent) => new BytesReader(bytes, index, args, indent); 204 | const switchCase = (type, members, indent, defaultReturn) => new SwitchCase(type, members, indent, defaultReturn); 205 | const returnType = (value, indent) => new ReturnType(value, indent); 206 | const importer = (from, types) => new Import(from, types); 207 | 208 | module.exports = { 209 | namespace, method, param, variable, switchCase, 210 | arrayType, eNum, call, bytesReader, returnType, importer 211 | }; 212 | -------------------------------------------------------------------------------- /cli/src/commands/compile/compile.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import fs from "fs"; 3 | import { generateDispatcher, generateFile, generateMetadata } from '.'; 4 | import { Constants } from "../../constants"; 5 | import { Utils } from "../../utils"; 6 | 7 | export class Compile { 8 | /** 9 | * @description Run compilation logic 10 | */ 11 | static run(): void { 12 | console.log('Compiling Subsembly Project'); 13 | if(!fs.existsSync(__dirname + '/node_modules')) { 14 | Compile.installDependencies(); 15 | } 16 | Compile.generateFiles(); 17 | Compile.buildWasm(); 18 | } 19 | 20 | /** 21 | * @description Install dependencies for the Subsembly project 22 | */ 23 | private static installDependencies(): void { 24 | console.log('Installing Subsembly dependencies...'); 25 | execSync(`yarn install`); 26 | } 27 | 28 | /** 29 | * @description Generate Metadata and Dispatcher files 30 | */ 31 | private static generateFiles(): void { 32 | console.log('Generating Metadata and Dispatcher files...'); 33 | const metadata = generateMetadata(); 34 | generateDispatcher(metadata); 35 | generateFile(metadata); 36 | return ; 37 | } 38 | /** 39 | * @description Convert optimized Wasm to Hex and write it in the file 40 | */ 41 | private static buildWasm(): void { 42 | console.log('Building wasm file...'); 43 | execSync(`yarn run asbuild:optimized`); 44 | 45 | const WASM_FILE = fs.readFileSync(Constants.WASM_PATH); 46 | const byteArray = new Uint8Array(WASM_FILE); 47 | 48 | const result = Utils.toHexString(byteArray); 49 | fs.writeFile('./build/subsembly-wasm', result, 'utf8', () => { 50 | console.info("Successfully created WASM Code file"); 51 | }); 52 | } 53 | } -------------------------------------------------------------------------------- /cli/src/commands/compile/dispatcher/index.js: -------------------------------------------------------------------------------- 1 | const { eNum, namespace, call, returnType, switchCase, 2 | method, param, bytesReader, importer } = require("../codegen"); 3 | const fs = require('fs'); 4 | const path = require("path"); 5 | 6 | /** 7 | * Helper functions used for generating Dispatcher file 8 | */ 9 | class DispatcherHelpers { 10 | /** 11 | * Depth level of indentation 12 | */ 13 | static indentLevel = 1; 14 | 15 | /** 16 | * Gets necessary imports for the file 17 | * @param {*} pallets 18 | */ 19 | static _getImports(pallets) { 20 | const palletNames = pallets.members.map(([name, _value]) => name); 21 | const scaleCodecImports = importer('as-scale-codec', ['BytesReader', 'ScaleString']).toString(); 22 | const palletImports = importer('../pallets', palletNames.filter(pallet => pallet !== 'System')).toString(); 23 | const runtimeImports = importer('../runtime', ['Balance', 'Moment', 'UncheckedExtrinsic']).toString(); 24 | const subsemblyCoreImports = importer('subsembly-core', ["AccountId", "Call", "ResponseCodes"]).toString(); 25 | 26 | return [scaleCodecImports, palletImports, runtimeImports, subsemblyCoreImports].join('\n'); 27 | } 28 | 29 | /** 30 | * Initializes BytesReader and decodes bytes to argument types 31 | * @param call 32 | */ 33 | static _generateCallBody(call) { 34 | const transfer = call.name === 'transfer' ? `const source = ext.signature.signer` : ''; 35 | const body = bytesReader('ext.method.args', 0, call.args, this.indentLevel + 5).toString(); 36 | return [transfer, body].join('\n'); 37 | } 38 | 39 | /** 40 | * Generate enums for modules and calls 41 | * @param modules metadata modules 42 | */ 43 | static _generateEnums(modules) { 44 | const pallets = eNum("Pallets", [], true); 45 | const moduleCalls = []; 46 | 47 | modules.forEach((module, index) => { 48 | pallets.addMember([module.name, index]); 49 | if (module.calls) { 50 | const calls = []; 51 | module.calls.forEach((call, index) => { 52 | calls.push([call.name, index]); 53 | }); 54 | moduleCalls.push(eNum(`${module.name}Calls`, calls, true)); 55 | }; 56 | }); 57 | return { 58 | pallets, 59 | moduleCalls 60 | } 61 | } 62 | 63 | /** 64 | * Get return statement for a call, depending on the return type. 65 | * Return type of the dispatch() is u8[] 66 | * @param {*} module 67 | * @param {*} method 68 | */ 69 | static _getReturnStatement(module, method) { 70 | let methodName = method.name; 71 | let callArgs = []; 72 | if(method.name === 'transfer') { 73 | methodName = '_applyTransfer'; 74 | callArgs.push('source'); 75 | } 76 | callArgs = callArgs.concat(method.args.map(arg => arg.name)); 77 | 78 | switch(method.type) { 79 | case 'void': 80 | const methodCall = call(module.replace("Call", ""), methodName, callArgs, this.indentLevel + 5); 81 | return methodCall.toString().concat("\n", returnType('ResponseCodes.SUCCESS;', this.indentLevel + 5).toString()); 82 | case 'Vec': 83 | return returnType(call(module.replace("Call", ""), methodName, callArgs), this.indentLevel + 5).toString(); 84 | default: 85 | return returnType(call(module.replace("Call", ""), `${methodName}().toU8a`, callArgs), this.indentLevel + 5).toString(); 86 | } 87 | } 88 | 89 | /** 90 | * Generate the switch statement for calls of the module 91 | * @param {*} module module to generate calls for 92 | * @param {*} calls calls of the module 93 | */ 94 | static _generateSwitchCall(module, calls) { 95 | const members = []; 96 | calls.forEach((method) => { 97 | const body = this._generateCallBody(method); 98 | members.push([`${module}Calls.${method.name}`, body + "\n" + this._getReturnStatement(module, method)]); 99 | }) 100 | return switchCase("ext.method.callIndex[1]", members, this.indentLevel + 4, returnType('ResponseCodes.CALL_ERROR')); 101 | } 102 | /** 103 | * Generate body of the dispatch() function 104 | * @param {} pallets enum of pallets 105 | * @param {} modules metadata modules 106 | */ 107 | static _generateBody(pallets, modules) { 108 | const palletMembers = []; 109 | 110 | pallets.members.forEach(([name, value]) => { 111 | const [module] = modules.filter((module) => module.name === name); 112 | if(module.calls){ 113 | palletMembers.push([`Pallets.${name}`, this._generateSwitchCall(name, module.calls)]); 114 | } 115 | }) 116 | return switchCase("ext.method.callIndex[0]", palletMembers, this.indentLevel + 2, returnType('ResponseCodes.CALL_ERROR')).toString(); 117 | } 118 | 119 | /** 120 | * Generate Dispatcher namespace that contains dispatcher function 121 | * @param {*} pallets enum of pallets 122 | * @param {*} modules metadata modules 123 | */ 124 | static _generateNamespace(pallets, modules) { 125 | const callParam = param('ext', 'UncheckedExtrinsic'); 126 | const dispatch = method('dispatch', [callParam], 'u8[]', this._generateBody(pallets, modules), true); 127 | return namespace('Dispatcher', [dispatch], true).toString() 128 | } 129 | 130 | /** 131 | * Combine file components and generate the file 132 | * @param {*} modules metadata modules 133 | */ 134 | static generateDispatcher(modules) { 135 | const { pallets, moduleCalls } = this._generateEnums(modules); 136 | const imports = this._getImports(pallets); 137 | const enums = pallets.toString().concat('\n', moduleCalls.map(module => module.toString()).join('\n')); 138 | const dispatcher = this._generateNamespace(pallets, modules); 139 | return [imports, enums, dispatcher].join('\n'); 140 | } 141 | } 142 | 143 | module.exports = function generateDispatcher(metadata) { 144 | const { modules, _extrinsic } = metadata.metadata.V12; 145 | const dispatcher = DispatcherHelpers.generateDispatcher(modules); 146 | fs.writeFileSync(path.join(process.cwd(), "assembly/generated/dispatcher.ts"), dispatcher); 147 | console.log("Successfully generated dispatcher.ts file in /assembly/generated/!"); 148 | } -------------------------------------------------------------------------------- /cli/src/commands/compile/index.js: -------------------------------------------------------------------------------- 1 | const generateMetadata = require('./metadata/metadata-json'); 2 | const generateFile = require('./metadata/metadata-ts'); 3 | const generateDispatcher = require('./dispatcher/index'); 4 | 5 | module.exports = { 6 | generateDispatcher, generateFile, generateMetadata 7 | }; 8 | -------------------------------------------------------------------------------- /cli/src/commands/compile/metadata/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getMetadata: require('./metadata-json'), 3 | generateFile: require('./metadata-ts') 4 | }; -------------------------------------------------------------------------------- /cli/src/commands/compile/metadata/metadata-json.js: -------------------------------------------------------------------------------- 1 | const ts = require('typescript'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const generateModuleMetadata = require("./module-metadata"); 5 | const palletsPath = path.join(process.cwd(), "assembly/pallets/") 6 | 7 | /** 8 | * Generates the metadata of the Subsembly runtime 9 | */ 10 | function generateMetadata() { 11 | /** 12 | * Template metadata object to be populated 13 | */ 14 | const metadata = { 15 | magicNumber: 1635018093, 16 | metadata: { 17 | V12: { 18 | modules: [], 19 | extrinsic: { 20 | version: 4, 21 | signedExtensions: [ 22 | "CheckSpecVersion", 23 | "CheckTxVersion", 24 | "CheckGenesis", 25 | "CheckMortality", 26 | "CheckNonce", 27 | "CheckWeight", 28 | "ChargeTransactionPayment" 29 | ] 30 | } 31 | } 32 | } 33 | }; 34 | 35 | /** 36 | * System module is not inside pallets directory, so we load it separately 37 | */ 38 | const systemNode = ts.createSourceFile( 39 | "System", 40 | fs.readFileSync(path.join(process.cwd(), 'assembly/frame/system.ts'), 'utf-8'), 41 | ts.ScriptTarget.Latest 42 | ); 43 | 44 | /** 45 | * Add System module metadata 46 | */ 47 | metadata.metadata.V12.modules.push(generateModuleMetadata(metadata.metadata.V12.modules.length, systemNode)); 48 | 49 | /** 50 | * Go through the pallets inside `pallets` folder and generate module metadata for each pallet 51 | */ 52 | fs.readdirSync(palletsPath).forEach(module => { 53 | // path to the top AS project folder 54 | const assemblyPath = path.join(palletsPath, module); 55 | // we ignore index.ts and json files 56 | if (!module.includes("index.ts") && !module.includes("json")) { 57 | // Go through the files inside the pallet 58 | fs.readdirSync(assemblyPath).forEach(file => { 59 | // Name of the file should be same as the name of the pallet/module 60 | if (file.includes(module)) { 61 | const moduleNode = ts.createSourceFile( 62 | module, 63 | fs.readFileSync(path.join(assemblyPath, file), "utf-8"), 64 | ts.ScriptTarget.Latest 65 | ); 66 | metadata.metadata.V12.modules.push(generateModuleMetadata(metadata.metadata.V12.modules.length, moduleNode)); 67 | } 68 | }); 69 | } 70 | } 71 | ); 72 | 73 | return metadata; 74 | } 75 | 76 | module.exports = generateMetadata; -------------------------------------------------------------------------------- /cli/src/commands/compile/metadata/metadata-ts.js: -------------------------------------------------------------------------------- 1 | const { TypeRegistry } = require("@polkadot/types"); 2 | const { numberToU8a } = require("@polkadot/util"); 3 | const fs = require('fs'); 4 | const path = require("path"); 5 | const { namespace, method, arrayType, variable } = require('../codegen'); 6 | 7 | function _generateNamespace(metadata) { 8 | const metadataArr = arrayType(metadata); 9 | const metadataVar = variable("metadata", metadataArr, "u8[]", true); 10 | const metadataMethod = method("metadata", [], "u8[]", 11 | `${metadataVar.toString()} 12 | return metadata;`, 13 | true 14 | ); 15 | return namespace("Metadata", [metadataMethod], true).toString(); 16 | } 17 | 18 | /** 19 | * Generate metadata.ts file with the encoded metadata 20 | */ 21 | function generateFile(rawMetadata) { 22 | const registry = new TypeRegistry(); 23 | const { modules, extrinsic } = rawMetadata.metadata.V12; 24 | 25 | // Create metadata, magicNumber and version 26 | const metadata = registry.createType("MetadataV12", { modules, extrinsic }); 27 | const magicNumber = registry.createType("U32", rawMetadata.magicNumber).toHex(true); 28 | const version = registry.createType("u8", 12).toHex(true); 29 | // SCALE encode metadata 30 | const encodedData = Array.from(numberToU8a(magicNumber)).concat(Array.from(numberToU8a(version))).concat(Array.from(metadata.toU8a())); 31 | fs.writeFileSync(path.join(process.cwd(), "assembly/generated/metadata.ts"), _generateNamespace(encodedData)); 32 | console.log("Successfully generated metadata.ts file in /assembly/generated/!"); 33 | } 34 | 35 | module.exports = generateFile; -------------------------------------------------------------------------------- /cli/src/commands/compile/metadata/types.json: -------------------------------------------------------------------------------- 1 | { 2 | "void": "void", 3 | "Bool": "bool", 4 | "UInt64": "u64", 5 | "UInt32": "u32", 6 | "UInt16": "u16", 7 | "UInt8": "u8", 8 | "Int64": "i64", 9 | "Int32": "i32", 10 | "Int16": "i16", 11 | "Int8": "i8", 12 | "i32": "i32", 13 | "u32": "u32", 14 | "i64": "i64", 15 | "i16": "i16", 16 | "u16": "u16", 17 | "ScaleString": "String", 18 | "ExtrinsicIndex": "Index", 19 | "CompactInt": "Compact", 20 | "AccountId": "AccountId", 21 | "SignedTransaction": "Extrinsic", 22 | "Inherent": "Extrinsic", 23 | "Extrinsic": "Extrinsic", 24 | "Block": "Block", 25 | "Balance": "Balance", 26 | "AccountData": "AccountData", 27 | "BlockNumber": "Compact", 28 | "ExtrinsicData": "Bytes", 29 | "AccountInfo": "AccountInfoWithRefCount", 30 | "Signature": "H512", 31 | "Hash": "Hash", 32 | "Phase": "Phase", 33 | "Moment": "Moment", 34 | "ByteArray": "Bytes", 35 | "Header": "Header", 36 | "Weight": "Weight", 37 | "Multiplier": "Multiplier", 38 | "DispatchInfo": "DispatchInfo", 39 | "PostDispatchInfo": "DispatchInfo", 40 | "WithdrawReasons": "WithdrawReasons", 41 | "ExistenceRequirement": "bool", 42 | "VecEvent": "Vec" 43 | } -------------------------------------------------------------------------------- /cli/src/commands/index.ts: -------------------------------------------------------------------------------- 1 | import { Compile } from './compile/compile'; 2 | import { Init } from "./init/init"; 3 | import { BuildSpec } from './spec/build-spec'; 4 | 5 | /** 6 | * @description List of command of Subsembly-cli 7 | */ 8 | export const commands = [ 9 | { 10 | command: 'init [to]', 11 | description: 'Initialize a new Subsembly starter project', 12 | // @ts-ignore 13 | builder: (yargs) => { 14 | yargs.positional('to', { 15 | describe: 'Directory to initialize new Subsembly project in. Defaults to current directory.', 16 | type: 'string' 17 | }) 18 | }, 19 | //@ts-ignore 20 | handler: async (argv) => { 21 | await Init.run(argv.to || ''); 22 | } 23 | }, 24 | { 25 | command: 'compile', 26 | description: 'Compiles the Subsembly project placed in the current directory to a WASM bytecode', 27 | //@ts-ignore 28 | builder: (yargs) => { 29 | }, 30 | //@ts-ignore 31 | handler: (argv) => { 32 | Compile.run(); 33 | } 34 | }, 35 | { 36 | command: 'spec [to] [src] [raw] [wasm]', 37 | description: 'Generates custom spec file or converts it', 38 | //@ts-ignore 39 | builder: (yargs) => { 40 | yargs.positional('to', { 41 | describe: 'Path for new spec generation', 42 | type: 'string' 43 | }).positional('src', { 44 | describe: 'Path to the spec file', 45 | type: 'string' 46 | }).positional('raw', { 47 | describe: 'Path for raw chain spec output', 48 | type: 'string' 49 | }).positional('wasm', { 50 | describe: 'Path to the wasm file', 51 | type: 'string' 52 | }) 53 | }, 54 | //@ts-ignore 55 | handler: (argv) => { 56 | BuildSpec.run(argv.to || "", argv.src || "", argv.raw || "", argv.wasm || ""); 57 | } 58 | } 59 | ]; -------------------------------------------------------------------------------- /cli/src/commands/init/init.ts: -------------------------------------------------------------------------------- 1 | import AdmZip = require('adm-zip'); 2 | import axios from 'axios'; 3 | import fs from 'fs-extra'; 4 | import path from 'path'; 5 | import process from 'process'; 6 | import { Constants } from '../../constants'; 7 | 8 | export class Init { 9 | /** 10 | * @description Runs initialization logic 11 | * @param to Directory to initialize new Subsembly project 12 | */ 13 | static async run(to: string): Promise { 14 | if(fs.existsSync(path.resolve(process.cwd(), to))) { 15 | 16 | if(fs.readdirSync(path.resolve(process.cwd(), to)).length !== 0) { 17 | console.error("Error: initialization directory is not empty!"); 18 | process.exit(1); 19 | } 20 | } 21 | // Get the information about latest release of Subsembly 22 | const { data } = await axios.get(Constants.REPO_URL); 23 | const zip_url: string = data.zipball_url; 24 | 25 | const response = await axios.get(zip_url, { 26 | responseType: 'arraybuffer' 27 | }); 28 | 29 | const zip = new AdmZip(response.data as Buffer); 30 | 31 | for(const entry of zip.getEntries()) { 32 | const isMatch = Constants.INIT_IGNORE.some(rx => rx.test(entry.entryName)); 33 | // By default extractEntryTo() extracts everything inside directory, which is not desired for us 34 | // since we may ignore only specific files inside the directory. 35 | if(!isMatch && !entry.isDirectory) { 36 | zip.extractEntryTo(entry, `./`, true, true); 37 | } 38 | }; 39 | 40 | try{ 41 | this.renameDir(to); 42 | console.log("Succesfully initialized new Subsembly starter project!"); 43 | } 44 | catch(error) { 45 | console.error("Error initializing Subsembly project " + error.message); 46 | process.exit(1); 47 | } 48 | } 49 | 50 | /** 51 | * @description Move files from unzipped folder to the provided directory 52 | * @param to direcrtory name 53 | */ 54 | private static renameDir(to: string): void { 55 | const dirs = fs.readdirSync(process.cwd()).filter((dir) => dir.match(Constants.ZIP_FILE_PREFIX)); 56 | const projectDir: string = path.join(process.cwd(), dirs[0]); 57 | 58 | fs.copySync(projectDir, path.join(process.cwd(), to), { overwrite: true }); 59 | fs.removeSync(projectDir); 60 | } 61 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/build-spec.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import SpecBuilder from './builder'; 4 | 5 | /** 6 | * @description Spec class used to generate new spec file and convert it to raw 7 | */ 8 | export class BuildSpec { 9 | /** 10 | * @description Run the build spec logic 11 | * @param srcPath where is the source spec 12 | * @param rawSpecPath where to save raw file 13 | * @param wasmPath where is the wasm code 14 | */ 15 | static run(to: string, srcPath: string, rawSpecPath: string, wasmPath: string): void { 16 | try{ 17 | if (to === '' && srcPath === '' && rawSpecPath === '' && wasmPath === '') { 18 | SpecBuilder.customSpec(path.resolve(process.cwd(), 'chain-spec.json')); 19 | return ; 20 | } 21 | if(to !== '') { 22 | SpecBuilder.customSpec(path.resolve(process.cwd(), to)); 23 | return ; 24 | } 25 | if(srcPath === '') { 26 | console.error('No source spec file is specified!'); 27 | process.exit(1); 28 | } 29 | if (rawSpecPath === '') { 30 | srcPath = path.resolve(process.cwd(), srcPath); 31 | rawSpecPath = path.resolve(path.dirname(srcPath), './raw-chain-spec.json'); 32 | } 33 | if (wasmPath === '') { 34 | if(!fs.existsSync(path.resolve(process.cwd(), './build/subsembly-wasm'))){ 35 | console.error(`No wasm file path provided and ./build/wasm-runtime does not exist`); 36 | process.exit(1); 37 | } 38 | wasmPath = path.resolve(process.cwd(), './build/subsembly-wasm'); 39 | } 40 | console.log("Converting spec file to raw...") 41 | SpecBuilder.toRaw( 42 | path.resolve(process.cwd(), srcPath), 43 | path.resolve(process.cwd(), rawSpecPath), 44 | path.resolve(process.cwd(), wasmPath) 45 | ); 46 | } 47 | catch(error) { 48 | console.error('Error: ' + error.message); 49 | process.exit(1); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/assembly/index.ts: -------------------------------------------------------------------------------- 1 | import { u128 } from 'as-bignum'; 2 | import { BytesReader, CompactInt, UInt128, UInt64 } from 'as-scale-codec'; 3 | import { AccountData, AccountId, Utils } from 'subsembly-core'; 4 | 5 | export type Balance = UInt128; 6 | 7 | /** 8 | * @description Gets the AccountData converted to the bytes 9 | * @param freeBalance free balance for the AccountData 10 | */ 11 | export function getAccountDataBytes(freeBalance: Uint8Array): u8[] { 12 | const balance = BytesReader.decodeInto(Utils.toU8Array(freeBalance)); 13 | const accData = new AccountData(balance, instantiate(u128.Zero), instantiate(u128.Zero), instantiate(u128.Zero)); 14 | return accData.toU8a(); 15 | } 16 | 17 | /** 18 | * @description Get bytes as AccountID 19 | * @param authorities list of authorities in bytes 20 | */ 21 | export function getAccountIdBytes(authorities: Uint8Array): u8[] { 22 | let input = Utils.toU8Array(authorities); 23 | let auths: u8[] = []; 24 | let bytesReader = new BytesReader(input); 25 | let counter = 0; 26 | 27 | while (bytesReader.getLeftoverBytes().length != 0){ 28 | let accId = bytesReader.readInto(); 29 | auths = auths.concat(accId.getAddress()); 30 | counter += 1; 31 | } 32 | 33 | let result: u8[] = []; 34 | const length = new CompactInt(counter); 35 | return result.concat(length.toU8a()) 36 | .concat(auths); 37 | } 38 | 39 | /** 40 | * @description Encode string 41 | * @param str value 42 | * @param scale SCALE encoded if true 43 | */ 44 | export function stringToU8a(str: string, scale: bool): u8[] { 45 | return Utils.stringsToBytes([str], scale); 46 | } 47 | 48 | export const UInt8Array_ID = idof(); -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/customSpec-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | }, 15 | "balances": { 16 | "balances": [ 17 | [ 18 | "5F25Yb9PB9GV6RcqKpWceyx1X48TfnBxWn9m84A1JUYvsEF2", 19 | 10000000000000000 20 | ], 21 | [ 22 | "5CnyWwRgxbjqPyi4y9zMFjUWPjgw7M1SLzzehHRnp4K52M1L", 23 | 10000000000000000 24 | ], 25 | [ 26 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs", 27 | 10000000000000000 28 | ], 29 | [ 30 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 31 | 10000000000000000 32 | ], 33 | [ 34 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 35 | 10000000000000000 36 | ] 37 | ] 38 | }, 39 | "aura": { 40 | "authorities": [ 41 | "5FfBQ3kwXrbdyoqLPvcXRp7ikWydXawpNs2Ceu3WwFdhZ8W4" 42 | ] 43 | }, 44 | "grandpa": { 45 | "authorities": [ 46 | ["5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2", 1] 47 | ] 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const GenesisBuilder = require('./src/genesis-builder'); 3 | const fs = require('fs'); 4 | const { hexAddPrefix } = require("@polkadot/util"); 5 | 6 | /** 7 | * @description Class with functions for generating and converting Subsembly spec files 8 | */ 9 | class SpecBuilder{ 10 | /** 11 | * @description Generate default customSpec file 12 | * @param {*} specPath 13 | */ 14 | static customSpec(specPath) { 15 | let templateSpec = require('./customSpec-template.json'); 16 | fs.writeFileSync(specPath, JSON.stringify(templateSpec, null, 2)); 17 | console.log('Successfully generated new custom spec file!'); 18 | } 19 | 20 | /** 21 | * @description Convert customSpec file to raw 22 | * @param {*} specPath path to chain spec file 23 | * @param {*} rawSpecPath path to raw chain spec file 24 | * @param {*} wasmPath path to wasm-subsembly 25 | */ 26 | static toRaw(specPath, rawSpecPath, wasmPath) { 27 | if(!fs.existsSync(specPath)){ 28 | throw new Error(`Spec file doesn't exist at the provided path: ${specPath}`); 29 | }; 30 | 31 | let customSpec = require(specPath); 32 | let wasmCode = '0x'; 33 | 34 | if(!fs.existsSync(wasmPath)){ 35 | throw new Error(`Wasm code doesn't exist at the provided path: ${wasmPath}`); 36 | } 37 | else{ 38 | wasmCode = hexAddPrefix(fs.readFileSync(wasmPath).toString()); 39 | } 40 | 41 | const rawGenesis = GenesisBuilder.toRaw(customSpec, wasmCode); 42 | 43 | customSpec.genesis = rawGenesis; 44 | 45 | fs.writeFileSync(rawSpecPath, JSON.stringify(customSpec, null, 2)); 46 | console.log("Successfully converted to raw json!"); 47 | } 48 | } 49 | 50 | module.exports = SpecBuilder; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spec-builder", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "asbuild:optimized": "asc assembly/index.ts -b build/build-spec.wasm -t build/build-spec.wat --runtime full --optimize", 9 | "asbuild": "npm run asbuild:optimized" 10 | }, 11 | "author": "Dastanbek Samatov", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@assemblyscript/loader": "^0.17.9", 15 | "as-scale-codec": "0.2.3", 16 | "subsembly-core": "1.1.0" 17 | }, 18 | "devDependencies": { 19 | "assemblyscript": "^0.17.9", 20 | "mocha": "^8.0.1", 21 | "chai": "^4.3.0", 22 | "chai-as-promised": "^7.1.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/genesis-builder.js: -------------------------------------------------------------------------------- 1 | const Balances = require("./modules/balances"); 2 | const System = require("./modules/system"); 3 | const Aura = require('./modules/aura'); 4 | const Grandpa = require('./modules/grandpa'); 5 | 6 | /** 7 | * Class for the genesis configuration 8 | */ 9 | class GenesisBuilder { 10 | 11 | /** 12 | * Converts genesis property of the class to Raw 13 | * @param genesis instance of class 14 | */ 15 | static toRaw(genesisConfig, wasm) { 16 | if (!(genesisConfig && genesisConfig.genesis && genesisConfig.genesis.runtime && genesisConfig.genesis.runtime.system)) { 17 | throw new Error('Error: Invalid Genesis config provided'); 18 | } 19 | 20 | const rawGenesis = { 21 | raw: { 22 | top: { 23 | } 24 | } 25 | }; 26 | 27 | const system = genesisConfig.genesis.runtime.system; 28 | system['code'] = wasm; 29 | const rawSystem = System.toRaw(system); 30 | Object.assign(rawGenesis.raw.top, rawSystem); 31 | 32 | // Add any Balances related raw data 33 | if (genesisConfig.genesis.runtime.balances) { 34 | const balances = genesisConfig.genesis.runtime.balances; 35 | const rawBalances = Balances.toRaw(balances.balances); 36 | Object.assign(rawGenesis.raw.top, rawBalances); 37 | } 38 | 39 | // Add any Aura related raw data 40 | if(genesisConfig.genesis.runtime.aura){ 41 | const rawAura = Aura.toRaw(genesisConfig.genesis.runtime.aura.authorities); 42 | Object.assign(rawGenesis.raw.top, rawAura); 43 | } 44 | if(genesisConfig.genesis.runtime.grandpa) { 45 | const rawGrandpa = Grandpa.toRaw(genesisConfig.genesis.runtime.grandpa.authorities); 46 | Object.assign(rawGenesis.raw.top, rawGrandpa); 47 | } 48 | rawGenesis['raw']["childrenDefault"] = {}; 49 | return rawGenesis; 50 | } 51 | } 52 | 53 | module.exports = GenesisBuilder -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/modules/aura.js: -------------------------------------------------------------------------------- 1 | const { u8aToHex } = require('@polkadot/util'); 2 | const { Keyring } = require('@polkadot/api'); 3 | const Utils = require('../utils'); 4 | 5 | class Aura { 6 | /** 7 | * @description Aura module prefix 8 | */ 9 | static MODULE_PREFIX = "Aura"; 10 | 11 | /** 12 | * @description Authorities storage key 13 | */ 14 | static MODULE_KEY = "Authorities"; 15 | 16 | /** 17 | * Converts key&value pair to scale-encoded type 18 | * @param authorities list of authorities 19 | */ 20 | static toRaw(authorities){ 21 | Utils.validateIsArray(authorities, "Aura: Invalid or no Aura array provided"); 22 | 23 | if (authorities.length === 0){ 24 | throw new Error("Aura: Array of authorities is empty"); 25 | } 26 | 27 | let rawAuthorities = []; 28 | const key = Utils.getHashKey(this.MODULE_PREFIX, this.MODULE_KEY, []); 29 | const keyring = new Keyring({ type: 'sr25519' }); 30 | 31 | authorities.forEach(element => { 32 | const keyringInstance = keyring.addFromAddress(element); 33 | rawAuthorities = rawAuthorities.concat(Array.from(keyringInstance.publicKey)); 34 | }); 35 | const auths = Utils.getAuthoritiesBytes(rawAuthorities); 36 | return { 37 | [key]: u8aToHex(auths) 38 | } 39 | } 40 | } 41 | 42 | module.exports = Aura; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/modules/balances.js: -------------------------------------------------------------------------------- 1 | const { getAccountDataBytes, __getArray, __newArray, UInt8Array_ID } = require('../wasm-loader'); 2 | const { Keyring } = require('@polkadot/api'); 3 | const { u8aToHex } = require('@polkadot/util'); 4 | const Utils = require('../utils'); 5 | const { TypeRegistry } = require('@polkadot/types'); 6 | const System = require('./system'); 7 | 8 | class Balances { 9 | /** 10 | * @description Storage prefix of the module 11 | */ 12 | static MODULE_PREFIX = "Balances"; 13 | /** 14 | * @description Account key for the the module 15 | */ 16 | static MODULE_KEY = "Account"; 17 | 18 | /** 19 | * 20 | * @param balances array with accountId and balances 21 | */ 22 | static toRaw(balancesArray) { 23 | Utils.validateIsArray(balancesArray, "Balances: Invalid or no balances array provided"); 24 | 25 | const rawBalances = {}; 26 | const keyring = new Keyring({ type: 'sr25519' }); 27 | const typeReg = new TypeRegistry(); 28 | balancesArray.forEach(balanceArray => { 29 | Utils.validateIsArray(balanceArray, "Balances: Invalid or no balances array provided"); 30 | const keyringInstance = keyring.addFromAddress(balanceArray[0]); 31 | const key = Utils.getHashKey(this.MODULE_PREFIX, this.MODULE_KEY, keyringInstance.publicKey); 32 | const systemKey = Utils.getHashKey(System.MODULE_PREFIX, System.MODULE_KEY, keyringInstance.publicKey); 33 | const accData = this.accDataToHex(typeReg.createType("U128", balanceArray[1].toString()).toU8a()); 34 | const accInfo = System.getAccountInfo(accData); 35 | rawBalances[key] = accData; 36 | rawBalances[systemKey] = accInfo; 37 | }); 38 | return rawBalances; 39 | } 40 | /** 41 | * 42 | * @param value encodes AccountData instance to hex 43 | */ 44 | static accDataToHex = (value) => { 45 | const accData = getAccountDataBytes(__newArray(UInt8Array_ID, value)); 46 | const res = __getArray(accData); 47 | return u8aToHex(res); 48 | } 49 | 50 | } 51 | 52 | module.exports = Balances -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/modules/grandpa.js: -------------------------------------------------------------------------------- 1 | const Utils = require('../utils'); 2 | const { u8aToHex, hexStripPrefix } = require('@polkadot/util'); 3 | const { Keyring } = require('@polkadot/api'); 4 | const { TypeRegistry } = require('@polkadot/types'); 5 | 6 | const { stringToHex } = require('@polkadot/util'); 7 | 8 | class Grandpa { 9 | /** 10 | * @description Well known key for GRANDPA authorities 11 | */ 12 | static GRANDPA_AUTHORITIES = ":grandpa_authorities"; 13 | /** 14 | * @description Convert GRANDPA module to raw 15 | * @param {*} authorities list of Grandpa authorities with weights 16 | */ 17 | static toRaw(authorities) { 18 | const typeReg = new TypeRegistry(); 19 | Utils.validateIsArray(authorities); 20 | 21 | if (authorities.length === 0){ 22 | throw new Error("Grandpa: Array of authorities is empty"); 23 | } 24 | // boolean byte indicating if authorities are present 25 | let rawAuthorities = "0x01"; 26 | const authsLen = typeReg.createType('Compact', authorities.length); 27 | rawAuthorities = rawAuthorities.concat(hexStripPrefix(u8aToHex(authsLen.toU8a()))) 28 | const keyring = new Keyring({ type: 'ed25519' }); 29 | 30 | authorities.forEach(([accountId, weight]) => { 31 | const keyringInstance = keyring.addFromAddress(accountId); 32 | const weightU8a = typeReg.createType("U64", weight).toU8a(); 33 | rawAuthorities = rawAuthorities.concat(hexStripPrefix(u8aToHex(keyringInstance.publicKey))); 34 | rawAuthorities = rawAuthorities.concat(hexStripPrefix(u8aToHex(weightU8a))); 35 | }); 36 | 37 | return { 38 | [stringToHex(this.GRANDPA_AUTHORITIES)]: rawAuthorities 39 | } 40 | } 41 | } 42 | 43 | module.exports = Grandpa; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/modules/system.js: -------------------------------------------------------------------------------- 1 | const { stringToHex, hexStripPrefix } = require('@polkadot/util'); 2 | 3 | class System { 4 | 5 | /** 6 | * The well known key :CODE 7 | */ 8 | static CODE = ":code"; 9 | /** 10 | * @description Storage suffix 11 | */ 12 | static MODULE_KEY = "Account"; 13 | /** 14 | * @description Name of the module for storage 15 | */ 16 | static MODULE_PREFIX = "System" 17 | 18 | /** 19 | * Converts the system instance to raw object 20 | * @param system instance of the class 21 | */ 22 | static toRaw(system) { 23 | if (!system.code) { 24 | throw new Error("Code property is not populated"); 25 | } 26 | return { [stringToHex(this.CODE)]: system.code }; 27 | } 28 | 29 | /** 30 | * Combines account data with nonce and refCount to form AccountInfo 31 | * @param {*} hexAccountData 32 | */ 33 | static getAccountInfo(hexAccountData) { 34 | // nonce and refCount are 0 for all accounts in genesis 35 | const nonce = "0x00000000"; 36 | const refCount = "0x00000000"; 37 | return nonce.concat(hexStripPrefix(refCount)).concat(hexStripPrefix(hexAccountData)); 38 | } 39 | } 40 | 41 | module.exports = System; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/utils.js: -------------------------------------------------------------------------------- 1 | const { xxhashAsU8a } = require("@polkadot/util-crypto"); 2 | const { u8aToHex } = require('@polkadot/util'); 3 | const { stringToU8a, __newString, 4 | __newArray, __getArray, 5 | getAccountIdBytes, UInt8Array_ID, 6 | } = require('./wasm-loader'); 7 | /** 8 | * @description Utility functions 9 | */ 10 | class Utils { 11 | /** 12 | * @description Get hashed storage key 13 | * @param {*} prefix 14 | * @param {*} key 15 | * @param {*} suffix 16 | */ 17 | static getHashKey(prefix, key, suffix) { 18 | // we use twoX128 hashing algorithm for keys 19 | const hashedPrefix = xxhashAsU8a(Utils.stringToBytes(prefix, false), 128); 20 | const hashedKey = xxhashAsU8a(Utils.stringToBytes(key, false), 128); 21 | const hashedSuffix = suffix.length > 0 ? xxhashAsU8a(suffix, 128) : []; 22 | const finalKey = new Uint8Array([...hashedPrefix, ...hashedKey, ...hashedSuffix]); 23 | return u8aToHex(finalKey); 24 | } 25 | 26 | /** 27 | * @description Convert string to bytes 28 | * @param {*} str value 29 | * @param {*} isScale SCALE encode if true 30 | */ 31 | static stringToBytes(str, isScale) { 32 | const bytesPtr = stringToU8a(__newString(str), isScale); 33 | const bytes = __getArray(bytesPtr); 34 | return bytes; 35 | } 36 | 37 | /** 38 | * Validates whether the provided parameter is array. Throws otherwise 39 | * @param {*} arr 40 | */ 41 | static validateIsArray (arr, errorMessage) { 42 | if (!Array.isArray(arr)) { 43 | throw new Error(errorMessage); 44 | } 45 | } 46 | 47 | /** 48 | * Get scale encoded list of Aura authorities 49 | * @param authorities list of authorities 50 | */ 51 | static getAuthoritiesBytes = (authorities) => { 52 | const aPtr = __newArray(UInt8Array_ID, authorities); 53 | const auths = getAccountIdBytes(aPtr); 54 | const result = __getArray(auths); 55 | return result; 56 | } 57 | } 58 | 59 | module.exports = Utils; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/src/wasm-loader.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const loader = require("@assemblyscript/loader"); 3 | const imports = {} 4 | const wasmModule = loader.instantiateSync(fs.readFileSync(__dirname + "/../build/build-spec.wasm"), imports); 5 | 6 | module.exports = wasmModule.exports; -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/build-spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const chai = require('chai'); 3 | const chaiAsPromised = require('chai-as-promised'); 4 | chai.use(chaiAsPromised); 5 | const fs = require('fs'); 6 | const Utils = require('./utils/utils'); 7 | const MockedConstants = require('./utils/mocked-constants.json'); 8 | 9 | describe('Build spec tests', () => { 10 | before(async function() { 11 | await Utils.handleRawFilesDir(); 12 | }); 13 | 14 | it('correctly converts customSpec with all properties', async function() { 15 | await Utils.spec('./test/json-files/customSpec.json', './test/actual-raw-files/customSpecRaw.json', './test/utils/wasm-exmpl'); 16 | 17 | assert.isTrue(fs.existsSync('./test/actual-raw-files/customSpecRaw.json'), 'file does not exist'); 18 | const actualRaw = require('./actual-raw-files/customSpecRaw.json'); 19 | assert.deepEqual(actualRaw, MockedConstants.CUSTOM_SPEC_RAW_FULL); 20 | }) 21 | 22 | it('correctly converts customSpec without Aura authorities', async function() { 23 | await Utils.spec('./test/json-files/customSpec-noAura.json', './test/actual-raw-files/customSpecRaw-noAura.json', './test/utils/wasm-exmpl'); 24 | 25 | assert.isTrue(fs.existsSync('./test/actual-raw-files/customSpecRaw-noAura.json'), 'file does not exist'); 26 | 27 | const actualRaw = require('./actual-raw-files/customSpecRaw-noAura.json'); 28 | assert.deepEqual(actualRaw, MockedConstants.CUSTOM_SPEC_NO_AURA); 29 | }) 30 | 31 | it('correctly converts customSpec without Grandpa authorities', async function() { 32 | await Utils.spec('./test/json-files/customspec-noGrandpa.json', './test/actual-raw-files/customSpecRaw-noGrandpa.json', './test/utils/wasm-exmpl'); 33 | 34 | assert.isTrue(fs.existsSync('./test/actual-raw-files/customSpecRaw-noGrandpa.json'), 'file does not exist'); 35 | 36 | const actualRaw = require('./actual-raw-files/customSpecRaw-noGrandpa.json'); 37 | assert.deepEqual(actualRaw, MockedConstants.CUSTOM_SPEC_NO_GRANDPA); 38 | }) 39 | 40 | it('correctly converts customSpec with system property only', async function() { 41 | await Utils.spec('./test/json-files/customSpec-code.json', './test/actual-raw-files/customSpecRaw-code.json', './test/utils/wasm-exmpl'); 42 | 43 | assert.isTrue(fs.existsSync('./test/actual-raw-files/customSpecRaw-code.json'), 'file does not exist'); 44 | 45 | const actualRaw = require('./actual-raw-files/customSpecRaw-code.json'); 46 | assert.deepEqual(actualRaw, MockedConstants.CUSTOM_SPEC_RAW_CODE); 47 | }) 48 | 49 | it('should fail to convert customSpec without system property', async function() { 50 | const result = Utils.spec('./test/json-files/customSpec-noCode.json', './test/actual-raw-files/customSpecRaw-noCode.json', './test/utils/wasm-exmpl'); 51 | await assert.isRejected(result, "Error: Invalid Genesis config provided"); 52 | }) 53 | 54 | it('should fail if there is no genesis property', async function(){ 55 | const result = Utils.spec('./test/json-files/customSpec-noGenesis.json', './test/actual-raw-files/customSpecRaw-noGenesis.json', './test/utils/wasm-exmpl'); 56 | await assert.isRejected(result, "Error: Invalid Genesis config provided"); 57 | }) 58 | 59 | it('should fail if balances property is not given', async function(){ 60 | const result = Utils.spec('./test/json-files/customSpec-noBalances.json', './test/actual-raw-files/customSpecRaw-noBalances.json', './test/utils/wasm-exmpl'); 61 | await assert.isRejected(result, "Balances: Invalid or no balances array provided"); 62 | }) 63 | 64 | it('should fail if there is no runtime property', async function(){ 65 | const result = Utils.spec('./test/json-files/customSpec-noRuntime.json', './test/actual-raw-files/customSpecRaw-noRuntime.json', './test/utils/wasm-exmpl'); 66 | await assert.isRejected(result, "Error: Invalid Genesis config provided"); 67 | }) 68 | 69 | after(async function() { 70 | await Utils.handleRawFilesDir(); 71 | }) 72 | }) -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-code.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-noAura.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | }, 15 | "balances": { 16 | "balances": [ 17 | [ 18 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 19 | 1152921504606846976 20 | ], 21 | [ 22 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 23 | 1152921504606846976 24 | ], 25 | [ 26 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 27 | 1152921504606846976 28 | ] 29 | ] 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-noBalances.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | }, 15 | "balances": {} 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-noCode.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "balances": { 13 | "balances": [ 14 | [ 15 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 16 | 1152921504606846976 17 | ], 18 | [ 19 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 20 | 1152921504606846976 21 | ], 22 | [ 23 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 24 | 1152921504606846976 25 | ] 26 | ] 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-noGenesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null 10 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec-noRuntime.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": {} 11 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customSpec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | }, 15 | "balances": { 16 | "balances": [ 17 | [ 18 | "5F25Yb9PB9GV6RcqKpWceyx1X48TfnBxWn9m84A1JUYvsEF2", 19 | 10000000000000000 20 | ], 21 | [ 22 | "5CnyWwRgxbjqPyi4y9zMFjUWPjgw7M1SLzzehHRnp4K52M1L", 23 | 10000000000000000 24 | ], 25 | [ 26 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs", 27 | 10000000000000000 28 | ], 29 | [ 30 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 31 | 10000000000000000 32 | ], 33 | [ 34 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 35 | 10000000000000000 36 | ] 37 | ] 38 | }, 39 | "aura": { 40 | "authorities": [ 41 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs", 42 | "5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2" 43 | ] 44 | }, 45 | "grandpa": { 46 | "authorities": [ 47 | [ 48 | "5C72o78Mfepmgmf2UCA6zSHiZ236V7Zr6cjhSLGXEVMtzqQ8", 49 | 1 50 | ], 51 | [ 52 | "5G9NWJ5P9uk7am24yCKeLZJqXWW6hjuMyRJDmw4ofqxG8Js2", 53 | 1 54 | ] 55 | ] 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/json-files/customspec-noGrandpa.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Local Testnet", 3 | "id": "local_testnet", 4 | "chainType": "Local", 5 | "bootNodes": [], 6 | "telemetryEndpoints": null, 7 | "protocolId": null, 8 | "properties": null, 9 | "consensusEngine": null, 10 | "genesis": { 11 | "runtime": { 12 | "system": { 13 | "code": "0x" 14 | }, 15 | "balances": { 16 | "balances": [ 17 | [ 18 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 19 | 1152921504606846976 20 | ], 21 | [ 22 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 23 | 1152921504606846976 24 | ], 25 | [ 26 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 27 | 1152921504606846976 28 | ] 29 | ] 30 | }, 31 | "aura": { 32 | "authorities": [ 33 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 34 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" 35 | ] 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /cli/src/commands/spec/builder/test/utils/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const SpecBuilder = require('../..'); 3 | const path = require('path'); 4 | /** 5 | * Utils class with methods that help to run cli program 6 | * */ 7 | 8 | class Utils { 9 | /** 10 | * @description Promisified SpecBuilder 11 | * @param file to read 12 | * @param output file to write raw json 13 | */ 14 | static spec(src, output, wasm) { 15 | return new Promise(function(resolve, reject) { 16 | try { 17 | SpecBuilder.toRaw(src, output, wasm); 18 | resolve(); 19 | } 20 | catch(error) { 21 | reject(error); 22 | } 23 | }) 24 | } 25 | 26 | /** 27 | * @description Creates folder for raw files if it doesn't exist, removes the folder, if it exists 28 | */ 29 | static handleRawFilesDir() { 30 | return new Promise(function(resolve, reject) { 31 | try { 32 | const rawFilesPath = path.resolve(__dirname, '../actual-raw-files'); 33 | if(fs.existsSync(rawFilesPath)) { 34 | resolve(fs.removeSync(rawFilesPath)); 35 | } 36 | else { 37 | resolve(fs.mkdirsSync(rawFilesPath)); 38 | } 39 | } 40 | catch(error) { 41 | reject(error); 42 | } 43 | }) 44 | } 45 | } 46 | module.exports = Utils; -------------------------------------------------------------------------------- /cli/src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Commonly used constants 3 | */ 4 | export class Constants { 5 | 6 | /** 7 | * @description URL for the latest bundled release of the Subsembly starter 8 | */ 9 | static readonly REPO_URL: string = 'https://api.github.com/repos/LimeChain/subsembly/releases/latest'; 10 | 11 | /** 12 | * @description Files/directories to ignore while initializing Subsembly project 13 | */ 14 | static readonly INIT_IGNORE: RegExp[] = [ 15 | /cli/, 16 | /insert-aura/, 17 | /test-node/, 18 | /README.md/, 19 | /images/, 20 | /build.js/, 21 | /utils/, 22 | /.github/, 23 | /tests/, 24 | /scripts/ 25 | ]; 26 | 27 | /** 28 | * @description Unzipped file starts with the prefix of github_account + repo_name 29 | */ 30 | static readonly ZIP_FILE_PREFIX: string = 'LimeChain-subsembly'; 31 | 32 | /** 33 | * @description Path to the compiled Wasm file of AS 34 | */ 35 | static readonly WASM_PATH: string = process.cwd() + '/build/runtime-optimized.wasm'; 36 | } -------------------------------------------------------------------------------- /cli/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import yargs from 'yargs'; 3 | import { commands } from './commands'; 4 | 5 | const run = () => { 6 | for (const command of commands) { 7 | yargs.command(command.command, command.description, command.builder, command.handler); 8 | } 9 | yargs.help('help'); 10 | yargs.version(); 11 | yargs.demandCommand(); 12 | yargs.argv; 13 | } 14 | 15 | run(); 16 | -------------------------------------------------------------------------------- /cli/src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Utility functions 3 | */ 4 | export namespace Utils { 5 | /** 6 | * @description Hex encode raw bytes 7 | * @param byteArray UInt8Array of bytes 8 | */ 9 | export function toHexString(byteArray: Uint8Array): string { 10 | return Array.from(byteArray, function (byte) { 11 | return ('0' + (byte & 0xFF).toString(16)).slice(-2); 12 | }).join('') 13 | } 14 | } -------------------------------------------------------------------------------- /cli/test/compile.js: -------------------------------------------------------------------------------- 1 | const Subsembly = require('./utils/subsembly'); 2 | const assert = require('chai').assert; 3 | const chai = require('chai'); 4 | const chaiAsPromised = require('chai-as-promised'); 5 | chai.use(chaiAsPromised); 6 | 7 | const path = require('path'); 8 | const fs = require('fs-extra'); 9 | 10 | /** 11 | Check for generated files 12 | Check for wasm file 13 | If there is a compilation error it should display it 14 | */ 15 | describe('Subsembly compile command', () => { 16 | before(async() => { 17 | fs.mkdirsSync(path.join(__dirname, './generated')); 18 | await Subsembly.run(".", 'init', { to: './test/generated/sub1'}) 19 | }) 20 | 21 | it('Should compile Subsembly project', async () => { 22 | await assert.isFulfilled(Subsembly.run(path.resolve(__dirname, './generated/sub1'), 'compile', {}), 'Unexpected error while compiling'); 23 | const genDirs = fs.readdirSync(path.resolve(__dirname, './generated/sub1/assembly/generated')); 24 | assert.include(genDirs, 'dispatcher.ts', 'dispatcher.ts is not generated'); 25 | assert.include(genDirs, 'metadata.ts', 'metadata.ts is not generated'); 26 | assert.isTrue(fs.existsSync(path.resolve(__dirname, './generated/sub1/build/subsembly-wasm'), 'subsembly-wasm was not generated')); 27 | }).timeout(30000); 28 | 29 | after(() => { 30 | fs.removeSync(path.join(__dirname, './generated')); 31 | }) 32 | }) -------------------------------------------------------------------------------- /cli/test/init.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const chai = require('chai'); 3 | const chaiAsPromised = require('chai-as-promised'); 4 | chai.use(chaiAsPromised); 5 | const { execSync } = require('child_process'); 6 | const path = require('path'); 7 | const fs = require('fs-extra'); 8 | const Subsembly = require('./utils/subsembly'); 9 | 10 | const Constants = require('./utils/constants'); 11 | /** 12 | Test that cli does not do anything if the folder is not empty 13 | Test that the cli inits a project in the current directory if to is omitted & verify that ignored folder/files are not there 14 | Test that the cli inits a project to the to directory if specified & verify that ignored folder/files are not there 15 | */ 16 | describe('Init command', () => { 17 | before(() => { 18 | fs.mkdirsSync(path.join(__dirname, './generated')); 19 | }) 20 | 21 | it('Works for non-existing dir', async () => { 22 | await assert.isFulfilled(Subsembly.run(".", 'init', { to: './test/generated/sub1'}), "Unexpected error initializing"); 23 | }) 24 | 25 | it('Populates the directory', async () => { 26 | await assert.isFulfilled(Subsembly.run(".", 'init', { to: './test/generated/sub2'}), "Unexpected error initializing"); 27 | const dirs = fs.readdirSync(path.join(__dirname, './generated/sub2')); 28 | assert.isAtLeast(dirs.length, 1, 'Directory was not generated'); 29 | assert.include(dirs, 'assembly', "Does not contain assembly files initialized correctly!"); 30 | }) 31 | 32 | it('Initializes in current dir if to command is ignored', async () => { 33 | // create a new dir and go there 34 | execSync(`cd ${path.join(__dirname, './generated')} && mkdir sub3 && cd sub3`) 35 | await assert.isFulfilled(Subsembly.run(`${path.join(__dirname, './generated/sub3')}`, 'init', {}), 'Unexpected error in initializing'); 36 | const dirs = fs.readdirSync(path.join(__dirname, './generated/sub3')); 37 | assert.isNotEmpty(dirs, "Not initialized"); 38 | assert.include(dirs, 'assembly', "Not initialized"); 39 | }) 40 | 41 | it('Should not include ignored files in the directory', async () => { 42 | await assert.isFulfilled(Subsembly.run('.', 'init', {to: './test/generated/sub4'}), 'Unexpected error in initializing'); 43 | const dirs = fs.readdirSync(path.join(__dirname, './generated/sub1')); 44 | const isMatch = dirs.filter(dir => Constants.INIT_IGNORE.some(rx => rx.test(dir))); 45 | assert.isEmpty(isMatch, "Some files are not ignored"); 46 | }) 47 | 48 | it('Should fail if the directory is not empty', async () => { 49 | await assert.isRejected(Subsembly.run('.', 'init', {to: './test/generated/sub4'})); 50 | }) 51 | 52 | after(() => { 53 | fs.removeSync(path.join(__dirname, './generated')); 54 | }) 55 | }) -------------------------------------------------------------------------------- /cli/test/spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const chai = require('chai'); 3 | const chaiAsPromised = require('chai-as-promised'); 4 | chai.use(chaiAsPromised); 5 | const path = require('path'); 6 | const fs = require('fs-extra'); 7 | const Subsembly = require('./utils/subsembly'); 8 | 9 | describe('Subsembly spec command', () => { 10 | before(() => { 11 | fs.mkdirsSync(path.join(__dirname, './generated')); 12 | fs.mkdirsSync(path.join(__dirname, './generated/spec-files')) 13 | }) 14 | const cwdSpec = path.resolve(__dirname, './generated/spec-files'); 15 | const wasmPath = path.resolve(__dirname, './utils/wasm-exmpl'); 16 | 17 | it('Should initialize new default spec', async () => { 18 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', {}), 'Unexpected error while generating spec!'); 19 | assert.isTrue(fs.existsSync(path.join(cwdSpec, './chain-spec.json')), 'Chain spec was not generated!'); 20 | }) 21 | 22 | it('Should initialize at specified path if to command is given', async () => { 23 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', {to: './chain-spec-1.json'}), 'Unexpected error while generating spec!'); 24 | assert.isTrue(fs.existsSync(path.join(cwdSpec, './chain-spec-1.json')), 'Chain spec does not exist!'); 25 | }) 26 | 27 | it('Should throw an error if srcPath is not specified when converting to raw', async () => { 28 | await assert.isRejected(Subsembly.run(cwdSpec, 'spec', { raw: './raw-chain-spec.json', wasm: wasmPath})); 29 | assert.isFalse(fs.existsSync(path.join(cwdSpec, './raw-chain-spec.json')), 'Raw spec file was generated!') 30 | }) 31 | 32 | it('Should convert spec file to raw', async() => { 33 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', {to: './chain-spec-3.json'}), 'Unexpected error while generating spec!'); 34 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', {src: './chain-spec-3.json', raw: './raw-chain-spec-2.json', wasm: wasmPath }), 'Unexpected error while generating spec!'); 35 | assert.isTrue(fs.existsSync(path.join(cwdSpec, './raw-chain-spec-2.json')), 'Raw chain spec does not exist!'); 36 | }) 37 | 38 | it('Should write raw file to the same directory as src file if not specified', async () => { 39 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', {to: './chain-spec-2.json'}), 'Unexpected error while generating spec!'); 40 | await assert.isFulfilled(Subsembly.run(cwdSpec, 'spec', { src: './chain-spec-2.json', wasm: wasmPath }), 'Unexpected error while generating spec!'); 41 | assert.isTrue(fs.existsSync(path.join(cwdSpec, './raw-chain-spec.json')), 'Raw chain spec does not exist!'); 42 | }) 43 | 44 | after(() => { 45 | fs.removeSync(path.join(__dirname, './generated')); 46 | }) 47 | }) -------------------------------------------------------------------------------- /cli/test/utils/constants.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | class Constants { 3 | static INIT_IGNORE = [ 4 | /cli/, 5 | /insert-aura/, 6 | /test-node/, 7 | /README.md/, 8 | /images/, 9 | /build.js/, 10 | /utils/, 11 | /.github/, 12 | /tests/, 13 | /scripts/ 14 | ]; 15 | static TEST_GEN_PATH = path.join(__dirname, './test-gen'); 16 | } 17 | 18 | module.exports = Constants; -------------------------------------------------------------------------------- /cli/test/utils/subsembly.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | const path = require('path'); 3 | 4 | /** 5 | * @description Wrapper class of cli for tests 6 | */ 7 | class Subsembly { 8 | /** 9 | * @description Runs a specific command with subsembly cli 10 | * @param {*} cwd a directory to run subsembly command from 11 | * @param {*} command subsembly command 12 | * @param {*} args object with key and value pairs for arguments 13 | */ 14 | static run(cwd, command, args) { 15 | const argsString = []; 16 | Object.entries(args).forEach(([key, value]) => { 17 | argsString.push(`--${key}=${value}`); 18 | }); 19 | const executable = path.relative(cwd, './dist/src/index.js'); 20 | const process = exec(`${ cwd ? `cd ${cwd} &&` : ''} ${executable} ${command} ${argsString.join(' ')} && cd -`); 21 | return new Promise (function(resolve, reject) { 22 | process.addListener('close', (code, signal) => { 23 | switch(code) { 24 | case 0: 25 | resolve(); 26 | default: 27 | reject(); 28 | } 29 | }) 30 | process.addListener('error', reject); 31 | }); 32 | } 33 | } 34 | 35 | module.exports = Subsembly; -------------------------------------------------------------------------------- /cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ES2017", 5 | "module": "commonjs", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "rootDir": ".", 9 | "outDir": "dist", 10 | "allowJs": true, 11 | "resolveJsonModule": true 12 | }, 13 | "include": [ 14 | "src/**/*", 15 | "utils/src/**/*" 16 | ], 17 | "exclude": [ 18 | "node_modules", 19 | "src/commands/spec/builder/assembly/*.ts", 20 | "src/commands/spec/builder/node_modules/**/*" 21 | ] 22 | } -------------------------------------------------------------------------------- /docs/.gitbook/assets/components_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/components_diagram.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (2).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (3).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (4).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (5).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (6).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image (7).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/image.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-23-at-15.36.51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-23-at-15.36.51.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-23-at-16.58.28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-23-at-16.58.28.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-23-at-17.32.55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-23-at-17.32.55.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-23-at-17.40.28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-23-at-17.40.28.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-31-at-16.27.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-31-at-16.27.13.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-31-at-18.00.40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-31-at-18.00.40.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-03-31-at-18.18.54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-03-31-at-18.18.54.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-01-at-16.47.08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-01-at-16.47.08.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-01-at-16.50.47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-01-at-16.50.47.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-1.44.09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-1.44.09.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-1.44.48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-1.44.48.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-1.45.59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-1.45.59.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-1.47.21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-1.47.21.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-17.56.26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-17.56.26.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-17.59.57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-17.59.57.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-18.01.17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-18.01.17.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/screenshot-2021-04-02-at-18.03.02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/screenshot-2021-04-02-at-18.03.02.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/web3_badge_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/docs/.gitbook/assets/web3_badge_black.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Substrate runtimes in AssemblyScript 3 | --- 4 | 5 | # Introduction 6 | 7 | ## `Subsembly` 8 | 9 | ### What is it? 10 | 11 | **`Subsembly`** is a framework used for designing and implementing Substrate runtimes from scratch. **`Subsembly`** utilises Substrate Core to build runtimes in a non-native language of Substrate. With **`Subsembly`** you can build Substrate runtimes rapidly, with a varying degrees of technical freedom. 12 | 13 | ### How does it work? 14 | 15 | **`Substrate`** runtimes can be designed in any other language than Rust, provided that the language targets **`WebAssembly`**. This is where **`AssemblyScript`** and **`Subsembly`** come into play. 16 | 17 | ### Funding and Support 18 | 19 | **`Subsembly`** is funded by [**Web3 Foundation**](https://web3.foundation) **Grants** and developed, maintained by [**LimeChain**](https://limechain.tech). 20 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Introduction](README.md) 4 | 5 | ## Getting Started 6 | 7 | * [Overview](getting-started/overview.md) 8 | * [Substrate Essentials](getting-started/substrate-essentials.md) 9 | * [CLI](getting-started/cli/README.md) 10 | * [subsembly init](getting-started/cli/init.md) 11 | * [subsembly compile](getting-started/cli/subsembly-compile.md) 12 | * [subsembly spec](getting-started/cli/subsembly-spec.md) 13 | 14 | ## Development 15 | 16 | * [Runtime Development](development/development.md) 17 | * [Chain Specification](development/chain-specification.md) 18 | * [Runtime Execution](development/runtime-running.md) 19 | 20 | ## Modules 21 | 22 | * [Core](modules/core/README.md) 23 | * [System](modules/core/untitled.md) 24 | * [Executive](modules/core/executive.md) 25 | * [Crypto](modules/core/crypto.md) 26 | * [Log](modules/core/log.md) 27 | * [Pre-built](modules/pre-built/README.md) 28 | * [Aura](modules/pre-built/aura.md) 29 | * [Balances](modules/pre-built/balances.md) 30 | * [Timestamp](modules/pre-built/timestamp.md) 31 | * [TransactionPayment](modules/pre-built/transactionpayment.md) 32 | 33 | ## Advanced 34 | 35 | * [as-scale-codec](advanced/as-scale-codec.md) 36 | * [subsembly-core](advanced/subsembly-core.md) 37 | * [Metadata](advanced/untitled/README.md) 38 | * [Dispatcher](advanced/untitled/dispatcher.md) 39 | 40 | ## Guides 41 | 42 | * [Create Your First Subsembly Runtime](guides/create-your-first-subsembly-runtime/README.md) 43 | * [Set Up](guides/create-your-first-subsembly-runtime/set-up.md) 44 | * [Start the Node](guides/create-your-first-subsembly-runtime/start-the-node.md) 45 | * [PolkadotJS](guides/create-your-first-subsembly-runtime/polkadotjs.md) 46 | * [Start Your Network](guides/start-your-network/README.md) 47 | * [Prepare the Network](guides/start-your-network/generate-authority-keys.md) 48 | * [Launch Validator Nodes](guides/start-your-network/launch-a-network.md) 49 | * [Launch the Third Node](guides/start-your-network/launch-the-third-node.md) 50 | * [Create a New Module](guides/create-a-new-module/README.md) 51 | * [Nicks Module](guides/create-a-new-module/nicks-module.md) 52 | * [Launch the Node](guides/create-a-new-module/launch-the-node.md) 53 | * [PolkadotJs](guides/create-a-new-module/polkadotjs.md) 54 | 55 | -------------------------------------------------------------------------------- /docs/advanced/as-scale-codec.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: AS implementation of SCALE Codec 3 | --- 4 | 5 | # as-scale-codec 6 | 7 | ### Overview 8 | 9 | **`as-scale-codec`** is an AssemblyScript implementation of Polkadot SCALE Codec. The codec is used as a communication mechanism between Substrate Hosts and Substrate Runtimes. 10 | 11 | This is an integral part of the **`Subsembly`**, since it provides SCALE encoding and decoding functionality for the types. 12 | 13 | The library is maintained by LimeChain and has improved significantly over the course of **`Subsembly`** development. 14 | 15 | ### Codec 16 | 17 | Every **`Subsembly`** type needs to have encoding and decoding features, which is provided from the **`Codec`** interface of **`as-scale-codec`**. If you were to create your custom type to use in your **`Subsembly`** runtime, type has to implement **`Codec`** interface. 18 | 19 | ### Examples 20 | 21 | #### Encoding 22 | 23 | Every type has а **toU8a** function. It encodes type value into an array of bytes 24 | 25 | ```text 26 | import { Bool, String } from "as-scale-codec" 27 | 28 | // Bool 29 | const scaleBool = new Bool(true); 30 | scaleBool.toU8a() // => [0x01] 31 | 32 | // String 33 | const scaleString = new ScaleString("a"); 34 | scaleString.toU8a() // => [0x04, 0x61] 35 | 36 | // UInt64 37 | const uInt64 = new UInt64(10); 38 | uInt64.toU8a(); // => [0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] 39 | ``` 40 | 41 | #### Decoding 42 | 43 | We can decode arbitrary bytes into as-scale-codec using BytesReader: 44 | 45 | ```text 46 | import { BytesReader } from 'as-scale-codec'; 47 | 48 | // Arbitrary SCALE encoded bytes 49 | const bytes: u8[] = [ 50 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 51 | 69, 0, 0, 0 52 | ]; 53 | 54 | // Instantiate BytesReader instance with SCALE encoded bytes 55 | const bytesReader = new BytesReader(bytes); 56 | 57 | // Read Int64 58 | bytesReader.readInto(); 59 | // => new Int(-1) 60 | 61 | // Read UInt32 62 | bytesReader.readInto(); 63 | // => new UInt32(69) 64 | ``` 65 | 66 | For more information, please visit [as-scale-codec](https://github.com/LimeChain/as-scale-codec) and SCALE [docs](https://substrate.dev/docs/en/knowledgebase/advanced/codec). 67 | 68 | -------------------------------------------------------------------------------- /docs/advanced/subsembly-core.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Types and modules for Subsembly 3 | --- 4 | 5 | # subsembly-core 6 | 7 | ### Overview 8 | 9 | **`subsembly-core`** is a library that provides the core components and types for the **`Subsembly`** framework. 10 | 11 | ### Models 12 | 13 | Folder consists of commonly used types in **`Subsembly`**, such as, **`Header`**, **`Extrinsic`**, **`Block`**, etc. All the models implement Codec interface from [`as-scale-codec`](https://github.com/limechain/as-scale-codec) and have corresponding SCALE encoding and decoding methods. 14 | 15 | ### Modules 16 | 17 | Folder consists of commonly used modules inside **`Subsembly`** runtime. For instance, `Log` class to display messages to the Host. 18 | 19 | ### Utils 20 | 21 | Folder consists of commonly used utility functions in **`Subsembly`**. For instance, it includes methods for serialising and deserialising data incoming from the Host. 22 | 23 | ### 24 | 25 | -------------------------------------------------------------------------------- /docs/advanced/untitled/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Metadata of the project 3 | --- 4 | 5 | # Metadata 6 | 7 | ### Overview 8 | 9 | **`Subsembly`** runtimes expose metadata to make the interaction easier. Each module's own metadata together form one general metadata of a blockchain. Metadata of the module provides all the necessary information about the module. 10 | 11 | ### Module Metadata 12 | 13 | Module metadata has five properties, each holding a list of module specific items: 14 | 15 | * **`calls`** - **`dispatchable`** extrinsic calls 16 | * **`storage items`** - list of module specific storage entries 17 | * **`events`** - list of module specific events 18 | * **`constants`** - list of module specific constants 19 | * **`errors`** - list of module specific well-known errors 20 | 21 | ### Metadata Encoding 22 | 23 | **`Subsembly`** metadata encoding follows the same rules as [Substrate Metadata](https://substrate.dev/docs/en/knowledgebase/runtime/metadata). 24 | 25 | ### Metadata Generation 26 | 27 | One of the preliminary steps of runtime compilation is to generate metadata and dispatcher files. This is achieved by parsing module files and **`runtime.ts`** file to populate metadata of the module. 28 | 29 | For example, storage items of each module are located in the beginning of the module file: 30 | 31 | ```text 32 | // balances.ts 33 | 34 | export namespace BalancesStorageEntries{ 35 | /** 36 | * @description Stores information about accountId 37 | * @storage_map AccountId 38 | */ 39 | export function Account(): StorageEntry{ 40 | return new StorageEntry("Balances", "Account"); 41 | } 42 | } 43 | ``` 44 | 45 | Extrinsic calls are parsed from the static functions of the declared modules. The following static function is parsed as an extrinsic call for Balances module. 46 | 47 | ```text 48 | /** 49 | * @description Transfer the given value from source to destination 50 | */ 51 | static transfer(source: AccountIdType, dest: AccountIdType, value: Balance): u8[] { 52 | // Logic of the function // 53 | } 54 | ``` 55 | 56 | Static function with a name starting with underscore \(`_`\) is ignored while parsing and considered as internal function. 57 | 58 | And constants for each module are declared inside the **`runtime.ts:`** 59 | 60 | For instance, constants for Timestamp are declared like this: 61 | 62 | ```text 63 | /** 64 | * @description Types and constants used in Timestamp pallet 65 | */ 66 | export class TimestampConfig { 67 | /** 68 | * @description Minimum period between timestamps 69 | */ 70 | static minimumPeriod(): Moment { 71 | return instantiate(5000); 72 | } 73 | } 74 | ``` 75 | 76 | -------------------------------------------------------------------------------- /docs/advanced/untitled/dispatcher.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Dispatcher for incoming extrinsic calls 3 | --- 4 | 5 | # Dispatcher 6 | 7 | ### Overview 8 | 9 | Dispatcher is a module with a single function that dispatches incoming calls to the respective modules. It is a derivative of metadata, so it's required to generate metadata first in order to have a dispatcher module. 10 | 11 | In essence, the workflow of the dispatcher is the following: 12 | 13 | 1. Receive extrinsic bytes from outside 14 | 2. Decode it as a local **`Extrinsic`** type 15 | 3. Decode **`DispatchInfo`** bytes of the extrinsic 16 | 4. From the resulting **`Call`** type, execute the extrinsic call 17 | 18 | The dispatcher returns an array of bytes that define the result of the extrinsic execution. 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/development/chain-specification.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Genesis chain spec 3 | --- 4 | 5 | # Chain Specification 6 | 7 | ## Overview 8 | 9 | A chain specification file is used to define network communications, consensus-related information and initial account balances that Substrate nodes must have at genesis. For more information, please refer to Substrate [docs](https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec#:~:text=A%20chain%20specification%2C%20or%20%22chain,it%20must%20have%20at%20genesis.). 10 | 11 | ## The Genesis State 12 | 13 | Chain spec provides initial state of the runtime to the node. Some modules require specific storage keys to be populated before running. For instance, chain spec contains accounts with initial account balances for the **`Balances`** module, initial list of authorities for **`Aura`** module, etc. 14 | 15 | ## **`:code`** 16 | 17 | Apart from the module specific information chain spec contains initial runtime logic compiled into **`wasm`**. It is placed as a value of **"code"** key. 18 | 19 | ## Customising Chain Spec 20 | 21 | This command will produce new default chain-spec file with predefined test accounts and Aura authorities: 22 | 23 | ```text 24 | subsembly spec --to=./chain-spec.json 25 | ``` 26 | 27 | Once you have the chain spec file, you are free to modify fields of the JSON file to suit your needs. For example, change initial balances of accounts, replace authorities, etc. 28 | 29 | ## Raw Chain Spec 30 | 31 | Before chain spec is supplied to the runtime, it needs to converted to raw so that the runtime can parse it correctly it. In order to do that, one must execute: 32 | 33 | ```text 34 | subsembly spec --src=./chain-spec.json --raw=./raw-chain-spec.json --wasm=./build/subsembly-wasm 35 | ``` 36 | 37 | Chain spec files must be converted to raw to make sure that all nodes can sync and build on the same chain even after runtime upgrades. 38 | 39 | -------------------------------------------------------------------------------- /docs/development/development.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Developing runtime 3 | --- 4 | 5 | # Runtime Development 6 | 7 | ## Getting Started 8 | 9 | First, make sure you have the CLI tool installed [globally](../getting-started/cli/). To start the runtime development, first initialise a new **`Subsembly`** project: 10 | 11 | ```text 12 | subsembly init --to={projectPath} 13 | ``` 14 | 15 | ## `Subsembly` File Structure 16 | 17 | Newly initialised **`Subsembly`** project has the following file structure: 18 | 19 | ```text 20 | subsembly-project 21 | assembly/ 22 | │ 23 | └───runtime <--- Runtime API entries && defined types 24 | | 25 | └───frame <--- Contains Executive and System modules used for orchestrating 26 | runtime/ 27 | │ 28 | └───pallets <--- Subsembly pallets included in the Runtime 29 | yarn.lock 30 | package.json <--- Subsembly dependencies 31 | ``` 32 | 33 | **Runtime configuration** 34 | 35 | Top-level runtime folder consists of Runtime API entries that are exposed to the Host. Some functions of API are general to the blockchain, such as block production, execution, extrinsic submission and some others are specific to the module, such as **`Aura`** authorities. There is also, `runtime.ts` file, where types and constants for the frame modules and pallets are defined. We define general types that are used across the runtime and also pallet specific constants and types. 36 | 37 | Some requirements for Runtime types, such as: 38 | 39 | * Type should implement `Codec` interface from `as-scale-codec`. It makes sure that every type in the Runtime can be SCALE encoded and decoded. 40 | * Make sure to avoid possible `IntegerOverflow` exceptions. For example, it does not make sense to use 8-bit unsigned integer as `Timestamp` `Moment` type, since the value of timestamp is way out of range of what 8-bit unsigned integer can hold. 41 | 42 | ## Compiling 43 | 44 | To compile the runtime: 45 | 46 | ```text 47 | subsembly compile 48 | ``` 49 | 50 | This should compile your runtime and place hex-encoded **`wasm`** binary to the **`build`** folder. 51 | 52 | -------------------------------------------------------------------------------- /docs/development/runtime-compilation.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Compile your runtime 3 | --- 4 | 5 | # Runtime Compilation 6 | 7 | ### Overview 8 | 9 | To compile the runtime: 10 | 11 | ```text 12 | subsembly compile 13 | ``` 14 | 15 | ### Workflow 16 | 17 | The workflow of the compile command is following: 18 | 19 | 1. Make sure the dependencies are installed by checking **`node_modules`** folder. 20 | 2. Generate metadata of the project 21 | 1. Parse system and other modules inside **`assembly/pallets`** directory to extract storage entries, calls and constants. 22 | 2. Generate files: 23 | 1. **`metadata.ts`** 24 | 25 | Contains a function that returns SCALE encoded metadata of the project 26 | 27 | 2. **`dispatcher.ts`** 28 | 29 | Contains a function that is used to dispatch extrinsic calls of corresponding modules 30 | 3. Build **`wasm`** file: 31 | 32 | Compile AS project into **`.wasm`** file, hex encode the binary and save it in build directory. 33 | 34 | -------------------------------------------------------------------------------- /docs/development/runtime-running.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Running the runtime 3 | --- 4 | 5 | # Runtime Execution 6 | 7 | ## Prerequisites 8 | 9 | This section assumes that you have already initialised new **Subsembly** project and have generated the chain specification file. And also have converted the chain specification file into raw. 10 | 11 | ## Run with Substrate Node 12 | 13 | Eventually, our aim is to be able to run **Substrate** node with a compiled **Subsembly** runtime that is placed in the chain specification file. This is currently work in progress. 14 | 15 | But for running **Subsembly** runtimes for now oyu have to use our customised Substrate node. More information on that can be found in the following [guide](../guides/create-your-first-subsembly-runtime/). 16 | 17 | -------------------------------------------------------------------------------- /docs/getting-started/cli/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Install and run Subsembly 3 | --- 4 | 5 | # CLI 6 | 7 | ## Overview 8 | 9 | **Subsembly** comes with a CLI tool that helps to ease the development of **`Subsembly`** runtimes. CLI tool contains following commands that simplify compiling and running **Subsembly** runtimes. 10 | 11 | ## Installation 12 | 13 | {% hint style="danger" %} 14 | The minimum supported **Node** version is 14.0.0 15 | {% endhint %} 16 | 17 | To install CLI tool, run: 18 | 19 | ``` 20 | npm install -g subsembly 21 | ``` 22 | 23 | This will install **`subsembly`** globally. 24 | 25 | Once you're done installing **`subsembly`**, make sure it was installed correctly: 26 | 27 | {% tabs %} 28 | {% tab title="Default" %} 29 | ```bash 30 | subsembly 31 | ``` 32 | {% endtab %} 33 | 34 | {% tab title="Help" %} 35 | ```bash 36 | subsembly --help 37 | ``` 38 | {% endtab %} 39 | {% endtabs %} 40 | 41 | The above commands should display the help page: 42 | 43 | ![Picture 1. Default page](../../.gitbook/assets/image%20%286%29.png) 44 | 45 | ![Picture 2. Help page](../../.gitbook/assets/image%20%281%29.png) 46 | 47 | -------------------------------------------------------------------------------- /docs/getting-started/cli/init.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Initialising new Subsembly project 3 | --- 4 | 5 | # subsembly init 6 | 7 | ### Overview 8 | 9 | In order for you to start working on a **`Subsembly`** based Runtime, you need to initialise new project that will provide all of the necessary boilerplate code with high-level abstractions so that you can focus on the business requirements of your Runtime. 10 | 11 | This command initialises new **`Subsemly`** project to the specified path. 12 | 13 | ### Syntax 14 | 15 | ```text 16 | subsembly init [to] 17 | ``` 18 | 19 | {% hint style="info" %} 20 | Initialisation directory should be empty. 21 | {% endhint %} 22 | 23 | #### Parameters 24 | 25 | * **`--to`** - optional parameter, determines initialisation directory for the new **`Subsembly`** project. Defaults to current directory. 26 | 27 | ### Examples 28 | 29 | ```text 30 | # With specified path 31 | $ subsembly init --to=./new-sub 32 | 33 | # Without specified path (initializes into the current directory) 34 | $ subsembly init 35 | ``` 36 | 37 | The result of the above command: 38 | 39 | ```text 40 | $ ls 41 | LICENSE assembly package.json 42 | Makefile yarn.lock 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /docs/getting-started/cli/subsembly-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: cli overview and usage 3 | --- 4 | 5 | # Install and Help 6 | 7 | ## Overview 8 | 9 | **`subsembly`** is a cli tool that is used to ease the development of **`Subsembly`** runtimes. CLI tool contains following commands that help developers to easily compile and run **`Subsembly`** runtimes. 10 | 11 | ## Installation 12 | 13 | **Requirements**: 14 | 15 | * Node >= 14.0 16 | 17 | To install CLI tool, run: 18 | 19 | ``` 20 | $ npm install -g subsembly 21 | ``` 22 | 23 | This will install **`subsembly`** globally. 24 | 25 | Once you're done installing **`subsembly`**, make sure it was installed correctly: 26 | 27 | {% tabs %} 28 | {% tab title="Default" %} 29 | ```bash 30 | $ subsembly 31 | ``` 32 | {% endtab %} 33 | 34 | {% tab title="Help" %} 35 | ```bash 36 | $ subsembly --help 37 | ``` 38 | {% endtab %} 39 | {% endtabs %} 40 | 41 | The above commands should display the help page. 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/getting-started/cli/subsembly-compile.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Project compilation 3 | --- 4 | 5 | # subsembly compile 6 | 7 | ### Overview 8 | 9 | Compiles current **`Subsembly`** project. More specifically it installs the dependencies and generates the required files for the Runtime to support the `metadata` interface used by PolkadotJS. Lastly, it compiles the **`Subsembly`** Runtime into the hex encoded `wasm` binary file \(WASM blob\). 10 | 11 | ### Workflow 12 | 13 | The workflow of the compile command is following: 14 | 15 | 1. Make sure the dependencies are installed by checking **`node_modules`** folder. 16 | 2. Generate metadata of the project 17 | 1. Parse system and other modules inside **`assembly/pallets`** directory to extract storage entries, calls and constants. 18 | 2. Generate files: 19 | 1. **`metadata.ts`** 20 | 21 | Contains a function that returns SCALE encoded metadata of the project 22 | 23 | 2. **`dispatcher.ts`** 24 | 25 | Contains a function that is used to dispatch extrinsic calls of corresponding modules 26 | 3. Build **`wasm`** file: 27 | 28 | Compile AS project into **`.wasm`** file, hex encode the binary and save it in build directory. 29 | 30 | ### Build folder 31 | 32 | AssemblyScript places compiled **`wasm`** files inside the build folder in the root directory. The hex encoded **`wasm`** bytecode of **`Subsembly`** runtime is also placed in the same folder. 33 | 34 | ### Syntax 35 | 36 | ```text 37 | subsembly compile 38 | ``` 39 | 40 | ### Examples 41 | 42 | ```text 43 | # creates new compiled wasm of the runtime in /build directory 44 | subsembly compile 45 | ``` 46 | 47 | And the result of this command is reflected in build folder: 48 | 49 | ![Picture 2. Build folder files](../../.gitbook/assets/image.png) 50 | 51 | -------------------------------------------------------------------------------- /docs/getting-started/cli/subsembly-spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Spec files 3 | --- 4 | 5 | # subsembly spec 6 | 7 | ### Overview 8 | 9 | A command used for generating chain specification files and converting them into raw one. 10 | 11 | ### Syntax 12 | 13 | ```text 14 | subsembly spec [to] [src] [raw] [wasm] 15 | ``` 16 | 17 | #### Parameters 18 | 19 | * **`--to`** - optional parameter, determines initialisation path for the new chain specification file, defaults to current directory 20 | * **`--src`** - optional parameter, determines source specification file to convert to raw, defaults to current directory 21 | * **`--raw`** - optional parameter, determines path to save converted raw chain specification file. Defaults to **`{path-of-src}/raw-chain-spec.json`** 22 | * **`--wasm`** - optional parameter, determines path to the compiled hex encoded **`wasm`** binary. Defaults to **`build/subsembly-wasm`** 23 | 24 | ### Examples 25 | 26 | Generating new spec file: 27 | 28 | {% hint style="info" %} 29 | It is recommended to put all your spec files inside spec-files/ folder in root directory 30 | {% endhint %} 31 | 32 | {% tabs %} 33 | {% tab title="Option 1" %} 34 | ```bash 35 | $ subsembly spec 36 | Successfully generated new custom spec file! 37 | ``` 38 | {% endtab %} 39 | 40 | {% tab title="Option 2" %} 41 | ```bash 42 | $ subsembly spec --to=./chain-spec.json 43 | Successfully generated new custom spec file! 44 | ``` 45 | {% endtab %} 46 | {% endtabs %} 47 | 48 | Both commands will produce the same output: 49 | 50 | ```bash 51 | $ cat chain-spec.json 52 | { 53 | "name": "Local Testnet", 54 | "id": "local_testnet", 55 | "chainType": "Local", 56 | "bootNodes": [], 57 | "telemetryEndpoints": null, 58 | "protocolId": null, 59 | "properties": null, 60 | "consensusEngine": null, 61 | "genesis": { 62 | "runtime": { 63 | "system": { 64 | "code": "0x" 65 | }, 66 | "balances": { 67 | "balances": [ 68 | [ 69 | "5F25Yb9PB9GV6RcqKpWceyx1X48TfnBxWn9m84A1JUYvsEF2", 70 | 1000001803 71 | ], 72 | [ 73 | "5CnyWwRgxbjqPyi4y9zMFjUWPjgw7M1SLzzehHRnp4K52M1L", 74 | 1000001803 75 | ], 76 | [ 77 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs", 78 | 1000001803 79 | ], 80 | [ 81 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 82 | 1000001803 83 | ], 84 | [ 85 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 86 | 1000001803 87 | ] 88 | ] 89 | }, 90 | "aura": { 91 | "authorities": [ 92 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs" 93 | ] 94 | } 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | Converting spec file to raw: 101 | 102 | {% tabs %} 103 | {% tab title="Option 1" %} 104 | ```bash 105 | $ subsembly spec --src=./spec-files/chain-spec.json --raw=./spec-files/raw-chain-spec.json --wasm=./build/subsembly-wasm 106 | ``` 107 | {% endtab %} 108 | 109 | {% tab title="Option 2" %} 110 | ```bash 111 | $ subsembly spec --src=./spec-files/chain-spec.json 112 | ``` 113 | {% endtab %} 114 | 115 | {% tab title="Option 3" %} 116 | ```bash 117 | $ subsembly spec --src=./spec-files/chain-spec.json --raw=./ 118 | ``` 119 | {% endtab %} 120 | {% endtabs %} 121 | 122 | Either of these commands will produce the same output: 123 | 124 | {% hint style="warning" %} 125 | This raw spec file is strictly for demonstration purposes. 126 | {% endhint %} 127 | 128 | ```bash 129 | $ cat raw-chain-spec.json 130 | { 131 | "name": "Local Testnet", 132 | "id": "local_testnet", 133 | "chainType": "Local", 134 | "bootNodes": [], 135 | "telemetryEndpoints": null, 136 | "protocolId": null, 137 | "properties": null, 138 | "consensusEngine": null, 139 | "genesis": { 140 | "raw": { 141 | "top": { 142 | "0x3a636f6465": "0x00...12100", 143 | "0xc2261276cc9d1f8598ea4b6a74b15c2fb99d880ec681799c0cf30e8886371da9d03d56e603c165d01a5d30685d13a92a": "0x0bd19a3b00000000000000000000000000000000000000000000000000000000", 144 | "0xc2261276cc9d1f8598ea4b6a74b15c2fb99d880ec681799c0cf30e8886371da9e81ba99a0bb95d747f27a3ead08fe418": "0x0bd19a3b00000000000000000000000000000000000000000000000000000000", 145 | "0xc2261276cc9d1f8598ea4b6a74b15c2fb99d880ec681799c0cf30e8886371da9c6ebe5ded0e8dd4d0db5692e34d8116a": "0x0bd19a3b00000000000000000000000000000000000000000000000000000000", 146 | "0xc2261276cc9d1f8598ea4b6a74b15c2fb99d880ec681799c0cf30e8886371da9518366b5b1bc7c99bae0ba710af1ac66": "0x0bd19a3b00000000000000000000000000000000000000000000000000000000", 147 | "0xc2261276cc9d1f8598ea4b6a74b15c2fb99d880ec681799c0cf30e8886371da9a647e755c30521d3d8cb3b41eccb98ea": "0x0bd19a3b00000000000000000000000000000000000000000000000000000000", 148 | "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x04dcc1461cba689c60dcae053ef09bc9e9524cdceb696ce39c7ed43bf3a5fa9659" 149 | }, 150 | "childrenDefault": {} 151 | } 152 | } 153 | } 154 | ``` 155 | 156 | -------------------------------------------------------------------------------- /docs/getting-started/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Substrate + AssemblyScript = Subsembly 3 | --- 4 | 5 | # Overview 6 | 7 | **`Subsembly`** framework used for developing [**`Substrate`**](substrate-essentials.md) runtimes in **`AssemblyScript`**. The project is work in progress. 8 | 9 | The following diagram shows the high-level components Architecture of **`Subsembly:`** 10 | 11 | ![High-level overview](../.gitbook/assets/components_diagram.png) 12 | 13 | * **Runtime API** - Implementation of Node <> Runtime Entries 14 | * **Runtime Configuration** - Configurable runtime similar to **`Substrate`** Runtimes 15 | * **FRAME** - Runtime components that handle the administrative functionalities 16 | * **Pallets** - Packages that deliver common functionality, reused in different chains 17 | * **Core** - Runtime components that provide low-level functionality 18 | 19 | -------------------------------------------------------------------------------- /docs/getting-started/substrate-essentials.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Substrate knowledge base 3 | --- 4 | 5 | # Substrate Essentials 6 | 7 | ### Substrate 8 | 9 | **Substrate** takes a modular approach to blockchain development and defines a rich set of primitives that allows developers to make use of powerful, familiar programming idioms. 10 | 11 | For more information about Substrate, please refer to their in-depth [documentation](https://substrate.dev/). 12 | 13 | ### Runtime 14 | 15 | > The runtime of a blockchain is the business logic that defines its behavior. In Substrate-based chains, the runtime is referred to as the "[state transition function](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#state-transition-function-stf)"; it is where Substrate developers define the storage items that are used to represent the blockchain's [state](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#state) as well as the [functions](https://substrate.dev/docs/en/knowledgebase/learn-substrate/extrinsics) that allow blockchain users to make changes to this state. 16 | 17 | ### How is it related? 18 | 19 | Substrate can be used in three ways: 20 | 21 | * **With the Substrate Node -** run node with runtime just by providing chain specification file to node 22 | * **With Substrate FRAME -** run node with your custom runtime 23 | * **With Substrate Core -** design and implement runtime from scratch and compile targeting **`WebAssembly`**. This is what **`Subsembly`** utilises. 24 | 25 | Since **`Substrate`** supports **`WebAssembly`** runtimes, anything that compiles down to that is theoretically able to serve as a Substrate runtime. AssemblyScript is a language very similar to TypeScript that compiles down to WebAssembly and **`Subsembly`** is an AssemblyScript framework. 26 | 27 | -------------------------------------------------------------------------------- /docs/guides/create-a-new-module/README.md: -------------------------------------------------------------------------------- 1 | # Create a New Module 2 | 3 | ## Overview 4 | 5 | One of the core features of Subsembly is the ability to add new modules to your runtime easily. In this tutorial we will demonstrate how new modules are added and implemented in a Subsembly runtime. 6 | 7 | ## Basics 8 | 9 | Here are some of the concepts and constraints you need to know before starting with this guide: 10 | 11 | * New module is added as a new folder inside the **`assembly/pallets/`** folder. 12 | * Inside the folder, usually we place the file which defines the pallet: storage entries, extrinsic calls, etc. 13 | * It's important to place your modules in the above path, since metadata looks for modules specifically in this path 14 | * Types specific to the module should be added and exported from the **`assembly/runtime/runtime.ts`** file 15 | * If you have any runtime API entries specific to your module, define a file with your functions exported and place them in **`assembly/runtime/api/`** folder 16 | 17 | -------------------------------------------------------------------------------- /docs/guides/create-a-new-module/launch-the-node.md: -------------------------------------------------------------------------------- 1 | # Launch the Node 2 | 3 | ### Overview 4 | 5 | In this section, we will launch the node with our newly modified runtime. 6 | 7 | ### Compile 8 | 9 | First, we compile our new runtime: 10 | 11 | ```text 12 | subsembly compile 13 | ``` 14 | 15 | You can see that assembly/pallets/generated/dispatcher.ts file has changed. Namely, we now have a dispatch call for our new module: 16 | 17 | ```text 18 | /--snip--/ 19 | 20 | // Extrinsic calls of Nicks module 21 | export enum NicksCalls { 22 | set_name = 0 23 | } 24 | 25 | /--snip--/ 26 | 27 | export namespace Dispatcher { 28 | export function dispatch(ext: UncheckedExtrinsic): u8[] { 29 | /--snip--/ 30 | case Pallets.Nicks: { 31 | switch(ext.method.callIndex[1]) { 32 | case NicksCalls.set_name: { 33 | let bytesReader = new BytesReader(ext.method.args); 34 | const origin = bytesReader.readInto(); 35 | const name = bytesReader.readInto(); 36 | return Nicks.set_name(origin, name); 37 | } 38 | default: { 39 | return ResponseCodes.CALL_ERROR 40 | } 41 | } 42 | } 43 | } 44 | /--snip--/ 45 | } 46 | ``` 47 | 48 | ### Launch 49 | 50 | Now that we have newly compiled runtime, it's time to launch the node. But first, make sure to generate new raw spec file, since we made changes in our runtime: 51 | 52 | ```text 53 | subsembly spec --src=./chain-spec.json 54 | ``` 55 | 56 | And now, this will launch the node: 57 | 58 | ```text 59 | make run-node-demo spec=./raw-chain-spec.json 60 | ``` 61 | 62 | Also, make sure to insert you Aura keys, so that the node starts the block production. 63 | 64 | -------------------------------------------------------------------------------- /docs/guides/create-a-new-module/nicks-module.md: -------------------------------------------------------------------------------- 1 | # Nicks Module 2 | 3 | ### Overview 4 | 5 | Nicks is an example module for keeping track of account names on-chain. It makes no effort to create a name hierarchy, be a DNS replacement or provide reverse lookups. This module is only for demonstration purposes, so do not use this pallet as-is in production. 6 | 7 | ### Module 8 | 9 | First, we define the module inside the **`pallets/`** folder as a **`ts`** file. The path to the module should look similar to this: 10 | 11 | ```text 12 | assembly/ 13 | ... 14 | pallets/ 15 | ... 16 | nicks/ 17 | nicks.ts 18 | ... 19 | ``` 20 | 21 | #### Storage entries 22 | 23 | Now we define storage entries inside **`nicks.ts`** for our module like this: 24 | 25 | ```text 26 | /** 27 | * @description Nicks Pallet storage entries 28 | */ 29 | export namespace NicksStorageEntries { 30 | /** 31 | * @description Name of AccountId 32 | * @storage_map AccountId 33 | */ 34 | export function NameOf(): StorageEntry{ 35 | return new StorageEntry("Nicks", "NameOf"); 36 | }; 37 | } 38 | ``` 39 | 40 | Some interesting details: 41 | 42 | * NicksStorageEntries - a namespace where we define storage entries as functions. It's required to be a namespace and named accordingly: **`moduleName + 'StorageEntries'`** 43 | * **`storage_map AccountId -`** specifies the type of storage entry \(Map\) and type of argument it receives \(AccountId\) 44 | * **`StorageEntry`** - An object that takes the type of value to be stored. And also module name and the entry name. 45 | 46 | #### Constants 47 | 48 | Sometimes your modules may require you to set constants that will be used inside the module. In **Subsembly**, constants are defined as a static function of a class, inside the **`assembly/runtime/runtime.ts`** file like this: 49 | 50 | ```text 51 | /--snip--/ 52 | 53 | export class NicksConfig { 54 | /** 55 | * @description Minimum length of the name 56 | * @returns min value 57 | */ 58 | static minLength(): UInt32 { 59 | return new UInt32(3); 60 | } 61 | 62 | /** 63 | * @description Maximum length of the name 64 | * @returns max value 65 | */ 66 | static maxLength(): UInt32 { 67 | return new UInt32(16); 68 | } 69 | } 70 | /--snip--/ 71 | ``` 72 | 73 | ### Module Calls 74 | 75 | And now we will define the core business logic of the module. It may include extrinsic calls that are exposed outside of the runtime and other internal function and constants. We define our Nicks module like this: 76 | 77 | ```text 78 | /--snip--/ 79 | /** 80 | * @description Nicks modules declaration 81 | */ 82 | export class Nicks { 83 | /** 84 | * @description Error code for too short name 85 | */ 86 | static TOO_SHORT_ERROR: u8 = 0; 87 | /** 88 | * @description Error code for too long name 89 | */ 90 | static TOO_LONG_ERROR: u8 = 1; 91 | 92 | /** 93 | * @description Sets name of the origin 94 | * @param origin AccountId 95 | * @param name Name of the account 96 | * @returns 97 | */ 98 | static set_name(origin: AccountIdType, name: ScaleString): u8[] { 99 | if (name.unwrap().length <= NicksConfig.minLength().unwrap()) { 100 | Log.error("Nicks: Name too short!"); 101 | return; 102 | } 103 | else if(name.unwrap().length >= NicksConfig.maxLength().unwrap()) { 104 | Log.error("Nicks: Name too long!"); 105 | return; 106 | } 107 | NicksStorageEntries.NameOf().set(name, origin); 108 | } 109 | } 110 | /--snip--/ 111 | ``` 112 | 113 | Here we have defined only one dispatchable call of our module. Note that, extrinsic call is a static function and returns array of bytes: code for the result of the extrinsic. 114 | 115 | * We define local error codes for the module as a static property 116 | * **`set_name`** - Sets the provided name for the given origin **`AccountId`** 117 | 118 | {% hint style="info" %} 119 | Don't forget to include your new module for exports inside **`assembly/pallets/index.ts !`** 120 | {% endhint %} 121 | 122 | ```text 123 | /--snip--/ 124 | export * from "./nicks/nicks"; 125 | /--snip--/ 126 | ``` 127 | 128 | -------------------------------------------------------------------------------- /docs/guides/create-a-new-module/polkadotjs.md: -------------------------------------------------------------------------------- 1 | # PolkadotJs 2 | 3 | ### Query storage 4 | 5 | Run the PolkadotJs in previous guides and open the **Chain State** section of the **Developer** tab. You should see something similar to this: 6 | 7 | ![Picture 1. Chain state](../../.gitbook/assets/screenshot-2021-04-01-at-16.47.08.png) 8 | 9 | We can query the chain, but it will be empty. 10 | 11 | ### Extrinsics 12 | 13 | In the same Developer tab switch to **Extrinsics** section. Pick **`Nicks`** module and select the **`setName`** extrinsic. Pick an **Account \(origin\)** to give the name. Submit the extrinsic and see if the extrinsic is approved. 14 | 15 | ![Picture 2. Setting new name](../../.gitbook/assets/screenshot-2021-04-02-at-18.01.17.png) 16 | 17 | You can also go back to the **Chain State** section and query the storage for the above account. 18 | 19 | ![Picture 3. Query name](../../.gitbook/assets/screenshot-2021-04-02-at-18.03.02.png) 20 | 21 | -------------------------------------------------------------------------------- /docs/guides/create-your-first-subsembly-runtime/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Guide for running your Subsembly runtime with Substrate runtime 3 | --- 4 | 5 | # Create Your First Subsembly Runtime 6 | 7 | ## Introduction 8 | 9 | In this guide, you will learn how to initialise a new **Subsembly** project, compile, and interact with it using **PolkadotJS** apps interface. 10 | 11 | This guide is aimed at someone who is new to **Subsembly** and wants to quickly get started with it. No deep knowledge about Substrate is required to complete this guide. 12 | 13 | Some prerequisites for completing this guide: 14 | 15 | * General familiarity with software development, specifically in **Typescript** or **NodeJS** 16 | * General familiarity with core blockchain concepts 17 | 18 | ## Outline 19 | 20 | Before we get started, here's the outline of the guide: 21 | 22 | 1. Set up your computer to be able to develop with **Subsembly**. 23 | 2. Use **Subsembly** starter project to run a **Subsembly** runtime with **Substrate** node. 24 | 3. Use **PolkadotJS** apps to interact with your runtime. 25 | 26 | -------------------------------------------------------------------------------- /docs/guides/create-your-first-subsembly-runtime/polkadotjs.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Interact with your runtime with PolkadotJS 3 | --- 4 | 5 | # PolkadotJS 6 | 7 | ## Interact 8 | 9 | Once your node is running, go to PolkadotJS interface at [https://polkadot.js.org/apps](https://polkadot.js.org/apps). PolkadotJS interface provides an interface for interacting with your node. You can explore the produced blocks, query the chain storage, submit extrinsics, etc. In this section we will explore various ways to interact with your runtime. 10 | 11 | ## Insert Aura keys 12 | 13 | One other option to insert your Aura keys is using PolkadotJS: 14 | 15 | Fill out the necessary fields and submit the `rpc` call. 16 | 17 | ![Picture 1. Inserting Aura keys](../../.gitbook/assets/screenshot-2021-03-23-at-17.32.55.png) 18 | 19 | ## Accounts 20 | 21 | In the accounts tab, you can explore list of accounts in the storage: 22 | 23 | ![Picture 2. Accounts tab](../../.gitbook/assets/screenshot-2021-03-23-at-17.40.28.png) 24 | 25 | ## Transfers 26 | 27 | Go to the **Extrinsics** section of the **Developer** tab and select **`transfer`** extrinsic call: 28 | 29 | ![Picture 3. Balances transfer](../../.gitbook/assets/screenshot-2021-04-02-at-1.44.48.png) 30 | 31 | Fill up the fields and submit the transaction. The next window will look similar to this: 32 | 33 | ![Picture 4. Transaction authorization](../../.gitbook/assets/screenshot-2021-04-02-at-1.45.59.png) 34 | 35 | Click **Sign and Submit** to send the transfer and in couple of seconds, you will get a notification about the status of your transaction: 36 | 37 | ![Picture 5. Successful transaction example](../../.gitbook/assets/screenshot-2021-04-02-at-1.47.21.png) 38 | 39 | ## Others 40 | 41 | You can see the block issuance and the details of the produced blocks. Querying the state of the chain can be done in the `Developer` -> `Chain state` 42 | 43 | -------------------------------------------------------------------------------- /docs/guides/create-your-first-subsembly-runtime/set-up.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Instructions to set up your computer 3 | --- 4 | 5 | # Set Up 6 | 7 | ## Prerequisites 8 | 9 | Your set up needs to meet the following requirements : 10 | 11 | * **`Node >= 14.0.0`** 12 | * **`make`** and **`curl`** packages 13 | 14 | ## Getting Started 15 | 16 | To initialise and compile the runtime, follow the steps in the [**Runtime Development**](../../development/development.md) section. The following steps should be completed before the next step of the guide: 17 | 18 | Initialise a new **Subsembly** project: 19 | 20 | ```text 21 | subsembly init --to=./subsembly-starter 22 | ``` 23 | 24 | Compile your runtime: 25 | 26 | ```text 27 | cd subsembly-starter 28 | subsembly compile 29 | ``` 30 | 31 | Generate new chain specification file: 32 | 33 | ```text 34 | subsembly spec --to=./chain-spec.json 35 | ``` 36 | 37 | Here, you will need to replace or add a new Aura authority in the chain spec file. For generating your own keys, refer to **Substrate** [tutorial](https://substrate.dev/docs/en/tutorials/start-a-private-network/keygen). By default chain spec comes with generic Alice address. If you want a different authority, just add the public address of authority inside the array of authorities: 38 | 39 | ```text 40 | "aura": { 41 | "authorities": [ 42 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs" 43 | ] 44 | } 45 | ``` 46 | 47 | {% hint style="info" %} 48 | The last step is very important, since you will have to insert your Aura keys in the next chapter. 49 | {% endhint %} 50 | 51 | Convert it to raw: 52 | 53 | ```text 54 | subsembly spec --src=./chain-spec.json --raw=./raw-chain-spec.json --wasm=./build/subsembly-wasm 55 | ``` 56 | 57 | To learn more about the above commands, please refer to [**Development**](../../development/development.md) section. After you have successfully completed above steps, go to the next step to see how you can interact with your runtime. 58 | 59 | -------------------------------------------------------------------------------- /docs/guides/create-your-first-subsembly-runtime/start-the-node.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Starting your node 3 | --- 4 | 5 | # Start the Node 6 | 7 | ## Subsembly Development Node 8 | 9 | We have a compiled, minimal **Substrate** node that we use for development and testing purposes. It is a modified version of [**substrate-node-template**](https://github.com/substrate-developer-hub). It uses **Aura** consensus for block production, and has support for **Balances** and **Timestamp** modules. 10 | 11 | ## Starting Your Node 12 | 13 | In the root directory of the project, there is a **Makefile** with useful commands to run the Substrate node. Make sure to install **`make, curl`** packages, if you don't have them installed. 14 | 15 | {% tabs %} 16 | {% tab title="MacOS" %} 17 | ```text 18 | brew install make curl 19 | ``` 20 | {% endtab %} 21 | 22 | {% tab title="Linux" %} 23 | ```text 24 | apt-get install make curl 25 | ``` 26 | {% endtab %} 27 | {% endtabs %} 28 | 29 | After we have the required packages installed, we can run **Substrate** node with our runtime: 30 | 31 | ```text 32 | make run-node-demo spec=./raw-chain-spec.json 33 | ``` 34 | 35 | {% hint style="info" %} 36 | The provided Chain spec file must be in raw format 37 | {% endhint %} 38 | 39 | Now, you will notice that your node is running, but no blocks are being produced. At this point you should insert your keys into the keystore. In our case, you need to insert **Aura** keys that are needed for block production: 40 | 41 | ```text 42 | curl --location --request POST '0.0.0.0:9933' \ 43 | --header 'Content-Type: application/json' \ 44 | --data-raw '{ 45 | "jsonrpc": "2.0", 46 | "method": "author_insertKey", 47 | "params": ["aura","dice height enter anger ahead chronic easily wave curious banana era happy","0xdcc1461cba689c60dcae053ef09bc9e9524cdceb696ce39c7ed43bf3a5fa9659"], 48 | "id": 1 49 | }' 50 | ``` 51 | 52 | If you receive this result, you have successfully inserted your Aura keys to the keystore. 53 | 54 | ```text 55 | {"jsonrpc":"2.0","result":null,"id":1} 56 | ``` 57 | 58 | Now, your node should start producing blocks: 59 | 60 | ```text 61 | Mar 23 13:35:15.524 INFO Substrate Node 62 | Mar 23 13:35:15.524 INFO ✌️ version 2.0.0-unknown-x86_64-linux-gnu 63 | Mar 23 13:35:15.524 INFO ❤️ by Substrate DevHub , 2017-2021 64 | Mar 23 13:35:15.525 INFO 📋 Chain specification: Local Testnet 65 | Mar 23 13:35:15.525 INFO 🏷 Node name: Node01 66 | Mar 23 13:35:15.525 INFO 👤 Role: AUTHORITY 67 | Mar 23 13:35:15.525 INFO 💾 Database: RocksDb at /tmp/node01/chains/local_testnet/db 68 | Mar 23 13:35:15.525 INFO ⛓ Native runtime: node-template-1 (node-template-1.tx1.au1) 69 | Mar 23 13:35:15.966 INFO 🔨 Initializing Genesis block/state (state: 0x7f78…b9f9, header-hash: 0x2df5…f598) 70 | Mar 23 13:35:16.020 INFO ⏱ Loaded block-time = 5000 milliseconds from genesis on first-launch 71 | Mar 23 13:35:16.021 WARN Using default protocol ID "sup" because none is configured in the chain specs 72 | Mar 23 13:35:16.030 INFO 🏷 Local node identity is: 12D3KooWKQvopYxZ1wRh5rgxY5gAzJBuknSmJwS5ThovJNhQyqpy (legacy representation: 12D3KooWKQvopYxZ1wRh5rgxY5gAzJBuknSmJwS5ThovJNhQyqpy) 73 | Mar 23 13:35:16.054 INFO 📦 Highest known block at #0 74 | Mar 23 13:35:16.058 INFO 〽️ Prometheus server started at 127.0.0.1:9615 75 | Mar 23 13:35:16.067 INFO Listening for new connections on 0.0.0.0:9944. 76 | Mar 23 13:35:41.041 INFO 💤 Idle (0 peers), best: #0 (0x2df5…f598), finalized #0 (0x2df5…f598), ⬇ 0 ⬆ 0 77 | Mar 23 13:35:46.041 INFO 💤 Idle (0 peers), best: #0 (0x2df5…f598), finalized #0 (0x2df5…f598), ⬇ 0 ⬆ 0 78 | Mar 23 13:35:50.026 INFO 🙌 Starting consensus session on top of parent 0x2df51d274299e45bf7e7fe937c42c3789015a562fbbfaf78d043a59f64a1f598 79 | Mar 23 13:35:50.081 INFO 🎁 Prepared block for proposing at 1 [hash: 0xc53e5594014ab33e4e3fed08704227604d7aa54019ad6edd0d7ad37d3091d238; parent_hash: 0x2df5…f598; extrinsics (1): [0x02bc…02c2]] 80 | Mar 23 13:35:50.085 INFO 🔖 Pre-sealed block for proposal at 1. Hash now 0xcbc9496220d89f2efb0ddd344710e76534a9cec9943ce9d4d7f0696882a3d8b4, previously 0xc53e5594014ab33e4e3fed08704227604d7aa54019ad6edd0d7ad37d3091d238. 81 | Mar 23 13:35:50.086 INFO ✨ Imported #1 (0xcbc9…d8b4) 82 | Mar 23 13:35:51.010 INFO 💤 Idle (0 peers), best: #1 (0xcbc9…d8b4), finalized #0 (0x2df5…f598), ⬇ 0 ⬆ 0 83 | Mar 23 13:35:55.024 INFO 🙌 Starting consensus session on top of parent 0xcbc9496220d89f2efb0ddd344710e76534a9cec9943ce9d4d7f0696882a3d8b4 84 | Mar 23 13:35:55.079 INFO 🎁 Prepared block for proposing at 2 [hash: 0xa8d90c9d83bf5be8d4b5f6b83d65dbf2bee04bb4811159d571e5275be6c839d8; parent_hash: 0xcbc9…d8b4; extrinsics (1): [0xb620…d531]] 85 | Mar 23 13:35:55.082 INFO 🔖 Pre-sealed block for proposal at 2. Hash now 0x1c4239f2f39ba23d38d76ced5168932d4ca9e9f3c2ce1ba40a7b252f506e8d7b, previously 0xa8d90c9d83bf5be8d4b5f6b83d65dbf2bee04bb4811159d571e5275be6c839d8. 86 | Mar 23 13:35:55.083 INFO ✨ Imported #2 (0x1c42…8d7b) 87 | Mar 23 13:35:56.011 INFO 💤 Idle (0 peers), best: #2 (0x1c42…8d7b), finalized #0 (0x2df5…f598), ⬇ 0 ⬆ 0 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /docs/guides/start-your-network/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Network of two Substrate nodes 3 | --- 4 | 5 | # Start Your Network 6 | 7 | ## Overview 8 | 9 | In this tutorial, you will learn how to start a two **Substrate** node network running **Subsembly** runtimes. 10 | 11 | ## Outline 12 | 13 | Before we get started, here's the outline of the what we are going to do in this guide: 14 | 15 | 1. Launch a Substrate blockchain network with **Subsembly** runtime. 16 | 2. Create key pairs to be used as network authorities. 17 | 3. Create, edit the chain spec file and launch the nodes. 18 | 19 | -------------------------------------------------------------------------------- /docs/guides/start-your-network/generate-authority-keys.md: -------------------------------------------------------------------------------- 1 | # Prepare the Network 2 | 3 | ## Overview 4 | 5 | Each node who wants to participate as validator in the blockchain network must generate their own private/public keys. In this section, we will generate two keys for the 2 validator nodes - `Alice` and `Bob`. 6 | 7 | ## Option 1: Subkey 8 | 9 | **`Subkey`** is a tool that generates keys specifically designed to be used with Substrate. The installation instructions can be found [here](https://substrate.dev/docs/en/knowledgebase/integrate/subkey). 10 | 11 | After you've installed the tool, it's time to generate the validator keys. We need to generate **`sr25519`** keys that will be used by Aura for block production. 12 | 13 | ```text 14 | subkey generate --scheme sr25519 15 | ``` 16 | 17 | You should see output similar to this: 18 | 19 | ```text 20 | Secret phrase `lady demand candy vacuum warm nurse shaft garment horror list burst strike` is account: 21 | Secret seed: 0xfd3a2bd1b11943302b4a007857189629be5ae7ba55bf90a15f6eca939d6c763b 22 | Public key (hex): 0xec9fd69c119fb45b6f6efca397db3e864649e6903cf227d4609ed53a66d3bf1e 23 | Account ID: 0xec9fd69c119fb45b6f6efca397db3e864649e6903cf227d4609ed53a66d3bf1e 24 | SS58 Address: 5HQxe4hw4bZm5uK4kUeq3Wkvw7Uem7NesYjB53BjAUizNZN6 25 | ``` 26 | 27 | **Important** 28 | 29 | Make sure to save your mnemonic phrase, public key and SS58 address variables as they will be used later. 30 | 31 | Perform the key generation twice, for both of the nodes and save the mnemonic, public key and SS58 address for the second generation as-well. 32 | 33 | ## Option 2: PolkadotJs 34 | 35 | You can also generate your own keys using **PolkadotJs** interface. In the **Accounts** tab, select **Add Account** button. This will generate **`sr25519`** keys by default. Again, make sure to save your mnemonic seed and public key. 36 | 37 | For example: 38 | 39 | ![Picture 1. Account generation](../../.gitbook/assets/screenshot-2021-03-31-at-18.00.40.png) 40 | 41 | ### Modify the Chain Spec 42 | 43 | We have already done this step in the last [guide](../create-your-first-subsembly-runtime/) and in the [Development](../../development/development.md) section. So the default content of **`chain-spec.json`** looks like this: 44 | 45 | ```text 46 | //--snip--// 47 | "runtime": { 48 | "system": { 49 | "code": "0x" 50 | }, 51 | "aura": { 52 | "authorities": [ 53 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs" 54 | ] 55 | } 56 | } 57 | //--snip--// 58 | ``` 59 | 60 | **`aura.authorities`** property in the chain spec defines the list of public keys or authorities that have the right to produce blocks. Delete the already specified authority in the list and add the 2 SS58 addresses that you generated 61 | 62 | ```text 63 | //--snip--/ 64 | "runtime": { 65 | "system": { 66 | "code": "0x" 67 | }, 68 | "aura": { 69 | "authorities": [ 70 | "5H49oi57ktRnYTbhVtKpGGk79rB9QXNcApYELLWcKa9W8nfs", 71 | "5HQxe4hw4bZm5uK4kUeq3Wkvw7Uem7NesYjB53BjAUizNZN6" 72 | ] 73 | } 74 | } 75 | //--snip--/ 76 | ``` 77 | 78 | And don't forget to convert your modified chain spec into raw: 79 | 80 | ```text 81 | subsembly spec --src=./chain-spec.json 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /docs/guides/start-your-network/launch-the-third-node.md: -------------------------------------------------------------------------------- 1 | # Launch the Third Node 2 | 3 | ### Start Third Node 4 | 5 | In the last step we launched two validating nodes. In this one, we launch the third one, non-validating validator node. 6 | 7 | ```text 8 | make run-node \ 9 | PORT=30335 \ 10 | WS-PORT=9946 \ 11 | RPC-PORT=9935 \ 12 | spec=raw-chain-spec.json \ 13 | NAME=node03 \ 14 | --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp /ip4/172.17.0.3/tcp/30334/p2p/12D3KooWE2e8yccz2skbaKmHnizcgY2vGyuUBehXbDnYok3Ze59A 15 | ``` 16 | 17 | You can connect and interact with the node using PolkadotJs similar to other nodes. And you should see output similar to this: 18 | 19 | ```text 20 | Apr 01 08:07:04.405 INFO Substrate Node 21 | Apr 01 08:07:04.405 INFO ✌️ version 2.0.0-unknown-x86_64-linux-gnu 22 | Apr 01 08:07:04.405 INFO ❤️ by Substrate DevHub , 2017-2021 23 | Apr 01 08:07:04.405 INFO 📋 Chain specification: Local Testnet 24 | Apr 01 08:07:04.405 INFO 🏷 Node name: node03 25 | Apr 01 08:07:04.405 INFO 👤 Role: FULL 26 | Apr 01 08:07:04.405 INFO 💾 Database: RocksDb at /tmp/node03/chains/local_testnet/db 27 | Apr 01 08:07:04.405 INFO ⛓ Native runtime: node-template-1 (node-template-1.tx1.au1) 28 | Apr 01 08:07:04.540 INFO 🔨 Initializing Genesis block/state (state: 0xb60f…8962, header-hash: 0x37c1…c164) 29 | Apr 01 08:07:04.566 INFO ⏱ Loaded block-time = 5000 milliseconds from genesis on first-launch 30 | Apr 01 08:07:04.567 WARN Using default protocol ID "sup" because none is configured in the chain specs 31 | Apr 01 08:07:04.567 INFO 🏷 Local node identity is: 12D3KooWH8ys3NdwcXs1svyZxMpAdqzrbLkuwT9EW4WYXTQf1mgq (legacy representation: 12D3KooWH8ys3NdwcXs1svyZxMpAdqzrbLkuwT9EW4WYXTQf1mgq) 32 | Apr 01 08:07:04.569 INFO 📦 Highest known block at #0 33 | Apr 01 08:07:04.569 INFO 〽️ Prometheus server started at 127.0.0.1:9615 34 | Apr 01 08:07:04.572 INFO Listening for new connections on 0.0.0.0:9946. 35 | Apr 01 08:07:05.091 INFO 🔍 Discovered new external address for our node: /ip4/172.17.0.4/tcp/30335/p2p/12D3KooWH8ys3NdwcXs1svyZxMpAdqzrbLkuwT9EW4WYXTQf1mgq 36 | Apr 01 08:07:09.574 INFO 💤 Idle (2 peers), best: #0 (0x37c1…c164), finalized #0 (0x37c1…c164), ⬇ 1.9kiB/s ⬆ 1.9kiB/s 37 | Apr 01 08:07:14.576 INFO 💤 Idle (2 peers), best: #0 (0x37c1…c164), finalized #0 (0x37c1…c164), ⬇ 0 ⬆ 0 38 | Apr 01 08:07:19.546 INFO 💤 Idle (2 peers), best: #0 (0x37c1…c164), finalized #0 (0x37c1…c164), ⬇ 0 ⬆ 0 39 | ``` 40 | 41 | Then your node should start the syncing process with the other nodes and start executing the blocks, without taking part in the production. 42 | 43 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This is the starter project for the `Subsembly` framework used for developing `Substrate` runtimes in `AssemblyScript`. The project is work in progress. 4 | 5 | -------------------------------------------------------------------------------- /docs/modules/core/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Core modules 3 | --- 4 | 5 | # Core 6 | 7 | ### Introduction 8 | 9 | There are some core modules that are critical and necessary for every **`Subsembly`** runtime. Each module has its specific purpose that is essential for the functioning of runtime. These modules are: 10 | 11 | * **`System`** 12 | * **`Executive`** 13 | * **`Log`** 14 | * **`Crypto`** 15 | 16 | -------------------------------------------------------------------------------- /docs/modules/core/crypto.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Cryptographic utility functions 3 | --- 4 | 5 | # Crypto 6 | 7 | ### Overview 8 | 9 | In **`subsembly-core`** we have another essential module that contains various cryptographic utility functions. For example, for verifying the signature of the transaction, we use **`verify`** function of **`Crypto`** module. 10 | 11 | -------------------------------------------------------------------------------- /docs/modules/core/executive.md: -------------------------------------------------------------------------------- 1 | # Executive 2 | 3 | ### Overview 4 | 5 | Acts as the orchestration layer for the runtime. Receives incoming extrinsic calls, decodes them and dispatches the calls to the respective modules of the runtime. It uses **`System`** library for accessing the low-level storage and performing some important actions during the block production phase. 6 | 7 | ### Example 8 | 9 | Runtime API entry **`Core_initialize_block`**calls **`initializeBlock`** function of the **`Executive`** module. 10 | 11 | ```typescript 12 | export function Core_initialize_block(data: i32, len: i32): u64 { 13 | const input = Serialiser.deserialiseInput(data, len); 14 | const header = BytesReader.decodeInto(input); 15 | Executive.initializeBlock(header); 16 | return Serialiser.serialiseResult([]); 17 | } 18 | ``` 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/modules/core/log.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Logging module 3 | --- 4 | 5 | # Log 6 | 7 | ### Overview 8 | 9 | Log module is used for displaying log messages to the host. The three types of messages are supported: **`info`**, **`warn`** and **`error`**. This module is defined in **`subsembly-core`** package and should be imported from it before using. 10 | 11 | ### Examples 12 | 13 | ```text 14 | import {Log} from 'subsembly-core' 15 | 16 | // log informing message 17 | Log.info("Important message"); 18 | 19 | // log warning message 20 | Log.warn("Warning!); 21 | 22 | // log error message 23 | Log.error("Error here!"); 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /docs/modules/core/untitled.md: -------------------------------------------------------------------------------- 1 | # System 2 | 3 | ### Overview 4 | 5 | System module provides low-level types, storage, and functions for your blockchain. All other pallets depend on the **`System`** library as the basis of your **`Subsembly`** runtime. 6 | 7 | System module defines critical storage items for your blockchain. Some of them are: 8 | 9 | * **`Account`** - stores account balance and nonce 10 | * **`ExtrinsicCount`** - total extrinsics count for current block 11 | * **`ParentHash`** - hash of the previous block 12 | * **`Events`** - vector of events during block execution 13 | 14 | And finally, it contains some important functions that access the storage, compute the extrinsics root, verify the origin of extrinsic, etc. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/modules/pre-built/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Modules ready for use 3 | --- 4 | 5 | # Pre-built 6 | 7 | ### Overview 8 | 9 | Modules are represented as an AS class and are used to compose runtimes. Each module has its own logic and can modify the functionality of the state transition function of the blockchain. 10 | 11 | It is possible to write your own custom module, provided that you implement all the necessary API entries. 12 | 13 | One example is a Timestamp module. It is used to set and get on-chain time. More specifically, it sets the storage items for the module, such as **`Now, DidUpdate`** that help saving and retrieving on-chain time of the blockchain. 14 | 15 | To ease and shorten the development time of the runtimes, **`Subsembly`** currently comes with already pre-built modules that could be easily integrated with the runtime. 16 | 17 | * **`Aura`** 18 | * **`Balances`** 19 | * **`Timestamp`** 20 | * **`TransactionPayment`** 21 | 22 | -------------------------------------------------------------------------------- /docs/modules/pre-built/aura.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Wrapper module for consensus 3 | --- 4 | 5 | # Aura 6 | 7 | ### Overview 8 | 9 | The Aura module extends Aura consensus by providing set of utility functions. 10 | 11 | ### Consensus 12 | 13 | Aura, also known as Authority-round, is a consensus in Substrate. It works by providing list of authorities who are expected to roughly agree on the current time. Aura works by having a list of authorities A who are expected to roughly agree on the current time. Time is divided up into discrete slots of t seconds each. For each slot **`s`**, the author of that slot is **`A[s % |A|]`** 14 | 15 | The author is allowed to issue one block but not more during that slot, and it will be built upon the longest valid chain that has been seen. 16 | 17 | Blocks from future steps will be either deferred or rejected depending on how far in the future they are. 18 | 19 | NOTE: Aura itself is designed to be generic over the crypto used. 20 | 21 | For more, visit Aura [docs](https://substrate.dev/docs/en/knowledgebase/runtime/frame). 22 | 23 | ### Functions 24 | 25 | * **`_getSlotDuration(): Moment`** - gets the configured Slot Duration 26 | * **`_getAuthorities(): u8[]`** - gets list of Authorities as SCALE encoded bytes 27 | * **`_checkInherent(t: Moment, data: InherentData): bool` -** verifies the validity of the inherent using the timestamp 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/modules/pre-built/balances.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Account balance manipulation 3 | --- 4 | 5 | # Balances 6 | 7 | ### Overview 8 | 9 | The Balances module provides functionality for manipulating accounts and balances. It provides functionality to: 10 | 11 | * Get and set account balances 12 | * Retrieving free and reserved balances 13 | * Validating Balance transfers 14 | * Creating accounts 15 | * Transferring an amount between accounts \(only free balance\) 16 | 17 | ### Dispatchable Calls 18 | 19 | Balances module exposes following dispatchable calls that could be included in the extrinsic: 20 | 21 | * **`transfer`** - transfer some liquid free balance to another account. 22 | * **`setBalance`** - set the balances of a given account. 23 | 24 | ### Configuration 25 | 26 | The initial accounts and their token balances are defined in genesis configuration. Also, some constants are defined for the Balances module: 27 | 28 | * **`existentialDeposit`** - the minimum balance an account may have 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/modules/pre-built/timestamp.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Get and set on-chain time 3 | --- 4 | 5 | # Timestamp 6 | 7 | ### Overview 8 | 9 | The Timestamp pallet allows the validators to set and validate a timestamp with each block. 10 | 11 | It uses **`inherents`** for timestamp data, which is provided by the block author and validated/verified by other validators. The timestamp can be set only once per block and must be set each block. There could be a constraint on how much time must pass before setting the new timestamp. 12 | 13 | ### Dispatchable Calls 14 | 15 | * **`set`** - sets the current time. When setting the new time, it must be greater than the last one \(set into storage\) with at least a MinimumPeriod 16 | 17 | ### Configuration 18 | 19 | Timestamp requires to set following constants during the runtime initialisation: 20 | 21 | * **`minimumPeriod`** - minimum period between timestamps. Also, minimal interval between two blocks. 22 | 23 | -------------------------------------------------------------------------------- /docs/modules/pre-built/transactionpayment.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Handle transaction payments 3 | --- 4 | 5 | # TransactionPayment 6 | 7 | ## Overview 8 | 9 | The Transaction Payment pallet provides the basic logic to compute pre-dispatch transaction fees. It provides a functionality to compute the absolute minimum transaction fee to be charged for extrinsic to be included in the block. 10 | 11 | ## Configuration 12 | 13 | This module requires the following constants to be set during runtime initialisation: 14 | 15 | * **`ExtrinsicBaseWeight`** - the base weight of an Extrinsic in the block, independent of the of extrinsic being executed. 16 | * **`NextFeeMultiplier`** - multiplier value for the next fee 17 | * **`TransactionByteFee` -** fee for each byte of a transaction 18 | 19 | -------------------------------------------------------------------------------- /images/components_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/images/components_diagram.png -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/web3_badge_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LimeChain/subsembly/a8528f4270fe719561a80c9559fb4176546f4b04/images/web3_badge_black.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subsembly-framework", 3 | "version": "1.0.0", 4 | "description": "Starter project for Subsembly framework for building Substrate runtimes in AssemblyScript", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "asc test", 8 | "asbuild:untouched": "asc assembly/index.ts -b build/runtime-untouched.wasm -t build/runtime-untouched.wat --runtime half --memoryBase=16777216 --importMemory --noAssert --use abort=", 9 | "asbuild:optimized": "asc assembly/index.ts -b build/runtime-optimized.wasm -t build/runtime-optimized.wat --runtime half --memoryBase=16777216 --importMemory --noAssert --optimize --use abort=", 10 | "build": "npm install --prefix ./cli && npm run build --prefix ./cli && node ./cli/dist/src/index.js compile" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/limechain/subsembly.git" 15 | }, 16 | "keywords": [ 17 | "substrate", 18 | "assemblyscript" 19 | ], 20 | "author": "Dastanbek Samatov ", 21 | "license": "Apache-2.0", 22 | "bugs": { 23 | "url": "https://github.com/limechain/subsembly/issues" 24 | }, 25 | "homepage": "https://github.com/limechain/subsembly#readme", 26 | "dependencies": { 27 | "as-bignum": "^0.2.6", 28 | "as-scale-codec": "0.2.3", 29 | "subsembly-core": "^1.1.0" 30 | }, 31 | "devDependencies": { 32 | "assemblyscript": "^0.17.1" 33 | } 34 | } 35 | --------------------------------------------------------------------------------