├── .gitignore
├── .gitmodules
├── Dockerfile
├── LICENSE
├── LICENSE-APACHEv2
├── LICENSE-MIT
├── Makefile
├── README.md
├── build.sh
├── docs
├── basics.md
└── installing.md
├── examples
├── balances.nim
├── default_func.nim
├── ee
│ ├── block_echo.nim
│ ├── helloworld.nim
│ └── panicoverride.nim
├── erc20.nim
├── hello.nim
├── king_of_the_hill.nim
├── map.nim
├── panicoverride.nim
├── payable.nim
├── registry.nim
├── substrate
│ ├── hello_world.nim
│ ├── malloc.c
│ ├── panicoverride.nim
│ └── setter.nim
├── wrc20.nim
└── wrc202.nim
├── include
└── string.h
├── nimplay.nim
├── nimplay
├── builtin_keywords.nim
├── ee_runtime.nim
├── ewasm_runtime.nim
├── function_signature.nim
├── logging.nim
├── nimplay_macros.nim
├── storage.nim
├── substrate_runtime.nim
├── types.nim
└── utils.nim
├── nimplay0_1.nim
├── tests
├── ee
│ ├── test.sh
│ ├── test_block_echo.yml
│ └── test_helloworld.yml
└── substrate
│ ├── package.json
│ ├── test.sh
│ ├── tests
│ ├── consts.ts
│ ├── contract-nimplay.spec.ts
│ └── utils.ts
│ ├── tsconfig.js
│ ├── tsconfig.json
│ └── utils.ts
└── tools
├── abi_gen.nim
├── deploy.py
├── eth_postprocess.sh
├── get_wabt.sh
├── k256_sig.nim
├── nim.cfg
├── nimplayc
└── substrate_postprocess.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/*
2 | tools/wabt/*
3 | tools/abi_gen
4 | examples/*.wasm
5 | .priv_key_hex
6 | *.wasm
7 | tests/ee/ewasm-scout/
8 | tests/substrate/node_modules/
9 |
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/nim-stew"]
2 | path = vendor/nim-stew
3 | url = https://github.com/status-im/nim-stew.git
4 | [submodule "vendor/stint"]
5 | path = vendor/stint
6 | url = https://github.com/status-im/nim-stint.git
7 | [submodule "vendor/nimcrypto"]
8 | path = vendor/nimcrypto
9 | url = https://github.com/cheatfate/nimcrypto.git
10 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 |
3 | ADD . /nimplay/
4 | WORKDIR /nimplay/
5 |
6 | RUN apt-get update && \
7 | apt-get install -y software-properties-common wget curl gcc g++ git cmake && \
8 | wget https://nim-lang.org/download/nim-0.20.2-linux_x64.tar.xz && \
9 | tar xf nim-0.20.2-linux_x64.tar.xz && \
10 | cd nim-0.20.2 && \
11 | ./install.sh /usr/bin && \
12 | cd .. && \
13 | rm -rf nim-0.20.2 && \
14 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
15 | apt-add-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main" && \
16 | apt-get install -y clang-8 lld-8 libc6-dev-i386 && \
17 | rm -rf wabt && \
18 | git clone --recursive https://github.com/WebAssembly/wabt wabt && \
19 | mkdir -p wabt/build && \
20 | cd wabt/build && \
21 | cmake ".." && \
22 | cmake --build "." && \
23 | make install && \
24 | cd ../../ && \
25 | rm -rf wabt && \
26 | nim c -d:release -p:/nimplay/vendor/nimcrypto -p:/nimplay/vendor/stint -p:/nimplay/vendor/nim-stew/ --out:/usr/bin/k256_sig tools/k256_sig.nim && \
27 | nim c -d:release -p:/nimplay/vendor/nimcrypto -p:/nimplay/vendor/stint -p:/nimplay/vendor/nim-stew/ --out:/usr/bin/abi_gen tools/abi_gen.nim && \
28 | cp tools/nimplayc /usr/bin/nimplayc && \
29 | update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 1000 && \
30 | update-alternatives --install /usr/bin/wasm-ld wasm-ld /usr/bin/wasm-ld-8 1000 && \
31 | apt-get remove -y cmake curl wget gcc software-properties-common && \
32 | apt-get auto-remove -y && \
33 | rm -rf /var/lib/apt/lists/*
34 |
35 | ENTRYPOINT ["/usr/bin/nim"]
36 |
37 |
38 | # apt-get install libc6-dev-i386
39 | # apt-get install lld-8
40 | # update-alternatives --install /usr/bin/wasm-ld wasm-ld /usr/bin/wasm-ld-8 1000
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Status
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE-APACHEv2:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2018 Status Research & Development GmbH
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Status Research & Development GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | user_id :=$(shell id -u $(shell whoami))
2 | pwd=$(shell pwd)
3 | POSTPROCESS=tools/eth_postprocess.sh
4 | PATH_PARAMS=-p:/code/vendor/nimcrypto -p:/code/vendor/stint -p:/code/vendor/nim-stew/
5 | # Use NLVM
6 | DOCKER_NLVM=docker run -e HOME='/tmp/' --user $(user_id):$(user_id) -w /code/ -v $(pwd):/code/ jacqueswww/nlvm
7 | DOCKER_NLVM_C=$(DOCKER_NLVM) $(PATH_PARAMS) c
8 | NLVM_WAMS32_FLAGS= --nlvm.target=wasm32 --gc:none -l:--no-entry -l:--allow-undefined -d:clang -d:release
9 | DOCKER_NLVM_C=$(DOCKER_NLVM) $(PATH_PARAMS) $(NLVM_WAMS32_FLAGS) c
10 | # Use nim + clang
11 | 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
12 | CLANG_OPTIONS_LINKER=-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all,--export-dynamic
13 |
14 | # Ewasm
15 | DOCKER_NIM_CLANG_C=$(DOCKER_NIM_CLANG) --cc:clang $(PATH_PARAMS) c
16 | DOCKER_NIM_CLANG_PASS_FLAGS_EWASM = --passC:"--target=wasm32-unknown-unknown-wasm" \
17 | --passL:"--target=wasm32-unknown-unknown-wasm" --passC:"-I./include" --clang.options.linker:"$(CLANG_OPTIONS_LINKER)"
18 | DOCKER_NIM_CLANG_FLAGS_EWASM=$(DOCKER_NIM_CLANG_PASS_FLAGS_EWASM) --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release
19 | DOCKER_NIM_CLANG_EWASM_C=$(DOCKER_NIM_CLANG) $(DOCKER_NIM_CLANG_FLAGS_EWASM) $(PATH_PARAMS) c
20 |
21 | # Substrate
22 | DOCKER_NIM_CLANG_PASS_FLAGS_SUBSTRATE = --passC:"--target=wasm32-unknown-unknown-wasm" \
23 | --passL:"--target=wasm32-unknown-unknown-wasm" --passC:"-I./include" \
24 | --clang.options.linker:"$(CLANG_OPTIONS_LINKER),--import-memory,--max-memory=131072"
25 | DOCKER_NIM_CLANG_FLAGS_SUBSTRATE=$(DOCKER_NIM_CLANG_PASS_FLAGS_SUBSTRATE) --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release
26 | SUBSTRATE_NIMC=$(DOCKER_NIM_CLANG) $(DOCKER_NIM_CLANG_FLAGS_SUBSTRATE) $(PATH_PARAMS) c
27 |
28 | ifdef USE_NLVM
29 | NIMC=$(DOCKER_NLVM_C)
30 | EWASM_NIMC=$(DOCKER_NLVM_C)
31 | else
32 | NIMC=$(DOCKER_NIM_CLANG_C)
33 | EWASM_NIMC=$(DOCKER_NIM_CLANG_EWASM_C)
34 | endif
35 |
36 | .PHONY: all
37 | all: tools examples
38 |
39 | .PHONY: get-nlvm-docker
40 | get-nlvm-docker:
41 | docker pull docker.io/jacqueswww/nlvm
42 |
43 | .PHONY: get-nimclang-docker
44 | get-nimclang-docker:
45 | docker pull docker.io/jacqueswww/nimclang
46 |
47 | .PHONY: get-wabt
48 | get-wabt:
49 | rm -rf tools/wabt
50 | ./tools/get_wabt.sh
51 | mv wabt tools/
52 |
53 | .PHONY: tools
54 | tools:
55 | $(NIMC) -d:release --out:tools/k256_sig tools/k256_sig.nim
56 | $(NIMC) -d:release --out:tools/abi_gen tools/abi_gen.nim
57 |
58 | .PHONY: clean
59 | clean:
60 | rm -f *.wasm *.ll *.wat
61 |
62 | .PHONY: get-nlvm examples
63 | get-nlvm-appimage:
64 | curl -L https://github.com/arnetheduck/nlvm/releases/download/continuous/nlvm-x86_64.AppImage -o tools/nlvm
65 | chmod +x tools/nlvm
66 |
67 | .PHONY: vendors
68 | vendors:
69 | cd vendors
70 | git submodule update --init
71 |
72 | .PHONY: ewasm_king_of_the_hill
73 | ewasm_king_of_the_hill:
74 | $(EWASM_NIMC) --out:examples/king_of_the_hill.wasm examples/king_of_the_hill.nim
75 | $(POSTPROCESS) examples/king_of_the_hill.wasm
76 |
77 | .PHONY: examples
78 | ewasm-examples: ewasm_king_of_the_hill
79 | $(EWASM_NIMC) --out:examples/registry.wasm examples/registry.nim
80 | $(POSTPROCESS) examples/registry.wasm
81 | $(EWASM_NIMC) --out:examples/balances.wasm examples/balances.nim
82 | $(POSTPROCESS) examples/balances.wasm
83 | $(EWASM_NIMC) --out:examples/erc20.wasm examples/erc20.nim
84 | $(POSTPROCESS) examples/erc20.wasm
85 | $(EWASM_NIMC) --out:examples/default_func.wasm examples/default_func.nim
86 | $(POSTPROCESS) examples/default_func.wasm
87 |
88 | .PHONY: ee-examples
89 | ee-examples:
90 | $(EWASM_NIMC) --out:examples/ee/helloworld.wasm examples/ee/helloworld.nim
91 | $(EWASM_NIMC) --out:examples/ee/block_echo.wasm examples/ee/block_echo.nim
92 |
93 | .PHONY: test-ee
94 | test-ee: ee-examples
95 | cd tests/ee/; \
96 | ./test.sh
97 |
98 | SUBSTRATE_POSTPROCESS=tools/substrate_postprocess.sh
99 |
100 | .PHONY: substrate-examples
101 | substrate-examples:
102 | $(SUBSTRATE_NIMC) --out:examples/substrate/hello_world.wasm examples/substrate/hello_world.nim
103 | $(SUBSTRATE_POSTPROCESS) examples/substrate/hello_world.wasm
104 | $(SUBSTRATE_NIMC) --out:examples/substrate/setter.wasm examples/substrate/setter.nim
105 | $(SUBSTRATE_POSTPROCESS) examples/substrate/setter.wasm
106 |
107 | .PHONY: test-substrate
108 | test-substrate: substrate-examples
109 | cd tests/substrate; \
110 | SUBSTRATE_PATH="${HOME}/.cargo/bin/substrate" ./test.sh
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://gitter.im/status-im/nimplay?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 |
5 | Disclaimer: WIP
6 |
7 | Nimplay is Domain Specific Language for writing smart contracts in Ethereum, using the Nim macro system.
8 |
9 | ## Language Principles
10 |
11 | *to be decided ;)*
12 |
13 | ## Docs
14 |
15 | [Installation Docs](docs/installing.md) to get nimplay up and running.
16 | [Language Basics](docs/basics.md) to get coding with nimplay.
17 |
18 | ## License
19 |
20 | Licensed and distributed under either of
21 |
22 | * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
23 |
24 | or
25 |
26 | * Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
27 |
28 | at your option. This file may not be copied, modified, or distributed except according to those terms.
29 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export WASM_LLVM_BIN="./llvm-wasm/bin/"
4 |
5 | function print_contract () {
6 | wasm_file=$1
7 | echo "${wasm_file} eWASM bytecode:"
8 | echo "0x"`xxd -p $wasm_file | tr -d '\n'`
9 | }
10 |
11 | nimble examples
12 |
13 | if [ $? -eq 0 ]; then
14 | print_contract examples/hello.wasm
15 | fi
16 |
--------------------------------------------------------------------------------
/docs/basics.md:
--------------------------------------------------------------------------------
1 | # Contract Interface
2 |
3 | To setup a simple contract one makes use of the `contract` block macro.
4 |
5 | ```nim
6 | contract("ContractName"):
7 |
8 | proc addition*(a: uint256, b: uint256): string =
9 | return a + b
10 |
11 | ```
12 |
13 | The string paramater given to the `contract` block macro can be any unique string.
14 |
15 | # Function Visibility
16 |
17 | All nimplay functions need to be annotated as either private or public, all functions marked with
18 | and asterisk `*` are public and can be called from a transaction or another contract.
19 | If a contract is not marked as public, it will be unreachable from outside the contract.
20 |
21 | # Payable
22 |
23 | To mark a function open to receive funds, the `payable` function pragma is used.
24 |
25 | ```nim
26 | contract("Main"):
27 |
28 | proc pay_me() {.payable.}: uint256 =
29 | 1.stuint(256)
30 | ```
31 |
32 | # Types
33 |
34 | Type Name | Nim Type | Runtime Size (B) | ABIv1 Size (B)
35 | ----------|------------- |------------------|---------------
36 | uint256 | StUint(256) | 32 | 32
37 | uint128 | StUint(128) | 16 | 32
38 | address | array[20,byte]| 20 | 32
39 | bytes32 | array[32,byte | 32 | 32
40 |
41 |
42 | # Builtin Variables
43 |
44 | To easily access the current transactions state as well block specific states the following builtin variables are exposed.
45 |
46 | Variable Name | Type | Contents
47 | --------------|--------- |----------
48 | msg.sender | address | Current address making CALL to contract
49 | msg.value | wei_value (uint128)| Ether value in wei
50 |
51 |
52 | # Logging
53 |
54 | To let the world know an event occured from a contract, Ethereum uses events. In Nimplay this can
55 | be achieved using the `{.event.}` with function without a body of code.
56 | To emit an event the `log` base keyword is used.
57 |
58 | ```nim
59 | contract("Main"):
60 | # Events
61 | proc nameGiven(name: bytes32, address: uint128) {.event.}
62 |
63 | proc setName*(name: bytes32):
64 | self.name = name
65 | log.nameGiven(name, msg.sender)
66 | ```
67 |
68 | To indicate what parameters have to be indexed, for easy retrieval later use `{.indexed.}`, a maximum of 3 indexed topics (or parameters) are allowed.
69 |
70 | ```nim
71 | contract("Main"):
72 | # Events
73 | proc nameGiven(name: bytes32, address {.indexed.}: uint128) {.event.}
74 |
75 | ```
76 |
77 | # Mappings
78 |
79 | Nimplay uses `StorageTable` type to indicated a mapping.
80 |
81 | ```nim
82 | contract("Registry"):
83 |
84 | names: StorageTable[bytes32, bytes32]
85 |
86 | proc set_name*(k: bytes32, v: bytes32) {.self.} =
87 | self.names[k] = v
88 |
89 | proc get_name*(k: bytes32): bytes32 {.self.} =
90 | self.names[k]
91 | ```
92 |
93 | # Default / Fallback Function
94 |
95 | To define a Fallback function, add function to your contract named `default`.
96 |
97 | ```nim
98 |
99 | contract("Default"):
100 | proc default*() =
101 | rever(nil, 0)
102 | # execute on all transactions
103 | ```
104 |
105 | If no Fallback is declared the fallback function causes a `revert()`.
106 |
107 |
108 |
--------------------------------------------------------------------------------
/docs/installing.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | The NimPlay installation is currently managed through a simple [Makefile](https://github.com/status-im/nimplay/blob/master/Makefile).
4 | To use it, you shouldn't need anything else besides _BASH_, _GNU Make_, _CMake_ and _Python_.
5 |
6 | ## Obtaining Nim + Clang Docker
7 |
8 | ```
9 | make get-nimclang-docker
10 | ```
11 |
12 | ## Obtaining NLVM docker
13 |
14 | To use NLVM instead of Nim + Clang use
15 |
16 | ```
17 | make get-nlvm-docker
18 | git submodule update --init
19 | make get-wabt
20 | make USE_NLVM=1 tools
21 | ```
22 |
23 | ### Using NLVM docker
24 |
25 | ```
26 | make USE_NLVM=1 examples
27 | ```
28 |
29 | ## Using Nim + Clang Docker
30 |
31 | ```
32 | make get-nimclang-docker
33 | git submodule update --init
34 | ```
35 | Note: nimclang docker image contains tools & wabt.
36 |
37 | ## Building the examples
38 |
39 | This repo includes a number of examples such as the [King of the Hill](https://github.com/status-im/nimplay/blob/master/examples/king_of_the_hill.nim) contract. To build them, use the following commands:
40 |
41 | ```
42 | make examples
43 | ```
44 | or
45 | ```
46 | make USE_NLVM=1 examples
47 | ```
48 |
49 | ## Deploying to EWASM Testnet
50 |
51 | Nimplay ships with a deploy python script (requires web3py installed). This script key can be used to deploy a contract
52 |
53 | Place 32 byte private key, hex encoded in `.priv_key_hex`
54 | ```
55 | cat > .priv_key_hex
56 | 0x0000000000000000000000000000000000000000000000000000000000000000
57 | ```
58 |
59 | To deploy a contract use:
60 | ```
61 | ./tools/deploy.py examples/king_of_the_hill.wasm
62 | ```
63 |
64 | The following enviroment variables are consumed by the deploy.py script
65 |
66 | | Varname |Default (unset) |
67 | |--- |--- |
68 | | PRIVATE_KEY_FILE| .priv_key_hex |
69 | | RPC_URL | http://ewasm.ethereum.org:8545|
70 | | WAT2WASM | ./tools/wabt/bin/wat2wasm |
71 | | WASM2WAT | ./tools/wabt/bin/wasm2wat |
72 |
73 | ### ModuleNotFoundError: No module named 'web3'
74 |
75 | This means web3py is not installed, please install using pip:
76 |
77 | ```
78 | pip install --user web3
79 | ```
80 |
81 | Also see how to make a [virtualenv](https://www.liquidweb.com/kb/creating-virtual-environment-ubuntu-16-04/) if you want your packages isolated.
82 |
--------------------------------------------------------------------------------
/examples/balances.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 |
4 | contract("Depository"):
5 |
6 | balances: StorageTable[address, bytes32, wei_value]
7 |
8 | proc deposit*(k: bytes32) {.self,msg,payable.} =
9 | self.balances[msg.sender][k] = msg.value
10 |
11 | proc get_balance*(k: bytes32): wei_value {.self,msg.} =
12 | self.balances[msg.sender][k]
13 |
14 | # proc withdraw*(amount: wei_value) =
15 | # var
16 | # balance = self.balances[msg.sender][k]
17 |
18 | # if amount <= balance:
19 | # self.balances[msg.sender][k] =
20 | # call(
21 | # getGasLeft(),
22 | # addr msg.sender
23 | # amount,
24 | # )
25 |
26 |
27 | # proc call*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
28 | # dataLength: int32, resultOffset: pointer,
29 | # resultLength: int32): int32
30 |
--------------------------------------------------------------------------------
/examples/default_func.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 |
4 | contract("Default"):
5 |
6 | proc default*() =
7 | discard
8 |
--------------------------------------------------------------------------------
/examples/ee/block_echo.nim:
--------------------------------------------------------------------------------
1 | import ../../nimplay/ee_runtime
2 |
3 | {.compile: "malloc.c".}
4 | proc malloc(n: int): pointer {.importc.}
5 |
6 |
7 | proc copy_into_ba(to_ba: var auto, offset: int, from_ba: auto) =
8 | for i, x in from_ba:
9 | if offset + i > sizeof(to_ba) - 1:
10 | break
11 | to_ba[offset + i] = x
12 |
13 |
14 | proc main() {.exportwasm.} =
15 | var
16 | pre_state_root {.noinit.}: array[32, byte]
17 | post_state_root {.noinit.}: array[32, byte]
18 | eth2_loadPreStateRoot(addr pre_state_root)
19 | var
20 | block_data_size = eth2_blockDataSize()
21 | block_data = malloc(block_data_size.int)
22 | eth2_blockDataCopy(block_data, 0, block_data_size)
23 | copyMem(addr post_state_root, block_data, 32)
24 | eth2_savePostStateRoot(addr post_state_root[0])
25 |
--------------------------------------------------------------------------------
/examples/ee/helloworld.nim:
--------------------------------------------------------------------------------
1 | import ../../nimplay/ee_runtime
2 |
3 |
4 | proc main() {.exportwasm.} =
5 | var
6 | pre_state_root {.noinit.}: array[32, byte]
7 | post_state_root {.noinit.}: array[32, byte]
8 | eth2_loadPreStateRoot(addr pre_state_root)
9 | debug_log("hello world!")
10 | debug_print32(42)
11 | debug_print64(99'u64)
12 |
13 | post_state_root = pre_state_root # no changes
14 | post_state_root[30] = 0x11'u8
15 | post_state_Root[31] = 0x22'u8
16 | eth2_savePostStateRoot(addr post_state_root)
17 |
--------------------------------------------------------------------------------
/examples/ee/panicoverride.nim:
--------------------------------------------------------------------------------
1 | {.push stack_trace: off, profiler:off.}
2 | proc debug_printMemHex*(offset: pointer, length: uint32) {.noreturn, cdecl, importc.}
3 | proc rawoutput(s: string) =
4 | debug_printMemHex(cstring(s), s.len.uint32)
5 | proc panic(s: string) = rawoutput(s)
6 | {.pop.}
7 |
--------------------------------------------------------------------------------
/examples/erc20.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 |
4 | contract("NimCoin"):
5 | # Globals
6 | var
7 | name*: bytes32
8 | symbol*: bytes32
9 | decimals*: uint256
10 | total_supply: uint256
11 | minter*: address
12 | initialised: bool
13 |
14 | # Maps
15 | balanceOf: StorageTable[address, uint256]
16 | allowances: StorageTable[address, address, uint256]
17 |
18 | initialised: bool
19 |
20 | # Events
21 | proc Transfer(ffrom: address, to: address, value: uint256) {.event.}
22 | proc Approval(owner: address, spender: address, value: uint256) {.event.}
23 |
24 | proc init*(nname: bytes32, ssymbol: bytes32, ddecimals: uint256, ttotal_supply: uint256) {.self,msg,log.} =
25 | if not self.initialised:
26 | return
27 |
28 | self.total_supply = ttotal_supply
29 | self.symbol = ssymbol
30 | self.decimals = ddecimals
31 | self.name = nname
32 | self.minter = msg.sender
33 | self.initialised = true
34 | log.Transfer(ZERO_ADDRESS, msg.sender, ttotal_supply)
35 |
36 | proc totalSupply*(): uint256 {.self.} =
37 | self.total_supply
38 |
39 | proc allowance(owner : address, spender : address): uint256 {.self.} =
40 | # @dev Function to check the amount of tokens that an owner allowed to a spender.
41 | # @param owner The address which owns the funds.
42 | # @param spender The address which will spend the funds.
43 | # @return An uint256 specifying the amount of tokens still available for the spender.
44 | self.allowances[owner][spender]
45 |
46 | proc transfer*(to: address, value: uint256): bool {.self,msg,log.} =
47 | # @param to The address to transfer to.
48 | # @param value The amount to be transferred.
49 | self.balanceOf[msg.sender] -= value
50 | self.balanceOf[to] += value
51 | log.Transfer(msg.sender, to, value)
52 | return true
53 |
54 | proc transferFrom(ffrom: address, to: address, value: uint256): bool {.self,msg,log.} =
55 | # @dev Transfer tokens ffrom one address to another.
56 | # Note that while this function emits a Transfer event, this is not required as per the specification,
57 | # and other compliant implementations may not emit the event.
58 | # @param ffrom address The address which you want to send tokens ffrom
59 | # @param to address The address which you want to transfer to
60 | # @param value uint256 the amount of tokens to be transferred
61 | self.balanceOf[ffrom] -= value
62 | self.balanceOf[to] += value
63 | self.allowances[ffrom][msg.sender] -= value
64 | log.Transfer(ffrom, to, value)
65 | return true
66 |
67 | proc approve(spender : address, value : uint256): bool {.self,msg,log.} =
68 | # @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
69 | # Beware that changing an allowance with this method brings the risk that someone may use both the old
70 | # and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
71 | # race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
72 | # https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
73 | # @param spender The address which will spend the funds.
74 | # @param value The amount of tokens to be spent.
75 | self.allowances[msg.sender][spender] =value
76 | log.Approval(msg.sender, spender, value)
77 | return true
78 |
79 | proc mint(to: address, value: uint256) {.self,msg,log.} =
80 | # @dev Mint an amount of the token and assigns it to an account.
81 | # This encapsulates the modification of balances such that the
82 | # proper events are emitted.
83 | # @param to The account that will receive the created tokens.
84 | # @param value The amount that will be created.
85 |
86 | assert msg.sender == self.minter
87 | assert to != ZERO_ADDRESS
88 | self.total_supply += value
89 | self.balanceOf[to] += value
90 | log.Transfer(ZERO_ADDRESS, to, value)
91 |
--------------------------------------------------------------------------------
/examples/hello.nim:
--------------------------------------------------------------------------------
1 | import ../eth_contracts
2 | import endians
3 | import stint
4 |
5 |
6 | # proc main() {.exportwasm.} =
7 | # proc test() =
8 | # return 1
9 | # var res = 1234.stuint(256).toByteArrayBE
10 | # finish(addr res, sizeof(res).int32)
11 |
12 | proc main() {.exportwasm.} =
13 | var selector: uint32
14 | callDataCopy(selector, 0)
15 | bigEndian32(addr selector, addr selector)
16 | case selector
17 | of 0xb0f0c96a'u32:
18 | var a: uint32 = 33333
19 | finish(addr a, sizeof(a).int32)
20 | else:
21 | finish(addr selector, sizeof(selector).int32)
22 |
23 | # var hello_param_0: array[32, byte]
24 | # callDataCopy(addr hello_param_0, 0, 32)
25 | # finish(addr hello_param_0, 32)
26 | # proc test(): int =
27 | # return 1
28 | # var res = 1234.stuint(256).toByteArrayBE
29 | # finish(addr res, sizeof(res).int32)
30 |
--------------------------------------------------------------------------------
/examples/king_of_the_hill.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 |
4 | contract("KingOfTheHill"):
5 |
6 | # Globals
7 | var
8 | king_name*: bytes32
9 | king_addr*: address
10 | king_value*: wei_value
11 |
12 | # Events
13 | proc BecameKing(name: bytes32, value: uint128) {.event.}
14 |
15 | # Methods
16 | proc becomeKing*(name: bytes32) {.payable,self,msg,log.} =
17 | if msg.value > self.king_value:
18 | self.king_name = name
19 | self.king_addr = msg.sender
20 | self.king_value = msg.value
21 | log.BecameKing(name, msg.value)
22 |
--------------------------------------------------------------------------------
/examples/map.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 | # proc concat[I1, I2: static[int]; T](a: array[I1, T], b: array[I2, T]): array[I1 + I2, T] =
4 | # result[0..a.high] = a
5 | # result[a.len..result.high] = b
6 |
7 | # proc exit_message(s: string) =
8 | # revert(cstring(s), s.len.int32)
9 |
10 | # # balances: StorageTable[address, wei_value]
11 | # proc setTableValue[N](table_id: int32, key: array[N, byte], value: var bytes32) {.inline.} =
12 | # type CombinedKey {.packed.} = object
13 | # table_id: int32
14 | # key: array[N, byte]
15 |
16 | # var
17 | # sha256_address: array[20, byte]
18 | # combined_key: CombinedKey
19 | # hashed_key: bytes32
20 |
21 | # sha256_address[19] = 2'u8
22 | # combined_key.table_id = table_id
23 | # combined_key.key = key
24 |
25 | # var res = call(
26 | # getGasLeft(), # gas
27 | # addr sha256_address, # addressOffset (ptr)
28 | # nil, # valueOffset (ptr)
29 | # addr combined_key, # dataOffset (ptr)
30 | # sizeof(combined_key).int32, # dataLength
31 | # )
32 | # if res == 1: # call failed
33 | # exit_message("Could not call sha256 in setTableValue")
34 | # # raise newException(Exception, "Could not call sha256 in setTableValue")
35 | # if getReturnDataSize() != 32.int32:
36 | # exit_message("Could not call sha256, Incorrect return size")
37 |
38 | # returnDataCopy(addr hashed_key, 0.int32, 32.int32)
39 | # storageStore(hashed_key, addr value)
40 |
41 |
42 | contract("MapStorage"):
43 |
44 | proc test*() =
45 | var
46 | key: array[5, byte] = [0x68'u8, 0x65'u8, 0x6c'u8, 0x6c'u8, 0x6f'u8]
47 |
48 | val: bytes32
49 | val[31] = 0x75'u8
50 | setTableValue(0.int32, key, val)
51 |
--------------------------------------------------------------------------------
/examples/panicoverride.nim:
--------------------------------------------------------------------------------
1 | {.push stack_trace: off, profiler:off.}
2 | proc revert(dataOffset: pointer; length: int32) {.noreturn, cdecl, importc.}
3 | proc rawoutput(s: string) =
4 | revert(cstring(s), s.len.int32)
5 | proc panic(s: string) = rawoutput(s)
6 | {.pop.}
7 |
8 |
9 | # Try LLVm builtin unreachable:
10 | # void myabort(void) __attribute__((noreturn));
11 | # void myabort(void) {
12 | # asm("int3");
13 | # __builtin_unreachable();
14 | # }
15 |
--------------------------------------------------------------------------------
/examples/payable.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay/nimplay_macros
2 | import ../nimplay/types
3 | import ../nimplay/ewasm_eei
4 | import stint
5 |
6 |
7 | contract("PayAssert"):
8 |
9 | proc plus_two*(a: uint256): uint256 {.payable.} =
10 | a + 2
11 |
12 | proc plus_one*(a: uint256): uint256 =
13 | a + 1
14 |
--------------------------------------------------------------------------------
/examples/registry.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay0_1
2 |
3 |
4 | contract("Registry"):
5 |
6 | names: StorageTable[bytes32, bytes32]
7 |
8 | proc set_name*(k: bytes32, v: bytes32) {.self.} =
9 | self.names[k] = v
10 |
11 | proc get_name*(k: bytes32): bytes32 {.self.} =
12 | self.names[k]
13 |
--------------------------------------------------------------------------------
/examples/substrate/hello_world.nim:
--------------------------------------------------------------------------------
1 | import ../../nimplay/substrate_runtime
2 |
3 |
4 | # Init function.
5 | proc deploy(): uint32 {.exportwasm.} =
6 | 0
7 |
8 | # Main function.
9 | proc call(): uint32 {.exportwasm.} =
10 | var
11 | s = cstring("Hello world!")
12 | ext_println(s, s.len.int32)
13 | ext_scratch_write(s, s.len.int32)
14 | 0 # return
15 |
--------------------------------------------------------------------------------
/examples/substrate/malloc.c:
--------------------------------------------------------------------------------
1 | extern unsigned char __heap_base;
2 |
3 | unsigned int bump_pointer = &__heap_base;
4 |
5 | void* malloc(int n) {
6 | unsigned int r = bump_pointer ;
7 | bump_pointer += n;
8 | return (void *)r;
9 | }
10 |
11 | void free(void* p) {
12 | // lol
13 | }
14 |
--------------------------------------------------------------------------------
/examples/substrate/panicoverride.nim:
--------------------------------------------------------------------------------
1 | {.push stack_trace: off, profiler:off.}
2 | proc ext_println*(offset: pointer, length: uint32) {.noreturn, cdecl, importc.}
3 | proc rawoutput(s: string) =
4 | ext_println(cstring(s), s.len.uint32)
5 | proc panic(s: string) = rawoutput(s)
6 | {.pop.}
7 |
--------------------------------------------------------------------------------
/examples/substrate/setter.nim:
--------------------------------------------------------------------------------
1 | import ../../nimplay/substrate_runtime
2 |
3 | {.compile: "malloc.c".}
4 | proc malloc(n: int): pointer {.importc.}
5 |
6 | type
7 | Action = enum
8 | Set = 0'u8, Get = 1'u8, SelfEvict = 2'u8
9 |
10 | # Init function.
11 | proc deploy(): uint32 {.exportwasm.} =
12 | 0
13 |
14 | proc get_scratch(): (pointer, int32) =
15 | var
16 | scratch_size = ext_scratch_size()
17 | mem_ptr = malloc(scratch_size.int)
18 | ext_scratch_read(mem_ptr, 0, scratch_size)
19 | (mem_ptr, scratch_size)
20 |
21 | proc print(s: cstring) =
22 | ext_println(s, s.len.int32)
23 |
24 |
25 | proc incr_pointer(oldp: pointer): pointer =
26 | var newp = cast[pointer](cast[uint](oldp) + 1u)
27 | newp
28 |
29 |
30 | proc set_val_in_store(scratch_ptr: pointer, scratch_size: int32) =
31 | print("Set".cstring)
32 | var
33 | key: array[32, byte] = [
34 | 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
35 | 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
36 | 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8, 2'u8,
37 | 2'u8, 2'u8, 2'u8, 2'u8, 2'u8
38 | ]
39 | offset_ptr = incr_pointer(scratch_ptr)
40 |
41 | ext_set_storage(addr key, 1.int32 , offset_ptr, scratch_size - 1)
42 |
43 | # Main function.
44 | proc call(): uint32 {.exportwasm.} =
45 | var
46 | selector: uint8
47 | (scratch_ptr, scratch_size) = get_scratch()
48 |
49 | copyMem(addr selector, scratch_ptr, 1)
50 |
51 | case selector
52 | of Action.Set.ord:
53 | set_val_in_store(scratch_ptr, scratch_size)
54 | of Action.Get.ord:
55 | print("Get: Todo".cstring)
56 | of Action.SelfEvict.ord:
57 | print("SelfEvict: Todo".cstring)
58 | else:
59 | print("Unknown action passed".cstring)
60 |
61 | 0 # return
62 |
63 | # sys::ext_set_storage(
64 | # key.as_ptr() as u32,
65 | # 1,
66 | # value.as_ptr() as u32,
67 | # value.len() as u32,
68 | # )
69 |
70 |
71 | # block_data = malloc(block_data_size.int)
72 | # eth2_blockDataCopy(block_data, 0, block_data_size)
73 | # copyMem(addr post_state_root, block_data, 32)
74 |
--------------------------------------------------------------------------------
/examples/wrc20.nim:
--------------------------------------------------------------------------------
1 | ## ewasm “WRC20” token contract coding challenge
2 | ## https://gist.github.com/axic/16158c5c88fbc7b1d09dfa8c658bc363
3 |
4 | import ../nimplay/ewasm_eei, endians
5 |
6 | proc do_balance() =
7 | if getCallDataSize() != 24:
8 | revert(nil, 0)
9 |
10 | var address{.noinit.}: array[20, byte]
11 | callDataCopy(address, 4)
12 |
13 | var balance{.noinit.}: array[32, byte]
14 | storageLoad(address, addr balance)
15 | finish(addr balance, sizeof(balance).int32)
16 |
17 | proc do_transfer() =
18 | if getCallDataSize() != 32:
19 | revert(nil, 0)
20 |
21 | var sender: array[20, byte]
22 | getCaller(addr sender)
23 | var recipient: array[20, byte]
24 | callDataCopy(recipient, 4)
25 | var value: array[8, byte]
26 | callDataCopy(value, 24)
27 |
28 | var senderBalance: array[32, byte]
29 | storageLoad(sender, addr senderBalance)
30 | var recipientBalance: array[32, byte]
31 | storageLoad(recipient, addr recipientBalance)
32 |
33 | var sb, rb, v: uint64
34 |
35 | bigEndian64(addr v, addr value)
36 | bigEndian64(addr sb, addr senderBalance[32 - 8])
37 |
38 | if sb < v:
39 | revert(nil, 0)
40 |
41 | bigEndian64(addr rb, addr recipientBalance[32 - 8])
42 |
43 | sb -= v
44 | rb += v # TODO there's an overflow possible here..
45 |
46 | bigEndian64(addr senderBalance[32 - 8], addr sb)
47 | bigEndian64(addr recipientBalance[32 - 8], addr rb)
48 |
49 | storageStore(sender, addr senderBalance)
50 | storageStore(recipient, addr recipientBalance)
51 |
52 | proc main() {.exportwasm.} =
53 | if getCallDataSize() < 4:
54 | revert(nil, 0)
55 | var selector: uint32
56 | callDataCopy(selector, 0)
57 | case selector
58 | of 0x9993021a'u32:
59 | do_balance()
60 | of 0x5d359fbd'u32:
61 | do_transfer()
62 | else:
63 | revert(nil, 0)
64 |
--------------------------------------------------------------------------------
/examples/wrc202.nim:
--------------------------------------------------------------------------------
1 | import ../nimplay/ewasm_eei
2 | import ../nimplay/nimplay_macros
3 | import ../nimplay/types
4 |
5 | # import endians
6 | # import macros
7 | import stint
8 |
9 | # expandMacros:
10 | contract("MyContract"):
11 | var
12 | a: uint256
13 | owner: address
14 | name: bytes32
15 |
16 | # proc default*() {.payable.}
17 | # ...
18 |
19 | # proc publicGetSender(slot_number: uint256) =
20 | # discard
21 | # dumpAstGen:
22 | # tmp_func_get_storage_owner()
23 | # proc tmp_func_get_storage_owner(): address =
24 | # var
25 | # tmp: array[32, byte]
26 | # position: array[32, byte]
27 | # slot_number: uint32 = 1
28 | # position[0..3] = cast[array[4, byte]](slot_number)
29 | # storageLoad(position, addr tmp)
30 | # var output: address
31 | # output[0..20] = tmp[0..20]
32 | # return output
33 |
34 | # proc get_value*(): uint128 {.payable.} =
35 | # var ba: array[16, byte]
36 | # getCallValue(addr ba)
37 | # var val: Stuint[128]
38 | # {.pragma: restrict, codegenDecl: "$# __restrict $#".}
39 | # let r_ptr {.restrict.} = cast[ptr array[128, byte]](addr val)
40 | # for i, b in ba:
41 | # r_ptr[i] = b
42 | # return val
43 |
44 | # proc ret_bytes32*(in_a: bytes32): bytes32 =
45 | # return in_a
46 |
47 | proc set_bytes32*(in_a: bytes32) {.self.} =
48 | self.name = in_a
49 |
50 | proc get_bytes32*(): bytes32 {.self.} =
51 | self.name
52 |
53 | # proc get_value*(): uint128 {.payable.} =
54 | # return msg.value
55 |
56 | # var b: array[16, byte]
57 | # var N = 16
58 | # for i in 0 ..< N:
59 | # b[N-1 - i] = a[i]
60 | # return Uint128.fromBytesBE(b)
61 |
62 | # proc test_out*(aaa: uint128): uint128 =
63 | # return 1222233344.stuint(128)
64 |
65 | # proc test_out_256*(): uint256 =
66 | # return 1222233344.stuint(256)
67 |
68 | # proc get_storage*(): uint256 =
69 | # return self.a
70 |
71 | # proc set_storage*(in_a: uint256) =
72 | # self.a = in_a
73 |
74 | # proc set_storage*(a: uint256) =
75 | # discard
76 |
77 | # proc set_storage*() =
78 | # var
79 | # pos = 0.stuint(32).toByteArrayBE
80 | # value = 999999999999999.stuint(256).toByteArrayBE
81 | # storageStore(pos, addr value)
82 |
83 | # return msg.sender
84 |
85 | # proc tmp_func_get_storage_owner(slot_number: uint256): address =
86 | # var tmp: array[32, byte]
87 | # var position = slot_number.toByteArrayBE
88 | # storageLoad(position, addr tmp)
89 | # var output: address
90 | # output[0..19] = tmp[12..31]
91 | # return output
92 |
93 | # proc addition(in_a: uint256, in_b: uint256): uint256 =
94 | # return in_a + in_b
95 |
96 | # proc get*(): uint256 =
97 | # var tmp: array[32, byte]
98 | # var pos = 0.stuint(32).toByteArrayBE
99 | # storageLoad(pos, addr tmp)
100 | # return Uint256.fromBytesBE(tmp)
101 |
102 | # proc set*() =
103 | # var tmp = 556677.stuint(256).toByteArrayBE
104 | # var pos = 0.stuint(32).toByteArrayBE
105 | # storageStore(pos, addr tmp)
106 |
107 | # proc get_storage*(slot_number: uint256): uint256 =
108 | # var tmp: uint256
109 | # var pos = cast[bytes32](slot_number)
110 | # storageLoad(pos, addr tmp)
111 | # return tmp
112 |
113 | # proc set_storage*(slot_number: uint256, value: uint256) =
114 | # var tmp: array[32, byte] = value.toByteArrayBE
115 | # var pos = cast[bytes32](slot_number)
116 | # storageStore(pos, addr tmp)
117 |
118 | # proc setOwner(in_owner: address) =
119 | # self.owner = in_owner
120 |
121 | # proc getOwner(): address =
122 | # return self.owner
123 |
124 | # proc get_sender222(): address =
125 | # if true:
126 | # return msg.sender
127 |
128 | # proc get_sender(): address =
129 | # if true:
130 | # return msg.sender
131 |
132 | # getCaller(addr tmp_addr)
133 |
134 | # blacklist storageStore
135 | # proc hello333(a: uint256): uint256 =
136 | # discard
137 |
138 | # proc hello(a: uint256): uint256 =
139 |
140 | # dumpAstGen:
141 | # let c: uint256 = Uint256.fromBytesBE(b)
142 | # case a:
143 | # else:
144 | # discard
145 | # var a = hello(1.uint256)
146 | # var b: array[32, byte]
147 | # callDataCopy(addr b, 4, 32)
148 | # var c: uint256 = Uint256.fromBytesBE(b)
149 |
150 | # bigEndian32(addr selector, addr selector)
151 |
152 | # hello(a)
153 | # var selector: uint32
154 | # callDataCopy(selector, 0)
155 | # case selector
156 | # of 0x9993021a'u32:
157 | # discard
158 | # else:
159 | # revert(nil, 0)
160 |
161 | # dumpAstGen:
162 | # var res {.noinit.}: uint256
163 | # dumpAstGen:
164 | # var res = hello(a)
165 | # var res_a = res.toByteArrayBE
166 | # finish(addr res_a, 256)
167 | # finish(nil, 0)
168 | # dumpAstGen:
169 | # bigEndian32(addr selector, addr selector)
170 | # return (123).stuint(256)
171 |
172 | # proc AllWorksToken() {.discardable.} =
173 | # var b: array[32, byte] = (77877).stuint(256).toByteArrayBE()
174 | # finish(addr b, 32)
175 |
176 | # proc world(a: uint256, b: uint256): uint256 =
177 | # return a + b
178 |
179 | # proc do_nothing(a: uint256, b: uint256) =
180 | # discard
181 |
182 | # func addition(a: uint256, b: uint256): uint256 =
183 | # return a + b
184 |
185 | # proc hellonone() =
186 | # discard
187 |
188 | # proc addition(a: uint256, b: uint256): uint256 =
189 | # return a + b
190 |
191 | # contract("MyContract"):
192 | # lib: from("lib/...")
193 |
194 | # proc hello(): uint256 =
195 | # lib.sha500()
196 |
197 |
198 |
199 | # library A:
200 |
201 | # # public init()
202 | # # owner
203 | # # selfdestruct()
204 | # # special_func(aaa)
205 |
206 |
207 | # contract B(A):
208 | # $$$$
209 | # $$$$
210 | # $$$$
211 | # $$$$
212 | # $$$$
213 |
--------------------------------------------------------------------------------
/include/string.h:
--------------------------------------------------------------------------------
1 | /*
2 | This is a stub file to suppress compilation errors, when nim calls out to
3 | memcpy, memset and memcpy. Currently we rely on clang optimizing these calls
4 | away, but in future we will likely have to come up with something better/reliable
5 | */
6 |
--------------------------------------------------------------------------------
/nimplay.nim:
--------------------------------------------------------------------------------
1 | import nimplay/nimplay_macros
2 |
3 |
--------------------------------------------------------------------------------
/nimplay/builtin_keywords.nim:
--------------------------------------------------------------------------------
1 | import
2 | macros, strformat, tables,
3 | strutils, sequtils, algorithm
4 |
5 | import
6 | ./types, ./utils, ./storage,
7 | ./logging
8 |
9 |
10 | proc extract_keys(base_node: NimNode, keys: var seq[NimNode]) =
11 | for x in base_node:
12 | if x.kind == nnkBracketExpr:
13 | extract_keys(x, keys)
14 | else:
15 | keys.add(x)
16 |
17 |
18 | proc check_global_exists(global_ctx: GlobalContext, var_name: string, node: NimNode) =
19 | if not (var_name in global_ctx.global_variables):
20 | raiseParserError(
21 | fmt"Invalid global variable {var_name}, has not been defined.",
22 | node
23 | )
24 |
25 |
26 | proc is_dot_variable(node: NimNode): bool =
27 | if node.kind == nnkDotExpr:
28 | var correct_types = (node[0].kind, node[1].kind) == (nnkIdent, nnkIdent)
29 | var correct_length = node.len == 2
30 | if correct_types and correct_length:
31 | return true
32 | return false
33 |
34 |
35 | proc is_message_sender(node: NimNode): bool =
36 | if is_dot_variable(node) and node[0].strVal == "msg" and node[1].strVal == "sender":
37 | return true
38 | return false
39 |
40 |
41 | proc is_message_value(node: NimNode): bool =
42 | if is_dot_variable(node) and node[0].strVal == "msg" and node[1].strVal == "value":
43 | return true
44 | return false
45 |
46 |
47 | proc has_self(node: NimNode, global_ctx: GlobalContext): bool =
48 | if is_dot_variable(node) and node[0].strVal == "self":
49 | global_ctx.check_global_exists(node[1].strVal, node)
50 | if node[1].strVal in global_ctx.global_variables:
51 | return true
52 | return false
53 |
54 |
55 | proc is_log_statement(node: NimNode, global_ctx: GlobalContext): bool =
56 | if node.kind == nnkCall:
57 | if is_dot_variable(node[0]) and strVal(node[0][0]) == "log":
58 | var
59 | func_name = strVal(node[0][1])
60 | if func_name in global_ctx.events:
61 | for func_name in global_ctx.events.keys():
62 | return true
63 | else:
64 | raiseParserError(
65 | fmt"Invalid log statement {func_name}, event was not defined.",
66 | node
67 | )
68 |
69 |
70 | proc has_self_assignment(node: NimNode): bool =
71 | if node.kind == nnkAsgn:
72 | if is_dot_variable(node[0]) and node[0][0].strVal == "self":
73 | return true
74 | return false
75 |
76 |
77 | proc has_self_storage_table_set(node: NimNode, global_ctx: GlobalContext): (bool, NimNode) =
78 | var n: NimNode
79 | if node.kind == nnkAsgn and node[0].kind == nnkBracketExpr:
80 | var keys: seq[NimNode]
81 | extract_keys(node[0], keys)
82 | if keys.len > 0 and is_dot_variable(keys[0]) and strVal(keys[0][0]) == "self":
83 | return (true, keys[0])
84 | return (false, n)
85 |
86 |
87 | proc has_self_storage_table_get(node: NimNode, global_ctx: GlobalContext): (bool, NimNode) =
88 | var n: NimNode
89 | if node.kind == nnkBracketExpr:
90 | var keys: seq[NimNode]
91 | extract_keys(node, keys)
92 | if keys.len > 0 and is_dot_variable(keys[0]) and strVal(keys[0][0]) == "self":
93 | return (true, keys[0])
94 | return (false, node)
95 |
96 |
97 | proc has_infix_storage_table(node: NimNode): (bool, NimNode) =
98 | var n: NimNode
99 | if node.kind == nnkInfix and node[1].kind == nnkBracketExpr:
100 | var keys: seq[NimNode]
101 | extract_keys(node[1], keys)
102 | if keys.len > 0 and is_dot_variable(keys[0]) and strVal(keys[0][0]) == "self":
103 | return (true, node)
104 | return (false, node)
105 |
106 |
107 | proc has_infix_self_storage(node: NimNode): (bool, NimNode) =
108 | if node.kind == nnkInfix:
109 | if is_dot_variable(node[1]) and node[1][0].strVal == "self":
110 | return (true, node)
111 | return (false, node)
112 |
113 |
114 | proc is_keyword(node: NimNode, global_ctx: GlobalContext): (bool, string) =
115 | var
116 | (valid, sub_node) = has_infix_self_storage(node)
117 | if valid:
118 | return (true, "infix_set_self." & strVal(sub_node[0])[0] & "." & strVal(sub_node[1][1]))
119 | (valid, sub_node) = has_infix_storage_table(node)
120 | if valid:
121 | var keys: seq[NimNode]
122 | extract_keys(node[1], keys)
123 | return (true, "infix_set_table." & strVal(sub_node[0])[0] & "." & strVal(keys[0][1]))
124 | (valid, sub_node) = has_self_storage_table_set(node, global_ctx)
125 | if valid:
126 | return (true, "set_table_self." & strVal(sub_node[1]))
127 | (valid, sub_node) = has_self_storage_table_get(node, global_ctx)
128 | if valid:
129 | return (true, "get_table_self." & strVal(sub_node[1]))
130 | if is_message_sender(node):
131 | return (true, "msg.sender")
132 | elif is_message_value(node):
133 | return (true, "msg.value")
134 | elif has_self_assignment(node):
135 | return (true, "set_self." & strVal(node[0][1]))
136 | elif has_self(node, global_ctx):
137 | return (true, "self." & strVal(node[1]))
138 | elif is_log_statement(node, global_ctx):
139 | return (true, "log." & strVal(node[0][1]))
140 | else:
141 | return (false, "")
142 |
143 |
144 | proc find_builtin_keywords(func_body: NimNode, used_keywords: var seq[string], global_ctx: GlobalContext) =
145 |
146 | for child in func_body:
147 | let (is_kw, kw_key_name) = is_keyword(child, global_ctx)
148 |
149 | var
150 | setter_kw = false
151 | exemption_list = @[
152 | "set_" & kw_key_name,
153 | "set_table_" & kw_key_name,
154 | "get_table_" & kw_key_name,
155 | ]
156 |
157 | for proposed_setter_kw in exemption_list:
158 | if proposed_setter_kw in used_keywords:
159 | setter_kw = true
160 | if is_kw and not setter_kw: # Should be added to globals list.
161 | used_keywords.add(kw_key_name)
162 | # For infix make sure we have getter & setter functions.
163 | if is_kw and kw_key_name.startsWith("infix_set_table"):
164 | var var_name = kw_key_name.split('.')[^1]
165 | used_keywords.add("set_table_self." & var_name)
166 | used_keywords.add("get_table_self." & var_name)
167 | elif is_kw and kw_key_name.startsWith("infix_set_self"):
168 | var var_name = kw_key_name.split('.')[^1]
169 | used_keywords.add("set_self." & var_name)
170 | used_keywords.add("self." & var_name)
171 | find_builtin_keywords(child, used_keywords, global_ctx)
172 |
173 |
174 | proc generate_defines(keywords: seq[string], global_ctx: GlobalContext): (NimNode, Table[string, string]) =
175 | # Allocate keywords that do not alter their value
176 | # during execution of a function e.g. msg.sender, msg.value etc.
177 | var stmts = newStmtList()
178 | var tmp_vars = initTable[string, string]()
179 | if "msg.sender" in keywords:
180 |
181 | var
182 | tmp_var_name = "msg_sender_tmp_variable_alloc"
183 | tmp_var_node = newIdentNode(tmp_var_name)
184 |
185 | var s = quote do:
186 | var `tmp_var_node` {.noinit.}: address
187 | getCaller(addr `tmp_var_node`)
188 |
189 | stmts.add(s)
190 | tmp_vars["msg.sender"] = tmp_var_name
191 |
192 | if "msg.value" in keywords:
193 | var tmp_func_name = fmt"msg_value_func"
194 | stmts.add(parseStmt("proc " & tmp_func_name & """(): uint128 =
195 | var ba {.noinit.}: array[16, byte]
196 | getCallValue(addr ba)
197 | var val {.noinit.}: Stuint[128]
198 | {.pragma: restrict, codegenDecl: "$# __restrict $#".}
199 | let r_ptr {.restrict.} = cast[ptr array[128, byte]](addr val)
200 | for i, b in ba:
201 | r_ptr[i] = b
202 | return val
203 | """))
204 | tmp_vars["msg.value"] = tmp_func_name
205 |
206 | for kw in keywords:
207 | if kw.startsWith("log."):
208 | var (new_proc, new_proc_name) = generate_log_func(kw, global_ctx)
209 | tmp_vars[kw] = new_proc_name
210 | stmts.add(new_proc)
211 | elif kw.startsWith("set_self."):
212 | var (new_proc, new_proc_name) = generate_storage_set_func(kw, global_ctx)
213 | tmp_vars[kw] = new_proc_name
214 | stmts.add(new_proc)
215 | elif kw.startsWith("set_table_self."):
216 | var (new_proc, new_proc_name) = generate_storage_table_set_func(kw, global_ctx)
217 | tmp_vars[kw] = new_proc_name
218 | stmts.add(new_proc)
219 | elif kw.startsWith("get_table_self."):
220 | var (new_proc, new_proc_name) = generate_storage_table_get_func(kw, global_ctx)
221 | tmp_vars[kw] = new_proc_name
222 | stmts.add(new_proc)
223 | elif kw.startsWith("self."):
224 | var (new_proc, new_proc_name) = generate_storage_get_func(kw, global_ctx)
225 | tmp_vars[kw] = new_proc_name
226 | stmts.add(new_proc)
227 | return (stmts, tmp_vars)
228 |
229 |
230 | proc check_keyword_defines(keywords_used: seq[string], local_ctx: LocalContext) =
231 | for keyword in keywords_used:
232 | if "infix_" in keyword:
233 | continue
234 | var base = keyword.replace("set_", "").replace("get_", "").replace("table_", "")
235 | if "." in base:
236 | base = base.split(".")[0]
237 | if not (base in local_ctx.sig.pragma_base_keywords):
238 | raiseParserError(
239 | fmt"Base Keyword {{.{base}.}} needs to be placed in the pragma of function '{local_ctx.sig.name}'.",
240 | local_ctx.sig.line_info
241 | )
242 |
243 |
244 | proc get_keyword_defines*(proc_def: NimNode, global_ctx: GlobalContext, local_ctx: LocalContext): (NimNode, Table[string, string]) =
245 | var
246 | keywords_used: seq[string]
247 | find_builtin_keywords(proc_def, keywords_used, global_ctx)
248 | keywords_used = deduplicate(keywords_used)
249 | check_keyword_defines(keywords_used, local_ctx)
250 | let (global_define_stmts, global_keyword_map) = generate_defines(keywords_used, global_ctx)
251 | return (global_define_stmts, global_keyword_map)
252 |
253 |
254 | proc get_next_storage_node(kw_key_name: string, global_keyword_map: Table[string, string], current_node: NimNode): NimNode =
255 | var
256 | keys: seq[NimNode]
257 |
258 | if kw_key_name.startsWith("self."):
259 | return nnkCall.newTree(
260 | newIdentNode(global_keyword_map[kw_key_name])
261 | )
262 | elif kw_key_name.startsWith("set_self."):
263 | return nnkCall.newTree(
264 | newIdentNode(global_keyword_map[kw_key_name]),
265 | current_node[1]
266 | )
267 | elif kw_key_name.startsWith("get_table_self."):
268 | var
269 | call_func = nnkCall.newTree(
270 | newIdentNode(global_keyword_map[kw_key_name]),
271 | )
272 | extract_keys(current_node, keys)
273 | for param in keys[1..keys.len - 1]:
274 | call_func.add(param)
275 | return call_func
276 | elif kw_key_name.startsWith("set_table_self."):
277 | var
278 | call_func = nnkCall.newTree(
279 | newIdentNode(global_keyword_map[kw_key_name]),
280 | )
281 | extract_keys(current_node[0], keys)
282 | for param in keys[1..^1]:
283 | call_func.add(param)
284 | call_func.add(current_node[1]) # val param
285 | return call_func
286 | elif kw_key_name.startsWith("infix"):
287 | # infix_set_table.balanceOf
288 | var
289 | set_call_func, get_call_func: NimNode
290 | tmp = kw_key_name.split(".")
291 | (infix_operator, var_name) = (tmp[1], tmp[2])
292 |
293 | if kw_key_name.startsWith("infix_set_table"):
294 | set_call_func = nnkCall.newTree(
295 | newIdentNode(global_keyword_map["set_table_self." & var_name]),
296 | )
297 | get_call_func = nnkCall.newTree(
298 | newIdentNode(global_keyword_map["get_table_self." & var_name]),
299 | )
300 | # echo treeRepr(current_node)
301 | extract_keys(current_node[1], keys)
302 | # Get value.
303 | for param in keys[1..keys.len - 1]:
304 | get_call_func.add(param)
305 | set_call_func.add(param)
306 |
307 | elif kw_key_name.startsWith("infix_set_self"):
308 | set_call_func = nnkCall.newTree(
309 | newIdentNode(global_keyword_map["set_self." & var_name]),
310 | )
311 | get_call_func = nnkCall.newTree(
312 | newIdentNode(global_keyword_map["self." & var_name]),
313 | )
314 | else:
315 | raise newException(ParserError, "Unknown infix operation")
316 |
317 | var
318 | op_get = newNimNode(nnkInfix)
319 | op_get.add(newIdentNode(infix_operator))
320 | op_get.add(get_call_func)
321 | op_get.add(current_node[2])
322 | set_call_func.add(op_get)
323 |
324 | return set_call_func
325 |
326 | else:
327 | raise newException(ParserError, "Unknown global self keyword")
328 |
329 |
330 | proc replace_keywords*(ast_node: NimNode, global_keyword_map: Table[string, string], global_ctx: GlobalContext): NimNode =
331 | var
332 | TMP_VAR_KEYWORDS = @["msg.sender"]
333 | TMP_FUNC_KEYWORDS = @["msg.value"]
334 |
335 | var res_node = copyNimNode(ast_node)
336 | for child in ast_node:
337 | var next: NimNode
338 | let (is_kw, kw_key_name) = is_keyword(child, global_ctx)
339 | if is_kw:
340 | if kw_key_name.startsWith("log."):
341 | next = generate_next_call_log_node(kw_key_name, global_keyword_map, child)
342 | elif "self." in kw_key_name or kw_key_name.startsWith("infix_set_table"):
343 | next = get_next_storage_node(kw_key_name, global_keyword_map, child)
344 | elif kw_key_name in TMP_FUNC_KEYWORDS:
345 | next = nnkCall.newTree(
346 | newIdentNode(global_keyword_map[kw_key_name])
347 | )
348 | elif kw_key_name in TMP_VAR_KEYWORDS:
349 | next = newIdentNode(global_keyword_map[kw_key_name])
350 | else:
351 | raise newException(ParserError, "No replacement specified for " & kw_key_name & " keyword")
352 | else:
353 | next = child
354 | res_node.add(replace_keywords(next, global_keyword_map, global_ctx))
355 | return res_node
356 |
--------------------------------------------------------------------------------
/nimplay/ee_runtime.nim:
--------------------------------------------------------------------------------
1 | import macros
2 |
3 | {.push cdecl, importc.}
4 | proc eth2_loadPreStateRoot*(memoryOffset: pointer)
5 | proc eth2_blockDataSize*(): uint32
6 | proc eth2_blockDataCopy*(outputOfset: pointer, offset: uint32, length: uint32)
7 | proc eth2_savePostStateRoot*(memoryOffset: pointer)
8 | proc eth2_pushNewDeposit*(memoryOffset: pointer, length: uint32)
9 | proc debug_print32*(value: uint32)
10 | proc debug_print64*(value: uint64)
11 | proc debug_printMem*(offset: pointer, length: uint32)
12 | proc debug_printMemHex*(offset: pointer, length: uint32)
13 | proc debug_printStorage*(pathOffset: pointer)
14 | proc debug_printStorageHex*(pathOffset: pointer)
15 |
16 | {.pop.}
17 |
18 |
19 | proc debug_log*(s: string) =
20 | debug_printMem(cstring(s), s.len.uint32)
21 |
22 |
23 | macro exportwasm*(p: untyped): untyped =
24 | expectKind(p, nnkProcDef)
25 | result = p
26 | result.addPragma(newIdentNode("exportc"))
27 | result.addPragma(
28 | newColonExpr(
29 | newIdentNode("codegenDecl"), newLit("__attribute__ ((visibility (\"default\"))) $# $#$#")
30 | )
31 | )
32 |
33 |
34 | # eth2::loadPreStateRoot(memoryOffset: u32ptr)
35 | # The current pre_state_root (256-bit value) is loaded from the memory offset pointed at
36 | # eth2::blockDataSize() -> u32
37 | # Returns the size of the block.data
38 | # eth2::blockDataCopy(memoryOffset: u32ptr, offset: u32, length: u32)
39 | # Copies length bytes from block.data + offset to the memory offset
40 | # eth2::savePostStateRoot(memoryOffset: u32ptr)
41 | # The post_state_root (256-bit value) is set to the content of the memory offset pointed at
42 | # eth2::pushNewDeposit(memoryOffset: u32ptr, length: u32)
43 | # This expects a Deposit 2 data structure to be stored at the memory offset (SSZ serialised?). It will be appended to the deposit list.
44 |
45 | # (type (;0;) (func (param i32)))
46 | # (type (;1;) (func (param i32 i32)))
47 | # (type (;2;) (func (param i64)))
48 | # (type (;3;) (func (result i32)))
49 | # (type (;4;) (func))
50 | # (type (;5;) (func (param i32 i32 i32) (result i32)))
51 | # (import "env" "eth2_loadPreStateRoot" (func $eth2_loadPreStateRoot (type 0)))
52 | # (import "env" "debug_printMem" (func $debug_printMem (type 1)))
53 | # (import "env" "debug_print32" (func $debug_print32 (type 0)))
54 | # (import "env" "debug_print64" (func $debug_print64 (type 2)))
55 | # (import "env" "debug_printMemHex" (func $debug_printMemHex (type 1)))
56 | # (import "env" "eth2_blockDataSize" (func $eth2_blockDataSize (type 3)))
57 | # (import "env" "eth2_pushNewDeposit" (func $eth2_pushNewDeposit (type 1)))
58 | # (import "env" "eth2_savePostStateRoot" (func $eth2_savePostStateRoot (type 0)))
59 |
60 | # pub fn eth2_loadPreStateRoot(offset: *const u32);
61 | # pub fn eth2_blockDataSize() -> u32;
62 | # pub fn eth2_blockDataCopy(outputOfset: *const u32, offset: u32, length: u32);
63 | # pub fn eth2_savePostStateRoot(offset: *const u32);
64 | # pub fn eth2_pushNewDeposit(offset: *const u32, length: u32);
65 |
66 | # pub fn debug_print32(value: u32);
67 | # pub fn debug_print64(value: u64);
68 | # pub fn debug_printMem(offset: *const u32, len: uint32)
69 | # pub fn debug_printMemHex(offset: *const u32, len: u32);
70 | # pub fn debug_printStorage(pathOffset: *const u32);
71 | # pub fn debug_printStorageHex(pathOffset: *const u32);
72 |
--------------------------------------------------------------------------------
/nimplay/ewasm_runtime.nim:
--------------------------------------------------------------------------------
1 | import macros
2 |
3 | import utils
4 |
5 | {.push cdecl, importc.}
6 |
7 | proc useGas*(amount: int64)
8 | ## Subtracts an amount to the gas counter
9 | ##
10 | ## Parameters:
11 | ## `amount` - the amount to subtract to the gas counter
12 |
13 | proc getAddress*(resultOffset: pointer)
14 | ## Gets address of currently executing account and stores it in memory at
15 | ## the given offset.
16 | ##
17 | ## Parameters:
18 | ## `resultOffset` the memory offset at which the address is to be stored (`address`)
19 |
20 | proc getExternalBalance*(addressOffset, resultOffset: pointer)
21 | ## Gets balance of the given account and loads it into memory at the given offset.
22 | ##
23 | ## Parameters:
24 | ## `addressOffset` the memory offset to load the address from (`address`)
25 | # `resultOffset` the memory offset to load the balance into (`u128`)
26 |
27 | proc getBlockHash*(number: int64, resultOffset: pointer)
28 | ## Gets the hash of one of the 256 most recent complete blocks.
29 | ##
30 | ## Parameters:
31 | ## `number` which block to load
32 | ## `resultOffset` the memory offset to load the hash into (`u256`)
33 |
34 | proc call*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
35 | dataLength: int32): int32
36 | ## Sends a message with arbitrary data to a given address path
37 | ##
38 | ## Parameters:
39 | ## `gas` **i64** the gas limit
40 | ## `addressOffset` the memory offset to load the address from (`address`)
41 | ## `valueOffset` the memory offset to load the value from (`u128`)
42 | ## `dataOffset` the memory offset to load data from (`bytes`)
43 | ## `dataLength` the length of data
44 | ## Returns:
45 | ## 0 on success, 1 on failure and 2 on `revert`
46 | proc call*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
47 | dataLength: int32, resultOffset: pointer,
48 | resultLength: int32): int32
49 |
50 | proc callDataCopy*(resultOffset: pointer, dataOffset, length: int32)
51 | ## Copies the input data in current environment to memory. This pertains to
52 | ## the input data passed with the message call instruction or transaction.
53 | ##
54 | ## Parameters:
55 | ## `resultOffset` the memory offset to load data into (`bytes`)
56 | ## `dataOffset` the offset in the input data
57 | ## `length` the length of data to copy
58 |
59 | proc getCallDataSize*(): int32
60 | ## Get size of input data in current environment. This pertains to the input
61 | ## data passed with the message call instruction or transaction.
62 | ##
63 | ## Returns: call data size
64 |
65 | proc callCode*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
66 | dataLength: int32): int32
67 | ## Message-call into this account with an alternative account's code.
68 | ##
69 | ## Parameters:
70 | ## `gas` the gas limit
71 | ## `addressOffset` the memory offset to load the address from (`address`)
72 | ## `valueOffset` the memory offset to load the value from (`u128`)
73 | ## `dataOffset` the memory offset to load data from (`bytes`)
74 | ## `dataLength` the length of data
75 | ##
76 | ## Returns: 0 on success, 1 on failure and 2 on `revert`
77 |
78 | proc callDelegate*(gas: int64, addressOffset, dataOffset: pointer,
79 | dataLength: int32)
80 | ## Message-call into this account with an alternative account’s code, but
81 | ## persisting the current values for sender and value.
82 | ##
83 | ## Parameters:
84 | ## `gas` the gas limit
85 | ## `addressOffset` the memory offset to load the address from (`address`)
86 | ## `dataOffset` the memory offset to load data from (`bytes`)
87 | ## `dataLength` the length of data
88 | ##
89 | ## Returns: 0 on success, 1 on failure and 2 on `revert`
90 |
91 | proc callStatic*(gas: int64, addressOffset, dataOffset: pointer,
92 | dataLength: int32)
93 | ## Sends a message with arbitrary data to a given address path, but disallow
94 | ## state modifications. This includes `log`, `create`, `selfdestruct` and `call`
95 | ## with a non-zero value.
96 | ##
97 | ## Parameters:
98 | ## `gas` the gas limit
99 | ## `addressOffset` the memory offset to load the address from (`address`)
100 | ## `dataOffset` the memory offset to load data from (`bytes`)
101 | ## `dataLength` the length of data
102 | ##
103 | ## Returns: 0 on success, 1 on failure and 2 on `revert`
104 |
105 | proc storageStore*(pathOffset, valueOffset: pointer)
106 | ## Store 256-bit a value in memory to persistent storage
107 | ##
108 | ## Parameters:
109 | ## `pathOffset` the memory offset to load the path from (`u256`)
110 | ## `valueOffset` the memory offset to load the value from (`u256`)
111 |
112 | proc storageLoad*(pathOffset, valueOffset: pointer)
113 | ## Loads a 256-bit a value to memory from persistent storage
114 | ##
115 | ## Parameters:
116 | ## `pathOffset` the memory offset to load the path from (`u256`)
117 | ## `resultOffset` the memory offset to store the result at (`u256`)
118 |
119 | proc getCaller*(resultOffset: pointer)
120 | ## Gets caller address and loads it into memory at the given offset. This is
121 | ## the address of the account that is directly responsible for this execution.
122 | ##
123 | ## Parameters:
124 | ## `resultOffset` the memory offset to load the address into (`address`)
125 |
126 | proc getCallValue*(resultOffset: pointer)
127 | ## Gets the deposited value by the instruction/transaction responsible for
128 | ## this execution and loads it into memory at the given location.
129 | ##
130 | ## Parameters:
131 | ## `resultOffset` the memory offset to load the value into (`u128`)
132 |
133 | proc codeCopy*(resultOffset: pointer, codeOffset, length: int32)
134 | ## Copies the code running in current environment to memory.
135 | ##
136 | ## Parameters:
137 | ## `resultOffset` the memory offset to load the result into (`bytes`)
138 | ## `codeOffset` the offset within the code
139 | ## `length` the length of code to copy
140 |
141 | proc getCodeSize*(): int32
142 | ## Gets the size of code running in current environment.
143 |
144 | proc getBlockCoinbase*(resultOffset: pointer)
145 | ## Gets the block’s beneficiary address and loads into memory.
146 | ##
147 | ## Parameters:
148 | ## `resultOffset` the memory offset to load the coinbase address into (`address`)
149 |
150 | proc create*(valueOffset, dataOffset: pointer, length: int32, resultOffset: pointer): int32
151 | ## Creates a new contract with a given value.
152 | ##
153 | ## Parameters:
154 | ## `valueOffset` the memory offset to load the value from (`u128`)
155 | ## `dataOffset` the memory offset to load the code for the new contract from (`bytes`)
156 | ## `length` the data length
157 | ## `resultOffset` the memory offset to write the new contract address to (`address`)
158 | ##
159 | ## Note: `create` will clear the return buffer in case of success or may fill
160 | ## it with data coming from `revert`.
161 | ##
162 | ## Returns: 0 on success, 1 on failure and 2 on `revert`
163 |
164 | proc getBlockDifficulty*(resultOffset: pointer)
165 | ## Get the block’s difficulty.
166 | ##
167 | ## Parameters:
168 | ## `resultOffset` the memory offset to load the difficulty into (`u256`)
169 |
170 | proc externalCodeCopy*(addressOffset, resultOffset: pointer, codeOffset, length: int32)
171 | ## Copies the code of an account to memory.
172 | ##
173 | ## Parameters:
174 | ## `addressOffset` the memory offset to load the address from (`address`)
175 | ## `resultOffset` the memory offset to load the result into (`bytes`)
176 | ## `codeOffset` the offset within the code
177 | ## `length` the length of code to copy
178 |
179 | proc getExternalCodeSize*(addressOffset: pointer): int32
180 | ## Get size of an account’s code.
181 | ##
182 | ## Parameters:
183 | ## `addressOffset` the memory offset to load the address from (`address`)
184 |
185 | proc getGasLeft*(): int64
186 | ## Returns the current gasCounter
187 |
188 | proc getBlockGasLimit*(): int64
189 | ## Get the block’s gas limit.
190 |
191 | proc getTxGasPrice*(valueOffset: pointer)
192 | ## Gets price of gas in current environment.
193 | ##
194 | ## Parameters:
195 | ## `valueOffset` the memory offset to write the value to (`u128`)
196 |
197 | proc log*(dataOffset: pointer, length, numberOfTopics: int32,
198 | topic1, topic2, topic3, topic4: pointer)
199 | ## Creates a new log in the current environment
200 | ##
201 | ## Parameters:
202 | ## `dataOffset` the memory offset to load data from (`bytes`)
203 | ## `length` the data length
204 | ## `numberOfTopics` the number of topics following (0 to 4)
205 | ## `topic1` the memory offset to load topic1 from (`u256`)
206 | ## `topic2` the memory offset to load topic2 from (`u256`)
207 | ## `topic3` the memory offset to load topic3 from (`u256`)
208 | ## `topic4` the memory offset to load topic4 from (`u256`)
209 |
210 | proc getBlockNumber*(): int64
211 | ## Get the block’s number.
212 |
213 | proc getTxOrigin*(resultOffset: pointer)
214 | ## Gets the execution's origination address and loads it into memory at the
215 | ## given offset. This is the sender of original transaction; it is never an
216 | ## account with non-empty associated code.
217 | ##
218 | ## Parameters:
219 | ## `resultOffset` the memory offset to load the origin address from (`address`)
220 |
221 | proc finish*(dataOffset: pointer, length: int32) {.noreturn.}
222 | ## Set the returning output data for the execution. This will cause a trap and
223 | ## the execution will be aborted immediately.
224 | ##
225 | ## Note: multiple invocations will overwrite the previous data.
226 | ##
227 | ## Parameters:
228 | ## `dataOffset` the memory offset of the output data (`bytes`)
229 | ## `length` the length of the output data
230 |
231 | proc revert*(dataOffset: pointer, length: int32) {.noreturn.}
232 | ## Set the returning output data for the execution. This will cause a trap and
233 | ## the execution will be aborted immediately.
234 | ##
235 | ## Note: multiple invocations will overwrite the previous data.
236 | ##
237 | ## Parameters:
238 | ## `dataOffset` the memory offset of the output data (`bytes`)
239 | ## `length` the length of the output data
240 |
241 | proc getReturnDataSize*(): int32
242 | ## Get size of current return data buffer to memory. This contains the return
243 | ## data from the last executed `call`, `callCode`, `callDelegate`, `callStatic`
244 | ## or `create`.
245 | ##
246 | ## Note: `create` only fills the return data buffer in case of a failure.
247 |
248 | proc returnDataCopy*(resultOffset: pointer, dataOffset, length: int32)
249 | ## Copies the current return data buffer to memory. This contains the return data
250 | ## from last executed `call`, `callCode`, `callDelegate`, `callStatic` or `create`.
251 | ##
252 | ## Note: `create` only fills the return data buffer in case of a failure.
253 | ##
254 | ## Parameters:
255 | ## `resultOffset` the memory offset to load data into (`bytes`)
256 | ## `dataOffset` the offset in the return data
257 | ## `length` the length of data to copy
258 |
259 | proc selfDestruct*(addressOffset: pointer)
260 | ## Mark account for later deletion and give the remaining balance to the specified
261 | ## beneficiary address. This will cause a trap and the execution will be aborted
262 | ## immediately.
263 | ##
264 | ## Parameters:
265 | ## `addressOffset` the memory offset to load the address from (`address`)
266 |
267 | proc getBlockTimestamp*(): int64
268 | ## Get the block’s timestamp.
269 |
270 | {.pop.}
271 |
272 | proc callDataCopy*[T](res: var T, offset: int) {.inline.} =
273 | callDataCopy(addr res, offset.int32, sizeof(res).int32)
274 |
275 | proc storageLoad*[N](path: array[N, byte], res: pointer) {.inline.} =
276 | when path.len < 32:
277 | type PaddedPath {.packed.} = object
278 | padding: array[32 - path.len, byte]
279 | p: array[N, byte]
280 | var p: PaddedPath
281 | p.p = path
282 | storageLoad(addr p, res)
283 | else:
284 | storageLoad(unsafeAddr path[0], res)
285 |
286 | proc storageStore*[N](path: array[N, byte], res: pointer) {.inline.} =
287 | when path.len < 32:
288 | type PaddedPath {.packed.} = object
289 | padding: array[32 - path.len, byte]
290 | p: array[N, byte]
291 | var p: PaddedPath
292 | p.p = path
293 | storageStore(addr p, res)
294 | else:
295 | storageStore(unsafeAddr path[0], res)
296 |
297 | macro exportwasm*(p: untyped): untyped =
298 | expectKind(p, nnkProcDef)
299 | result = p
300 | result.addPragma(newIdentNode("exportc"))
301 | result.addPragma(
302 | newColonExpr(
303 | newIdentNode("codegenDecl"), newLit("__attribute__ ((visibility (\"default\"))) $# $#$#")
304 | )
305 | )
306 |
--------------------------------------------------------------------------------
/nimplay/function_signature.nim:
--------------------------------------------------------------------------------
1 | import macros, strutils, stint, strformat
2 |
3 | import ./utils, ./types
4 |
5 |
6 | template generate_method_sig*(func_sig: untyped, v2_sig: bool = false): string =
7 | var inputs: seq[string]
8 | for input in func_sig.inputs:
9 | inputs.add(input.var_type)
10 | var method_str = func_sig.name & "(" & join(inputs, ",") & ")"
11 | if v2_sig and len(func_sig.outputs) > 0:
12 | var outputs: seq[string]
13 | for output in func_sig.outputs:
14 | outputs.add(output.var_type)
15 | method_str = method_str & ":(" & join(outputs, ",") & ")"
16 | method_str
17 |
18 |
19 | proc getKHash*(inp: string): string {.compileTime.} =
20 | when defined(osk256_sig):
21 | let exec_string = "/usr/bin/k256_sig \"" & inp & "\""
22 | else:
23 | let exec_string = "../tools/k256_sig \"" & inp & "\""
24 | let outp_shell = staticExec(exec_string)
25 | return outp_shell
26 |
27 |
28 | proc generate_method_id*(func_sig: FunctionSignature): string =
29 | # var method_str = generate_method_sig(func_sig)
30 | # echo method_str
31 | # return keccak_256.digest(method_str).data
32 | return getKHash(generate_method_sig(func_sig))[0..7]
33 |
34 |
35 | proc generate_function_signature*(proc_def: NimNode, global_ctx: GlobalContext): FunctionSignature =
36 | expectKind(proc_def, nnkProcDef)
37 |
38 | var func_sig = FunctionSignature()
39 | for child in proc_def:
40 | case child.kind:
41 | of nnkIdent:
42 | func_sig.name = strVal(child)
43 | func_sig.is_private = true
44 | of nnkPostfix:
45 | func_sig.name = strVal(child[1])
46 | func_sig.is_private = false
47 | of nnkFormalParams:
48 | for param in child:
49 | case param.kind
50 | of nnkIdent:
51 | func_sig.outputs.add(
52 | VariableType(
53 | name: "out1",
54 | var_type: strVal(param)
55 | )
56 | )
57 | of nnkIdentDefs:
58 | check_valid_variable_name(param[0], global_ctx)
59 | func_sig.inputs.add(
60 | VariableType(
61 | name: strVal(param[0]),
62 | var_type: strVal(param[1])
63 | )
64 | )
65 | of nnkEmpty:
66 | discard
67 | else:
68 | raise newException(Exception, "unknown param type" & treeRepr(child))
69 | of nnkPragma:
70 | for pragma_child in child:
71 | if pragma_child.kind == nnkIdent:
72 | var pragma_name = strVal(pragma_child)
73 | # Add pragma to list of pragmas
74 | if not (pragma_name in ALLOWED_PRAGMAS):
75 | raiseParserError(
76 | fmt"Unsupported pragma: {pragma_name}, must be one of " & ALLOWED_PRAGMAS.join(","),
77 | child
78 | )
79 | func_sig.pragma_base_keywords.add(pragma_name)
80 | if pragma_name == "payable":
81 | func_sig.payable = true
82 | else:
83 | discard
84 | # raise newException(Exception, "unknown func type" & treeRepr(child))
85 |
86 | func_sig.method_sig = generate_method_sig(func_sig)
87 | func_sig.method_id = generate_method_id(func_sig)
88 | func_sig.line_info = lineInfoObj(proc_def)
89 |
90 | return func_sig
91 |
92 |
93 | proc strip_pragmas*(proc_def: NimNode): NimNode =
94 | var new_proc_def = copyNimNode(proc_def)
95 | for c in proc_def:
96 | if c.kind != nnkPragma:
97 | new_proc_def.add(copyNimTree(c))
98 | else:
99 | new_proc_def.add(newEmptyNode())
100 | return new_proc_def
101 |
102 |
--------------------------------------------------------------------------------
/nimplay/logging.nim:
--------------------------------------------------------------------------------
1 | import
2 | macros, strutils, strformat, tables
3 |
4 | import
5 | ./types, ./utils, ./function_signature
6 |
7 |
8 | proc get_buffer_sizes(event_sig: EventSignature): (int, int) =
9 | var
10 | data_total_size = 0
11 | indexed_total_size = 32 # log signature always place in topic 1
12 | for event in event_sig.inputs:
13 | if event.indexed:
14 | indexed_total_size += get_byte_size_of(event.var_type)
15 | else:
16 | data_total_size += get_byte_size_of(event.var_type)
17 | (data_total_size, indexed_total_size) # return
18 |
19 |
20 | proc get_converter_ident(param_name, param_type: string): (int, string) =
21 | case param_type:
22 | of "uint256":
23 | (0, param_name & ".toByteArrayBE")
24 | of "uint128":
25 | (16, param_name & ".toByteArrayBE")
26 | of "address":
27 | (32 - 20, param_name)
28 | else:
29 | (0, param_name)
30 |
31 |
32 | proc get_indexed_param_locations(event_sig: EventSignature): seq[int] =
33 | var
34 | locations: seq[int]
35 | i = 0
36 | for event_param in event_sig.inputs:
37 | if event_param.indexed:
38 | locations.add(event_param.param_position)
39 | i += 1
40 | locations # return
41 |
42 |
43 | proc get_param_copiers(event_sig: EventSignature, buffer_name: string, indexed: bool, start_position: int = 0): (NimNode, Table[string, int]) =
44 | var
45 | copy_stmts = newStmtList()
46 | current_abi_offset = start_position
47 | position_map: Table[string, int]
48 | for event_param in event_sig.inputs:
49 | if event_param.indexed == indexed:
50 | var (type_offset, converted_param_name) = get_converter_ident(event_param.name, event_param.var_type)
51 | var abi_bytearr_offset = current_abi_offset + type_offset
52 | position_map[event_param.name] = current_abi_offset
53 | copy_stmts.add(
54 | parseStmt(
55 | fmt"""
56 | copy_into_ba({buffer_name}, {abi_bytearr_offset}, {converted_param_name})
57 | """)
58 | )
59 | current_abi_offset += get_byte_size_of(event_param.var_type)
60 | (copy_stmts, position_map)
61 |
62 |
63 | proc get_new_proc_def(event_sig: EventSignature): NimNode =
64 | # copyNimTree(func_def)
65 | var
66 | param_list: seq[string]
67 | for param in event_sig.inputs:
68 | param_list.add(fmt"{param.name}:{param.var_type}")
69 | var
70 | param_str = param_list.join(";")
71 | new_proc = parseExpr(
72 | fmt"""
73 | proc {event_sig.name} ({param_str}) =
74 | discard
75 | """
76 | )
77 | new_proc
78 |
79 |
80 | proc generate_log_func*(log_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
81 | # To make a log statement we allocate a chunk of data memory.
82 | # This buffer contains:
83 | # 0-32 contains the 32 byte keccak, this also becomes topic 1 (indexed).
84 | # 32-N contains the rest of the data portion, the data portion is not indexed.
85 | var
86 | event_name = log_keyword.split(".")[1]
87 | new_proc_name = fmt"log_{event_name}"
88 | event_sig = global_ctx.events[event_name]
89 | func_def = event_sig.definition
90 | event_func_def = get_new_proc_def(event_sig)
91 | (data_total_size, indexed_total_size) = get_buffer_sizes(event_sig)
92 | event_id_int_arr = hexstr_to_intarray[32](getKHash(generate_method_sig(event_sig)))
93 | data_buffer_name = "data_output_buffer"
94 | indexed_buffer_name = "indexed_output_buffer"
95 | indexed_param_locations = get_indexed_param_locations(event_sig)
96 |
97 | # Create function definition.
98 |
99 | event_func_def[0] = newIdentNode(new_proc_name)
100 | event_func_def[4] = newEmptyNode()
101 | # Create data section buffer.
102 | event_func_def[6] = parseStmt(
103 | fmt"""
104 | var
105 | {data_buffer_name} {{.noinit.}}: array[{data_total_size}, byte]
106 | {indexed_buffer_name} {{.noinit.}}: array[{indexed_total_size}, byte]
107 | """
108 | )
109 |
110 | var
111 | event_sig_int_arr = nnkBracket.newTree()
112 | for x in event_id_int_arr:
113 | event_sig_int_arr.add(parseExpr(intToStr(x) & "'u8"))
114 | event_func_def[6].add(
115 | newCommentStmtNode("Set event signature")
116 | )
117 | event_func_def[6].add(
118 | nnkAsgn.newTree(
119 | nnkBracketExpr.newTree(
120 | newIdentNode(indexed_buffer_name),
121 | nnkInfix.newTree(
122 | newIdentNode(".."),
123 | newLit(0),
124 | newLit(31)
125 | )
126 | ),
127 | event_sig_int_arr
128 | )
129 | )
130 |
131 | let
132 | # Copy data into memory
133 | (data_copiers, _) = get_param_copiers(
134 | event_sig,
135 | data_buffer_name,
136 | indexed=false
137 | )
138 | # Copy indexed data into memory
139 | (indexed_copiers, indexed_position_maps) = get_param_copiers(
140 | event_sig,
141 | indexed_buffer_name,
142 | indexed=true,
143 | start_position=32
144 | )
145 | event_func_def[6].add(
146 | data_copiers
147 | )
148 | event_func_def[6].add(
149 | indexed_copiers
150 | )
151 | # Create topic parameters (1-3, after indexed function sig).
152 | var
153 | topic_list: seq[string]
154 | topic_count = 0
155 | for i, param in event_sig.inputs:
156 | if param.indexed:
157 | topic_list.add(fmt"addr {indexed_buffer_name}[{intToStr(indexed_position_maps[param.name])}]")
158 | topic_count += 1
159 | # Add nils
160 | for _ in 1..(3 - len(topic_list)):
161 | topic_list.add("nil")
162 | # Call Log
163 | event_func_def[6].add(
164 | parseStmt(
165 | fmt"""
166 | log(addr {data_buffer_name}[0], {data_total_size}.int32, {1 + topic_count}.int32, addr {indexed_buffer_name}[0], {topic_list.join(",")})
167 | """
168 | )
169 | )
170 | return (event_func_def, new_proc_name)
171 |
172 |
173 | proc generate_next_call_log_node*(kw_key_name: string, global_keyword_map: Table[string, string], current_node: NimNode): NimNode =
174 | if kw_key_name.startsWith("log."):
175 | let event_func_name = kw_key_name.split(".")[1]
176 | var new_call = copyNimTree(current_node)
177 | new_call[0] = newIdentNode(global_keyword_map[kw_key_name])
178 | return new_call
179 | else:
180 | raise newException(ParserError, fmt"Unknown '{kw_key_name}' log keyword supplied")
181 |
--------------------------------------------------------------------------------
/nimplay/nimplay_macros.nim:
--------------------------------------------------------------------------------
1 | import macros
2 | import stint
3 | import system
4 | import strformat
5 | import tables
6 | import endians
7 | import sequtils
8 | import strutils
9 |
10 | import ./function_signature, ./builtin_keywords, ./types, ./utils
11 |
12 |
13 | proc get_func_name(proc_def: NimNode): string =
14 | var func_name = ""
15 | for child in proc_def:
16 | if child.kind == nnkPostfix:
17 | func_name = strVal(child[1])
18 | break
19 | if child.kind == nnkIdent:
20 | func_name = strVal(child)
21 | break
22 | return func_name
23 |
24 |
25 | proc get_local_input_type_conversion(tmp_var_name, tmp_var_converted_name, var_type: string): (NimNode, NimNode) =
26 | case var_type
27 | of "uint256", "uint128", "wei_value":
28 | var base_stint_type = "Uint256"
29 | if var_type in @["uint128", "wei_value"]:
30 | base_stint_type = "UInt128"
31 | var convert_node = nnkLetSection.newTree(
32 | nnkIdentDefs.newTree(
33 | newIdentNode(tmp_var_converted_name),
34 | newIdentNode(var_type),
35 | nnkCall.newTree(
36 | nnkDotExpr.newTree(
37 | newIdentNode(base_stint_type),
38 | newIdentNode("fromBytesBE")
39 | ),
40 | newIdentNode(tmp_var_name)
41 | )
42 | )
43 | )
44 | var ident_node = newIdentNode(tmp_var_converted_name)
45 | return (ident_node, convert_node)
46 | of "bytes32":
47 | return (newIdentNode(tmp_var_name), newEmptyNode())
48 | of "address":
49 | var convert_node = parseStmt(unindent(fmt"""
50 | var {tmp_var_converted_name}: address
51 | copy_into_ba({tmp_var_converted_name}, 12, {tmp_var_name})
52 | """
53 | ))
54 | return (newIdentNode(tmp_var_converted_name), convert_node)
55 | else:
56 | raise newException(ParserError, fmt"get_local_input_type_conversion: Unknown '{var_type}' type supplied!")
57 |
58 |
59 | proc get_local_output_type_conversion(tmp_result_name, tmp_result_converted_name, var_type: string): (NimNode, NimNode) =
60 | case var_type
61 | of "bool":
62 | var ident_node = newIdentNode(tmp_result_converted_name)
63 | var conversion_node = parseStmt(unindent(fmt"""
64 | var {tmp_result_converted_name}: array[32, byte]
65 | if {tmp_result_name}: {tmp_result_converted_name}[31] = 1
66 | """))
67 | return (ident_node, conversion_node)
68 | of "uint256":
69 | var ident_node = newIdentNode(tmp_result_converted_name)
70 | var conversion_node = nnkVarSection.newTree(
71 | nnkIdentDefs.newTree(
72 | newIdentNode(tmp_result_converted_name),
73 | newEmptyNode(),
74 | nnkDotExpr.newTree(
75 | newIdentNode(tmp_result_name),
76 | newIdentNode("toByteArrayBE")
77 | )
78 | )
79 | )
80 | return (ident_node, conversion_node)
81 | of "uint128", "wei_value":
82 | var ident_node = newIdentNode(tmp_result_converted_name)
83 | var conversion_node = parseStmt(unindent(fmt"""
84 | var {tmp_result_converted_name} {{.noinit.}}: array[32, byte]
85 | {tmp_result_converted_name}[16..31] = toByteArrayBE({tmp_result_name})
86 | """))
87 | return (ident_node, conversion_node)
88 | of "address":
89 | var ident_node = newIdentNode(tmp_result_converted_name)
90 | var conversion_node = nnkStmtList.newTree(
91 | nnkVarSection.newTree( # var a: array[32, byte]
92 | nnkIdentDefs.newTree(
93 | newIdentNode(tmp_result_converted_name),
94 | nnkBracketExpr.newTree(
95 | newIdentNode("array"),
96 | newLit(32),
97 | newIdentNode("byte"),
98 | ),
99 | newEmptyNode()
100 | )
101 | ),
102 | nnkAsgn.newTree( # a[11..31] = tmp_addr
103 | nnkBracketExpr.newTree(
104 | newIdentNode(tmp_result_converted_name),
105 | nnkInfix.newTree(
106 | newIdentNode(".."),
107 | newLit(12),
108 | newLit(31)
109 | )
110 | ),
111 | newIdentNode(tmp_result_name)
112 | )
113 | )
114 | return (ident_node, conversion_node)
115 | # return (newIdentNode(tmp_result_name), newEmptyNode())
116 | of "bytes32":
117 | return (newIdentNode(tmp_result_name), newEmptyNode())
118 | else:
119 | raise newException(ParserError, fmt"Unknown '{var_type}' type supplied!")
120 |
121 |
122 | proc generate_context(proc_def: NimNode, global_ctx: GlobalContext): LocalContext =
123 | var ctx = LocalContext()
124 | ctx.name = get_func_name(proc_def)
125 | ctx.sig = generate_function_signature(proc_def, global_ctx)
126 | (ctx.keyword_define_stmts, ctx.global_keyword_map) = get_keyword_defines(proc_def, global_ctx, ctx)
127 | return ctx
128 |
129 | proc handle_global_storage_tables(table_def: NimNode, global_ctx :var GlobalContext, slot_number: var int) =
130 | expectKind(table_def, nnkCall)
131 | var
132 | key_types: seq[string]
133 | var_name = strVal(table_def[0])
134 | var_types = table_def[1][0]
135 |
136 | for x in var_types[1..var_types.len - 1]:
137 | key_types.add(strVal(x))
138 |
139 | var var_struct = VariableType(
140 | name: var_name,
141 | var_type: "StorageTable",
142 | key_types: key_types,
143 | slot: slot_number
144 | )
145 | global_ctx.global_variables[var_name] = var_struct
146 | inc(slot_number)
147 |
148 |
149 | proc handle_global_defines(var_section: NimNode, global_ctx :var GlobalContext, slot_number: var int) =
150 |
151 | for child in var_section:
152 | case child.kind
153 | of nnkIdentDefs:
154 | var create_getter = false
155 | if (child[0].kind, child[1].kind) == (nnkPostfix, nnkIdent) and strVal(child[0][0]) == "*":
156 | # create getter.
157 | create_getter = true
158 | elif (child[0].kind, child[1].kind) != (nnkIdent, nnkIdent):
159 | raiseParserError(
160 | "Global variables need to be defined as 'var_name: var_type'",
161 | child
162 | )
163 |
164 | var
165 | var_node = if create_getter: child[0][1] else: child[0]
166 | var_name = strVal(var_node)
167 | var_type = strVal(child[1])
168 |
169 | check_valid_variable_name(var_node, global_ctx)
170 | # echo var_name & " -> " & var_type
171 | if var_name in global_ctx.global_variables:
172 | raiseParserError(
173 | fmt"Global variable '{var_name}' has already been defined",
174 | child
175 | )
176 | var var_struct = VariableType(
177 | name: var_name,
178 | var_type: var_type,
179 | slot: slot_number
180 | )
181 | if create_getter:
182 | global_ctx.getter_funcs.add(var_struct)
183 | global_ctx.global_variables[var_name] = var_struct
184 | inc(slot_number)
185 | else:
186 | raiseParserError(
187 | fmt"Unsupported statement in global var section",
188 | child
189 | )
190 |
191 |
192 | proc handle_event_defines(event_def: NimNode, global_ctx: var GlobalContext) =
193 | expectKind(event_def, nnkProcDef)
194 | if event_def[6].kind != nnkEmpty:
195 | raiseParserError("Event definition expect no function bodies", event_def)
196 | if not (event_def[4].kind == nnkPragma and strVal(event_def[4][0]) == "event"):
197 | raiseParserError("Events require event pragma e.g. 'proc EventName(a: uint256) {.event.}'", event_def)
198 |
199 | var event_name = strVal(event_def[0])
200 | if event_name in global_ctx.events:
201 | raiseParserError(fmt"Event '{event_name}' has already been defined.", event_def)
202 | var
203 | event_sig = EventSignature(
204 | name: event_name,
205 | definition: event_def
206 | )
207 | param_position = 0
208 |
209 | for child in event_def:
210 | if child.kind == nnkFormalParams:
211 | var params = child
212 | for param in params:
213 | if param.kind == nnkIdentDefs:
214 | if param[0].kind == nnkPragmaExpr and param[0][1].kind == nnkPragma:
215 | var
216 | pragma_expr = param[0][1][0]
217 | if pragma_expr.kind != nnkIdent or strVal(pragma_expr) != "indexed":
218 | raiseParserError("Unsupported pragma", pragma_expr)
219 | event_sig.inputs.add(EventType(
220 | name: strVal(param[0][0]),
221 | var_type: strVal(param[1]),
222 | indexed: true,
223 | param_position: param_position
224 | ))
225 | elif param[0].kind == nnkIdent:
226 | check_valid_variable_name(param[0], global_ctx)
227 | var param_name = strVal(param[0])
228 | event_sig.inputs.add(EventType(
229 | name: param_name,
230 | var_type: strVal(param[1]),
231 | indexed: false,
232 | param_position: param_position
233 | ))
234 | else:
235 | raiseParserError("Unsupported event parameter type", params)
236 | param_position += 1
237 | if len(event_sig.inputs) == 0:
238 | raiseParserError("Event requires parameters", event_def)
239 | if filter(event_sig.inputs, proc(x: EventType): bool = x.indexed).len > 3:
240 | raiseParserError("Can only have 3 indexed parameters", event_def)
241 | global_ctx.events[event_name] = event_sig
242 |
243 |
244 | template get_util_functions() {.dirty.} =
245 | proc copy_into_ba(to_ba: var auto, offset: int, from_ba: auto) =
246 | for i, x in from_ba:
247 | if offset + i > sizeof(to_ba) - 1:
248 | break
249 | to_ba[offset + i] = x
250 |
251 | func to_bytes32(a: auto): bytes32 =
252 | var
253 | tmp: bytes32
254 | copy_into_ba(tmp, 0, a.toBytesLE)
255 | tmp
256 |
257 | func to_uint128(a: bytes32): uint128 =
258 | var
259 | tmp_ba {.noinit.}: array[32, byte]
260 | tmp: uint128
261 | copy_into_ba(tmp_ba, 0, a)
262 | tmp = fromBytes(Uint128, tmp_ba)
263 | tmp
264 |
265 | func to_uint256(a: bytes32): uint256 =
266 | var
267 | tmp_ba {.noinit.}: array[32, byte]
268 | tmp: uint256
269 | copy_into_ba(tmp_ba, 0, a)
270 | tmp = fromBytes(Uint256, tmp_ba)
271 | tmp
272 |
273 | proc assertNotPayable() =
274 | var b {.noinit.}: array[16, byte]
275 | getCallValue(addr b)
276 | if Uint128.fromBytesBE(b) > 0.stuint(128):
277 | revert(nil, 0)
278 |
279 | proc exitMessage(s: string) =
280 | revert(cstring(s), s.len.int32)
281 |
282 | proc concat[I1, I2: static[int]; T](a: array[I1, T], b: array[I2, T]): array[I1 + I2, T] =
283 | result[0..a.high] = a
284 | result[a.len..result.high] = b
285 |
286 | proc getHashedKey[N](table_id: int32, key: array[N, byte]): bytes32 =
287 | type CombinedKey {.packed.} = object
288 | table_id: int32
289 | key: array[N, byte]
290 | var
291 | hashed_key {.noinit.}: bytes32
292 | combined_key: CombinedKey
293 | sha256_address: array[20, byte]
294 |
295 | sha256_address[19] = 2'u8
296 | combined_key.table_id = table_id
297 | combined_key.key = key
298 |
299 | var res = call(
300 | getGasLeft(), # gas
301 | addr sha256_address, # addressOffset (ptr)
302 | nil, # valueOffset (ptr)
303 | addr combined_key, # dataOffset (ptr)
304 | sizeof(combined_key).int32, # dataLength
305 | )
306 | if res == 1: # call failed
307 | exitMessage("Could not call sha256 in setTableValue")
308 | # raise newException(Exception, "Could not call sha256 in setTableValue")
309 | if getReturnDataSize() != 32.int32:
310 | exitMessage("Could not call sha256, Incorrect return size")
311 | returnDataCopy(addr hashed_key, 0.int32, 32.int32)
312 |
313 | hashed_key # return
314 |
315 | proc setTableValue[N](table_id: int32, key: array[N, byte], value: var bytes32) =
316 | var hashed_key = getHashedKey(table_id, key)
317 | storageStore(hashed_key, addr value)
318 |
319 | proc getTableValue[N](table_id: int32, key: array[N, byte], ): bytes32 =
320 | var
321 | value: bytes32
322 | hashed_key: bytes32 = getHashedKey(table_id, key)
323 | storageLoad(hashed_key, addr value)
324 | value # return
325 |
326 |
327 | proc get_getter_func(var_struct: VariableType): NimNode =
328 | let
329 | proc_name_node = newIdentNode(var_struct.name)
330 | proc_return_type_node = newIdentNode(var_struct.var_type)
331 | storage_name_node = newIdentNode(var_struct.name)
332 | quote do:
333 | proc `proc_name_node`*():`proc_return_type_node` {.self.} = ## generated getter
334 | self.`storage_name_node`
335 |
336 |
337 | proc check_default_func(sig: FunctionSignature, global_ctx: GlobalContext, child: NimNode) =
338 | ## Check default function is correctly defined.
339 | if global_ctx.has_default_func:
340 | raiseParserError("Only one default function may be defined per Contract.", child)
341 | if sig.is_private:
342 | raiseParserError("Default function has to be public and payable.", child)
343 | if sig.inputs.len > 0 or sig.outputs.len > 0:
344 | raiseParserError("Default function can not have arguments nor return a value.", child)
345 |
346 |
347 | proc handle_contract_interface(in_stmts: NimNode): NimNode =
348 | var
349 | main_out_stmts = newStmtList()
350 | function_signatures = newSeq[FunctionSignature]()
351 | global_ctx = GlobalContext()
352 |
353 | main_out_stmts.add(getAst(get_util_functions()))
354 | # var util_funcs = get_util_functions()
355 | # discard util_funcs
356 | var slot_number = 0
357 | for child in in_stmts:
358 | if child.kind == nnkCall and child[1].kind == nnkStmtList and child[1][0].kind == nnkBracketExpr:
359 | if strVal(child[1][0][0]) == "StorageTable":
360 | handle_global_storage_tables(child, global_ctx, slot_number)
361 | else:
362 | raiseParserError("Unsupported global definition.", child)
363 | if child.kind == nnkVarSection:
364 | handle_global_defines(child, global_ctx, slot_number)
365 |
366 | # Inject getter functions if needed.
367 | if global_ctx.getter_funcs.len > 0:
368 | for var_struct in global_ctx.getter_funcs:
369 | in_stmts.add(get_getter_func(var_struct))
370 |
371 | for child in in_stmts:
372 | case child.kind:
373 | of nnkProcDef:
374 | if child[6].kind == nnkEmpty: # Event definition.
375 | handle_event_defines(child, global_ctx)
376 | continue
377 | var ctx = generate_context(child, global_ctx)
378 | if ctx.sig.name == "default":
379 | check_default_func(ctx.sig, global_ctx, child)
380 | global_ctx.has_default_func = true
381 | function_signatures.add(ctx.sig)
382 | var new_proc_def = strip_pragmas(child)
383 | new_proc_def = replace_keywords(
384 | ast_node=new_proc_def,
385 | global_keyword_map=ctx.global_keyword_map,
386 | global_ctx=global_ctx
387 | )
388 | # Insert global defines.
389 | new_proc_def[6].insert(0, ctx.keyword_define_stmts)
390 | main_out_stmts.add(new_proc_def)
391 | else:
392 | discard
393 |
394 | if filter(function_signatures, proc(x: FunctionSignature): bool = not x.is_private).len == 0:
395 | raise newException(
396 | ParserError,
397 | "No public functions have been defined, use * postfix to annotate public functions. e.g. proc myfunc*(a: uint256)"
398 | )
399 |
400 | # Build Main Entrypoint.
401 | var out_stmts = newStmtList()
402 | out_stmts.add(
403 | nnkVarSection.newTree( # var selector: uint32
404 | nnkIdentDefs.newTree(
405 | newIdentNode("selector"),
406 | newIdentNode("uint32"),
407 | newEmptyNode()
408 | )
409 | ),
410 | nnkCall.newTree( # callDataCopy(selector, 0)
411 | newIdentNode("callDataCopy"),
412 | newIdentNode("selector"),
413 | newLit(0)
414 | ),
415 | )
416 |
417 | # Convert selector.
418 | out_stmts.add(
419 | nnkStmtList.newTree(
420 | newCall(
421 | bindSym"bigEndian32",
422 | nnkCommand.newTree(
423 | newIdentNode("addr"),
424 | newIdentNode("selector")
425 | ),
426 | nnkCommand.newTree(
427 | newIdentNode("addr"),
428 | newIdentNode("selector")
429 | )
430 | )
431 | )
432 | )
433 |
434 | var selector_CaseStmt = nnkCaseStmt.newTree(
435 | newIdentNode("selector")
436 | )
437 |
438 | # Build function selector.
439 | for func_sig in function_signatures:
440 | if func_sig.is_private:
441 | continue
442 | if func_sig.name == "default" : # default function should have a selector.
443 | continue
444 | echo "Building " & func_sig.method_sig
445 | var call_and_copy_block = nnkStmtList.newTree()
446 | var call_to_func = nnkCall.newTree(
447 | newIdentNode(func_sig.name)
448 | )
449 | var start_offset = 4
450 |
451 | if not func_sig.payable:
452 | call_and_copy_block.add(parseStmt("assertNotPayable()"))
453 |
454 | for idx, param in func_sig.inputs:
455 | var
456 | static_param_size = get_byte_size_of(param.var_type)
457 | tmp_var_name = fmt"{func_sig.name}_param_{idx}"
458 | tmp_var_converted_name = fmt"{func_sig.name}_param_{idx}_converted"
459 | # var :
460 | # callDataCopy(addr , , )
461 | call_and_copy_block.add(
462 | parseStmt(unindent(fmt"""
463 | var {tmp_var_name} {{.noinit.}}: array[{static_param_size}, byte]
464 | callDataCopy(addr {tmp_var_name}, {start_offset}, {static_param_size})
465 | """)
466 | )
467 | )
468 |
469 | # Get conversion code if necessary.
470 | let (ident_node, convert_node) = get_local_input_type_conversion(
471 | tmp_var_name,
472 | tmp_var_converted_name,
473 | param.var_type
474 | )
475 | if not (ident_node.kind == nnkEmpty):
476 | if not (convert_node.kind == nnkEmpty):
477 | call_and_copy_block.add(convert_node)
478 | call_to_func.add(ident_node)
479 | start_offset += static_param_size
480 |
481 | # Handle returned data from function.
482 | if len(func_sig.outputs) == 0:
483 | # Add final function call.
484 | call_and_copy_block.add(call_to_func)
485 | call_and_copy_block.add(
486 | nnkStmtList.newTree(
487 | nnkCall.newTree(
488 | newIdentNode("finish"),
489 | newNilLit(),
490 | newLit(0)
491 | )
492 | )
493 | )
494 | elif len(func_sig.outputs) == 1:
495 | var assign_result_block = nnkAsgn.newTree()
496 | var param = func_sig.outputs[0]
497 | var idx = 0
498 | # create placeholder variables
499 | var tmp_result_name = fmt"{func_sig.name}_result_{idx}"
500 | var tmp_result_converted_name = tmp_result_name & "_arr"
501 | call_and_copy_block.add(
502 | nnkVarSection.newTree(
503 | nnkIdentDefs.newTree(
504 | nnkPragmaExpr.newTree(
505 | newIdentNode(tmp_result_name),
506 | nnkPragma.newTree(
507 | newIdentNode("noinit")
508 | )
509 | ),
510 | newIdentNode(param.var_type),
511 | newEmptyNode()
512 | )
513 | )
514 | )
515 | assign_result_block.add(newIdentNode(tmp_result_name))
516 | assign_result_block.add(call_to_func)
517 |
518 | call_and_copy_block.add(assign_result_block)
519 | let (tmp_conversion_ident_node, conversion_node) = get_local_output_type_conversion(
520 | tmp_result_name,
521 | tmp_result_converted_name,
522 | param.var_type
523 | )
524 |
525 | call_and_copy_block.add(conversion_node)
526 |
527 | call_and_copy_block.add(
528 | nnkCall.newTree(
529 | newIdentNode("finish"),
530 | nnkCommand.newTree(
531 | newIdentNode("addr"),
532 | tmp_conversion_ident_node
533 | ),
534 | newLit(get_byte_size_of(param.var_type))
535 | )
536 | )
537 | else:
538 | raiseParserError(
539 | "Can only handle functions with a single variable output ATM.",
540 | func_sig.line_info
541 | )
542 |
543 | selector_CaseStmt.add(
544 | nnkOfBranch.newTree( # of 0x<>'u32:
545 | parseExpr( "0x" & func_sig.method_id & "'u32"),
546 | call_and_copy_block
547 | )
548 | )
549 |
550 | # Add default function.
551 | var default_func_node: NimNode
552 | if global_ctx.has_default_func:
553 | var
554 | default_fsig = filter(
555 | function_signatures, proc(x: FunctionSignature): bool = x.name == "default"
556 | )[0]
557 | assert_payable_str = if default_fsig.payable: "assertNotPayable()" else: ""
558 | default_func_node = parseStmt(unindent(fmt"""
559 | {assert_payable_str}
560 | default()
561 | finish(nil, 0)
562 | """))
563 | else:
564 | default_func_node = parseStmt(unindent("""
565 | discard
566 | """))
567 | echo treeRepr(default_func_node)
568 | # Add default to function table.
569 | selector_CaseStmt.add(
570 | nnkElse.newTree(
571 | default_func_node
572 | )
573 | )
574 | out_stmts.add(selector_CaseStmt)
575 | out_stmts.add(nnkCall.newTree(
576 | newIdentNode("revert"),
577 | newNilLit(),
578 | newLit(0)
579 | )
580 | )
581 |
582 | # Build Main Func
583 | # proc main() {.exportwasm.} =
584 | # if getCallDataSize() < 4:
585 | # revert(nil, 0)
586 |
587 | var main_func = nnkStmtList.newTree(
588 | nnkProcDef.newTree(
589 | newIdentNode("main"),
590 | newEmptyNode(),
591 | newEmptyNode(),
592 | nnkFormalParams.newTree(
593 | newEmptyNode()
594 | ),
595 | nnkPragma.newTree(
596 | newIdentNode("exportwasm")
597 | ),
598 | newEmptyNode(),
599 | out_stmts,
600 | )
601 | )
602 | main_out_stmts.add(main_func)
603 | # echo out_stmts
604 |
605 | # build selector:
606 | # keccak256("balance(address):(uint64)")[0, 4]
607 |
608 | return main_out_stmts
609 |
610 |
611 | macro contract*(contract_name: string, proc_def: untyped): untyped =
612 | echo contract_name
613 | # echo "Before:"
614 | # echo treeRepr(proc_def)
615 | expectKind(proc_def, nnkStmtList)
616 | var stmtlist = handle_contract_interface(proc_def)
617 | echo "Final Contract Code:"
618 | echo repr(stmtlist)
619 | return stmtlist
620 |
--------------------------------------------------------------------------------
/nimplay/storage.nim:
--------------------------------------------------------------------------------
1 | import macros
2 | import strformat
3 | import strutils
4 | import tables
5 |
6 | import ./types, ./utils
7 |
8 |
9 | proc generate_storage_get_func*(storage_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
10 | var
11 | global_var_name = storage_keyword.split(".")[1]
12 | new_proc_name = fmt"get_{global_var_name}_from_storage"
13 | var_info = global_ctx.global_variables[global_var_name]
14 | slot_number = var_info.slot
15 |
16 | if var_info.var_type == "uint256":
17 | var new_proc = parseStmt(fmt"""
18 | proc {new_proc_name}(): uint256 =
19 | var
20 | tmp {{.noinit.}}: array[32, byte]
21 | pos = {$slot_number}.stuint(32).toByteArrayBE
22 | storageLoad(pos, addr tmp)
23 | return Uint256.fromBytesBE(tmp)
24 | """)
25 | return (new_proc, new_proc_name)
26 | elif var_info.var_type == "uint128" or var_info.var_type == "wei_value":
27 | var new_proc = parseStmt(fmt"""
28 | proc {new_proc_name}(): {var_info.var_type} =
29 | var
30 | tmp {{.noinit.}}: array[32, byte]
31 | tmp_ba {{.noinit.}}: array[16, byte]
32 | pos = {$slot_number}.stuint(32).toByteArrayBE
33 | storageLoad(pos, addr tmp)
34 | for i in 0..15:
35 | tmp_ba[i] = tmp[i]
36 | return Uint128.fromBytesBE(tmp_ba)
37 | """)
38 | return (new_proc, new_proc_name)
39 | elif var_info.var_type == "address":
40 | var new_proc = parseStmt(fmt"""
41 | proc {new_proc_name}(): address =
42 | var
43 | tmp {{.noinit.}}: array[32, byte]
44 | pos = {$slot_number}.stuint(32).toByteArrayBE
45 | storageLoad(pos, addr tmp)
46 | var out_var: address
47 | # out_var[0..19] = tmp[12..31]
48 | for i, b in tmp:
49 | if i >= 12:
50 | out_var[i - 12] = b
51 | return out_var
52 | """)
53 | return (new_proc, new_proc_name)
54 | elif var_info.var_type == "bytes32":
55 | var new_proc = parseStmt(fmt"""
56 | proc {new_proc_name}(): bytes32 =
57 | var
58 | tmp {{.noinit.}}: bytes32
59 | pos = {$slot_number}.stuint(32).toByteArrayBE
60 | storageLoad(pos, addr tmp)
61 | return tmp
62 | """)
63 | return (new_proc, new_proc_name)
64 | elif var_info.var_type == "bool":
65 | var new_proc = parseStmt(fmt"""
66 | proc {new_proc_name}(): bool =
67 | var
68 | tmp {{.noinit.}}: bytes32
69 | pos = {$slot_number}.stuint(32).toByteArrayBE
70 | storageLoad(pos, addr tmp)
71 | if tmp[31] == 1:
72 | return true
73 | else:
74 | return false
75 | """)
76 | return (new_proc, new_proc_name)
77 | else:
78 | raise newException(ParserError, var_info.var_type & " storage is not supported at the moment.")
79 |
80 |
81 | proc generate_storage_set_func*(storage_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
82 | var
83 | global_var_name = storage_keyword.split(".")[1]
84 | new_proc_name = fmt"set_{global_var_name}_in_storage"
85 | var_info = global_ctx.global_variables[global_var_name]
86 | slot_number = var_info.slot
87 |
88 | if var_info.var_type == "uint256":
89 | var new_proc = parseStmt(fmt"""
90 | proc {new_proc_name}(value:uint256) =
91 | var
92 | tmp {{.noinit.}}: array[32, byte] = value.toByteArrayBE
93 | pos = {$slot_number}.stuint(32).toByteArrayBE
94 | storageStore(pos, addr tmp)
95 | """)
96 | return (new_proc, new_proc_name)
97 | elif var_info.var_type == "uint128" or var_info.var_type == "wei_value":
98 | var new_proc = parseStmt(fmt"""
99 | proc {new_proc_name}(value: {var_info.var_type}) =
100 | var
101 | tmp {{.noinit.}}: array[32, byte]
102 | pos = {$slot_number}.stuint(32).toByteArrayBE
103 | tmp_ba = value.toByteArrayBE
104 | for i in 0..15:
105 | tmp[i] = tmp_ba[i]
106 | storageStore(pos, addr tmp)
107 | """)
108 | return (new_proc, new_proc_name)
109 | elif var_info.var_type == "address":
110 | var new_proc = parseStmt(fmt"""
111 | proc {new_proc_name}(value: address) =
112 | var
113 | tmp {{.noinit.}}: array[32, byte]
114 | pos = {$slot_number}.stuint(32).toByteArrayBE
115 | for i, b in value:
116 | tmp[12 + i] = b
117 | storageStore(pos, addr tmp)
118 | """)
119 | return (new_proc, new_proc_name)
120 | elif var_info.var_type == "bytes32":
121 | var new_proc = parseStmt(fmt"""
122 | proc {new_proc_name}(value: bytes32) =
123 | var pos = {$slot_number}.stuint(32).toByteArrayBE
124 | storageStore(pos, unsafeAddr value)
125 | """)
126 | return (new_proc, new_proc_name)
127 | elif var_info.var_type == "bool":
128 | var new_proc = parseStmt(fmt"""
129 | proc {new_proc_name}(value: bool) =
130 | var
131 | pos = {$slot_number}.stuint(32).toByteArrayBE
132 | v: bytes32
133 | if value:
134 | v[31] = 1u8
135 | storageStore(pos, unsafeAddr value)
136 | """)
137 | return (new_proc, new_proc_name)
138 | else:
139 | raise newException(ParserError, var_info.var_type & " storage is not supported at the moment.")
140 |
141 |
142 | proc get_combined_key_info(var_info: VariableType): (string, seq[string], string, int) =
143 | var
144 | value_conversion = ""
145 | key_param_arr: seq[string]
146 | key_copy_block: string
147 | total_key_size = 0
148 | key_count = 0
149 |
150 | for key_type in var_info.key_types[0..^2]:
151 | var current_key_name = fmt"key{key_count}"
152 | key_param_arr.add(
153 | fmt"{current_key_name}: " & key_type
154 | )
155 | key_copy_block.add(
156 | fmt" copy_into_ba(combined_key, {total_key_size}, {current_key_name})" & '\n'
157 | )
158 | inc(key_count)
159 | total_key_size += get_memory_byte_size_of(key_type)
160 |
161 | (value_conversion, key_param_arr, key_copy_block, total_key_size)
162 |
163 |
164 | proc generate_storage_table_set_func*(storage_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
165 | var
166 | global_var_name = storage_keyword.split(".")[1]
167 | var_info = global_ctx.global_variables[global_var_name]
168 | new_proc_name = fmt"set_{global_var_name}_in_storage_table"
169 | value_type = var_info.key_types[^1]
170 | table_id = var_info.slot
171 | (value_conversion, key_param_arr, key_copy_block, total_key_size) = get_combined_key_info(var_info)
172 |
173 | if value_type == "bytes32":
174 | value_conversion = "tmp_val = val"
175 | elif value_type in @["wei_value", "uint128"]:
176 | value_conversion = "tmp_val = val.to_bytes32"
177 | elif value_type == "uint256":
178 | value_conversion = "tmp_val = val.to_bytes32"
179 | elif value_type == "bool":
180 | value_conversion = "if val: tmp_val[31] = 1"
181 | else:
182 | raise newException(ParserError, "Unsupported '{value_type}' value type.")
183 |
184 | var s = fmt"""
185 | proc {new_proc_name}({key_param_arr.join(",")}, val: {value_type}) =
186 | var
187 | {value_conversion}
188 | combined_key {{.noinit.}}: array[{total_key_size}, byte]
189 | {key_copy_block}
190 | setTableValue({table_id}.int32, combined_key, tmp_val)
191 | """
192 | echo s
193 | var new_proc = parseStmt(s)
194 | return (new_proc, new_proc_name)
195 |
196 |
197 | proc generate_storage_table_get_func*(storage_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
198 | var
199 | global_var_name = storage_keyword.split(".")[1]
200 | new_proc_name = fmt"get_{global_var_name}_in_storage_table"
201 | var_info = global_ctx.global_variables[global_var_name]
202 | value_type = var_info.key_types[^1]
203 | table_id = var_info.slot
204 | (value_conversion, key_param_arr, key_copy_block, total_key_size) = get_combined_key_info(var_info)
205 | echo value_type
206 |
207 | if value_type == "bytes32":
208 | value_conversion = "tmp_val"
209 | elif value_type == "bool":
210 | value_conversion = "tmp_val[31] == 1"
211 | elif value_type in @["wei_value", "uint128"]:
212 | value_conversion = "tmp_val.to_uint128"
213 | elif value_type == "uint256":
214 | value_conversion = "tmp_val.to_uint256"
215 | else:
216 | raise newException(ParserError, "Unsupported StorageTable '{value_type}' value type.")
217 |
218 | var new_proc = parseStmt(fmt"""
219 | proc {new_proc_name}({key_param_arr.join(",")}): {value_type} =
220 | var
221 | tmp_val: bytes32
222 | combined_key {{.noinit.}}: array[{total_key_size}, byte]
223 | {key_copy_block}
224 | tmp_val = getTableValue({table_id}.int32, combined_key)
225 | {value_conversion}
226 | """)
227 | return (new_proc, new_proc_name)
228 |
--------------------------------------------------------------------------------
/nimplay/substrate_runtime.nim:
--------------------------------------------------------------------------------
1 | import macros
2 |
3 | {.push cdecl, importc.}
4 |
5 | proc ext_address*()
6 | proc ext_block_number*()
7 | proc ext_gas_left*()
8 | proc ext_gas_price*()
9 | proc ext_get_storage*(key_ptr: pointer): int32
10 | proc ext_now*()
11 | proc ext_println*(str_ptr: pointer, str_len: int32) # experimental; will be removed.
12 | proc ext_random_seed*()
13 | proc ext_scratch_read*(dest_ptr: pointer, offset: int32, len: int32)
14 | proc ext_scratch_size*(): int32
15 | proc ext_scratch_write*(src_ptr: pointer, len: int32)
16 | proc ext_set_rent_allowance*(value_ptr: pointer, value_len: int32)
17 | proc ext_set_storage*(key_ptr: pointer, value_non_null: int32, value_ptr: pointer, value_len: int32)
18 | proc ext_value_transferred*()
19 |
20 | {.pop.}
21 |
22 | macro exportwasm*(p: untyped): untyped =
23 | expectKind(p, nnkProcDef)
24 | result = p
25 | result.addPragma(newIdentNode("exportc"))
26 | result.addPragma(
27 | newColonExpr(
28 | newIdentNode("codegenDecl"), newLit("__attribute__ ((visibility (\"default\"))) $# $#$#")
29 | )
30 | )
31 |
--------------------------------------------------------------------------------
/nimplay/types.nim:
--------------------------------------------------------------------------------
1 | import stint
2 | import tables
3 | import macros
4 | import sequtils
5 |
6 | # Nimplay types.
7 |
8 | type
9 | uint256* = StUint[256]
10 | int128* = StInt[128]
11 | uint128* = StUint[128]
12 | address* = array[20, byte]
13 | bytes32* = array[32, byte]
14 | wei_value* = uint128
15 |
16 | # Nimplay structs.
17 |
18 | type
19 | VariableType* = object
20 | name*: string
21 | var_type*: string
22 | key_types*: seq[string]
23 | slot*: int64
24 |
25 | type
26 | EventType* = object
27 | name*: string
28 | var_type*: string
29 | indexed*: bool
30 | param_position*: int
31 |
32 | type
33 | FunctionSignature* = object
34 | name*: string
35 | inputs*: seq[VariableType]
36 | outputs*: seq[VariableType]
37 | constant*: bool
38 | payable*: bool
39 | method_id*: string
40 | method_sig*: string
41 | is_private*: bool
42 | line_info*: LineInfo
43 | pragma_base_keywords*: seq[string] # list of pragmas
44 |
45 | type
46 | EventSignature* = object
47 | name*: string
48 | inputs*: seq[EventType]
49 | outputs*: seq[EventType]
50 | definition*: NimNode
51 |
52 | type
53 | LocalContext* = object
54 | # Name of the function.
55 | name*: string
56 | # Function signature, used for function selection and ABI encoding / decoding.
57 | sig*: FunctionSignature
58 | # Global temp variables create at beginning of the function.
59 | keyword_define_stmts*: NimNode
60 | # Map of temp variables that have to be replaced by.
61 | global_keyword_map*: Table[string, string]
62 |
63 | type
64 | GlobalContext* = object
65 | global_variables*: Table[string, VariableType]
66 | events*: Table[string, EventSignature]
67 | getter_funcs*: seq[VariableType]
68 | has_default_func*: bool
69 |
70 | # Exceptions.
71 |
72 | type ParserError* = object of Exception
73 |
74 | # Nimplay constants.
75 |
76 | let
77 | KEYWORDS* {.compileTime.} = @["contract", "self", "log"]
78 | TYPE_NAMES* {.compileTime.} = @["address", "uint256", "bytes32", "int128", "uint128"]
79 | ALL_KEYWORDS* {.compileTime.} = concat(TYPE_NAMES, KEYWORDS)
80 | ALLOWED_PRAGMAS* {.compileTime.} = @["payable", "event", "self", "msg", "log"]
81 |
82 | # Constants
83 |
84 | let ZERO_ADDRESS*: address = [
85 | 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
86 | 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8
87 | ]
88 |
--------------------------------------------------------------------------------
/nimplay/utils.nim:
--------------------------------------------------------------------------------
1 | import
2 | macros, strformat, strutils,
3 | tables, math, macros
4 |
5 | import
6 | ./types
7 |
8 |
9 | proc raiseParserError*(in_msg: string, line_info: LineInfo) =
10 | var out_msg = "\n\n" & line_info.filename & "\n"
11 | var line_no = 1
12 | var line_no_prefix = $line_no & ":"
13 | var file_str = staticRead(line_info.filename)
14 | for line in file_str.splitLines():
15 | if line_no == line_info.line:
16 | line_no_prefix = $line_no & ":"
17 | out_msg &= line_no_prefix
18 | out_msg &= line & "\n"
19 | out_msg &= "-".repeat(line_info.column + line_no_prefix.len) & "^\n"
20 | break
21 | inc(line_no)
22 | out_msg &= "PlayParserError: " & in_msg & "\n\n"
23 | raise newException(ParserError, out_msg)
24 |
25 |
26 | proc raiseParserError*(msg: string, node: NimNode) =
27 | let line_info = lineInfoObj(node)
28 | raiseParserError(msg, line_info)
29 |
30 |
31 | proc valid_var_chars(var_name: string): bool =
32 | var res = false
33 | for c in var_name:
34 | if c in {'a'..'z'}:
35 | res = true
36 | elif c in {'A'..'Z'}:
37 | res = true
38 | elif c == '_':
39 | res = true
40 | elif c in {'0'..'9'}:
41 | res = true
42 | else:
43 | res = false
44 | break
45 | return res
46 |
47 |
48 | proc check_valid_variable_name*(node: NimNode, global_ctx: GlobalContext) =
49 | expectKind(node, nnkIdent)
50 | var
51 | name = strVal(node)
52 | err_msg = ""
53 |
54 | if global_ctx.global_variables.hasKey(name):
55 | err_msg = "Variable name is same as a global variable, please chose another name."
56 | elif name in ALL_KEYWORDS:
57 | err_msg = "Variable name to similar to a keyword."
58 | elif not valid_var_chars(name): # prevent homograph attack.
59 | err_msg = "Invalid variable name, only alphanumeric characters with underscore are supported."
60 | if err_msg != "":
61 | raiseParserError(err_msg, node)
62 |
63 |
64 | proc get_byte_size_of*(type_str: string): int =
65 | let BASE32_TYPES_NAMES: array = [
66 | "bool",
67 | "uint256",
68 | "uint128",
69 | "int128",
70 | "address",
71 | "bytes32",
72 | "wei_value"
73 | ]
74 | if type_str in BASE32_TYPES_NAMES:
75 | return 32
76 | else:
77 | raise newException(ParserError, fmt"Unknown '{type_str}' type supplied!")
78 |
79 |
80 | proc get_memory_byte_size_of*(type_str: string): int =
81 | if type_str in @["uint256", "bytes32"]:
82 | return 32
83 | elif type_str in @["address"]:
84 | return 20
85 | elif type_str in @["uint128", "int128", "wei_value"]:
86 | return 16
87 | else:
88 | raise newException(ParserError, fmt"Unknown '{type_str}' type supplied!")
89 |
90 |
91 | func get_bit_size_of*(type_str: string): int =
92 | get_byte_size_of(type_str) * 8
93 |
94 |
95 | proc hexstr_tointarray*[N: static[int]](in_str: string): array[N, int]=
96 | var out_arr: array[N, int]
97 | for i in countup(0, in_str.len - 2, 2):
98 | out_arr[floorDiv(i, 2)] = fromHex[int](in_str[i..i+1])
99 | out_arr
100 |
--------------------------------------------------------------------------------
/nimplay0_1.nim:
--------------------------------------------------------------------------------
1 | import nimplay/nimplay_macros
2 | import nimplay/types
3 | import nimplay/ewasm_runtime
4 | import nimcrypto/[sha, ripemd, hash, blake2]
5 | import stint
6 |
7 | export nimplay_macros
8 | export types
9 | export ewasm_runtime
10 | export stint
11 | export hash
12 | export sha
13 | export ripemd
14 | export blake2
15 |
--------------------------------------------------------------------------------
/tests/ee/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
3 | RUST_VERSION="nightly"
4 | REPO_DIR="ewasm-scout"
5 | REPO_URL="https://github.com/ewasm/scout.git"
6 | SCOUT_TEST_FILES=./*.yml
7 |
8 |
9 | if [[ -e $REPO_DIR ]]; then
10 | rm -v $REPO_DIR/*.yml
11 | cp $SCOUT_TEST_FILES $REPO_DIR/
12 | cd $REPO_DIR
13 | git pull
14 | else
15 | git clone $REPO_URL $REPO_DIR
16 | rm -v $REPO_DIR/*.yml
17 | cp $SCOUT_TEST_FILES $REPO_DIR/
18 | cd $REPO_DIR
19 | fi
20 |
21 |
22 | if [[ ! -e $CARGO_HOME ]]; then
23 | echo "Fetching rustup"
24 | curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain $RUST_VERSION
25 | fi
26 |
27 | rustup target add wasm32-unknown-unknown
28 | rustup component add rustfmt
29 | rustup update
30 | cargo install chisel
31 | # make build
32 | cargo build --release
33 | # make test
34 | # target/release/phase2-scout $SCOUT_TEST_FILE
35 |
36 | for testfile in $SCOUT_TEST_FILES
37 | do
38 | echo $testfile
39 | RUST_LOG=debug target/release/phase2-scout $testfile || exit
40 | break
41 | done
42 |
--------------------------------------------------------------------------------
/tests/ee/test_block_echo.yml:
--------------------------------------------------------------------------------
1 |
2 | beacon_state:
3 | execution_scripts:
4 | - ../../../examples/ee/block_echo.wasm
5 | shard_pre_state:
6 | exec_env_states:
7 | - "0000000000000000000000000000000000000000000000000000000000000000"
8 | shard_blocks:
9 | - env: 0
10 | data: "fafbfcfdfef0f0f0f0f0ffffffffffffffffffffffffffffffffe6e5e4e3e2e1"
11 | shard_post_state:
12 | exec_env_states:
13 | - "fafbfcfdfef0f0f0f0f0ffffffffffffffffffffffffffffffffe6e5e4e3e2e1"
14 |
--------------------------------------------------------------------------------
/tests/ee/test_helloworld.yml:
--------------------------------------------------------------------------------
1 |
2 | beacon_state:
3 | execution_scripts:
4 | - ../../../examples/ee/helloworld.wasm
5 | shard_pre_state:
6 | exec_env_states:
7 | - "0000000000000000000000000000000000000000000000000000000000000000"
8 | shard_blocks:
9 | - env: 0
10 | data: ""
11 | - env: 0
12 | data: ""
13 | shard_post_state:
14 | exec_env_states:
15 | - "0000000000000000000000000000000000000000000000000000000000001122"
16 |
--------------------------------------------------------------------------------
/tests/substrate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "srml-contract-waterfall-nimplay",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "author": "Sergey Shulepov ",
6 | "contributors": [
7 | "Stefanie Doll "
8 | ],
9 | "license": "MIT",
10 | "dependencies": {
11 | "@polkadot/api": "^0.97.0-beta.39",
12 | "@polkadot/api-contract": "^0.97.0-beta.39",
13 | "blakejs": "^1.1.0",
14 | "typescript": "^3.6.4"
15 | },
16 | "devDependencies": {
17 | "@types/jest": "^24.0.18",
18 | "@types/node": "^10.12.18",
19 | "@typescript-eslint/eslint-plugin": "^2.6.0",
20 | "@typescript-eslint/parser": "^2.6.0",
21 | "bn.js": "^5.0.0",
22 | "eslint": "^6.6.0",
23 | "jest": "^24.9.0",
24 | "ts-jest": "^24.1.0",
25 | "ts-loader": "^5.3.2"
26 | },
27 | "jest": {
28 | "preset": "ts-jest/presets/js-with-ts"
29 | },
30 | "scripts": {
31 | "test": "NODE_ENV=abc jest"
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/substrate/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -z "$SUBSTRATE_PATH" ]; then
4 | echo "Please specify the path to substrate in the SUBSTRATE_PATH environment variable"
5 | exit 1
6 | fi
7 |
8 | if [ ! -f "$SUBSTRATE_PATH" ]; then
9 | echo "$SUBSTRATE_PATH doesn't exist"
10 | exit 2
11 | fi
12 |
13 | # Purge dev chain and then spin up the substrate node in background
14 | $SUBSTRATE_PATH purge-chain --dev -y
15 | $SUBSTRATE_PATH --dev -l debug &
16 | SUBSTRATE_PID=$!
17 |
18 | # # Execute tests
19 | yarn && yarn test --verbose
20 |
21 | # # Kill the spawned substrate node
22 | kill -9 $SUBSTRATE_PID
23 |
--------------------------------------------------------------------------------
/tests/substrate/tests/consts.ts:
--------------------------------------------------------------------------------
1 | import BN from "bn.js";
2 |
3 | export const WSURL = "ws://127.0.0.1:9944";
4 | export const DOT: BN = new BN("1000000000000000");
5 | export const CREATION_FEE: BN = DOT.muln(200);
6 | export const GAS_REQUIRED = 50000;
7 | export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
8 | export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
9 |
10 |
--------------------------------------------------------------------------------
/tests/substrate/tests/contract-nimplay.spec.ts:
--------------------------------------------------------------------------------
1 | // Adapted from https://github.com/paritytech/srml-contracts-waterfall/
2 |
3 | import { ApiPromise, SubmittableResult, WsProvider } from "@polkadot/api";
4 | import { Abi } from '@polkadot/api-contract';
5 | import testKeyring from "@polkadot/keyring/testing";
6 | import { u8aToHex } from "@polkadot/util";
7 | import { randomAsU8a } from "@polkadot/util-crypto";
8 | import { KeyringPair } from "@polkadot/keyring/types";
9 | import { Option } from "@polkadot/types";
10 | import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
11 |
12 | import { ALICE, CREATION_FEE, WSURL } from "./consts";
13 | import {
14 | callContract,
15 | instantiate,
16 | getContractStorage,
17 | putCode
18 | } from "./utils";
19 |
20 | // This is a test account that is going to be created and funded each test.
21 | const keyring = testKeyring({ type: "sr25519" });
22 | const alicePair = keyring.getPair(ALICE);
23 | let testAccount: KeyringPair;
24 | let api: ApiPromise;
25 |
26 | beforeAll((): void => {
27 | jest.setTimeout(30000);
28 | });
29 |
30 | beforeEach(
31 | async (done): Promise<() => void> => {
32 | api = await ApiPromise.create({ provider: new WsProvider(WSURL) });
33 | testAccount = keyring.addFromSeed(randomAsU8a(32));
34 |
35 | return api.tx.balances
36 | .transfer(testAccount.address, CREATION_FEE.muln(3))
37 | .signAndSend(alicePair, (result: SubmittableResult): void => {
38 | if (
39 | result.status.isFinalized &&
40 | result.findRecord("system", "ExtrinsicSuccess")
41 | ) {
42 | console.log("New test account has been created.");
43 | done();
44 | }
45 | });
46 | }
47 | );
48 |
49 | describe("Nimplay Hellow World", () => {
50 | test("Can deploy and execute", async (done): Promise => {
51 | const codeHash = await putCode(
52 | api,
53 | testAccount,
54 | "../../../examples/substrate/hello_world.wasm"
55 | );
56 | expect(codeHash).toBeDefined();
57 | const address: Address = await instantiate(
58 | api,
59 | testAccount,
60 | codeHash,
61 | "0x00",
62 | CREATION_FEE
63 | );
64 | expect(address).toBeDefined();
65 | await callContract(api, testAccount, address, "0x00");
66 | done();
67 | });
68 | });
69 |
70 | describe("Nimplay Storage Setter", () => {
71 | test("Setter: Can deploy and execute", async (done): Promise => {
72 | // See https://github.com/paritytech/srml-contracts-waterfall/issues/6 for info about
73 | // how to get the STORAGE_KEY of an instantiated contract
74 |
75 | const STORAGE_KEY = (new Uint8Array(32)).fill(2);
76 | // Deploy contract code on chain and retrieve the code hash
77 | const codeHash = await putCode(
78 | api,
79 | testAccount,
80 | "../../../examples/substrate/setter.wasm"
81 | );
82 | expect(codeHash).toBeDefined();
83 |
84 | // Instantiate a new contract instance and retrieve the contracts address
85 | const address: Address = await instantiate(
86 | api,
87 | testAccount,
88 | codeHash,
89 | "0x00",
90 | CREATION_FEE
91 | );
92 | expect(address).toBeDefined();
93 |
94 | const initialValue: Uint8Array = await getContractStorage(
95 | api,
96 | address,
97 | STORAGE_KEY
98 | );
99 | expect(initialValue).toBeDefined();
100 | expect(initialValue.toString()).toEqual("");
101 |
102 | await callContract(api, testAccount, address, "0x00");
103 | var val_hex = "03".repeat(32);
104 | // "0x00" indicates calling "Set" Action
105 | await callContract(api, testAccount, address, "0x00" + val_hex);
106 | const newValue = await getContractStorage(api, address, STORAGE_KEY);
107 | expect(newValue.toString()).toEqual("0x" + val_hex);
108 |
109 | done();
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/tests/substrate/tests/utils.ts:
--------------------------------------------------------------------------------
1 | import { ApiPromise, SubmittableResult } from "@polkadot/api";
2 | import { KeyringPair } from "@polkadot/keyring/types";
3 | import { Option, StorageData } from "@polkadot/types";
4 | import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
5 | import BN from "bn.js";
6 | import fs from "fs";
7 | import path from "path";
8 | const blake = require('blakejs')
9 |
10 | import { GAS_REQUIRED } from "./consts";
11 |
12 | export async function sendAndReturnFinalized(signer: KeyringPair, tx: any) {
13 | return new Promise(function(resolve, reject) {
14 | tx.signAndSend(signer, (result: SubmittableResult) => {
15 | if (result.status.isFinalized) {
16 | // Return result of the submittable extrinsic after the transfer is finalized
17 | resolve(result as SubmittableResult);
18 | }
19 | if (
20 | result.status.isDropped ||
21 | result.status.isInvalid ||
22 | result.status.isUsurped
23 | ) {
24 | reject(result as SubmittableResult);
25 | console.error("ERROR: Transaction could not be finalized.");
26 | }
27 | });
28 | });
29 | }
30 |
31 | export async function putCode(
32 | api: ApiPromise,
33 | signer: KeyringPair,
34 | fileName: string,
35 | gasRequired: number = GAS_REQUIRED
36 | ): Promise {
37 | const wasmCode = fs
38 | .readFileSync(path.join(__dirname, fileName))
39 | .toString("hex");
40 | const tx = api.tx.contracts.putCode(gasRequired, `0x${wasmCode}`);
41 | const result: any = await sendAndReturnFinalized(signer, tx);
42 | const record = result.findRecord("contracts", "CodeStored");
43 |
44 | if (!record) {
45 | console.error("ERROR: No code stored after executing putCode()");
46 | }
47 | // Return code hash.
48 | return record.event.data[0];
49 | }
50 |
51 | export async function instantiate(
52 | api: ApiPromise,
53 | signer: KeyringPair,
54 | codeHash: Hash,
55 | inputData: any,
56 | endowment: BN,
57 | gasRequired: number = GAS_REQUIRED
58 | ): Promise {
59 | const tx = api.tx.contracts.instantiate(
60 | endowment,
61 | gasRequired,
62 | codeHash,
63 | inputData
64 | );
65 | const result: any = await sendAndReturnFinalized(signer, tx);
66 | const record = result.findRecord("contracts", "Instantiated");
67 |
68 | if (!record) {
69 | console.error("ERROR: No new instantiated contract");
70 | }
71 | // Return the address of instantiated contract.
72 | return record.event.data[1];
73 | }
74 |
75 | export async function callContract(
76 | api: ApiPromise,
77 | signer: KeyringPair,
78 | contractAddress: Address,
79 | inputData: any,
80 | gasRequired: number = GAS_REQUIRED,
81 | endowment: number = 0
82 | ): Promise {
83 | const tx = api.tx.contracts.call(
84 | contractAddress,
85 | endowment,
86 | gasRequired,
87 | inputData
88 | );
89 | await sendAndReturnFinalized(signer, tx);
90 | }
91 |
92 | export async function getContractStorage(
93 | api: ApiPromise,
94 | contractAddress: Address,
95 | storageKey: Uint8Array
96 | ): Promise {
97 | const contractInfo = await api.query.contracts.contractInfoOf(
98 | contractAddress
99 | );
100 | // Return the value of the contracts storage
101 | const storageKeyBlake2b = blake.blake2bHex(storageKey, null, 32);
102 | return await api.rpc.state.getChildStorage(
103 | (contractInfo as Option).unwrap().asAlive.trieId,
104 | '0x' + storageKeyBlake2b
105 | );
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/tests/substrate/tsconfig.js:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "compilerOptions": {
4 | "target": "esnext",
5 | "module": "commonjs",
6 | "lib": ["dom", "es2018"],
7 | "resolveJsonModule": true,
8 | "strict": true,
9 | "noImplicitAny": true,
10 | "strictNullChecks": true,
11 | "strictFunctionTypes": true,
12 | "strictBindCallApply": true,
13 | "strictPropertyInitialization": true,
14 | "noImplicitThis": true,
15 | "alwaysStrict": true,
16 | "allowJs": true,
17 | "esModuleInterop": true
18 | },
19 | "exclude": [
20 | "contracts/rust",
21 | "lib",
22 | "node_modules",
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/tests/substrate/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "compilerOptions": {
4 | "target": "esnext",
5 | "module": "commonjs",
6 | "lib": ["dom", "es2018"],
7 | "resolveJsonModule": true,
8 | "strict": true,
9 | "noImplicitAny": true,
10 | "strictNullChecks": true,
11 | "strictFunctionTypes": true,
12 | "strictBindCallApply": true,
13 | "strictPropertyInitialization": true,
14 | "noImplicitThis": true,
15 | "alwaysStrict": true,
16 | "allowJs": true,
17 | "esModuleInterop": true
18 | },
19 | "exclude": [
20 | "contracts/rust",
21 | "lib",
22 | "node_modules",
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/tests/substrate/utils.ts:
--------------------------------------------------------------------------------
1 | import BN from "bn.js";
2 |
3 | export const WSURL = "ws://127.0.0.1:9944";
4 | export const DOT: BN = new BN("1000000000000000");
5 | export const CREATION_FEE: BN = DOT.muln(200);
6 | export const GAS_REQUIRED = 50000;
7 | export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
8 | export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
9 |
10 |
--------------------------------------------------------------------------------
/tools/abi_gen.nim:
--------------------------------------------------------------------------------
1 | import compiler / [ast, vmdef, vm, nimeval, options, parser, idents, condsyms,
2 | nimconf, extccomp, astalgo, llstream, pathutils]
3 | import json
4 | import os
5 |
6 |
7 | import
8 | ../nimplay/types
9 |
10 |
11 | type
12 | VariableType* = object
13 | name: string
14 | var_type: string
15 |
16 | type
17 | EventType* = object
18 | name: string
19 | var_type: string
20 | indexed: bool
21 |
22 | type
23 | FunctionSignature* = object
24 | name: string
25 | inputs: seq[VariableType]
26 | outputs: seq[VariableType]
27 | constant: bool
28 | payable: bool
29 |
30 | type
31 | EventSignature* = object
32 | name: string
33 | inputs: seq[EventType]
34 | indexed: bool
35 |
36 |
37 | proc get_abi_type(nimplay_type_str: string): string =
38 | if nimplay_type_str in @["uint256", "bytes32", "int128", "uint128", "address"]:
39 | nimplay_type_str
40 | elif nimplay_type_str == "wei_value":
41 | "uint128"
42 | else:
43 | raise newException(Exception, "Unknown type: " & nimplay_type_str)
44 |
45 |
46 | proc string_to_ast*(s: string): PNode {.raises: [Exception] .} =
47 | var conf = newConfigRef()
48 | var cache = newIdentCache()
49 | condsyms.initDefines(conf.symbols)
50 | conf.projectName = "stdinfile"
51 | conf.projectFull = "stdinfile".AbsoluteFile
52 | conf.projectPath = canonicalizePath(conf, getCurrentDir().AbsoluteFile).AbsoluteDir
53 | conf.projectIsStdin = true
54 | loadConfigs(DefaultConfig, cache, conf)
55 | extccomp.initVars(conf)
56 | var node = parseString(s, cache, conf)
57 | if len(node.sons) > 1:
58 | result = node
59 | else:
60 | result = node.sons[0]
61 |
62 |
63 | proc generate_function_signature(proc_def: PNode): FunctionSignature =
64 | doAssert(proc_def.kind == nkFuncDef or proc_def.kind == nkProcDef)
65 | var
66 | func_sig = FunctionSignature()
67 |
68 | for child in proc_def.sons:
69 | case child.kind:
70 | of nkPostfix:
71 | func_sig.name = child[1].ident.s
72 | of nkFormalParams:
73 | for param in child:
74 | case param.kind
75 | of nkIdent:
76 | func_sig.outputs.add(
77 | VariableType(
78 | name: "out1",
79 | var_type: get_abi_type(param.ident.s)
80 | )
81 | )
82 | of nkIdentDefs:
83 | func_sig.inputs.add(
84 | VariableType(
85 | name: param.sons[0].ident.s,
86 | var_type: get_abi_type(param.sons[1].ident.s)
87 | )
88 | )
89 | of nkEmpty:
90 | discard
91 | else:
92 | raise newException(Exception, "unknown param type")
93 | of nkPragma:
94 | for pragma_child in child:
95 | if pragma_child.kind == nkIdent and pragma_child.ident.s == "payable":
96 | func_sig.payable = true
97 | else:
98 | discard
99 | return func_sig
100 |
101 |
102 | proc generate_event_signature(proc_def: PNode): EventSignature =
103 | doAssert(proc_def.kind == nkProcDef)
104 | var
105 | event_sig = EventSignature()
106 | for child in proc_def.sons:
107 | case child.kind:
108 | of nkIdent:
109 | event_sig.name = child.ident.s
110 | of nkFormalParams:
111 | for param in child:
112 | if param.kind == nkIdentDefs:
113 | var
114 | ev: EventType
115 | if param[0].kind == nkPragmaExpr and param[0][1].kind == nkPragma:
116 | ev = EventType(
117 | name: param.sons[0].sons[0].ident.s,
118 | var_type: param.sons[1].ident.s,
119 | indexed: true
120 | )
121 | else:
122 | ev = EventType(
123 | name: param.sons[0].ident.s,
124 | var_type: param.sons[1].ident.s,
125 | indexed: false
126 | )
127 | event_sig.inputs.add(ev)
128 | else:
129 | discard
130 |
131 | event_sig # return
132 |
133 |
134 | proc generateSignatures(stmts: PNode): (seq[FunctionSignature], seq[EventSignature]) =
135 | var
136 | public_functions: seq[FunctionSignature]
137 | events: seq[EventSignature]
138 | for child in stmts:
139 | if child.kind == nkCall and len(child.sons) > 2:
140 | var first_son = child.sons[0]
141 | if first_son.kind == nkIdent and first_son.ident.s == "contract":
142 | var main_block = child
143 | for b in main_block.sons:
144 | if b.kind == nkStmtList:
145 | for s in b.sons:
146 | if s.kind == nkVarSection: # handle getters.
147 | for var_def in s.sons:
148 | if var_def.kind == nkIdentDefs:
149 | var
150 | potential_postfix = var_def.sons[0]
151 | if potential_postfix.kind == nkPostfix and potential_postfix.sons[0].ident.s == "*":
152 | var
153 | func_name = potential_postfix.sons[1].ident.s
154 | type_name = var_def.sons[1].ident.s
155 | sig = FunctionSignature(name: func_name)
156 | sig.outputs.add(
157 | VariableType(name: "out1", var_type: get_abi_type(type_name))
158 | )
159 | public_functions.add(
160 | sig
161 | )
162 | elif s.kind == nkFuncDef or s.kind == nkProcDef:
163 | # only get public functions.
164 | if s[0].kind == nkPostfix and s[0][0].ident.s == "*":
165 | public_functions.add(
166 | generate_function_signature(s)
167 | )
168 | # handle event signature
169 | elif s[6].kind == nkEmpty:
170 | events.add(
171 | generate_event_signature(s)
172 | )
173 | return (public_functions, events)
174 |
175 |
176 | proc generateJSONABI(funcs: seq[FunctionSignature], events: seq[EventSignature]): string =
177 | var jsonObject = %* []
178 |
179 | proc getVarTypes(var_types: seq[VariableType]): JsonNode =
180 | var types = %* []
181 | for t in var_types:
182 | types.add(%* {
183 | "name": t.name,
184 | "type": t.var_type,
185 | })
186 | types # return
187 |
188 |
189 | func getFunc(f: FunctionSignature): JsonNode =
190 | %* {
191 | "name": f.name,
192 | "inputs": getVarTypes(f.inputs),
193 | "outputs": getVarTypes(f.outputs),
194 | "constant": f.constant,
195 | "payable": f.payable,
196 | "type": "function"
197 | }
198 |
199 | proc getEventTypes(var_types: seq[EventType]): JsonNode =
200 | var types = %* []
201 | for t in var_types:
202 | types.add(%* {
203 | "name": t.name,
204 | "type": t.var_type,
205 | "indexed": t.indexed
206 | })
207 | types
208 |
209 | func getEvent(e: EventSignature): JsonNode =
210 | %* {
211 | "inputs": getEventTypes(e.inputs),
212 | "name": e.name,
213 | "type": "event",
214 | "anonymous": false
215 | }
216 |
217 | for fn in funcs:
218 | jsonObject.add(getFunc(fn))
219 |
220 | for event in events:
221 | jsonObject.add(getEvent(event))
222 |
223 | $jsonObject # return
224 |
225 |
226 | proc main() =
227 | if paramCount() != 1:
228 | echo("Requires .nim file with contract() block macro")
229 | quit()
230 | if not existsFile(commandLineParams()[0]):
231 | echo("Requires .nim file with contract() block macro")
232 | quit()
233 |
234 | var nimFile = commandLineParams()[0]
235 | let node = string_to_ast(readFile(nimFile))
236 |
237 | if node.kind == nkStmtList:
238 | let (functions, events) = generateSignatures(node)
239 | echo(generateJSONABI(functions, events))
240 | else:
241 | raise newException(Exception, "Expected nkStmtList")
242 |
243 |
244 | when is_main_module:
245 | main()
246 |
--------------------------------------------------------------------------------
/tools/deploy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Deployment script for NimPlay contracts
3 | # requirements:
4 | import os
5 | import sys
6 |
7 | import argparse
8 | import json
9 | import web3
10 | import textwrap
11 | import subprocess
12 | import tempfile
13 |
14 | from web3 import Web3
15 | from web3.providers import HTTPProvider
16 |
17 | PRIVATE_KEY = os.environ.get('PRIVATE_KEY')
18 | PRIVATE_KEY_FILE = os.environ.get('PRIVATE_KEY_FILE', '.priv_key_hex')
19 | RPC_URL = os.environ.get('RPC_URL', 'http://ewasm.ethereum.org:8545')
20 | WAT2WASM = os.environ.get('WAT2WASM', './tools/wabt/bin/wat2wasm')
21 | WASM2WAT = os.environ.get('WASM2WAT', './tools/wabt/bin/wasm2wat')
22 |
23 |
24 | if not PRIVATE_KEY and PRIVATE_KEY_FILE:
25 | PRIVATE_KEY = open(PRIVATE_KEY_FILE, 'r').read()
26 |
27 | if not PRIVATE_KEY:
28 | print('Private Key required, user either PRIVATE_KEY or PRIVATE_KEY_FILE')
29 | sys.exit(1)
30 |
31 |
32 | def watb_util(binary, code):
33 | with tempfile.NamedTemporaryFile() as in_file, tempfile.NamedTemporaryFile() as out_file:
34 | outb = code.encode() if isinstance(code, str) else code
35 | in_file.write(outb)
36 | in_file.flush()
37 | completed_process = subprocess.run([binary, in_file.name, '-o', out_file.name])
38 | out_file.seek(0)
39 | b = out_file.read()
40 | return b
41 |
42 |
43 | def wasm2wat(code):
44 | return watb_util(WASM2WAT, code)
45 |
46 |
47 | def wat2wasm(code):
48 | return watb_util(WAT2WASM, code)
49 |
50 |
51 | def get_abi(fpath):
52 | cmdline = ['./tools/abi_gen', fpath.replace('wasm', 'nim')]
53 | completed_process = subprocess.run(
54 | cmdline,
55 | stdout=subprocess.PIPE,
56 | )
57 | if completed_process.returncode != 0:
58 | print(completed_process.stdout)
59 | print(completed_process.stderr)
60 | raise Exception('Could not get ABI')
61 | return json.loads(completed_process.stdout)
62 |
63 |
64 | def create_deploy_bytecode(fpath):
65 | with open(fpath, 'rb') as f:
66 | wasmb = f.read()
67 | hexb = Web3.toHex(wasmb)[2:]
68 | total_len = len(hexb) // 2
69 | escaped_hexb = "\\" + "\\".join(hexb[x:x+2] for x in range(0, len(hexb), 2))
70 | code = textwrap.dedent(f"""
71 | (module
72 | (type (;0;) (func (param i32 i32)))
73 | (type (;1;) (func))
74 | (import "ethereum" "finish" (func (;0;) (type 0)))
75 | (func (;1;) (type 1)
76 | i32.const 0
77 | i32.const {total_len}
78 | call 0)
79 | (memory (;0;) 100)
80 | (export "memory" (memory 0))
81 | (export "main" (func 1))
82 | (data (;0;) (i32.const 0) "{escaped_hexb}"))
83 | """)
84 | print(code)
85 | deploy_wasmb = wat2wasm(code)
86 | abi = get_abi(fpath)
87 | return abi, Web3.toHex(deploy_wasmb)
88 |
89 |
90 | def main(contract_file, get_shell=False):
91 | abi, bytecode = create_deploy_bytecode(contract_file)
92 |
93 | w3 = Web3(HTTPProvider(RPC_URL))
94 | acct = w3.eth.account.privateKeyToAccount(PRIVATE_KEY)
95 | contract_ = w3.eth.contract(
96 | abi=abi,
97 | bytecode=bytecode
98 | )
99 |
100 | construct_txn = contract_.constructor().buildTransaction({
101 | 'from': acct.address,
102 | 'nonce': w3.eth.getTransactionCount(acct.address),
103 | # 'gas': 8 * 10**6,
104 | 'gasPrice': w3.toWei('1', 'wei')}
105 | )
106 |
107 | # Deploy
108 | print(f"Account address: {acct.address}")
109 | receipt = None
110 | signed = acct.signTransaction(construct_txn)
111 | tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction)
112 | print(f'Deployed Transaction: {tx_hash.hex()}')
113 | w3.eth.waitForTransactionReceipt(tx_hash)
114 | print('Mined transaction')
115 | receipt = w3.eth.waitForTransactionReceipt(tx_hash)
116 | print(f'Contract Address: {receipt["contractAddress"]}')
117 | print(f"Success?: {receipt['status'] == 1}")
118 | print(receipt)
119 |
120 | if get_shell:
121 | contract_ = w3.eth.contract(
122 | abi=abi,
123 | address=receipt["contractAddress"]
124 | )
125 | print('Contract located on contract_ variable.')
126 | import pdb; pdb.set_trace()
127 |
128 |
129 | if __name__ == '__main__':
130 | parser = argparse.ArgumentParser()
131 | parser.add_argument("contract_file")
132 | parser.add_argument("--get-shell", help="Break to pdb debugger right after deploying.", action="store_true")
133 | args = parser.parse_args()
134 | main(args.contract_file, args.get_shell)
135 |
--------------------------------------------------------------------------------
/tools/eth_postprocess.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | WASM_FILE=$1
4 |
5 | set -ex
6 |
7 | #wasm2wat="tools/wabt/build/wasm2wat"
8 | #wat2wasm="tools/wabt/build/wat2wasm"
9 |
10 | wasm2wat="docker run --entrypoint=wasm2wat -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
11 | wat2wasm="docker run --entrypoint=wat2wasm -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
12 |
13 | # Replace "env" with "ethereum"
14 | $wasm2wat "$WASM_FILE" |
15 | sed 's/(import "env" /(import "ethereum" /g' |
16 | sed '/(export.*memory\|main.*/! s/(export.*//g' > ./wasm.tmp
17 |
18 | $wat2wasm -o "$WASM_FILE" ./wasm.tmp
19 |
20 | rm ./wasm.tmp
21 |
--------------------------------------------------------------------------------
/tools/get_wabt.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf wabt
4 | git clone --recursive https://github.com/WebAssembly/wabt wabt
5 | mkdir -p wabt/build
6 | cd wabt/build
7 | cmake ".."
8 | cmake --build "."
9 |
--------------------------------------------------------------------------------
/tools/k256_sig.nim:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import nimcrypto
4 |
5 |
6 |
7 | proc main() =
8 | if paramCount() != 1:
9 | echo("Requires single parameter to be hashed")
10 | quit()
11 |
12 | echo(keccak256.digest(commandLineParams()[0]))
13 |
14 |
15 | when is_main_module:
16 | main()
17 |
--------------------------------------------------------------------------------
/tools/nim.cfg:
--------------------------------------------------------------------------------
1 | --path:"$lib/packages/docutils"
2 | --path:"$nim/"
3 | -d:nimcore
4 | -d:nimOldCaseObjects
5 |
--------------------------------------------------------------------------------
/tools/nimplayc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # For use inside Nimplay Docker image.
3 | NIM_FILE=$1
4 | if [[ $# -eq 2 ]];
5 | then
6 | WASM_FILE="$2"
7 | else
8 | WASM_FILE="${NIM_FILE%.*}.wasm"
9 | fi
10 |
11 | PANIC_PATH="$(dirname ${NIM_FILE})"
12 |
13 | PATH_PARAMS="-p:/nimplay/ -p:/nimplay/vendor/nimcrypto -p:/nimplay/vendor/stint -p:/nimplay/vendor/nim-stew/"
14 | NIM_CLANG_C_FLAGS="--passC:\""--target=wasm32-unknown-unknown-wasm\"" --passC:\""-I./include\"""
15 | NIM_CLANG_LINKER_FLAGS="--clang.options.linker:\"-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all,--export-dynamic\" --passL:\"--target=wasm32-unknown-unknown-wasm\""
16 | NIM_CLANG_ALL_FLAGS="${NIM_CLANG_C_FLAGS} ${NIM_CLANG_LINKER_FLAGS} --os:standalone --cpu:i386 --cc:clang --gc:none --nomain -d:release"
17 | NIM_CLANG_WASM32_C="nim c -d:osk256_sig ${PATH_PARAMS} ${NIM_CLANG_ALL_FLAGS}"
18 | FULL_CMD="${NIM_CLANG_WASM32_C} --out:${WASM_FILE} ${NIM_FILE}"
19 |
20 |
21 | cp /nimplay/examples/panicoverride.nim $PANIC_PATH/
22 | echo $FULL_CMD
23 | eval $FULL_CMD
24 | if [ $? -ne 0 ]; then
25 | rm $PANIC_PATH/panicoverride.nim
26 | exit 1
27 | fi
28 |
29 |
30 | # Postprocess produced wasm file
31 | wasm2wat="wasm2wat"
32 | wat2wasm="wat2wasm"
33 |
34 | # Replace "env" with "ethereum"
35 | $wasm2wat "$WASM_FILE" |
36 | sed 's/(import "env" /(import "ethereum" /g' |
37 | sed '/(export.*memory\|main.*/! s/(export.*//g' > ./wasm.tmp
38 |
39 | $wat2wasm -o "$WASM_FILE" ./wasm.tmp
40 |
41 | rm ./wasm.tmp
42 | rm $PANIC_PATH/panicoverride.nim
43 |
--------------------------------------------------------------------------------
/tools/substrate_postprocess.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | WASM_FILE=$1
4 |
5 | set -ex
6 |
7 | #wasm2wat="tools/wabt/build/wasm2wat"
8 | #wat2wasm="tools/wabt/build/wat2wasm"
9 |
10 | wasm2wat="docker run --entrypoint=wasm2wat -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
11 | wat2wasm="docker run --entrypoint=wat2wasm -w /code/ -v $(pwd):/code/ jacqueswww/nimclang "
12 |
13 | # Replace "env" with "ethereum"
14 | $wasm2wat "$WASM_FILE" |
15 | sed '/(export.*deploy\|call.*/! s/(export.*//g' > ./wasm.tmp
16 |
17 | $wat2wasm -o "$WASM_FILE" ./wasm.tmp
18 |
19 | rm ./wasm.tmp
20 |
--------------------------------------------------------------------------------