commit c0c672b5816046a9ce28e4f69af8c1ebb3f02409
parent ed31faa0362d1ef8c69875d34c48edd659a99541
Author: Antoine A <>
Date: Tue, 1 Feb 2022 17:02:17 +0100
eth_wire: initialize
Diffstat:
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