diff --git a/.gitignore b/.gitignore index cd789637f93b0..c7a3830779c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target/ **/*.rs.bk *.swp -runtime/**/target/ +wasm-runtime/**/target/ **/._* diff --git a/Cargo.lock b/Cargo.lock index f570676f20e47..a05893e615489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,14 @@ dependencies = [ "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "assert_matches" version = "1.1.0" @@ -90,6 +98,15 @@ name = "bitflags" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byteorder" version = "1.1.0" @@ -129,6 +146,20 @@ dependencies = [ "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "coco" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crunchy" version = "0.1.6" @@ -153,6 +184,11 @@ name = "dtoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "either" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "elastic-array" version = "0.9.0" @@ -170,6 +206,10 @@ dependencies = [ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "environmental" +version = "0.1.0" + [[package]] name = "error-chain" version = "0.11.0" @@ -257,6 +297,11 @@ dependencies = [ "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "globset" version = "0.1.4" @@ -425,6 +470,11 @@ name = "lazy_static" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lazy_static" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazycell" version = "0.5.1" @@ -499,6 +549,14 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "native-runtime" +version = "0.1.0" +dependencies = [ + "runtime-support 0.1.0", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.31" @@ -561,6 +619,15 @@ dependencies = [ "parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.2.6" @@ -663,6 +730,7 @@ dependencies = [ "polkadot-primitives 0.1.0", "polkadot-serializer 0.1.0", "polkadot-state-machine 0.1.0", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -671,15 +739,19 @@ dependencies = [ name = "polkadot-primitives" version = "0.1.0" dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "fixed-hash 0.1.0 (git+https://github.com/paritytech/primitives.git)", "polkadot-serializer 0.1.0", "pretty_assertions 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.1.0 (git+https://github.com/paritytech/primitives.git)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -719,7 +791,6 @@ version = "0.1.0" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", @@ -759,6 +830,26 @@ dependencies = [ "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rayon" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_syscall" version = "0.1.31" @@ -797,6 +888,18 @@ dependencies = [ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ring" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rlp" version = "0.2.1" @@ -809,6 +912,17 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "runtime-support" +version = "0.1.0" +dependencies = [ + "environmental 0.1.0", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "polkadot-state-machine 0.1.0", +] + [[package]] name = "rustc-demangle" version = "0.1.5" @@ -845,6 +959,11 @@ name = "scoped-tls" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "semver" version = "0.1.20" @@ -1063,6 +1182,14 @@ dependencies = [ "rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "twox-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "uint" version = "0.1.0" @@ -1099,6 +1226,11 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "untrusted" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" version = "1.0.0" @@ -1142,6 +1274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum arrayvec 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e003cbf6e0e1c43a0fc8df2ea8ea24174514d35cbcf60c35ca6112e0139f65e2" +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983" @@ -1150,15 +1283,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5442186ef6560f30f1ee4b9c1e4c87a35a6879d3644550cc248ec2b955eb5fcd" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" +"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" @@ -1171,6 +1308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" "checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" "checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf" +"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a" "checksum hashdb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d97be07c358c5b461268b4ce60304024c5fa5acfd4bd8cd743639f0252003cf5" "checksum heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "54fab2624374e5137ae4df13bf32b0b269cb804df42d13a51221bbd431d1a237" @@ -1188,6 +1326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" @@ -1204,6 +1343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)" = "235801e9531998c4bb307f4ea6833c9f40a4cf132895219ac8c2cd25a9b310f7" "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_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" @@ -1211,11 +1351,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pretty_assertions 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94b6bbc8a323d89a019c4cdde21850522fb8405e97add70827177fc2f86c1495" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" +"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" +"checksum rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b609139d83da75902f88fd6c01820046840a18471e4dfcd5ac7c0f46bea53" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" +"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" "checksum rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "babe6fce20c0ca9b1582998734c4569082d0ad08e43772a1c6c40aef4f106ef9" "checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" "checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" @@ -1223,6 +1366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" @@ -1250,11 +1394,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" "checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" "checksum triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9291c7f0fae44858b5e087dd462afb382354120003778f1695b44aab98c7abd7" +"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435" "checksum uint 0.1.0 (git+https://github.com/paritytech/primitives.git)" = "" "checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index a0f6303a6782f..21232d39ef32b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,14 +12,16 @@ members = [ "candidate-agreement", "client", "collator", + "environmental", "executor", "primitives", "rpc", "rpc_servers", + "native-runtime", "serializer", "state_machine", "validator", ] exclude = [ - "runtime" + "wasm-runtime" ] diff --git a/environmental/Cargo.toml b/environmental/Cargo.toml new file mode 100644 index 0000000000000..52c3337f8a130 --- /dev/null +++ b/environmental/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "environmental" +version = "0.1.0" +authors = ["Parity Technologies "] diff --git a/environmental/src/lib.rs b/environmental/src/lib.rs new file mode 100644 index 0000000000000..ef5b5967bea9f --- /dev/null +++ b/environmental/src/lib.rs @@ -0,0 +1,272 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Safe global references to stack variables. +//! +//! Set up a global reference with environmental! macro giving it a name and type. +//! Use the `using` function scoped under its name to name a reference and call a function that +//! takes no parameters yet can access said reference through the similarly placed `with` function. +//! +//! # Examples +//! +//! ``` +//! #[macro_use] extern crate environmental; +//! // create a place for the global reference to exist. +//! environmental!(counter: u32); +//! fn stuff() { +//! // do some stuff, accessing the named reference as desired. +//! counter::with(|i| *i += 1); +//! } +//! fn main() { +//! // declare a stack variable of the same type as our global declaration. +//! let mut counter_value = 41u32; +//! // call stuff, setting up our `counter` environment as a refrence to our counter_value var. +//! counter::using(&mut counter_value, stuff); +//! println!("The answer is {:?}", counter_value); // will print 42! +//! stuff(); // safe! doesn't do anything. +//! } +//! ``` + +use std::cell::RefCell; +use std::thread::LocalKey; + +#[doc(hidden)] +pub fn using R>( + global: &'static LocalKey>>, + protected: &mut T, + f: F +) -> R { + // store the `protected` reference as a pointer so we can provide it to logic running within + // `f`. + // while we record this pointer (while it's non-zero) we guarantee: + // - it will only be used once at any time (no reentrancy); + // - that no other thread will use it; and + // - that we do not use the original mutating reference while the pointer. + // exists. + global.with(|r| { + let original = { + let mut global = r.borrow_mut(); + ::std::mem::replace(&mut *global, Some(protected as _)) + }; + + // even if `f` panics the original will be replaced. + struct ReplaceOriginal<'a, T: 'a + ?Sized> { + original: Option<*mut T>, + global: &'a RefCell>, + } + + impl<'a, T: 'a + ?Sized> Drop for ReplaceOriginal<'a, T> { + fn drop(&mut self) { + *self.global.borrow_mut() = self.original.take(); + } + } + + let _guard = ReplaceOriginal { + original, + global: r, + }; + + f() + }) +} + +#[doc(hidden)] +pub fn with R>( + global: &'static LocalKey>>, + mutator: F, +) -> Option { + global.with(|r| unsafe { + let ptr = r.borrow_mut(); + match *ptr { + Some(ptr) => { + // safe because it's only non-zero when it's being called from using, which + // is holding on to the underlying reference (and not using it itself) safely. + Some(mutator(&mut *ptr)) + } + None => None, + } + }) +} + +/// Declare a new global reference module whose underlying value does not contain references. +/// +/// Will create a module of a given name that contains two functions: +/// * `pub fn using R>(protected: &mut $t, f: F) -> R` +/// This executes `f`, returning its value. During the call, the module's reference is set to +/// be equal to `protected`. +/// * `pub fn with R>(f: F) -> Option` +/// This executes `f`, returning `Some` of its value if called from code that is being executed +/// as part of a `using` call. If not, it returns `None`. `f` is provided with one argument: the +/// same reference as provided to the most recent `using` call. +/// +/// # Examples +/// +/// Initializing the global context with a given value. +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// environmental!(counter: u32); +/// fn main() { +/// let mut counter_value = 41u32; +/// counter::using(&mut counter_value, || { +/// let odd = counter::with(|value| +/// if *value % 2 == 1 { +/// *value += 1; true +/// } else { +/// *value -= 3; false +/// }).unwrap(); // safe because we're inside a counter::using +/// println!("counter was {}", match odd { true => "odd", _ => "even" }); +/// }); +/// +/// println!("The answer is {:?}", counter_value); // 42 +/// } +/// ``` +/// +/// Roughly the same, but with a trait object: +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// +/// trait Increment { fn increment(&mut self); } +/// +/// impl Increment for i32 { +/// fn increment(&mut self) { *self += 1 } +/// } +/// +/// environmental!(val: Increment + 'static); +/// +/// fn main() { +/// let mut local = 0i32; +/// val::using(&mut local, || { +/// val::with(|v| for _ in 0..5 { v.increment() }); +/// }); +/// +/// assert_eq!(local, 5); +/// } +/// ``` +#[macro_export] +macro_rules! environmental { + ($name:ident : $t:ty) => { + #[allow(non_camel_case_types)] + struct $name { __private_field: () } + + thread_local!(static GLOBAL: ::std::cell::RefCell> + = ::std::cell::RefCell::new(None)); + + impl $name { + #[allow(unused_imports)] + + pub fn using R>( + protected: &mut $t, + f: F + ) -> R { + $crate::using(&GLOBAL, protected, f) + } + + pub fn with R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; +} + +#[cfg(test)] +mod tests { + + #[test] + fn simple_works() { + environmental!(counter: u32); + + fn stuff() { counter::with(|value| *value += 1); }; + + // declare a stack variable of the same type as our global declaration. + let mut local = 41u32; + + // call stuff, setting up our `counter` environment as a refrence to our local counter var. + counter::using(&mut local, stuff); + assert_eq!(local, 42); + stuff(); // safe! doesn't do anything. + } + + #[test] + fn overwrite_with_lesser_lifetime() { + environmental!(items: Vec); + + let mut local_items = vec![1, 2, 3]; + items::using(&mut local_items, || { + let dies_at_end = vec![4, 5, 6]; + items::with(|items| *items = dies_at_end); + }); + + assert_eq!(local_items, vec![4, 5, 6]); + } + + #[test] + fn declare_with_trait_object() { + trait Foo { + fn get(&self) -> i32; + fn set(&mut self, x: i32); + } + + impl Foo for i32 { + fn get(&self) -> i32 { *self } + fn set(&mut self, x: i32) { *self = x } + } + + environmental!(foo: Foo + 'static); + + fn stuff() { + foo::with(|value| { + let new_val = value.get() + 1; + value.set(new_val); + }); + } + + let mut local = 41i32; + foo::using(&mut local, stuff); + + assert_eq!(local, 42); + + stuff(); // doesn't do anything. + } + + #[test] + fn unwind_recursive() { + use std::panic; + + environmental!(items: Vec); + + let panicked = panic::catch_unwind(|| { + let mut local_outer = vec![1, 2, 3]; + + items::using(&mut local_outer, || { + let mut local_inner = vec![4, 5, 6]; + items::using(&mut local_inner, || { + panic!("are you unsafe?"); + }) + }); + }).is_err(); + + assert!(panicked); + + let mut was_cleared = true; + items::with(|_items| was_cleared = false); + + assert!(was_cleared); + } +} diff --git a/executor/Cargo.toml b/executor/Cargo.toml index 8aa72f4327b81..0401bd4c771f8 100644 --- a/executor/Cargo.toml +++ b/executor/Cargo.toml @@ -12,6 +12,7 @@ serde = "1.0" serde_derive = "1.0" parity-wasm = "0.15.0" byteorder = "1.1" +rustc-hex = "1.0.0" [dev-dependencies] assert_matches = "1.1" diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 8bf7f42b623fe..f1da582538744 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -33,6 +33,7 @@ extern crate polkadot_state_machine as state_machine; extern crate serde; extern crate parity_wasm; extern crate byteorder; +extern crate rustc_hex; #[macro_use] extern crate error_chain; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index f52b2cbf715e1..7fc9a741f83b5 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -26,6 +26,7 @@ use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; use wasm_utils::{MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; +use primitives::{ed25519, blake2_256, twox_128, twox_256}; struct Heap { end: u32, @@ -76,7 +77,7 @@ impl WritePrimitive for MemoryInstance { } impl_function_executor!(this: FunctionExecutor<'e, E>, - ext_print(utf8_data: *const u8, utf8_len: i32) => { + ext_print(utf8_data: *const u8, utf8_len: u32) => { if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) { if let Ok(message) = String::from_utf8(utf8) { println!("Runtime: {}", message); @@ -96,7 +97,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { + ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { let _ = this.memory.clear(dest as usize, val as u8, count as usize); println!("memset {} with {}, {} bytes", dest, val, count); dest @@ -110,12 +111,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.heap.deallocate(addr); println!("free {}", addr) }, - ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { + ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { if let (Ok(key), Ok(value)) = (this.memory.get(key_data, key_len as usize), this.memory.get(value_data, value_len as usize)) { this.ext.set_storage(key, value); } }, - ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { + ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { if let Ok(value) = this.ext.storage(&key) { let offset = this.heap.allocate(value.len() as u32) as u32; @@ -126,6 +127,63 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.write_primitive(written_out, written); offset as u32 + }, + ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32 => { + if let Ok(key) = this.memory.get(key_data, key_len as usize) { + if let Ok(value) = this.ext.storage(&key) { + let written = ::std::cmp::min(value_len as usize, value.len()); + let _ = this.memory.set(value_data, &value[0..written]); + written as u32 + } else { 0 } + } else { 0 } + }, + ext_chain_id() -> u64 => { + this.ext.chain_id() + }, + ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { + let result = + if let Ok(value) = this.memory.get(data, len as usize) { + twox_128(&value) + } else { + [0; 16] + }; + let _ = this.memory.set(out, &result); + }, + ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { + let result = + if let Ok(value) = this.memory.get(data, len as usize) { + twox_256(&value) + } else { + [0; 32] + }; + let _ = this.memory.set(out, &result); + }, + ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { + let result = + if let Ok(value) = this.memory.get(data, len as usize) { + blake2_256(&value) + } else { + [0; 32] + }; + let _ = this.memory.set(out, &result); + }, + ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { + (||{ + let mut sig = [0u8; 64]; + if let Err(_) = this.memory.get_into(sig_data, &mut sig[..]) { + return 0; + }; + let mut pubkey = [0u8; 32]; + if let Err(_) = this.memory.get_into(pubkey_data, &mut pubkey[..]) { + return 0; + }; + + if let Ok(msg) = this.memory.get(msg_data, msg_len as usize) { + if ed25519::Signature::from(sig).verify(&msg, &ed25519::Public::from(pubkey)) { 1 } else { 0 } + } else { + 0 + } + })() } => <'e, E: Externalities + 'e> ); @@ -181,6 +239,7 @@ impl CodeExecutor for WasmExecutor { mod tests { use super::*; + use rustc_hex::FromHex; #[derive(Debug, Default)] struct TestExternalities { @@ -196,53 +255,82 @@ mod tests { fn set_storage(&mut self, key: Vec, value: Vec) { self.storage.insert(key, value); } + + fn chain_id(&self) -> u64 { 42 } } #[test] - fn should_pass_externalities_at_call() { + fn storage_should_work() { let mut ext = TestExternalities::default(); - ext.set_storage(b"\0code".to_vec(), b"The code".to_vec()); - - let program = ProgramInstance::new().unwrap(); - - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); - - let output = { - let memory = module.memory(ItemIndex::Internal(0)).unwrap(); - let mut fec = FunctionExecutor::new(&memory, &mut ext); - - let data = b"Hello world"; - let size = data.len() as u32; - let offset = fec.heap.allocate(size); - memory.set(offset, data).unwrap(); - - let returned = program - .params_with_external("env", &mut fec) - .map(|p| p - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) - .and_then(|p| module.execute_export("test_data_in", p)) - .map_err(|_| -> Error { ErrorKind::Runtime.into() }).expect("function should be callable"); - - if let Some(I64(r)) = returned { - println!("returned {:?} ({:?}, {:?})", r, r as u32, (r >> 32) as u32 as usize); - memory.get(r as u32, (r >> 32) as u32 as usize).expect("memory address should be reasonable.") - } else { - panic!("bad return value, not u64"); - } - }; + ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let output = WasmExecutor.call(&mut ext, &test_code[..], "test_data_in", &CallData(b"Hello world".to_vec())).unwrap(); assert_eq!(output, b"all ok!".to_vec()); let expected: HashMap<_, _> = map![ - b"\0code".to_vec() => b"Hello world".to_vec(), b"input".to_vec() => b"Hello world".to_vec(), - b"code".to_vec() => b"The code".to_vec(), - b"\0validator_count".to_vec() => vec![1], - b"\0validator".to_vec() => b"Hello world".to_vec() + b"foo".to_vec() => b"bar".to_vec(), + b"baz".to_vec() => b"bar".to_vec() ]; assert_eq!(expected, ext.storage); } + + #[test] + fn blake2_256_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_blake2_256", &CallData(b"".to_vec())).unwrap(), + FromHex::from_hex("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + ); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_blake2_256", &CallData(b"Hello world!".to_vec())).unwrap(), + FromHex::from_hex("3fbc092db9350757e2ab4f7ee9792bfcd2f5220ada5a4bc684487f60c6034369").unwrap() + ); + } + + #[test] + fn twox_256_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_twox_256", &CallData(b"".to_vec())).unwrap(), + FromHex::from_hex("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a").unwrap() + ); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_twox_256", &CallData(b"Hello world!".to_vec())).unwrap(), + FromHex::from_hex("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74").unwrap() + ); + } + + #[test] + fn twox_128_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_twox_128", &CallData(b"".to_vec())).unwrap(), + FromHex::from_hex("99e9d85137db46ef4bbea33613baafd5").unwrap() + ); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_twox_128", &CallData(b"Hello world!".to_vec())).unwrap(), + FromHex::from_hex("b27dfd7f223f177f2a13647b533599af").unwrap() + ); + } + + #[test] + fn ed25519_verify_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + let key = ed25519::Pair::from_seed(&blake2_256(b"test")); + let sig = key.sign(b"all ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_ed25519_verify", &CallData(calldata)).unwrap(), + vec![1] + ); + } } diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index 577e6ab0b23d6..f6440628b986e 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -75,13 +75,13 @@ macro_rules! reverse_params { #[macro_export] macro_rules! marshall { - ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); - Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) - }); ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ reverse_params!($body, $self, $context, $( $names : $params ),*); Ok(None) + }); + ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); + Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) }) } @@ -119,13 +119,13 @@ pub trait IntoUserDefinedElements { #[macro_export] macro_rules! impl_function_executor { ( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => ( - impl $( $pre ) + $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { + impl $($pre)+ $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); } - impl $( $pre ) + $structname { + impl $($pre)+ $structname { signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); } - impl $( $pre ) + $crate::wasm_utils::IntoUserDefinedElements for $structname { + impl $($pre)+ $crate::wasm_utils::IntoUserDefinedElements for $structname { fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> { $crate::wasm_utils::UserDefinedElements { executor: Some(self), diff --git a/native-runtime/Cargo.toml b/native-runtime/Cargo.toml new file mode 100644 index 0000000000000..eb5eb82408f18 --- /dev/null +++ b/native-runtime/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "native-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[features] +default = ["with-std"] +with-std = [] +without-std = [] + +[dependencies] +runtime-support = { path = "./support", version = "0.1" } +rustc-hex = "1.0" diff --git a/native-runtime/src b/native-runtime/src new file mode 120000 index 0000000000000..12e3333860cd6 --- /dev/null +++ b/native-runtime/src @@ -0,0 +1 @@ +../wasm-runtime/polkadot/src \ No newline at end of file diff --git a/native-runtime/support/Cargo.toml b/native-runtime/support/Cargo.toml new file mode 100644 index 0000000000000..1c24c014e64f5 --- /dev/null +++ b/native-runtime/support/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "runtime-support" +version = "0.1.0" +authors = ["Parity Technologies "] + +[features] +strict = [] + +[dependencies] +lazy_static = "1.0.0" +parking_lot = "0.5" +polkadot-state-machine = { path = "../../state_machine" , version = "0.1" } +environmental = { path = "../../environmental", version = "0.1.0" } +polkadot-primitives = { path = "../../primitives", version = "0.1.0" } diff --git a/native-runtime/support/src/lib.rs b/native-runtime/support/src/lib.rs new file mode 100644 index 0000000000000..348c0b17a9ccb --- /dev/null +++ b/native-runtime/support/src/lib.rs @@ -0,0 +1,149 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! The with-std support functions for the runtime. + +#[macro_use] +extern crate environmental; +extern crate polkadot_state_machine; +extern crate polkadot_primitives as primitives; + +use std::fmt; +use primitives::ed25519; + +pub use std::vec::Vec; +pub use std::rc::Rc; +pub use std::cell::RefCell; +pub use std::boxed::Box; +pub use std::slice; +pub use std::mem::{size_of, transmute, swap, uninitialized}; + +pub use polkadot_state_machine::Externalities; + +// TODO: use the real error, not NoError. + +#[derive(Debug)] +/// As it says - an empty type we use for errors. +pub struct NoError; +impl fmt::Display for NoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } +} + +environmental!(ext : Externalities + 'static); + +/// Get `key` from storage and return a `Vec`, empty if there's a problem. +pub fn storage(key: &[u8]) -> Vec { + ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec())) + .unwrap_or(None) + .unwrap_or_else(|| vec![]) +} + +/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return +/// the number of bytes that the key in storage was. +pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { + ext::with(|ext| { + if let Ok(value) = ext.storage(key) { + let written = ::std::cmp::min(value.len(), value_out.len()); + value_out[0..written].copy_from_slice(&value[0..written]); + value.len() + } else { + 0 + } + }).unwrap_or(0) +} + +pub fn set_storage(key: &[u8], value: &[u8]) { + ext::with(|ext| + ext.set_storage(key.to_vec(), value.to_vec()) + ); +} + +/// The current relay chain identifier. +pub fn chain_id() -> u64 { + ext::with(|ext| + ext.chain_id() + ).unwrap_or(0) +} + +/// Conduct a Keccak-256 hash of the given data. +pub use primitives::{blake2_256, twox_128, twox_256}; + +/// Verify a ed25519 signature. +pub fn ed25519_verify(sig: &[u8; 64], msg: &[u8], pubkey: &[u8; 32]) -> bool { + ed25519::verify(&sig[..], msg, &pubkey[..]) +} + +/// Execute the given closure with global function available whose functionality routes into the +/// externalities `ext`. Forwards the value that the closure returns. +pub fn with_externalities R>(ext: &mut (Externalities + 'static), f: F) -> R { + ext::using(ext, f) +} + +#[macro_export] +macro_rules! impl_stubs { + ($( $name:ident ),*) => {} +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + + #[derive(Debug, Default)] + struct TestExternalities { + storage: HashMap, Vec>, + } + impl Externalities for TestExternalities { + type Error = NoError; + + fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } + + fn set_storage(&mut self, key: Vec, value: Vec) { + self.storage.insert(key, value); + } + + fn chain_id(&self) -> u64 { 42 } + } + + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + #[test] + fn storage_works() { + let mut t = TestExternalities { storage: map![], }; + assert!(with_externalities(&mut t, || { + assert_eq!(storage(b"hello"), b"".to_vec()); + set_storage(b"hello", b"world"); + assert_eq!(storage(b"hello"), b"world".to_vec()); + assert_eq!(storage(b"foo"), b"".to_vec()); + set_storage(b"foo", &[1, 2, 3][..]); + true + })); + + t.storage = map![b"foo".to_vec() => b"bar".to_vec()]; + + assert!(!with_externalities(&mut t, || { + assert_eq!(storage(b"hello"), b"".to_vec()); + assert_eq!(storage(b"foo"), b"bar".to_vec()); + false + })); + } +} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index f31afccf5b266..77eccdea883e8 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -9,8 +9,12 @@ fixed-hash = { git = "https://github.com/paritytech/primitives.git" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" -tiny-keccak = "1.3" uint = { git = "https://github.com/paritytech/primitives.git" } +ring = "0.12" +untrusted = "0.5" +twox-hash = "1.1.0" +byteorder = "1.1" +blake2-rfc = "0.2.18" [dev-dependencies] polkadot-serializer = { path = "../serializer", version = "0.1" } diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 740b0c4cdada3..8cd077fd78f13 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -97,7 +97,7 @@ mod tests { "candidates": [ { "parachainIndex": 10, - "collatorSignature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "collatorSignature": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "unprocessedIngress": [], "block": "0x01030508" } diff --git a/primitives/src/ed25519.rs b/primitives/src/ed25519.rs new file mode 100644 index 0000000000000..b28e0bde6e2cc --- /dev/null +++ b/primitives/src/ed25519.rs @@ -0,0 +1,235 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Simple Ed25519 API. + +use untrusted; +use ring::{rand, signature}; +use rustc_hex::FromHex; + +/// Verify a message without type checking the parameters' types for the right size. +pub fn verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { + let public_key = untrusted::Input::from(public); + let msg = untrusted::Input::from(message); + let sig = untrusted::Input::from(sig); + + match signature::verify(&signature::ED25519, public_key, msg, sig) { + Ok(_) => true, + _ => false, + } +} + +/// A public key. +#[derive(PartialEq, Clone, Debug)] +pub struct Public ([u8; 32]); + +/// A key pair. +pub struct Pair(signature::Ed25519KeyPair); + +/// A signature. +#[derive(Clone)] +pub struct Signature ([u8; 64]); + +impl Signature { + /// A new signature from the given 64-byte `data`. + pub fn from(data: [u8; 64]) -> Self { + Signature(data) + } + + /// A new signature from the given slice that should be 64 bytes long. + pub fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 64]; + r.copy_from_slice(data); + Signature(r) + } +} + +impl AsRef<[u8; 64]> for Signature { + fn as_ref(&self) -> &[u8; 64] { + &self.0 + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl Public { + /// A new instance from the given 32-byte `data`. + pub fn from(data: [u8; 32]) -> Self { + Public(data) + } + + /// A new instance from the given slice that should be 32 bytes long. + pub fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(data); + Public(r) + } +} + +impl AsRef<[u8; 32]> for Public { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl Pair { + /// Generate new secure (random) key pair. + pub fn new() -> Pair { + let rng = rand::SystemRandom::new(); + let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); + Pair(signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap()) + } + /// Make a new key pair from a seed phrase. + pub fn from_seed(seed: &[u8; 32]) -> Pair { + Pair(signature::Ed25519KeyPair::from_seed_unchecked(untrusted::Input::from(&seed[..])).unwrap()) + } + /// Make a new key pair from the raw secret. + pub fn from_secret(secret: &[u8; 32]) -> Pair { + let mut pkcs8_bytes = FromHex::from_hex("302e020100300506032b657004220420").unwrap(); + pkcs8_bytes.extend_from_slice(&secret[..]); + Pair(signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(untrusted::Input::from(&pkcs8_bytes)).unwrap()) + } + /// Make a new key pair from the raw secret and public key (it will check to make sure + /// they correspond to each other). + pub fn from_both(secret_public: &[u8; 64]) -> Option { + let mut pkcs8_bytes = FromHex::from_hex("3053020101300506032b657004220420").unwrap(); + pkcs8_bytes.extend_from_slice(&secret_public[0..32]); + pkcs8_bytes.extend_from_slice(&[0xa1u8, 0x23, 0x03, 0x21, 0x00]); + pkcs8_bytes.extend_from_slice(&secret_public[32..64]); + signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(untrusted::Input::from(&pkcs8_bytes)).ok().map(Pair) + } + /// Sign a message. + pub fn sign(&self, message: &[u8]) -> Signature { + let mut r = [0u8; 64]; + r.copy_from_slice(self.0.sign(message).as_ref()); + Signature(r) + } + /// Get the public key. + pub fn public(&self) -> Public { + let mut r = [0u8; 32]; + let pk = self.0.public_key_bytes(); + r.copy_from_slice(pk); + Public(r) + } +} +impl Signature { + /// Verify a message. + pub fn verify(&self, message: &[u8], public: &Public) -> bool { + let peer_public_key = untrusted::Input::from(&public.0[..]); + let msg = untrusted::Input::from(message); + let sig = untrusted::Input::from(&self.0[..]); + + match signature::verify(&signature::ED25519, peer_public_key, msg, sig) { + Ok(_) => true, + _ => false, + } + } +} +impl From<&'static str> for Public { + fn from(hex: &'static str) -> Self { + let mut r = [0u8; 32]; + r.copy_from_slice(&FromHex::from_hex(hex).unwrap()[0..32]); + Public(r) + } +} +impl From<&'static str> for Pair { + fn from(hex: &'static str) -> Self { + let data = FromHex::from_hex(hex).expect("Key pair given is static so hex should be good."); + match data.len() { + 32 => { + let mut r = [0u8; 32]; + r.copy_from_slice(&data[0..32]); + Pair::from_secret(&r) + } + 64 => { + let mut r = [0u8; 64]; + r.copy_from_slice(&data[0..64]); + Pair::from_both(&r).expect("Key pair given is static so should be good.") + } + _ => { + panic!("Key pair given is static so should be correct length."); + } + } + } +} +impl From<&'static str> for Signature { + fn from(hex: &'static str) -> Self { + let mut r = [0u8; 64]; + r.copy_from_slice(&FromHex::from_hex(hex).unwrap()[0..64]); + Signature(r) + } +} + +impl PartialEq for Signature { + fn eq(&self, other: &Signature) -> bool { + self.0.iter().eq(other.0.iter()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_vector_should_work() { + let pair: Pair = "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60".into(); + let public = pair.public(); + assert_eq!(public, "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".into()); + let message = b""; + let signature: Signature = "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b".into(); + assert!(&pair.sign(&message[..]) == &signature); + assert!(signature.verify(&message[..], &public)); + } + + #[test] + fn generated_pair_should_work() { + let pair = Pair::new(); + let public = pair.public(); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(signature.verify(&message[..], &public)); + } + + #[test] + fn seeded_pair_should_work() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + assert_eq!(public, "2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".into()); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(signature.verify(&message[..], &public)); + } + + #[test] + fn can_sign_transaction() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + assert_eq!(public, "2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".into()); + let message = FromHex::from_hex("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000").unwrap(); + let signature = pair.sign(&message[..]); + assert!(signature.verify(&message[..], &public)); + } +} diff --git a/primitives/src/hash.rs b/primitives/src/hash.rs index cef573f6f29b1..0b56d5757fafb 100644 --- a/primitives/src/hash.rs +++ b/primitives/src/hash.rs @@ -41,8 +41,8 @@ impl_hash!(H160, 20); impl_serde!(H160, 20); impl_hash!(H256, 32); impl_serde!(H256, 32); -impl_hash!(H520, 65); -impl_serde!(H520, 65); +impl_hash!(H512, 64); +impl_serde!(H512, 64); #[cfg(test)] mod tests { diff --git a/primitives/src/hashing.rs b/primitives/src/hashing.rs new file mode 100644 index 0000000000000..f82ec7ef8bf78 --- /dev/null +++ b/primitives/src/hashing.rs @@ -0,0 +1,106 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Hashing functions. + +use blake2_rfc; +use twox_hash; + +/// Do a Blake2 512-bit hash and place result in `dest`. +pub fn blake2_512_into(data: &[u8], dest: &mut[u8; 64]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(64, &[], data).as_bytes()); +} + +/// Do a Blake2 512-bit hash and return result. +pub fn blake2_512(data: &[u8]) -> [u8; 64] { + let mut r: [u8; 64] = unsafe { ::std::mem::uninitialized() }; + blake2_512_into(data, &mut r); + r +} + +/// Do a Blake2 256-bit hash and place result in `dest`. +pub fn blake2_256_into(data: &[u8], dest: &mut[u8; 32]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes()); +} + +/// Do a Blake2 256-bit hash and return result. +pub fn blake2_256(data: &[u8]) -> [u8; 32] { + let mut r: [u8; 32] = unsafe { ::std::mem::uninitialized() }; + blake2_256_into(data, &mut r); + r +} + +/// Do a Blake2 128-bit hash and place result in `dest`. +pub fn blake2_128_into(data: &[u8], dest: &mut[u8; 16]) { + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(16, &[], data).as_bytes()); +} + +/// Do a Blake2 128-bit hash and return result. +pub fn blake2_128(data: &[u8]) -> [u8; 16] { + let mut r: [u8; 16] = unsafe { ::std::mem::uninitialized() }; + blake2_128_into(data, &mut r); + r +} + +/// Do a XX 128-bit hash and place result in `dest`. +pub fn twox_128_into(data: &[u8], dest: &mut [u8; 16]) { + use ::std::hash::Hasher; + let mut h0 = twox_hash::XxHash::with_seed(0); + let mut h1 = twox_hash::XxHash::with_seed(1); + h0.write(data); + h1.write(data); + let r0 = h0.finish(); + let r1 = h1.finish(); + use byteorder::{ByteOrder, LittleEndian}; + LittleEndian::write_u64(&mut dest[0..8], r0); + LittleEndian::write_u64(&mut dest[8..16], r1); +} + +/// Do a XX 128-bit hash and return result. +pub fn twox_128(data: &[u8]) -> [u8; 16] { + let mut r: [u8; 16] = unsafe { ::std::mem::uninitialized() }; + twox_128_into(data, &mut r); + r +} + +/// Do a XX 256-bit hash and place result in `dest`. +pub fn twox_256_into(data: &[u8], dest: &mut [u8; 32]) { + use ::std::hash::Hasher; + use byteorder::{ByteOrder, LittleEndian}; + let mut h0 = twox_hash::XxHash::with_seed(0); + let mut h1 = twox_hash::XxHash::with_seed(1); + let mut h2 = twox_hash::XxHash::with_seed(2); + let mut h3 = twox_hash::XxHash::with_seed(3); + h0.write(data); + h1.write(data); + h2.write(data); + h3.write(data); + let r0 = h0.finish(); + let r1 = h1.finish(); + let r2 = h2.finish(); + let r3 = h3.finish(); + LittleEndian::write_u64(&mut dest[0..8], r0); + LittleEndian::write_u64(&mut dest[8..16], r1); + LittleEndian::write_u64(&mut dest[16..24], r2); + LittleEndian::write_u64(&mut dest[24..32], r3); +} + +/// Do a XX 256-bit hash and return result. +pub fn twox_256(data: &[u8]) -> [u8; 32] { + let mut r: [u8; 32] = unsafe { ::std::mem::uninitialized() }; + twox_256_into(data, &mut r); + r +} diff --git a/primitives/src/hexdisplay.rs b/primitives/src/hexdisplay.rs new file mode 100644 index 0000000000000..d6299a2e41d1e --- /dev/null +++ b/primitives/src/hexdisplay.rs @@ -0,0 +1,60 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Wrapper type for byte collections that outputs hex. + +/// Simple wrapper to display hex representation of bytes. +pub struct HexDisplay<'a>(&'a [u8]); + +impl<'a> HexDisplay<'a> { + /// Create new instance that will display `d` as a hex string when displayed. + pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) } +} + +impl<'a> ::std::fmt::Display for HexDisplay<'a> { + fn fmt(&self, fmtr: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + for byte in self.0 { + try!( fmtr.write_fmt(format_args!("{:02x}", byte))); + } + Ok(()) + } +} + +/// Simple trait to transferm various types to `&[u8]` +pub trait AsBytesRef { + /// Transferm `self` into `&[u8]`. + fn as_bytes_ref(&self) -> &[u8]; +} + +impl AsBytesRef for [u8] { + fn as_bytes_ref(&self) -> &[u8] { &self } +} + +impl AsBytesRef for Vec { + fn as_bytes_ref(&self) -> &[u8] { &self } +} + +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl AsBytesRef for $t { + fn as_bytes_ref(&self) -> &[u8] { &self[..] } + } + )* } +} + +impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index ca32f302f3e8f..c774ec7f42cad 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -20,7 +20,11 @@ extern crate rustc_hex; extern crate serde; -extern crate tiny_keccak; +extern crate ring; +extern crate untrusted; +extern crate twox_hash; +extern crate byteorder; +extern crate blake2_rfc; #[macro_use] extern crate crunchy; @@ -46,16 +50,20 @@ pub mod hash; pub mod parachain; pub mod uint; pub mod validator; +pub mod ed25519; +pub mod hexdisplay; +pub mod hashing; /// Alias to 160-bit hash when used in the context of an account address. pub type Address = hash::H160; /// Alias to 520-bit hash when used in the context of a signature. -pub type Signature = hash::H520; +pub type Signature = hash::H512; pub use self::hash::{H160, H256}; pub use self::uint::{U256, U512}; +pub use hashing::{blake2_256, twox_128, twox_256}; /// A hash function. pub fn hash(data: &[u8]) -> hash::H256 { - tiny_keccak::keccak256(data).into() + blake2_256(data).into() } diff --git a/primitives/src/parachain.rs b/primitives/src/parachain.rs index 1d64f3c82429a..ed44134ce27ef 100644 --- a/primitives/src/parachain.rs +++ b/primitives/src/parachain.rs @@ -118,7 +118,7 @@ mod tests { block: BlockData(vec![1, 2, 3]), }), r#"{ "parachainIndex": 5, - "collatorSignature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a", + "collatorSignature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a", "unprocessedIngress": [ [ 1, diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs deleted file mode 100644 index ec3fabb89cd15..0000000000000 --- a/runtime/polkadot/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![cfg_attr(feature = "strict", deny(warnings))] - -#![feature(alloc)] -extern crate alloc; -use alloc::vec::Vec; - -#[macro_use] -extern crate runtime_support; -use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; - -impl_stub!(test_data_in); -fn test_data_in(input: Vec) -> Vec { - print(b"set_storage" as &[u8]); - set_storage(b"input", &input); - - print(b"code" as &[u8]); - set_storage(b"code", &code()); - - print(b"set_code" as &[u8]); - set_code(&input); - - print(b"storage" as &[u8]); - let copy = storage(b"input"); - - print(b"validators" as &[u8]); - let mut v = validators(); - v.push(copy); - - print(b"set_validators" as &[u8]); - set_validators(&v.iter().map(Vec::as_slice).collect::>()); - - print(b"finished!" as &[u8]); - b"all ok!".to_vec() -} diff --git a/runtime/support/src/lib.rs b/runtime/support/src/lib.rs deleted file mode 100644 index 472497d284548..0000000000000 --- a/runtime/support/src/lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![cfg_attr(feature = "strict", deny(warnings))] - -#![feature(alloc)] - -extern crate alloc; -use alloc::vec::Vec; - -extern crate pwasm_libc; -extern crate pwasm_alloc; - -#[lang = "panic_fmt"] -#[no_mangle] -pub fn panic_fmt() -> ! { - loop {} -} - -extern "C" { - fn ext_print(utf8_data: *const u8, utf8_len: i32); - fn ext_print_num(value: u64); - fn ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); - fn ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; -} - -pub fn storage(key: &[u8]) -> Vec { - let mut length: i32 = 0; - unsafe { - let ptr = ext_get_allocated_storage(&key[0], key.len() as i32, &mut length); - Vec::from_raw_parts(ptr, length as usize, length as usize) - } -} - -pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - ext_set_storage( - &key[0] as *const u8, key.len() as i32, - &value[0] as *const u8, value.len() as i32 - ); - } -} - -pub fn code() -> Vec { - storage(b"\0code") -} - -pub fn set_code(new: &[u8]) { - set_storage(b"\0code", new) -} - -fn value_vec(mut value: usize, initial: Vec) -> Vec { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; - } - acc -} - -pub fn set_validator(index: usize, validator: &[u8]) { - set_storage(&value_vec(index, b"\0validator".to_vec()), validator); -} - -pub fn validator(index: usize) -> Vec { - storage(&value_vec(index, b"\0validator".to_vec())) -} - -pub fn set_validator_count(count: usize) { - (count..validator_count()).for_each(|i| set_validator(i, &[])); - set_storage(b"\0validator_count", &value_vec(count, Vec::new())); -} - -pub fn validator_count() -> usize { - storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) -} - -pub fn validators() -> Vec> { - (0..validator_count()).into_iter().map(validator).collect() -} - -pub fn set_validators(validators: &[&[u8]]) { - set_validator_count(validators.len()); - validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); -} - -pub trait Printable { - fn print(self); -} - -impl<'a> Printable for &'a [u8] { - fn print(self) { - unsafe { - ext_print(&self[0] as *const u8, self.len() as i32); - } - } -} - -impl Printable for u64 { - fn print(self) { - unsafe { ext_print_num(self); } - } -} - -pub fn print(value: T) { - value.print(); -} - -#[macro_export] -macro_rules! impl_stub { - ($name:ident) => { - pub mod _internal { - extern crate alloc; - - #[no_mangle] - pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { - let input = unsafe { - ::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) - }; - - let output = super::$name(input); - &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) - } - } - } -} diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm deleted file mode 100644 index 6ce8afb77ce21..0000000000000 Binary files a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm and /dev/null differ diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm deleted file mode 100644 index 9fe583e7e40ee..0000000000000 Binary files a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm and /dev/null differ diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm deleted file mode 100644 index 6ce8afb77ce21..0000000000000 Binary files a/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and /dev/null differ diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm deleted file mode 100644 index 9fe583e7e40ee..0000000000000 Binary files a/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm and /dev/null differ diff --git a/runtime/test/src/lib.rs b/runtime/test/src/lib.rs deleted file mode 100644 index ec3fabb89cd15..0000000000000 --- a/runtime/test/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![cfg_attr(feature = "strict", deny(warnings))] - -#![feature(alloc)] -extern crate alloc; -use alloc::vec::Vec; - -#[macro_use] -extern crate runtime_support; -use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; - -impl_stub!(test_data_in); -fn test_data_in(input: Vec) -> Vec { - print(b"set_storage" as &[u8]); - set_storage(b"input", &input); - - print(b"code" as &[u8]); - set_storage(b"code", &code()); - - print(b"set_code" as &[u8]); - set_code(&input); - - print(b"storage" as &[u8]); - let copy = storage(b"input"); - - print(b"validators" as &[u8]); - let mut v = validators(); - v.push(copy); - - print(b"set_validators" as &[u8]); - set_validators(&v.iter().map(Vec::as_slice).collect::>()); - - print(b"finished!" as &[u8]); - b"all ok!".to_vec() -} diff --git a/state_machine/Cargo.toml b/state_machine/Cargo.toml index a0d25c821cb4b..458bae8e1efe4 100644 --- a/state_machine/Cargo.toml +++ b/state_machine/Cargo.toml @@ -7,7 +7,6 @@ description = "Polkadot State Machine" [dependencies] polkadot-primitives = { path = "../primitives", version = "0.1.0" } hashdb = "0.1.1" -keccak-hash = "0.1.0" patricia-trie = "0.1.0" memorydb = "0.1.1" triehash = "0.1" diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index ce7b9a02680dd..1452558ccd957 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -73,4 +73,8 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> fn set_storage(&mut self, key: Vec, value: Vec) { self.overlay.set_storage(key, value); } + + fn chain_id(&self) -> u64 { + 42 + } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 58ba558322ac8..cd25cdcb90ee9 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -22,7 +22,6 @@ extern crate polkadot_primitives as primitives; extern crate hashdb; extern crate memorydb; -extern crate keccak_hash; extern crate patricia_trie; extern crate triehash; @@ -114,13 +113,9 @@ impl OverlayedChanges { pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} -fn value_vec(mut value: usize, initial: Vec) -> Vec { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; - } - acc +fn to_keyed_vec(value: u32, mut prepend: Vec) -> Vec { + prepend.extend((0..::std::mem::size_of::()).into_iter().map(|i| (value >> (i * 8)) as u8)); + prepend } /// Externalities: pinned to specific active address. @@ -134,12 +129,15 @@ pub trait Externalities { /// Set storage of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec); - /// Get the current set of validators. - fn validators(&self) -> Result, Self::Error> { - (0..self.storage(b"\0validator_count")?.into_iter() + /// Get the identity of the chain. + fn chain_id(&self) -> u64; + + /// Get the current set of authorities from storage. + fn authorities(&self) -> Result, Self::Error> { + (0..self.storage(b"con:aut:len")?.into_iter() .rev() - .fold(0, |acc, &i| (acc << 8) + (i as usize))) - .map(|i| self.storage(&value_vec(i, b"\0validator".to_vec()))) + .fold(0, |acc, &i| (acc << 8) + (i as u32))) + .map(|i| self.storage(&to_keyed_vec(i, b"con:aut:".to_vec()))) .collect() } } @@ -179,7 +177,7 @@ pub fn execute( overlay: &mut *overlay }; // make a copy. - let code = externalities.storage(b"\0code").unwrap_or(&[]).to_vec(); + let code = externalities.storage(b":code").unwrap_or(&[]).to_vec(); exec.call( &mut externalities, @@ -245,25 +243,27 @@ mod tests { fn set_storage(&mut self, key: Vec, value: Vec) { self.storage.insert(key, value); } + + fn chain_id(&self) -> u64 { 42 } } #[test] - fn validators_call_works() { + fn authorities_call_works() { let mut ext = TestExternalities::default(); - assert_eq!(ext.validators(), Ok(vec![])); + assert_eq!(ext.authorities(), Ok(vec![])); - ext.set_storage(b"\0validator_count".to_vec(), vec![]); - assert_eq!(ext.validators(), Ok(vec![])); + ext.set_storage(b"con:aut:len".to_vec(), vec![0u8; 4]); + assert_eq!(ext.authorities(), Ok(vec![])); - ext.set_storage(b"\0validator_count".to_vec(), vec![1]); - assert_eq!(ext.validators(), Ok(vec![&[][..]])); + ext.set_storage(b"con:aut:len".to_vec(), vec![1u8, 0, 0, 0]); + assert_eq!(ext.authorities(), Ok(vec![&[][..]])); - ext.set_storage(b"\0validator".to_vec(), b"first".to_vec()); - assert_eq!(ext.validators(), Ok(vec![&b"first"[..]])); + ext.set_storage(b"con:aut:\0\0\0\0".to_vec(), b"first".to_vec()); + assert_eq!(ext.authorities(), Ok(vec![&b"first"[..]])); - ext.set_storage(b"\0validator_count".to_vec(), vec![2]); - ext.set_storage(b"\0validator\x01".to_vec(), b"second".to_vec()); - assert_eq!(ext.validators(), Ok(vec![&b"first"[..], &b"second"[..]])); + ext.set_storage(b"con:aut:len".to_vec(), vec![2u8, 0, 0, 0]); + ext.set_storage(b"con:aut:\x01\0\0\0".to_vec(), b"second".to_vec()); + assert_eq!(ext.authorities(), Ok(vec![&b"first"[..], &b"second"[..]])); } } diff --git a/runtime/Cargo.lock b/wasm-runtime/Cargo.lock similarity index 100% rename from runtime/Cargo.lock rename to wasm-runtime/Cargo.lock diff --git a/runtime/Cargo.toml b/wasm-runtime/Cargo.toml similarity index 100% rename from runtime/Cargo.toml rename to wasm-runtime/Cargo.toml diff --git a/runtime/build.sh b/wasm-runtime/build.sh similarity index 100% rename from runtime/build.sh rename to wasm-runtime/build.sh diff --git a/runtime/init.sh b/wasm-runtime/init.sh similarity index 100% rename from runtime/init.sh rename to wasm-runtime/init.sh diff --git a/runtime/polkadot/Cargo.toml b/wasm-runtime/polkadot/Cargo.toml similarity index 75% rename from runtime/polkadot/Cargo.toml rename to wasm-runtime/polkadot/Cargo.toml index a313da2a1ef88..ce2d9909a7cde 100644 --- a/runtime/polkadot/Cargo.toml +++ b/wasm-runtime/polkadot/Cargo.toml @@ -8,3 +8,8 @@ crate-type = ["cdylib"] [dependencies] runtime-support = { path = "../support", version = "0.1" } + +[features] +default = ["without-std"] +with-std = [] +without-std = [] diff --git a/wasm-runtime/polkadot/src/codec/endiansensitive.rs b/wasm-runtime/polkadot/src/codec/endiansensitive.rs new file mode 100644 index 0000000000000..a1576284228e9 --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/endiansensitive.rs @@ -0,0 +1,50 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Endian manager. + +/// Trait to allow conversion to a know endian representation when sensitive. +pub trait EndianSensitive: Sized { + fn to_le(self) -> Self { self } + fn to_be(self) -> Self { self } + fn from_le(self) -> Self { self } + fn from_be(self) -> Self { self } + fn as_be_then T>(&self, f: F) -> T { f(&self) } + fn as_le_then T>(&self, f: F) -> T { f(&self) } +} + +macro_rules! impl_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t { + fn to_le(self) -> Self { <$t>::to_le(self) } + fn to_be(self) -> Self { <$t>::to_be(self) } + fn from_le(self) -> Self { <$t>::from_le(self) } + fn from_be(self) -> Self { <$t>::from_be(self) } + fn as_be_then T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } + fn as_le_then T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } + } + )* } +} +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t {} + )* } +} + +impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); +impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); diff --git a/wasm-runtime/polkadot/src/codec/joiner.rs b/wasm-runtime/polkadot/src/codec/joiner.rs new file mode 100644 index 0000000000000..92a5aa87fca97 --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/joiner.rs @@ -0,0 +1,32 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Vec serialiser. + +use runtime_support::Vec; +use slicable::Slicable; + +/// Trait to allow itself to be serialised into a `Vec` +pub trait Joiner { + fn join(self, value: &T) -> Self; +} + +impl Joiner for Vec { + fn join(mut self, value: &T) -> Vec { + value.as_slice_then(|s| self.extend_from_slice(s)); + self + } +} diff --git a/wasm-runtime/polkadot/src/codec/keyedvec.rs b/wasm-runtime/polkadot/src/codec/keyedvec.rs new file mode 100644 index 0000000000000..1f803b7c6206e --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/keyedvec.rs @@ -0,0 +1,56 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Serialiser and prepender. + +use runtime_support::Vec; +use slicable::Slicable; + +/// Trait to allow itselg to be serialised and prepended by a given slice. +pub trait KeyedVec { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec; +} + +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl KeyedVec for $t { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { + let mut r = prepend_key.to_vec(); + r.extend_from_slice(&self[..]); + r + } + } + )* } +} + +macro_rules! impl_endians { + ( $( $t:ty ),* ) => { $( + impl KeyedVec for $t { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { + self.as_slice_then(|slice| { + let mut r = prepend_key.to_vec(); + r.extend_from_slice(slice); + r + }) + } + } + )* } +} + +impl_endians!(u8, i8, u16, u32, u64, usize, i16, i32, i64, isize); +impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); diff --git a/wasm-runtime/polkadot/src/codec/mod.rs b/wasm-runtime/polkadot/src/codec/mod.rs new file mode 100644 index 0000000000000..7d9bc90c4fe12 --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Codec utils. + +pub mod endiansensitive; +pub mod streamreader; +pub mod joiner; +pub mod slicable; +pub mod keyedvec; diff --git a/wasm-runtime/polkadot/src/codec/slicable.rs b/wasm-runtime/polkadot/src/codec/slicable.rs new file mode 100644 index 0000000000000..5ec042ec9bf5a --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/slicable.rs @@ -0,0 +1,88 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Serialisation. + +use runtime_support::{Vec, size_of, transmute, uninitialized, slice}; +use joiner::Joiner; +use endiansensitive::EndianSensitive; + +/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. +pub trait Slicable: Sized { + fn from_slice(value: &[u8]) -> Option { + Self::set_as_slice(|out| if value.len() == out.len() { + out.copy_from_slice(&value); + true + } else { + false + }) + } + fn to_vec(&self) -> Vec { + self.as_slice_then(|s| s.to_vec()) + } + fn set_as_slice bool>(set_slice: F) -> Option; + fn as_slice_then R>(&self, f: F) -> R { + f(&self.to_vec()) + } + fn size_of(_value: &[u8]) -> Option; +} + +/// Trait to mark that a type is not trivially (essentially "in place") serialisable. +pub trait NonTrivialSlicable: Slicable {} + +impl Slicable for T { + fn set_as_slice bool>(fill_slice: F) -> Option { + let size = size_of::(); + let mut result: T = unsafe { uninitialized() }; + let result_slice = unsafe { + slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size) + }; + if fill_slice(result_slice) { + Some(result.from_le()) + } else { + None + } + } + fn as_slice_then R>(&self, f: F) -> R { + let size = size_of::(); + self.as_le_then(|le| { + let value_slice = unsafe { + slice::from_raw_parts(transmute::<*const Self, *const u8>(le), size) + }; + f(value_slice) + }) + } + fn size_of(_value: &[u8]) -> Option { + Some(size_of::()) + } +} + +impl Slicable for Vec { + fn from_slice(value: &[u8]) -> Option { + Some(value[4..].to_vec()) + } + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + fn to_vec(&self) -> Vec { + let mut r: Vec = Vec::new().join(&(self.len() as u32)); + r.extend_from_slice(&self); + r + } + fn size_of(data: &[u8]) -> Option { + u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) + } +} diff --git a/wasm-runtime/polkadot/src/codec/streamreader.rs b/wasm-runtime/polkadot/src/codec/streamreader.rs new file mode 100644 index 0000000000000..371ceed4eeac4 --- /dev/null +++ b/wasm-runtime/polkadot/src/codec/streamreader.rs @@ -0,0 +1,74 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Deserialiser. + +use slicable::Slicable; + +/// Simple deserialiser. +pub struct StreamReader<'a> { + data: &'a[u8], + offset: usize, +} + +impl<'a> StreamReader<'a> { + /// Create a new deserialiser based on the `data`. + pub fn new(data: &'a[u8]) -> Self { + StreamReader { + data: data, + offset: 0, + } + } + + /// Deserialise a single item from the data stream. + pub fn read(&mut self) -> Option { + let size = T::size_of(&self.data[self.offset..])?; + let new_offset = self.offset + size; + let slice = &self.data[self.offset..new_offset]; + self.offset = new_offset; + Slicable::from_slice(slice) + } +} +/* +// Not in use yet +// TODO: introduce fn size_will_be(&self) -> usize; to Slicable trait and implement +struct StreamWriter<'a> { + data: &'a mut[u8], + offset: usize, +} + +impl<'a> StreamWriter<'a> { + pub fn new(data: &'a mut[u8]) -> Self { + StreamWriter { + data: data, + offset: 0, + } + } + pub fn write(&mut self, value: &T) -> bool { + value.as_slice_then(|s| { + let new_offset = self.offset + s.len(); + if self.data.len() <= new_offset { + let slice = &mut self.data[self.offset..new_offset]; + self.offset = new_offset; + slice.copy_from_slice(s); + true + } else { + false + } + }) + } +} +*/ diff --git a/wasm-runtime/polkadot/src/lib.rs b/wasm-runtime/polkadot/src/lib.rs new file mode 100644 index 0000000000000..7a96b6cee4437 --- /dev/null +++ b/wasm-runtime/polkadot/src/lib.rs @@ -0,0 +1,54 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! The Polkadot runtime. This can be compiled with #[no_std], ready for Wasm. + +#![cfg_attr(feature = "without-std", no_std)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#[macro_use] +extern crate runtime_support; + +#[cfg(test)] +extern crate rustc_hex; + +mod codec; +#[macro_use] +mod support; +mod runtime; +pub use codec::{endiansensitive, streamreader, joiner, slicable, keyedvec}; +pub use support::{primitives, function, environment, storable}; +#[cfg(test)] +pub use support::{testing, statichex}; + +use runtime_support::Vec; +use slicable::Slicable; +use primitives::{Block, UncheckedTransaction}; + +/// Execute a block, with `input` being the canonical serialisation of the block. Returns the +/// empty vector. +pub fn execute_block(input: Vec) -> Vec { + runtime::system::execute_block(Block::from_slice(&input).unwrap()); + Vec::new() +} + +/// Execute a given, serialised, transaction. Returns the empty vector. +pub fn execute_transaction(input: Vec) -> Vec { + runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap()); + Vec::new() +} + +impl_stubs!(execute_block, execute_transaction); diff --git a/wasm-runtime/polkadot/src/runtime/consensus.rs b/wasm-runtime/polkadot/src/runtime/consensus.rs new file mode 100644 index 0000000000000..1e12135806f22 --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/consensus.rs @@ -0,0 +1,44 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Conensus module for runtime; manages the authority set ready for the native code. + +use runtime_support::Vec; +use storable::StorageVec; +use primitives::SessionKey; + +struct AuthorityStorageVec {} +impl StorageVec for AuthorityStorageVec { + type Item = SessionKey; + const PREFIX: &'static[u8] = b"con:aut:"; +} + +/// Get the current set of authorities. These are the session keys. +pub fn authorities() -> Vec { + AuthorityStorageVec::items() +} + +/// Set the current set of authorities' session keys. +/// +/// Called by `next_session` only. +pub fn set_authorities(authorities: &[SessionKey]) { + AuthorityStorageVec::set_items(authorities); +} + +/// Set a single authority by index. +pub fn set_authority(index: u32, key: &SessionKey) { + AuthorityStorageVec::set_item(index, key); +} diff --git a/wasm-runtime/polkadot/src/runtime/mod.rs b/wasm-runtime/polkadot/src/runtime/mod.rs new file mode 100644 index 0000000000000..f83922c42fe45 --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/mod.rs @@ -0,0 +1,30 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! The Polkadot runtime. + +#[allow(unused)] +pub mod system; +#[allow(unused)] +pub mod consensus; +#[allow(unused)] +pub mod staking; +#[allow(unused)] +pub mod timestamp; +#[allow(unused)] +pub mod session; + +// TODO: governance, polkadao diff --git a/wasm-runtime/polkadot/src/runtime/session.rs b/wasm-runtime/polkadot/src/runtime/session.rs new file mode 100644 index 0000000000000..907b3e16b162e --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/session.rs @@ -0,0 +1,229 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Session manager: is told the validators and allows them to manage their session keys for the +//! consensus module. + +use runtime_support::Vec; +use keyedvec::KeyedVec; +use storable::{kill, Storable, StorageVec}; +use primitives::{AccountID, SessionKey, BlockNumber}; +use runtime::{system, staking, consensus}; + +struct ValidatorStorageVec {} +impl StorageVec for ValidatorStorageVec { + type Item = AccountID; + const PREFIX: &'static[u8] = b"ses:val:"; +} + +// TRANSACTION API (available to all transactors) + +/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next +/// session. +pub fn set_key(validator: &AccountID, key: &SessionKey) { + // set new value for next session + key.store(&validator.to_keyed_vec(b"ses:nxt:")); +} + +// PUBLIC API (available to other runtime modules) + +/// Get the current set of authorities. These are the session keys. +pub fn validators() -> Vec { + ValidatorStorageVec::items() +} + +/// Set the current set of validators. +/// +/// Called by staking::next_era() only. `next_session` should be called after this in order to +/// update the session keys to the next validator set. +pub fn set_validators(new: &[AccountID]) { + ValidatorStorageVec::set_items(new); + consensus::set_authorities(new); +} + +/// The number of blocks in each session. +pub fn length() -> BlockNumber { + Storable::lookup_default(b"ses:len") +} + +/// The current era index. +pub fn current_index() -> BlockNumber { + Storable::lookup_default(b"ses:ind") +} + +/// Set the current era index. +pub fn set_current_index(new: BlockNumber) { + new.store(b"ses:ind"); +} + +/// The block number at which the era length last changed. +pub fn last_length_change() -> BlockNumber { + Storable::lookup_default(b"ses:llc") +} + +/// Set a new era length. Won't kick in until the next era change (at current length). +pub fn set_length(new: BlockNumber) { + new.store(b"ses:nln"); +} + +/// Hook to be called after transaction processing. +pub fn check_rotate_session() { + // do this last, after the staking system has had chance to switch out the authorities for the + // new set. + // check block number and call next_session if necessary. + if (system::block_number() - last_length_change()) % length() == 0 { + rotate_session(); + } +} + +// PRIVATE (not available for use externally) + +/// Move onto next session: register the new authority set. +fn rotate_session() { + // Increment current session index. + set_current_index(current_index() + 1); + + // Enact era length change. + if let Some(next_len) = u64::lookup(b"ses:nln") { + next_len.store(b"ses:len"); + system::block_number().store(b"ses:llc"); + kill(b"ses:nln"); + } + + // Update any changes in session keys. + validators().iter().enumerate().for_each(|(i, v)| { + let k = v.to_keyed_vec(b"ses:nxt:"); + if let Some(n) = Storable::lookup(&k) { + consensus::set_authority(i as u32, &n); + kill(&k); + } + }); +} + +#[cfg(test)] +mod tests { + use runtime_support::{with_externalities, twox_128}; + use keyedvec::KeyedVec; + use joiner::Joiner; + use testing::{one, two, TestExternalities}; + use primitives::AccountID; + use runtime::{consensus, session}; + use environment::with_env; + + fn simple_setup() -> TestExternalities { + TestExternalities { storage: map![ + twox_128(b"ses:len").to_vec() => vec![].join(&2u64), + // the validators (10, 20, ...) + twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), + twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32], + twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32], + // initial session keys (11, 21, ...) + twox_128(b"con:aut:len").to_vec() => vec![].join(&2u32), + twox_128(&0u32.to_keyed_vec(b"con:aut:")).to_vec() => vec![11; 32], + twox_128(&1u32.to_keyed_vec(b"con:aut:")).to_vec() => vec![21; 32] + ], } + } + + #[test] + fn simple_setup_should_work() { + let mut t = simple_setup(); + with_externalities(&mut t, || { + assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); + assert_eq!(session::length(), 2u64); + assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); + }); + } + + #[test] + fn session_length_change_should_work() { + let mut t = simple_setup(); + with_externalities(&mut t, || { + // Block 1: Change to length 3; no visible change. + with_env(|e| e.block_number = 1); + session::set_length(3); + session::check_rotate_session(); + assert_eq!(session::length(), 2); + assert_eq!(session::current_index(), 0); + + // Block 2: Length now changed to 3. Index incremented. + with_env(|e| e.block_number = 2); + session::set_length(3); + session::check_rotate_session(); + assert_eq!(session::length(), 3); + assert_eq!(session::current_index(), 1); + + // Block 3: Length now changed to 3. Index incremented. + with_env(|e| e.block_number = 3); + session::check_rotate_session(); + assert_eq!(session::length(), 3); + assert_eq!(session::current_index(), 1); + + // Block 4: Change to length 2; no visible change. + with_env(|e| e.block_number = 4); + session::set_length(2); + session::check_rotate_session(); + assert_eq!(session::length(), 3); + assert_eq!(session::current_index(), 1); + + // Block 5: Length now changed to 2. Index incremented. + with_env(|e| e.block_number = 5); + session::check_rotate_session(); + assert_eq!(session::length(), 2); + assert_eq!(session::current_index(), 2); + + // Block 6: No change. + with_env(|e| e.block_number = 6); + session::check_rotate_session(); + assert_eq!(session::length(), 2); + assert_eq!(session::current_index(), 2); + + // Block 7: Next index. + with_env(|e| e.block_number = 7); + session::check_rotate_session(); + assert_eq!(session::length(), 2); + assert_eq!(session::current_index(), 3); + }); + } + + #[test] + fn session_change_should_work() { + let mut t = simple_setup(); + with_externalities(&mut t, || { + // Block 1: No change + with_env(|e| e.block_number = 1); + session::check_rotate_session(); + assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); + + // Block 2: Session rollover, but no change. + with_env(|e| e.block_number = 2); + session::check_rotate_session(); + assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); + + // Block 3: Set new key for validator 2; no visible change. + with_env(|e| e.block_number = 3); + session::set_key(&[20; 32], &[22; 32]); + assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); + + session::check_rotate_session(); + assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); + + // Block 4: Session rollover, authority 2 changes. + with_env(|e| e.block_number = 4); + session::check_rotate_session(); + assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]); + }); + } +} diff --git a/wasm-runtime/polkadot/src/runtime/staking.rs b/wasm-runtime/polkadot/src/runtime/staking.rs new file mode 100644 index 0000000000000..fc7c12798e3ed --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/staking.rs @@ -0,0 +1,363 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Staking manager: Handles balances and periodically determines the best set of validators. + +use runtime_support::Vec; +use keyedvec::KeyedVec; +use storable::{Storable, StorageVec}; +use primitives::{BlockNumber, AccountID}; +use runtime::{system, session}; + +/// The balance of an account. +pub type Balance = u64; + +/// The amount of bonding period left in an account. Measured in eras. +pub type Bondage = u64; + +struct IntentionStorageVec {} +impl StorageVec for IntentionStorageVec { + type Item = AccountID; + const PREFIX: &'static[u8] = b"ses:wil:"; +} + +// Each identity's stake may be in one of three bondage states, given by an integer: +// - n | n <= current_era(): inactive: free to be transferred. +// - ~0: active: currently representing a validator. +// - n | n > current_era(): deactivating: recently representing a validator and not yet +// ready for transfer. + +/// The length of the bonding duration in eras. +pub fn bonding_duration() -> BlockNumber { + Storable::lookup_default(b"sta:loc") +} + +/// The length of a staking era in sessions. +pub fn validator_count() -> usize { + u32::lookup_default(b"sta:vac") as usize +} + +/// The length of a staking era in blocks. +pub fn era_length() -> BlockNumber { + sessions_per_era() * session::length() +} + +/// The length of a staking era in sessions. +pub fn sessions_per_era() -> BlockNumber { + Storable::lookup_default(b"sta:spe") +} + +/// The current era index. +pub fn current_era() -> BlockNumber { + Storable::lookup_default(b"sta:era") +} + +/// The block number at which the era length last changed. +pub fn last_era_length_change() -> BlockNumber { + Storable::lookup_default(b"sta:lec") +} + +/// The balance of a given account. +pub fn balance(who: &AccountID) -> Balance { + Storable::lookup_default(&who.to_keyed_vec(b"sta:bal:")) +} + +/// The liquidity-state of a given account. +pub fn bondage(who: &AccountID) -> Bondage { + Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:")) +} + +/// Transfer some unlocked staking balance to another staker. +pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { + let from_key = transactor.to_keyed_vec(b"sta:bal:"); + let from_balance = Balance::lookup_default(&from_key); + assert!(from_balance >= value); + let to_key = dest.to_keyed_vec(b"sta:bal:"); + let to_balance: Balance = Storable::lookup_default(&to_key); + assert!(bondage(transactor) <= bondage(dest)); + assert!(to_balance + value > to_balance); // no overflow + (from_balance - value).store(&from_key); + (to_balance + value).store(&to_key); +} + +/// Declare the desire to stake for the transactor. +/// +/// Effects will be felt at the beginning of the next era. +pub fn stake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + // can't be in the list twice. + assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); + intentions.push(transactor.clone()); + IntentionStorageVec::set_items(&intentions); + u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:")); +} + +/// Retract the desire to stake for the transactor. +/// +/// Effects will be felt at the beginning of the next era. +pub fn unstake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + if let Some(position) = intentions.iter().position(|t| t == transactor) { + intentions.swap_remove(position); + } else { + panic!("Cannot unstake if not already staked."); + } + IntentionStorageVec::set_items(&intentions); + (current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:")); +} + +/// Hook to be called after to transaction processing. +pub fn check_new_era() { + // check block number and call new_era if necessary. + if (system::block_number() - last_era_length_change()) % era_length() == 0 { + new_era(); + } +} + +// PRIVATE + +/// The era has changed - enact new staking set. +/// +/// NOTE: This always happens on a session change. +fn new_era() { + // Increment current era. + (current_era() + 1).store(b"sta:era"); + + // Enact era length change. + let next_spe: u64 = Storable::lookup_default(b"sta:nse"); + if next_spe > 0 && next_spe != sessions_per_era() { + next_spe.store(b"sta:spe"); + system::block_number().store(b"sta:lec"); + } + + // TODO: evaluate desired staking amounts and nominations and optimise to find the best + // combination of validators, then use session::set_validators(). + + // for now, this just orders would-be stakers by their balances and chooses the top-most + // validator_count() of them. + let mut intentions = IntentionStorageVec::items() + .into_iter() + .map(|v| (balance(&v), v)) + .collect::>(); + intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1)); + session::set_validators( + &intentions.into_iter() + .map(|(_, v)| v) + .take(validator_count()) + .collect::>() + ); +} + +/// Set a new era length. Won't kick in until the next era change (at current length). +fn set_sessions_per_era(new: BlockNumber) { + new.store(b"sta:nse"); +} + +#[cfg(test)] +mod tests { + use runtime_support::{with_externalities, twox_128}; + use keyedvec::KeyedVec; + use joiner::Joiner; + use testing::{one, two, TestExternalities}; + use primitives::AccountID; + use runtime::{staking, session}; + use environment::with_env; + + #[test] + fn staking_should_work() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let four = [4u8; 32]; + + let mut t = TestExternalities { storage: map![ + twox_128(b"ses:len").to_vec() => vec![].join(&1u64), + twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), + twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32], + twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32], + twox_128(b"sta:spe").to_vec() => vec![].join(&2u64), + twox_128(b"sta:vac").to_vec() => vec![].join(&2u32), + twox_128(b"sta:loc").to_vec() => vec![].join(&3u64), + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&10u64), + twox_128(&two.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&20u64), + twox_128(&three.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&30u64), + twox_128(&four.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&40u64) + ], }; + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 2u64); + assert_eq!(staking::validator_count(), 2usize); + assert_eq!(staking::bonding_duration(), 3u64); + assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); + + // Block 1: Add three validators. No obvious change. + with_env(|e| e.block_number = 1); + staking::stake(&one); + staking::stake(&two); + staking::stake(&four); + staking::check_new_era(); + assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); + + // Block 2: New validator set now. + with_env(|e| e.block_number = 2); + staking::check_new_era(); + assert_eq!(session::validators(), vec![four.clone(), two.clone()]); + + // Block 3: Unstake highest, introduce another staker. No change yet. + with_env(|e| e.block_number = 3); + staking::stake(&three); + staking::unstake(&four); + staking::check_new_era(); + + // Block 4: New era - validators change. + with_env(|e| e.block_number = 4); + staking::check_new_era(); + assert_eq!(session::validators(), vec![three.clone(), two.clone()]); + + // Block 5: Transfer stake from highest to lowest. No change yet. + with_env(|e| e.block_number = 5); + staking::transfer(&four, &one, 40); + staking::check_new_era(); + + // Block 6: Lowest now validator. + with_env(|e| e.block_number = 6); + staking::check_new_era(); + assert_eq!(session::validators(), vec![one.clone(), three.clone()]); + + // Block 7: Unstake three. No change yet. + with_env(|e| e.block_number = 7); + staking::unstake(&three); + staking::check_new_era(); + assert_eq!(session::validators(), vec![one.clone(), three.clone()]); + + // Block 8: Back to one and two. + with_env(|e| e.block_number = 8); + staking::check_new_era(); + assert_eq!(session::validators(), vec![one.clone(), two.clone()]); + }); + } + + #[test] + fn staking_eras_work() { + let mut t = TestExternalities { storage: map![ + twox_128(b"ses:len").to_vec() => vec![].join(&1u64), + twox_128(b"sta:spe").to_vec() => vec![].join(&2u64) + ], }; + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 2u64); + assert_eq!(staking::sessions_per_era(), 2u64); + assert_eq!(staking::last_era_length_change(), 0u64); + assert_eq!(staking::current_era(), 0u64); + + // Block 1: No change. + with_env(|e| e.block_number = 1); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 2u64); + assert_eq!(staking::last_era_length_change(), 0u64); + assert_eq!(staking::current_era(), 0u64); + + // Block 2: Simple era change. + with_env(|e| e.block_number = 2); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 2u64); + assert_eq!(staking::last_era_length_change(), 0u64); + assert_eq!(staking::current_era(), 1u64); + + // Block 3: Schedule an era length change; no visible changes. + with_env(|e| e.block_number = 3); + staking::set_sessions_per_era(3); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 2u64); + assert_eq!(staking::last_era_length_change(), 0u64); + assert_eq!(staking::current_era(), 1u64); + + // Block 4: Era change kicks in. + with_env(|e| e.block_number = 4); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 3u64); + assert_eq!(staking::last_era_length_change(), 4u64); + assert_eq!(staking::current_era(), 2u64); + + // Block 5: No change. + with_env(|e| e.block_number = 5); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 3u64); + assert_eq!(staking::last_era_length_change(), 4u64); + assert_eq!(staking::current_era(), 2u64); + + // Block 6: No change. + with_env(|e| e.block_number = 6); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 3u64); + assert_eq!(staking::last_era_length_change(), 4u64); + assert_eq!(staking::current_era(), 2u64); + + // Block 7: Era increment. + with_env(|e| e.block_number = 7); + staking::check_new_era(); + assert_eq!(staking::sessions_per_era(), 3u64); + assert_eq!(staking::last_era_length_change(), 4u64); + assert_eq!(staking::current_era(), 3u64); + }); + } + + #[test] + fn staking_balance_works() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&42u64) + ], }; + + with_externalities(&mut t, || { + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 0); + }); + } + + #[test] + fn staking_balance_transfer_works() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64) + ], }; + + with_externalities(&mut t, || { + staking::transfer(&one, &two, 69); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } + + #[test] + #[should_panic] + fn staking_balance_transfer_when_bonded_doesnt_work() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64) + ], }; + + with_externalities(&mut t, || { + staking::stake(&one); + staking::transfer(&one, &two, 69); + }); + } +} diff --git a/wasm-runtime/polkadot/src/runtime/system.rs b/wasm-runtime/polkadot/src/runtime/system.rs new file mode 100644 index 0000000000000..5ffa03ed818a7 --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/system.rs @@ -0,0 +1,154 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code +//! and depositing logs. + +use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder, Hashable}; +use runtime_support::{Vec, swap}; +use storable::Storable; +use keyedvec::KeyedVec; +use environment::with_env; +use runtime::{staking, session}; + +/// The current block number being processed. Set by `execute_block`. +pub fn block_number() -> BlockNumber { + with_env(|e| e.block_number) +} + +/// Get the block hash of a given block (uses storage). +pub fn block_hash(number: BlockNumber) -> Hash { + Storable::lookup_default(&number.to_keyed_vec(b"sys:old:")) +} + +/// Deposits a log and ensures it matches the blocks log data. +pub fn deposit_log(log: &[u8]) { + with_env(|e| { + assert_eq!(log, &e.digest.logs[e.next_log_index][..]); + e.next_log_index += 1; + }); +} + +pub fn execute_block(mut block: Block) { + // populate environment from header. + with_env(|e| { + e.block_number = block.header.number; + swap(&mut e.digest, &mut block.header.digest); + e.next_log_index = 0; + }); + + let ref header = block.header; + + // check parent_hash is correct. + assert!( + header.number > 0 && block_hash(header.number - 1) == header.parent_hash, + "Parent hash should be valid." + ); + + // TODO: check transaction trie root represents the transactions. + // this requires non-trivial changes to the externals API or compiling trie rooting into wasm + // so will wait until a little later. + + // store the header hash in storage. + let header_hash_key = header.number.to_keyed_vec(b"sys:old:"); + header.blake2_256().store(&header_hash_key); + + // execute transactions + block.transactions.iter().for_each(execute_transaction); + + staking::check_new_era(); + session::check_rotate_session(); + + // any final checks + final_checks(&block); + + // TODO: check storage root. + // this requires non-trivial changes to the externals API or compiling trie rooting into wasm + // so will wait until a little later. +} + +/// Execute a given transaction. +pub fn execute_transaction(utx: &UncheckedTransaction) { + // Verify the signature is good. + assert!(utx.ed25519_verify(), "All transactions should be properly signed"); + + let ref tx = utx.transaction; + + // check nonce + let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); + let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key); + assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); + + // increment nonce in storage + (expected_nonce + 1).store(&nonce_key); + + // decode parameters and dispatch + tx.function.dispatch(&tx.signed, &tx.input_data); +} + +/// Set the new code. +pub fn set_code(new: &[u8]) { + new.store(b":code"); +} + +fn final_checks(_block: &Block) { + with_env(|e| { + assert_eq!(e.next_log_index, e.digest.logs.len()); + }); +} + +#[cfg(test)] +mod tests { + use joiner::Joiner; + use function::Function; + use keyedvec::KeyedVec; + use slicable::Slicable; + use runtime_support::{with_externalities, twox_128}; + use primitives::{UncheckedTransaction, Transaction}; + use statichex::StaticHexInto; + use runtime::{system, staking}; + use testing::{TestExternalities, HexDisplay, one, two}; + + #[test] + fn staking_balance_transfer_dispatch_works() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let tx = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 0, + function: Function::StakingTransfer, + input_data: vec![].join(&two).join(&69u64), + }, + signature: "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a".convert(), + }; + // tx: 2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000 + // sig: 679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a + + println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec())); + + with_externalities(&mut t, || { + system::execute_transaction(&tx); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } +} diff --git a/wasm-runtime/polkadot/src/runtime/timestamp.rs b/wasm-runtime/polkadot/src/runtime/timestamp.rs new file mode 100644 index 0000000000000..7040473b1a426 --- /dev/null +++ b/wasm-runtime/polkadot/src/runtime/timestamp.rs @@ -0,0 +1,54 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Timestamp manager: just handles the current timestamp. + +use storable::Storable; + +/// Representation of a time. +pub type Timestamp = u64; + +/// Get the current time. +pub fn get() -> Timestamp { + Storable::lookup_default(b"tim:val") +} + +/// Set the current time. +pub fn set(now: Timestamp) { + now.store(b"tim:val") +} + +#[cfg(test)] +mod tests { + use joiner::Joiner; + use keyedvec::KeyedVec; + use runtime_support::{with_externalities, twox_128}; + use runtime::timestamp; + use testing::TestExternalities; + + #[test] + fn timestamp_works() { + let mut t = TestExternalities { storage: map![ + twox_128(b"tim:val").to_vec() => vec![].join(&42u64) + ], }; + + with_externalities(&mut t, || { + assert_eq!(timestamp::get(), 42); + timestamp::set(69); + assert_eq!(timestamp::get(), 69); + }); + } +} diff --git a/wasm-runtime/polkadot/src/support/environment.rs b/wasm-runtime/polkadot/src/support/environment.rs new file mode 100644 index 0000000000000..c01a1f4e92823 --- /dev/null +++ b/wasm-runtime/polkadot/src/support/environment.rs @@ -0,0 +1,78 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Environment API: Allows certain information to be accessed throughout the runtime. + +use runtime_support::{Rc, RefCell, transmute, Box}; +use primitives::{BlockNumber, Digest}; + +#[derive(Default)] +/// The information that can be accessed globally. +pub struct Environment { + /// The current block number. + pub block_number: BlockNumber, + /// The current block digest. + pub digest: Digest, + /// The number of log items in this block that have been accounted for so far. + pub next_log_index: usize, +} + +/// Do something with the environment and return its value. Keep the function short. +pub fn with_env T>(f: F) -> T { + let e = env(); + let mut eb = e.borrow_mut(); + f(&mut *eb) +} + +#[cfg(not(test))] +fn env() -> Rc> { + // Initialize it to a null value + static mut SINGLETON: *const Rc> = 0 as *const Rc>; + + unsafe { + if SINGLETON == 0 as *const Rc> { + // Make it + let singleton: Rc> = Rc::new(RefCell::new(Default::default())); + + // Put it in the heap so it can outlive this call + SINGLETON = transmute(Box::new(singleton)); + } + + // Now we give out a copy of the data that is safe to use concurrently. + (*SINGLETON).clone() + } +} + +#[cfg(test)] +fn env() -> Rc> { + // Initialize it to a null value + thread_local!{ + static SINGLETON: RefCell<*const Rc>> = RefCell::new(0 as *const Rc>); + } + + SINGLETON.with(|s| unsafe { + if *s.borrow() == 0 as *const Rc> { + // Make it + let singleton: Rc> = Rc::new(RefCell::new(Default::default())); + + // Put it in the heap so it can outlive this call + *s.borrow_mut() = transmute(Box::new(singleton)); + } + + // Now we give out a copy of the data that is safe to use concurrently. + (**s.borrow()).clone() + }) +} diff --git a/wasm-runtime/polkadot/src/support/function.rs b/wasm-runtime/polkadot/src/support/function.rs new file mode 100644 index 0000000000000..f4ee9c79e6e4d --- /dev/null +++ b/wasm-runtime/polkadot/src/support/function.rs @@ -0,0 +1,74 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Function data: This describes a function that can be called from an external transaction. + +use primitives::AccountID; +use streamreader::StreamReader; +use runtime::{staking, session, timestamp}; + +/// The functions that a transaction can call (and be dispatched to). +#[cfg_attr(test, derive(PartialEq, Debug))] +#[derive(Clone, Copy)] +pub enum Function { + StakingStake, + StakingUnstake, + StakingTransfer, + SessionSetKey, + TimestampSet, +} + +impl Function { + /// Derive `Some` value from a `u8`, or `None` if it's invalid. + pub fn from_u8(value: u8) -> Option { + match value { + x if x == Function::StakingStake as u8 => Some(Function::StakingStake), + x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake), + x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer), + x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey), + x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet), + _ => None, + } + } +} + +impl Function { + /// Dispatch the function. + pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) { + let mut params = StreamReader::new(data); + match *self { + Function::StakingStake => { + staking::stake(transactor); + } + Function::StakingUnstake => { + staking::unstake(transactor); + } + Function::StakingTransfer => { + let dest = params.read().unwrap(); + let value = params.read().unwrap(); + staking::transfer(transactor, &dest, value); + } + Function::SessionSetKey => { + let session = params.read().unwrap(); + session::set_key(transactor, &session); + } + Function::TimestampSet => { + let t = params.read().unwrap(); + timestamp::set(t); + } + } + } +} diff --git a/wasm-runtime/polkadot/src/support/mod.rs b/wasm-runtime/polkadot/src/support/mod.rs new file mode 100644 index 0000000000000..56ad3a355c5b2 --- /dev/null +++ b/wasm-runtime/polkadot/src/support/mod.rs @@ -0,0 +1,28 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Support code for the runtime. + +pub mod primitives; +pub mod function; +pub mod environment; +pub mod storable; + +#[cfg(test)] +pub mod statichex; +#[cfg(test)] +#[macro_use] +pub mod testing; diff --git a/wasm-runtime/polkadot/src/support/primitives.rs b/wasm-runtime/polkadot/src/support/primitives.rs new file mode 100644 index 0000000000000..3ab385d488933 --- /dev/null +++ b/wasm-runtime/polkadot/src/support/primitives.rs @@ -0,0 +1,515 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Primitive types. + +use runtime_support::Vec; +use streamreader::StreamReader; +use joiner::Joiner; +use slicable::{Slicable, NonTrivialSlicable}; +use function::Function; +use runtime_support::{size_of, blake2_256, twox_128, twox_256, ed25519_verify}; + +#[cfg(test)] +use std::fmt; + +/// The Ed25519 pubkey that identifies an account. +pub type AccountID = [u8; 32]; +/// The Ed25519 pub key of an session that belongs to an authority. This is used as what the +/// external environment/consensus algorithm calls an "authority". +pub type SessionKey = AccountID; +/// Indentifier for a chain. +pub type ChainID = u64; +/// Index of a block in the chain. +pub type BlockNumber = u64; +/// Index of a transaction. +pub type TxOrder = u64; +/// A hash of some data. +pub type Hash = [u8; 32]; + +#[derive(Clone, Default)] +#[cfg_attr(test, derive(PartialEq, Debug))] +/// The digest of a block, useful for light-clients. +pub struct Digest { + /// All logs that have happened in the block. + pub logs: Vec>, +} + +#[derive(Clone)] +#[cfg_attr(test, derive(PartialEq, Debug))] +/// The header for a block. +pub struct Header { + /// The parent block's "hash" (actually the Blake2-256 hash of its serialised header). + pub parent_hash: Hash, + /// The block's number (how many ancestors does it have?). + pub number: BlockNumber, + /// The root of the trie that represents this block's final storage map. + pub state_root: Hash, + /// The root of the trie that represents this block's transactions, indexed by a 32-bit integer. + pub transaction_root: Hash, + /// The digest for this block. + pub digest: Digest, +} + +impl Slicable for Header { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Header { + parent_hash: reader.read()?, + number: reader.read()?, + state_root: reader.read()?, + transaction_root: reader.read()?, + digest: Digest { logs: reader.read()?, }, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.parent_hash) + .join(&self.number) + .join(&self.state_root) + .join(&self.transaction_root) + .join(&self.digest.logs) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = size_of::() + size_of::() + size_of::() + size_of::(); + let second_part = >>::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Header {} + +#[cfg_attr(test, derive(PartialEq, Debug))] +/// A vetted and verified transaction from the external world. +pub struct Transaction { + /// Who signed it (note this is not a signature). + pub signed: AccountID, + /// The number of transactions have come before from the same signer. + pub nonce: TxOrder, + /// The function that should be called. + pub function: Function, + /// Serialised input data to the function. + pub input_data: Vec, +} + +impl Slicable for Transaction { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Transaction { + signed: reader.read()?, + nonce: reader.read()?, + function: Function::from_u8(reader.read()?)?, + input_data: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.signed) + .join(&self.nonce) + .join(&(self.function as u8)) + .join(&self.input_data) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = size_of::() + size_of::() + size_of::(); + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +pub trait Hashable: Sized { + fn blake2_256(&self) -> [u8; 32]; + fn twox_128(&self) -> [u8; 16]; + fn twox_256(&self) -> [u8; 32]; +} + +impl Hashable for T { + fn blake2_256(&self) -> [u8; 32] { + blake2_256(&self.to_vec()) + } + fn twox_128(&self) -> [u8; 16] { + twox_128(&self.to_vec()) + } + fn twox_256(&self) -> [u8; 32] { + twox_256(&self.to_vec()) + } +} + +impl NonTrivialSlicable for Transaction {} + +/// A transactions right from the external world. Unchecked. +pub struct UncheckedTransaction { + /// The actual transaction information. + pub transaction: Transaction, + /// The signature; should be an Ed25519 signature applied to the serialised `transaction` field. + pub signature: [u8; 64], +} + +impl UncheckedTransaction { + /// Verify the signature. + pub fn ed25519_verify(&self) -> bool { + let msg = self.transaction.to_vec(); + ed25519_verify(&self.signature, &msg, &self.transaction.signed) + } +} + +#[cfg(test)] +impl PartialEq for UncheckedTransaction { + fn eq(&self, other: &Self) -> bool { + self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction + } +} + +#[cfg(test)] +impl fmt::Debug for UncheckedTransaction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UncheckedTransaction({:?})", self.transaction) + } +} + +impl Slicable for UncheckedTransaction { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(UncheckedTransaction { + signature: reader.read()?, + transaction: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.signature) + .join(&self.transaction) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = size_of::<[u8; 64]>(); + let second_part = ::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for UncheckedTransaction {} + +#[cfg_attr(test, derive(PartialEq, Debug))] +/// A Polkadot relay chain block. +pub struct Block { + /// The header of the block. + pub header: Header, + /// All transactions. + pub transactions: Vec, +} + +impl Slicable for Block { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Block { + header: reader.read()?, + transactions: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.header) + .join(&self.transactions) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = Header::size_of(data)?; + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Block {} + +impl NonTrivialSlicable for Vec where Vec: Slicable {} + +impl Slicable for Vec { + fn from_slice(value: &[u8]) -> Option { + let len = Self::size_of(&value[0..4])?; + let mut off = 4; + let mut r = Vec::new(); + while off < len { + let element_len = T::size_of(&value[off..])?; + r.push(T::from_slice(&value[off..off + element_len])?); + off += element_len; + } + Some(r) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + let vecs = self.iter().map(Slicable::to_vec).collect::>(); + let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a}); + let mut r = Vec::new().join(&(len as u32)); + vecs.iter().for_each(|v| r.extend_from_slice(v)); + r + } + + fn size_of(data: &[u8]) -> Option { + u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use joiner::Joiner; + use function::Function; + + #[test] + fn serialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }; + let serialised = tx.to_vec(); + assert_eq!(serialised, vec![ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]); + } + + #[test] + fn deserialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }; + let data = [ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]; + let deserialised = Transaction::from_slice(&data).unwrap(); + assert_eq!(deserialised, tx); + } + + #[test] + fn serialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let serialised = h.to_vec(); + assert_eq!(serialised, vec![ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]); + } + + #[test] + fn deserialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let data = [ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]; + let deserialised = Header::from_slice(&data).unwrap(); + assert_eq!(deserialised, h); + } + + #[test] + fn serialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }, + signature: [1u8; 64], + }; + let tx2 = UncheckedTransaction { + transaction: Transaction { + signed: two.clone(), + nonce: 42, + function: Function::StakingStake, + input_data: Vec::new(), + }, + signature: [2u8; 64], + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let serialised = b.to_vec(); + assert_eq!(serialised, vec![ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 2, 1, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0 + ]); + } + + #[test] + fn deserialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }, + signature: [1u8; 64], + }; + let tx2 = UncheckedTransaction { + transaction: Transaction { + signed: two.clone(), + nonce: 42, + function: Function::StakingStake, + input_data: Vec::new(), + }, + signature: [2u8; 64], + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let data = [ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 2, 1, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0 + ]; + let deserialised = Block::from_slice(&data).unwrap(); + assert_eq!(deserialised, b); + } +} diff --git a/wasm-runtime/polkadot/src/support/statichex.rs b/wasm-runtime/polkadot/src/support/statichex.rs new file mode 100644 index 0000000000000..152ceadc0b444 --- /dev/null +++ b/wasm-runtime/polkadot/src/support/statichex.rs @@ -0,0 +1,52 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Interpret a static string of hex as a desired type. + +use rustc_hex::FromHex; + +/// Trait to allow conversion from a static hex string to an instance. +pub trait StaticHexConversion: Sized { + /// Convert the static str into Self. Use just like `From::from`. + fn from_static_hex(hex: &'static str) -> Self; +} + +macro_rules! impl_sizes { + ( $( $t:expr ),* ) => { $( + impl StaticHexConversion for [u8; $t] { + fn from_static_hex(hex: &'static str) -> Self { + let mut r = [0u8; $t]; + r.copy_from_slice(&FromHex::from_hex(hex).unwrap()); + r + } + } + )* } +} + +impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128); + +/// Trait to allow converting from itself (only implemented for a static str) into some useful +/// type (which must implement `StaticHexConversion`). +pub trait StaticHexInto { + /// Convert self (i.e. a static str) into the appropriate type. Use just like `Into::into`. + fn convert(self) -> T; +} + +impl StaticHexInto for &'static str { + fn convert(self) -> T { + T::from_static_hex(self) + } +} diff --git a/wasm-runtime/polkadot/src/support/storable.rs b/wasm-runtime/polkadot/src/support/storable.rs new file mode 100644 index 0000000000000..3caf3ed19bfae --- /dev/null +++ b/wasm-runtime/polkadot/src/support/storable.rs @@ -0,0 +1,87 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Stuff to do with the runtime's storage. + +use slicable::Slicable; +use endiansensitive::EndianSensitive; +use keyedvec::KeyedVec; +use runtime_support::{self, twox_128, Vec}; + +/// Trait for a value which may be stored in the storage DB. +pub trait Storable { + /// Lookup the value in storage and deserialise, giving a default value if not found. + fn lookup_default(key: &[u8]) -> Self where Self: Sized + Default { Self::lookup(key).unwrap_or_else(Default::default) } + /// Lookup `Some` value in storage and deserialise; `None` if it's not there. + fn lookup(_key: &[u8]) -> Option where Self: Sized { unimplemented!() } + /// Place the value in storage under `key`. + fn store(&self, key: &[u8]); +} + +// TODO: consider using blake256 to avoid possible eclipse attack. + +/// Remove `key` from storage. +pub fn kill(key: &[u8]) { runtime_support::set_storage(&twox_128(key)[..], b""); } + +impl Storable for T { + fn lookup(key: &[u8]) -> Option { + Slicable::set_as_slice(|out| runtime_support::read_storage(&twox_128(key)[..], out) == out.len()) + } + fn store(&self, key: &[u8]) { + self.as_slice_then(|slice| runtime_support::set_storage(&twox_128(key)[..], slice)); + } +} + +impl Storable for [u8] { + fn store(&self, key: &[u8]) { + runtime_support::set_storage(&twox_128(key)[..], self) + } +} + +/// A trait to conveniently store a vector of storable data. +// TODO: add iterator support +pub trait StorageVec { + type Item: Default + Sized + Storable; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: &[Self::Item]) { + Self::set_count(items.len() as u32); + items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i)); + } + + fn set_item(index: u32, item: &Self::Item) { + item.store(&index.to_keyed_vec(Self::PREFIX)); + } + + fn item(index: u32) -> Self::Item { + Storable::lookup_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default())); + count.store(&b"len".to_keyed_vec(Self::PREFIX)); + } + + fn count() -> u32 { + Storable::lookup_default(&b"len".to_keyed_vec(Self::PREFIX)) + } +} diff --git a/wasm-runtime/polkadot/src/support/testing.rs b/wasm-runtime/polkadot/src/support/testing.rs new file mode 100644 index 0000000000000..e2e7b40562e51 --- /dev/null +++ b/wasm-runtime/polkadot/src/support/testing.rs @@ -0,0 +1,106 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Testing helpers. + +use runtime_support::{NoError, Externalities}; +use std::collections::HashMap; +use primitives::AccountID; +use statichex::StaticHexInto; + +#[derive(Debug, Default)] +/// Simple externaties implementation. +pub struct TestExternalities { + /// The storage map. + pub storage: HashMap, Vec>, +} + +impl Externalities for TestExternalities { + type Error = NoError; + + fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } + + fn set_storage(&mut self, key: Vec, value: Vec) { + self.storage.insert(key, value); + } + + fn chain_id(&self) -> u64 { 42 } +} + +#[macro_export] +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} + +/// One account (to which we know the secret key). +pub fn one() -> AccountID { + "2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".convert() +} +/// Another account (secret key known). +pub fn two() -> AccountID { + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".convert() +} + +/// Hex display, this time for no_std. See main codebase for documentation. +pub struct HexDisplay<'a>(&'a [u8]); + +impl<'a> HexDisplay<'a> { + /// See main codebase for documentation. + pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) } +} + +impl<'a> ::std::fmt::Display for HexDisplay<'a> { + fn fmt(&self, fmtr: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + for byte in self.0 { + try!( fmtr.write_fmt(format_args!("{:02x}", byte))); + } + Ok(()) + } +} + +/// See main codebase for documentation. +pub trait AsBytesRef { + /// See main codebase for documentation. + fn as_bytes_ref(&self) -> &[u8]; +} + +impl AsBytesRef for [u8] { + fn as_bytes_ref(&self) -> &[u8] { &self } +} + +impl<'a> AsBytesRef for &'a[u8] { + fn as_bytes_ref(&self) -> &[u8] { self } +} + +impl AsBytesRef for Vec { + fn as_bytes_ref(&self) -> &[u8] { &self[..] } +} + +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl AsBytesRef for $t { + fn as_bytes_ref(&self) -> &[u8] { &self[..] } + } + )* } +} + +impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); diff --git a/runtime/pwasm-alloc/Cargo.toml b/wasm-runtime/pwasm-alloc/Cargo.toml similarity index 100% rename from runtime/pwasm-alloc/Cargo.toml rename to wasm-runtime/pwasm-alloc/Cargo.toml diff --git a/runtime/pwasm-alloc/README.md b/wasm-runtime/pwasm-alloc/README.md similarity index 100% rename from runtime/pwasm-alloc/README.md rename to wasm-runtime/pwasm-alloc/README.md diff --git a/runtime/pwasm-alloc/src/lib.rs b/wasm-runtime/pwasm-alloc/src/lib.rs similarity index 100% rename from runtime/pwasm-alloc/src/lib.rs rename to wasm-runtime/pwasm-alloc/src/lib.rs diff --git a/runtime/pwasm-libc/Cargo.toml b/wasm-runtime/pwasm-libc/Cargo.toml similarity index 100% rename from runtime/pwasm-libc/Cargo.toml rename to wasm-runtime/pwasm-libc/Cargo.toml diff --git a/runtime/pwasm-libc/README.md b/wasm-runtime/pwasm-libc/README.md similarity index 100% rename from runtime/pwasm-libc/README.md rename to wasm-runtime/pwasm-libc/README.md diff --git a/runtime/pwasm-libc/src/lib.rs b/wasm-runtime/pwasm-libc/src/lib.rs similarity index 100% rename from runtime/pwasm-libc/src/lib.rs rename to wasm-runtime/pwasm-libc/src/lib.rs diff --git a/runtime/support/Cargo.toml b/wasm-runtime/support/Cargo.toml similarity index 100% rename from runtime/support/Cargo.toml rename to wasm-runtime/support/Cargo.toml diff --git a/wasm-runtime/support/src/lib.rs b/wasm-runtime/support/src/lib.rs new file mode 100644 index 0000000000000..b65b574351554 --- /dev/null +++ b/wasm-runtime/support/src/lib.rs @@ -0,0 +1,144 @@ +#![no_std] +#![feature(lang_items)] +#![feature(alloc)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +//#[macro_use] +extern crate alloc; +pub use alloc::vec::Vec; +pub use alloc::boxed::Box; +pub use alloc::rc::Rc; +pub use core::mem::{transmute, size_of, uninitialized, swap}; +pub use core::slice; +pub use core::cell::{RefCell, Ref, RefMut}; + +extern crate pwasm_libc; +extern crate pwasm_alloc; + +#[lang = "panic_fmt"] +#[no_mangle] +pub fn panic_fmt() -> ! { + loop {} +} + +extern "C" { + fn ext_print(utf8_data: *const u8, utf8_len: u32); + fn ext_print_num(value: u64); + fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); + fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; + fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32; + fn ext_chain_id() -> u64; + fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); + fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); + fn ext_twox_256(data: *const u8, len: u32, out: *mut u8); + fn ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32; +} + +pub fn storage(key: &[u8]) -> Vec { + let mut length: u32 = 0; + unsafe { + let ptr = ext_get_allocated_storage(&key[0], key.len() as u32, &mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } +} + +pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { + ext_set_storage( + &key[0] as *const u8, key.len() as u32, + &value[0] as *const u8, value.len() as u32 + ); + } +} + +pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { + unsafe { + ext_get_storage_into(&key[0], key.len() as u32, &mut value_out[0], value_out.len() as u32) as usize + } +} + +/// The current relay chain identifier. +pub fn chain_id() -> u64 { + unsafe { + ext_chain_id() + } +} + +/// Conduct a 256-bit Blake2 hash. +pub fn blake2_256(data: &[u8]) -> [u8; 32] { + unsafe { + let mut result: [u8; 32] = uninitialized(); + // guaranteed to write into result. + ext_blake2_256(&data[0], data.len() as u32, &mut result[0]); + result + } +} + +/// Conduct four XX hashes to give a 256-bit result. +pub fn twox_256(data: &[u8]) -> [u8; 32] { + unsafe { + let mut result: [u8; 32] = uninitialized(); + // guaranteed to write into result. + ext_twox_256(&data[0], data.len() as u32, &mut result[0]); + result + } +} + +/// Conduct two XX hashes to give a 256-bit result. +pub fn twox_128(data: &[u8]) -> [u8; 16] { + unsafe { + let mut result: [u8; 16] = uninitialized(); + // guaranteed to write into result. + ext_twox_128(&data[0], data.len() as u32, &mut result[0]); + result + } +} + +/// Verify a ed25519 signature. +pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool { + sig.len() != 64 || pubkey.len() != 32 || unsafe { + ext_ed25519_verify(&msg[0], msg.len() as u32, &sig[0], &pubkey[0]) + } == 0 +} + +pub trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + unsafe { + ext_print(&self[0] as *const u8, self.len() as u32); + } + } +} + +impl Printable for u64 { + fn print(self) { + unsafe { ext_print_num(self); } + } +} + +pub fn print(value: T) { + value.print(); +} + +#[macro_export] +macro_rules! impl_stubs { + ( $( $name:ident ),* ) => { + pub mod _internal { + $( + #[no_mangle] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input = unsafe { + $crate::Vec::from_raw_parts(input_data, input_len, input_len) + }; + + let output = super::$name(input); + &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) + } + )* + } + } +} diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm new file mode 100644 index 0000000000000..ddd9dc70e64e3 Binary files /dev/null and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm new file mode 100644 index 0000000000000..8d3d3fb1ee5ff Binary files /dev/null and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm new file mode 100644 index 0000000000000..5c717847a5a2d Binary files /dev/null and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm new file mode 100644 index 0000000000000..64fb6a48bfd6f Binary files /dev/null and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/runtime/test/Cargo.toml b/wasm-runtime/test/Cargo.toml similarity index 100% rename from runtime/test/Cargo.toml rename to wasm-runtime/test/Cargo.toml diff --git a/wasm-runtime/test/src/lib.rs b/wasm-runtime/test/src/lib.rs new file mode 100644 index 0000000000000..1cf1934976a6b --- /dev/null +++ b/wasm-runtime/test/src/lib.rs @@ -0,0 +1,46 @@ +#![no_std] +#![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +extern crate alloc; +use alloc::vec::Vec; + +#[macro_use] +extern crate runtime_support; +use runtime_support::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify}; + +fn test_blake2_256(input: Vec) -> Vec { + blake2_256(&input).to_vec() +} + +fn test_twox_256(input: Vec) -> Vec { + twox_256(&input).to_vec() +} + +fn test_twox_128(input: Vec) -> Vec { + twox_128(&input).to_vec() +} + +fn test_ed25519_verify(input: Vec) -> Vec { + let sig = &input[0..64]; + let pubkey = &input[64..96]; + let msg = b"all ok!"; + [ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec() +} + +fn test_data_in(input: Vec) -> Vec { + print(b"set_storage" as &[u8]); + set_storage(b"input", &input); + + print(b"storage" as &[u8]); + let foo = storage(b"foo"); + + print(b"set_storage" as &[u8]); + set_storage(b"baz", &foo); + + print(b"finished!" as &[u8]); + b"all ok!".to_vec() +} + +impl_stubs!(test_data_in, test_blake2_256, test_twox_256, test_twox_128, test_ed25519_verify);