Smart-contract inspection and instrumentation. (#165)

* Extract mock module.

* Wasm smart-contract instrumentation

* Rebuild binaries.

* Memory limits and tests.
This commit is contained in:
Sergey Pepyakin
2018-05-23 14:45:57 +03:00
committed by Gav Wood
parent d97110733d
commit 7312a26d09
15 changed files with 755 additions and 242 deletions
+23
View File
@@ -1129,6 +1129,14 @@ dependencies = [
"parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parity-wasm"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parity-wordlist" name = "parity-wordlist"
version = "1.2.0" version = "1.2.0"
@@ -1499,6 +1507,16 @@ dependencies = [
name = "pwasm-libc" name = "pwasm-libc"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "pwasm-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quick-error" name = "quick-error"
version = "1.2.1" version = "1.2.1"
@@ -2179,7 +2197,10 @@ dependencies = [
name = "substrate-runtime-staking" name = "substrate-runtime-staking"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 0.1.0", "safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0", "substrate-codec 0.1.0",
@@ -2824,6 +2845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba1ceaec13865445bcf05117867e4c6456d91c3617cdff2f3ef77b92b18cd12" "checksum parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba1ceaec13865445bcf05117867e4c6456d91c3617cdff2f3ef77b92b18cd12"
"checksum parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe"
"checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412"
@@ -2837,6 +2859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0"
"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892"
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
+22
View File
@@ -389,6 +389,14 @@ dependencies = [
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parity-wasm"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.4.8" version = "0.4.8"
@@ -469,6 +477,16 @@ dependencies = [
name = "pwasm-libc" name = "pwasm-libc"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "pwasm-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "0.3.15" version = "0.3.15"
@@ -813,6 +831,8 @@ name = "substrate-runtime-staking"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 0.1.0", "safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0", "substrate-codec 0.1.0",
@@ -1087,6 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" "checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd" "checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
"checksum parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" "checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8" "checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
@@ -1094,6 +1115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0"
"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892"
"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
+22
View File
@@ -354,6 +354,14 @@ dependencies = [
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parity-wasm"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.4.8" version = "0.4.8"
@@ -469,6 +477,16 @@ dependencies = [
name = "pwasm-libc" name = "pwasm-libc"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "pwasm-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "0.3.15" version = "0.3.15"
@@ -813,6 +831,8 @@ name = "substrate-runtime-staking"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 0.1.0", "safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0", "substrate-codec 0.1.0",
@@ -1087,6 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" "checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd" "checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
"checksum parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" "checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8" "checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
@@ -1094,6 +1115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0"
"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892"
"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
@@ -18,9 +18,12 @@ substrate-runtime-primitives = { path = "../primitives", default_features = fals
substrate-runtime-consensus = { path = "../consensus", default_features = false } substrate-runtime-consensus = { path = "../consensus", default_features = false }
substrate-runtime-system = { path = "../system", default_features = false } substrate-runtime-system = { path = "../system", default_features = false }
substrate-runtime-session = { path = "../session", default_features = false } substrate-runtime-session = { path = "../session", default_features = false }
parity-wasm = { version = "0.30", default_features = false }
pwasm-utils = { version = "0.2", default_features = false }
[dev-dependencies] [dev-dependencies]
wabt = "0.1.7" wabt = "0.1.7"
assert_matches = "1.1"
[features] [features]
default = ["std"] default = ["std"]
@@ -37,4 +40,6 @@ std = [
"substrate-runtime-primitives/std", "substrate-runtime-primitives/std",
"substrate-runtime-session/std", "substrate-runtime-session/std",
"substrate-runtime-system/std", "substrate-runtime-system/std",
"pwasm-utils/std",
"parity-wasm/std",
] ]
@@ -16,16 +16,64 @@
//! Smart-contract execution module. //! Smart-contract execution module.
// TODO: Extract to it's own crate?
use codec::Slicable; use codec::Slicable;
use primitives::traits::As; use primitives::traits::As;
use rstd::prelude::*; use rstd::prelude::*;
use sandbox; use sandbox;
use {AccountDb, Module, OverlayAccountDb, Trait}; use {AccountDb, Module, OverlayAccountDb, Trait};
use parity_wasm::elements::{self, External, MemoryType};
use pwasm_utils;
use pwasm_utils::rules;
/// Error that can occur while preparing or executing wasm smart-contract.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
/// Error happened while serializing the module.
Serialization,
/// Error happened while deserializing the module.
Deserialization,
/// Internal memory declaration has been found in the module.
InternalMemoryDeclared,
/// Gas instrumentation failed.
///
/// This most likely indicates the module isn't valid.
GasInstrumentation,
/// Stack instrumentation failed.
///
/// This most likely indicates the module isn't valid.
StackHeightInstrumentation,
/// Error happened during invocation of the contract's entrypoint.
///
/// Most likely because of trap.
Invoke,
/// Error happened during instantiation.
///
/// This might indicate that `start` function trapped, or module isn't
/// instantiable and/or unlinkable.
Instantiate,
/// Memory creation error.
///
/// This might happen when the memory import has invalid descriptor or
/// requested too much resources.
Memory,
}
struct ExecutionExt<'a, 'b: 'a, T: Trait + 'b> { struct ExecutionExt<'a, 'b: 'a, T: Trait + 'b> {
account_db: &'a mut OverlayAccountDb<'b, T>, account_db: &'a mut OverlayAccountDb<'b, T>,
account: T::AccountId, account: T::AccountId,
memory: sandbox::Memory, memory: sandbox::Memory,
gas_used: u64,
gas_limit: u64,
} }
impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> { impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> {
fn account(&self) -> &T::AccountId { fn account(&self) -> &T::AccountId {
@@ -40,13 +88,44 @@ impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> {
fn memory(&self) -> &sandbox::Memory { fn memory(&self) -> &sandbox::Memory {
&self.memory &self.memory
} }
/// Account for used gas.
///
/// Returns `false` if there is not enough gas or addition of the specified
/// amount of gas has lead to overflow. On success returns `true`.
///
/// Intuition about the return value sense is to answer the question 'are we allowed to continue?'
fn charge_gas(&mut self, amount: u64) -> bool {
match self.gas_used.checked_add(amount) {
None => false,
Some(val) if val > self.gas_limit => false,
Some(val) => {
self.gas_used = val;
true
}
}
}
} }
pub(crate) fn execute<'a, 'b: 'a, T: Trait>( pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
code: &[u8], code: &[u8],
account: &T::AccountId, account: &T::AccountId,
account_db: &'a mut OverlayAccountDb<'b, T>, account_db: &'a mut OverlayAccountDb<'b, T>,
) -> bool { gas_limit: u64,
) -> Result<(), Error> {
// ext_gas(amount: u32)
//
// Account for used gas. Traps if gas used is greater than gas limit.
//
// - amount: How much gas is used.
fn ext_gas<T: Trait>(e: &mut ExecutionExt<T>, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
let amount = args[0].as_i32().unwrap() as u32;
if e.charge_gas(amount as u64) {
Ok(sandbox::ReturnValue::Unit)
} else {
Err(sandbox::HostError)
}
}
// ext_put_storage(location_ptr: u32, value_non_null: u32, value_ptr: u32); // ext_put_storage(location_ptr: u32, value_non_null: u32, value_ptr: u32);
// //
// Change the value at the given location in storage or remove it. // Change the value at the given location in storage or remove it.
@@ -153,13 +232,13 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
Ok(sandbox::ReturnValue::Unit) Ok(sandbox::ReturnValue::Unit)
} }
// TODO: Inspect the binary to extract the initial page count. let PreparedContract {
let memory = match sandbox::Memory::new(1, None) { instrumented_code,
Ok(memory) => memory, memory,
Err(_) => return false, } = prepare_contract(code)?;
};
let mut imports = sandbox::EnvironmentDefinitionBuilder::new(); let mut imports = sandbox::EnvironmentDefinitionBuilder::new();
imports.add_host_func("env", "gas", ext_gas::<T>);
imports.add_host_func("env", "ext_set_storage", ext_set_storage::<T>); imports.add_host_func("env", "ext_set_storage", ext_set_storage::<T>);
imports.add_host_func("env", "ext_get_storage", ext_get_storage::<T>); imports.add_host_func("env", "ext_get_storage", ext_get_storage::<T>);
imports.add_host_func("env", "ext_transfer", ext_transfer::<T>); imports.add_host_func("env", "ext_transfer", ext_transfer::<T>);
@@ -171,11 +250,514 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
account: account.clone(), account: account.clone(),
account_db, account_db,
memory, memory,
gas_limit,
gas_used: 0,
}; };
let mut instance = match sandbox::Instance::new(code, &imports, &mut exec_ext) { let mut instance =
Ok(instance) => instance, sandbox::Instance::new(&instrumented_code, &imports, &mut exec_ext)
Err(_err) => return false, .map_err(|_| Error::Instantiate)?;
}; instance
instance.invoke(b"call", &[], &mut exec_ext).is_ok() .invoke(b"call", &[], &mut exec_ext)
.map(|_| ())
.map_err(|_| Error::Invoke)
}
#[derive(Clone)]
struct Config {
/// Gas cost of a growing memory by single page.
grow_mem_cost: u32,
/// Gas cost of a regular operation.
regular_op_cost: u32,
/// How tall the stack is allowed to grow?
///
/// See https://wiki.parity.io/WebAssembly-StackHeight to find out
/// how the stack frame cost is calculated.
max_stack_height: u32,
//// What is the maximal memory pages amount is allowed to have for
/// a contract.
max_memory_pages: u32,
}
impl Default for Config {
fn default() -> Config {
Config {
grow_mem_cost: 1,
regular_op_cost: 1,
max_stack_height: 64 * 1024,
max_memory_pages: 16,
}
}
}
struct ContractModule {
// An `Option` is used here for loaning (`take()`-ing) the module.
// Invariant: Can't be `None` (i.e. on enter and on exit from the function
// the value *must* be `Some`).
module: Option<elements::Module>,
config: Config,
}
impl ContractModule {
fn new(original_code: &[u8], config: Config) -> Result<ContractModule, Error> {
let module =
elements::deserialize_buffer(original_code).map_err(|_| Error::Deserialization)?;
Ok(ContractModule {
module: Some(module),
config,
})
}
/// Ensures that module doesn't declare internal memories.
///
/// In this runtime we only allow wasm module to import memory from the environment.
/// Memory section contains declarations of internal linear memories, so if we find one
/// we reject such a module.
fn ensure_no_internal_memory(&self) -> Result<(), Error> {
let module = self.module
.as_ref()
.expect("On entry to the function `module` can't be None; qed");
if module
.memory_section()
.map_or(false, |ms| ms.entries().len() > 0)
{
return Err(Error::InternalMemoryDeclared);
}
Ok(())
}
fn inject_gas_metering(&mut self) -> Result<(), Error> {
let gas_rules = rules::Set::new(self.config.regular_op_cost, Default::default())
.with_grow_cost(self.config.grow_mem_cost)
.with_forbidden_floats();
let module = self.module
.take()
.expect("On entry to the function `module` can't be `None`; qed");
let contract_module = pwasm_utils::inject_gas_counter(module, &gas_rules)
.map_err(|_| Error::GasInstrumentation)?;
self.module = Some(contract_module);
Ok(())
}
fn inject_stack_height_metering(&mut self) -> Result<(), Error> {
let module = self.module
.take()
.expect("On entry to the function `module` can't be `None`; qed");
let contract_module =
pwasm_utils::stack_height::inject_limiter(module, self.config.max_stack_height)
.map_err(|_| Error::StackHeightInstrumentation)?;
self.module = Some(contract_module);
Ok(())
}
/// Find the memory import entry and return it's descriptor.
fn find_mem_import(&self) -> Option<&MemoryType> {
let import_section = self.module
.as_ref()
.expect("On entry to the function `module` can't be `None`; qed")
.import_section()?;
for import in import_section.entries() {
if let ("env", "memory", &External::Memory(ref memory_type)) =
(import.module(), import.field(), import.external())
{
return Some(memory_type);
}
}
None
}
fn into_wasm_code(mut self) -> Result<Vec<u8>, Error> {
elements::serialize(
self.module
.take()
.expect("On entry to the function `module` can't be `None`; qed"),
).map_err(|_| Error::Serialization)
}
}
struct PreparedContract {
instrumented_code: Vec<u8>,
memory: sandbox::Memory,
}
fn prepare_contract(original_code: &[u8]) -> Result<PreparedContract, Error> {
let config = Config::default();
let mut contract_module = ContractModule::new(original_code, config.clone())?;
contract_module.ensure_no_internal_memory()?;
contract_module.inject_gas_metering()?;
contract_module.inject_stack_height_metering()?;
// Inspect the module to extract the initial and maximum page count.
let memory = match contract_module.find_mem_import() {
Some(memory_type) => {
let limits = memory_type.limits();
match (limits.initial(), limits.maximum()) {
(initial, Some(maximum)) if initial > maximum => {
// Requested initial number of pages should not exceed the requested maximum.
return Err(Error::Memory);
}
(_, Some(maximum)) if maximum > config.max_memory_pages => {
// Maximum number of pages should not exceed the configured maximum.
return Err(Error::Memory);
}
(_, None) => {
// Maximum number of pages should be always declared.
// This isn't a hard requirement and can be treated as a maxiumum set
// to configured maximum.
return Err(Error::Memory)
}
(initial, maximum) => sandbox::Memory::new(
initial,
maximum,
)
}
},
// If none memory imported then just crate an empty placeholder.
// Any access to it will lead to out of bounds trap.
None => sandbox::Memory::new(0, Some(0)),
}.map_err(|_| Error::Memory)?;
Ok(PreparedContract {
instrumented_code: contract_module.into_wasm_code()?,
memory,
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt;
use wabt;
use runtime_io::with_externalities;
use mock::{Staking, Test, new_test_ext};
use ::{CodeOf, ContractAddressFor, DirectAccountDb, FreeBalance, StorageMap};
impl fmt::Debug for PreparedContract {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PreparedContract {{ .. }}")
}
}
fn parse_and_prepare_wat(wat: &str) -> Result<PreparedContract, Error> {
let wasm = wabt::Wat2Wasm::new()
.validate(false)
.convert(wat)
.unwrap();
prepare_contract(wasm.as_ref())
}
#[test]
fn internal_memory_declaration() {
let r = parse_and_prepare_wat(
r#"(module (memory 1 1))"#,
);
assert_matches!(r, Err(Error::InternalMemoryDeclared));
}
#[test]
fn memory() {
// This test assumes that maximum page number is configured to a certain number.
assert_eq!(Config::default().max_memory_pages, 16);
let r = parse_and_prepare_wat(
r#"(module (import "env" "memory" (memory 1 1)))"#,
);
assert_matches!(r, Ok(_));
// No memory import
let r = parse_and_prepare_wat(
r#"(module)"#,
);
assert_matches!(r, Ok(_));
// incorrect import name. That's kinda ok, since this will fail
// at later stage when imports will be resolved.
let r = parse_and_prepare_wat(
r#"(module (import "vne" "memory" (memory 1 1)))"#,
);
assert_matches!(r, Ok(_));
// initial exceed maximum
let r = parse_and_prepare_wat(
r#"(module (import "env" "memory" (memory 16 1)))"#,
);
assert_matches!(r, Err(Error::Memory));
// no maximum
let r = parse_and_prepare_wat(
r#"(module (import "env" "memory" (memory 1)))"#,
);
assert_matches!(r, Err(Error::Memory));
// requested maximum exceed configured maximum
let r = parse_and_prepare_wat(
r#"(module (import "env" "memory" (memory 1 17)))"#,
);
assert_matches!(r, Err(Error::Memory));
}
#[test]
fn contract_transfer() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<FreeBalance<Test>>::insert(2, 30);
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
Staking::transfer(&0, 1, 11);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 5);
assert_eq!(Staking::balance(&2), 36);
});
}
fn escaped_bytestring(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut result = String::new();
for b in bytes {
write!(result, "\\{:02x}", b).unwrap();
}
result
}
#[test]
fn contract_create() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
let code_create = wabt::wat2wasm(format!(
r#"
(module
;; ext_create(code_ptr: u32, code_len: u32, value: u32)
(import "env" "ext_create" (func $ext_create (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))
(func (export "call")
(call $ext_create
(i32.const 4) ;; Pointer to `code`
(i32.const {code_length}) ;; Length of `code`
(i32.const 3) ;; Value to transfer
)
)
(data (i32.const 4) "{escaped_code_transfer}")
)
"#,
escaped_code_transfer = escaped_bytestring(&code_transfer),
code_length = code_transfer.len(),
)).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_create.to_vec());
// When invoked, the contract at address `1` must create a contract with 'transfer' code.
Staking::transfer(&0, 1, 11);
let derived_address =
<Test as Trait>::DetermineContractAddress::contract_address_for(&code_transfer, &1);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 8);
assert_eq!(Staking::balance(&derived_address), 3);
});
}
#[test]
fn contract_adder() {
let code_adder = wabt::wat2wasm(r#"
(module
;; ext_set_storage(location_ptr: i32, value_non_null: bool, value_ptr: i32)
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32)))
;; ext_get_storage(location_ptr: i32, value_ptr: i32)
(import "env" "ext_get_storage" (func $ext_get_storage (param i32 i32)))
(import "env" "memory" (memory 1 1))
(func (export "call")
(call $ext_get_storage
(i32.const 4) ;; Point to a location of the storage.
(i32.const 36) ;; The result will be written at this address.
)
(i32.store
(i32.const 36)
(i32.add
(i32.load
(i32.const 36)
)
(i32.const 1)
)
)
(call $ext_set_storage
(i32.const 4) ;; Pointer to a location of the storage.
(i32.const 1) ;; Value is not null.
(i32.const 36) ;; Pointer to a data we want to put in the storage.
)
)
;; Location of storage to put the data. 32 bytes.
(data (i32.const 4) "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01")
)
"#).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_adder);
Staking::transfer(&0, 1, 1);
Staking::transfer(&0, 1, 1);
let storage_addr = [0x01u8; 32];
let value =
AccountDb::<Test>::get_storage(&DirectAccountDb, &1, &storage_addr).unwrap();
assert_eq!(
&value,
&[
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]
);
});
}
#[test]
fn contract_out_of_gas() {
// This code should make 100_000 iterations so it should
// consume more than 100_000 units of gas.
let code_loop = wabt::wat2wasm(
r#"
(module
(func (export "call")
(local $i i32)
loop $l
;; $i = $i + 1
(set_local $i
(i32.add
(get_local $i)
(i32.const 1)
)
)
;; if $i < 100_000u32: goto $l
(br_if $l
(i32.lt_u
(get_local $i)
(i32.const 100000)
)
)
end
)
)
"#,
).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
// Set initial balances.
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_loop.to_vec());
// Transfer some balance from 0 to 1. This will trigger execution
// of the smart-contract code at address 1.
Staking::transfer(&0, 1, 11);
// The balance should remain unchanged since we are expecting
// out-of-gas error which will revert transfer.
assert_eq!(Staking::balance(&0), 111);
});
}
#[test]
fn contract_internal_mem() {
let code_mem = wabt::wat2wasm(
r#"
(module
;; Internal memory is not allowed.
(memory 1 1)
(func (export "call")
nop
)
)
"#,
).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
// Set initial balances.
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_mem.to_vec());
// Transfer some balance from 0 to 1.
Staking::transfer(&0, 1, 11);
// The balance should remain unchanged since we are expecting
// validation error caused by internal memory declaration.
assert_eq!(Staking::balance(&0), 111);
});
}
} }
+12 -231
View File
@@ -24,6 +24,10 @@ extern crate serde;
#[cfg(test)] #[cfg(test)]
extern crate wabt; extern crate wabt;
#[cfg(test)]
#[macro_use]
extern crate assert_matches;
#[macro_use] #[macro_use]
extern crate substrate_runtime_support as runtime_support; extern crate substrate_runtime_support as runtime_support;
@@ -38,6 +42,8 @@ extern crate substrate_runtime_consensus as consensus;
extern crate substrate_runtime_sandbox as sandbox; extern crate substrate_runtime_sandbox as sandbox;
extern crate substrate_runtime_session as session; extern crate substrate_runtime_session as session;
extern crate substrate_runtime_system as system; extern crate substrate_runtime_system as system;
extern crate pwasm_utils;
extern crate parity_wasm;
#[cfg(test)] use std::fmt::Debug; #[cfg(test)] use std::fmt::Debug;
use rstd::prelude::*; use rstd::prelude::*;
@@ -49,6 +55,8 @@ use runtime_support::{StorageValue, StorageMap, Parameter};
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment}; use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment};
mod contract; mod contract;
#[cfg(test)]
mod mock;
#[cfg(test)] #[cfg(test)]
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@@ -564,6 +572,8 @@ impl<T: Trait> Module<T> {
assert!(to_balance + value > to_balance); // no overflow assert!(to_balance + value > to_balance); // no overflow
// TODO: a fee, based upon gaslimit/gasprice. // TODO: a fee, based upon gaslimit/gasprice.
let gas_limit = 100_000;
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime // TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
// code in contract itself and use that. // code in contract itself and use that.
@@ -581,7 +591,7 @@ impl<T: Trait> Module<T> {
} else { } else {
// TODO: logging (logs are just appended into a notable storage-based vector and cleared every // TODO: logging (logs are just appended into a notable storage-based vector and cleared every
// block). // block).
contract::execute(&dest_code, dest, &mut overlay) contract::execute(&dest_code, dest, &mut overlay, gas_limit).is_ok()
}; };
if should_commit { if should_commit {
@@ -703,61 +713,7 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T> {
mod tests { mod tests {
use super::*; use super::*;
use runtime_io::with_externalities; use runtime_io::with_externalities;
use substrate_primitives::H256; use mock::*;
use primitives::BuildExternalities;
use primitives::traits::{HasPublicAux, Identity};
use primitives::testing::{Digest, Header};
pub struct Test;
impl HasPublicAux for Test {
type PublicAux = u64;
}
impl consensus::Trait for Test {
type PublicAux = <Self as HasPublicAux>::PublicAux;
type SessionKey = u64;
}
impl system::Trait for Test {
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = runtime_io::BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Header = Header;
}
impl session::Trait for Test {
type ConvertAccountIdToSessionKey = Identity;
}
impl Trait for Test {
type Balance = u64;
type DetermineContractAddress = DummyContractAddressFor;
}
fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities {
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
t.extend(consensus::GenesisConfig::<Test>{
code: vec![],
authorities: vec![],
}.build_externalities());
t.extend(session::GenesisConfig::<Test>{
session_length,
validators: vec![10, 20],
}.build_externalities());
t.extend(GenesisConfig::<Test>{
sessions_per_era,
current_era,
balances: if monied { vec![(1, 10), (2, 20), (3, 30), (4, 40)] } else { vec![] },
intentions: vec![],
validator_count: 2,
bonding_duration: 3,
transaction_fee: 0,
}.build_externalities());
t
}
type System = system::Module<Test>;
type Session = session::Module<Test>;
type Staking = Module<Test>;
#[test] #[test]
fn staking_should_work() { fn staking_should_work() {
@@ -1043,179 +999,4 @@ mod tests {
assert_eq!(Staking::free_balance(&2), 42); assert_eq!(Staking::free_balance(&2), 42);
}); });
} }
#[test]
fn contract_transfer() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<FreeBalance<Test>>::insert(2, 30);
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
Staking::transfer(&0, 1, 11);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 5);
assert_eq!(Staking::balance(&2), 36);
});
}
fn escaped_bytestring(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut result = String::new();
for b in bytes {
write!(result, "\\{:02x}", b).unwrap();
}
result
}
#[test]
fn contract_create() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
let code_create = wabt::wat2wasm(format!(
r#"
(module
;; ext_create(code_ptr: u32, code_len: u32, value: u32)
(import "env" "ext_create" (func $ext_create (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_create
(i32.const 4) ;; Pointer to `code`
(i32.const {code_length}) ;; Length of `code`
(i32.const 3) ;; Value to transfer
)
)
(data (i32.const 4) "{escaped_code_transfer}")
)
"#,
escaped_code_transfer = escaped_bytestring(&code_transfer),
code_length = code_transfer.len(),
)).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_create.to_vec());
// When invoked, the contract at address `1` must create a contract with 'transfer' code.
Staking::transfer(&0, 1, 11);
let derived_address =
<Test as Trait>::DetermineContractAddress::contract_address_for(&code_transfer, &1);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 8);
assert_eq!(Staking::balance(&derived_address), 3);
});
}
#[test]
fn contract_adder() {
let code_adder = wabt::wat2wasm(r#"
(module
;; ext_set_storage(location_ptr: i32, value_non_null: bool, value_ptr: i32)
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32)))
;; ext_get_storage(location_ptr: i32, value_ptr: i32)
(import "env" "ext_get_storage" (func $ext_get_storage (param i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_get_storage
(i32.const 4) ;; Point to a location of the storage.
(i32.const 36) ;; The result will be written at this address.
)
(i32.store
(i32.const 36)
(i32.add
(i32.load
(i32.const 36)
)
(i32.const 1)
)
)
(call $ext_set_storage
(i32.const 4) ;; Pointer to a location of the storage.
(i32.const 1) ;; Value is not null.
(i32.const 36) ;; Pointer to a data we want to put in the storage.
)
)
;; Location of storage to put the data. 32 bytes.
(data (i32.const 4) "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01")
)
"#).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_adder);
Staking::transfer(&0, 1, 1);
Staking::transfer(&0, 1, 1);
let storage_addr = [0x01u8; 32];
let value =
AccountDb::<Test>::get_storage(&DirectAccountDb, &1, &storage_addr).unwrap();
assert_eq!(
&value,
&[
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]
);
});
}
} }
@@ -0,0 +1,78 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Test utilities
#![cfg(test)]
use primitives::BuildExternalities;
use primitives::traits::{HasPublicAux, Identity};
use primitives::testing::{Digest, Header};
use substrate_primitives::H256;
use runtime_io;
use {DummyContractAddressFor, GenesisConfig, Module, Trait, consensus, session, system};
pub struct Test;
impl HasPublicAux for Test {
type PublicAux = u64;
}
impl consensus::Trait for Test {
type PublicAux = <Self as HasPublicAux>::PublicAux;
type SessionKey = u64;
}
impl system::Trait for Test {
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = runtime_io::BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Header = Header;
}
impl session::Trait for Test {
type ConvertAccountIdToSessionKey = Identity;
}
impl Trait for Test {
type Balance = u64;
type DetermineContractAddress = DummyContractAddressFor;
}
pub fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities {
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
t.extend(consensus::GenesisConfig::<Test>{
code: vec![],
authorities: vec![],
}.build_externalities());
t.extend(session::GenesisConfig::<Test>{
session_length,
validators: vec![10, 20],
}.build_externalities());
t.extend(GenesisConfig::<Test>{
sessions_per_era,
current_era,
balances: if monied { vec![(1, 10), (2, 20), (3, 30), (4, 40)] } else { vec![] },
intentions: vec![],
validator_count: 2,
bonding_duration: 3,
transaction_fee: 0,
}.build_externalities());
t
}
pub type System = system::Module<Test>;
pub type Session = session::Module<Test>;
pub type Staking = Module<Test>;