depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit c0c672b5816046a9ce28e4f69af8c1ebb3f02409
parent ed31faa0362d1ef8c69875d34c48edd659a99541
Author: Antoine A <>
Date:   Tue,  1 Feb 2022 17:02:17 +0100

eth_wire: initialize

Diffstat:
MCargo.lock | 426+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
MCargo.toml | 1+
Mbtc-wire/Cargo.toml | 2+-
Rbtc-wire/src/bin/test.rs -> btc-wire/src/bin/btc_test.rs | 0
Mbtc-wire/src/info.rs | 4++++
Aeth_wire/Cargo.toml | 21+++++++++++++++++++++
Aeth_wire/src/bin/eth_test.rs | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aeth_wire/src/bin/eth_wire_utils.rs | 20++++++++++++++++++++
Aeth_wire/src/lib.rs | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aeth_wire/src/main.rs | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aeth_wire/src/metadata.rs | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aeth_wire/src/rpc.rs | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscript/prepare.sh | 15+++++++++++++--
Mtest/common.sh | 44+++++++++++++++++++++++++++++++++++++++++---
14 files changed, 1378 insertions(+), 26 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -24,6 +24,12 @@ dependencies = [ ] [[package]] +name = "anyhow" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" + +[[package]] name = "argh" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -53,6 +59,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f8c380fa28aa1b36107cd97f0196474bb7241bb95a453c5c01a15ac74b2eac" [[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] name = "async-trait" version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -126,6 +144,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] name = "block-buffer" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -135,6 +175,12 @@ dependencies = [ ] [[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] name = "bstr" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -171,6 +217,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] +name = "byte-slice-cast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698" + +[[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -227,6 +279,12 @@ dependencies = [ ] [[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] name = "cpufeatures" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -316,6 +374,12 @@ dependencies = [ ] [[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] name = "crypto-common" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -415,12 +479,34 @@ dependencies = [ ] [[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] name = "digest" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" dependencies = [ - "block-buffer", + "block-buffer 0.10.0", "crypto-common", "generic-array", "subtle", @@ -452,6 +538,63 @@ dependencies = [ ] [[package]] +name = "eth_wire" +version = "0.1.0" +dependencies = [ + "argh", + "serde", + "serde_json", + "serde_repr", + "taler-common", + "thiserror", + "uri-pack", + "web3", +] + +[[package]] +name = "ethabi" +version = "14.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01317735d563b3bad2d5f90d2e1799f414165408251abb762510f40e790e69a" +dependencies = [ + "anyhow", + "ethereum-types", + "hex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64b5df66a228d85e4b17e5d6c6aa43b0310898ffe8a85988c4c032357aaabfd" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -467,6 +610,18 @@ dependencies = [ ] [[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] name = "flexi_logger" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -498,6 +653,12 @@ dependencies = [ ] [[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] name = "futures" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -569,6 +730,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" [[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] name = "futures-util" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -658,7 +825,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" dependencies = [ - "digest", + "digest 0.10.1", ] [[package]] @@ -749,6 +916,44 @@ dependencies = [ ] [[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -788,6 +993,27 @@ dependencies = [ ] [[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -795,9 +1021,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.114" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0005d08a8f7b65fb8073cb697aa0b12b631ed251ce73d862ce50eeb52ce3b50" +checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" [[package]] name = "listenfd" @@ -812,9 +1038,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] @@ -840,7 +1066,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae" dependencies = [ - "digest", + "digest 0.10.1", ] [[package]] @@ -929,9 +1155,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" +checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" dependencies = [ "libc", ] @@ -943,6 +1169,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] name = "ordered-multimap" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -953,6 +1185,32 @@ dependencies = [ ] [[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "parking_lot" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1111,6 +1369,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] +name = "primitive-types" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1151,6 +1432,12 @@ dependencies = [ ] [[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] name = "rand" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1248,6 +1535,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] +name = "rlp" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] name = "rust-ini" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1258,6 +1555,12 @@ dependencies = [ ] [[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1320,9 +1623,9 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" [[package]] name = "serde" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] @@ -1339,9 +1642,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -1413,7 +1716,19 @@ checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.1", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", ] [[package]] @@ -1436,15 +1751,21 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "socket2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f82496b90c36d70af5fcd482edaa2e0bd16fade569de1330405fecbbdac736b" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi", ] [[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] name = "stringprep" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1496,6 +1817,12 @@ dependencies = [ ] [[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1526,9 +1853,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ "itoa 1.0.1", "libc", @@ -1543,6 +1870,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" [[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1569,9 +1905,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ "bytes", "libc", @@ -1632,6 +1968,15 @@ dependencies = [ ] [[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] name = "tower-service" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1670,6 +2015,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] +name = "uint" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b413ebfe8c2c74a69ff124699dd156a7fa41cb1d09ba6df94aa2f2b0a4a3a" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] name = "unicode-bidi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1833,6 +2190,29 @@ dependencies = [ ] [[package]] +name = "web3" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd24abe6f2b68e0677f843059faea87bcbd4892e39f02886f366d8222c3c540d" +dependencies = [ + "arrayvec 0.5.2", + "derive_more", + "ethabi", + "ethereum-types", + "futures", + "futures-timer", + "hex", + "jsonrpc-core", + "log", + "parking_lot", + "pin-project", + "rlp", + "serde", + "serde_json", + "tiny-keccak", +] + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1882,3 +2262,9 @@ dependencies = [ "tokio", "tokio-postgres", ] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" diff --git a/Cargo.toml b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "wire-gateway", "btc-wire", + "eth_wire", "uri-pack", "taler-common", ] diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml @@ -28,7 +28,7 @@ thiserror = "1.0.30" uri-pack = { path = "../uri-pack" } base64 = "0.13.0" # Taler libs -taler-common = {path = "../taler-common"} +taler-common = { path = "../taler-common" } # Ini parser rust-ini = "0.17.0" diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/btc_test.rs diff --git a/btc-wire/src/info.rs b/btc-wire/src/info.rs @@ -28,6 +28,10 @@ pub enum DecodeErr { UnexpectedEOF, } +// TODO rename Info to OutMetadata to match eth_wire +// TODO use a common url format for both +// TODO generic metadata struct ? + /// Encoded metadata for outgoing transaction #[derive(Debug, Clone, PartialEq, Eq)] pub enum Info { diff --git a/eth_wire/Cargo.toml b/eth_wire/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "eth_wire" +version = "0.1.0" +edition = "2021" +license = "AGPL-3.0-or-later" + +[dependencies] +# Cli args +argh = "0.1.7" +# Serialization library +serde = { version = "1.0.133", features = ["derive"] } +serde_json = "1.0.75" +serde_repr = "0.1.7" +# Ethereum RPC client +web3 = { version = "0.17.0", default-features = false} +# Error macros +thiserror = "1.0.30" +# Optimized uri binary format +uri-pack = { path = "../uri-pack" } +# Taler libs +taler-common = { path = "../taler-common" } diff --git a/eth_wire/src/bin/eth_test.rs b/eth_wire/src/bin/eth_test.rs @@ -0,0 +1,139 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +use std::{panic::AssertUnwindSafe, str::FromStr, time::Duration}; + +use eth_wire::{ + metadata::{InMetadata, OutMetadata}, + rpc::{self, Rpc}, +}; +use taler_common::{rand_slice, url::Url}; +use web3::types::{H256, U256}; + +pub fn main() { + //let path = std::env::args().nth(1).unwrap(); + + let mut rpc = Rpc::new("/tmp/tmp.0fnWV9egdD/eth/geth.ipc").unwrap(); + + let accounts = rpc.list_accounts().unwrap(); + for account in &accounts { + rpc.unlock_account(&account, "password").unwrap(); + } + let wire = accounts[0]; + let client = accounts[1]; + let reserve = accounts[2]; + + let test_value = U256::from(1500032); + let bounce_value = U256::from(10000u32); + let test_url = Url::from_str("http://test.com").unwrap(); + + let mut runner = TestRunner::new(); + + runner.test("Deposit", || { + let rng = rand_slice(); + let hash = rpc.deposit(client, wire, test_value, rng).unwrap(); + assert!(tx_pending(&mut rpc, &hash).unwrap()); + mine_pending(&mut rpc).unwrap(); + assert!(!tx_pending(&mut rpc, &hash).unwrap()); + let tx = rpc.get_transaction(&hash).unwrap().unwrap(); + let metadata = InMetadata::decode(&tx.input.0).unwrap(); + assert!(matches!(metadata, InMetadata::Deposit { reserve_pub } if reserve_pub == rng)); + assert_eq!(tx.value, test_value); + }); + + runner.test("Withdraw", || { + let rng = rand_slice(); + let hash = rpc + .withdraw(wire, client, test_value, rng, test_url.clone()) + .unwrap(); + assert!(tx_pending(&mut rpc, &hash).unwrap()); + mine_pending(&mut rpc).unwrap(); + assert!(!tx_pending(&mut rpc, &hash).unwrap()); + let tx = rpc.get_transaction(&hash).unwrap().unwrap(); + let metadata = OutMetadata::decode(&tx.input.0).unwrap(); + assert!( + matches!(metadata, OutMetadata::Withdraw { wtid, url } if wtid == rng && url == url) + ); + assert_eq!(tx.value, test_value); + }); + + runner.test("Bounce", || { + let rng = rand_slice(); + let deposit = rpc.deposit(client, wire, test_value, rng).unwrap(); + let hash = rpc.bounce(deposit, bounce_value).unwrap(); + assert!(tx_pending(&mut rpc, &hash).unwrap()); + mine_pending(&mut rpc).unwrap(); + assert!(!tx_pending(&mut rpc, &hash).unwrap()); + let tx = rpc.get_transaction(&hash).unwrap().unwrap(); + let metadata = OutMetadata::decode(&tx.input.0).unwrap(); + assert!(matches!(metadata, OutMetadata::Bounce { bounced } if bounced == deposit)); + assert_eq!(tx.value, test_value - bounce_value); + }); + + runner.conclude(); +} + +/// Check a specific transaction is pending +fn tx_pending(rpc: &mut Rpc, id: &H256) -> rpc::Result<bool> { + Ok(rpc.pending_transactions()?.iter().any(|t| t.hash == *id)) +} + +/// Mine pending transactions +fn mine_pending(rpc: &mut Rpc) -> rpc::Result<()> { + rpc.miner_start()?; + while !rpc.pending_transactions()?.is_empty() { + std::thread::sleep(Duration::from_millis(200)); + } + rpc.miner_stop()?; + Ok(()) +} + +/// Run test track success and errors +struct TestRunner { + nb_ok: usize, + nb_err: usize, +} + +impl TestRunner { + fn new() -> Self { + Self { + nb_err: 0, + nb_ok: 0, + } + } + + fn test(&mut self, name: &str, test: impl FnOnce()) { + println!("{}", name); + + let result = std::panic::catch_unwind(AssertUnwindSafe(test)); + if result.is_ok() { + println!("OK"); + self.nb_ok += 1; + } else { + println!("ERR"); + self.nb_err += 1; + } + } + + /// Wait for tests completion and print results + fn conclude(self) { + println!( + "Result for {} tests: {} ok and {} err", + self.nb_ok + self.nb_err, + self.nb_ok, + self.nb_err + ); + } +} diff --git a/eth_wire/src/bin/eth_wire_utils.rs b/eth_wire/src/bin/eth_wire_utils.rs @@ -0,0 +1,19 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +fn main() { + +} +\ No newline at end of file diff --git a/eth_wire/src/lib.rs b/eth_wire/src/lib.rs @@ -0,0 +1,84 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +use metadata::{InMetadata, OutMetadata}; +use taler_common::url::Url; +use web3::{ + ethabi::Address, + types::{Bytes, H256, U256}, +}; + +pub mod metadata; +pub mod rpc; + +impl rpc::Rpc { + pub fn deposit( + &mut self, + from: Address, + to: Address, + value: U256, + reserve_pub: [u8; 32], + ) -> rpc::Result<H256> { + let metadata = InMetadata::Deposit { reserve_pub }; + let encoded = metadata.encode(); + self.send_transaction(&rpc::TransactionRequest { + from, + to, + value, + gas: None, + gas_price: None, + data: Some(Bytes::from(encoded)), + }) + } + + pub fn withdraw( + &mut self, + from: Address, + to: Address, + value: U256, + wtid: [u8; 32], + url: Url, + ) -> rpc::Result<H256> { + let metadata = OutMetadata::Withdraw { wtid, url }; + let encoded = metadata.encode(); + self.send_transaction(&rpc::TransactionRequest { + from, + to, + value, + gas: None, + gas_price: None, + data: Some(Bytes::from(encoded)), + }) + } + + pub fn bounce(&mut self, hash: H256, bounce_fee: U256) -> rpc::Result<H256> { + let tx = self + .get_transaction(&hash)? + .expect("Cannot bounce a non existent transaction"); + let bounce_value = tx.value.saturating_sub(bounce_fee); + let metadata = OutMetadata::Bounce { bounced: hash }; + let encoded = metadata.encode(); + // TODO do not bounce empty amount + self.send_transaction(&rpc::TransactionRequest { + from: tx.to.expect("Cannot bounce contract transaction"), + to: tx.from.expect("Cannot bounce coinbase transaction"), + value: bounce_value, + gas: None, + gas_price: None, + data: Some(Bytes::from(encoded)), + }) + } +} diff --git a/eth_wire/src/main.rs b/eth_wire/src/main.rs @@ -0,0 +1,165 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +use std::time::Duration; + +use web3::types::SyncState; + +use crate::rpc::Rpc; + +mod rpc; + +fn main() { + taler_common::log::init(); + let home = std::env::var("HOME").unwrap(); + let mut rpc = Rpc::new(format!("{}/.ethereum/geth.ipc", home)).unwrap(); + + let config = taler_common::config::Config::load_from_file( + std::env::args_os().nth(1).expect("Missing conf path arg"), + ); + //println!("Calling accounts."); + //let mut accounts = web3.eth().accounts().await?; + //println!("Accounts: {:?}", accounts); + /*accounts.push("00a329c0648769a73afac7f9381e08fb43dbea72".parse().unwrap()); + + println!("Calling balance."); + for account in &accounts { + let balance = web3.eth().balance(*account, None).await?; + println!("Balance of {:?}: {}", account, balance); + } + + web3.eth() + .send_transaction(TransactionRequest { + from: accounts[0], + to: Some(accounts[1]), + value: Some(U256::exp10(8)), + data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])), + ..Default::default() + }) + .await?; + + println!("Calling balance."); + for account in &accounts { + let balance = web3.eth().balance(*account, None).await?; + println!("Balance of {:?}: {}", account, balance); + } + + let filter = web3 + .eth_filter() + .create_logs_filter( + FilterBuilder::default() + .from_block(BlockNumber::Earliest) + .to_block(BlockNumber::Latest) + .address(vec![accounts[1]]) + .build(), + ) + .await?; + + let result = filter.poll().await?; + dbg!(result); + web3.eth() + .send_transaction(TransactionRequest { + from: accounts[0], + to: Some(accounts[1]), + value: Some(U256::exp10(8)), + data: Some(Bytes::from(vec![0, 1, 2, 3, 4, 5])), + ..Default::default() + }) + .await?; + + println!("Calling balance."); + for account in &accounts { + let balance = web3.eth().balance(*account, None).await?; + println!("Balance of {:?}: {}", account, balance); + } + let result = filter.poll().await?; + dbg!(result); + + let filter = web3.eth_filter().create_blocks_filter().await?; + let result = filter.poll().await?; + dbg!(result);*/ + + loop { + let sync = rpc.syncing().unwrap(); + match sync { + SyncState::Syncing(info) => { + println!( + "{}% ({}/{})", + info.current_block * 100 / info.highest_block, + info.current_block, + info.highest_block + ); + break; + std::thread::sleep(Duration::from_secs(60)); + } + SyncState::NotSyncing => break, + } + } + + //let nb = web3.eth().block_number().await.unwrap().as_u64(); + //println!("{} blocks", nb); + + /*let mut db = rusty_leveldb::DB::open( + format!("{}/.ethereum/geth/chaindata", home), + Options::default(), + ) + .unwrap(); + + // Getting hash using hashKey + let mut key = [b'h', 0, 0, 0, 0, 0, 0, 0, 0, b'n']; + U64::from(40).to_big_endian(&mut key[1..9]); + dbg!(&key); + let block_hash = db.get(&key).unwrap(); + dbg!(&block_hash); + + let mut key = Vec::new(); + key.push(b'h'); + key.extend_from_slice(&40u64.to_be_bytes()); + key.extend_from_slice(&block_hash); + dbg!(&key); + let header = db.get(&key).unwrap(); + //let header = BlockHeader:: ::from(header); + dbg!(header); + + return;*/ + /*let start = Instant::now(); + let mut nb_block = 0; + let mut nb_tx = 0; + let mut prev = 0; + { + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + while let Some(block) = rpc.block(nb_block).unwrap() { + nb_block += 1; + nb_tx += block.transactions.len(); + if nb_block % 1000 == 0 { + let elapsed = start.elapsed().as_secs(); + writeln!( + &mut stdout, + "{:>5}kb {:>10}t {:>7}+ {:}h{:0>2}m{:0>2}s", + nb_block / 1000, + nb_tx, + nb_tx - prev, + elapsed / (60 * 60), + (elapsed % (60 * 60)) / 60, + elapsed % 60 + ) + .ok(); + prev = nb_tx; + } + } + } + println!("Done scanned {} blocks in {:?}", nb_block, start.elapsed());*/ +} diff --git a/eth_wire/src/metadata.rs b/eth_wire/src/metadata.rs @@ -0,0 +1,177 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +use taler_common::url::Url; +use web3::types::H256; + +#[derive(Debug, Clone, Copy, thiserror::Error)] +pub enum DecodeErr { + #[error("Unknown first byte: {0}")] + UnknownFirstByte(u8), + #[error(transparent)] + UriPack(#[from] uri_pack::DecodeErr), + #[error("Unexpected end of file")] + UnexpectedEOF, +} + +/// Encoded metadata for outgoing transaction +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OutMetadata { + Withdraw { wtid: [u8; 32], url: Url }, + Bounce { bounced: H256 }, +} + +// We leave a potential special meaning for u8::MAX +const BOUNCE_BYTE: u8 = u8::MAX - 1; + +impl OutMetadata { + pub fn encode(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + match self { + OutMetadata::Withdraw { wtid, url } => { + buffer.push(if url.scheme() == "http" { 1 } else { 0 }); + buffer.extend_from_slice(wtid); + let parts = format!("{}{}", url.domain().unwrap_or(""), url.path()); + let packed = uri_pack::pack_uri(&parts).unwrap(); + buffer.extend_from_slice(&packed); + } + OutMetadata::Bounce { bounced } => { + buffer.push(BOUNCE_BYTE); + buffer.extend_from_slice(bounced.as_bytes()); + } + } + return buffer; + } + + pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> { + if bytes.is_empty() { + return Err(DecodeErr::UnexpectedEOF); + } + match bytes[0] { + 0..=1 => { + if bytes.len() < 33 { + return Err(DecodeErr::UnexpectedEOF); + } + let packed = format!( + "http{}://{}", + if bytes[0] == 0 { "s" } else { "" }, + uri_pack::unpack_uri(&bytes[33..])?, + ); + let url = Url::parse(&packed).unwrap(); + Ok(OutMetadata::Withdraw { + wtid: bytes[1..33].try_into().unwrap(), + url, + }) + } + BOUNCE_BYTE => { + if bytes.len() < 33 { + return Err(DecodeErr::UnexpectedEOF); + } + Ok(OutMetadata::Bounce { + bounced: H256::from_slice(&bytes[1..33]), + }) + } + unknown => Err(DecodeErr::UnknownFirstByte(unknown)), + } + } +} + +/// Encoded metadata for incoming transaction +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InMetadata { + Deposit { reserve_pub: [u8; 32] }, +} + +impl InMetadata { + pub fn encode(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + match self { + InMetadata::Deposit { reserve_pub } => { + buffer.push(0); + buffer.extend_from_slice(reserve_pub); + } + } + return buffer; + } + + pub fn decode(bytes: &[u8]) -> Result<Self, DecodeErr> { + if bytes.is_empty() { + return Err(DecodeErr::UnexpectedEOF); + } + match bytes[0] { + 0 => { + if bytes.len() < 33 { + return Err(DecodeErr::UnexpectedEOF); + } + Ok(InMetadata::Deposit { + reserve_pub: bytes[1..33].try_into().unwrap(), + }) + } + unknown => Err(DecodeErr::UnknownFirstByte(unknown)), + } + } +} + +#[cfg(test)] +mod test { + use taler_common::{rand_slice, url::Url}; + use web3::types::H256; + + use crate::metadata::{InMetadata, OutMetadata}; + + #[test] + fn decode_encode_deposit() { + for _ in 0..4 { + let metadata = InMetadata::Deposit { + reserve_pub: rand_slice(), + }; + let encoded = metadata.encode(); + let decoded = InMetadata::decode(&encoded).unwrap(); + assert_eq!(decoded, metadata); + } + } + + #[test] + fn decode_encode_withdraw() { + let urls = [ + "https://git.taler.net/", + "https://git.taler.net/depolymerization.git/", + "http://git.taler.net/", + "http://git.taler.net/depolymerization.git/", + ]; + for url in urls { + let wtid = rand_slice(); + let url = Url::parse(url).unwrap(); + let metadata = OutMetadata::Withdraw { wtid, url }; + let encoded = metadata.encode(); + let decoded = OutMetadata::decode(&encoded).unwrap(); + assert_eq!(decoded, metadata); + } + } + + #[test] + fn decode_encode_bounce() { + for _ in 0..4 { + let id: [u8; 32] = rand_slice(); + let metadata = OutMetadata::Bounce { + bounced: H256::from_slice(&id), + }; + let encoded = metadata.encode(); + let decoded = OutMetadata::decode(&encoded).unwrap(); + assert_eq!(decoded, metadata); + } + } +} diff --git a/eth_wire/src/rpc.rs b/eth_wire/src/rpc.rs @@ -0,0 +1,306 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +//! This is a very simple RPC client designed only for a specific geth version +//! and to use on an secure unix domain socket to a trusted node +//! +//! We only parse the thing we actually use, this reduce memory usage and +//! make our code more compatible with future deprecation + +use std::{ + fmt::Debug, + io::{self, ErrorKind, Read}, + net::Shutdown, + os::unix::net::UnixStream, + path::Path, +}; +use web3::types::{Address, Bytes, SyncState, H256, U256, U64}; + +#[derive(Debug, serde::Serialize)] +struct RpcRequest<'a, T: serde::Serialize> { + method: &'a str, + id: u64, + params: &'a T, +} + +#[derive(Debug, serde::Deserialize)] +struct RpcResponse<T> { + result: Option<T>, + error: Option<RpcErr>, + id: u64, +} + +#[derive(Debug, serde::Deserialize)] +struct RpcErr { + code: i64, + message: String, +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0:?}")] + Transport(#[from] std::io::Error), + #[error("{code:?} - {msg}")] + RPC { code: i64, msg: String }, + #[error("JSON: {0}")] + Json(#[from] serde_json::Error), + #[error("No result or error")] + Null, +} + +pub type Result<T> = std::result::Result<T, Error>; + +const EMPTY: [(); 0] = []; + +/// Bitcoin RPC connection +pub struct Rpc { + id: u64, + conn: UnixStream, + buf: Vec<u8>, + cursor: usize, +} + +impl Rpc { + pub fn new(path: impl AsRef<Path>) -> io::Result<Self> { + let conn = UnixStream::connect(path)?; + + Ok(Self { + id: 0, + conn, + buf: vec![0u8; 8 * 1024], + cursor: 0, + }) + } + + fn call<T>(&mut self, method: &str, params: &impl serde::Serialize) -> Result<T> + where + T: serde::de::DeserializeOwned + Debug, + { + // TODO rethink timeout + let request = RpcRequest { + method, + id: self.id, + params, + }; + + // Send request + serde_json::to_writer(&mut self.conn, &request)?; + loop { + if self.cursor == self.buf.len() { + self.buf.resize(self.cursor * 2, 0); + } + match self.conn.read(&mut self.buf[self.cursor..]) { + Ok(nb) => { + self.cursor += nb; + let mut de: serde_json::StreamDeserializer<_, RpcResponse<T>> = + serde_json::Deserializer::from_slice(&self.buf[..self.cursor]).into_iter(); + + if let Some(Ok(response)) = de.next() { + let read = de.byte_offset(); + self.buf.copy_within(read..self.cursor, 0); + self.cursor -= read; + assert_eq!(self.id, response.id); + self.id += 1; + return if let Some(ok) = response.result { + Ok(ok) + } else { + Err(match response.error { + Some(err) => Error::RPC { + code: err.code, + msg: err.message, + }, + None => Error::Null, + }) + }; + } else if nb == 0 { + self.conn.shutdown(Shutdown::Both).ok(); + Err(std::io::Error::new(ErrorKind::UnexpectedEof, "Stream EOF"))?; + } + } + Err(e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => Err(e)?, + } + } + } + + fn batch<T>(&mut self, requests: &[(&str, impl serde::Serialize)]) -> Result<Vec<T>> + where + T: serde::de::DeserializeOwned + Debug, + { + let requests: Vec<_> = requests + .into_iter() + .enumerate() + .map(|(i, (method, params))| { + let request = RpcRequest { + method, + id: self.id, + params, + }; + self.id + i as u64; + request + }) + .collect(); + + // Send request + serde_json::to_writer(&mut self.conn, &requests)?; + loop { + if self.cursor == self.buf.len() { + self.buf.resize(self.cursor * 2, 0); + } + match self.conn.read(&mut self.buf[self.cursor..]) { + Ok(nb) => { + self.cursor += nb; + + let mut de: serde_json::StreamDeserializer<_, Vec<RpcResponse<T>>> = + serde_json::Deserializer::from_slice(&self.buf[..self.cursor]).into_iter(); + + if let Some(Ok(mut responses)) = de.next() { + //let mut responses = result?; + responses.sort_unstable_by_key(|r| r.id); + //dbg!(&responses); + let read = de.byte_offset(); + self.buf.copy_within(read..self.cursor, 0); + self.cursor -= read; + self.id += requests.len() as u64; + return responses + .into_iter() + .map(|response| { + if let Some(ok) = response.result { + Ok(ok) + } else { + let err = response.error.unwrap(); + Err(Error::RPC { + code: err.code, + msg: err.message, + }) + } + }) + .collect(); + } else if nb == 0 { + self.conn.shutdown(Shutdown::Both).ok(); + Err(std::io::Error::new(ErrorKind::UnexpectedEof, "Stream EOF"))?; + } + } + Err(e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => Err(e)?, + } + } + } + + pub fn syncing(&mut self) -> Result<SyncState> { + self.call("eth_syncing", &EMPTY) + } + + pub fn list_accounts(&mut self) -> Result<Vec<Address>> { + self.call("personal_listAccounts", &EMPTY) + } + + pub fn new_account(&mut self, passwd: &str) -> Result<Vec<Address>> { + self.call("personal_newAccount", &[passwd]) + } + + pub fn import_account(&mut self, hex: &str, passwd: &str) -> Result<bool> { + self.call("personal_importRawKey", &(hex, passwd)) + } + + pub fn unlock_account(&mut self, account: &Address, passwd: &str) -> Result<bool> { + self.call("personal_unlockAccount", &(account, passwd, 0)) + } + + pub fn get_transaction(&mut self, hash: &H256) -> Result<Option<Transaction>> { + self.call("eth_getTransactionByHash", &[hash]) + } + + pub fn send_transaction(&mut self, params: &TransactionRequest) -> Result<H256> { + self.call("eth_sendTransaction", &[params]) + } + + pub fn block(&mut self, nb: u64) -> Result<Option<Block>> { + self.call("eth_getBlockByNumber", &(U64::from(nb), &true)) + } + + pub fn block_range(&mut self, start: u64, nb: u64) -> Result<Vec<Option<Block>>> { + self.batch( + &(start..start + nb) + .map(|nb| ("eth_getBlockByNumber", (U64::from(nb), &true))) + .collect::<Vec<_>>(), + ) + } + + pub fn pending_transactions(&mut self) -> Result<Vec<Transaction>> { + self.call("eth_pendingTransactions", &EMPTY) + } + + pub fn miner_start(&mut self) -> Result<()> { + match self.call("miner_start", &[1]) { + Err(Error::Null) => Ok(()), + i => i, + } + } + + pub fn miner_stop(&mut self) -> Result<()> { + match self.call("miner_stop", &EMPTY) { + Err(Error::Null) => Ok(()), + i => i, + } + } +} + +#[derive(Debug, serde::Deserialize)] +pub struct Block { + /// Hash of the block + pub hash: Option<H256>, + /// Hash of the parent + #[serde(rename = "parentHash")] + pub parent_hash: H256, + /// Transactions + pub transactions: Vec<Transaction>, +} + +/// Description of a Transaction, pending or in the chain. +#[derive(Debug, serde::Deserialize)] +pub struct Transaction { + /// Hash + pub hash: H256, + /// Sender address (None when coinbase) + pub from: Option<Address>, + /// Recipient address (None when contract creation) + pub to: Option<Address>, + /// Transferred value + pub value: U256, + /// Input data + pub input: Bytes, +} + +/// Send Transaction Parameters +#[derive(Debug, serde::Serialize)] +pub struct TransactionRequest { + /// Sender address + pub from: Address, + /// Recipient address + pub to: Address, + /// Transferred value + pub value: U256, + /// Supplied gas (None for sensible default) + #[serde(skip_serializing_if = "Option::is_none")] + pub gas: Option<U256>, + /// Gas price (None for sensible default) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "gasPrice")] + pub gas_price: Option<U256>, + /// Transaction data + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option<Bytes>, +} diff --git a/script/prepare.sh b/script/prepare.sh @@ -32,6 +32,17 @@ mkdir -pv ~/bitcoin tar xvzf btc.tar.gz mv -v bitcoin-22.0/* ~/bitcoin + +echo "Ⅳ - Install Go Ethereum (Geth) v1.10.15 " +cd $DIR +curl -L https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.10.15-8be800ff.tar.gz -o geth.tar.gz +rm -rfv ~/geth +mkdir -pv ~/geth +tar xvzf geth.tar.gz +mv -v geth-alltools-linux-amd64-1.10.15-8be800ff/* ~/geth + echo "Ⅲ - Config" + +echo "Add ~/postgresql/bin to your path" echo "Add ~/bitcoin/bin to your path" -echo "Add ~/postgresql/bin to your path" -\ No newline at end of file +echo "Add ~/geth to your path" +\ No newline at end of file diff --git a/test/common.sh b/test/common.sh @@ -2,8 +2,6 @@ ## Test utils -set -eu - # Cleanup to run whenever we exit function cleanup() { pg_ctl stop -D $DB_DIR -w &> /dev/null @@ -21,9 +19,11 @@ trap cleanup EXIT DIR=$(mktemp -d) BTC_DIR=$DIR/bitcoin BTC_DIR2=$DIR/bitcoin2 +ETH_DIR=$DIR/eth +ETH_DIR2=$DIR/eth2 DB_DIR=$DIR/db CONF=$DIR/taler.conf -for dir in $BTC_DIR $BTC_DIR2 $DB_DIR log; do +for dir in $BTC_DIR $BTC_DIR2 $ETH_DIR $ETH_DIR2 $DB_DIR log; do mkdir -p $dir done @@ -35,6 +35,8 @@ done # Setup command helpers BTC_CLI="bitcoin-cli -datadir=$BTC_DIR" BTC_CLI2="bitcoin-cli -datadir=$BTC_DIR2" +ETH_CLI="geth -datadir=$ETH_DIR" +ETH_CLI2="geth -datadir=$ETH_DIR2" # Load test.conf as bash variables function load_config() { @@ -198,6 +200,42 @@ function stress_btc_wire() { target/release/btc-wire $CONF &>> log/btc_wire1.log & } +# ----- Ethereum node ----- # + +# Start a geth dev node, generate money, wallet and addresses +function init_eth() { + for pswd in "wire" "client" "reserve"; do + $ETH_CLI account new --password <(echo "password") &> /dev/null + done + local ADDR=`$ETH_CLI account list 2> /dev/null | grep -oP '(?<={).*?(?=})'` + WIRE=`sed -n '1p' <(echo "$ADDR")` + CLIENT=`sed -n '2p' <(echo "$ADDR")` + RESERVE=`sed -n '3p' <(echo "$ADDR")` + echo "{ + \"config\": { + \"chainId\": 42, + \"homesteadBlock\": 0, + \"eip150Block\": 0, + \"eip155Block\": 0, + \"eip158Block\": 0, + \"byzantiumBlock\": 0, + \"constantinopleBlock\": 0, + \"petersburgBlock\": 0, + \"istanbulBlock\": 0, + \"ethash\": {} + }, + \"difficulty\": \"1\", + \"gasLimit\": \"0\", + \"baseFeePerGas\": null, + \"alloc\": { + \"$CLIENT\": { \"balance\": \"1000000000000000000\" }, + \"$WIRE\": { \"balance\": \"500000000000000000\" } + } +}" > $DIR/genesis.json + $ETH_CLI init $DIR/genesis.json + $ETH_CLI +} + # ----- Gateway ------ # # Start wire_gateway in test mode