├── .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 | [](https://opensource.org/licenses/Apache-2.0) 
8 | 
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 |
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 | 
44 |
45 | 
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 | 
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 | 
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 | 
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 | 
16 |
17 | You can also go back to the **Chain State** section and query the storage for the above account.
18 |
19 | 
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 | 
18 |
19 | ## Accounts
20 |
21 | In the accounts tab, you can explore list of accounts in the storage:
22 |
23 | 
24 |
25 | ## Transfers
26 |
27 | Go to the **Extrinsics** section of the **Developer** tab and select **`transfer`** extrinsic call:
28 |
29 | 
30 |
31 | Fill up the fields and submit the transaction. The next window will look similar to this:
32 |
33 | 
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 | 
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 | 
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 |
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 |
--------------------------------------------------------------------------------