[Remote signer] Add signer consumer lib (#1763)
Adds a library `common/remote_signer_consumer`
This commit is contained in:
parent
3db9072fee
commit
1a530e5a93
621
Cargo.lock
generated
621
Cargo.lock
generated
@ -224,6 +224,15 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29"
|
||||
dependencies = [
|
||||
"term 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1_der"
|
||||
version = "0.6.3"
|
||||
@ -243,6 +252,117 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0"
|
||||
dependencies = [
|
||||
"extend",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"vec-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73079b49cd26b8fd5a15f68fc7707fc78698dc2a3d61430f2a7a9430230dfa04"
|
||||
dependencies = [
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"futures-lite",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"nb-connect",
|
||||
"once_cell",
|
||||
"parking",
|
||||
"polling",
|
||||
"vec-arena",
|
||||
"waker-fn",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-mutex"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e82538bc65a25dbdff70e4c5439d52f068048ab97cdea0acd73f131594caa1"
|
||||
dependencies = [
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-mutex",
|
||||
"blocking",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log 0.4.11",
|
||||
"memchr",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab 0.4.2",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
|
||||
|
||||
[[package]]
|
||||
name = "async-tls"
|
||||
version = "0.10.0"
|
||||
@ -256,6 +376,17 @@ dependencies = [
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.0"
|
||||
@ -271,6 +402,12 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0db678acb667b525ac40a324fc5f7d3390e29239b31c7327bb8157f5b4fff593"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||
|
||||
[[package]]
|
||||
name = "attohttpc"
|
||||
version = "0.10.1"
|
||||
@ -347,6 +484,17 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "basic-cookies"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38"
|
||||
dependencies = [
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "beacon_chain"
|
||||
version = "0.2.0"
|
||||
@ -447,6 +595,21 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.9.1"
|
||||
@ -571,6 +734,20 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bls"
|
||||
version = "0.2.0"
|
||||
@ -731,6 +908,12 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
|
||||
|
||||
[[package]]
|
||||
name = "cached_tree_hash"
|
||||
version = "0.1.0"
|
||||
@ -818,7 +1001,7 @@ dependencies = [
|
||||
"ansi_term 0.11.0",
|
||||
"atty",
|
||||
"bitflags 1.2.1",
|
||||
"strsim",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
@ -932,6 +1115,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
||||
dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.6"
|
||||
@ -1209,6 +1401,37 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e"
|
||||
dependencies = [
|
||||
"curl-sys",
|
||||
"libc",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"socket2",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.38+curl-7.73.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498ecfb4f59997fd40023d62a9f1e506e768b2baeb59a1d311eb9751cdcd7e3f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libnghttp2-sys",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.0.0"
|
||||
@ -1301,6 +1524,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
@ -1329,6 +1564,17 @@ dependencies = [
|
||||
"eth2_testnet_config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "2.0.2"
|
||||
@ -1430,6 +1676,18 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"serde",
|
||||
"strsim 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.6"
|
||||
@ -1516,6 +1774,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
|
||||
dependencies = [
|
||||
"log 0.4.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.26"
|
||||
@ -1881,6 +2148,12 @@ dependencies = [
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
|
||||
|
||||
[[package]]
|
||||
name = "exit-future"
|
||||
version = "0.2.0"
|
||||
@ -1890,6 +2163,18 @@ dependencies = [
|
||||
"futures 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extend"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
@ -1908,6 +2193,15 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.8.0"
|
||||
@ -2091,6 +2385,21 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.8"
|
||||
@ -2267,6 +2576,19 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.8.0"
|
||||
@ -2564,6 +2886,33 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "httpmock"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1e098238acaa95e0bb3fd357317817fde35efb9cdedf74b18b24698499eea9d"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"async-trait",
|
||||
"base64 0.13.0",
|
||||
"basic-cookies",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"difference",
|
||||
"futures-util",
|
||||
"hyper 0.13.9",
|
||||
"isahc",
|
||||
"lazy_static",
|
||||
"levenshtein",
|
||||
"log 0.4.11",
|
||||
"puddle",
|
||||
"qstring",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
"tokio 0.2.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
@ -2816,6 +3165,31 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
|
||||
|
||||
[[package]]
|
||||
name = "isahc"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cf84ee8215bcaa999a24870485ef49d051cd858985a80d123e56be2d921d811"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"crossbeam-channel 0.5.0",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"curl",
|
||||
"curl-sys",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"http 0.2.1",
|
||||
"log 0.4.11",
|
||||
"mime 0.3.16",
|
||||
"once_cell",
|
||||
"slab 0.4.2",
|
||||
"sluice",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
@ -2890,6 +3264,46 @@ dependencies = [
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||
dependencies = [
|
||||
"log 0.4.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60fb56191fb8ed5311597e5750debe6779c9fdb487dbaa5ff302592897d7a2c8"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"atty",
|
||||
"bit-set",
|
||||
"diff",
|
||||
"docopt",
|
||||
"ena",
|
||||
"itertools 0.9.0",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2 0.8.2",
|
||||
"string_cache",
|
||||
"term 0.5.2",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6771161eff561647fad8bb7e745e002c304864fb8f436b52b30acda51fca4408"
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.2.2"
|
||||
@ -2966,6 +3380,12 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "levenshtein"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66189c12161c65c0023ceb53e2fccc0013311bcb36a7cbd0f9c5e938b408ac96"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.80"
|
||||
@ -2996,6 +3416,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
|
||||
[[package]]
|
||||
name = "libnghttp2-sys"
|
||||
version = "0.1.4+1.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03624ec6df166e79e139a2310ca213283d6b3c30810c54844f307086d4488df1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libp2p"
|
||||
version = "0.30.0"
|
||||
@ -3700,6 +4130,16 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb-connect"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.35"
|
||||
@ -3750,6 +4190,12 @@ dependencies = [
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
@ -4015,6 +4461,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.9.0"
|
||||
@ -4131,6 +4583,15 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.27"
|
||||
@ -4207,6 +4668,19 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"wepoll-sys",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.6.1"
|
||||
@ -4232,6 +4706,12 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "primitive-types"
|
||||
version = "0.7.3"
|
||||
@ -4245,6 +4725,30 @@ dependencies = [
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@ -4381,6 +4885,24 @@ dependencies = [
|
||||
"unescape",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "puddle"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cf84452e80b28e2b05e53964d6f5a44a57978ce19b4920be49e1a61079a24d7"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qstring"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e"
|
||||
dependencies = [
|
||||
"percent-encoding 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@ -4780,17 +5302,33 @@ dependencies = [
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remote_signer_consumer"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"rand 0.7.3",
|
||||
"remote_signer_test",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"tokio 0.2.23",
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remote_signer_test"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"environment",
|
||||
"hex",
|
||||
"httpmock",
|
||||
"remote_signer_client",
|
||||
"remote_signer_consumer",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempdir",
|
||||
"tokio 0.2.23",
|
||||
"types",
|
||||
]
|
||||
|
||||
@ -5148,6 +5686,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_regex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.6"
|
||||
@ -5320,6 +5868,12 @@ dependencies = [
|
||||
"validator_client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.3.0"
|
||||
@ -5421,7 +5975,7 @@ dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
"slog",
|
||||
"term",
|
||||
"term 0.6.1",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
@ -5454,6 +6008,18 @@ dependencies = [
|
||||
"types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sluice"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed13b7cb46f13a15db2c4740f087a848acc8b31af89f95844d40137451f89b1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.13"
|
||||
@ -5671,12 +6237,31 @@ dependencies = [
|
||||
"bytes 0.4.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
@ -5775,6 +6360,17 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dirs 1.0.5",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.6.1"
|
||||
@ -6774,6 +7370,12 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
|
||||
|
||||
[[package]]
|
||||
name = "vec-arena"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
@ -6798,6 +7400,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
@ -7089,6 +7697,15 @@ dependencies = [
|
||||
"ws",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-sys"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
|
@ -32,11 +32,12 @@ members = [
|
||||
"common/lighthouse_version",
|
||||
"common/logging",
|
||||
"common/lru_cache",
|
||||
"common/remote_signer_consumer",
|
||||
"common/slot_clock",
|
||||
"common/test_random_derive",
|
||||
"common/warp_utils",
|
||||
"common/task_executor",
|
||||
"common/test_random_derive",
|
||||
"common/validator_dir",
|
||||
"common/warp_utils",
|
||||
|
||||
"consensus/cached_tree_hash",
|
||||
"consensus/int_to_bytes",
|
||||
|
15
common/remote_signer_consumer/Cargo.toml
Normal file
15
common/remote_signer_consumer/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "remote_signer_consumer"
|
||||
version = "0.2.0"
|
||||
authors = ["Herman Junge <herman@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7.3"
|
||||
remote_signer_test = { path = "../../testing/remote_signer_test" }
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.10.8", features = ["json"] }
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
tokio = { version = "0.2.22", features = ["time"] }
|
||||
types = { path = "../../consensus/types" }
|
88
common/remote_signer_consumer/src/http_client.rs
Normal file
88
common/remote_signer_consumer/src/http_client.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use crate::{
|
||||
Error, RemoteSignerObject, RemoteSignerRequestBody, RemoteSignerResponseBodyError,
|
||||
RemoteSignerResponseBodyOK,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
pub use reqwest::Url;
|
||||
use types::{Domain, Fork, Hash256};
|
||||
|
||||
/// A wrapper around `reqwest::Client` which provides convenience methods
|
||||
/// to interface with a BLS Remote Signer.
|
||||
pub struct RemoteSignerHttpConsumer {
|
||||
client: reqwest::Client,
|
||||
server: Url,
|
||||
}
|
||||
|
||||
impl RemoteSignerHttpConsumer {
|
||||
pub fn from_components(server: Url, client: reqwest::Client) -> Self {
|
||||
Self { client, server }
|
||||
}
|
||||
|
||||
/// `POST /sign/:public-key`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key` - Goes within the url to identify the key we want to use as signer.
|
||||
/// * `bls_domain` - BLS Signature domain. Supporting `BeaconProposer`, `BeaconAttester`,`Randao`.
|
||||
/// * `data` - A `BeaconBlock`, `AttestationData`, or `Epoch`.
|
||||
/// * `fork` - A `Fork` object containing previous and current versions.
|
||||
/// * `genesis_validators_root` - A `Hash256` for domain separation and chain versioning.
|
||||
///
|
||||
/// It sends through the wire a serialized `RemoteSignerRequestBody`.
|
||||
pub async fn sign<R: RemoteSignerObject>(
|
||||
&self,
|
||||
public_key: &str,
|
||||
bls_domain: Domain,
|
||||
data: R,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
) -> Result<String, Error> {
|
||||
if public_key.is_empty() {
|
||||
return Err(Error::InvalidParameter(
|
||||
"Empty parameter public_key".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut path = self.server.clone();
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("sign")
|
||||
.push(public_key);
|
||||
|
||||
let bls_domain = match bls_domain {
|
||||
Domain::BeaconProposer => data.validate_object(bls_domain),
|
||||
Domain::BeaconAttester => data.validate_object(bls_domain),
|
||||
Domain::Randao => data.validate_object(bls_domain),
|
||||
_ => Err(Error::InvalidParameter(format!(
|
||||
"Unsupported BLS Domain: {:?}",
|
||||
bls_domain
|
||||
))),
|
||||
}?;
|
||||
|
||||
let body = RemoteSignerRequestBody {
|
||||
bls_domain,
|
||||
data,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.post(path)
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Reqwest)?;
|
||||
|
||||
match response.status() {
|
||||
StatusCode::OK => match response.json::<RemoteSignerResponseBodyOK>().await {
|
||||
Ok(resp_json) => Ok(resp_json.signature),
|
||||
Err(e) => Err(Error::Reqwest(e)),
|
||||
},
|
||||
_ => match response.json::<RemoteSignerResponseBodyError>().await {
|
||||
Ok(resp_json) => Err(Error::ServerMessage(resp_json.error)),
|
||||
Err(e) => Err(Error::Reqwest(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
211
common/remote_signer_consumer/src/lib.rs
Normal file
211
common/remote_signer_consumer/src/lib.rs
Normal file
@ -0,0 +1,211 @@
|
||||
//! Enables the [Lighthouse Ethereum 2.0 Client] to consume signatures from the
|
||||
//! [BLS Remote Signer].
|
||||
//!
|
||||
//! ## About
|
||||
//!
|
||||
//! The lighthouse client needs to include this crate, and implement the
|
||||
//! adequate bypasses and CLI flags needed to find the remote signer and perform
|
||||
//! the HTTP requests.
|
||||
//!
|
||||
//! As defined by the [EIP-3030] specification, this crate will take the
|
||||
//! received object data and parameters, and send them to the remote signer
|
||||
//! for the production of a signing root hash and signature (the latter if the
|
||||
//! signer has in storage the key identified at request).
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! ### RemoteSignerHttpConsumer
|
||||
//!
|
||||
//! Just provide an `Url` and a timeout
|
||||
//!
|
||||
//! ```
|
||||
//! use remote_signer_consumer::RemoteSignerHttpConsumer;
|
||||
//! use reqwest::{ClientBuilder, Url};
|
||||
//! use tokio::time::Duration;
|
||||
//!
|
||||
//! let url: Url = "http://127.0.0.1:9000".parse().unwrap();
|
||||
//! let reqwest_client = ClientBuilder::new()
|
||||
//! .timeout(Duration::from_secs(2))
|
||||
//! .build()
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let signer = RemoteSignerHttpConsumer::from_components(url, reqwest_client);
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! ## sign API
|
||||
//!
|
||||
//! `POST /sign/:identifier`
|
||||
//!
|
||||
//! ### Arguments
|
||||
//!
|
||||
//! #### `public_key`
|
||||
//!
|
||||
//! Goes within the url to identify the key we want to use as signer.
|
||||
//!
|
||||
//! #### `bls_domain`
|
||||
//!
|
||||
//! [BLS Signature domain]. Supporting `BeaconProposer`, `BeaconAttester`,
|
||||
//! `Randao`.
|
||||
//!
|
||||
//! #### `data`
|
||||
//!
|
||||
//! A `BeaconBlock`, `AttestationData`, or `Epoch`.
|
||||
//!
|
||||
//! #### `fork`
|
||||
//!
|
||||
//! A [`Fork`] object, containing previous and current versions.
|
||||
//!
|
||||
//! #### `genesis_validators_root`
|
||||
//!
|
||||
//! A [`Hash256`] for domain separation and chain versioning.
|
||||
//!
|
||||
//! ### Behavior
|
||||
//!
|
||||
//! Upon receiving and validating the parameters, the signer sends through the
|
||||
//! wire a serialized `RemoteSignerRequestBody`. Receiving a `200` message with
|
||||
//! the `signature` field inside a JSON payload, or an error.
|
||||
//!
|
||||
//! ## How it works
|
||||
//!
|
||||
//! The production of a _local_ signature (i.e. inside the Lighthouse client)
|
||||
//! has slight variations among the kind of objects (block, attestation,
|
||||
//! randao).
|
||||
//!
|
||||
//! To sign a message, the following procedures are needed:
|
||||
//!
|
||||
//! * Get the `fork_version` - From the objects `Fork` and `Epoch`.
|
||||
//! * Compute the [`fork_data_root`] - From the `fork_version` and the
|
||||
//! `genesis_validators_root`.
|
||||
//! * Compute the [`domain`] - From the `fork_data_root` and the `bls_domain`.
|
||||
//! * With the `domain`, the object (or `epoch` in the case of [`randao`])
|
||||
//! can be merkelized into its [`signing_root`] to be signed.
|
||||
//!
|
||||
//! In short, to obtain a signature from the remote signer, we need to produce
|
||||
//! (and serialize) the following objects:
|
||||
//!
|
||||
//! * `bls_domain`.
|
||||
//! * `data` of the object, if this is a block proposal, an attestation, or an epoch.
|
||||
//! * `epoch`, obtained from the object.
|
||||
//! * `fork`.
|
||||
//! * `genesis_validators_root`.
|
||||
//!
|
||||
//! And, of course, the identifier of the secret key, the `public_key`.
|
||||
//!
|
||||
//! ## Future Work
|
||||
//!
|
||||
//! ### EIP-3030
|
||||
//!
|
||||
//! Work is being done to [standardize the API of the remote signers].
|
||||
//!
|
||||
//! [`domain`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_domain
|
||||
//! [`Epoch`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
||||
//! [`fork_data_root`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_fork_data_root
|
||||
//! [`Fork`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#fork
|
||||
//! [`Hash256`]: https://docs.rs/ethereum-types/0.9.2/ethereum_types/struct.H256.html
|
||||
//! [`randao`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#randao
|
||||
//! [`signing_root`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_signing_root
|
||||
//! [BLS Remote Signer]: https://github.com/sigp/rust-bls-remote-signer
|
||||
//! [BLS Signature domain]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types
|
||||
//! [EIP-3030]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3030.md
|
||||
//! [Lighthouse Ethereum 2.0 Client]: https://github.com/sigp/lighthouse
|
||||
//! [standardize the API of the remote signers]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3030.md
|
||||
|
||||
mod http_client;
|
||||
|
||||
pub use http_client::RemoteSignerHttpConsumer;
|
||||
pub use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The `reqwest` client raised an error.
|
||||
Reqwest(reqwest::Error),
|
||||
/// The server returned an error message where the body was able to be parsed.
|
||||
ServerMessage(String),
|
||||
/// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`.
|
||||
InvalidUrl(Url),
|
||||
/// The supplied parameter is invalid.
|
||||
InvalidParameter(String),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RemoteSignerRequestBody<T> {
|
||||
/// BLS Signature domain. Supporting `BeaconProposer`, `BeaconAttester`,`Randao`.
|
||||
bls_domain: String,
|
||||
|
||||
/// A `BeaconBlock`, `AttestationData`, or `Epoch`.
|
||||
data: T,
|
||||
|
||||
/// A `Fork` object containing previous and current versions.
|
||||
fork: Fork,
|
||||
|
||||
/// A `Hash256` for domain separation and chain versioning.
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RemoteSignerResponseBodyOK {
|
||||
signature: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RemoteSignerResponseBodyError {
|
||||
error: String,
|
||||
}
|
||||
|
||||
/// Allows the verification of the BeaconBlock and AttestationData objects
|
||||
/// to be sent through the wire, against their BLS Domains.
|
||||
pub trait RemoteSignerObject: SignedRoot + Serialize {
|
||||
fn validate_object(&self, domain: Domain) -> Result<String, Error>;
|
||||
fn get_epoch(&self) -> Epoch;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RemoteSignerObject for BeaconBlock<E> {
|
||||
fn validate_object(&self, domain: Domain) -> Result<String, Error> {
|
||||
match domain {
|
||||
Domain::BeaconProposer => Ok("beacon_proposer".to_string()),
|
||||
_ => Err(Error::InvalidParameter(format!(
|
||||
"Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got {:?}",
|
||||
domain
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_epoch(&self) -> Epoch {
|
||||
self.epoch()
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteSignerObject for AttestationData {
|
||||
fn validate_object(&self, domain: Domain) -> Result<String, Error> {
|
||||
match domain {
|
||||
Domain::BeaconAttester => Ok("beacon_attester".to_string()),
|
||||
_ => Err(Error::InvalidParameter(format!(
|
||||
"Domain mismatch for the AttestationData object. Expected BeaconAttester, got {:?}",
|
||||
domain
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_epoch(&self) -> Epoch {
|
||||
self.target.epoch
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteSignerObject for Epoch {
|
||||
fn validate_object(&self, domain: Domain) -> Result<String, Error> {
|
||||
match domain {
|
||||
Domain::Randao => Ok("randao".to_string()),
|
||||
_ => Err(Error::InvalidParameter(format!(
|
||||
"Domain mismatch for the Epoch object. Expected Randao, got {:?}",
|
||||
domain
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_epoch(&self) -> Epoch {
|
||||
*self
|
||||
}
|
||||
}
|
181
common/remote_signer_consumer/tests/message_preparation.rs
Normal file
181
common/remote_signer_consumer/tests/message_preparation.rs
Normal file
@ -0,0 +1,181 @@
|
||||
mod message_preparation {
|
||||
use remote_signer_consumer::Error;
|
||||
use remote_signer_test::*;
|
||||
use types::Domain;
|
||||
|
||||
#[test]
|
||||
fn beacon_block_and_bls_domain_mismatch() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
macro_rules! test_case {
|
||||
($f: expr, $bls_domain: expr, $msg: expr) => {
|
||||
match do_sign_request(&test_client, get_input_data_and_set_domain($f, $bls_domain))
|
||||
.unwrap_err()
|
||||
{
|
||||
Error::InvalidParameter(message) => assert_eq!(message, $msg),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_case!(
|
||||
get_input_data_block,
|
||||
Domain::BeaconAttester,
|
||||
"Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got BeaconAttester"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_block,
|
||||
Domain::Randao,
|
||||
"Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got Randao"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_attestation,
|
||||
Domain::BeaconProposer,
|
||||
"Domain mismatch for the AttestationData object. Expected BeaconAttester, got BeaconProposer"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_attestation,
|
||||
Domain::Randao,
|
||||
"Domain mismatch for the AttestationData object. Expected BeaconAttester, got Randao"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_randao,
|
||||
Domain::BeaconProposer,
|
||||
"Domain mismatch for the Epoch object. Expected Randao, got BeaconProposer"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_randao,
|
||||
Domain::BeaconAttester,
|
||||
"Domain mismatch for the Epoch object. Expected Randao, got BeaconAttester"
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_public_key_parameter() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
macro_rules! test_case {
|
||||
($f: expr, $p: expr, $msg: expr) => {
|
||||
match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p))
|
||||
.unwrap_err()
|
||||
{
|
||||
Error::InvalidParameter(message) => assert_eq!(message, $msg),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_case!(get_input_data_block, "", "Empty parameter public_key");
|
||||
test_case!(get_input_data_attestation, "", "Empty parameter public_key");
|
||||
test_case!(get_input_data_randao, "", "Empty parameter public_key");
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_public_key_param() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
macro_rules! test_case {
|
||||
($f: expr, $p: expr, $msg: expr) => {
|
||||
match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p))
|
||||
.unwrap_err()
|
||||
{
|
||||
Error::ServerMessage(message) => assert_eq!(message, $msg),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_case!(get_input_data_block, "/", "Invalid public key: %2F");
|
||||
test_case!(get_input_data_attestation, "/", "Invalid public key: %2F");
|
||||
test_case!(get_input_data_randao, "/", "Invalid public key: %2F");
|
||||
test_case!(get_input_data_block, "//", "Invalid public key: %2F%2F");
|
||||
test_case!(get_input_data_block, "///", "Invalid public key: %2F%2F%2F");
|
||||
test_case!(
|
||||
get_input_data_block,
|
||||
"/?'or 1 = 1 --",
|
||||
"Invalid public key: %2F%3F\'or%201%20=%201%20--"
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_bls_domain() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let test_case = |bls_domain, msg| {
|
||||
let mut test_input = get_input_data_block(0xc137);
|
||||
test_input.bls_domain = bls_domain;
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::InvalidParameter(message) => assert_eq!(message, msg),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
};
|
||||
|
||||
test_case(Domain::Deposit, "Unsupported BLS Domain: Deposit");
|
||||
test_case(
|
||||
Domain::VoluntaryExit,
|
||||
"Unsupported BLS Domain: VoluntaryExit",
|
||||
);
|
||||
test_case(
|
||||
Domain::SelectionProof,
|
||||
"Unsupported BLS Domain: SelectionProof",
|
||||
);
|
||||
test_case(
|
||||
Domain::AggregateAndProof,
|
||||
"Unsupported BLS Domain: AggregateAndProof",
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_public_key_param_additional_path_segments() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
macro_rules! test_case {
|
||||
($f: expr, $p: expr, $msg: expr) => {
|
||||
match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p))
|
||||
.unwrap_err()
|
||||
{
|
||||
Error::ServerMessage(message) => assert_eq!(message, $msg),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_case!(
|
||||
get_input_data_block,
|
||||
"this/receipt",
|
||||
"Invalid public key: this%2Freceipt"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_attestation,
|
||||
"/this/receipt/please",
|
||||
"Invalid public key: %2Fthis%2Freceipt%2Fplease"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_randao,
|
||||
"this/receipt/please?",
|
||||
"Invalid public key: this%2Freceipt%2Fplease%3F"
|
||||
);
|
||||
test_case!(
|
||||
get_input_data_block,
|
||||
&format!("{}/valid/pk", PUBLIC_KEY_1),
|
||||
format!("Invalid public key: {}%2Fvalid%2Fpk", PUBLIC_KEY_1)
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
}
|
168
common/remote_signer_consumer/tests/mock.rs
Normal file
168
common/remote_signer_consumer/tests/mock.rs
Normal file
@ -0,0 +1,168 @@
|
||||
mod mock {
|
||||
use remote_signer_consumer::Error;
|
||||
use remote_signer_test::*;
|
||||
|
||||
#[test]
|
||||
fn timeout() {
|
||||
let mock_server =
|
||||
set_up_mock_server_with_timeout(200, "{\"signature\":\"irrelevant_value\"}", 2);
|
||||
let test_client = set_up_test_consumer_with_timeout(&mock_server.url(""), 1);
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert!(error_msg.contains("error sending request for url (http://127.0.0.1:"));
|
||||
assert!(error_msg.contains("/sign/"));
|
||||
assert!(error_msg.contains(PUBLIC_KEY_1));
|
||||
assert!(error_msg.contains("): operation timed out"));
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_json_in_ok_response() {
|
||||
let mock_server = set_up_mock_server(200, "NO JSON");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert_eq!(
|
||||
error_msg,
|
||||
"error decoding response body: expected value at line 1 column 1"
|
||||
);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_signature_in_ok_json() {
|
||||
let mock_server = set_up_mock_server(200, "{\"foo\":\"bar\"}");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert_eq!(
|
||||
error_msg,
|
||||
"error decoding response body: missing field `signature` at line 1 column 13"
|
||||
);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_signature_in_ok_json() {
|
||||
let mock_server = set_up_mock_server(200, "{\"signature\":\"\"}");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap();
|
||||
|
||||
assert_eq!(r, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_fields_in_ok_json() {
|
||||
let mock_server = set_up_mock_server(
|
||||
200,
|
||||
&format!(
|
||||
"{{\"signature\":\"{}\", \"foo\":\"bar\", \"red\":\"green\"}}",
|
||||
EXPECTED_SIGNATURE_1
|
||||
),
|
||||
);
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap();
|
||||
|
||||
assert_eq!(r, EXPECTED_SIGNATURE_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_json_in_error_response() {
|
||||
let mock_server = set_up_mock_server(500, "NO JSON");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert_eq!(
|
||||
error_msg,
|
||||
"error decoding response body: expected value at line 1 column 1"
|
||||
);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_error_field_in_error_json() {
|
||||
let mock_server = set_up_mock_server(500, "{\"foo\":\"bar\"}");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert_eq!(
|
||||
error_msg,
|
||||
"error decoding response body: missing field `error` at line 1 column 13"
|
||||
);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_error_field_in_error_json() {
|
||||
let mock_server = set_up_mock_server(500, "{\"error\":\"\"}");
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::ServerMessage(msg) => {
|
||||
assert_eq!(msg, "");
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_fields_in_error_json() {
|
||||
let mock_server = set_up_mock_server(
|
||||
500,
|
||||
"{\"error\":\"some_error_msg\", \"foo\":\"bar\", \"red\":\"green\"}",
|
||||
);
|
||||
let test_client = set_up_test_consumer(&mock_server.url(""));
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let r = do_sign_request(&test_client, test_input).unwrap_err();
|
||||
|
||||
match r {
|
||||
Error::ServerMessage(msg) => {
|
||||
assert_eq!(msg, "some_error_msg");
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
225
common/remote_signer_consumer/tests/post.rs
Normal file
225
common/remote_signer_consumer/tests/post.rs
Normal file
@ -0,0 +1,225 @@
|
||||
mod post {
|
||||
use remote_signer_consumer::{Error, RemoteSignerHttpConsumer};
|
||||
use remote_signer_test::*;
|
||||
use reqwest::{ClientBuilder, Url};
|
||||
use tokio::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn server_unavailable() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
test_signer.shutdown();
|
||||
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::Reqwest(e) => {
|
||||
let error_msg = e.to_string();
|
||||
assert!(error_msg.contains("error sending request for url"));
|
||||
assert!(error_msg.contains(PUBLIC_KEY_1));
|
||||
assert!(error_msg.contains("error trying to connect"));
|
||||
assert!(error_msg.contains("tcp connect error"));
|
||||
assert!(error_msg.contains("Connection refused"));
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_error() {
|
||||
let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
set_permissions(tmp_dir.path(), 0o40311);
|
||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40311);
|
||||
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
set_permissions(tmp_dir.path(), 0o40755);
|
||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40755);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::ServerMessage(message) => assert_eq!(message, "Storage error: PermissionDenied"),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_url() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
|
||||
let run_testcase = |u: &str| -> Result<String, String> {
|
||||
let url: Url = u.parse().map_err(|e| format!("[ParseError] {:?}", e))?;
|
||||
|
||||
let reqwest_client = ClientBuilder::new()
|
||||
.timeout(Duration::from_secs(12))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let test_client = RemoteSignerHttpConsumer::from_components(url, reqwest_client);
|
||||
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
signature.map_err(|e| match e {
|
||||
Error::InvalidUrl(message) => format!("[InvalidUrl] {:?}", message),
|
||||
Error::Reqwest(re) => {
|
||||
if re.is_builder() {
|
||||
format!("[Reqwest - Builder] {:?}", re.url().unwrap())
|
||||
} else if re.is_request() {
|
||||
format!("[Reqwest - Request] {:?}", re.url().unwrap())
|
||||
} else {
|
||||
format!("[Reqwest] {:?}", re)
|
||||
}
|
||||
}
|
||||
_ => format!("{:?}", e),
|
||||
})
|
||||
};
|
||||
|
||||
let testcase = |u: &str, msg: &str| assert_eq!(run_testcase(u).unwrap_err(), msg);
|
||||
|
||||
// url::parser::ParseError.
|
||||
// These cases don't even make it to the step of building a RemoteSignerHttpConsumer.
|
||||
testcase("", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase("/4/8/15/16/23/42", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase("localhost", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase(":", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase("0.0:0", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase(":aa", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase("0:", "[ParseError] RelativeUrlWithoutBase");
|
||||
testcase("ftp://", "[ParseError] EmptyHost");
|
||||
testcase("http://", "[ParseError] EmptyHost");
|
||||
testcase("http://127.0.0.1:abcd", "[ParseError] InvalidPort");
|
||||
testcase("http://280.0.0.1", "[ParseError] InvalidIpv4Address");
|
||||
|
||||
// `Error::InvalidUrl`.
|
||||
// The RemoteSignerHttpConsumer is created, but fails at `path_segments_mut()`.
|
||||
testcase("localhost:abcd", "[InvalidUrl] Url { scheme: \"localhost\", host: None, port: None, path: \"abcd\", query: None, fragment: None }");
|
||||
testcase("localhost:", "[InvalidUrl] Url { scheme: \"localhost\", host: None, port: None, path: \"\", query: None, fragment: None }");
|
||||
|
||||
// `Reqwest::Error` of the `Builder` kind.
|
||||
// POST is not made.
|
||||
testcase(
|
||||
"unix:/run/foo.socket",
|
||||
&format!(
|
||||
"[Reqwest - Builder] Url {{ scheme: \"unix\", host: None, port: None, path: \"/run/foo.socket/sign/{}\", query: None, fragment: None }}",
|
||||
PUBLIC_KEY_1
|
||||
),
|
||||
);
|
||||
// `Reqwest::Error` of the `Request` kind.
|
||||
testcase(
|
||||
"http://127.0.0.1:0",
|
||||
&format!(
|
||||
"[Reqwest - Request] Url {{ scheme: \"http\", host: Some(Ipv4(127.0.0.1)), port: Some(0), path: \"/sign/{}\", query: None, fragment: None }}",
|
||||
PUBLIC_KEY_1
|
||||
),
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_url() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
|
||||
let run_testcase = |u: &str| -> Result<String, String> {
|
||||
let url: Url = u.parse().unwrap();
|
||||
|
||||
let reqwest_client = ClientBuilder::new()
|
||||
.timeout(Duration::from_secs(12))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let test_client = RemoteSignerHttpConsumer::from_components(url, reqwest_client);
|
||||
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
signature.map_err(|e| format!("{:?}", e))
|
||||
};
|
||||
|
||||
let testcase = |u: &str, msgs: Vec<&str>| {
|
||||
let r = run_testcase(u).unwrap_err();
|
||||
|
||||
for msg in msgs.iter() {
|
||||
assert!(r.contains(msg), format!("{:?} should contain {:?}", r, msg));
|
||||
}
|
||||
};
|
||||
|
||||
testcase(
|
||||
"http://error-dns",
|
||||
vec![
|
||||
"reqwest::Error",
|
||||
"kind: Request",
|
||||
&format!("/sign/{}", PUBLIC_KEY_1),
|
||||
"hyper::Error(Connect, ConnectError",
|
||||
"dns error",
|
||||
"failed to lookup address information",
|
||||
],
|
||||
);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_public_key() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let mut test_input = get_input_data_block(0xc137);
|
||||
test_input.public_key = ABSENT_PUBLIC_KEY.to_string();
|
||||
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::ServerMessage(msg) => {
|
||||
assert_eq!(msg, format!("Key not found: {}", ABSENT_PUBLIC_KEY))
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_secret_key() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let mut test_input = get_input_data_block(0xc137);
|
||||
test_input.public_key = PUBLIC_KEY_FOR_INVALID_SECRET_KEY.to_string();
|
||||
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::ServerMessage(msg) => assert_eq!(
|
||||
msg,
|
||||
format!(
|
||||
"Invalid secret key: public_key: {}; Invalid hex character: W at index 0",
|
||||
PUBLIC_KEY_FOR_INVALID_SECRET_KEY
|
||||
)
|
||||
),
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_mismatch() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let mut test_input = get_input_data_block(0xc137);
|
||||
test_input.public_key = MISMATCHED_PUBLIC_KEY.to_string();
|
||||
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
match signature.unwrap_err() {
|
||||
Error::ServerMessage(msg) => {
|
||||
assert_eq!(msg, format!("Key mismatch: {}", MISMATCHED_PUBLIC_KEY))
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
49
common/remote_signer_consumer/tests/sign_attestation.rs
Normal file
49
common/remote_signer_consumer/tests/sign_attestation.rs
Normal file
@ -0,0 +1,49 @@
|
||||
mod sign_attestation {
|
||||
use rand::Rng;
|
||||
use remote_signer_test::*;
|
||||
|
||||
#[test]
|
||||
fn sanity_check_deterministic() {
|
||||
let test_input_local = get_input_local_signer_attestation(0xc137);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_attestation(0xc137);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
assert_eq!(local_signature, HAPPY_PATH_ATT_SIGNATURE_C137);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_random() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let seed = rng.gen::<u64>() / 1024;
|
||||
|
||||
let test_input_local = get_input_local_signer_attestation(seed);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_attestation(seed);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn happy_path() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_attestation(0xc137);
|
||||
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(signature.unwrap(), HAPPY_PATH_ATT_SIGNATURE_C137);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
}
|
49
common/remote_signer_consumer/tests/sign_block.rs
Normal file
49
common/remote_signer_consumer/tests/sign_block.rs
Normal file
@ -0,0 +1,49 @@
|
||||
mod sign_block {
|
||||
use rand::Rng;
|
||||
use remote_signer_test::*;
|
||||
|
||||
#[test]
|
||||
fn sanity_check_deterministic() {
|
||||
let test_input_local = get_input_local_signer_block(0xc137);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
assert_eq!(local_signature, HAPPY_PATH_BLOCK_SIGNATURE_C137);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_random() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let seed = rng.gen::<u64>() / 1024;
|
||||
|
||||
let test_input_local = get_input_local_signer_block(seed);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_block(seed);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn happy_path() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
let test_input = get_input_data_block(0xc137);
|
||||
|
||||
let signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(signature.unwrap(), HAPPY_PATH_BLOCK_SIGNATURE_C137);
|
||||
|
||||
test_signer.shutdown();
|
||||
}
|
||||
}
|
50
common/remote_signer_consumer/tests/sign_randao.rs
Normal file
50
common/remote_signer_consumer/tests/sign_randao.rs
Normal file
@ -0,0 +1,50 @@
|
||||
mod sign_randao {
|
||||
use rand::Rng;
|
||||
use remote_signer_test::*;
|
||||
|
||||
#[test]
|
||||
fn sanity_check_deterministic() {
|
||||
let test_input_local = get_input_local_signer_randao(0xc137);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let test_input = get_input_data_randao(0xc137);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
assert_eq!(local_signature, HAPPY_PATH_RANDAO_SIGNATURE_C137);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_random() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let seed = rng.gen::<u64>();
|
||||
|
||||
let test_input_local = get_input_local_signer_randao(seed);
|
||||
let local_signature = test_input_local.sign();
|
||||
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let test_input = get_input_data_randao(seed);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(local_signature, remote_signature.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn happy_path() {
|
||||
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||
let test_client = set_up_test_consumer(&test_signer.address);
|
||||
|
||||
let test_input = get_input_data_randao(0xc137);
|
||||
|
||||
let remote_signature = do_sign_request(&test_client, test_input);
|
||||
|
||||
assert_eq!(remote_signature.unwrap(), HAPPY_PATH_RANDAO_SIGNATURE_C137);
|
||||
}
|
||||
}
|
@ -6,10 +6,14 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.3"
|
||||
client = { path = "../../remote_signer/client", package = "remote_signer_client" }
|
||||
environment = { path = "../../lighthouse/environment" }
|
||||
hex = "0.4.2"
|
||||
httpmock = "0.5.1"
|
||||
remote_signer_client = { path = "../../remote_signer/client" }
|
||||
remote_signer_consumer = { path = "../../common/remote_signer_consumer" }
|
||||
reqwest = { version = "0.10.8", features = ["blocking", "json"] }
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
serde_json = "1.0.58"
|
||||
tempdir = "0.3.7"
|
||||
tokio = { version = "0.2.22", features = ["time"] }
|
||||
types = { path = "../../consensus/types" }
|
||||
|
138
testing/remote_signer_test/src/api_test_signer.rs
Normal file
138
testing/remote_signer_test/src/api_test_signer.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use crate::*;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use environment::{Environment, EnvironmentBuilder};
|
||||
pub use local_signer_test_data::*;
|
||||
use remote_signer_client::Client;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
use types::EthSpec;
|
||||
|
||||
pub struct ApiTestSigner<E: EthSpec> {
|
||||
pub address: String,
|
||||
environment: Environment<E>,
|
||||
}
|
||||
|
||||
pub struct ApiTestResponse {
|
||||
pub status: u16,
|
||||
pub json: Value,
|
||||
}
|
||||
|
||||
impl ApiTestSigner<E> {
|
||||
pub fn new(arg_vec: Vec<&str>) -> Self {
|
||||
let matches = set_matches(arg_vec);
|
||||
let mut environment = get_environment(false);
|
||||
let runtime_context = environment.core_context();
|
||||
|
||||
let client = environment
|
||||
.runtime()
|
||||
.block_on(Client::new(runtime_context, &matches))
|
||||
.map_err(|e| format!("Failed to init Rest API: {}", e))
|
||||
.unwrap();
|
||||
|
||||
let address = get_address(&client);
|
||||
|
||||
Self {
|
||||
address,
|
||||
environment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(mut self) {
|
||||
self.environment.fire_signal()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_matches(arg_vec: Vec<&str>) -> ArgMatches<'static> {
|
||||
let matches = App::new("BLS_Remote_Signer")
|
||||
.arg(
|
||||
Arg::with_name("storage-raw-dir")
|
||||
.long("storage-raw-dir")
|
||||
.value_name("DIR"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
.long("port")
|
||||
.value_name("PORT")
|
||||
.default_value("9000")
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
matches.get_matches_from(arg_vec)
|
||||
}
|
||||
|
||||
pub fn get_environment(is_log_active: bool) -> Environment<E> {
|
||||
let environment_builder = EnvironmentBuilder::mainnet();
|
||||
|
||||
let builder = if is_log_active {
|
||||
environment_builder.async_logger("info", None).unwrap()
|
||||
} else {
|
||||
environment_builder.null_logger().unwrap()
|
||||
};
|
||||
|
||||
builder
|
||||
.multi_threaded_tokio_runtime()
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn set_up_api_test_signer_raw_dir() -> (ApiTestSigner<E>, TempDir) {
|
||||
let tmp_dir = TempDir::new("bls-remote-signer-test").unwrap();
|
||||
let arg_vec = vec![
|
||||
"this_test",
|
||||
"--port",
|
||||
"0",
|
||||
"--storage-raw-dir",
|
||||
tmp_dir.path().to_str().unwrap(),
|
||||
];
|
||||
let test_signer = ApiTestSigner::new(arg_vec);
|
||||
|
||||
(test_signer, tmp_dir)
|
||||
}
|
||||
|
||||
pub fn set_up_api_test_signer_to_sign_message() -> (ApiTestSigner<E>, TempDir) {
|
||||
let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir();
|
||||
add_sub_dirs(&tmp_dir);
|
||||
add_key_files(&tmp_dir);
|
||||
add_non_key_files(&tmp_dir);
|
||||
add_mismatched_key_file(&tmp_dir);
|
||||
add_invalid_secret_key_file(&tmp_dir);
|
||||
|
||||
(test_signer, tmp_dir)
|
||||
}
|
||||
|
||||
pub fn http_get(url: &str) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::get(url).unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http_post(url: &str, hashmap: HashMap<&str, &str>) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.post(url)
|
||||
.json(&hashmap)
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http_post_custom_body(url: &str, body: &str) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.post(url)
|
||||
.body(body.to_string())
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
148
testing/remote_signer_test/src/consumer.rs
Normal file
148
testing/remote_signer_test/src/consumer.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use crate::*;
|
||||
use remote_signer_client::api_response::SignatureApiResponse;
|
||||
use remote_signer_consumer::{Error, RemoteSignerHttpConsumer, RemoteSignerObject, Url};
|
||||
use reqwest::ClientBuilder;
|
||||
use serde::Serialize;
|
||||
use tokio::runtime::Builder;
|
||||
use tokio::time::Duration;
|
||||
use types::{AttestationData, BeaconBlock, Epoch, EthSpec, Fork, Hash256};
|
||||
|
||||
pub fn set_up_test_consumer(test_signer_address: &str) -> RemoteSignerHttpConsumer {
|
||||
set_up_test_consumer_with_timeout(test_signer_address, 12)
|
||||
}
|
||||
|
||||
pub fn set_up_test_consumer_with_timeout(
|
||||
test_signer_address: &str,
|
||||
timeout: u64,
|
||||
) -> RemoteSignerHttpConsumer {
|
||||
let url: Url = test_signer_address.parse().unwrap();
|
||||
let reqwest_client = ClientBuilder::new()
|
||||
.timeout(Duration::from_secs(timeout))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
RemoteSignerHttpConsumer::from_components(url, reqwest_client)
|
||||
}
|
||||
|
||||
pub fn do_sign_request<E: EthSpec, T: RemoteSignerObject>(
|
||||
test_client: &RemoteSignerHttpConsumer,
|
||||
test_input: RemoteSignerTestData<E, T>,
|
||||
) -> Result<String, Error> {
|
||||
let mut runtime = Builder::new()
|
||||
.basic_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(test_client.sign(
|
||||
&test_input.public_key,
|
||||
test_input.bls_domain,
|
||||
test_input.data,
|
||||
test_input.fork,
|
||||
test_input.genesis_validators_root,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BlockRequestBody<E: EthSpec> {
|
||||
bls_domain: String,
|
||||
data: BeaconBlock<E>,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_block_body(seed: u64) -> String {
|
||||
let block: BeaconBlock<E> = get_block(seed);
|
||||
let epoch = block.epoch();
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let block_request_body = BlockRequestBody {
|
||||
bls_domain: "beacon_proposer".to_string(),
|
||||
data: block,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&block_request_body).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct AttestationRequestBody {
|
||||
bls_domain: String,
|
||||
data: AttestationData,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_attestation_body(seed: u64) -> String {
|
||||
let attestation = get_attestation::<E>(seed);
|
||||
let epoch = attestation.target.epoch;
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let attestation_request_body = AttestationRequestBody {
|
||||
bls_domain: "beacon_attester".to_string(),
|
||||
data: attestation,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&attestation_request_body).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct RandaoRequestBody {
|
||||
bls_domain: String,
|
||||
data: Epoch,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_randao_body(seed: u64) -> String {
|
||||
let epoch = Epoch::new(seed);
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let randao_request_body = RandaoRequestBody {
|
||||
bls_domain: "randao".to_string(),
|
||||
data: epoch,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&randao_request_body).unwrap()
|
||||
}
|
||||
|
||||
pub fn assert_sign_ok(resp: ApiTestResponse, expected_signature: &str) {
|
||||
assert_eq!(resp.status, 200);
|
||||
assert_eq!(
|
||||
serde_json::from_value::<SignatureApiResponse>(resp.json)
|
||||
.unwrap()
|
||||
.signature,
|
||||
expected_signature
|
||||
);
|
||||
}
|
||||
|
||||
pub fn assert_sign_error(resp: ApiTestResponse, http_status: u16, error_msg: &str) {
|
||||
assert_eq!(resp.status, http_status);
|
||||
assert_eq!(resp.json["error"].as_str().unwrap(), error_msg);
|
||||
}
|
@ -1,320 +1,18 @@
|
||||
mod api_test_signer;
|
||||
mod constants;
|
||||
mod objects;
|
||||
mod consumer;
|
||||
mod local_signer_test_data;
|
||||
mod mock;
|
||||
mod remote_signer_test_data;
|
||||
mod utils;
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use client::api_response::SignatureApiResponse;
|
||||
use client::Client;
|
||||
pub use api_test_signer::*;
|
||||
pub use constants::*;
|
||||
use environment::{Environment, EnvironmentBuilder};
|
||||
pub use objects::*;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::fs::{create_dir, File};
|
||||
use std::io::Write;
|
||||
use std::net::IpAddr::{V4, V6};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use tempdir::TempDir;
|
||||
use types::{AttestationData, BeaconBlock, Epoch, EthSpec, Fork, Hash256, MainnetEthSpec};
|
||||
pub use consumer::*;
|
||||
pub use local_signer_test_data::*;
|
||||
pub use mock::*;
|
||||
pub use remote_signer_test_data::*;
|
||||
use types::MainnetEthSpec;
|
||||
pub use utils::*;
|
||||
|
||||
pub type E = MainnetEthSpec;
|
||||
|
||||
pub struct ApiTestSigner<E: EthSpec> {
|
||||
pub address: String,
|
||||
environment: Environment<E>,
|
||||
}
|
||||
|
||||
pub struct ApiTestResponse {
|
||||
pub status: u16,
|
||||
pub json: Value,
|
||||
}
|
||||
|
||||
impl ApiTestSigner<MainnetEthSpec> {
|
||||
pub fn new(arg_vec: Vec<&str>) -> Self {
|
||||
let matches = set_matches(arg_vec);
|
||||
let mut environment = get_environment(false);
|
||||
let runtime_context = environment.core_context();
|
||||
|
||||
let client = environment
|
||||
.runtime()
|
||||
.block_on(Client::new(runtime_context, &matches))
|
||||
.map_err(|e| format!("Failed to init Rest API: {}", e))
|
||||
.unwrap();
|
||||
|
||||
let address = get_address(&client);
|
||||
|
||||
Self {
|
||||
address,
|
||||
environment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(mut self) {
|
||||
self.environment.fire_signal()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_matches(arg_vec: Vec<&str>) -> ArgMatches<'static> {
|
||||
let matches = App::new("BLS_Remote_Signer")
|
||||
.arg(
|
||||
Arg::with_name("storage-raw-dir")
|
||||
.long("storage-raw-dir")
|
||||
.value_name("DIR"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
.long("port")
|
||||
.value_name("PORT")
|
||||
.default_value("9000")
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
matches.get_matches_from(arg_vec)
|
||||
}
|
||||
|
||||
pub fn get_environment(is_log_active: bool) -> Environment<E> {
|
||||
let environment_builder = EnvironmentBuilder::mainnet();
|
||||
|
||||
let builder = if is_log_active {
|
||||
environment_builder.async_logger("info", None).unwrap()
|
||||
} else {
|
||||
environment_builder.null_logger().unwrap()
|
||||
};
|
||||
|
||||
builder
|
||||
.multi_threaded_tokio_runtime()
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn set_up_api_test_signer_raw_dir() -> (ApiTestSigner<E>, TempDir) {
|
||||
let tmp_dir = TempDir::new("bls-remote-signer-test").unwrap();
|
||||
let arg_vec = vec![
|
||||
"this_test",
|
||||
"--port",
|
||||
"0",
|
||||
"--storage-raw-dir",
|
||||
tmp_dir.path().to_str().unwrap(),
|
||||
];
|
||||
let test_signer = ApiTestSigner::new(arg_vec);
|
||||
|
||||
(test_signer, tmp_dir)
|
||||
}
|
||||
|
||||
pub fn set_up_api_test_signer_to_sign_message() -> (ApiTestSigner<E>, TempDir) {
|
||||
let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir();
|
||||
add_sub_dirs(&tmp_dir);
|
||||
add_key_files(&tmp_dir);
|
||||
add_non_key_files(&tmp_dir);
|
||||
add_mismatched_key_file(&tmp_dir);
|
||||
add_invalid_secret_key_file(&tmp_dir);
|
||||
|
||||
(test_signer, tmp_dir)
|
||||
}
|
||||
|
||||
pub fn get_address(client: &Client) -> String {
|
||||
let listening_address = client.get_listening_address();
|
||||
let ip = match listening_address.ip() {
|
||||
V4(ip) => ip.to_string(),
|
||||
V6(ip) => ip.to_string(),
|
||||
};
|
||||
|
||||
format!("http://{}:{}", ip, listening_address.port())
|
||||
}
|
||||
|
||||
pub fn set_permissions(path: &Path, perm_octal: u32) {
|
||||
let metadata = fs::metadata(path).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(perm_octal);
|
||||
fs::set_permissions(path, permissions).unwrap();
|
||||
}
|
||||
|
||||
pub fn add_key_files(tmp_dir: &TempDir) {
|
||||
let pairs = vec![
|
||||
(PUBLIC_KEY_1, SECRET_KEY_1),
|
||||
(PUBLIC_KEY_2, SECRET_KEY_2),
|
||||
(PUBLIC_KEY_3, SECRET_KEY_3),
|
||||
];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_mismatched_key_file(tmp_dir: &TempDir) {
|
||||
let pairs = vec![(MISMATCHED_PUBLIC_KEY, SECRET_KEY_1)];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_invalid_secret_key_file(tmp_dir: &TempDir) {
|
||||
let pairs = vec![(PUBLIC_KEY_FOR_INVALID_SECRET_KEY, INVALID_SECRET_KEY)];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_non_key_files(tmp_dir: &TempDir) {
|
||||
let pairs = vec![
|
||||
(SILLY_FILE_NAME_1, SILLY_CONTENT_1),
|
||||
(SILLY_FILE_NAME_2, SILLY_CONTENT_2),
|
||||
(SILLY_FILE_NAME_3, SILLY_CONTENT_3),
|
||||
];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
fn add_files(tmp_dir: &TempDir, pairs: Vec<(&str, &str)>) {
|
||||
for pair in pairs {
|
||||
let file_path = tmp_dir.path().join(pair.0);
|
||||
let mut tmp_file = File::create(file_path).unwrap();
|
||||
writeln!(tmp_file, "{}", pair.1).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sub_dirs(tmp_dir: &TempDir) {
|
||||
let random_sub_dir_path = tmp_dir.path().join("random_sub_dir_name");
|
||||
create_dir(random_sub_dir_path).unwrap();
|
||||
|
||||
let another_sub_dir_path = tmp_dir.path().join(SUB_DIR_NAME);
|
||||
create_dir(another_sub_dir_path).unwrap();
|
||||
}
|
||||
|
||||
pub fn http_get(url: &str) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::get(url).unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http_post(url: &str, hashmap: HashMap<&str, &str>) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.post(url)
|
||||
.json(&hashmap)
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http_post_custom_body(url: &str, body: &str) -> ApiTestResponse {
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.post(url)
|
||||
.body(body.to_string())
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
ApiTestResponse {
|
||||
status: response.status().as_u16(),
|
||||
json: serde_json::from_str(&response.text().unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BlockRequestBody<E: EthSpec> {
|
||||
bls_domain: String,
|
||||
data: BeaconBlock<E>,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_block_body(seed: u64) -> String {
|
||||
let block: BeaconBlock<E> = get_block(seed);
|
||||
let epoch = block.epoch();
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let block_request_body = BlockRequestBody {
|
||||
bls_domain: "beacon_proposer".to_string(),
|
||||
data: block,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&block_request_body).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct AttestationRequestBody {
|
||||
bls_domain: String,
|
||||
data: AttestationData,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_attestation_body(seed: u64) -> String {
|
||||
let attestation = get_attestation::<E>(seed);
|
||||
let epoch = attestation.target.epoch;
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let attestation_request_body = AttestationRequestBody {
|
||||
bls_domain: "beacon_attester".to_string(),
|
||||
data: attestation,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&attestation_request_body).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct RandaoRequestBody {
|
||||
bls_domain: String,
|
||||
data: Epoch,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
}
|
||||
|
||||
pub fn get_test_randao_body(seed: u64) -> String {
|
||||
let epoch = Epoch::new(seed);
|
||||
|
||||
let fork = Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
};
|
||||
|
||||
let genesis_validators_root = Hash256::from_low_u64_be(seed);
|
||||
|
||||
let randao_request_body = RandaoRequestBody {
|
||||
bls_domain: "randao".to_string(),
|
||||
data: epoch,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
};
|
||||
|
||||
serde_json::to_string(&randao_request_body).unwrap()
|
||||
}
|
||||
|
||||
pub fn assert_sign_ok(resp: ApiTestResponse, expected_signature: &str) {
|
||||
assert_eq!(resp.status, 200);
|
||||
assert_eq!(
|
||||
serde_json::from_value::<SignatureApiResponse>(resp.json)
|
||||
.unwrap()
|
||||
.signature,
|
||||
expected_signature
|
||||
);
|
||||
}
|
||||
|
||||
pub fn assert_sign_error(resp: ApiTestResponse, http_status: u16, error_msg: &str) {
|
||||
assert_eq!(resp.status, http_status);
|
||||
assert_eq!(resp.json["error"].as_str().unwrap(), error_msg);
|
||||
}
|
||||
|
103
testing/remote_signer_test/src/local_signer_test_data.rs
Normal file
103
testing/remote_signer_test/src/local_signer_test_data.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use crate::*;
|
||||
use hex::decode;
|
||||
use remote_signer_consumer::RemoteSignerObject;
|
||||
use std::mem;
|
||||
use types::{
|
||||
AttestationData, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, SecretKey,
|
||||
SignedRoot,
|
||||
};
|
||||
|
||||
pub struct LocalSignerTestData<T: RemoteSignerObject> {
|
||||
secret_key: SecretKey,
|
||||
spec: ChainSpec,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
obj: T,
|
||||
}
|
||||
|
||||
impl<T: RemoteSignerObject> LocalSignerTestData<T> {
|
||||
pub fn new(obj: T) -> Self {
|
||||
let epoch = obj.get_epoch();
|
||||
|
||||
Self {
|
||||
secret_key: SecretKey::deserialize(&decode(SECRET_KEY_1).unwrap()).unwrap(),
|
||||
spec: E::default_spec(),
|
||||
fork: Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
},
|
||||
genesis_validators_root: Hash256::from_low_u64_be(0xc137),
|
||||
obj,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSignerTestData<BeaconBlock<E>> {
|
||||
pub fn sign(&self) -> String {
|
||||
let signed_block = self.obj.clone().sign(
|
||||
&self.secret_key,
|
||||
&self.fork,
|
||||
self.genesis_validators_root,
|
||||
&self.spec,
|
||||
);
|
||||
|
||||
signed_block.signature.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSignerTestData<AttestationData> {
|
||||
pub fn sign(&self) -> String {
|
||||
let domain = self.spec.get_domain(
|
||||
self.obj.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
&self.fork,
|
||||
self.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = self.obj.signing_root(domain);
|
||||
let signature = &self.secret_key.sign(message);
|
||||
|
||||
signature.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSignerTestData<Epoch> {
|
||||
pub fn sign(&self) -> String {
|
||||
let domain = self.spec.get_domain(
|
||||
self.obj,
|
||||
Domain::Randao,
|
||||
&self.fork,
|
||||
self.genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = self.obj.signing_root(domain);
|
||||
let signature = &self.secret_key.sign(message);
|
||||
|
||||
signature.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_input_local_signer_block(seed: u64) -> LocalSignerTestData<BeaconBlock<E>> {
|
||||
let block: BeaconBlock<E>;
|
||||
|
||||
unsafe {
|
||||
block = mem::transmute(get_block::<E>(seed));
|
||||
}
|
||||
|
||||
LocalSignerTestData::new(block)
|
||||
}
|
||||
|
||||
pub fn get_input_local_signer_attestation(seed: u64) -> LocalSignerTestData<AttestationData> {
|
||||
let attestation: AttestationData;
|
||||
|
||||
unsafe {
|
||||
attestation = mem::transmute(get_attestation::<E>(seed));
|
||||
}
|
||||
|
||||
LocalSignerTestData::new(attestation)
|
||||
}
|
||||
|
||||
pub fn get_input_local_signer_randao(seed: u64) -> LocalSignerTestData<Epoch> {
|
||||
LocalSignerTestData::new(Epoch::new(seed))
|
||||
}
|
20
testing/remote_signer_test/src/mock.rs
Normal file
20
testing/remote_signer_test/src/mock.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use crate::*;
|
||||
use httpmock::{Method::POST, MockServer};
|
||||
use tokio::time::Duration;
|
||||
|
||||
pub fn set_up_mock_server(status: u16, body: &str) -> MockServer {
|
||||
set_up_mock_server_with_timeout(status, body, 0)
|
||||
}
|
||||
|
||||
pub fn set_up_mock_server_with_timeout(status: u16, body: &str, delay: u64) -> MockServer {
|
||||
let server = MockServer::start();
|
||||
|
||||
server.mock(|when, then| {
|
||||
when.method(POST).path(format!("/sign/{}", PUBLIC_KEY_1));
|
||||
then.status(status)
|
||||
.delay(Duration::from_secs(delay))
|
||||
.body(body);
|
||||
});
|
||||
|
||||
server
|
||||
}
|
69
testing/remote_signer_test/src/remote_signer_test_data.rs
Normal file
69
testing/remote_signer_test/src/remote_signer_test_data.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use crate::*;
|
||||
use remote_signer_consumer::RemoteSignerObject;
|
||||
use std::marker::PhantomData;
|
||||
use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256};
|
||||
|
||||
pub struct RemoteSignerTestData<E: EthSpec, T: RemoteSignerObject> {
|
||||
pub public_key: String,
|
||||
pub bls_domain: Domain,
|
||||
pub data: T,
|
||||
pub fork: Fork,
|
||||
pub genesis_validators_root: Hash256,
|
||||
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec, T: RemoteSignerObject> RemoteSignerTestData<E, T> {
|
||||
pub fn new(public_key: &str, data: T, bls_domain: Domain) -> Self {
|
||||
let epoch = data.get_epoch();
|
||||
|
||||
Self {
|
||||
public_key: public_key.to_string(),
|
||||
bls_domain,
|
||||
data,
|
||||
fork: Fork {
|
||||
previous_version: [1; 4],
|
||||
current_version: [2; 4],
|
||||
epoch,
|
||||
},
|
||||
genesis_validators_root: Hash256::from_low_u64_be(0xc137),
|
||||
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_input_data_block(seed: u64) -> RemoteSignerTestData<E, BeaconBlock<E>> {
|
||||
let block = get_block::<E>(seed);
|
||||
RemoteSignerTestData::new(PUBLIC_KEY_1, block, Domain::BeaconProposer)
|
||||
}
|
||||
|
||||
pub fn get_input_data_attestation(seed: u64) -> RemoteSignerTestData<E, AttestationData> {
|
||||
let attestation = get_attestation::<E>(seed);
|
||||
RemoteSignerTestData::new(PUBLIC_KEY_1, attestation, Domain::BeaconAttester)
|
||||
}
|
||||
|
||||
pub fn get_input_data_randao(seed: u64) -> RemoteSignerTestData<E, Epoch> {
|
||||
let epoch = Epoch::new(seed);
|
||||
RemoteSignerTestData::new(PUBLIC_KEY_1, epoch, Domain::Randao)
|
||||
}
|
||||
|
||||
pub fn get_input_data_and_set_domain<E: EthSpec, T: RemoteSignerObject>(
|
||||
f: fn(u64) -> RemoteSignerTestData<E, T>,
|
||||
bls_domain: Domain,
|
||||
) -> RemoteSignerTestData<E, T> {
|
||||
let mut test_input = f(0xc137);
|
||||
test_input.bls_domain = bls_domain;
|
||||
|
||||
test_input
|
||||
}
|
||||
|
||||
pub fn get_input_data_and_set_public_key<E: EthSpec, T: RemoteSignerObject>(
|
||||
f: fn(u64) -> RemoteSignerTestData<E, T>,
|
||||
p: &str,
|
||||
) -> RemoteSignerTestData<E, T> {
|
||||
let mut test_input = f(0xc137);
|
||||
test_input.public_key = p.to_string();
|
||||
|
||||
test_input
|
||||
}
|
@ -1,3 +1,17 @@
|
||||
use crate::*;
|
||||
pub use constants::*;
|
||||
pub use consumer::*;
|
||||
pub use local_signer_test_data::*;
|
||||
pub use mock::*;
|
||||
use remote_signer_client::Client;
|
||||
pub use remote_signer_test_data::*;
|
||||
use std::fs;
|
||||
use std::fs::{create_dir, File};
|
||||
use std::io::Write;
|
||||
use std::net::IpAddr::{V4, V6};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use tempdir::TempDir;
|
||||
use types::{
|
||||
AggregateSignature, Attestation, AttestationData, AttesterSlashing, BeaconBlock,
|
||||
BeaconBlockHeader, BitList, Checkpoint, Deposit, DepositData, Epoch, EthSpec, FixedVector,
|
||||
@ -5,6 +19,71 @@ use types::{
|
||||
SignedBeaconBlockHeader, SignedVoluntaryExit, Slot, Unsigned, VariableList, VoluntaryExit,
|
||||
};
|
||||
|
||||
pub fn get_address(client: &Client) -> String {
|
||||
let listening_address = client.get_listening_address();
|
||||
let ip = match listening_address.ip() {
|
||||
V4(ip) => ip.to_string(),
|
||||
V6(ip) => ip.to_string(),
|
||||
};
|
||||
|
||||
format!("http://{}:{}", ip, listening_address.port())
|
||||
}
|
||||
|
||||
pub fn set_permissions(path: &Path, perm_octal: u32) {
|
||||
let metadata = fs::metadata(path).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(perm_octal);
|
||||
fs::set_permissions(path, permissions).unwrap();
|
||||
}
|
||||
|
||||
pub fn add_key_files(tmp_dir: &TempDir) {
|
||||
let pairs = vec![
|
||||
(PUBLIC_KEY_1, SECRET_KEY_1),
|
||||
(PUBLIC_KEY_2, SECRET_KEY_2),
|
||||
(PUBLIC_KEY_3, SECRET_KEY_3),
|
||||
];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_mismatched_key_file(tmp_dir: &TempDir) {
|
||||
let pairs = vec![(MISMATCHED_PUBLIC_KEY, SECRET_KEY_1)];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_invalid_secret_key_file(tmp_dir: &TempDir) {
|
||||
let pairs = vec![(PUBLIC_KEY_FOR_INVALID_SECRET_KEY, INVALID_SECRET_KEY)];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
pub fn add_non_key_files(tmp_dir: &TempDir) {
|
||||
let pairs = vec![
|
||||
(SILLY_FILE_NAME_1, SILLY_CONTENT_1),
|
||||
(SILLY_FILE_NAME_2, SILLY_CONTENT_2),
|
||||
(SILLY_FILE_NAME_3, SILLY_CONTENT_3),
|
||||
];
|
||||
|
||||
add_files(tmp_dir, pairs);
|
||||
}
|
||||
|
||||
fn add_files(tmp_dir: &TempDir, pairs: Vec<(&str, &str)>) {
|
||||
for pair in pairs {
|
||||
let file_path = tmp_dir.path().join(pair.0);
|
||||
let mut tmp_file = File::create(file_path).unwrap();
|
||||
writeln!(tmp_file, "{}", pair.1).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sub_dirs(tmp_dir: &TempDir) {
|
||||
let random_sub_dir_path = tmp_dir.path().join("random_sub_dir_name");
|
||||
create_dir(random_sub_dir_path).unwrap();
|
||||
|
||||
let another_sub_dir_path = tmp_dir.path().join(SUB_DIR_NAME);
|
||||
create_dir(another_sub_dir_path).unwrap();
|
||||
}
|
||||
|
||||
/// We spice up some of the values, based on a given `seed` parameter.
|
||||
pub fn get_block<E: EthSpec>(seed: u64) -> BeaconBlock<E> {
|
||||
let spec = &mut E::default_spec();
|
Loading…
Reference in New Issue
Block a user