mirror of https://github.com/status-im/nimplay.git
commit
a18f3ce052
|
@ -3,4 +3,7 @@ tools/wabt/*
|
|||
tools/abi_gen
|
||||
examples/*.wasm
|
||||
.priv_key_hex
|
||||
*.wasm
|
||||
tests/ee/ewasm-scout/
|
||||
tests/substrate/node_modules/
|
||||
|
||||
|
|
58
Makefile
58
Makefile
|
@ -9,18 +9,28 @@ NLVM_WAMS32_FLAGS= --nlvm.target=wasm32 --gc:none -l:--no-entry -l:--allow-undef
|
|||
DOCKER_NLVM_C=$(DOCKER_NLVM) $(PATH_PARAMS) $(NLVM_WAMS32_FLAGS) c
|
||||
# Use nim + clang
|
||||
DOCKER_NIM_CLANG=docker run -e HOME='/tmp/' --user $(user_id):$(user_id) -w /code/ -v $(pwd):/code/ --entrypoint="/usr/bin/nim" jacqueswww/nimclang --verbosity:2
|
||||
DOCKER_NIM_CLANG_PASS_FLAGS = --passC:"--target=wasm32-unknown-unknown-wasm" \
|
||||
--passL:"--target=wasm32-unknown-unknown-wasm" --passC:"-I./include" --clang.options.linker:"-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all,--export-dynamic"
|
||||
DOCKER_NIM_CLANG_FLAGS=$(DOCKER_NIM_CLANG_PASS_FLAGS) --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release
|
||||
CLANG_OPTIONS_LINKER=-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all,--export-dynamic
|
||||
|
||||
# Ewasm
|
||||
DOCKER_NIM_CLANG_C=$(DOCKER_NIM_CLANG) --cc:clang $(PATH_PARAMS) c
|
||||
DOCKER_NIM_CLANG_WASM32_C=$(DOCKER_NIM_CLANG) $(DOCKER_NIM_CLANG_FLAGS) $(PATH_PARAMS) c
|
||||
DOCKER_NIM_CLANG_PASS_FLAGS_EWASM = --passC:"--target=wasm32-unknown-unknown-wasm" \
|
||||
--passL:"--target=wasm32-unknown-unknown-wasm" --passC:"-I./include" --clang.options.linker:"$(CLANG_OPTIONS_LINKER)"
|
||||
DOCKER_NIM_CLANG_FLAGS_EWASM=$(DOCKER_NIM_CLANG_PASS_FLAGS_EWASM) --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release
|
||||
DOCKER_NIM_CLANG_EWASM_C=$(DOCKER_NIM_CLANG) $(DOCKER_NIM_CLANG_FLAGS_EWASM) $(PATH_PARAMS) c
|
||||
|
||||
# Substrate
|
||||
DOCKER_NIM_CLANG_PASS_FLAGS_SUBSTRATE = --passC:"--target=wasm32-unknown-unknown-wasm" \
|
||||
--passL:"--target=wasm32-unknown-unknown-wasm" --passC:"-I./include" \
|
||||
--clang.options.linker:"$(CLANG_OPTIONS_LINKER),--import-memory,--max-memory=131072"
|
||||
DOCKER_NIM_CLANG_FLAGS_SUBSTRATE=$(DOCKER_NIM_CLANG_PASS_FLAGS_SUBSTRATE) --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release
|
||||
SUBSTRATE_NIMC=$(DOCKER_NIM_CLANG) $(DOCKER_NIM_CLANG_FLAGS_SUBSTRATE) $(PATH_PARAMS) c
|
||||
|
||||
ifdef USE_NLVM
|
||||
NIMC=$(DOCKER_NLVM_C)
|
||||
WASM32_NIMC=$(DOCKER_NLVM_C)
|
||||
EWASM_NIMC=$(DOCKER_NLVM_C)
|
||||
else
|
||||
NIMC=$(DOCKER_NIM_CLANG_C)
|
||||
WASM32_NIMC=$(DOCKER_NIM_CLANG_WASM32_C)
|
||||
EWASM_NIMC=$(DOCKER_NIM_CLANG_EWASM_C)
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
|
@ -59,30 +69,42 @@ vendors:
|
|||
cd vendors
|
||||
git submodule update --init
|
||||
|
||||
.PHONY: king_of_the_hill
|
||||
king_of_the_hill:
|
||||
$(WASM32_NIMC) --out:examples/king_of_the_hill.wasm examples/king_of_the_hill.nim
|
||||
.PHONY: ewasm_king_of_the_hill
|
||||
ewasm_king_of_the_hill:
|
||||
$(EWASM_NIMC) --out:examples/king_of_the_hill.wasm examples/king_of_the_hill.nim
|
||||
$(POSTPROCESS) examples/king_of_the_hill.wasm
|
||||
|
||||
.PHONY: examples
|
||||
examples: king_of_the_hill
|
||||
$(WASM32_NIMC) --out:examples/registry.wasm examples/registry.nim
|
||||
ewasm-examples: ewasm_king_of_the_hill
|
||||
$(EWASM_NIMC) --out:examples/registry.wasm examples/registry.nim
|
||||
$(POSTPROCESS) examples/registry.wasm
|
||||
$(WASM32_NIMC) --out:examples/balances.wasm examples/balances.nim
|
||||
$(EWASM_NIMC) --out:examples/balances.wasm examples/balances.nim
|
||||
$(POSTPROCESS) examples/balances.wasm
|
||||
$(WASM32_NIMC) --out:examples/erc20.wasm examples/erc20.nim
|
||||
$(EWASM_NIMC) --out:examples/erc20.wasm examples/erc20.nim
|
||||
$(POSTPROCESS) examples/erc20.wasm
|
||||
$(WASM32_NIMC) --out:examples/default_func.wasm examples/default_func.nim
|
||||
$(EWASM_NIMC) --out:examples/default_func.wasm examples/default_func.nim
|
||||
$(POSTPROCESS) examples/default_func.wasm
|
||||
|
||||
|
||||
.PHONY: ee-examples
|
||||
ee-examples:
|
||||
$(WASM32_NIMC) --out:examples/ee/helloworld.wasm examples/ee/helloworld.nim
|
||||
$(WASM32_NIMC) --out:examples/ee/bazaar.wasm examples/ee/bazaar.nim
|
||||
|
||||
$(EWASM_NIMC) --out:examples/ee/helloworld.wasm examples/ee/helloworld.nim
|
||||
$(EWASM_NIMC) --out:examples/ee/block_echo.wasm examples/ee/block_echo.nim
|
||||
|
||||
.PHONY: test-ee
|
||||
test-ee: ee-examples
|
||||
cd tests/ee/; \
|
||||
./test.sh
|
||||
|
||||
SUBSTRATE_POSTPROCESS=tools/substrate_postprocess.sh
|
||||
|
||||
.PHONY: substrate-examples
|
||||
substrate-examples:
|
||||
$(SUBSTRATE_NIMC) --out:examples/substrate/hello_world.wasm examples/substrate/hello_world.nim
|
||||
$(SUBSTRATE_POSTPROCESS) examples/substrate/hello_world.wasm
|
||||
$(SUBSTRATE_NIMC) --out:examples/substrate/setter.wasm examples/substrate/setter.nim
|
||||
$(SUBSTRATE_POSTPROCESS) examples/substrate/setter.wasm
|
||||
|
||||
.PHONY: test-substrate
|
||||
test-substrate: substrate-examples
|
||||
cd tests/substrate; \
|
||||
SUBSTRATE_PATH="${HOME}/.cargo/bin/substrate" ./test.sh
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import system/alloc
|
||||
|
||||
import ../../nimplay/ee_runtime
|
||||
|
||||
|
||||
proc main() {.exportwasm.} =
|
||||
var
|
||||
pre_state_root {.noinit.}: array[32, byte]
|
||||
post_state_root {.noinit.}: array[32, byte]
|
||||
|
||||
eth2_loadPreStateRoot(addr pre_state_root)
|
||||
var
|
||||
block_data_size = eth2_blockDataSize()
|
||||
block_data = alloc(block_data_size)
|
||||
|
||||
eth2_blockDataCopy(addr block_data, 0, block_data_size)
|
||||
eth2_savePostStateRoot(addr post_state_root)
|
|
@ -0,0 +1,24 @@
|
|||
import ../../nimplay/ee_runtime
|
||||
|
||||
{.compile: "malloc.c".}
|
||||
proc malloc(n: int): pointer {.importc.}
|
||||
|
||||
|
||||
proc copy_into_ba(to_ba: var auto, offset: int, from_ba: auto) =
|
||||
for i, x in from_ba:
|
||||
if offset + i > sizeof(to_ba) - 1:
|
||||
break
|
||||
to_ba[offset + i] = x
|
||||
|
||||
|
||||
proc main() {.exportwasm.} =
|
||||
var
|
||||
pre_state_root {.noinit.}: array[32, byte]
|
||||
post_state_root {.noinit.}: array[32, byte]
|
||||
eth2_loadPreStateRoot(addr pre_state_root)
|
||||
var
|
||||
block_data_size = eth2_blockDataSize()
|
||||
block_data = malloc(block_data_size.int)
|
||||
eth2_blockDataCopy(block_data, 0, block_data_size)
|
||||
copyMem(addr post_state_root, block_data, 32)
|
||||
eth2_savePostStateRoot(addr post_state_root[0])
|
|
@ -4,3 +4,11 @@ proc rawoutput(s: string) =
|
|||
revert(cstring(s), s.len.int32)
|
||||
proc panic(s: string) = rawoutput(s)
|
||||
{.pop.}
|
||||
|
||||
|
||||
# Try LLVm builtin unreachable:
|
||||
# void myabort(void) __attribute__((noreturn));
|
||||
# void myabort(void) {
|
||||
# asm("int3");
|
||||
# __builtin_unreachable();
|
||||
# }
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import ../../nimplay/substrate_runtime
|
||||
|
||||
|
||||
# Init function.
|
||||
proc deploy(): uint32 {.exportwasm.} =
|
||||
0
|
||||
|
||||
# Main function.
|
||||
proc call(): uint32 {.exportwasm.} =
|
||||
var
|
||||
s = cstring("Hello world!")
|
||||
ext_println(s, s.len.int32)
|
||||
ext_scratch_write(s, s.len.int32)
|
||||
0 # return
|
|
@ -0,0 +1,13 @@
|
|||
extern unsigned char __heap_base;
|
||||
|
||||
unsigned int bump_pointer = &__heap_base;
|
||||
|
||||
void* malloc(int n) {
|
||||
unsigned int r = bump_pointer ;
|
||||
bump_pointer += n;
|
||||
return (void *)r;
|
||||
}
|
||||
|
||||
void free(void* p) {
|
||||
// lol
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{.push stack_trace: off, profiler:off.}
|
||||
proc ext_println*(offset: pointer, length: uint32) {.noreturn, cdecl, importc.}
|
||||
proc rawoutput(s: string) =
|
||||
ext_println(cstring(s), s.len.uint32)
|
||||
proc panic(s: string) = rawoutput(s)
|
||||
{.pop.}
|
|
@ -0,0 +1,73 @@
|
|||
import ../../nimplay/substrate_runtime
|
||||
|
||||
{.compile: "malloc.c".}
|
||||
proc malloc(n: int): pointer {.importc.}
|
||||
|
||||
type
|
||||
Action = enum
|
||||
Set = 0'u8, Get = 1'u8, SelfEvict = 2'u8
|
||||
|
||||
# Init function.
|
||||
proc deploy(): uint32 {.exportwasm.} =
|
||||
0
|
||||
|
||||
proc get_scratch(): (pointer, int32) =
|
||||
var
|
||||
scratch_size = ext_scratch_size()
|
||||
mem_ptr = malloc(scratch_size.int)
|
||||
ext_scratch_read(mem_ptr, 0, scratch_size)
|
||||
(mem_ptr, scratch_size)
|
||||
|
||||
proc print(s: cstring) =
|
||||
ext_println(s, s.len.int32)
|
||||
|
||||
|
||||
proc incr_pointer(oldp: pointer): pointer =
|
||||
var newp = cast[pointer](cast[uint](oldp) + 1u)
|
||||
newp
|
||||
|
||||
|
||||
proc set_val_in_store(scratch_ptr: pointer, scratch_size: int32) =
|
||||
print("Set".cstring)
|
||||
var
|
||||
key: array[32, byte] = [
|
||||
2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
|
||||
2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
|
||||
2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
|
||||
2'u8, 2'u8, 2'u8, 2'u8, 2'u8
|
||||
]
|
||||
offset_ptr = incr_pointer(scratch_ptr)
|
||||
|
||||
ext_set_storage(addr key, 1.int32 , offset_ptr, scratch_size - 1)
|
||||
|
||||
# Main function.
|
||||
proc call(): uint32 {.exportwasm.} =
|
||||
var
|
||||
selector: uint8
|
||||
(scratch_ptr, scratch_size) = get_scratch()
|
||||
|
||||
copyMem(addr selector, scratch_ptr, 1)
|
||||
|
||||
case selector
|
||||
of Action.Set.ord:
|
||||
set_val_in_store(scratch_ptr, scratch_size)
|
||||
of Action.Get.ord:
|
||||
print("Get: Todo".cstring)
|
||||
of Action.SelfEvict.ord:
|
||||
print("SelfEvict: Todo".cstring)
|
||||
else:
|
||||
print("Unknown action passed".cstring)
|
||||
|
||||
0 # return
|
||||
|
||||
# sys::ext_set_storage(
|
||||
# key.as_ptr() as u32,
|
||||
# 1,
|
||||
# value.as_ptr() as u32,
|
||||
# value.len() as u32,
|
||||
# )
|
||||
|
||||
|
||||
# block_data = malloc(block_data_size.int)
|
||||
# eth2_blockDataCopy(block_data, 0, block_data_size)
|
||||
# copyMem(addr post_state_root, block_data, 32)
|
|
@ -0,0 +1,30 @@
|
|||
import macros
|
||||
|
||||
{.push cdecl, importc.}
|
||||
|
||||
proc ext_address*()
|
||||
proc ext_block_number*()
|
||||
proc ext_gas_left*()
|
||||
proc ext_gas_price*()
|
||||
proc ext_get_storage*(key_ptr: pointer): int32
|
||||
proc ext_now*()
|
||||
proc ext_println*(str_ptr: pointer, str_len: int32) # experimental; will be removed.
|
||||
proc ext_random_seed*()
|
||||
proc ext_scratch_read*(dest_ptr: pointer, offset: int32, len: int32)
|
||||
proc ext_scratch_size*(): int32
|
||||
proc ext_scratch_write*(src_ptr: pointer, len: int32)
|
||||
proc ext_set_rent_allowance*(value_ptr: pointer, value_len: int32)
|
||||
proc ext_set_storage*(key_ptr: pointer, value_non_null: int32, value_ptr: pointer, value_len: int32)
|
||||
proc ext_value_transferred*()
|
||||
|
||||
{.pop.}
|
||||
|
||||
macro exportwasm*(p: untyped): untyped =
|
||||
expectKind(p, nnkProcDef)
|
||||
result = p
|
||||
result.addPragma(newIdentNode("exportc"))
|
||||
result.addPragma(
|
||||
newColonExpr(
|
||||
newIdentNode("codegenDecl"), newLit("__attribute__ ((visibility (\"default\"))) $# $#$#")
|
||||
)
|
||||
)
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
|
||||
RUST_VERSION="nightly"
|
||||
REPO_DIR="ewasm-scout"
|
||||
REPO_URL="https://github.com/ewasm/scout.git"
|
||||
SCOUT_TEST_FILES=./*.yml
|
||||
|
||||
|
||||
if [[ -e $REPO_DIR ]]; then
|
||||
rm -v $REPO_DIR/*.yml
|
||||
cp $SCOUT_TEST_FILES $REPO_DIR/
|
||||
cd $REPO_DIR
|
||||
git pull
|
||||
else
|
||||
git clone $REPO_URL $REPO_DIR
|
||||
rm -v $REPO_DIR/*.yml
|
||||
cp $SCOUT_TEST_FILES $REPO_DIR/
|
||||
cd $REPO_DIR
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -e $CARGO_HOME ]]; then
|
||||
echo "Fetching rustup"
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain $RUST_VERSION
|
||||
fi
|
||||
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup component add rustfmt
|
||||
rustup update
|
||||
cargo install chisel
|
||||
# make build
|
||||
cargo build --release
|
||||
# make test
|
||||
# target/release/phase2-scout $SCOUT_TEST_FILE
|
||||
|
||||
for testfile in $SCOUT_TEST_FILES
|
||||
do
|
||||
echo $testfile
|
||||
RUST_LOG=debug target/release/phase2-scout $testfile || exit
|
||||
break
|
||||
done
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
beacon_state:
|
||||
execution_scripts:
|
||||
- ../../../examples/ee/block_echo.wasm
|
||||
shard_pre_state:
|
||||
exec_env_states:
|
||||
- "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
shard_blocks:
|
||||
- env: 0
|
||||
data: "fafbfcfdfef0f0f0f0f0ffffffffffffffffffffffffffffffffe6e5e4e3e2e1"
|
||||
shard_post_state:
|
||||
exec_env_states:
|
||||
- "fafbfcfdfef0f0f0f0f0ffffffffffffffffffffffffffffffffe6e5e4e3e2e1"
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "srml-contract-waterfall-nimplay",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "Sergey Shulepov <sergei@parity.io>",
|
||||
"contributors": [
|
||||
"Stefanie Doll <stefie@parity.io>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@polkadot/api": "^0.97.0-beta.39",
|
||||
"@polkadot/api-contract": "^0.97.0-beta.39",
|
||||
"blakejs": "^1.1.0",
|
||||
"typescript": "^3.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^10.12.18",
|
||||
"@typescript-eslint/eslint-plugin": "^2.6.0",
|
||||
"@typescript-eslint/parser": "^2.6.0",
|
||||
"bn.js": "^5.0.0",
|
||||
"eslint": "^6.6.0",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^24.1.0",
|
||||
"ts-loader": "^5.3.2"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest/presets/js-with-ts"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "NODE_ENV=abc jest"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -z "$SUBSTRATE_PATH" ]; then
|
||||
echo "Please specify the path to substrate in the SUBSTRATE_PATH environment variable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SUBSTRATE_PATH" ]; then
|
||||
echo "$SUBSTRATE_PATH doesn't exist"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Purge dev chain and then spin up the substrate node in background
|
||||
$SUBSTRATE_PATH purge-chain --dev -y
|
||||
$SUBSTRATE_PATH --dev -l debug &
|
||||
SUBSTRATE_PID=$!
|
||||
|
||||
# # Execute tests
|
||||
yarn && yarn test --verbose
|
||||
|
||||
# # Kill the spawned substrate node
|
||||
kill -9 $SUBSTRATE_PID
|
|
@ -0,0 +1,9 @@
|
|||
import BN from "bn.js";
|
||||
|
||||
export const WSURL = "ws://127.0.0.1:9944";
|
||||
export const DOT: BN = new BN("1000000000000000");
|
||||
export const CREATION_FEE: BN = DOT.muln(200);
|
||||
export const GAS_REQUIRED = 50000;
|
||||
export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
|
||||
export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// Adapted from https://github.com/paritytech/srml-contracts-waterfall/
|
||||
|
||||
import { ApiPromise, SubmittableResult, WsProvider } from "@polkadot/api";
|
||||
import { Abi } from '@polkadot/api-contract';
|
||||
import testKeyring from "@polkadot/keyring/testing";
|
||||
import { u8aToHex } from "@polkadot/util";
|
||||
import { randomAsU8a } from "@polkadot/util-crypto";
|
||||
import { KeyringPair } from "@polkadot/keyring/types";
|
||||
import { Option } from "@polkadot/types";
|
||||
import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
|
||||
|
||||
import { ALICE, CREATION_FEE, WSURL } from "./consts";
|
||||
import {
|
||||
callContract,
|
||||
instantiate,
|
||||
getContractStorage,
|
||||
putCode
|
||||
} from "./utils";
|
||||
|
||||
// This is a test account that is going to be created and funded each test.
|
||||
const keyring = testKeyring({ type: "sr25519" });
|
||||
const alicePair = keyring.getPair(ALICE);
|
||||
let testAccount: KeyringPair;
|
||||
let api: ApiPromise;
|
||||
|
||||
beforeAll((): void => {
|
||||
jest.setTimeout(30000);
|
||||
});
|
||||
|
||||
beforeEach(
|
||||
async (done): Promise<() => void> => {
|
||||
api = await ApiPromise.create({ provider: new WsProvider(WSURL) });
|
||||
testAccount = keyring.addFromSeed(randomAsU8a(32));
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(testAccount.address, CREATION_FEE.muln(3))
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (
|
||||
result.status.isFinalized &&
|
||||
result.findRecord("system", "ExtrinsicSuccess")
|
||||
) {
|
||||
console.log("New test account has been created.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe("Nimplay Hellow World", () => {
|
||||
test("Can deploy and execute", async (done): Promise<void> => {
|
||||
const codeHash = await putCode(
|
||||
api,
|
||||
testAccount,
|
||||
"../../../examples/substrate/hello_world.wasm"
|
||||
);
|
||||
expect(codeHash).toBeDefined();
|
||||
const address: Address = await instantiate(
|
||||
api,
|
||||
testAccount,
|
||||
codeHash,
|
||||
"0x00",
|
||||
CREATION_FEE
|
||||
);
|
||||
expect(address).toBeDefined();
|
||||
await callContract(api, testAccount, address, "0x00");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Nimplay Storage Setter", () => {
|
||||
test("Setter: Can deploy and execute", async (done): Promise<void> => {
|
||||
// See https://github.com/paritytech/srml-contracts-waterfall/issues/6 for info about
|
||||
// how to get the STORAGE_KEY of an instantiated contract
|
||||
|
||||
const STORAGE_KEY = (new Uint8Array(32)).fill(2);
|
||||
// Deploy contract code on chain and retrieve the code hash
|
||||
const codeHash = await putCode(
|
||||
api,
|
||||
testAccount,
|
||||
"../../../examples/substrate/setter.wasm"
|
||||
);
|
||||
expect(codeHash).toBeDefined();
|
||||
|
||||
// Instantiate a new contract instance and retrieve the contracts address
|
||||
const address: Address = await instantiate(
|
||||
api,
|
||||
testAccount,
|
||||
codeHash,
|
||||
"0x00",
|
||||
CREATION_FEE
|
||||
);
|
||||
expect(address).toBeDefined();
|
||||
|
||||
const initialValue: Uint8Array = await getContractStorage(
|
||||
api,
|
||||
address,
|
||||
STORAGE_KEY
|
||||
);
|
||||
expect(initialValue).toBeDefined();
|
||||
expect(initialValue.toString()).toEqual("");
|
||||
|
||||
await callContract(api, testAccount, address, "0x00");
|
||||
var val_hex = "03".repeat(32);
|
||||
// "0x00" indicates calling "Set" Action
|
||||
await callContract(api, testAccount, address, "0x00" + val_hex);
|
||||
const newValue = await getContractStorage(api, address, STORAGE_KEY);
|
||||
expect(newValue.toString()).toEqual("0x" + val_hex);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
import { ApiPromise, SubmittableResult } from "@polkadot/api";
|
||||
import { KeyringPair } from "@polkadot/keyring/types";
|
||||
import { Option, StorageData } from "@polkadot/types";
|
||||
import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
|
||||
import BN from "bn.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
const blake = require('blakejs')
|
||||
|
||||
import { GAS_REQUIRED } from "./consts";
|
||||
|
||||
export async function sendAndReturnFinalized(signer: KeyringPair, tx: any) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
tx.signAndSend(signer, (result: SubmittableResult) => {
|
||||
if (result.status.isFinalized) {
|
||||
// Return result of the submittable extrinsic after the transfer is finalized
|
||||
resolve(result as SubmittableResult);
|
||||
}
|
||||
if (
|
||||
result.status.isDropped ||
|
||||
result.status.isInvalid ||
|
||||
result.status.isUsurped
|
||||
) {
|
||||
reject(result as SubmittableResult);
|
||||
console.error("ERROR: Transaction could not be finalized.");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function putCode(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
fileName: string,
|
||||
gasRequired: number = GAS_REQUIRED
|
||||
): Promise<Hash> {
|
||||
const wasmCode = fs
|
||||
.readFileSync(path.join(__dirname, fileName))
|
||||
.toString("hex");
|
||||
const tx = api.tx.contracts.putCode(gasRequired, `0x${wasmCode}`);
|
||||
const result: any = await sendAndReturnFinalized(signer, tx);
|
||||
const record = result.findRecord("contracts", "CodeStored");
|
||||
|
||||
if (!record) {
|
||||
console.error("ERROR: No code stored after executing putCode()");
|
||||
}
|
||||
// Return code hash.
|
||||
return record.event.data[0];
|
||||
}
|
||||
|
||||
export async function instantiate(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
codeHash: Hash,
|
||||
inputData: any,
|
||||
endowment: BN,
|
||||
gasRequired: number = GAS_REQUIRED
|
||||
): Promise<Address> {
|
||||
const tx = api.tx.contracts.instantiate(
|
||||
endowment,
|
||||
gasRequired,
|
||||
codeHash,
|
||||
inputData
|
||||
);
|
||||
const result: any = await sendAndReturnFinalized(signer, tx);
|
||||
const record = result.findRecord("contracts", "Instantiated");
|
||||
|
||||
if (!record) {
|
||||
console.error("ERROR: No new instantiated contract");
|
||||
}
|
||||
// Return the address of instantiated contract.
|
||||
return record.event.data[1];
|
||||
}
|
||||
|
||||
export async function callContract(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
contractAddress: Address,
|
||||
inputData: any,
|
||||
gasRequired: number = GAS_REQUIRED,
|
||||
endowment: number = 0
|
||||
): Promise<void> {
|
||||
const tx = api.tx.contracts.call(
|
||||
contractAddress,
|
||||
endowment,
|
||||
gasRequired,
|
||||
inputData
|
||||
);
|
||||
await sendAndReturnFinalized(signer, tx);
|
||||
}
|
||||
|
||||
export async function getContractStorage(
|
||||
api: ApiPromise,
|
||||
contractAddress: Address,
|
||||
storageKey: Uint8Array
|
||||
): Promise<StorageData> {
|
||||
const contractInfo = await api.query.contracts.contractInfoOf(
|
||||
contractAddress
|
||||
);
|
||||
// Return the value of the contracts storage
|
||||
const storageKeyBlake2b = blake.blake2bHex(storageKey, null, 32);
|
||||
return await api.rpc.state.getChildStorage(
|
||||
(contractInfo as Option<ContractInfo>).unwrap().asAlive.trieId,
|
||||
'0x' + storageKeyBlake2b
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": ["dom", "es2018"],
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": [
|
||||
"contracts/rust",
|
||||
"lib",
|
||||
"node_modules",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": ["dom", "es2018"],
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": [
|
||||
"contracts/rust",
|
||||
"lib",
|
||||
"node_modules",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import BN from "bn.js";
|
||||
|
||||
export const WSURL = "ws://127.0.0.1:9944";
|
||||
export const DOT: BN = new BN("1000000000000000");
|
||||
export const CREATION_FEE: BN = DOT.muln(200);
|
||||
export const GAS_REQUIRED = 50000;
|
||||
export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
|
||||
export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
WASM_FILE=$1
|
||||
|
||||
set -ex
|
||||
|
||||
#wasm2wat="tools/wabt/build/wasm2wat"
|
||||
#wat2wasm="tools/wabt/build/wat2wasm"
|
||||
|
||||
wasm2wat="docker run --entrypoint=wasm2wat -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
|
||||
wat2wasm="docker run --entrypoint=wat2wasm -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
|
||||
|
||||
# Replace "env" with "ethereum"
|
||||
$wasm2wat "$WASM_FILE" |
|
||||
sed '/(export.*deploy\|call.*/! s/(export.*//g' > ./wasm.tmp
|
||||
|
||||
$wat2wasm -o "$WASM_FILE" ./wasm.tmp
|
||||
|
||||
rm ./wasm.tmp
|
Loading…
Reference in New Issue