Update testnet tooling (#1001)
* Add progress on new deposits * Add deposited command to account manager * Remove old lcli::helpers mod * Clean clap_utils * Refactor lcli deposit contract commands to use IPC * Make testnet optional for environment * Use dbg formatting for deploy address * Add command to generate bootnode enr * Ensure lcli returns with 1 on error * Ensure account manager returns 1 on error * Disallow deposits to the zero address * Update web3 in eth1 crate * Ensure correct lighthouse dir is created * Reduce deposit gas requirement * Update cargo.lock * Add progress on new deposits * Add deposited command to account manager * Remove old lcli::helpers mod * Clean clap_utils * Refactor lcli deposit contract commands to use IPC * Add command to generate bootnode enr * Ensure lcli returns with 1 on error * Ensure account manager returns 1 on error * Update web3 in eth1 crate * Update Cargo.lock * Move lcli out of main install script * Change --limit to --at-least * Change --datadir to --validator-dir * Remove duplication in docs
This commit is contained in:
parent
f9e8dad1fb
commit
7b86c9a08f
9
.github/workflows/test-suite.yml
vendored
9
.github/workflows/test-suite.yml
vendored
@ -74,3 +74,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Typecheck benchmark code without running it
|
- name: Typecheck benchmark code without running it
|
||||||
run: make check-benches
|
run: make check-benches
|
||||||
|
install-lcli:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: cargo-fmt
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Get latest version of stable Rust
|
||||||
|
run: rustup update stable
|
||||||
|
- name: Build lcli via Makefile
|
||||||
|
run: make install-lcli
|
||||||
|
91
Cargo.lock
generated
91
Cargo.lock
generated
@ -6,6 +6,7 @@ version = "0.0.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bls 0.2.0",
|
"bls 0.2.0",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap_utils 0.1.0",
|
||||||
"deposit_contract 0.2.0",
|
"deposit_contract 0.2.0",
|
||||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"environment 0.2.0",
|
"environment 0.2.0",
|
||||||
@ -22,7 +23,7 @@ dependencies = [
|
|||||||
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"types 0.2.0",
|
"types 0.2.0",
|
||||||
"validator_client 0.2.0",
|
"validator_client 0.2.0",
|
||||||
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -493,6 +494,18 @@ dependencies = [
|
|||||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"eth2_ssz 0.1.2",
|
||||||
|
"eth2_testnet_config 0.2.0",
|
||||||
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"types 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clear_on_drop"
|
name = "clear_on_drop"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@ -890,6 +903,16 @@ dependencies = [
|
|||||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "0.99.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@ -1079,7 +1102,7 @@ dependencies = [
|
|||||||
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tree_hash 0.1.1",
|
"tree_hash 0.1.1",
|
||||||
"types 0.2.0",
|
"types 0.2.0",
|
||||||
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1091,7 +1114,7 @@ dependencies = [
|
|||||||
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"types 0.2.0",
|
"types 0.2.0",
|
||||||
"web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1224,6 +1247,20 @@ dependencies = [
|
|||||||
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethabi"
|
||||||
|
version = "9.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethabi"
|
name = "ethabi"
|
||||||
version = "11.0.0"
|
version = "11.0.0"
|
||||||
@ -1790,6 +1827,18 @@ dependencies = [
|
|||||||
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonrpc-core"
|
||||||
|
version = "14.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keccak"
|
name = "keccak"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1824,10 +1873,12 @@ name = "lcli"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap_utils 0.1.0",
|
||||||
"deposit_contract 0.2.0",
|
"deposit_contract 0.2.0",
|
||||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"environment 0.2.0",
|
"environment 0.2.0",
|
||||||
"eth1_test_rig 0.2.0",
|
"eth1_test_rig 0.2.0",
|
||||||
|
"eth2-libp2p 0.2.0",
|
||||||
"eth2_ssz 0.1.2",
|
"eth2_ssz 0.1.2",
|
||||||
"eth2_testnet_config 0.2.0",
|
"eth2_testnet_config 0.2.0",
|
||||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2339,6 +2390,7 @@ dependencies = [
|
|||||||
"account_manager 0.0.1",
|
"account_manager 0.0.1",
|
||||||
"beacon_node 0.2.0",
|
"beacon_node 0.2.0",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap_utils 0.1.0",
|
||||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"environment 0.2.0",
|
"environment 0.2.0",
|
||||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -4706,6 +4758,7 @@ dependencies = [
|
|||||||
"tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tree_hash 0.1.1",
|
"tree_hash 0.1.1",
|
||||||
"types 0.2.0",
|
"types 0.2.0",
|
||||||
|
"web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4909,6 +4962,34 @@ dependencies = [
|
|||||||
"websocket 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"websocket 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web3"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"jsonrpc-core 14.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"websocket 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki"
|
name = "webpki"
|
||||||
version = "0.21.2"
|
version = "0.21.2"
|
||||||
@ -5173,6 +5254,7 @@ dependencies = [
|
|||||||
"checksum db-key 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b72465f46d518f6015d9cf07f7f3013a95dd6b9c2747c3d65ae0cce43929d14f"
|
"checksum db-key 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b72465f46d518f6015d9cf07f7f3013a95dd6b9c2747c3d65ae0cce43929d14f"
|
||||||
"checksum derivative 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135"
|
"checksum derivative 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3c6d883546668a3e2011b6a716a7330b82eabb0151b138217f632c8243e17135"
|
||||||
"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe"
|
"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe"
|
||||||
|
"checksum derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7"
|
||||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||||
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||||
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
||||||
@ -5187,6 +5269,7 @@ dependencies = [
|
|||||||
"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd"
|
"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd"
|
||||||
"checksum ethabi 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97652a7d1f2504d6c885c87e242a06ccef5bd3054093d3fb742d8fb64806231a"
|
"checksum ethabi 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97652a7d1f2504d6c885c87e242a06ccef5bd3054093d3fb742d8fb64806231a"
|
||||||
"checksum ethabi 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebdeeea85a6d217b9fcc862906d7e283c047e04114165c433756baf5dce00a6c"
|
"checksum ethabi 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebdeeea85a6d217b9fcc862906d7e283c047e04114165c433756baf5dce00a6c"
|
||||||
|
"checksum ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "965126c64662832991f5a748893577630b558e47fa94e7f35aefcd20d737cef7"
|
||||||
"checksum ethbloom 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3932e82d64d347a045208924002930dc105a138995ccdc1479d0f05f0359f17c"
|
"checksum ethbloom 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3932e82d64d347a045208924002930dc105a138995ccdc1479d0f05f0359f17c"
|
||||||
"checksum ethbloom 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cfe1c169414b709cf28aa30c74060bdb830a03a8ba473314d079ac79d80a5f"
|
"checksum ethbloom 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cfe1c169414b709cf28aa30c74060bdb830a03a8ba473314d079ac79d80a5f"
|
||||||
"checksum ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62d1bc682337e2c5ec98930853674dd2b4bd5d0d246933a9e98e5280f7c76c5f"
|
"checksum ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62d1bc682337e2c5ec98930853674dd2b4bd5d0d246933a9e98e5280f7c76c5f"
|
||||||
@ -5243,6 +5326,7 @@ dependencies = [
|
|||||||
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||||
"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
|
"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
|
||||||
"checksum jsonrpc-core 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b83fdc5e0218128d0d270f2f2e7a5ea716f3240c8518a58bc89e6716ba8581"
|
"checksum jsonrpc-core 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b83fdc5e0218128d0d270f2f2e7a5ea716f3240c8518a58bc89e6716ba8581"
|
||||||
|
"checksum jsonrpc-core 14.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25525f6002338fb4debb5167a89a0b47f727a5a48418417545ad3429758b7fec"
|
||||||
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||||
@ -5509,6 +5593,7 @@ dependencies = [
|
|||||||
"checksum wasm-bindgen-test-macro 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cf2f86cd78a2aa7b1fb4bb6ed854eccb7f9263089c79542dca1576a1518a8467"
|
"checksum wasm-bindgen-test-macro 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cf2f86cd78a2aa7b1fb4bb6ed854eccb7f9263089c79542dca1576a1518a8467"
|
||||||
"checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac"
|
"checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac"
|
||||||
"checksum web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
|
"checksum web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
|
||||||
|
"checksum web3 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0631c83208cf420eeb2ed9b6cb2d5fc853aa76a43619ccec2a3d52d741f1261"
|
||||||
"checksum web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "076f34ed252d74a8521e3b013254b1a39f94a98f23aae7cfc85cda6e7b395664"
|
"checksum web3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "076f34ed252d74a8521e3b013254b1a39f94a98f23aae7cfc85cda6e7b395664"
|
||||||
"checksum webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
|
"checksum webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
|
||||||
"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4"
|
"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4"
|
||||||
|
@ -5,6 +5,7 @@ members = [
|
|||||||
"eth2/state_processing",
|
"eth2/state_processing",
|
||||||
"eth2/types",
|
"eth2/types",
|
||||||
"eth2/utils/bls",
|
"eth2/utils/bls",
|
||||||
|
"eth2/utils/clap_utils",
|
||||||
"eth2/utils/compare_fields",
|
"eth2/utils/compare_fields",
|
||||||
"eth2/utils/compare_fields_derive",
|
"eth2/utils/compare_fields_derive",
|
||||||
"eth2/utils/deposit_contract",
|
"eth2/utils/deposit_contract",
|
||||||
|
5
Makefile
5
Makefile
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
EF_TESTS = "tests/ef_tests"
|
EF_TESTS = "tests/ef_tests"
|
||||||
|
|
||||||
# Builds the entire workspace in release (optimized).
|
# Builds the Lighthouse binary in release (optimized).
|
||||||
#
|
#
|
||||||
# Binaries will most likely be found in `./target/release`
|
# Binaries will most likely be found in `./target/release`
|
||||||
install:
|
install:
|
||||||
cargo install --path lighthouse --force --locked
|
cargo install --path lighthouse --force --locked
|
||||||
|
|
||||||
|
# Builds the lcli binary in release (optimized).
|
||||||
|
install-lcli:
|
||||||
cargo install --path lcli --force --locked
|
cargo install --path lcli --force --locked
|
||||||
|
|
||||||
# Runs the full workspace tests in **release**, without downloading any additional
|
# Runs the full workspace tests in **release**, without downloading any additional
|
||||||
|
@ -24,5 +24,6 @@ hex = "0.3"
|
|||||||
validator_client = { path = "../validator_client" }
|
validator_client = { path = "../validator_client" }
|
||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
eth2_testnet_config = { path = "../eth2/utils/eth2_testnet_config" }
|
eth2_testnet_config = { path = "../eth2/utils/eth2_testnet_config" }
|
||||||
web3 = "0.8.0"
|
web3 = "0.10.0"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
|
clap_utils = { path = "../eth2/utils/clap_utils" }
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::deposits;
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
|
|
||||||
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||||
@ -7,6 +8,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("validator")
|
SubCommand::with_name("validator")
|
||||||
.about("Generate or manage Etheruem 2.0 validators.")
|
.about("Generate or manage Etheruem 2.0 validators.")
|
||||||
|
.subcommand(deposits::cli_app())
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("new")
|
SubCommand::with_name("new")
|
||||||
.about("Create a new Ethereum 2.0 validator.")
|
.about("Create a new Ethereum 2.0 validator.")
|
||||||
|
129
account_manager/src/deposits.rs
Normal file
129
account_manager/src/deposits.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use clap_utils;
|
||||||
|
use environment::Environment;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use types::EthSpec;
|
||||||
|
use validator_client::validator_directory::ValidatorDirectoryBuilder;
|
||||||
|
use web3::{transports::Ipc, types::Address, Web3};
|
||||||
|
|
||||||
|
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
App::new("deposited")
|
||||||
|
.about("Creates new Lighthouse validator keys and directories. Each newly-created validator
|
||||||
|
will have a deposit transaction formed and submitted to the deposit contract via
|
||||||
|
--eth1-ipc. Will only write each validator keys to disk if the deposit transaction returns
|
||||||
|
successfully from the eth1 node. The process exits immediately if any Eth1 tx fails. Does
|
||||||
|
not wait for Eth1 confirmation blocks, so there is no guarantee that a deposit will be
|
||||||
|
accepted in the Eth1 chain.")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("validator-dir")
|
||||||
|
.long("validator-dir")
|
||||||
|
.value_name("VALIDATOR_DIRECTORY")
|
||||||
|
.help("The path where the validator directories will be created. Defaults to ~/.lighthouse/validators")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("eth1-ipc")
|
||||||
|
.long("eth1-ipc")
|
||||||
|
.value_name("ETH1_IPC_PATH")
|
||||||
|
.help("Path to an Eth1 JSON-RPC IPC endpoint")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("from-address")
|
||||||
|
.long("from-address")
|
||||||
|
.value_name("FROM_ETH1_ADDRESS")
|
||||||
|
.help("The address that will submit the eth1 deposit. Must be unlocked on the node
|
||||||
|
at --eth1-ipc.")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("deposit-gwei")
|
||||||
|
.long("deposit-gwei")
|
||||||
|
.value_name("DEPOSIT_GWEI")
|
||||||
|
.help("The GWEI value of the deposit amount. Defaults to the minimum amount
|
||||||
|
required for an active validator (MAX_EFFECTIVE_BALANCE.")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("count")
|
||||||
|
.long("count")
|
||||||
|
.value_name("DEPOSIT_COUNT")
|
||||||
|
.help("The number of deposits to create, regardless of how many already exist")
|
||||||
|
.conflicts_with("limit")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("at-least")
|
||||||
|
.long("at-least")
|
||||||
|
.value_name("VALIDATOR_COUNT")
|
||||||
|
.help("Observe the number of validators in --validator-dir, only creating enough to
|
||||||
|
ensure reach the given count. Never deletes an existing validator.")
|
||||||
|
.conflicts_with("count")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cli_run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) -> Result<(), String> {
|
||||||
|
let spec = env.core_context().eth2_config.spec;
|
||||||
|
|
||||||
|
let validator_dir = clap_utils::parse_path_with_default_in_home_dir(
|
||||||
|
matches,
|
||||||
|
"validator_dir",
|
||||||
|
PathBuf::new().join(".lighthouse").join("validators"),
|
||||||
|
)?;
|
||||||
|
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
|
||||||
|
let from_address: Address = clap_utils::parse_required(matches, "from-address")?;
|
||||||
|
let deposit_gwei = clap_utils::parse_optional(matches, "deposit-gwei")?
|
||||||
|
.unwrap_or_else(|| spec.max_effective_balance);
|
||||||
|
let count: Option<usize> = clap_utils::parse_optional(matches, "count")?;
|
||||||
|
let at_least: Option<usize> = clap_utils::parse_optional(matches, "at-least")?;
|
||||||
|
|
||||||
|
let n = match (count, at_least) {
|
||||||
|
(Some(_), Some(_)) => Err("Cannot supply --count and --at-least".to_string()),
|
||||||
|
(None, None) => Err("Must supply either --count or --at-least".to_string()),
|
||||||
|
(Some(count), None) => Ok(count),
|
||||||
|
(None, Some(at_least)) => fs::read_dir(&validator_dir)
|
||||||
|
.map(|iter| at_least.saturating_sub(iter.count()))
|
||||||
|
.map_err(|e| format!("Unable to read {:?}: {}", validator_dir, e)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let deposit_contract = env
|
||||||
|
.testnet
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| "Unable to run account manager without a testnet dir".to_string())?
|
||||||
|
.deposit_contract_address()
|
||||||
|
.map_err(|e| format!("Unable to parse deposit contract address: {}", e))?;
|
||||||
|
|
||||||
|
if deposit_contract == Address::zero() {
|
||||||
|
return Err("Refusing to deposit to the zero address. Check testnet configuration.".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_event_loop_handle, transport) =
|
||||||
|
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
|
||||||
|
let web3 = Web3::new(transport);
|
||||||
|
|
||||||
|
for _ in 0..n {
|
||||||
|
let validator = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(
|
||||||
|
ValidatorDirectoryBuilder::default()
|
||||||
|
.spec(spec.clone())
|
||||||
|
.custom_deposit_amount(deposit_gwei)
|
||||||
|
.thread_random_keypairs()
|
||||||
|
.submit_eth1_deposit(web3.clone(), from_address, deposit_contract),
|
||||||
|
)?
|
||||||
|
.create_directory(validator_dir.clone())?
|
||||||
|
.write_keypair_files()?
|
||||||
|
.write_eth1_data_file()?
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
if let Some(voting_keypair) = validator.voting_keypair {
|
||||||
|
println!("{:?}", voting_keypair.pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
|
mod deposits;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use deposit_contract::DEPOSIT_GAS;
|
use deposit_contract::DEPOSIT_GAS;
|
||||||
@ -6,7 +7,7 @@ use environment::{Environment, RuntimeContext};
|
|||||||
use eth2_testnet_config::Eth2TestnetConfig;
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
use futures::{future, Future, IntoFuture, Stream};
|
use futures::{future, Future, IntoFuture, Stream};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use slog::{crit, error, info, Logger};
|
use slog::{error, info, Logger};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@ -21,20 +22,8 @@ use web3::{
|
|||||||
|
|
||||||
pub use cli::cli_app;
|
pub use cli::cli_app;
|
||||||
|
|
||||||
/// Run the account manager, logging an error if the operation did not succeed.
|
|
||||||
pub fn run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) {
|
|
||||||
let log = env.core_context().log.clone();
|
|
||||||
match run_account_manager(matches, env) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => crit!(log, "Account manager failed"; "error" => e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the account manager, returning an error if the operation did not succeed.
|
/// Run the account manager, returning an error if the operation did not succeed.
|
||||||
fn run_account_manager<T: EthSpec>(
|
pub fn run<T: EthSpec>(matches: &ArgMatches, mut env: Environment<T>) -> Result<(), String> {
|
||||||
matches: &ArgMatches,
|
|
||||||
mut env: Environment<T>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let context = env.core_context();
|
let context = env.core_context();
|
||||||
let log = context.log.clone();
|
let log = context.log.clone();
|
||||||
|
|
||||||
@ -60,6 +49,7 @@ fn run_account_manager<T: EthSpec>(
|
|||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("validator", Some(matches)) => match matches.subcommand() {
|
("validator", Some(matches)) => match matches.subcommand() {
|
||||||
|
("deposited", Some(matches)) => deposits::cli_run(matches, env)?,
|
||||||
("new", Some(matches)) => run_new_validator_subcommand(matches, datadir, env)?,
|
("new", Some(matches)) => run_new_validator_subcommand(matches, datadir, env)?,
|
||||||
_ => {
|
_ => {
|
||||||
return Err("Invalid 'validator new' command. See --help.".to_string());
|
return Err("Invalid 'validator new' command. See --help.".to_string());
|
||||||
|
@ -8,7 +8,7 @@ edition = "2018"
|
|||||||
eth1_test_rig = { path = "../../tests/eth1_test_rig" }
|
eth1_test_rig = { path = "../../tests/eth1_test_rig" }
|
||||||
environment = { path = "../../lighthouse/environment" }
|
environment = { path = "../../lighthouse/environment" }
|
||||||
toml = "^0.5"
|
toml = "^0.5"
|
||||||
web3 = "0.8.0"
|
web3 = "0.10.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
reqwest = "0.9"
|
reqwest = "0.9"
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
//! Helper functions and an extension trait for Ethereum 2 ENRs.
|
//! Helper functions and an extension trait for Ethereum 2 ENRs.
|
||||||
|
|
||||||
|
pub use libp2p::{core::identity::Keypair, discv5::enr::CombinedKey};
|
||||||
|
|
||||||
use super::ENR_FILENAME;
|
use super::ENR_FILENAME;
|
||||||
use crate::types::{Enr, EnrBitfield};
|
use crate::types::{Enr, EnrBitfield};
|
||||||
use crate::NetworkConfig;
|
use crate::NetworkConfig;
|
||||||
use libp2p::core::identity::Keypair;
|
use libp2p::discv5::enr::EnrBuilder;
|
||||||
use libp2p::discv5::enr::{CombinedKey, EnrBuilder};
|
|
||||||
use slog::{debug, warn};
|
use slog::{debug, warn};
|
||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use ssz_types::BitVector;
|
use ssz_types::BitVector;
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
pub(crate) mod enr;
|
pub(crate) mod enr;
|
||||||
|
|
||||||
// Allow external use of the lighthouse ENR builder
|
// Allow external use of the lighthouse ENR builder
|
||||||
pub use enr::build_enr;
|
pub use enr::{build_enr, CombinedKey, Keypair};
|
||||||
|
|
||||||
use crate::metrics;
|
use crate::metrics;
|
||||||
use crate::{error, Enr, NetworkConfig, NetworkGlobals};
|
use crate::{error, Enr, NetworkConfig, NetworkGlobals};
|
||||||
use enr::{Eth2Enr, BITFIELD_ENR_KEY, ETH2_ENR_KEY};
|
use enr::{Eth2Enr, BITFIELD_ENR_KEY, ETH2_ENR_KEY};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use libp2p::core::{identity::Keypair, ConnectedPoint, Multiaddr, PeerId};
|
use libp2p::core::{ConnectedPoint, Multiaddr, PeerId};
|
||||||
use libp2p::discv5::enr::NodeId;
|
use libp2p::discv5::enr::NodeId;
|
||||||
use libp2p::discv5::{Discv5, Discv5Event};
|
use libp2p::discv5::{Discv5, Discv5Event};
|
||||||
use libp2p::multiaddr::Protocol;
|
use libp2p::multiaddr::Protocol;
|
||||||
@ -30,7 +30,7 @@ const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120;
|
|||||||
/// Initial delay between peer searches.
|
/// Initial delay between peer searches.
|
||||||
const INITIAL_SEARCH_DELAY: u64 = 5;
|
const INITIAL_SEARCH_DELAY: u64 = 5;
|
||||||
/// Local ENR storage filename.
|
/// Local ENR storage filename.
|
||||||
const ENR_FILENAME: &str = "enr.dat";
|
pub const ENR_FILENAME: &str = "enr.dat";
|
||||||
/// Number of peers we'd like to have connected to a given long-lived subnet.
|
/// Number of peers we'd like to have connected to a given long-lived subnet.
|
||||||
const TARGET_SUBNET_PEERS: u64 = 3;
|
const TARGET_SUBNET_PEERS: u64 = 3;
|
||||||
|
|
||||||
|
@ -22,4 +22,4 @@ pub use libp2p::{multiaddr, Multiaddr};
|
|||||||
pub use libp2p::{PeerId, Swarm};
|
pub use libp2p::{PeerId, Swarm};
|
||||||
pub use peer_manager::{PeerDB, PeerInfo, PeerSyncStatus};
|
pub use peer_manager::{PeerDB, PeerInfo, PeerSyncStatus};
|
||||||
pub use rpc::RPCEvent;
|
pub use rpc::RPCEvent;
|
||||||
pub use service::Service;
|
pub use service::{Service, NETWORK_KEY_FILENAME};
|
||||||
|
@ -27,7 +27,7 @@ use types::{EnrForkId, EthSpec};
|
|||||||
type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
|
type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
|
||||||
type Libp2pBehaviour<TSpec> = Behaviour<Substream<StreamMuxerBox>, TSpec>;
|
type Libp2pBehaviour<TSpec> = Behaviour<Substream<StreamMuxerBox>, TSpec>;
|
||||||
|
|
||||||
const NETWORK_KEY_FILENAME: &str = "key";
|
pub const NETWORK_KEY_FILENAME: &str = "key";
|
||||||
/// The time in milliseconds to wait before banning a peer. This allows for any Goodbye messages to be
|
/// The time in milliseconds to wait before banning a peer. This allows for any Goodbye messages to be
|
||||||
/// flushed and protocols to be negotiated.
|
/// flushed and protocols to be negotiated.
|
||||||
const BAN_PEER_WAIT_TIMEOUT: u64 = 200;
|
const BAN_PEER_WAIT_TIMEOUT: u64 = 200;
|
||||||
|
@ -18,6 +18,7 @@ TL;DR isn't adequate.
|
|||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
make install-lcli
|
||||||
lcli new-testnet
|
lcli new-testnet
|
||||||
lcli interop-genesis 128
|
lcli interop-genesis 128
|
||||||
lighthouse bn --testnet-dir ~/.lighthouse/testnet --dummy-eth1 --http --enr-match
|
lighthouse bn --testnet-dir ~/.lighthouse/testnet --dummy-eth1 --http --enr-match
|
||||||
@ -27,7 +28,6 @@ lighthouse vc --testnet-dir ~/.lighthouse/testnet --allow-unsynced testnet insec
|
|||||||
Optionally update the genesis time to now:
|
Optionally update the genesis time to now:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
<<<<<<< HEAD
|
|
||||||
lcli change-genesis-time ~/.lighthouse/testnet/genesis.ssz $(date +%s)
|
lcli change-genesis-time ~/.lighthouse/testnet/genesis.ssz $(date +%s)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ used for starting testnets and debugging.
|
|||||||
Install `lcli` from the root directory of this repository with:
|
Install `lcli` from the root directory of this repository with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install --path lcli --force
|
make install-lcli
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.2 Create a testnet directory
|
### 1.2 Create a testnet directory
|
||||||
|
15
eth2/utils/clap_utils/Cargo.toml
Normal file
15
eth2/utils/clap_utils/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "clap_utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = "2.33.0"
|
||||||
|
hex = "0.3"
|
||||||
|
dirs = "2.0"
|
||||||
|
types = { path = "../../types" }
|
||||||
|
eth2_testnet_config = { path = "../eth2_testnet_config" }
|
||||||
|
eth2_ssz = { path = "../ssz" }
|
116
eth2/utils/clap_utils/src/lib.rs
Normal file
116
eth2/utils/clap_utils/src/lib.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
//! A helper library for parsing values from `clap::ArgMatches`.
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
|
use hex;
|
||||||
|
use ssz::Decode;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use types::EthSpec;
|
||||||
|
|
||||||
|
/// Attempts to load the testnet dir at the path if `name` is in `matches`, returning an error if
|
||||||
|
/// the path cannot be found or the testnet dir is invalid.
|
||||||
|
///
|
||||||
|
/// If `name` is not in `matches`, attempts to return the "hard coded" testnet dir.
|
||||||
|
pub fn parse_testnet_dir_with_hardcoded_default<E: EthSpec>(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<Eth2TestnetConfig<E>, String> {
|
||||||
|
parse_required::<PathBuf>(matches, name)
|
||||||
|
.and_then(|path| {
|
||||||
|
Eth2TestnetConfig::load(path.clone())
|
||||||
|
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e))
|
||||||
|
})
|
||||||
|
.map(Result::Ok)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
Eth2TestnetConfig::hard_coded().map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"The hard-coded testnet directory was invalid. \
|
||||||
|
This happens when Lighthouse is migrating between spec versions. \
|
||||||
|
Error : {}",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `name` is in `matches`, parses the value as a path. Otherwise, attempts to find the user's
|
||||||
|
/// home directory and appends `default` to it.
|
||||||
|
pub fn parse_path_with_default_in_home_dir(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
name: &'static str,
|
||||||
|
default: PathBuf,
|
||||||
|
) -> Result<PathBuf, String> {
|
||||||
|
matches
|
||||||
|
.value_of(name)
|
||||||
|
.map(|dir| {
|
||||||
|
dir.parse::<PathBuf>()
|
||||||
|
.map_err(|e| format!("Unable to parse {}: {}", name, e))
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
dirs::home_dir()
|
||||||
|
.map(|home| home.join(default))
|
||||||
|
.ok_or_else(|| format!("Unable to locate home directory. Try specifying {}", name))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of `name` or an error if it is not in `matches` or does not parse
|
||||||
|
/// successfully using `std::string::FromStr`.
|
||||||
|
pub fn parse_required<T>(matches: &ArgMatches, name: &'static str) -> Result<T, String>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
parse_optional(matches, name)?.ok_or_else(|| format!("{} not specified", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of `name` (if present) or an error if it does not parse successfully using
|
||||||
|
/// `std::string::FromStr`.
|
||||||
|
pub fn parse_optional<T>(matches: &ArgMatches, name: &'static str) -> Result<Option<T>, String>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
matches
|
||||||
|
.value_of(name)
|
||||||
|
.map(|val| {
|
||||||
|
val.parse()
|
||||||
|
.map_err(|e| format!("Unable to parse {}: {}", name, e))
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of `name` or an error if it is not in `matches` or does not parse
|
||||||
|
/// successfully using `ssz::Decode`.
|
||||||
|
///
|
||||||
|
/// Expects the value of `name` to be 0x-prefixed ASCII-hex.
|
||||||
|
pub fn parse_ssz_required<T: Decode>(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<T, String> {
|
||||||
|
parse_ssz_optional(matches, name)?.ok_or_else(|| format!("{} not specified", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of `name` (if present) or an error if it does not parse successfully using
|
||||||
|
/// `ssz::Decode`.
|
||||||
|
///
|
||||||
|
/// Expects the value of `name` (if any) to be 0x-prefixed ASCII-hex.
|
||||||
|
pub fn parse_ssz_optional<T: Decode>(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<Option<T>, String> {
|
||||||
|
matches
|
||||||
|
.value_of(name)
|
||||||
|
.map(|val| {
|
||||||
|
if val.starts_with("0x") {
|
||||||
|
let vec = hex::decode(&val[2..])
|
||||||
|
.map_err(|e| format!("Unable to parse {} as hex: {:?}", name, e))?;
|
||||||
|
|
||||||
|
T::from_ssz_bytes(&vec)
|
||||||
|
.map_err(|e| format!("Unable to parse {} as SSZ: {:?}", name, e))
|
||||||
|
} else {
|
||||||
|
Err(format!("Unable to parse {}, must have 0x prefix", name))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}
|
@ -22,7 +22,7 @@ impl From<ethabi::Error> for DecodeError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000;
|
pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000;
|
||||||
pub const DEPOSIT_GAS: usize = 4_000_000;
|
pub const DEPOSIT_GAS: usize = 400_000;
|
||||||
pub const ABI: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.json");
|
pub const ABI: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.json");
|
||||||
pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.bytecode");
|
pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.bytecode");
|
||||||
pub const DEPOSIT_DATA_LEN: usize = 420; // lol
|
pub const DEPOSIT_DATA_LEN: usize = 420; // lol
|
||||||
|
@ -27,3 +27,5 @@ dirs = "2.0"
|
|||||||
genesis = { path = "../beacon_node/genesis" }
|
genesis = { path = "../beacon_node/genesis" }
|
||||||
deposit_contract = { path = "../eth2/utils/deposit_contract" }
|
deposit_contract = { path = "../eth2/utils/deposit_contract" }
|
||||||
tree_hash = { path = "../eth2/utils/tree_hash" }
|
tree_hash = { path = "../eth2/utils/tree_hash" }
|
||||||
|
clap_utils = { path = "../eth2/utils/clap_utils" }
|
||||||
|
eth2-libp2p = { path = "../beacon_node/eth2-libp2p" }
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use crate::helpers::{parse_hex_bytes, parse_u64};
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use clap_utils::{parse_required, parse_ssz_required};
|
||||||
use deposit_contract::{decode_eth1_tx_data, DEPOSIT_DATA_LEN};
|
use deposit_contract::{decode_eth1_tx_data, DEPOSIT_DATA_LEN};
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
|
|
||||||
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
||||||
let rlp_bytes = parse_hex_bytes(matches, "deposit-data")?;
|
let rlp_bytes = parse_ssz_required::<Vec<u8>>(matches, "deposit-data")?;
|
||||||
let amount = parse_u64(matches, "deposit-amount")?;
|
let amount = parse_required(matches, "deposit-amount")?;
|
||||||
|
|
||||||
if rlp_bytes.len() != DEPOSIT_DATA_LEN {
|
if rlp_bytes.len() != DEPOSIT_DATA_LEN {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
@ -1,31 +1,35 @@
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use clap_utils;
|
||||||
|
use deposit_contract::{
|
||||||
|
testnet::{ABI, BYTECODE},
|
||||||
|
CONTRACT_DEPLOY_GAS,
|
||||||
|
};
|
||||||
use environment::Environment;
|
use environment::Environment;
|
||||||
use eth1_test_rig::DepositContract;
|
use futures::{Future, IntoFuture};
|
||||||
use std::fs::File;
|
use std::path::PathBuf;
|
||||||
use std::io::Read;
|
|
||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
use web3::{transports::Http, Web3};
|
use web3::{
|
||||||
|
contract::{Contract, Options},
|
||||||
|
transports::Ipc,
|
||||||
|
types::{Address, U256},
|
||||||
|
Web3,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
||||||
let confirmations = matches
|
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
|
||||||
.value_of("confirmations")
|
let from_address: Address = clap_utils::parse_required(matches, "from-address")?;
|
||||||
.ok_or_else(|| "Confirmations not specified")?
|
let confirmations: usize = clap_utils::parse_required(matches, "confirmations")?;
|
||||||
.parse::<usize>()
|
|
||||||
.map_err(|e| format!("Failed to parse confirmations: {}", e))?;
|
|
||||||
|
|
||||||
let password = parse_password(matches)?;
|
let (_event_loop_handle, transport) =
|
||||||
|
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
|
||||||
|
let web3 = Web3::new(transport);
|
||||||
|
|
||||||
let endpoint = matches
|
let bytecode = String::from_utf8(BYTECODE.to_vec()).map_err(|e| {
|
||||||
.value_of("eth1-endpoint")
|
|
||||||
.ok_or_else(|| "eth1-endpoint not specified")?;
|
|
||||||
|
|
||||||
let (_event_loop, transport) = Http::new(&endpoint).map_err(|e| {
|
|
||||||
format!(
|
format!(
|
||||||
"Failed to start HTTP transport connected to ganache: {:?}",
|
"Unable to parse deposit contract bytecode as utf-8: {:?}",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let web3 = Web3::new(transport);
|
|
||||||
|
|
||||||
// It's unlikely that this will be the _actual_ deployment block, however it'll be close
|
// It's unlikely that this will be the _actual_ deployment block, however it'll be close
|
||||||
// enough to serve our purposes.
|
// enough to serve our purposes.
|
||||||
@ -37,54 +41,26 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
|
|||||||
.block_on(web3.eth().block_number())
|
.block_on(web3.eth().block_number())
|
||||||
.map_err(|e| format!("Failed to get block number: {}", e))?;
|
.map_err(|e| format!("Failed to get block number: {}", e))?;
|
||||||
|
|
||||||
info!("Present eth1 block number is {}", deploy_block);
|
let address = env.runtime().block_on(
|
||||||
|
Contract::deploy(web3.eth(), &ABI)
|
||||||
|
.map_err(|e| format!("Unable to build contract deployer: {:?}", e))?
|
||||||
|
.confirmations(confirmations)
|
||||||
|
.options(Options {
|
||||||
|
gas: Some(U256::from(CONTRACT_DEPLOY_GAS)),
|
||||||
|
..Options::default()
|
||||||
|
})
|
||||||
|
.execute(bytecode, (), from_address)
|
||||||
|
.into_future()
|
||||||
|
.map_err(|e| format!("Unable to execute deployment: {:?}", e))
|
||||||
|
.and_then(|pending| {
|
||||||
|
pending.map_err(|e| format!("Unable to await pending contract: {:?}", e))
|
||||||
|
})
|
||||||
|
.map(|tx_receipt| tx_receipt.address())
|
||||||
|
.map_err(|e| format!("Failed to execute deployment: {:?}", e)),
|
||||||
|
)?;
|
||||||
|
|
||||||
info!("Deploying the bytecode at https://github.com/sigp/unsafe-eth2-deposit-contract",);
|
println!("deposit_contract_address: {:?}", address);
|
||||||
|
println!("deposit_contract_deploy_block: {}", deploy_block);
|
||||||
info!(
|
|
||||||
"Submitting deployment transaction, waiting for {} confirmations",
|
|
||||||
confirmations
|
|
||||||
);
|
|
||||||
|
|
||||||
let deposit_contract = env
|
|
||||||
.runtime()
|
|
||||||
.block_on(DepositContract::deploy_testnet(
|
|
||||||
web3,
|
|
||||||
confirmations,
|
|
||||||
password,
|
|
||||||
))
|
|
||||||
.map_err(|e| format!("Failed to deploy contract: {}", e))?;
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Deposit contract deployed. address: {}, deploy_block: {}",
|
|
||||||
deposit_contract.address(),
|
|
||||||
deploy_block
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_password(matches: &ArgMatches) -> Result<Option<String>, String> {
|
|
||||||
if let Some(password_path) = matches.value_of("password") {
|
|
||||||
Ok(Some(
|
|
||||||
File::open(password_path)
|
|
||||||
.map_err(|e| format!("Unable to open password file: {:?}", e))
|
|
||||||
.and_then(|mut file| {
|
|
||||||
let mut password = String::new();
|
|
||||||
file.read_to_string(&mut password)
|
|
||||||
.map_err(|e| format!("Unable to read password file to string: {:?}", e))
|
|
||||||
.map(|_| password)
|
|
||||||
})
|
|
||||||
.map(|password| {
|
|
||||||
// Trim the linefeed from the end.
|
|
||||||
if password.ends_with('\n') {
|
|
||||||
password[0..password.len() - 1].to_string()
|
|
||||||
} else {
|
|
||||||
password
|
|
||||||
}
|
|
||||||
})?,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
60
lcli/src/generate_bootnode_enr.rs
Normal file
60
lcli/src/generate_bootnode_enr.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use clap::ArgMatches;
|
||||||
|
use eth2_libp2p::{
|
||||||
|
discovery::{build_enr, CombinedKey, Keypair, ENR_FILENAME},
|
||||||
|
NetworkConfig, NETWORK_KEY_FILENAME,
|
||||||
|
};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use types::{EnrForkId, EthSpec};
|
||||||
|
|
||||||
|
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
||||||
|
let ip: IpAddr = clap_utils::parse_required(matches, "ip")?;
|
||||||
|
let udp_port: u16 = clap_utils::parse_required(matches, "udp-port")?;
|
||||||
|
let tcp_port: u16 = clap_utils::parse_required(matches, "tcp-port")?;
|
||||||
|
let output_dir: PathBuf = clap_utils::parse_required(matches, "output-dir")?;
|
||||||
|
|
||||||
|
if output_dir.exists() {
|
||||||
|
return Err(format!(
|
||||||
|
"{:?} already exists, will not override",
|
||||||
|
output_dir
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut config = NetworkConfig::default();
|
||||||
|
config.enr_address = Some(ip);
|
||||||
|
config.enr_udp_port = Some(udp_port);
|
||||||
|
config.enr_tcp_port = Some(tcp_port);
|
||||||
|
|
||||||
|
let local_keypair = Keypair::generate_secp256k1();
|
||||||
|
let enr_key: CombinedKey = local_keypair
|
||||||
|
.clone()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| format!("Unable to convert keypair: {:?}", e))?;
|
||||||
|
let enr = build_enr::<T>(&enr_key, &config, EnrForkId::default())
|
||||||
|
.map_err(|e| format!("Unable to create ENR: {:?}", e))?;
|
||||||
|
|
||||||
|
fs::create_dir_all(&output_dir).map_err(|e| format!("Unable to create output-dir: {:?}", e))?;
|
||||||
|
|
||||||
|
let mut enr_file = File::create(output_dir.join(ENR_FILENAME))
|
||||||
|
.map_err(|e| format!("Unable to create {}: {:?}", ENR_FILENAME, e))?;
|
||||||
|
enr_file
|
||||||
|
.write_all(&enr.to_base64().as_bytes())
|
||||||
|
.map_err(|e| format!("Unable to write ENR to {}: {:?}", ENR_FILENAME, e))?;
|
||||||
|
|
||||||
|
let secret_bytes = match local_keypair {
|
||||||
|
Keypair::Secp256k1(key) => key.secret().to_bytes(),
|
||||||
|
_ => return Err("Key is not a secp256k1 key".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut key_file = File::create(output_dir.join(NETWORK_KEY_FILENAME))
|
||||||
|
.map_err(|e| format!("Unable to create {}: {:?}", NETWORK_KEY_FILENAME, e))?;
|
||||||
|
key_file
|
||||||
|
.write_all(&secret_bytes)
|
||||||
|
.map_err(|e| format!("Unable to write key to {}: {:?}", NETWORK_KEY_FILENAME, e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
185
lcli/src/main.rs
185
lcli/src/main.rs
@ -5,7 +5,7 @@ mod change_genesis_time;
|
|||||||
mod check_deposit_data;
|
mod check_deposit_data;
|
||||||
mod deploy_deposit_contract;
|
mod deploy_deposit_contract;
|
||||||
mod eth1_genesis;
|
mod eth1_genesis;
|
||||||
mod helpers;
|
mod generate_bootnode_enr;
|
||||||
mod interop_genesis;
|
mod interop_genesis;
|
||||||
mod new_testnet;
|
mod new_testnet;
|
||||||
mod parse_hex;
|
mod parse_hex;
|
||||||
@ -18,6 +18,7 @@ use log::Level;
|
|||||||
use parse_hex::run_parse_hex;
|
use parse_hex::run_parse_hex;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use transition_blocks::run_transition_blocks;
|
use transition_blocks::run_transition_blocks;
|
||||||
use types::{test_utils::TestingBeaconStateBuilder, EthSpec, MainnetEthSpec, MinimalEthSpec};
|
use types::{test_utils::TestingBeaconStateBuilder, EthSpec, MainnetEthSpec, MinimalEthSpec};
|
||||||
@ -27,8 +28,7 @@ fn main() {
|
|||||||
|
|
||||||
let matches = App::new("Lighthouse CLI Tool")
|
let matches = App::new("Lighthouse CLI Tool")
|
||||||
.about(
|
.about(
|
||||||
"Performs various testing-related tasks, modelled after zcli. \
|
"Performs various testing-related tasks, including defining testnets.",
|
||||||
by @protolambda.",
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("spec")
|
Arg::with_name("spec")
|
||||||
@ -40,6 +40,15 @@ fn main() {
|
|||||||
.possible_values(&["minimal", "mainnet"])
|
.possible_values(&["minimal", "mainnet"])
|
||||||
.default_value("mainnet")
|
.default_value("mainnet")
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("testnet-dir")
|
||||||
|
.short("d")
|
||||||
|
.long("testnet-dir")
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.help("The testnet dir. Defaults to ~/.lighthouse/testnet"),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("genesis_yaml")
|
SubCommand::with_name("genesis_yaml")
|
||||||
.about("Generates a genesis YAML file")
|
.about("Generates a genesis YAML file")
|
||||||
@ -119,13 +128,22 @@ fn main() {
|
|||||||
"Deploy a testing eth1 deposit contract.",
|
"Deploy a testing eth1 deposit contract.",
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("eth1-endpoint")
|
Arg::with_name("eth1-ipc")
|
||||||
|
.long("eth1-ipc")
|
||||||
.short("e")
|
.short("e")
|
||||||
.long("eth1-endpoint")
|
.value_name("ETH1_IPC_PATH")
|
||||||
.value_name("HTTP_SERVER")
|
.help("Path to an Eth1 JSON-RPC IPC endpoint")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value("http://localhost:8545")
|
.required(true)
|
||||||
.help("The URL to the eth1 JSON-RPC http API."),
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("from-address")
|
||||||
|
.long("from-address")
|
||||||
|
.short("f")
|
||||||
|
.value_name("FROM_ETH1_ADDRESS")
|
||||||
|
.help("The address that will submit the contract creation. Must be unlocked.")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("confirmations")
|
Arg::with_name("confirmations")
|
||||||
@ -135,13 +153,6 @@ fn main() {
|
|||||||
.default_value("3")
|
.default_value("3")
|
||||||
.help("The number of block confirmations before declaring the contract deployed."),
|
.help("The number of block confirmations before declaring the contract deployed."),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("password")
|
|
||||||
.long("password")
|
|
||||||
.value_name("FILE")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The password file to unlock the eth1 account (see --index)"),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("refund-deposit-contract")
|
SubCommand::with_name("refund-deposit-contract")
|
||||||
@ -149,37 +160,32 @@ fn main() {
|
|||||||
"Calls the steal() function on a testnet eth1 contract.",
|
"Calls the steal() function on a testnet eth1 contract.",
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("testnet-dir")
|
Arg::with_name("eth1-ipc")
|
||||||
.short("d")
|
.long("eth1-ipc")
|
||||||
.long("testnet-dir")
|
|
||||||
.value_name("PATH")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The testnet dir. Defaults to ~/.lighthouse/testnet"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("eth1-endpoint")
|
|
||||||
.short("e")
|
.short("e")
|
||||||
.long("eth1-endpoint")
|
.value_name("ETH1_IPC_PATH")
|
||||||
.value_name("HTTP_SERVER")
|
.help("Path to an Eth1 JSON-RPC IPC endpoint")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value("http://localhost:8545")
|
.required(true)
|
||||||
.help("The URL to the eth1 JSON-RPC http API."),
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("password")
|
Arg::with_name("from-address")
|
||||||
.long("password")
|
.long("from-address")
|
||||||
.value_name("FILE")
|
.short("f")
|
||||||
|
.value_name("FROM_ETH1_ADDRESS")
|
||||||
|
.help("The address that will submit the contract creation. Must be unlocked.")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("The password file to unlock the eth1 account (see --index)"),
|
.required(true)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("account-index")
|
Arg::with_name("contract-address")
|
||||||
.short("i")
|
.long("contract-address")
|
||||||
.long("account-index")
|
.short("c")
|
||||||
.value_name("INDEX")
|
.value_name("CONTRACT_ETH1_ADDRESS")
|
||||||
|
.help("The address of the contract to be refunded. Its owner must match
|
||||||
|
--from-address.")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value("0")
|
.required(true)
|
||||||
.help("The eth1 accounts[] index which will send the transaction"),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@ -187,14 +193,6 @@ fn main() {
|
|||||||
.about(
|
.about(
|
||||||
"Listens to the eth1 chain and finds the genesis beacon state",
|
"Listens to the eth1 chain and finds the genesis beacon state",
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("testnet-dir")
|
|
||||||
.short("d")
|
|
||||||
.long("testnet-dir")
|
|
||||||
.value_name("PATH")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The testnet dir. Defaults to ~/.lighthouse/testnet"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("eth1-endpoint")
|
Arg::with_name("eth1-endpoint")
|
||||||
.short("e")
|
.short("e")
|
||||||
@ -210,14 +208,6 @@ fn main() {
|
|||||||
.about(
|
.about(
|
||||||
"Produces an interop-compatible genesis state using deterministic keypairs",
|
"Produces an interop-compatible genesis state using deterministic keypairs",
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("testnet-dir")
|
|
||||||
.short("d")
|
|
||||||
.long("testnet-dir")
|
|
||||||
.value_name("PATH")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The testnet dir. Defaults to ~/.lighthouse/testnet"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("validator-count")
|
Arg::with_name("validator-count")
|
||||||
.long("validator-count")
|
.long("validator-count")
|
||||||
@ -263,13 +253,6 @@ fn main() {
|
|||||||
.about(
|
.about(
|
||||||
"Produce a new testnet directory.",
|
"Produce a new testnet directory.",
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("testnet-dir")
|
|
||||||
.long("testnet-dir")
|
|
||||||
.value_name("DIRECTORY")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The output path for the new testnet directory. Defaults to ~/.lighthouse/testnet"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("min-genesis-time")
|
Arg::with_name("min-genesis-time")
|
||||||
.long("min-genesis-time")
|
.long("min-genesis-time")
|
||||||
@ -384,11 +367,55 @@ fn main() {
|
|||||||
function signature."),
|
function signature."),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("generate-bootnode-enr")
|
||||||
|
.about(
|
||||||
|
"Generates an ENR address to be used as a pre-genesis boot node..",
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("ip")
|
||||||
|
.long("ip")
|
||||||
|
.value_name("IP_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("The IP address to be included in the ENR and used for discovery"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("udp-port")
|
||||||
|
.long("udp-port")
|
||||||
|
.value_name("UDP_PORT")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("The UDP port to be included in the ENR and used for discovery"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("tcp-port")
|
||||||
|
.long("tcp-port")
|
||||||
|
.value_name("TCP_PORT")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("The TCP port to be included in the ENR and used for application comms"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("output-dir")
|
||||||
|
.long("output-dir")
|
||||||
|
.value_name("OUTPUT_DIRECTORY")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("The directory in which to create the network dir"),
|
||||||
|
)
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
macro_rules! run_with_spec {
|
macro_rules! run_with_spec {
|
||||||
($env_builder: expr) => {
|
($env_builder: expr) => {
|
||||||
run($env_builder, &matches)
|
match run($env_builder, &matches) {
|
||||||
|
Ok(()) => process::exit(0),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to run lcli: {}", e);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,14 +430,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<T: EthSpec>(env_builder: EnvironmentBuilder<T>, matches: &ArgMatches) {
|
fn run<T: EthSpec>(env_builder: EnvironmentBuilder<T>, matches: &ArgMatches) -> Result<(), String> {
|
||||||
let env = env_builder
|
let env = env_builder
|
||||||
.multi_threaded_tokio_runtime()
|
.multi_threaded_tokio_runtime()
|
||||||
.expect("should start tokio runtime")
|
.map_err(|e| format!("should start tokio runtime: {:?}", e))?
|
||||||
.async_logger("trace", None)
|
.async_logger("trace", None)
|
||||||
.expect("should start null logger")
|
.map_err(|e| format!("should start null logger: {:?}", e))?
|
||||||
.build()
|
.build()
|
||||||
.expect("should build env");
|
.map_err(|e| format!("should build env: {:?}", e))?;
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("genesis_yaml", Some(matches)) => {
|
("genesis_yaml", Some(matches)) => {
|
||||||
@ -449,30 +476,34 @@ fn run<T: EthSpec>(env_builder: EnvironmentBuilder<T>, matches: &ArgMatches) {
|
|||||||
_ => unreachable!("guarded by slog possible_values"),
|
_ => unreachable!("guarded by slog possible_values"),
|
||||||
};
|
};
|
||||||
info!("Genesis state YAML file created. Exiting successfully.");
|
info!("Genesis state YAML file created. Exiting successfully.");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
("transition-blocks", Some(matches)) => run_transition_blocks::<T>(matches)
|
("transition-blocks", Some(matches)) => run_transition_blocks::<T>(matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to transition blocks: {}", e)),
|
.map_err(|e| format!("Failed to transition blocks: {}", e)),
|
||||||
("pretty-hex", Some(matches)) => run_parse_hex::<T>(matches)
|
("pretty-hex", Some(matches)) => {
|
||||||
.unwrap_or_else(|e| error!("Failed to pretty print hex: {}", e)),
|
run_parse_hex::<T>(matches).map_err(|e| format!("Failed to pretty print hex: {}", e))
|
||||||
|
}
|
||||||
("deploy-deposit-contract", Some(matches)) => {
|
("deploy-deposit-contract", Some(matches)) => {
|
||||||
deploy_deposit_contract::run::<T>(env, matches)
|
deploy_deposit_contract::run::<T>(env, matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run deploy-deposit-contract command: {}", e))
|
.map_err(|e| format!("Failed to run deploy-deposit-contract command: {}", e))
|
||||||
}
|
}
|
||||||
("refund-deposit-contract", Some(matches)) => {
|
("refund-deposit-contract", Some(matches)) => {
|
||||||
refund_deposit_contract::run::<T>(env, matches)
|
refund_deposit_contract::run::<T>(env, matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run refund-deposit-contract command: {}", e))
|
.map_err(|e| format!("Failed to run refund-deposit-contract command: {}", e))
|
||||||
}
|
}
|
||||||
("eth1-genesis", Some(matches)) => eth1_genesis::run::<T>(env, matches)
|
("eth1-genesis", Some(matches)) => eth1_genesis::run::<T>(env, matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run eth1-genesis command: {}", e)),
|
.map_err(|e| format!("Failed to run eth1-genesis command: {}", e)),
|
||||||
("interop-genesis", Some(matches)) => interop_genesis::run::<T>(env, matches)
|
("interop-genesis", Some(matches)) => interop_genesis::run::<T>(env, matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run interop-genesis command: {}", e)),
|
.map_err(|e| format!("Failed to run interop-genesis command: {}", e)),
|
||||||
("change-genesis-time", Some(matches)) => change_genesis_time::run::<T>(matches)
|
("change-genesis-time", Some(matches)) => change_genesis_time::run::<T>(matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run change-genesis-time command: {}", e)),
|
.map_err(|e| format!("Failed to run change-genesis-time command: {}", e)),
|
||||||
("new-testnet", Some(matches)) => new_testnet::run::<T>(matches)
|
("new-testnet", Some(matches)) => new_testnet::run::<T>(matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run new_testnet command: {}", e)),
|
.map_err(|e| format!("Failed to run new_testnet command: {}", e)),
|
||||||
("check-deposit-data", Some(matches)) => check_deposit_data::run::<T>(matches)
|
("check-deposit-data", Some(matches)) => check_deposit_data::run::<T>(matches)
|
||||||
.unwrap_or_else(|e| error!("Failed to run check-deposit-data command: {}", e)),
|
.map_err(|e| format!("Failed to run check-deposit-data command: {}", e)),
|
||||||
(other, _) => error!("Unknown subcommand {}. See --help.", other),
|
("generate-bootnode-enr", Some(matches)) => generate_bootnode_enr::run::<T>(matches)
|
||||||
|
.map_err(|e| format!("Failed to run generate-bootnode-enr command: {}", e)),
|
||||||
|
(other, _) => Err(format!("Unknown subcommand {}. See --help.", other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
use crate::helpers::*;
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use clap_utils::{
|
||||||
|
parse_optional, parse_path_with_default_in_home_dir, parse_required, parse_ssz_optional,
|
||||||
|
};
|
||||||
use eth2_testnet_config::Eth2TestnetConfig;
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use types::{EthSpec, YamlConfig};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use types::{Address, EthSpec, YamlConfig};
|
||||||
|
|
||||||
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
||||||
let testnet_dir_path = parse_path_with_default_in_home_dir(
|
let testnet_dir_path = parse_path_with_default_in_home_dir(
|
||||||
@ -10,18 +13,18 @@ pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
|||||||
"testnet-dir",
|
"testnet-dir",
|
||||||
PathBuf::from(".lighthouse/testnet"),
|
PathBuf::from(".lighthouse/testnet"),
|
||||||
)?;
|
)?;
|
||||||
let min_genesis_time = parse_u64_opt(matches, "min-genesis-time")?;
|
let min_genesis_time = parse_optional(matches, "min-genesis-time")?;
|
||||||
let min_genesis_delay = parse_u64(matches, "min-genesis-delay")?;
|
let min_genesis_delay = parse_required(matches, "min-genesis-delay")?;
|
||||||
let min_genesis_active_validator_count =
|
let min_genesis_active_validator_count =
|
||||||
parse_u64(matches, "min-genesis-active-validator-count")?;
|
parse_required(matches, "min-genesis-active-validator-count")?;
|
||||||
let min_deposit_amount = parse_u64(matches, "min-deposit-amount")?;
|
let min_deposit_amount = parse_required(matches, "min-deposit-amount")?;
|
||||||
let max_effective_balance = parse_u64(matches, "max-effective-balance")?;
|
let max_effective_balance = clap_utils::parse_required(matches, "max-effective-balance")?;
|
||||||
let effective_balance_increment = parse_u64(matches, "effective-balance-increment")?;
|
let effective_balance_increment = parse_required(matches, "effective-balance-increment")?;
|
||||||
let ejection_balance = parse_u64(matches, "ejection-balance")?;
|
let ejection_balance = parse_required(matches, "ejection-balance")?;
|
||||||
let eth1_follow_distance = parse_u64(matches, "eth1-follow-distance")?;
|
let eth1_follow_distance = parse_required(matches, "eth1-follow-distance")?;
|
||||||
let deposit_contract_deploy_block = parse_u64(matches, "deposit-contract-deploy-block")?;
|
let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?;
|
||||||
let genesis_fork_version = parse_fork_opt(matches, "genesis-fork-version")?;
|
let genesis_fork_version = parse_ssz_optional::<[u8; 4]>(matches, "genesis-fork-version")?;
|
||||||
let deposit_contract_address = parse_address(matches, "deposit-contract-address")?;
|
let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
|
||||||
|
|
||||||
if testnet_dir_path.exists() {
|
if testnet_dir_path.exists() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@ -57,3 +60,10 @@ pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
|||||||
|
|
||||||
testnet.write_to_file(testnet_dir_path)
|
testnet.write_to_file(testnet_dir_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn time_now() -> Result<u64, String> {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map(|duration| duration.as_secs())
|
||||||
|
.map_err(|e| format!("Unable to get time: {:?}", e))
|
||||||
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use crate::deploy_deposit_contract::parse_password;
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use environment::Environment;
|
use environment::Environment;
|
||||||
use eth2_testnet_config::Eth2TestnetConfig;
|
use futures::Future;
|
||||||
use futures::{future, Future};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
use web3::{
|
use web3::{
|
||||||
transports::Http,
|
transports::Ipc,
|
||||||
types::{Address, TransactionRequest, U256},
|
types::{Address, TransactionRequest, U256},
|
||||||
Web3,
|
Web3,
|
||||||
};
|
};
|
||||||
@ -15,102 +13,28 @@ use web3::{
|
|||||||
pub const STEAL_FN_SIGNATURE: &[u8] = &[0xcf, 0x7a, 0x89, 0x65];
|
pub const STEAL_FN_SIGNATURE: &[u8] = &[0xcf, 0x7a, 0x89, 0x65];
|
||||||
|
|
||||||
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
||||||
let endpoint = matches
|
let eth1_ipc_path: PathBuf = clap_utils::parse_required(matches, "eth1-ipc")?;
|
||||||
.value_of("eth1-endpoint")
|
let from: Address = clap_utils::parse_required(matches, "from-address")?;
|
||||||
.ok_or_else(|| "eth1-endpoint not specified")?;
|
let contract_address: Address = clap_utils::parse_required(matches, "contract-address")?;
|
||||||
|
|
||||||
let account_index = matches
|
let (_event_loop_handle, transport) =
|
||||||
.value_of("account-index")
|
Ipc::new(eth1_ipc_path).map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
|
||||||
.ok_or_else(|| "No account-index".to_string())?
|
let web3 = Web3::new(transport);
|
||||||
.parse::<usize>()
|
|
||||||
.map_err(|e| format!("Unable to parse account-index: {}", e))?;
|
|
||||||
|
|
||||||
let password_opt = parse_password(matches)?;
|
env.runtime().block_on(
|
||||||
|
web3.eth()
|
||||||
let testnet_dir = matches
|
.send_transaction(TransactionRequest {
|
||||||
.value_of("testnet-dir")
|
|
||||||
.ok_or_else(|| ())
|
|
||||||
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
dirs::home_dir()
|
|
||||||
.map(|home| home.join(".lighthouse").join("testnet"))
|
|
||||||
.expect("should locate home directory")
|
|
||||||
});
|
|
||||||
|
|
||||||
let eth2_testnet_config: Eth2TestnetConfig<T> = Eth2TestnetConfig::load(testnet_dir)?;
|
|
||||||
|
|
||||||
let (_event_loop, transport) = Http::new(&endpoint).map_err(|e| {
|
|
||||||
format!(
|
|
||||||
"Failed to start HTTP transport connected to ganache: {:?}",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let web3_1 = Web3::new(transport);
|
|
||||||
let web3_2 = web3_1.clone();
|
|
||||||
|
|
||||||
// Convert from `types::Address` to `web3::types::Address`.
|
|
||||||
let deposit_contract = Address::from_slice(
|
|
||||||
eth2_testnet_config
|
|
||||||
.deposit_contract_address()?
|
|
||||||
.as_fixed_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let future = web3_1
|
|
||||||
.eth()
|
|
||||||
.accounts()
|
|
||||||
.map_err(|e| format!("Failed to get accounts: {:?}", e))
|
|
||||||
.and_then(move |accounts| {
|
|
||||||
accounts
|
|
||||||
.get(account_index)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| "Insufficient accounts for deposit".to_string())
|
|
||||||
})
|
|
||||||
.and_then(move |from_address| {
|
|
||||||
let future: Box<dyn Future<Item = Address, Error = String> + Send> =
|
|
||||||
if let Some(password) = password_opt {
|
|
||||||
// Unlock for only a single transaction.
|
|
||||||
let duration = None;
|
|
||||||
|
|
||||||
let future = web3_1
|
|
||||||
.personal()
|
|
||||||
.unlock_account(from_address, &password, duration)
|
|
||||||
.then(move |result| match result {
|
|
||||||
Ok(true) => Ok(from_address),
|
|
||||||
Ok(false) => Err("Eth1 node refused to unlock account".to_string()),
|
|
||||||
Err(e) => Err(format!("Eth1 unlock request failed: {:?}", e)),
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::new(future)
|
|
||||||
} else {
|
|
||||||
Box::new(future::ok(from_address))
|
|
||||||
};
|
|
||||||
|
|
||||||
future
|
|
||||||
})
|
|
||||||
.and_then(move |from| {
|
|
||||||
let tx_request = TransactionRequest {
|
|
||||||
from,
|
from,
|
||||||
to: Some(deposit_contract),
|
to: Some(contract_address),
|
||||||
gas: Some(U256::from(400_000)),
|
gas: Some(U256::from(400_000)),
|
||||||
gas_price: None,
|
gas_price: None,
|
||||||
value: Some(U256::zero()),
|
value: Some(U256::zero()),
|
||||||
data: Some(STEAL_FN_SIGNATURE.into()),
|
data: Some(STEAL_FN_SIGNATURE.into()),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
};
|
})
|
||||||
|
.map_err(|e| format!("Failed to call deposit fn: {:?}", e)),
|
||||||
web3_2
|
)?;
|
||||||
.eth()
|
|
||||||
.send_transaction(tx_request)
|
|
||||||
.map_err(|e| format!("Failed to call deposit fn: {:?}", e))
|
|
||||||
})
|
|
||||||
.map(move |tx| info!("Refund transaction submitted: eth1_tx_hash: {:?}", tx))
|
|
||||||
.map_err(move |e| error!("Unable to submit refund transaction: error: {}", e));
|
|
||||||
|
|
||||||
env.runtime()
|
|
||||||
.block_on(future)
|
|
||||||
.map_err(|()| "Failed to send transaction".to_string())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -19,3 +19,4 @@ environment = { path = "./environment" }
|
|||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
validator_client = { "path" = "../validator_client" }
|
validator_client = { "path" = "../validator_client" }
|
||||||
account_manager = { "path" = "../account_manager" }
|
account_manager = { "path" = "../account_manager" }
|
||||||
|
clap_utils = { path = "../eth2/utils/clap_utils" }
|
||||||
|
@ -28,6 +28,7 @@ pub struct EnvironmentBuilder<E: EthSpec> {
|
|||||||
log: Option<Logger>,
|
log: Option<Logger>,
|
||||||
eth_spec_instance: E,
|
eth_spec_instance: E,
|
||||||
eth2_config: Eth2Config,
|
eth2_config: Eth2Config,
|
||||||
|
testnet: Option<Eth2TestnetConfig<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnvironmentBuilder<MinimalEthSpec> {
|
impl EnvironmentBuilder<MinimalEthSpec> {
|
||||||
@ -38,6 +39,7 @@ impl EnvironmentBuilder<MinimalEthSpec> {
|
|||||||
log: None,
|
log: None,
|
||||||
eth_spec_instance: MinimalEthSpec,
|
eth_spec_instance: MinimalEthSpec,
|
||||||
eth2_config: Eth2Config::minimal(),
|
eth2_config: Eth2Config::minimal(),
|
||||||
|
testnet: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,6 +52,7 @@ impl EnvironmentBuilder<MainnetEthSpec> {
|
|||||||
log: None,
|
log: None,
|
||||||
eth_spec_instance: MainnetEthSpec,
|
eth_spec_instance: MainnetEthSpec,
|
||||||
eth2_config: Eth2Config::mainnet(),
|
eth2_config: Eth2Config::mainnet(),
|
||||||
|
testnet: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,6 +65,7 @@ impl EnvironmentBuilder<InteropEthSpec> {
|
|||||||
log: None,
|
log: None,
|
||||||
eth_spec_instance: InteropEthSpec,
|
eth_spec_instance: InteropEthSpec,
|
||||||
eth2_config: Eth2Config::interop(),
|
eth2_config: Eth2Config::interop(),
|
||||||
|
testnet: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +144,7 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
/// Setups eth2 config using the CLI arguments.
|
/// Setups eth2 config using the CLI arguments.
|
||||||
pub fn eth2_testnet_config(
|
pub fn eth2_testnet_config(
|
||||||
mut self,
|
mut self,
|
||||||
eth2_testnet_config: &Eth2TestnetConfig<E>,
|
eth2_testnet_config: Eth2TestnetConfig<E>,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
// Create a new chain spec from the default configuration.
|
// Create a new chain spec from the default configuration.
|
||||||
self.eth2_config.spec = eth2_testnet_config
|
self.eth2_config.spec = eth2_testnet_config
|
||||||
@ -155,6 +159,8 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
self.testnet = Some(eth2_testnet_config);
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +175,7 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
.ok_or_else(|| "Cannot build environment without log".to_string())?,
|
.ok_or_else(|| "Cannot build environment without log".to_string())?,
|
||||||
eth_spec_instance: self.eth_spec_instance,
|
eth_spec_instance: self.eth_spec_instance,
|
||||||
eth2_config: self.eth2_config,
|
eth2_config: self.eth2_config,
|
||||||
|
testnet: self.testnet,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,6 +218,7 @@ pub struct Environment<E: EthSpec> {
|
|||||||
log: Logger,
|
log: Logger,
|
||||||
eth_spec_instance: E,
|
eth_spec_instance: E,
|
||||||
pub eth2_config: Eth2Config,
|
pub eth2_config: Eth2Config,
|
||||||
|
pub testnet: Option<Eth2TestnetConfig<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> Environment<E> {
|
impl<E: EthSpec> Environment<E> {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
use beacon_node::{get_eth2_testnet_config, get_testnet_dir, ProductionBeaconNode};
|
use beacon_node::ProductionBeaconNode;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
|
use clap_utils;
|
||||||
use env_logger::{Builder, Env};
|
use env_logger::{Builder, Env};
|
||||||
use environment::EnvironmentBuilder;
|
use environment::EnvironmentBuilder;
|
||||||
use slog::{crit, info, warn};
|
use slog::{crit, info, warn};
|
||||||
@ -123,12 +124,13 @@ fn run<E: EthSpec>(
|
|||||||
.ok_or_else(|| "Expected --debug-level flag".to_string())?;
|
.ok_or_else(|| "Expected --debug-level flag".to_string())?;
|
||||||
|
|
||||||
let log_format = matches.value_of("log-format");
|
let log_format = matches.value_of("log-format");
|
||||||
let eth2_testnet_config = get_eth2_testnet_config(&get_testnet_dir(matches))?;
|
let eth2_testnet_config =
|
||||||
|
clap_utils::parse_testnet_dir_with_hardcoded_default(matches, "testnet-dir")?;
|
||||||
|
|
||||||
let mut environment = environment_builder
|
let mut environment = environment_builder
|
||||||
.async_logger(debug_level, log_format)?
|
.async_logger(debug_level, log_format)?
|
||||||
.multi_threaded_tokio_runtime()?
|
.multi_threaded_tokio_runtime()?
|
||||||
.eth2_testnet_config(ð2_testnet_config)?
|
.eth2_testnet_config(eth2_testnet_config)?
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let log = environment.core_context().log;
|
let log = environment.core_context().log;
|
||||||
@ -164,7 +166,7 @@ fn run<E: EthSpec>(
|
|||||||
|
|
||||||
if let Some(sub_matches) = matches.subcommand_matches("account_manager") {
|
if let Some(sub_matches) = matches.subcommand_matches("account_manager") {
|
||||||
// Pass the entire `environment` to the account manager so it can run blocking operations.
|
// Pass the entire `environment` to the account manager so it can run blocking operations.
|
||||||
account_manager::run(sub_matches, environment);
|
account_manager::run(sub_matches, environment)?;
|
||||||
|
|
||||||
// Exit as soon as account manager returns control.
|
// Exit as soon as account manager returns control.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -5,7 +5,7 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
web3 = "0.8.0"
|
web3 = "0.10.0"
|
||||||
tokio = "0.1.22"
|
tokio = "0.1.22"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
types = { path = "../../eth2/types"}
|
types = { path = "../../eth2/types"}
|
||||||
|
@ -41,3 +41,4 @@ bls = { path = "../eth2/utils/bls" }
|
|||||||
remote_beacon_node = { path = "../eth2/utils/remote_beacon_node" }
|
remote_beacon_node = { path = "../eth2/utils/remote_beacon_node" }
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
|
web3 = "0.10.0"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use bls::get_withdrawal_credentials;
|
use bls::get_withdrawal_credentials;
|
||||||
use deposit_contract::encode_eth1_tx_data;
|
use deposit_contract::{encode_eth1_tx_data, DEPOSIT_GAS};
|
||||||
|
use futures::{Future, IntoFuture};
|
||||||
use hex;
|
use hex;
|
||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
@ -12,6 +13,10 @@ use types::{
|
|||||||
test_utils::generate_deterministic_keypair, ChainSpec, DepositData, Hash256, Keypair,
|
test_utils::generate_deterministic_keypair, ChainSpec, DepositData, Hash256, Keypair,
|
||||||
PublicKey, SecretKey, Signature,
|
PublicKey, SecretKey, Signature,
|
||||||
};
|
};
|
||||||
|
use web3::{
|
||||||
|
types::{Address, TransactionRequest, U256},
|
||||||
|
Transport, Web3,
|
||||||
|
};
|
||||||
|
|
||||||
const VOTING_KEY_PREFIX: &str = "voting";
|
const VOTING_KEY_PREFIX: &str = "voting";
|
||||||
const WITHDRAWAL_KEY_PREFIX: &str = "withdrawal";
|
const WITHDRAWAL_KEY_PREFIX: &str = "withdrawal";
|
||||||
@ -241,7 +246,7 @@ impl ValidatorDirectoryBuilder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_eth1_data_file(mut self) -> Result<Self, String> {
|
fn get_deposit_data(&self) -> Result<(Vec<u8>, u64), String> {
|
||||||
let voting_keypair = self
|
let voting_keypair = self
|
||||||
.voting_keypair
|
.voting_keypair
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -254,30 +259,35 @@ impl ValidatorDirectoryBuilder {
|
|||||||
.amount
|
.amount
|
||||||
.ok_or_else(|| "write_eth1_data_file requires an amount")?;
|
.ok_or_else(|| "write_eth1_data_file requires an amount")?;
|
||||||
let spec = self.spec.as_ref().ok_or_else(|| "build requires a spec")?;
|
let spec = self.spec.as_ref().ok_or_else(|| "build requires a spec")?;
|
||||||
|
|
||||||
|
let withdrawal_credentials = Hash256::from_slice(&get_withdrawal_credentials(
|
||||||
|
&withdrawal_keypair.pk,
|
||||||
|
spec.bls_withdrawal_prefix_byte,
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut deposit_data = DepositData {
|
||||||
|
pubkey: voting_keypair.pk.clone().into(),
|
||||||
|
withdrawal_credentials,
|
||||||
|
amount,
|
||||||
|
signature: Signature::empty_signature().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
deposit_data.signature = deposit_data.create_signature(&voting_keypair.sk, &spec);
|
||||||
|
|
||||||
|
let deposit_data = encode_eth1_tx_data(&deposit_data)
|
||||||
|
.map_err(|e| format!("Unable to encode eth1 deposit tx data: {:?}", e))?;
|
||||||
|
|
||||||
|
Ok((deposit_data, amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_eth1_data_file(mut self) -> Result<Self, String> {
|
||||||
let path = self
|
let path = self
|
||||||
.directory
|
.directory
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|directory| directory.join(ETH1_DEPOSIT_DATA_FILE))
|
.map(|directory| directory.join(ETH1_DEPOSIT_DATA_FILE))
|
||||||
.ok_or_else(|| "write_eth1_data_filer requires a directory")?;
|
.ok_or_else(|| "write_eth1_data_filer requires a directory")?;
|
||||||
|
|
||||||
let deposit_data = {
|
let (deposit_data, _) = self.get_deposit_data()?;
|
||||||
let withdrawal_credentials = Hash256::from_slice(&get_withdrawal_credentials(
|
|
||||||
&withdrawal_keypair.pk,
|
|
||||||
spec.bls_withdrawal_prefix_byte,
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut deposit_data = DepositData {
|
|
||||||
pubkey: voting_keypair.pk.clone().into(),
|
|
||||||
withdrawal_credentials,
|
|
||||||
amount,
|
|
||||||
signature: Signature::empty_signature().into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
deposit_data.signature = deposit_data.create_signature(&voting_keypair.sk, &spec);
|
|
||||||
|
|
||||||
encode_eth1_tx_data(&deposit_data)
|
|
||||||
.map_err(|e| format!("Unable to encode eth1 deposit tx data: {:?}", e))?
|
|
||||||
};
|
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
return Err(format!("Eth1 data file already exists at: {:?}", path));
|
return Err(format!("Eth1 data file already exists at: {:?}", path));
|
||||||
@ -293,6 +303,31 @@ impl ValidatorDirectoryBuilder {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn submit_eth1_deposit<T: Transport>(
|
||||||
|
self,
|
||||||
|
web3: Web3<T>,
|
||||||
|
from: Address,
|
||||||
|
deposit_contract: Address,
|
||||||
|
) -> impl Future<Item = Self, Error = String> {
|
||||||
|
self.get_deposit_data()
|
||||||
|
.into_future()
|
||||||
|
.and_then(move |(deposit_data, deposit_amount)| {
|
||||||
|
web3.eth()
|
||||||
|
.send_transaction(TransactionRequest {
|
||||||
|
from,
|
||||||
|
to: Some(deposit_contract),
|
||||||
|
gas: Some(DEPOSIT_GAS.into()),
|
||||||
|
gas_price: None,
|
||||||
|
value: Some(from_gwei(deposit_amount)),
|
||||||
|
data: Some(deposit_data.into()),
|
||||||
|
nonce: None,
|
||||||
|
condition: None,
|
||||||
|
})
|
||||||
|
.map_err(|e| format!("Failed to send transaction: {:?}", e))
|
||||||
|
})
|
||||||
|
.map(|_tx| self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<ValidatorDirectory, String> {
|
pub fn build(self) -> Result<ValidatorDirectory, String> {
|
||||||
Ok(ValidatorDirectory {
|
Ok(ValidatorDirectory {
|
||||||
directory: self.directory.ok_or_else(|| "build requires a directory")?,
|
directory: self.directory.ok_or_else(|| "build requires a directory")?,
|
||||||
@ -303,6 +338,11 @@ impl ValidatorDirectoryBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts gwei to wei.
|
||||||
|
fn from_gwei(gwei: u64) -> U256 {
|
||||||
|
U256::from(gwei) * U256::exp10(9)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user