mirror of https://github.com/status-im/nimplay.git
commit
a18f3ce052
|
@ -3,4 +3,7 @@ tools/wabt/*
|
||||||
tools/abi_gen
|
tools/abi_gen
|
||||||
examples/*.wasm
|
examples/*.wasm
|
||||||
.priv_key_hex
|
.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
|
DOCKER_NLVM_C=$(DOCKER_NLVM) $(PATH_PARAMS) $(NLVM_WAMS32_FLAGS) c
|
||||||
# Use nim + clang
|
# 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=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" \
|
CLANG_OPTIONS_LINKER=-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all,--export-dynamic
|
||||||
--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
|
# Ewasm
|
||||||
DOCKER_NIM_CLANG_C=$(DOCKER_NIM_CLANG) --cc:clang $(PATH_PARAMS) c
|
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
|
ifdef USE_NLVM
|
||||||
NIMC=$(DOCKER_NLVM_C)
|
NIMC=$(DOCKER_NLVM_C)
|
||||||
WASM32_NIMC=$(DOCKER_NLVM_C)
|
EWASM_NIMC=$(DOCKER_NLVM_C)
|
||||||
else
|
else
|
||||||
NIMC=$(DOCKER_NIM_CLANG_C)
|
NIMC=$(DOCKER_NIM_CLANG_C)
|
||||||
WASM32_NIMC=$(DOCKER_NIM_CLANG_WASM32_C)
|
EWASM_NIMC=$(DOCKER_NIM_CLANG_EWASM_C)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
@ -59,30 +69,42 @@ vendors:
|
||||||
cd vendors
|
cd vendors
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
.PHONY: king_of_the_hill
|
.PHONY: ewasm_king_of_the_hill
|
||||||
king_of_the_hill:
|
ewasm_king_of_the_hill:
|
||||||
$(WASM32_NIMC) --out:examples/king_of_the_hill.wasm examples/king_of_the_hill.nim
|
$(EWASM_NIMC) --out:examples/king_of_the_hill.wasm examples/king_of_the_hill.nim
|
||||||
$(POSTPROCESS) examples/king_of_the_hill.wasm
|
$(POSTPROCESS) examples/king_of_the_hill.wasm
|
||||||
|
|
||||||
.PHONY: examples
|
.PHONY: examples
|
||||||
examples: king_of_the_hill
|
ewasm-examples: ewasm_king_of_the_hill
|
||||||
$(WASM32_NIMC) --out:examples/registry.wasm examples/registry.nim
|
$(EWASM_NIMC) --out:examples/registry.wasm examples/registry.nim
|
||||||
$(POSTPROCESS) examples/registry.wasm
|
$(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
|
$(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
|
$(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
|
$(POSTPROCESS) examples/default_func.wasm
|
||||||
|
|
||||||
|
|
||||||
.PHONY: ee-examples
|
.PHONY: ee-examples
|
||||||
ee-examples:
|
ee-examples:
|
||||||
$(WASM32_NIMC) --out:examples/ee/helloworld.wasm examples/ee/helloworld.nim
|
$(EWASM_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/block_echo.wasm examples/ee/block_echo.nim
|
||||||
|
|
||||||
|
|
||||||
.PHONY: test-ee
|
.PHONY: test-ee
|
||||||
test-ee: ee-examples
|
test-ee: ee-examples
|
||||||
cd tests/ee/; \
|
cd tests/ee/; \
|
||||||
./test.sh
|
./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)
|
revert(cstring(s), s.len.int32)
|
||||||
proc panic(s: string) = rawoutput(s)
|
proc panic(s: string) = rawoutput(s)
|
||||||
{.pop.}
|
{.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