diff --git a/.gitignore b/.gitignore index d7dc17eed2..bc75674a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ tags .vagrant # runtime folder -/nodes/ +/ckb.toml +/ckb-miner.toml +/data +/specs diff --git a/.travis.yml b/.travis.yml index a312748972..073cc2780c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ git: depth: 2 submodules: false -if: 'branch IN (master, develop, staging, trying) OR type != push OR fork = true OR tag =~ ^v' +if: 'branch IN (master, develop, staging, trying) OR type != push OR fork = true OR tag IS present' env: global: @@ -18,10 +18,10 @@ matrix: include: - rust: 1.33.0 os: osx - env: FMT=true CHECK=true TEST=true + env: FMT=true CHECK=true TEST=true REL_PKG=darwin_amd64.zip - rust: 1.33.0 os: linux - env: TEST=true + env: TEST=true REL_PKG=linux_amd64.tar.gz addons: apt: @@ -37,6 +37,16 @@ before_install: if [ "$TRAVIS_OS_NAME" = "osx" ]; then ulimit -n 8192; fi install: ./devtools/ci/install.sh script: ./devtools/ci/script.sh +deploy: + provider: releases + api_key: "$GITHUB_TOKEN" + file: "releases/ckb_${TRAVIS_TAG}_${REL_PKG}" + skip_cleanup: true + draft: true + on: + tags: true + condition: '"$GITHUB_TOKEN" != "" && "$REL_PKG" != ""' + before_cache: - rm -rf ./target/debug/incremental/ - cargo sweep -f diff --git a/Cargo.lock b/Cargo.lock index 9e2452b90a..bda451ea0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,17 +316,16 @@ dependencies = [ "ckb-network 0.9.0-pre", "ckb-notify 0.9.0-pre", "ckb-pow 0.9.0-pre", + "ckb-resource 0.9.0-pre", "ckb-rpc 0.9.0-pre", "ckb-shared 0.9.0-pre", "ckb-sync 0.9.0-pre", "ckb-traits 0.9.0-pre", "ckb-util 0.9.0-pre", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "config 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "crypto 0.9.0-pre", "ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dir 0.9.0-pre", "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "hash 0.9.0-pre", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -392,11 +391,9 @@ version = "0.9.0-pre" dependencies = [ "ckb-core 0.9.0-pre", "ckb-pow 0.9.0-pre", - "includedir 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "includedir_codegen 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ckb-resource 0.9.0-pre", "numext-fixed-hash 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "numext-fixed-uint 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -518,11 +515,11 @@ dependencies = [ "snap 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "stop-handler 0.9.0-pre", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", - "tentacle-discovery 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", - "tentacle-identify 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", - "tentacle-ping 0.2.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", - "tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", + "tentacle-discovery 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", + "tentacle-identify 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", + "tentacle-ping 0.2.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", + "tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "unsigned-varint 0.2.2 (git+https://github.com/paritytech/unsigned-varint)", ] @@ -571,6 +568,17 @@ dependencies = [ "siphasher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ckb-resource" +version = "0.9.0-pre" +dependencies = [ + "includedir 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "includedir_codegen 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ckb-rpc" version = "0.9.0-pre" @@ -778,21 +786,6 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "config" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", - "serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "console" version = "0.7.5" @@ -1069,10 +1062,6 @@ dependencies = [ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dir" -version = "0.9.0-pre" - [[package]] name = "dtoa" version = "0.4.3" @@ -1156,6 +1145,11 @@ name = "faster-hex" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "flatbuffers" version = "0.5.0" @@ -1562,11 +1556,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "1.3.0" @@ -1612,15 +1601,6 @@ dependencies = [ "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "linked-hash-map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "linked-hash-map" version = "0.5.1" @@ -1856,14 +1836,6 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "num-traits" version = "0.2.6" @@ -2017,6 +1989,11 @@ dependencies = [ "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "owning_ref" version = "0.4.0" @@ -2066,10 +2043,13 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2083,6 +2063,15 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.24" @@ -2473,11 +2462,6 @@ dependencies = [ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rust-ini" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rustc-demangle" version = "0.1.13" @@ -2636,11 +2620,6 @@ dependencies = [ "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" version = "1.0.90" @@ -2649,18 +2628,6 @@ dependencies = [ "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde-hjson" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "serde_bytes" version = "0.11.1" @@ -2689,14 +2656,6 @@ dependencies = [ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde_test" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "serde_urlencoded" version = "0.5.4" @@ -2870,23 +2829,23 @@ dependencies = [ [[package]] name = "tentacle" version = "0.2.0-alpha.1" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-yamux 0.1.4 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tokio-yamux 0.1.4 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", ] [[package]] name = "tentacle-discovery" version = "0.1.0" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2897,7 +2856,7 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", - "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2905,32 +2864,32 @@ dependencies = [ [[package]] name = "tentacle-identify" version = "0.1.0" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tentacle-ping" version = "0.2.0" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "generic-channel 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)", + "tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)", ] [[package]] name = "tentacle-secio" version = "0.1.0" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2984,6 +2943,16 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -3200,7 +3169,7 @@ dependencies = [ [[package]] name = "tokio-yamux" version = "0.1.4" -source = "git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec#ab661f065dc8667a04f12122250f5fb759872dec" +source = "git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95#53cb765b94041543a9c8582aa4d0d34fb2ac6d95" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3210,14 +3179,6 @@ dependencies = [ "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "toml" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "toml" version = "0.5.0" @@ -3521,14 +3482,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "yaml-rust" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [metadata] "checksum MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" @@ -3572,7 +3525,6 @@ dependencies = [ "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum config 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e82d07fac0a5eeaa9d959b5194d01bb66e414665f547416958d2b430f8f4852" "checksum console 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2bf3720d3f3fc30b721ef1ae54e13af3264af4af39dc476a8de56a6ee1e2184b" "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" @@ -3612,6 +3564,7 @@ dependencies = [ "checksum faketime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17087bd8c5a4a3e8bd40ecd9d3bda587459abcf67ca94211df09ec8451404cf8" "checksum faster-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea2e4ecc921ec2cbb8b10f0d400fe448554779d2ac5e0bfbb065836d9d8483a" "checksum faster-hex 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8cccaafb5aae8c282692e5590f341925edea6c696e8715ff0d973320b2646" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum flatbuffers 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea0c34f669be9911826facafe996adfda978aeee67285a13556869e2d8b8331f" "checksum flatbuffers-verifier 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "228f0226833a59e66d5ba28d15fca4efce5d598314d56179b22b3d89fbb6bf10" "checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" @@ -3653,14 +3606,12 @@ dependencies = [ "checksum jsonrpc-http-server 10.0.1 (git+https://github.com/nervosnetwork/jsonrpc?branch=http_remake)" = "" "checksum jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9527f01ef25f251d64082cbefc0c6d6f367349afe6848ef908a674e06b2bdd3" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" "checksum librocksdb-sys 5.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfb546562f9b450237bb8df7a31961849ee9fb1186d9e356db1d7a6b7609ff2" "checksum libsqlite3-sys 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3567bc1a0c84e2c0d71eeb4a1f08451babf7843babd733158777d9c686dad9f3" -"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum linked-hash-map 0.5.1 (git+https://github.com/nervosnetwork/linked-hash-map?rev=df27f21)" = "" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" @@ -3686,7 +3637,6 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" @@ -3701,6 +3651,7 @@ dependencies = [ "checksum openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0d6b781aac4ac1bd6cafe2a2f0ad8c16ae8e1dd5184822a16c50139f8838d9" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)" = "33c86834957dd5b915623e94f2f4ab2c70dd8f6b70679824155d5ae21dbd495d" +"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61ae6944d4435d41f4d0f12108c5cbb9207cbb14bc8f2b4984c6e930dc9c8e41" "checksum parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e8eab0287ccde7821e337a124dc5a4f1d6e4c25d10cc91e3f9361615dd95076" @@ -3708,6 +3659,7 @@ dependencies = [ "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" @@ -3749,7 +3701,6 @@ dependencies = [ "checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" "checksum rocksdb 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3eca7dfb97566985090e6bc4a529af42d0adda683d346a024104ee1b1932e340" "checksum rusqlite 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6381ddfe91dbb659b4b132168da15985bc84162378cf4fcdc4eb99c857d063e2" -"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c" @@ -3767,13 +3718,10 @@ dependencies = [ "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum sentry 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00dc2223b4c6de017ea99ce854c35019f92d2775e4b734b39342ff6cb35aced5" "checksum sentry-types 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b23e3d9c8c6e4a1523f24df6753c4088bfe16c44a73c8881c1d23c70f28ae280" -"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" "checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" -"checksum serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" "checksum serde_bytes 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aaff47db6ef8771cca5d88febef2f22f47f645420e51226374049f68c6b08569" "checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" @@ -3795,15 +3743,16 @@ dependencies = [ "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" -"checksum tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" -"checksum tentacle-discovery 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" -"checksum tentacle-identify 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" -"checksum tentacle-ping 0.2.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" -"checksum tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" +"checksum tentacle 0.2.0-alpha.1 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" +"checksum tentacle-discovery 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" +"checksum tentacle-identify 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" +"checksum tentacle-ping 0.2.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" +"checksum tentacle-secio 0.1.0 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" @@ -3822,8 +3771,7 @@ dependencies = [ "checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum tokio-yamux 0.1.4 (git+https://github.com/nervosnetwork/p2p?rev=ab661f065dc8667a04f12122250f5fb759872dec)" = "" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum tokio-yamux 0.1.4 (git+https://github.com/nervosnetwork/p2p?rev=53cb765b94041543a9c8582aa4d0d34fb2ac6d95)" = "" "checksum toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c5890a989fa47ecdc7bcb4c63a77a82c18f306714104b1decfd722db17b39e" "checksum trust-dns 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "65096825b064877da37eeeb9a83390bd23433eabfc503a6476dc5b1949034aa7" "checksum trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" @@ -3863,4 +3811,3 @@ dependencies = [ "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/Cargo.toml b/Cargo.toml index b6cd1ee1fb..f0f1539c6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ toml = "0.5" log = "0.4" futures = "0.1" crossbeam-channel = "0.3" -config-tool = { package= "config", version = "0.9" } ckb-util = { path = "util" } ckb-core = { path = "core" } ckb-chain = { path = "chain" } @@ -26,10 +25,10 @@ ckb-db = { path = "db" } ckb-pow = { path = "pow" } ckb-network = { path = "network"} ckb-rpc = { path = "rpc"} +ckb-resource = { path = "resource"} logger = { path = "util/logger" } numext-fixed-hash = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"] } numext-fixed-uint = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"] } -dir = { path = "util/dir" } ctrlc = { version = "3.1", features = ["termination"] } ckb-sync = { path = "sync"} crypto = { path = "util/crypto"} @@ -52,7 +51,6 @@ members = [ "util/merkle-tree", "util/jsonrpc-types", "util/crypto", - "util/dir", "util/instrument", "util/build-info", "util/occupied-capacity", @@ -72,7 +70,8 @@ members = [ "verification", "script", "pow", - "benches" + "benches", + "resource", ] [profile.release] diff --git a/Makefile b/Makefile index f52adb262e..2ab8a18f28 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,11 @@ check-cfbc-version: $(CFBC) -o $(shell dirname $@) $*.bfbs rm -f $*.bfbs $*_builder.rs -.PHONY: build prod prod-test docker gen gen-clean check-cfbc-version +clean: + rm -rf ckb.toml ckb-miner.toml specs/ + +.PHONY: build prod prod-test docker +.PHONY: gen gen-clean clean check-cfbc-version .PHONY: fmt test clippy doc doc-deps check stats .PHONY: ci info security-audit .PHONY: integration integration-release diff --git a/README.md b/README.md index 20ff4122f5..bbfdf22bd6 100644 --- a/README.md +++ b/README.md @@ -34,126 +34,9 @@ The contribution workflow is described in [CONTRIBUTING.md](CONTRIBUTING.md), an --- -## Build dependencies +## Documentations -CKB is currently tested mainly with `stable-1.33.0` on Linux and Mac OSX. - -We recommend installing Rust through [rustup](https://www.rustup.rs/) - -```bash -# Get rustup from rustup.rs, then in your `ckb` folder: -rustup override set 1.33.0 -rustup component add rustfmt -rustup component add clippy -``` - -Report new breakage is welcome. - -You also need to get the following packages: - -* Ubuntu and Debian: - -```shell -sudo apt-get install git gcc libc6-dev pkg-config libssl-dev libclang-dev clang -``` - -* Arch Linux - -```shell -sudo pacman -Sy git gcc pkgconf clang -``` - -* macOS: - -```shell -brew install autoconf libtool -``` - ---- - -## Build from source & testing - -```bash -# get ckb source code -git clone https://github.com/nervosnetwork/ckb.git -cd ckb - -# build in release mode -make build -``` - -You can run the full test suite, or just run a specific package test: - -```bash -# Run the full suite -cargo test --all -# Run a specific package test -cargo test -p ckb-chain -``` - ---- - -## Quick Start - -### Start Node - -Create the default runtime directory: - -```shell -cp -r nodes_template/ nodes -``` - -Use the config file to start the node - -```shell -target/release/ckb run -``` - -It searches config file `ckb.toml`, `nodes/default.toml` in the shell -working directory in that order. Alternatively, the argument `-c` can specify -the config file used to start the node. - -The default config file saves data in `nodes/default/`. - -### Use RPC - -Find RPC port in the log output, the following command assumes 8114 is used: - -```shell -curl -d '{"id": 1, "jsonrpc": "2.0", "method":"get_tip_header","params": []}' \ - -H 'content-type:application/json' 'http://localhost:8114' -``` - -### Run Miner - -Run miner, gets a block template to mine. - -```shell -target/release/ckb miner -``` - -### Advanced - -Run multiple nodes in different data directories. - -Create the config file for new nodes, for example: - -```shell -cp nodes/default.toml nodes/node2.toml -``` - -Update `data_dir` configuration in config file to a different directory. - -``` -data_dir = "node2" -``` - -Then start the new node using the new config file - -```shell -target/release/ckb run -c nodes/node2.toml -``` - -The option `ckb.chain` configures the chain spec. It accepts a path to the spec toml file. The directory `nodes_template/spec` has all the pre-defined specs. Please note that nodes with different chain specs may fail to connect with each other. - -The chain spec can switch between different PoW engines. Wiki has the [instructions](https://github.com/nervosnetwork/ckb/wiki/PoW-Engines) about how to configure it. +- [Get CKB](docs/get-ckb.md) +- [Quick Start](docs/quick-start.md) +- [Configure CKB](docs/configure.md) +- [CKB Core Development](docs/ckb-core-dev.md) diff --git a/core/src/script.rs b/core/src/script.rs index 5ecd364684..8a05bc2fb6 100644 --- a/core/src/script.rs +++ b/core/src/script.rs @@ -94,7 +94,7 @@ mod tests { #[test] fn always_success_script_hash() { - let always_success = include_bytes!("../../nodes_template/spec/cells/always_success"); + let always_success = include_bytes!("../../resource/specs/cells/always_success"); let always_success_hash: H256 = (&blake2b_256(&always_success[..])).into(); let script = Script::new(vec![], always_success_hash); diff --git a/db/src/config.rs b/db/src/config.rs index fff1abc9d3..6c9bea5df8 100644 --- a/db/src/config.rs +++ b/db/src/config.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; #[derive(Clone, Debug, Deserialize, Default)] pub struct DBConfig { + #[serde(default)] pub path: PathBuf, pub options: Option>, } diff --git a/devtools/ci/script.sh b/devtools/ci/script.sh index 1e42202152..1f7a64c890 100755 --- a/devtools/ci/script.sh +++ b/devtools/ci/script.sh @@ -24,3 +24,25 @@ if [ "$TRAVIS_BRANCH" = master -o "$TRAVIS_BRANCH" = staging -o "$TRAVIS_BRANCH" # Switch to release mode when the running time is much longer than the build time. # make integration-release fi + +if [ -n "$TRAVIS_TAG" -a -n "$GITHUB_TOKEN" -a -n "$REL_PKG" ]; then + make build + rm -rf releases + mkdir releases + PKG_NAME="ckb_${TRAVIS_TAG}_${REL_PKG%%.*}" + mkdir "releases/$PKG_NAME" + mv target/release/ckb "releases/$PKG_NAME" + cp README.md CHANGELOG.md COPYING "releases/$PKG_NAME" + cp -R devtools/init/ "releases/$PKG_NAME" + if [ -d docs ]; then + cp -R docs "releases/$PKG_NAME" + fi + + pushd releases + if [ "${REL_PKG#*.}" = "tar.gz" ]; then + tar -czf $PKG_NAME.tar.gz $PKG_NAME + else + zip -r $PKG_NAME.zip $PKG_NAME + fi + popd +fi diff --git a/devtools/git/bors-changelog.py b/devtools/git/bors-changelog.py index 8068b57375..2379dac955 100755 --- a/devtools/git/bors-changelog.py +++ b/devtools/git/bors-changelog.py @@ -5,16 +5,23 @@ import subprocess from collections import namedtuple, OrderedDict + +def _str(s): + if sys.version_info >= (3, 0): + return s.decode('utf-8') + return s + + if len(sys.argv) > 1: since = sys.argv[1] else: - tag_rev = subprocess.check_output( - ['git', 'rev-list', '--tags', '--max-count=1']).strip() - since = subprocess.check_output( - ['git', 'describe', '--tags', tag_rev]).strip() + tag_rev = _str(subprocess.check_output( + ['git', 'rev-list', '--tags', '--max-count=1']).strip()) + since = _str(subprocess.check_output( + ['git', 'describe', '--tags', tag_rev]).strip()) -logs = subprocess.check_output( - ['git', 'log', '--merges', '{}...HEAD'.format(since)]) +logs = _str(subprocess.check_output( + ['git', 'log', '--merges', '{}...HEAD'.format(since)])) START_RE = re.compile(r'\s+(\d+): (?:(\w+)(\([^\)]+\))?: )?(.*r=.*)') END_RE = re.compile(r'\s+Co-authored-by:') diff --git a/devtools/init/README.md b/devtools/init/README.md new file mode 100644 index 0000000000..3d9cb45285 --- /dev/null +++ b/devtools/init/README.md @@ -0,0 +1,11 @@ +# Init/Service Scripts + +This folder provides the init/service scripts to start CKB node and miner as +daemons on various Unix like distributions. + +See the README in each folder for the detailed instructions. + +## Disclaimer + +Users are expected to know how to administer their system, and these files +should be considered as only a guide or suggestion to setup CKB. diff --git a/devtools/init/linux-systemd/README.md b/devtools/init/linux-systemd/README.md new file mode 100644 index 0000000000..7b3698ff1d --- /dev/null +++ b/devtools/init/linux-systemd/README.md @@ -0,0 +1,82 @@ +# CKB systemd unit configuration + +The provided files should work with systemd version 219 or later. + +## Instructions + +The following instructions assume that: + +* you want to run ckb as user `ckb` and group `ckb`, and store data in `/var/lib/ckb`. +* you want to join testnet. +* you are logging in as a non-root user account that has `sudo` permissions to execute commands as root. + +First, get ckb and move the binary into the system binary directory, and setup the appropriate ownership and permissions: + +```bash +sudo cp /path/to/ckb /usr/local/bin +sudo chown root:root /usr/local/bin/ckb +sudo chmod 755 /usr/local/bin/ckb +``` + +Setup the directories and generate config files for testnet. + +```bash +sudo mkdir /var/lib/ckb +sudo /usr/local/bin/ckb init -C /var/lib/ckb --spec testnet --log stdout +``` + +Setup the user and group and the appropriate ownership and permissions. + +```bash +sudo groupadd ckb +sudo useradd \ + -g ckb --no-user-group \ + --home-dir /var/lib/ckb --no-create-home \ + --shell /usr/sbin/nologin \ + --system ckb + +sudo chown -R ckb:ckb /var/lib/ckb +sudo chmod 755 /var/lib/ckb +sudo chmod 644 /var/lib/ckb/ckb.toml /var/lib/ckb/ckb-miner.toml +``` + +Install the systemd service unit configuration file, reload the systemd daemon, +and start the node: + +```bash +curl -L -O https://raw.githubusercontent.com/nervosnetwork/ckb/master/devtools/init/linux-systemd/ckb.service +sudo cp ckb.service /etc/systemd/system/ +sudo chown root:root /etc/systemd/system/ckb.service +sudo chmod 644 /etc/systemd/system/ckb.service +sudo systemctl daemon-reload +sudo systemctl start ckb.service +``` + +Start the node automatically on boot if you like: + +```bash +sudo systemctl enable ckb.service +``` + +If ckb doesn't seem to start properly you can view the logs to figure out the problem: + +```bash +journalctl --boot -u ckb.service +``` + +Following the similar instructions to start a miner: + +```bash +curl -L -O https://raw.githubusercontent.com/nervosnetwork/ckb/master/devtools/init/linux-systemd/ckb-miner.service +sudo cp ckb-miner.service /etc/systemd/system/ +sudo chown root:root /etc/systemd/system/ckb-miner.service +sudo chmod 644 /etc/systemd/system/ckb-miner.service +sudo systemctl daemon-reload +sudo systemctl start ckb-miner.service +``` + +Let the miner starts automatically on boot: + +```bash +sudo systemctl enable ckb-miner.service +``` diff --git a/devtools/init/linux-systemd/ckb-miner.service b/devtools/init/linux-systemd/ckb-miner.service new file mode 100644 index 0000000000..338cd1881e --- /dev/null +++ b/devtools/init/linux-systemd/ckb-miner.service @@ -0,0 +1,21 @@ +[Unit] +Description=Nervos CKB Miner +Documentation=https://github.com/nervosnetwork/ckb +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service + +[Service] +User=ckb +Group=ckb +WorkingDirectory=/var/lib/ckb + +ExecStart=/usr/local/bin/ckb miner +Restart=on-abnormal +KillMode=mixed +TimeoutStopSec=5s + +LimitNOFILE=1048576 +LimitNPROC=512 + +[Install] +WantedBy=multi-user.target diff --git a/devtools/init/linux-systemd/ckb.service b/devtools/init/linux-systemd/ckb.service new file mode 100644 index 0000000000..7bc6894295 --- /dev/null +++ b/devtools/init/linux-systemd/ckb.service @@ -0,0 +1,21 @@ +[Unit] +Description=Nervos CKB Node +Documentation=https://github.com/nervosnetwork/ckb +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service + +[Service] +User=ckb +Group=ckb +WorkingDirectory=/var/lib/ckb + +ExecStart=/usr/local/bin/ckb +Restart=on-abnormal +KillMode=mixed +TimeoutStopSec=5s + +LimitNOFILE=1048576 +LimitNPROC=512 + +[Install] +WantedBy=multi-user.target diff --git a/docker/hub/Dockerfile b/docker/hub/Dockerfile index fe41e6d67b..e071deecd3 100644 --- a/docker/hub/Dockerfile +++ b/docker/hub/Dockerfile @@ -72,9 +72,6 @@ COPY --from=ckb-builder /ckb/target/release/ckb /bin/ckb RUN echo "#!/bin/bash \n ckb \$@" > ./entrypoint.sh RUN chmod +x ./entrypoint.sh -# copy "nodes_template" to `WORKDIR`/nodes/ -COPY --chown=ckb:ckb nodes_template/ nodes/ - #switch use USER ckb diff --git a/docs/ckb-core-dev.md b/docs/ckb-core-dev.md new file mode 100644 index 0000000000..3a645aca99 --- /dev/null +++ b/docs/ckb-core-dev.md @@ -0,0 +1,31 @@ +# CKB Core Development + +## Running Test + +Install dependencies + +``` +rustup component add rustfmt +rustup component add clippy +``` + +Run tests + +``` +make ci +``` + +Run acceptance integration tests + + +``` +cargo build +cd test && cargo run ../target/debug/ckb +``` + +## Chain Spec + +The subcommand `ckb init` has an option `--export-spec` to export spec files +as well, which allows editing the chain spec for development. + +The chain spec can switch between different PoW engines. Wiki has the [instructions](https://github.com/nervosnetwork/ckb/wiki/PoW-Engines) about how to configure it.- diff --git a/docs/configure.md b/docs/configure.md new file mode 100644 index 0000000000..a4c02da79e --- /dev/null +++ b/docs/configure.md @@ -0,0 +1,81 @@ +# Configure CKB + +## How CKB Locates Config File + +CKB looks for configuration files in ``, which is the current working directory by default. Different subcommands use different config file names: + +- `ckb run`: `ckb.toml` +- `ckb miner`: `ckb-miner.toml` +- `ckb import`: `ckb.toml` +- `ckb export`: `ckb.toml` +- `ckb cli`: no config file required yet + +Command line argument `-C ` sets the value of `` to ``, which must come before subcommand. + +If configuration file is missing, the default config files bundled in the executable will be used. + +Some config file may refer to other files, for example, `chain.spec` in +`ckb.toml` and `system_cells` in chain spec file. The file is referred via +either absolute path, or a path relative to the directory containing the +config file currently being parsed. Take following directory hierarchy as an +example: + +``` +ckb.toml +specs/dev.toml +specs/cells/always_success +``` + +Then `ckb.toml` refers `dev.toml` as `specs/dev.toml`, while +`specs/dev.toml` refers `always_success` as `cells/always_success`. + +For security reason, there is a limitation of the file reference. The bundled +file can only refer to bundled files, while a file located in the file system +can either refer to another file in the file system or a bundled one. + +## How to Change Config + +First export the bundled config files into current directory using subcommand `init`. + +``` +ckb init +``` + +Then edit the generated config files according to the in-line comments. + +## Chain Spec + +The option `ckb.chain` configures the chain spec, which controls which kind of chain to run. +This option is set to a spec used for development by default. + +The subcommand `init` supports exporting the default options for different +chain specs. The following command lists all supported chain specs. + +``` +ckb init --list-specs +``` + +Here is an example to export config files for testnet. + +``` +ckb init --spec testnet +``` + +Nodes running different chain specs cannot synchronize with each other, so be carefully when editing this option. + +## How to Run Multiple Nodes + +Each node requires its own ``. Since the default ports will conflict, please export the config files and edit the listen ports in the config files. + +``` +mkdir node1 node2 +ckb -C node1 init +ckb -C node2 init +# Change listen ports 8114/8115 to 8116/8117 in node2/ckb.toml. +# Change `rpc_url` in node2/ckb.toml to use 8116. +# You may also want to add each other as a boot node in the configuration file. +# start node1 +ckb -C node1 run +# start node2 +ckb -C node2 run +``` diff --git a/docs/get-ckb.md b/docs/get-ckb.md new file mode 100644 index 0000000000..0ff74ae7a5 --- /dev/null +++ b/docs/get-ckb.md @@ -0,0 +1,62 @@ +# Get CKB + +## Download from Releases + +We will publish binaries for each release via [Github Releases](https://github.com/nervosnetwork/ckb/releases). If your system +is listed there, you can download the package directory. + + +## Build from Source + +### Install Build Dependencies + +CKB is currently tested mainly with `stable-1.33.0` on Linux and macOS. + +We recommend installing Rust through [rustup](https://www.rustup.rs/) + +```bash +# Get rustup from rustup.rs, then in your `ckb` folder: +rustup override set 1.33.0 +``` + +Report new breakage is welcome. + +You also need to get the following packages: + +#### Ubuntu and Debian + +```shell +sudo apt-get install git gcc libc6-dev pkg-config libssl-dev libclang-dev clang +``` + +#### Arch Linux + +```shell +sudo pacman -Sy git gcc pkgconf clang +``` + +#### macOS + +```shell +brew install autoconf libtool +``` + +### Build + +```bash +# get ckb source code +git clone https://github.com/nervosnetwork/ckb.git +cd ckb + +# build in release mode +make build +``` + +This will build the executable `target/release/ckb`. Please add the directory +to `PATH` or copy/link the file into a directory already in the `PATH`. + +```base +export PATH="$(pwd)/target/release:$PATH" +# or +# ln -snf "$(pwd)/target/release/ckb" /usr/local/bin/ckb +``` diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 0000000000..206f9a3ee6 --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,29 @@ +# Quick Start + +Following steps will assume that the shell can find the executable `ckb`, see +how to [build CKB from source](build.md). + +## Start Node + +```shell +ckb run +``` + +It will start a node using the default configurations and store files in `data/dev` in current directory. + +## Use RPC + +Find RPC port in the log output, the following command assumes 8114 is used: + +```shell +curl -d '{"id": 1, "jsonrpc": "2.0", "method":"get_tip_header","params": []}' \ + -H 'content-type:application/json' 'http://localhost:8114' +``` + +## Run Miner + +Run miner, gets a block template to mine. + +```shell +ckb miner +``` diff --git a/network/Cargo.toml b/network/Cargo.toml index 5d7c770be8..75c5afa6fb 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -23,11 +23,11 @@ tokio = "0.1.18" futures = "0.1" snap = "0.2" crossbeam-channel = "0.3" -p2p = { git = "https://github.com/nervosnetwork/p2p", rev="ab661f065dc8667a04f12122250f5fb759872dec", package="tentacle" } -secio = { git = "https://github.com/nervosnetwork/p2p", rev="ab661f065dc8667a04f12122250f5fb759872dec", package="tentacle-secio" } -p2p-ping = { git = "https://github.com/nervosnetwork/p2p", rev="ab661f065dc8667a04f12122250f5fb759872dec", package="tentacle-ping" } -p2p-discovery = { git = "https://github.com/nervosnetwork/p2p", rev="ab661f065dc8667a04f12122250f5fb759872dec", package="tentacle-discovery" } -p2p-identify = { git = "https://github.com/nervosnetwork/p2p", rev="ab661f065dc8667a04f12122250f5fb759872dec", package="tentacle-identify" } +p2p = { git = "https://github.com/nervosnetwork/p2p", rev="53cb765b94041543a9c8582aa4d0d34fb2ac6d95", package="tentacle" } +secio = { git = "https://github.com/nervosnetwork/p2p", rev="53cb765b94041543a9c8582aa4d0d34fb2ac6d95", package="tentacle-secio" } +p2p-ping = { git = "https://github.com/nervosnetwork/p2p", rev="53cb765b94041543a9c8582aa4d0d34fb2ac6d95", package="tentacle-ping" } +p2p-discovery = { git = "https://github.com/nervosnetwork/p2p", rev="53cb765b94041543a9c8582aa4d0d34fb2ac6d95", package="tentacle-discovery" } +p2p-identify = { git = "https://github.com/nervosnetwork/p2p", rev="53cb765b94041543a9c8582aa4d0d34fb2ac6d95", package="tentacle-identify" } faketime = "0.2.0" rusqlite = {version = "0.16.0", features = ["bundled"]} lazy_static = "1.3.0" diff --git a/network/src/config.rs b/network/src/config.rs index 981aa5056c..1a544ee063 100644 --- a/network/src/config.rs +++ b/network/src/config.rs @@ -20,6 +20,7 @@ pub struct NetworkConfig { pub reserved_only: bool, pub max_peers: u32, pub max_outbound_peers: u32, + #[serde(default)] pub path: PathBuf, pub ping_interval_secs: u64, pub ping_timeout_secs: u64, diff --git a/network/src/network.rs b/network/src/network.rs index 558a14e743..471202e1d2 100644 --- a/network/src/network.rs +++ b/network/src/network.rs @@ -339,33 +339,47 @@ pub struct EventHandler { } impl ServiceHandle for EventHandler { - fn handle_error(&mut self, _context: &mut ServiceContext, error: ServiceError) { + fn handle_error(&mut self, context: &mut ServiceContext, error: ServiceError) { warn!(target: "network", "p2p service error: {:?}", error); - if let ServiceError::DialerError { - ref address, - ref error, - } = error - { - debug!(target: "network", "add self address: {:?}", address); - if error == &P2pError::ConnectSelf { - let addr = address - .iter() - .filter(|proto| match proto { - multiaddr::Protocol::P2p(_) => false, - _ => true, - }) - .collect(); - self.network_state - .listened_addresses - .write() - .insert(addr, std::u8::MAX); + match error { + ServiceError::DialerError { + ref address, + ref error, + } => { + debug!(target: "network", "add self address: {:?}", address); + if error == &P2pError::ConnectSelf { + let addr = address + .iter() + .filter(|proto| match proto { + multiaddr::Protocol::P2p(_) => false, + _ => true, + }) + .collect(); + self.network_state + .listened_addresses + .write() + .insert(addr, std::u8::MAX); + } + if let Some(peer_id) = extract_peer_id(address) { + self.network_state + .failed_dials + .write() + .insert(peer_id, Instant::now()); + } } - if let Some(peer_id) = extract_peer_id(address) { - self.network_state - .failed_dials - .write() - .insert(peer_id, Instant::now()); + ServiceError::ProtocolError { id, .. } => { + if let Err(err) = context.control().disconnect(id) { + warn!(target: "network", "send disconnect task(session_id={}) failed, error={:?}", id, err); + } + } + ServiceError::MuxerError { + session_context, .. + } => { + if let Err(err) = context.control().disconnect(session_context.id) { + warn!(target: "network", "send disconnect task(session_id={}) failed, error={:?}", session_context.id, err); + } } + _ => {} } } diff --git a/network/src/protocols/discovery.rs b/network/src/protocols/discovery.rs index 87b24ec204..b6d97f96dc 100644 --- a/network/src/protocols/discovery.rs +++ b/network/src/protocols/discovery.rs @@ -126,6 +126,7 @@ impl ServiceProtocol for DiscoveryProtocol { } else { warn!(target: "network", "other channel error: {:?}", err); } + self.discovery_senders.remove(&session.id); } } } diff --git a/network/src/protocols/mod.rs b/network/src/protocols/mod.rs index 47679622a7..2cc61791b3 100644 --- a/network/src/protocols/mod.rs +++ b/network/src/protocols/mod.rs @@ -11,7 +11,7 @@ use crate::{ NetworkState, PeerIndex, ProtocolContext, ProtocolContextMutRef, ServiceControl, SessionInfo, }; use bytes::Bytes; -use log::{debug, error, info, warn}; +use log::{debug, error, info, trace, warn}; use p2p::{ builder::MetaBuilder, service::{ProtocolHandle, ProtocolMeta}, @@ -20,6 +20,10 @@ use p2p::{ }; use std::sync::Arc; use std::time::{Duration, Instant}; +use tokio::codec::length_delimited; + +// Max message frame length: 20MB +const MAX_FRAME_LENGTH: usize = 20 * 1024 * 1024; pub type ProtocolVersion = u32; @@ -76,6 +80,13 @@ impl CKBProtocol { MetaBuilder::default() .id(self.id) .name(move |_| protocol_name.clone()) + .codec(|| { + Box::new( + length_delimited::Builder::new() + .max_frame_length(MAX_FRAME_LENGTH) + .new_codec(), + ) + }) .support_versions(supported_versions) .service_handle(move || { let handler = CKBHandler::new(self.id, self.network_state, self.handler); @@ -231,7 +242,7 @@ impl ServiceProtocol for CKBHandler { .map(|peer_index| (peer_id, peer_index)) }) { - debug!( + trace!( target: "network", "ckb protocol received, addr: {}, protocol: {}, peer_id: {:?}", session.address, @@ -337,6 +348,7 @@ impl CKBProtocolContext for DefaultCKBProtocolContext { .network_state .get_peer_id(peer_index) .ok_or_else(|| PeerError::IndexNotFound(peer_index))?; + let session_id = self .network_state .peers_registry diff --git a/nodes_template/default.toml b/nodes_template/default.toml deleted file mode 100644 index e1e60f22a8..0000000000 --- a/nodes_template/default.toml +++ /dev/null @@ -1,56 +0,0 @@ -data_dir = "default" - -[db] -path = "default/db" - -[chain] -spec = { Local = "spec/dev.toml" } - -[logger] -file = "ckb.log" -filter = "info" -color = true - -[network] -path = "default/network" -listen_addresses = ["/ip4/0.0.0.0/tcp/8115"] -public_addresses = [] -bootnodes = [] -reserved_peers = [] -reserved_only = false -max_peers = 125 -max_outbound_peers = 30 -ping_interval_secs = 15 -ping_timeout_secs = 20 -connect_outbound_interval_secs = 15 - -[rpc] -listen_address = "0.0.0.0:8114" - -# Default is 10MiB = 10 * 1024 * 1024 -max_request_body_size = 10485760 - -# List of API modules: ["Net", "Pool", "Miner", "Chain", "Trace"] -modules = ["Net", "Pool", "Miner", "Chain"] - -[sync] -verification_level = "Full" -orphan_block_limit = 1024 - -[tx_pool] -max_pool_size = 10000 -max_orphan_size = 10000 -max_proposal_size = 10000 -max_cache_size = 1000 -max_pending_size = 10000 -trace = 100 -txs_verify_cache_size = 100000 - -[block_assembler] -# value is set as always success binary hash -binary_hash = "0x0000000000000000000000000000000000000000000000000000000000000001" -args = [] - -[sentry] -# set to blank to disable sentry error collection -dsn = "https://48c6a88d92e246478e2d53b5917a887c@sentry.io/1422795" diff --git a/nodes_template/miner.toml b/nodes_template/miner.toml deleted file mode 100644 index 9f7da13c15..0000000000 --- a/nodes_template/miner.toml +++ /dev/null @@ -1,19 +0,0 @@ -data_dir = "default" -chain = { Local = "spec/dev.toml" } -rpc_url = "http://127.0.0.1:8114/" -cycles_limit = 100000000 -bytes_limit = 10000000 -max_version = 0 -block_on_submit = true - -# block template polling interval in milliseconds -poll_interval = 1000 - -[logger] -file = "miner.log" -filter = "info" -color = true - -[sentry] -# set to blank to disable sentry error collection -dsn = "https://48c6a88d92e246478e2d53b5917a887c@sentry.io/1422795" diff --git a/resource/Cargo.toml b/resource/Cargo.toml new file mode 100644 index 0000000000..a906a1918d --- /dev/null +++ b/resource/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ckb-resource" +version = "0.9.0-pre" +license = "MIT" +authors = ["Nervos Core Dev "] +edition = "2018" +build = "build.rs" +include = ["/specs", "/ckb.toml", "/ckb-miner.toml"] + +[dependencies] +phf = "0.7.21" +includedir = "0.5.0" +tempfile = "3.0" + +[build-dependencies] +includedir_codegen = "0.5.0" +walkdir = "2.1.4" diff --git a/resource/build.rs b/resource/build.rs new file mode 100644 index 0000000000..5072d3aeb8 --- /dev/null +++ b/resource/build.rs @@ -0,0 +1,26 @@ +use includedir_codegen::Compression; +use walkdir::WalkDir; + +fn main() { + let mut bundled = includedir_codegen::start("BUNDLED"); + + for f in &["ckb.toml", "ckb-miner.toml"] { + bundled + .add_file(f, Compression::Gzip) + .expect("add files to resource bundle"); + } + for entry in WalkDir::new("specs").follow_links(true).into_iter() { + match entry { + Ok(ref e) + if !e.file_type().is_dir() && !e.file_name().to_string_lossy().starts_with(".") => + { + bundled + .add_file(e.path(), Compression::Gzip) + .expect("add files to resource bundle"); + } + _ => (), + } + } + + bundled.build("bundled.rs").expect("build resource bundle"); +} diff --git a/resource/ckb-miner.toml b/resource/ckb-miner.toml new file mode 100644 index 0000000000..6af270c648 --- /dev/null +++ b/resource/ckb-miner.toml @@ -0,0 +1,45 @@ +# Config generated by `ckb init` # {{ +# see => resource/src/template.rs +# testnet => # Config generated by `ckb init --spec testnet` +# }} + +data_dir = "data" + +[chain] +# Choose the kind of chains to run, possible values: +# - specs/dev.toml +# - specs/testnet.toml +spec = "specs/dev.toml" # {{ +# testnet => spec = "specs/testnet.toml" +# integration => spec = "specs/integration.toml" +# }} + +[logger] +filter = "info" # {{ +# integration => filter = "info,network=trace,rpc=debug,sync=debug,relay=debug" +# }} +color = true +log_to_file = true # {{ +# _ => log_to_file = {log_to_file} +# }} +log_to_stdout = true # {{ +# _ => log_to_stdout = {log_to_stdout} +# }} + +[sentry] +# set to blank to disable sentry error collection +dsn = "https://48c6a88d92e246478e2d53b5917a887c@sentry.io/1422795" # {{ +# integration => dsn = "" +# }} + +[miner] +rpc_url = "http://127.0.0.1:8114/" # {{ +# _ => rpc_url = "http://127.0.0.1:{rpc_port}/" +# }} +cycles_limit = 100000000 +bytes_limit = 10000000 +max_version = 0 +block_on_submit = true + +# block template polling interval in milliseconds +poll_interval = 1000 diff --git a/resource/ckb.toml b/resource/ckb.toml new file mode 100644 index 0000000000..6cb5d333f9 --- /dev/null +++ b/resource/ckb.toml @@ -0,0 +1,84 @@ +# Config generated by `ckb init` # {{ +# see => resource/src/template.rs +# testnet => # Config generated by `ckb init --spec testnet` +# }} + +data_dir = "data" + +[chain] +# Choose the kind of chains to run, possible values: +# - specs/dev.toml +# - specs/testnet.toml +spec = "specs/dev.toml" # {{ +# testnet => spec = "specs/testnet.toml" +# integration => spec = "specs/integration.toml" +# }} + +[logger] +filter = "info" # {{ +# integration => filter = "info,network=trace,rpc=debug,sync=debug,relay=debug" +# }} +color = true +log_to_file = true # {{ +# _ => log_to_file = {log_to_file} +# }} +log_to_stdout = true # {{ +# _ => log_to_stdout = {log_to_stdout} +# }} + +[sentry] +# set to blank to disable sentry error collection +dsn = "https://48c6a88d92e246478e2d53b5917a887c@sentry.io/1422795" # {{ +# integration => dsn = "" +# }} + +[network] +listen_addresses = ["/ip4/0.0.0.0/tcp/8115"] # {{ +# _ => listen_addresses = ["/ip4/0.0.0.0/tcp/{p2p_port}"] +# }} +public_addresses = [] + +# Node connects to nodes listed here to discovery other peers when there's no local stored peers. +# When chain.spec is changed, this usually should also be changed to the bootnodes in the new chain. +bootnodes = [] + +reserved_peers = [] +reserved_only = false +max_peers = 125 +max_outbound_peers = 30 +ping_interval_secs = 15 +ping_timeout_secs = 20 +connect_outbound_interval_secs = 15 # {{ +# integration => connect_outbound_interval_secs = 1 +# }} + +[rpc] +listen_address = "0.0.0.0:8114" # {{ +# _ => listen_address = "0.0.0.0:{rpc_port}" +# }} + +# Default is 10MiB = 10 * 1024 * 1024 +max_request_body_size = 10485760 + +# List of API modules: ["Net", "Pool", "Miner", "Chain", "Trace"] +modules = ["Net", "Pool", "Miner", "Chain"] # {{ +# integration => modules = ["Net", "Pool", "Miner", "Chain", "Trace", "IntegrationTest"] +# }} + +[sync] +verification_level = "Full" +orphan_block_limit = 1024 + +[tx_pool] +max_pool_size = 10000 +max_orphan_size = 10000 +max_proposal_size = 10000 +max_cache_size = 1000 +max_pending_size = 10000 +trace = 100 +txs_verify_cache_size = 100000 + +[block_assembler] +# value is set as always success binary hash +binary_hash = "0x0000000000000000000000000000000000000000000000000000000000000001" +args = [] diff --git a/nodes_template/spec/cells/always_success b/resource/specs/cells/always_success similarity index 100% rename from nodes_template/spec/cells/always_success rename to resource/specs/cells/always_success diff --git a/spec/chainspecs/testnet/cells/secp256k1_blake2b_sighash_all b/resource/specs/cells/secp256k1_blake2b_sighash_all similarity index 100% rename from spec/chainspecs/testnet/cells/secp256k1_blake2b_sighash_all rename to resource/specs/cells/secp256k1_blake2b_sighash_all diff --git a/nodes_template/spec/dev.toml b/resource/specs/dev.toml similarity index 100% rename from nodes_template/spec/dev.toml rename to resource/specs/dev.toml diff --git a/test/fixtures/nodes_template/spec/integration_test.toml b/resource/specs/integration.toml similarity index 100% rename from test/fixtures/nodes_template/spec/integration_test.toml rename to resource/specs/integration.toml diff --git a/spec/chainspecs/testnet/testnet.toml b/resource/specs/testnet.toml similarity index 100% rename from spec/chainspecs/testnet/testnet.toml rename to resource/specs/testnet.toml diff --git a/resource/src/lib.rs b/resource/src/lib.rs new file mode 100644 index 0000000000..36ddd8b91d --- /dev/null +++ b/resource/src/lib.rs @@ -0,0 +1,337 @@ +// Shields clippy errors in generated bundled.rs +#![allow(clippy::unreadable_literal)] + +mod template; + +pub use self::template::{ + TemplateContext, AVAILABLE_SPECS, DEFAULT_P2P_PORT, DEFAULT_RPC_PORT, DEFAULT_SPEC, +}; +pub use std::io::{Error, Result}; + +use self::template::Template; +use std::borrow::Cow; +use std::fs; +use std::io::{self, BufReader, Read}; +use std::path::{Path, PathBuf}; +use tempfile::NamedTempFile; + +include!(concat!(env!("OUT_DIR"), "/bundled.rs")); + +pub const CKB_CONFIG_FILE_NAME: &str = "ckb.toml"; +pub const MINER_CONFIG_FILE_NAME: &str = "ckb-miner.toml"; +pub const SPECS_RESOURCE_DIR_NAME: &str = "specs/"; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Resource { + Bundled(String), + FileSystem(PathBuf), +} + +impl Resource { + /// Gets resource content + pub fn get(&self) -> Result> { + match self { + Resource::Bundled(key) => BUNDLED.get(key), + Resource::FileSystem(path) => { + let mut file = BufReader::new(fs::File::open(path)?); + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + Ok(Cow::Owned(data)) + } + } + } + + /// Gets resource input stream + pub fn read(&self) -> Result> { + match self { + Resource::Bundled(key) => BUNDLED.read(key), + Resource::FileSystem(path) => Ok(Box::new(BufReader::new(fs::File::open(path)?))), + } + } +} + +pub struct ResourceLocator { + root_dir: PathBuf, +} + +impl ResourceLocator { + pub fn root_dir(&self) -> &Path { + self.root_dir.as_path() + } + + /// Creates a ResourceLocator using `path` as root directory. + /// + /// It returns error if the directory does not exists and current user has no permission to create it. + pub fn with_root_dir(root_dir: PathBuf) -> Result { + fs::create_dir_all(&root_dir)?; + + root_dir + .canonicalize() + .map(|root_dir| ResourceLocator { root_dir }) + } + + pub fn current_dir() -> Result { + let root_dir = ::std::env::current_dir()?; + root_dir + .canonicalize() + .map(|root_dir| ResourceLocator { root_dir }) + } + + pub fn ckb(&self) -> Resource { + self.resolve(PathBuf::from(CKB_CONFIG_FILE_NAME)).unwrap() + } + + pub fn miner(&self) -> Resource { + self.resolve(PathBuf::from(MINER_CONFIG_FILE_NAME)).unwrap() + } + + /// Resolves a resource using a path. + /// + /// The path may be absolute or relative. This function tries the file system first. If the file + /// is absent in the file system and it is relative, the function will search in the bundled files. + /// + /// The relative path is relative to the resource root directory. + /// + /// All the bundled files are assumed in the resource root directory. + /// + /// It returns None when no resource with the path is found. + pub fn resolve(&self, path: PathBuf) -> Option { + if path.is_absolute() { + return file_system(path); + } + + file_system(self.root_dir.join(&path)).or_else(|| bundled(path)) + } + + /// Resolves a resource using a path as the path is refered in the resource `relative_to`. + /// + /// This function is similar to [`ResourceLocator::resolve`]. The difference is how to resolve a relative path. + /// + /// [`ResourceLocator::resolve`]: struct.ResourceLocator.html#method.open + /// + /// The relative path is relative to the directory containing the resource `relative_to`. + /// + /// For security reason, when `relative_to` is `Resource::Bundled`, the return value is either + /// `Some(Resource::Bundled)` or `None`. A bundled file is forbidden to reference a file in the + /// file system. + pub fn resolve_relative_to(&self, path: PathBuf, relative_to: &Resource) -> Option { + match relative_to { + Resource::Bundled(key) => { + // Bundled file can only refer to bundled files. + let relative_start_dir = parent_dir(PathBuf::from(key)).join(&path); + bundled(relative_start_dir) + } + Resource::FileSystem(relative_to_path) => { + if path.is_absolute() { + return file_system(path); + } + + let start_dir = parent_dir(relative_to_path.clone()); + file_system(start_dir.join(&path)).or_else(|| { + start_dir + .strip_prefix(&self.root_dir) + .ok() + .and_then(|relative_start_dir| bundled(relative_start_dir.join(path))) + }) + } + } + } + + pub fn exported(&self) -> bool { + BUNDLED + .file_names() + .any(|name| self.root_dir.join(name).exists()) + } + + pub fn export_ckb<'a>(&self, context: &TemplateContext<'a>) -> Result<()> { + let ckb = Resource::Bundled(CKB_CONFIG_FILE_NAME.to_string()); + let template = Template::new(from_utf8(ckb.get()?)?); + let mut out = NamedTempFile::new_in(&self.root_dir)?; + template.write_to(&mut out, context)?; + out.persist(self.root_dir.join(CKB_CONFIG_FILE_NAME))?; + Ok(()) + } + + pub fn export_miner<'a>(&self, context: &TemplateContext<'a>) -> Result<()> { + let miner = Resource::Bundled(MINER_CONFIG_FILE_NAME.to_string()); + let template = Template::new(from_utf8(miner.get()?)?); + let mut out = NamedTempFile::new_in(&self.root_dir)?; + template.write_to(&mut out, context)?; + out.persist(self.root_dir.join(MINER_CONFIG_FILE_NAME))?; + Ok(()) + } + + pub fn export_specs(&self) -> Result<()> { + for name in BUNDLED.file_names() { + if name.starts_with(SPECS_RESOURCE_DIR_NAME) { + let path = self.root_dir.join(name); + fs::create_dir_all(path.parent().unwrap())?; + let mut out = NamedTempFile::new_in(&self.root_dir)?; + io::copy(&mut BUNDLED.read(name)?, &mut out)?; + out.into_temp_path().persist(path)?; + } + } + + Ok(()) + } +} + +#[cfg(windows)] +fn path_as_key(path: &PathBuf) -> Cow { + Cow::Owned(path.to_string_lossy().replace("\\", "/")) +} + +#[cfg(not(windows))] +fn path_as_key(path: &PathBuf) -> Cow { + path.to_string_lossy() +} + +fn file_system(path: PathBuf) -> Option { + path.canonicalize().ok().map(Resource::FileSystem) +} + +fn bundled(path: PathBuf) -> Option { + let key = path_as_key(&path); + if BUNDLED.is_available(&key) { + Some(Resource::Bundled(key.into_owned())) + } else { + None + } +} + +fn parent_dir(mut path: PathBuf) -> PathBuf { + path.pop(); + path +} + +fn from_utf8(data: Cow<[u8]>) -> Result { + String::from_utf8(data.to_vec()).map_err(|err| Error::new(io::ErrorKind::Other, err)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::path::{Path, PathBuf}; + + fn mkdir() -> tempfile::TempDir { + tempfile::Builder::new() + .prefix("ckb_resource_test") + .tempdir() + .unwrap() + } + + fn touch>(path: P) -> PathBuf { + fs::create_dir_all(path.as_ref().parent().unwrap()).expect("create dir in test"); + fs::OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .expect("touch file in test"); + + path.as_ref().canonicalize().expect("touch file in test") + } + + #[test] + fn test_resource_locator_resolve() { + let dir = mkdir(); + let spec_dev_path = touch(dir.path().join("specs/dev.toml")); + + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()) + .expect("resource root dir exists"); + + assert_eq!( + locator.resolve("ckb.toml".into()), + Some(Resource::Bundled("ckb.toml".into())) + ); + + assert_eq!( + locator.resolve("specs/testnet.toml".into()), + Some(Resource::Bundled("specs/testnet.toml".into())) + ); + assert_eq!( + locator.resolve("specs/dev.toml".into()), + Some(Resource::FileSystem(spec_dev_path.clone())) + ); + + assert_eq!(locator.resolve(dir.path().join("ckb.toml")), None); + assert_eq!(locator.resolve("x.toml".into()), None); + } + + #[test] + fn test_resource_locator_resolve_relative_to() { + let dir = mkdir(); + let spec_dev_path = touch(dir.path().join("specs/dev.toml")); + let always_success_path = touch(dir.path().join("specs/cells/always_success")); + + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()) + .expect("resource root dir exists"); + + // Relative to Bundled("ckb.toml") + { + let ckb = Resource::Bundled("ckb.toml".into()); + + assert_eq!( + locator.resolve_relative_to("specs/dev.toml".into(), &ckb), + Some(Resource::Bundled("specs/dev.toml".into())) + ); + assert_eq!( + locator.resolve_relative_to("specs/testnet.toml".into(), &ckb), + Some(Resource::Bundled("specs/testnet.toml".into())) + ); + assert_eq!(locator.resolve_relative_to("x".into(), &ckb), None); + assert_eq!( + locator.resolve_relative_to(spec_dev_path.clone(), &ckb), + None, + ); + } + + // Relative to Bundled("specs/dev.toml") + { + let ckb = Resource::Bundled("specs/dev.toml".into()); + + assert_eq!( + locator.resolve_relative_to("cells/always_success".into(), &ckb), + Some(Resource::Bundled("specs/cells/always_success".into())) + ); + assert_eq!( + locator.resolve_relative_to("cells/secp256k1_blake2b_sighash_all".into(), &ckb), + Some(Resource::Bundled( + "specs/cells/secp256k1_blake2b_sighash_all".into() + )) + ); + assert_eq!(locator.resolve_relative_to("x".into(), &ckb), None); + assert_eq!( + locator.resolve_relative_to(always_success_path.clone(), &ckb), + None, + ); + } + + // Relative to FileSystem("specs/dev.toml") + { + let spec_dev = Resource::FileSystem(spec_dev_path.clone()); + + assert_eq!( + locator.resolve_relative_to("cells/always_success".into(), &spec_dev), + Some(Resource::FileSystem(always_success_path.clone())) + ); + assert_eq!( + locator + .resolve_relative_to("cells/secp256k1_blake2b_sighash_all".into(), &spec_dev), + Some(Resource::Bundled( + "specs/cells/secp256k1_blake2b_sighash_all".into() + )) + ); + assert_eq!(locator.resolve_relative_to("x".into(), &spec_dev), None); + + assert_eq!( + locator.resolve_relative_to(always_success_path.clone(), &spec_dev), + Some(Resource::FileSystem(always_success_path.clone())), + ); + assert_eq!( + locator.resolve_relative_to(dir.path().join("ckb.toml"), &spec_dev), + None, + ); + } + } +} diff --git a/resource/src/template.rs b/resource/src/template.rs new file mode 100644 index 0000000000..b9dce47a6d --- /dev/null +++ b/resource/src/template.rs @@ -0,0 +1,114 @@ +pub const DEFAULT_SPEC: &str = "dev"; +pub const AVAILABLE_SPECS: &[&str] = &["dev", "testnet"]; +pub const DEFAULT_RPC_PORT: &str = "8114"; +pub const DEFAULT_P2P_PORT: &str = "8115"; + +const START_MARKER: &str = " # {{"; +const END_MAKER: &str = "# }}"; +const WILDCARD_BRANCH: &str = "# _ => "; + +use std::io; + +pub struct Template(T); + +pub struct TemplateContext<'a> { + pub spec: &'a str, + pub rpc_port: &'a str, + pub p2p_port: &'a str, + pub log_to_file: bool, + pub log_to_stdout: bool, +} + +impl<'a> Default for TemplateContext<'a> { + fn default() -> Self { + TemplateContext { + spec: DEFAULT_SPEC, + rpc_port: DEFAULT_RPC_PORT, + p2p_port: DEFAULT_P2P_PORT, + log_to_file: true, + log_to_stdout: true, + } + } +} + +impl Template { + pub fn new(content: T) -> Self { + Template(content) + } +} + +fn writeln(w: &mut W, s: &str, context: &TemplateContext) -> io::Result<()> { + writeln!( + w, + "{}", + s.replace("{rpc_port}", context.rpc_port) + .replace("{p2p_port}", context.p2p_port) + .replace("{log_to_file}", &format!("{}", context.log_to_file)) + .replace("{log_to_stdout}", &format!("{}", context.log_to_stdout)) + ) +} + +#[derive(Debug)] +pub enum TemplateState<'a> { + SearchStartMarker, + MatchBranch(&'a str), + SearchEndMarker, +} + +impl Template +where + T: AsRef, +{ + pub fn write_to<'c, W: io::Write>( + &self, + w: &mut W, + context: &TemplateContext<'c>, + ) -> io::Result<()> { + let spec_branch = format!("# {} => ", context.spec).to_string(); + + let mut state = TemplateState::SearchStartMarker; + for line in self.0.as_ref().lines() { + // dbg!((line, &state)); + match state { + TemplateState::SearchStartMarker => { + if line.ends_with(START_MARKER) { + state = TemplateState::MatchBranch(line); + } else { + writeln!(w, "{}", line)?; + } + } + TemplateState::MatchBranch(start_line) => { + if line == END_MAKER { + writeln!( + w, + "{}", + &start_line[..(start_line.len() - START_MARKER.len())], + )?; + state = TemplateState::SearchStartMarker; + } else if line.starts_with(&spec_branch) { + writeln(w, &line[spec_branch.len()..], context)?; + state = TemplateState::SearchEndMarker; + } else if line.starts_with(WILDCARD_BRANCH) { + writeln(w, &line[WILDCARD_BRANCH.len()..], context)?; + state = TemplateState::SearchEndMarker; + } + } + TemplateState::SearchEndMarker => { + if line == END_MAKER { + state = TemplateState::SearchStartMarker; + } + } + } + } + + if let TemplateState::MatchBranch(start_line) = state { + writeln!( + w, + "{}", + &start_line[..(start_line.len() - START_MARKER.len())], + )?; + } + + Ok(()) + } +} diff --git a/rpc/src/module/pool.rs b/rpc/src/module/pool.rs index 3339d5e5a6..20aba0cb51 100644 --- a/rpc/src/module/pool.rs +++ b/rpc/src/module/pool.rs @@ -48,7 +48,10 @@ impl PoolRpc for PoolRpcImpl { Ok(cycles) => Some(cycles), }; let entry = PoolEntry::new(tx.clone(), 0, cycles); - chain_state.mut_tx_pool().enqueue_tx(entry); + if !chain_state.mut_tx_pool().enqueue_tx(entry) { + // Duplicate tx + return Ok(tx_hash); + } cycles }; match cycles { diff --git a/shared/src/chain_state.rs b/shared/src/chain_state.rs index 03f20a50c1..807640a60e 100644 --- a/shared/src/chain_state.rs +++ b/shared/src/chain_state.rs @@ -12,7 +12,7 @@ use ckb_core::transaction::{OutPoint, ProposalShortId, Transaction}; use ckb_core::Cycle; use ckb_verification::{TransactionError, TransactionVerifier}; use fnv::FnvHashSet; -use log::error; +use log::{error, trace}; use lru_cache::LruCache; use numext_fixed_hash::H256; use numext_fixed_uint::U256; @@ -104,7 +104,12 @@ impl ChainState { let short_id = tx.proposal_short_id(); let rtx = self.resolve_tx_from_pool(&tx, &tx_pool); let verify_result = self.verify_rtx(&rtx, max_cycles); + let tx_hash = tx.hash(); if self.contains_proposal_id(&short_id) { + if !tx_pool.filter.insert(tx_hash.clone()) { + trace!(target: "tx_pool", "discarding already known transaction {:#x}", tx_hash); + return Err(PoolError::Duplicate); + } let entry = PoolEntry::new(tx, 0, verify_result.map(Some).unwrap_or(None)); self.staging_tx(&mut tx_pool, entry, max_cycles)?; Ok(verify_result.map_err(PoolError::InvalidTx)?) @@ -113,12 +118,16 @@ impl ChainState { Ok(cycles) => { // enqueue tx with cycles let entry = PoolEntry::new(tx, 0, Some(cycles)); - tx_pool.enqueue_tx(entry); + if !tx_pool.enqueue_tx(entry) { + return Err(PoolError::Duplicate); + } Ok(cycles) } Err(TransactionError::UnknownInput) => { let entry = PoolEntry::new(tx, 0, None); - tx_pool.enqueue_tx(entry); + if !tx_pool.enqueue_tx(entry) { + return Err(PoolError::Duplicate); + } Err(PoolError::InvalidTx(TransactionError::UnknownInput)) } Err(err) => Err(PoolError::InvalidTx(err)), diff --git a/shared/src/shared.rs b/shared/src/shared.rs index 1c9b522bac..e72a2c326d 100644 --- a/shared/src/shared.rs +++ b/shared/src/shared.rs @@ -42,12 +42,7 @@ impl ::std::clone::Clone for Shared { } impl Shared { - pub fn new( - store: CI, - consensus: Consensus, - txs_verify_cache_size: usize, - tx_pool_config: TxPoolConfig, - ) -> Self { + pub fn new(store: CI, consensus: Consensus, tx_pool_config: TxPoolConfig) -> Self { let store = Arc::new(store); let chain_state = { // check head in store or save the genesis block as head @@ -73,6 +68,7 @@ impl Shared { .expect("block_ext stored") .total_difficulty; + let txs_verify_cache_size = tx_pool_config.txs_verify_cache_size; Arc::new(Mutex::new(ChainState::new( &store, header, @@ -378,22 +374,10 @@ impl SharedBuilder { self } - pub fn txs_verify_cache_size(mut self, value: usize) -> Self { - if let Some(c) = self.tx_pool_config.as_mut() { - c.txs_verify_cache_size = value; - }; - self - } - pub fn build(self) -> Shared> { let store = ChainKVStore::new(self.db.unwrap()); let consensus = self.consensus.unwrap_or_else(Consensus::default); let tx_pool_config = self.tx_pool_config.unwrap_or_else(Default::default); - Shared::new( - store, - consensus, - tx_pool_config.txs_verify_cache_size, - tx_pool_config, - ) + Shared::new(store, consensus, tx_pool_config) } } diff --git a/shared/src/tx_pool/types.rs b/shared/src/tx_pool/types.rs index d6ff0b313a..64cd81d0cd 100644 --- a/shared/src/tx_pool/types.rs +++ b/shared/src/tx_pool/types.rs @@ -79,6 +79,8 @@ pub enum PoolError { TimeOut, /// BlockNumber is not right InvalidBlockNumber, + /// Duplicate tx + Duplicate, } impl fmt::Display for PoolError { diff --git a/spec/Cargo.toml b/spec/Cargo.toml index 25ad4254d3..ecdaf9d174 100644 --- a/spec/Cargo.toml +++ b/spec/Cargo.toml @@ -4,8 +4,6 @@ version = "0.9.0-pre" license = "MIT" authors = ["Nervos Core Dev "] edition = "2018" -build = "build.rs" -include = ["/chainspecs"] [dependencies] serde = "1.0" @@ -15,8 +13,4 @@ numext-fixed-hash = { version = "0.1", features = ["support_rand", "support_heap numext-fixed-uint = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"] } ckb-core = { path = "../core" } ckb-pow = { path = "../pow" } -includedir = "0.5.0" -phf = "0.7.21" - -[build-dependencies] -includedir_codegen = "0.5.0" +ckb-resource = { path = "../resource" } diff --git a/spec/build.rs b/spec/build.rs deleted file mode 100644 index 69b7c92dbb..0000000000 --- a/spec/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -use includedir_codegen::Compression; - -fn main() { - includedir_codegen::start("FILES") - .dir("chainspecs", Compression::Gzip) - .build("chainspecs.rs") - .unwrap(); -} diff --git a/spec/src/lib.rs b/spec/src/lib.rs index fdee65030d..2279a9c12c 100644 --- a/spec/src/lib.rs +++ b/spec/src/lib.rs @@ -5,9 +5,6 @@ //! In order to run a chain different to the official public one, //! with a config file specifying chain = "path" under [ckb]. -// Shields clippy errors in generated chainspecs.rs file. -#![allow(clippy::unreadable_literal)] - use crate::consensus::Consensus; use ckb_core::block::BlockBuilder; use ckb_core::header::HeaderBuilder; @@ -15,74 +12,29 @@ use ckb_core::script::Script; use ckb_core::transaction::{CellOutput, Transaction, TransactionBuilder}; use ckb_core::{Capacity, Cycle}; use ckb_pow::{Pow, PowEngine}; +use ckb_resource::{Resource, ResourceLocator}; use numext_fixed_hash::H256; use numext_fixed_uint::U256; use serde_derive::Deserialize; use std::error::Error; -use std::fs::File; -use std::io::Read; -use std::path::{Display, Path, PathBuf}; +use std::fmt; +use std::path::PathBuf; use std::sync::Arc; pub mod consensus; -include!(concat!(env!("OUT_DIR"), "/chainspecs.rs")); - -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] -pub enum SpecPath { - Testnet, - Local(PathBuf), -} - -impl SpecPath { - pub fn display(&self) -> Display { - match self { - SpecPath::Testnet => Path::new("Testnet").display(), - SpecPath::Local(path) => path.display(), - } - } - - pub fn expand_path>(&self, base: P) -> Self { - match self { - SpecPath::Testnet => SpecPath::Testnet, - SpecPath::Local(path) => { - if path.is_relative() { - SpecPath::Local(base.as_ref().join(path)) - } else { - SpecPath::Local(path.to_path_buf()) - } - } - } - } - - fn path(&self) -> PathBuf { - match self { - SpecPath::Testnet => PathBuf::from("testnet/testnet.toml"), - SpecPath::Local(path) => PathBuf::from(path), - } - } - - fn load_file>(&self, path: P) -> Result, Box> { - match self { - SpecPath::Testnet => { - let s = path.as_ref().to_str().expect("chain spec path"); - Ok(FILES - .get(&format!("chainspecs/{}", s)) - .expect("hardcoded spec") - .to_vec()) - } - SpecPath::Local(_) => { - let mut file = File::open(&path)?; - let mut data = Vec::new(); - file.read_to_end(&mut data)?; - Ok(data) - } - } - } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ChainSpec { + pub resource: Resource, + pub name: String, + pub genesis: Genesis, + pub params: Params, + pub system_cells: Vec, + pub pow: Pow, } -#[derive(Clone, PartialEq, Eq, Debug, Deserialize)] -pub struct ChainSpec { +#[derive(Deserialize)] +struct ChainSpecConfig { pub name: String, pub genesis: Genesis, pub params: Params, @@ -119,38 +71,80 @@ pub struct SystemCell { pub path: PathBuf, } -pub(self) fn build_system_cell_transaction( - cells: &[SystemCell], - spec_path: &SpecPath, -) -> Result> { - let mut outputs = Vec::new(); - for system_cell in cells { - let data = spec_path.load_file(&system_cell.path)?; - - // TODO: we should provide a proper lock script here so system cells - // can be updated. - let output = CellOutput::new(data.len() as Capacity, data, Script::default(), None); - outputs.push(output); +#[derive(Debug)] +pub struct FileNotFoundError; + +impl FileNotFoundError { + fn boxed() -> Box { + Box::new(FileNotFoundError) } +} - Ok(TransactionBuilder::default().outputs(outputs).build()) +impl Error for FileNotFoundError {} + +impl fmt::Display for FileNotFoundError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChainSpec: file not found") + } } impl ChainSpec { - pub fn read_from_file(spec_path: &SpecPath) -> Result> { - let config_bytes = spec_path.load_file(spec_path.path())?; - let config_str = String::from_utf8(config_bytes)?; - let mut spec: Self = toml::from_str(&config_str)?; - spec.resolve_paths(spec_path.path().parent().expect("chain spec path resolve")); - - Ok(spec) + pub fn resolve_relative_to( + locator: &ResourceLocator, + spec_path: PathBuf, + config_file: &Resource, + ) -> Result> { + let resource = match locator.resolve_relative_to(spec_path, config_file) { + Some(r) => r, + None => return Err(FileNotFoundError::boxed()), + }; + let config_bytes = resource.get()?; + let spec_config: ChainSpecConfig = toml::from_slice(&config_bytes)?; + + let system_cells_result: Result, FileNotFoundError> = spec_config + .system_cells + .into_iter() + .map(|c| { + locator + .resolve_relative_to(c.path, &resource) + .ok_or(FileNotFoundError) + }) + .collect(); + + Ok(ChainSpec { + resource, + system_cells: system_cells_result?, + name: spec_config.name, + genesis: spec_config.genesis, + params: spec_config.params, + pow: spec_config.pow, + }) } pub fn pow_engine(&self) -> Arc { self.pow.engine() } - pub fn to_consensus(&self, spec_path: &SpecPath) -> Result> { + fn build_system_cell_transaction(&self) -> Result> { + let outputs_result: Result, _> = self + .system_cells + .iter() + .map(|c| { + c.get().map(|data| { + let data = data.into_owned(); + // TODO: we should provide a proper lock script here so system cells + // can be updated. + CellOutput::new(data.len() as u64, data, Script::default(), None) + }) + }) + .collect(); + + let outputs = outputs_result?; + + Ok(TransactionBuilder::default().outputs(outputs).build()) + } + + pub fn to_consensus(&self) -> Result> { let header = HeaderBuilder::default() .version(self.genesis.version) .parent_hash(self.genesis.parent_hash.clone()) @@ -164,10 +158,7 @@ impl ChainSpec { .build(); let genesis_block = BlockBuilder::default() - .commit_transaction(build_system_cell_transaction( - &self.system_cells, - &spec_path, - )?) + .commit_transaction(self.build_system_cell_transaction()?) .header(header) .build(); @@ -180,14 +171,6 @@ impl ChainSpec { Ok(consensus) } - - fn resolve_paths(&mut self, base: &Path) { - for mut cell in &mut self.system_cells { - if cell.path.is_relative() { - cell.path = base.join(&cell.path); - } - } - } } #[cfg(test)] @@ -196,36 +179,19 @@ pub mod test { #[test] fn test_chain_spec_load() { - println!( - "{:?}", - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../nodes_template/spec/dev.toml") - .display() - ); - let dev = ChainSpec::read_from_file(&SpecPath::Local( - Path::new(env!("CARGO_MANIFEST_DIR")).join("../nodes_template/spec/dev.toml"), - )); + let locator = ResourceLocator::current_dir().unwrap(); + let ckb = locator.ckb(); + let dev = ChainSpec::resolve_relative_to(&locator, PathBuf::from("specs/dev.toml"), &ckb); assert!(dev.is_ok(), format!("{:?}", dev)); - for cell in &dev.unwrap().system_cells { - assert!(cell.path.exists()); - } } #[test] fn always_success_type_hash() { - let spec_path = SpecPath::Local( - Path::new(env!("CARGO_MANIFEST_DIR")).join("../nodes_template/spec/dev.toml"), - ); - let always_success_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../nodes_template/spec/cells/always_success"); - - let tx = build_system_cell_transaction( - &[SystemCell { - path: always_success_path, - }], - &spec_path, - ) - .unwrap(); + let locator = ResourceLocator::current_dir().unwrap(); + let ckb = locator.ckb(); + let dev = ChainSpec::resolve_relative_to(&locator, PathBuf::from("specs/dev.toml"), &ckb) + .unwrap(); + let tx = dev.build_system_cell_transaction().unwrap(); // Tx and Output hash will be used in some test cases directly, assert here for convenience assert_eq!( @@ -248,12 +214,14 @@ pub mod test { #[test] fn test_testnet_chain_spec_load() { - let spec_path = SpecPath::Testnet; - let result = ChainSpec::read_from_file(&spec_path); - assert!(result.is_ok(), format!("{:?}", result)); - let chain_spec = result.unwrap(); - - let result = build_system_cell_transaction(&chain_spec.system_cells, &spec_path); + let locator = ResourceLocator::current_dir().unwrap(); + let ckb = locator.ckb(); + let testnet = + ChainSpec::resolve_relative_to(&locator, PathBuf::from("specs/testnet.toml"), &ckb); + assert!(testnet.is_ok(), format!("{:?}", testnet)); + let chain_spec = testnet.unwrap(); + + let result = chain_spec.build_system_cell_transaction(); assert!(result.is_ok(), format!("{:?}", result)); let tx = result.unwrap(); diff --git a/src/cli/args.rs b/src/cli/args.rs deleted file mode 100644 index b7434bb86c..0000000000 --- a/src/cli/args.rs +++ /dev/null @@ -1,93 +0,0 @@ -// use build_info::Version; -use build_info::{get_version, Version}; -use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; - -const CKB_CONFIG_HELP: &str = "Specify the configuration file PATH. Tries ckb.toml, nodes/default.toml in working directory when omitted."; -const MINER_CONFIG_HELP: &str = "Specify the configuration file PATH. Tries miner.toml, nodes/miner.toml in working directory when omitted."; - -pub fn get_matches() -> ArgMatches<'static> { - let version = get_version!(); - - App::new("ckb") - .author("Nervos Core Dev ") - .about("Nervos CKB - The Common Knowledge Base") - .version(version.short().as_str()) - .long_version(version.long().as_str()) - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(run()) - .subcommand(miner()) - .subcommand(export()) - .subcommand(import()) - .subcommand(cli()) - .get_matches() -} - -fn run() -> App<'static, 'static> { - SubCommand::with_name("run") - .arg(arg_config_with_help(CKB_CONFIG_HELP)) - .about("Running ckb node") -} - -fn miner() -> App<'static, 'static> { - SubCommand::with_name("miner") - .arg(arg_config_with_help(MINER_CONFIG_HELP)) - .about("Running ckb miner") -} - -fn arg_config_with_help(help: &'static str) -> Arg<'static, 'static> { - Arg::with_name("config") - .short("c") - .long("config") - .value_name("CONFIG") - .takes_value(true) - .help(help) -} - -fn arg_format() -> Arg<'static, 'static> { - Arg::with_name("format") - .short("f") - .long("format") - .value_name("FORMAT") - .required(true) - .takes_value(true) - .help("Specify the format.") -} - -fn export() -> App<'static, 'static> { - SubCommand::with_name("export") - .about("Export ckb data") - .arg(arg_format()) - .arg(arg_config_with_help(CKB_CONFIG_HELP)) - .arg( - Arg::with_name("target") - .short("t") - .long("target") - .value_name("PATH") - .required(true) - .index(1) - .help("Specify the export target path."), - ) -} - -fn import() -> App<'static, 'static> { - SubCommand::with_name("import") - .about("Import ckb data") - .arg(arg_config_with_help(CKB_CONFIG_HELP)) - .arg(arg_format()) - .arg( - Arg::with_name("source") - .short("s") - .long("source") - .value_name("PATH") - .required(true) - .index(1) - .help("Specify the exported data path."), - ) -} - -fn cli() -> App<'static, 'static> { - SubCommand::with_name("cli") - .about("Running ckb cli") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("keygen").about("Generate new key")) -} diff --git a/src/cli/export.rs b/src/cli/export.rs deleted file mode 100644 index be2bc9a1ae..0000000000 --- a/src/cli/export.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::super::setup::Setup; -use ckb_db::diskdb::RocksDB; -use ckb_instrument::{Export, Format}; -use ckb_shared::cachedb::CacheDB; -use ckb_shared::shared::SharedBuilder; -use clap::{value_t, ArgMatches}; - -pub fn export(setup: &Setup, matches: &ArgMatches) { - let format = value_t!(matches.value_of("format"), Format).unwrap_or_else(|e| e.exit()); - let target = value_t!(matches.value_of("target"), String).unwrap_or_else(|e| e.exit()); - - let shared = SharedBuilder::>::default() - .consensus( - setup - .chain_spec - .to_consensus(&setup.configs.chain.spec) - .unwrap(), - ) - .db(&setup.configs.db) - .build(); - Export::new(shared, format, target.into()) - .execute() - .unwrap_or_else(|e| panic!("Export error {:?} ", e)); -} diff --git a/src/cli/import.rs b/src/cli/import.rs deleted file mode 100644 index c4c12fca24..0000000000 --- a/src/cli/import.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::super::setup::Setup; -use ckb_chain::chain::ChainBuilder; -use ckb_db::diskdb::RocksDB; -use ckb_instrument::{Format, Import}; -use ckb_notify::NotifyService; -use ckb_shared::cachedb::CacheDB; -use ckb_shared::shared::SharedBuilder; -use clap::{value_t, ArgMatches}; - -pub fn import(setup: &Setup, matches: &ArgMatches) { - let format = value_t!(matches.value_of("format"), Format).unwrap_or_else(|e| e.exit()); - let source = value_t!(matches.value_of("source"), String).unwrap_or_else(|e| e.exit()); - - let shared = SharedBuilder::>::default() - .consensus( - setup - .chain_spec - .to_consensus(&setup.configs.chain.spec) - .unwrap(), - ) - .db(&setup.configs.db) - .build(); - - let notify = NotifyService::default().start::<&str>(None); - let chain_service = ChainBuilder::new(shared.clone(), notify).build(); - let chain_controller = chain_service.start::<&str>(Some("ImportChainService")); - - Import::new(chain_controller, format, source.into()) - .execute() - .unwrap_or_else(|e| panic!("Import error {:?} ", e)); -} diff --git a/src/cli/miner.rs b/src/cli/miner.rs deleted file mode 100644 index de4bc5554f..0000000000 --- a/src/cli/miner.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::cli::SentryConfig; -use crate::helper::{require_path_exists, to_absolute_path}; -use ckb_chain_spec::{ChainSpec, SpecPath}; -use ckb_miner::{Client, Miner, MinerConfig}; -use ckb_util::Mutex; -use clap::ArgMatches; -use crossbeam_channel::unbounded; -use dir::Directories; -use logger::{self, Config as LogConfig}; -use serde_derive::Deserialize; -use std::error::Error; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use std::thread; - -const DEFAULT_CONFIG_PATHS: &[&str] = &["miner.toml", "nodes/miner.toml"]; - -#[derive(Clone, Debug, Deserialize)] -struct Config { - pub logger: LogConfig, - #[serde(flatten)] - pub miner: MinerConfig, - pub chain: SpecPath, - pub data_dir: PathBuf, - pub sentry: SentryConfig, -} - -impl Config { - fn resolve_paths(&mut self, base: &Path) { - self.chain = self.chain.expand_path(base); - - if self.data_dir.is_relative() { - self.data_dir = base.join(&self.data_dir); - } - - let dirs = Directories::new(&self.data_dir); - if let Some(ref file) = self.logger.file { - let path = dirs.join("logs"); - self.logger.file = Some(path.join(file)); - } - } - - pub fn read_from_file>(path: P) -> Result> { - let config_str = std::fs::read_to_string(path.as_ref())?; - let mut config: Self = toml::from_str(&config_str)?; - config.resolve_paths(path.as_ref().parent().unwrap_or_else(|| { - eprintln!("Invalid config file path {:?}", path.as_ref()); - ::std::process::exit(1); - })); - Ok(config) - } -} - -pub fn miner(matches: &ArgMatches) { - let config_path = get_config_path(matches); - - let config = Config::read_from_file(config_path).unwrap_or_else(|e| { - eprintln!("Invalid config file {:?}", e); - ::std::process::exit(1); - }); - - let _logger_guard = logger::init(config.logger.clone()).expect("Init Logger"); - let _sentry_guard = config.sentry.clone().init(); - - let chain_spec = ChainSpec::read_from_file(&config.chain).expect("Load chain spec"); - - let (new_work_tx, new_work_rx) = unbounded(); - - let work = Arc::new(Mutex::new(None)); - - let client = Client::new(Arc::clone(&work), new_work_tx, config.miner); - - let miner = Miner::new(work, chain_spec.pow_engine(), new_work_rx, client.clone()); - - thread::Builder::new() - .name("client".to_string()) - .spawn(move || client.poll_block_template()) - .expect("Start client failed!"); - - miner.run() -} - -fn find_default_config_path() -> Option { - DEFAULT_CONFIG_PATHS - .iter() - .map(PathBuf::from) - .find(|p| p.exists()) -} - -pub fn get_config_path(matches: &ArgMatches) -> PathBuf { - to_absolute_path( - matches - .value_of("config") - .map_or_else(find_default_config_path, |v| { - require_path_exists(PathBuf::from(v)) - }) - .unwrap_or_else(|| { - eprintln!("Miner config file not found!"); - ::std::process::exit(1); - }), - ) -} diff --git a/src/cli/mod.rs b/src/cli/mod.rs deleted file mode 100644 index a24e7f2d70..0000000000 --- a/src/cli/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod args; -mod export; -mod import; -mod miner; -mod run_impl; -mod sentry_config; - -pub use self::args::get_matches; -pub use self::export::export; -pub use self::import::import; -pub use self::miner::miner; -pub use self::run_impl::{keygen, run}; -pub use self::sentry_config::SentryConfig; diff --git a/src/helper.rs b/src/helper.rs index 63a0e2636b..3f860a9298 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1,7 +1,8 @@ -use ckb_util::{Condvar, Mutex}; -use ctrlc; -use std::path::PathBuf; +use ckb_util::{parking_lot::deadlock, Condvar, Mutex}; +use log::warn; use std::sync::Arc; +use std::thread; +use std::time::Duration; pub fn wait_for_exit() { let exit = Arc::new((Mutex::new(()), Condvar::new())); @@ -17,20 +18,21 @@ pub fn wait_for_exit() { exit.1.wait(&mut l); } -pub fn require_path_exists(path: PathBuf) -> Option { - if path.exists() { - Some(path) - } else { - None - } -} +pub fn deadlock_detection() { + thread::spawn(move || loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + continue; + } -pub fn to_absolute_path(path: PathBuf) -> PathBuf { - if path.is_absolute() { - path - } else { - let mut absulute_path = ::std::env::current_dir().expect("get current_dir"); - absulute_path.push(path); - absulute_path - } + warn!("{} deadlocks detected", deadlocks.len()); + for (i, threads) in deadlocks.iter().enumerate() { + warn!("Deadlock #{}", i); + for t in threads { + warn!("Thread Id {:#?}", t.thread_id()); + warn!("{:#?}", t.backtrace()); + } + } + }); } diff --git a/src/main.rs b/src/main.rs index 33008d22c0..6ae1a79485 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,44 +1,41 @@ -mod cli; mod helper; mod setup; +mod subcommand; -use crate::setup::{get_config_path, Setup}; -use clap::ArgMatches; -use log::info; +use setup::{cli, ExitCode, Setup}; -fn main() { +fn run_app() -> Result<(), ExitCode> { // Always print backtrace on panic. ::std::env::set_var("RUST_BACKTRACE", "full"); - let matches = cli::get_matches(); - - match matches.subcommand() { - ("cli", Some(cli_matches)) => match cli_matches.subcommand() { - ("keygen", _) => cli::keygen(), - _ => unreachable!(), - }, - ("run", Some(run_matches)) => { - let setup = setup(&run_matches); - let _logger_guard = logger::init(setup.configs.logger.clone()).expect("Init Logger"); - let _sentry_guard = setup.configs.sentry.clone().init(); - cli::run(setup); + let app_matches = cli::get_matches(); + match app_matches.subcommand() { + (cli::CMD_INIT, Some(matches)) => return subcommand::init(Setup::init(&matches)?), + (cli::CMD_CLI, Some(matches)) => { + return match matches.subcommand() { + (cli::CMD_KEYGEN, _) => subcommand::cli::keygen(), + _ => unreachable!(), + }; } - ("miner", Some(miner_matches)) => cli::miner(&miner_matches), - ("export", Some(export_matches)) => cli::export(&setup(&export_matches), export_matches), - ("import", Some(import_matches)) => cli::import(&setup(&import_matches), import_matches), + _ => { + // continue + } + } + + let setup = Setup::from_matches(&app_matches)?; + let _guard = setup.setup_app(); + + match app_matches.subcommand() { + (cli::CMD_RUN, _) => subcommand::run(setup.run()?), + (cli::CMD_MINER, _) => subcommand::miner(setup.miner()?), + (cli::CMD_EXPORT, Some(matches)) => subcommand::export(setup.export(&matches)?), + (cli::CMD_IMPORT, Some(matches)) => subcommand::import(setup.import(&matches)?), _ => unreachable!(), } } -fn setup(matches: &ArgMatches<'static>) -> Setup { - let config_path = get_config_path(matches); - info!(target: "main", "Setup with config {}", config_path.display()); - Setup::setup(&config_path).unwrap_or_else(|e| { - eprintln!( - "Failed to setup with config {}, cause err: {:?}", - config_path.display(), - e - ); - ::std::process::exit(1); - }) +fn main() { + if let Some(exit_code) = run_app().err() { + ::std::process::exit(exit_code.into()); + } } diff --git a/src/setup.rs b/src/setup.rs deleted file mode 100644 index 7a53c04106..0000000000 --- a/src/setup.rs +++ /dev/null @@ -1,280 +0,0 @@ -use crate::cli::SentryConfig; -use crate::helper::{require_path_exists, to_absolute_path}; -use ckb_chain_spec::{ChainSpec, SpecPath}; -use ckb_db::DBConfig; -use ckb_miner::BlockAssemblerConfig; -use ckb_network::NetworkConfig; -use ckb_rpc::Config as RpcConfig; -use ckb_shared::tx_pool::TxPoolConfig; -use ckb_sync::Config as SyncConfig; -use clap::ArgMatches; -use config_tool::{Config as ConfigTool, ConfigError, File}; -use dir::Directories; -use logger::Config as LogConfig; -use serde_derive::Deserialize; -use std::error::Error; -use std::path::{Path, PathBuf}; - -const DEFAULT_CONFIG_PATHS: &[&str] = &["ckb.toml", "nodes/default.toml"]; - -#[derive(Clone, Debug)] -pub struct Setup { - pub configs: Configs, - pub chain_spec: ChainSpec, - pub dirs: Directories, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct ChainConfig { - pub spec: SpecPath, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct Configs { - pub data_dir: PathBuf, - pub db: DBConfig, - pub chain: ChainConfig, - pub logger: LogConfig, - pub network: NetworkConfig, - pub rpc: RpcConfig, - pub block_assembler: BlockAssemblerConfig, - pub sync: SyncConfig, - pub tx_pool: TxPoolConfig, - pub sentry: SentryConfig, -} - -pub fn get_config_path(matches: &ArgMatches) -> PathBuf { - to_absolute_path( - matches - .value_of("config") - .map_or_else(find_default_config_path, |v| { - require_path_exists(PathBuf::from(v)) - }) - .unwrap_or_else(|| { - eprintln!("No config file found!"); - ::std::process::exit(1); - }), - ) -} - -fn find_default_config_path() -> Option { - DEFAULT_CONFIG_PATHS - .iter() - .map(PathBuf::from) - .find(|p| p.exists()) -} - -impl Setup { - pub(crate) fn with_configs(mut configs: Configs) -> Result> { - let dirs = Directories::new(&configs.data_dir); - - if let Some(file) = configs.logger.file { - let path = dirs.join("logs"); - configs.logger.file = Some(path.join(file)); - } - - let chain_spec = ChainSpec::read_from_file(&configs.chain.spec).map_err(|e| { - Box::new(ConfigError::Message(format!( - "invalid chain spec {}, {}", - configs.chain.spec.display(), - e - ))) - })?; - - Ok(Setup { - configs, - chain_spec, - dirs, - }) - } - - pub fn setup>(config_path: T) -> Result> { - let mut config_tool = ConfigTool::new(); - - config_tool.merge(File::from(config_path.as_ref()))?; - - let mut configs: Configs = config_tool.try_into()?; - configs.resolve_paths(config_path.as_ref().parent().unwrap()); - - Self::with_configs(configs) - } -} - -impl Configs { - fn resolve_paths(&mut self, base: &Path) { - if self.data_dir.is_relative() { - self.data_dir = base.join(&self.data_dir); - } - self.chain.spec = self.chain.spec.expand_path(base); - if self.db.path.is_relative() { - self.db.path = base.join(&self.db.path); - } - if self.network.path.is_relative() { - self.network.path = base.join(&self.network.path); - } - } -} - -#[cfg(test)] -pub mod test { - use super::*; - use config_tool::File as ConfigFile; - use std::fs::File; - use std::io::Write; - use std::path::Path; - use tempfile; - - fn override_default_config_file>(config_path: &T) -> Result> { - let mut config_tool = ConfigTool::new(); - let default_config_path = - Path::new(env!("CARGO_MANIFEST_DIR")).join("nodes_template/default.toml"); - config_tool.merge(ConfigFile::from(default_config_path.as_path()))?; - config_tool.merge(ConfigFile::from(config_path.as_ref()))?; - - let mut configs: Configs = config_tool.try_into()?; - configs.resolve_paths(default_config_path.parent().unwrap()); - - Setup::with_configs(configs) - } - - fn write_file>(file: P, content: &str) { - let mut file = File::create(file).expect("test dir clean"); - file.write_all(content.as_bytes()) - .expect("write test content");; - } - - fn test_chain_spec() -> &'static str { - r#" - name = "ckb_test_custom" - - [genesis] - version = 0 - parent_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - timestamp = 0 - txs_commit = "0x0000000000000000000000000000000000000000000000000000000000000000" - txs_proposal = "0x0000000000000000000000000000000000000000000000000000000000000000" - difficulty = "0x233" - uncles_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - - [genesis.seal] - nonce = 233 - proof = [2, 3, 3] - - [params] - initial_block_reward = 233 - max_block_cycles = 100000000 - - [pow] - func = "Cuckoo" - - [pow.params] - edge_bits = 29 - cycle_length = 42 - - [[system_cells]] - path = "verify" - - [[system_cells]] - path = "always_success" - "# - } - - #[test] - fn test_load_config() { - let tmp_dir = tempfile::Builder::new() - .prefix("test_load_config") - .tempdir() - .unwrap(); - - let test_conifg = r#" - [network] - listen_addresses = ["/ip4/1.1.1.1/tcp/1"] - "#; - let config_path = tmp_dir.path().join("config.toml"); - write_file(&config_path, test_conifg); - let setup = override_default_config_file(&config_path); - assert!(setup.is_ok()); - assert_eq!( - setup.unwrap().configs.network.listen_addresses, - vec!["/ip4/1.1.1.1/tcp/1".parse().unwrap()] - ); - } - - #[test] - fn test_load_db_config() { - let tmp_dir = tempfile::Builder::new() - .prefix("test_load_db_config") - .tempdir() - .unwrap(); - - let test_conifg = r#" - [db.options] - disable_auto_compactions = "true" - paranoid_file_checks = "true" - "#; - let config_path = tmp_dir.path().join("config.toml"); - write_file(&config_path, test_conifg); - let setup = override_default_config_file(&config_path).unwrap(); - let options: Vec<(&str, &str)> = setup - .configs - .db - .options - .as_ref() - .unwrap() - .iter() - .map(|(k, v)| (k.as_str(), v.as_str())) - .collect(); - assert_eq!( - options.contains(&("disable_auto_compactions", "true")), - true - ); - assert_eq!(options.contains(&("paranoid_file_checks", "true")), true); - } - - #[test] - fn test_custom_chain_spec_with_config() { - let tmp_dir = tempfile::Builder::new() - .prefix("test_custom_chain_spec_with_config") - .tempdir() - .unwrap(); - - let chain_spec_path = tmp_dir.path().join("ckb_test_custom.toml"); - let test_config = format!( - r#" - [chain] - spec = {{ Local = "{}" }} - "#, - chain_spec_path.to_str().unwrap() - ); - - let config_path = tmp_dir.path().join("config.toml"); - write_file(&config_path, &test_config); - write_file(&chain_spec_path, test_chain_spec()); - - let setup = override_default_config_file(&config_path); - assert!(setup.is_ok()); - assert_eq!(setup.unwrap().chain_spec.name, "ckb_test_custom"); - } - - #[test] - fn test_testnet_chain_spec_with_config() { - let tmp_dir = tempfile::Builder::new() - .prefix("test_testnet_chain_spec_with_config") - .tempdir() - .unwrap(); - - let test_config = r#" - [chain] - spec = "Testnet" - "#; - - let config_path = tmp_dir.path().join("config.toml"); - write_file(&config_path, &test_config); - - let setup = override_default_config_file(&config_path); - assert!(setup.is_ok()); - let setup = setup.unwrap(); - assert_eq!(setup.configs.chain.spec, SpecPath::Testnet); - assert_eq!(setup.chain_spec.name, "ckb_testnet"); - } -} diff --git a/src/setup/app_config.rs b/src/setup/app_config.rs new file mode 100644 index 0000000000..231b32ede4 --- /dev/null +++ b/src/setup/app_config.rs @@ -0,0 +1,383 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use serde_derive::Deserialize; + +use ckb_chain_spec::ChainSpec; +use ckb_db::DBConfig; +use ckb_miner::BlockAssemblerConfig; +use ckb_miner::MinerConfig; +use ckb_network::NetworkConfig; +use ckb_resource::{Resource, ResourceLocator}; +use ckb_rpc::Config as RpcConfig; +use ckb_shared::tx_pool::TxPoolConfig; +use ckb_sync::Config as SyncConfig; +use logger::Config as LogConfig; + +use super::sentry_config::SentryConfig; +use super::{cli, ExitCode}; + +pub struct AppConfig { + resource: Resource, + content: AppConfigContent, +} + +pub enum AppConfigContent { + CKB(Box), + Miner(Box), +} + +#[derive(Clone, Debug, Deserialize)] +pub struct CKBAppConfig { + pub chain: ChainConfig, + pub data_dir: PathBuf, + pub logger: LogConfig, + pub sentry: SentryConfig, + + pub block_assembler: BlockAssemblerConfig, + #[serde(default)] + pub db: DBConfig, + pub network: NetworkConfig, + pub rpc: RpcConfig, + pub sync: SyncConfig, + pub tx_pool: TxPoolConfig, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct MinerAppConfig { + pub chain: ChainConfig, + pub data_dir: PathBuf, + pub logger: LogConfig, + pub sentry: SentryConfig, + + pub miner: MinerConfig, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct ChainConfig { + pub spec: PathBuf, +} + +impl AppConfig { + pub fn load_for_subcommand( + locator: &ResourceLocator, + subcommand_name: &str, + ) -> Result { + match subcommand_name { + cli::CMD_MINER => { + let resource = locator.miner(); + let config: MinerAppConfig = toml::from_slice(&resource.get()?)?; + + Ok(AppConfig { + resource, + content: AppConfigContent::with_miner( + config.derive_options(locator.root_dir())?, + ), + }) + } + _ => { + let resource = locator.ckb(); + let config: CKBAppConfig = toml::from_slice(&resource.get()?)?; + Ok(AppConfig { + resource, + content: AppConfigContent::with_ckb( + config.derive_options(locator.root_dir(), subcommand_name)?, + ), + }) + } + } + } + + pub fn logger(&self) -> &LogConfig { + match &self.content { + AppConfigContent::CKB(config) => &config.logger, + AppConfigContent::Miner(config) => &config.logger, + } + } + + pub fn sentry(&self) -> &SentryConfig { + match &self.content { + AppConfigContent::CKB(config) => &config.sentry, + AppConfigContent::Miner(config) => &config.sentry, + } + } + + pub fn chain_spec(&self, locator: &ResourceLocator) -> Result { + let spec_path = PathBuf::from(match &self.content { + AppConfigContent::CKB(config) => &config.chain.spec, + AppConfigContent::Miner(config) => &config.chain.spec, + }); + ChainSpec::resolve_relative_to(locator, spec_path, &self.resource).map_err(|err| { + eprintln!("{:?}", err); + ExitCode::Config + }) + } + + pub fn into_ckb(self) -> Result, ExitCode> { + match self.content { + AppConfigContent::CKB(config) => Ok(config), + _ => { + eprintln!("unmatched config file"); + Err(ExitCode::Failure) + } + } + } + + pub fn into_miner(self) -> Result, ExitCode> { + match self.content { + AppConfigContent::Miner(config) => Ok(config), + _ => { + eprintln!("unmatched config file"); + Err(ExitCode::Failure) + } + } + } +} + +impl AppConfigContent { + fn with_ckb(config: CKBAppConfig) -> AppConfigContent { + AppConfigContent::CKB(Box::new(config)) + } + fn with_miner(config: MinerAppConfig) -> AppConfigContent { + AppConfigContent::Miner(Box::new(config)) + } +} + +impl CKBAppConfig { + fn derive_options(mut self, root_dir: &Path, subcommand_name: &str) -> Result { + self.data_dir = canonicalize_data_dir(self.data_dir, root_dir)?; + if self.logger.log_to_file { + self.logger.file = Some(touch( + mkdir(self.data_dir.join("logs"))?.join(subcommand_name.to_string() + ".log"), + )?); + } + self.db.path = mkdir(self.data_dir.join("db"))?; + self.network.path = mkdir(self.data_dir.join("network"))?; + + Ok(self) + } +} + +impl MinerAppConfig { + fn derive_options(mut self, root_dir: &Path) -> Result { + self.data_dir = canonicalize_data_dir(self.data_dir, root_dir)?; + if self.logger.log_to_file { + self.logger.file = Some(touch(mkdir(self.data_dir.join("logs"))?.join("miner.log"))?); + } + + Ok(self) + } +} + +fn canonicalize_data_dir(data_dir: PathBuf, root_dir: &Path) -> Result { + let path = if data_dir.is_absolute() { + data_dir + } else { + root_dir.join(data_dir) + }; + + mkdir(path) +} + +fn mkdir(dir: PathBuf) -> Result { + fs::create_dir_all(&dir)?; + dir.canonicalize().map_err(Into::into) +} + +fn touch(path: PathBuf) -> Result { + fs::OpenOptions::new() + .create(true) + .append(true) + .open(&path)?; + + Ok(path) +} + +#[cfg(test)] +mod tests { + use super::*; + use ckb_resource::TemplateContext; + + fn mkdir() -> tempfile::TempDir { + tempfile::Builder::new() + .prefix("app_config_test") + .tempdir() + .unwrap() + } + + #[test] + fn test_ckb_toml() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_RUN) + .unwrap_or_else(|err| panic!(err)); + let ckb_config = app_config.into_ckb().unwrap_or_else(|err| panic!(err)); + assert_eq!(ckb_config.chain.spec, PathBuf::from("specs/dev.toml")); + assert_eq!( + ckb_config.logger.file, + Some(locator.root_dir().join("data/logs/run.log")) + ); + assert_eq!(ckb_config.db.path, locator.root_dir().join("data/db")); + assert_eq!( + ckb_config.network.path, + locator.root_dir().join("data/network") + ); + } + + #[test] + fn test_miner_toml() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_MINER) + .unwrap_or_else(|err| panic!(err)); + let miner_config = app_config.into_miner().unwrap_or_else(|err| panic!(err)); + assert_eq!(miner_config.chain.spec, PathBuf::from("specs/dev.toml")); + assert_eq!( + miner_config.logger.file, + Some(locator.root_dir().join("data/logs/miner.log")) + ); + } + + #[test] + fn test_export_dev_config_files() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let context = TemplateContext { + spec: "dev", + rpc_port: "7000", + p2p_port: "8000", + log_to_file: true, + log_to_stdout: true, + }; + { + locator.export_ckb(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_RUN) + .unwrap_or_else(|err| panic!(err)); + let ckb_config = app_config.into_ckb().unwrap_or_else(|err| panic!(err)); + assert_eq!(ckb_config.logger.filter, Some("info".to_string())); + assert_eq!(ckb_config.chain.spec, PathBuf::from("specs/dev.toml")); + assert_eq!( + ckb_config.network.listen_addresses, + vec!["/ip4/0.0.0.0/tcp/8000".parse().unwrap()] + ); + assert_eq!(ckb_config.network.connect_outbound_interval_secs, 15); + assert_eq!(ckb_config.rpc.listen_address, "0.0.0.0:7000"); + } + { + locator.export_miner(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_MINER) + .unwrap_or_else(|err| panic!(err)); + let miner_config = app_config.into_miner().unwrap_or_else(|err| panic!(err)); + assert_eq!(miner_config.logger.filter, Some("info".to_string())); + assert_eq!(miner_config.chain.spec, PathBuf::from("specs/dev.toml")); + assert_eq!(miner_config.miner.rpc_url, "http://127.0.0.1:7000/"); + } + } + + #[test] + fn test_log_to_stdout_only() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let context = TemplateContext { + spec: "dev", + rpc_port: "7000", + p2p_port: "8000", + log_to_file: false, + log_to_stdout: true, + }; + { + locator.export_ckb(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_RUN) + .unwrap_or_else(|err| panic!(err)); + let ckb_config = app_config.into_ckb().unwrap_or_else(|err| panic!(err)); + assert_eq!(ckb_config.logger.file, None); + assert_eq!(ckb_config.logger.log_to_file, false); + assert_eq!(ckb_config.logger.log_to_stdout, true); + } + { + locator.export_miner(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_MINER) + .unwrap_or_else(|err| panic!(err)); + let miner_config = app_config.into_miner().unwrap_or_else(|err| panic!(err)); + assert_eq!(miner_config.logger.file, None); + assert_eq!(miner_config.logger.log_to_file, false); + assert_eq!(miner_config.logger.log_to_stdout, true); + } + } + + #[test] + fn test_export_testnet_config_files() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let context = TemplateContext { + spec: "testnet", + rpc_port: "7000", + p2p_port: "8000", + log_to_file: true, + log_to_stdout: true, + }; + locator.export_ckb(&context).expect("export config files"); + { + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_RUN) + .unwrap_or_else(|err| panic!(err)); + let ckb_config = app_config.into_ckb().unwrap_or_else(|err| panic!(err)); + assert_eq!(ckb_config.logger.filter, Some("info".to_string())); + assert_eq!(ckb_config.chain.spec, PathBuf::from("specs/testnet.toml")); + assert_eq!( + ckb_config.network.listen_addresses, + vec!["/ip4/0.0.0.0/tcp/8000".parse().unwrap()] + ); + assert_eq!(ckb_config.network.connect_outbound_interval_secs, 15); + assert_eq!(ckb_config.rpc.listen_address, "0.0.0.0:7000"); + } + { + locator.export_miner(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_MINER) + .unwrap_or_else(|err| panic!(err)); + let miner_config = app_config.into_miner().unwrap_or_else(|err| panic!(err)); + assert_eq!(miner_config.logger.filter, Some("info".to_string())); + assert_eq!(miner_config.chain.spec, PathBuf::from("specs/testnet.toml")); + assert_eq!(miner_config.miner.rpc_url, "http://127.0.0.1:7000/"); + } + } + + #[test] + fn test_export_integration_config_files() { + let dir = mkdir(); + let locator = ResourceLocator::with_root_dir(dir.path().to_path_buf()).unwrap(); + let context = TemplateContext { + spec: "integration", + rpc_port: "7000", + p2p_port: "8000", + log_to_file: true, + log_to_stdout: true, + }; + locator.export_ckb(&context).expect("export config files"); + { + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_RUN) + .unwrap_or_else(|err| panic!(err)); + let ckb_config = app_config.into_ckb().unwrap_or_else(|err| panic!(err)); + assert_eq!( + ckb_config.chain.spec, + PathBuf::from("specs/integration.toml") + ); + assert_eq!( + ckb_config.network.listen_addresses, + vec!["/ip4/0.0.0.0/tcp/8000".parse().unwrap()] + ); + assert_eq!(ckb_config.network.connect_outbound_interval_secs, 1); + assert_eq!(ckb_config.rpc.listen_address, "0.0.0.0:7000"); + } + { + locator.export_miner(&context).expect("export config files"); + let app_config = AppConfig::load_for_subcommand(&locator, cli::CMD_MINER) + .unwrap_or_else(|err| panic!(err)); + let miner_config = app_config.into_miner().unwrap_or_else(|err| panic!(err)); + assert_eq!( + miner_config.chain.spec, + PathBuf::from("specs/integration.toml") + ); + assert_eq!(miner_config.miner.rpc_url, "http://127.0.0.1:7000/"); + } + } +} diff --git a/src/setup/args.rs b/src/setup/args.rs new file mode 100644 index 0000000000..934bc4be21 --- /dev/null +++ b/src/setup/args.rs @@ -0,0 +1,44 @@ +use super::app_config::CKBAppConfig; +use ckb_chain_spec::consensus::Consensus; +use ckb_instrument::Format; +use ckb_miner::MinerConfig; +use ckb_pow::PowEngine; +use ckb_resource::ResourceLocator; +use std::path::PathBuf; +use std::sync::Arc; + +pub struct ExportArgs { + pub config: Box, + pub consensus: Consensus, + pub format: Format, + pub target: PathBuf, +} + +pub struct ImportArgs { + pub config: Box, + pub consensus: Consensus, + pub format: Format, + pub source: PathBuf, +} + +pub struct RunArgs { + pub config: Box, + pub consensus: Consensus, +} + +pub struct MinerArgs { + pub config: MinerConfig, + pub pow_engine: Arc, +} + +pub struct InitArgs { + pub locator: ResourceLocator, + pub spec: String, + pub rpc_port: String, + pub p2p_port: String, + pub log_to_file: bool, + pub log_to_stdout: bool, + pub export_specs: bool, + pub list_specs: bool, + pub force: bool, +} diff --git a/src/setup/cli.rs b/src/setup/cli.rs new file mode 100644 index 0000000000..5b6df9da1f --- /dev/null +++ b/src/setup/cli.rs @@ -0,0 +1,154 @@ +use build_info::{get_version, Version}; +use ckb_resource::{DEFAULT_P2P_PORT, DEFAULT_RPC_PORT, DEFAULT_SPEC}; +use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; + +pub const CMD_RUN: &str = "run"; +pub const CMD_MINER: &str = "miner"; +pub const CMD_EXPORT: &str = "export"; +pub const CMD_IMPORT: &str = "import"; +pub const CMD_INIT: &str = "init"; +pub const CMD_CLI: &str = "cli"; +pub const CMD_KEYGEN: &str = "keygen"; + +pub const ARG_CONFIG_DIR: &str = "config-dir"; +pub const ARG_FORMAT: &str = "format"; +pub const ARG_TARGET: &str = "target"; +pub const ARG_SOURCE: &str = "source"; +pub const ARG_LIST_SPECS: &str = "list-specs"; +pub const ARG_SPEC: &str = "spec"; +pub const ARG_EXPORT_SPECS: &str = "export-specs"; +pub const ARG_P2P_PORT: &str = "p2p-port"; +pub const ARG_RPC_PORT: &str = "rpc-port"; +pub const ARG_FORCE: &str = "force"; +pub const ARG_LOG_TO: &str = "log-to"; + +pub fn get_matches() -> ArgMatches<'static> { + let version = get_version!(); + + App::new("ckb") + .author("Nervos Core Dev ") + .about("Nervos CKB - The Common Knowledge Base") + .version(version.short().as_str()) + .long_version(version.long().as_str()) + .setting(AppSettings::SubcommandRequiredElseHelp) + .arg( + Arg::with_name(ARG_CONFIG_DIR) + .global(true) + .short("C") + .value_name("path") + .takes_value(true) + .help( + "Run as if ckb was started in instead of the current working directory.", + ), + ) + .subcommand(run()) + .subcommand(miner()) + .subcommand(export()) + .subcommand(import()) + .subcommand(cli()) + .subcommand(init()) + .get_matches() +} + +fn run() -> App<'static, 'static> { + SubCommand::with_name(CMD_RUN).about("Running ckb node") +} + +fn miner() -> App<'static, 'static> { + SubCommand::with_name(CMD_MINER).about("Running ckb miner") +} + +fn arg_format() -> Arg<'static, 'static> { + Arg::with_name(ARG_FORMAT) + .short("f") + .long(ARG_FORMAT) + .possible_values(&["bin", "json"]) + .required(true) + .takes_value(true) + .help("Specify the format.") +} + +fn export() -> App<'static, 'static> { + SubCommand::with_name(CMD_EXPORT) + .about("Export ckb data") + .arg(arg_format()) + .arg( + Arg::with_name(ARG_TARGET) + .short("t") + .long(ARG_TARGET) + .value_name("path") + .required(true) + .index(1) + .help("Specify the export target path."), + ) +} + +fn import() -> App<'static, 'static> { + SubCommand::with_name(CMD_IMPORT) + .about("Import ckb data") + .arg(arg_format()) + .arg( + Arg::with_name(ARG_SOURCE) + .short("s") + .long(ARG_SOURCE) + .value_name("path") + .required(true) + .index(1) + .help("Specify the exported data path."), + ) +} + +fn cli() -> App<'static, 'static> { + SubCommand::with_name(CMD_CLI) + .about("CLI tools") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(SubCommand::with_name(CMD_KEYGEN).about("Generate new key")) +} + +fn init() -> App<'static, 'static> { + SubCommand::with_name(CMD_INIT) + .about("Create a CKB direcotry or reinitialize an existing one") + .arg( + Arg::with_name(ARG_LIST_SPECS) + .short("l") + .long(ARG_LIST_SPECS) + .help("List available chain specs"), + ) + .arg( + Arg::with_name(ARG_SPEC) + .short("s") + .long(ARG_SPEC) + .default_value(DEFAULT_SPEC) + .help("Export config files for "), + ) + .arg( + Arg::with_name(ARG_LOG_TO) + .long(ARG_LOG_TO) + .possible_values(&["file", "stdout", "both"]) + .default_value("both") + .help("Configures where the logs should print"), + ) + .arg( + Arg::with_name(ARG_FORCE) + .short("f") + .long(ARG_FORCE) + .help("Force overwriting existing files"), + ) + .arg( + Arg::with_name(ARG_RPC_PORT) + .long(ARG_RPC_PORT) + .default_value(DEFAULT_RPC_PORT) + .help("Replace CKB RPC port in the exported config file"), + ) + .arg( + Arg::with_name(ARG_P2P_PORT) + .long(ARG_P2P_PORT) + .default_value(DEFAULT_P2P_PORT) + .help("Replace CKB P2P port in the exported config file"), + ) + .arg( + Arg::with_name(ARG_EXPORT_SPECS) + .long(ARG_EXPORT_SPECS) + .help("Export spec files as well"), + ) +} diff --git a/src/setup/exit_code.rs b/src/setup/exit_code.rs new file mode 100644 index 0000000000..1ace6310fc --- /dev/null +++ b/src/setup/exit_code.rs @@ -0,0 +1,45 @@ +use std::io; + +/// Uses 0, 64 - 113 as exit code. +#[repr(i32)] +#[derive(Copy, Clone)] +pub enum ExitCode { + Cli = 64, + Config = 65, + IO = 66, + Failure = 113, +} + +impl ExitCode { + pub fn into(self) -> i32 { + self as i32 + } +} + +impl From for ExitCode { + fn from(err: io::Error) -> ExitCode { + eprintln!("IO Error: {:?}", err); + ExitCode::IO + } +} + +impl From for ExitCode { + fn from(err: toml::de::Error) -> ExitCode { + eprintln!("Config Error: {:?}", err); + ExitCode::Config + } +} + +impl From for ExitCode { + fn from(err: log::SetLoggerError) -> ExitCode { + eprintln!("Config Error: {:?}", err); + ExitCode::Config + } +} + +impl From for ExitCode { + fn from(err: clap::Error) -> ExitCode { + eprintln!("Args Error: {:?}", err); + ExitCode::Cli + } +} diff --git a/src/setup/mod.rs b/src/setup/mod.rs new file mode 100644 index 0000000000..ea5c4a17ea --- /dev/null +++ b/src/setup/mod.rs @@ -0,0 +1,169 @@ +mod app_config; +mod args; +pub mod cli; +mod exit_code; +mod sentry_config; + +pub use app_config::AppConfig; +pub use args::{ExportArgs, ImportArgs, InitArgs, MinerArgs, RunArgs}; +pub use exit_code::ExitCode; + +use ckb_chain_spec::{consensus::Consensus, ChainSpec}; +use ckb_instrument::Format; +use ckb_resource::ResourceLocator; +use clap::{value_t, ArgMatches}; +use logger::LoggerInitGuard; +use std::path::PathBuf; + +pub struct Setup { + subcommand_name: String, + resource_locator: ResourceLocator, + config: AppConfig, +} + +pub struct SetupGuard { + #[allow(dead_code)] + logger_guard: LoggerInitGuard, + #[allow(dead_code)] + sentry_guard: Option, +} + +impl Setup { + pub fn from_matches<'m>(matches: &ArgMatches<'m>) -> Result { + let subcommand_name = match matches.subcommand_name() { + Some(subcommand_name) => subcommand_name, + None => { + eprintln!("expect a subcommand"); + return Err(ExitCode::Cli); + } + }; + + let resource_locator = locator_from_matches(matches)?; + let config = AppConfig::load_for_subcommand(&resource_locator, subcommand_name)?; + + Ok(Setup { + subcommand_name: subcommand_name.to_string(), + resource_locator, + config, + }) + } + + pub fn setup_app(&self) -> Result { + let logger_guard = logger::init(self.config.logger().clone())?; + let sentry_guard = if is_daemon(&self.subcommand_name) { + Some(self.config.sentry().init()) + } else { + None + }; + + Ok(SetupGuard { + logger_guard, + sentry_guard, + }) + } + + pub fn run(self) -> Result { + let consensus = self.consensus()?; + let config = self.config.into_ckb()?; + + Ok(RunArgs { config, consensus }) + } + + pub fn miner(self) -> Result { + let spec = self.chain_spec()?; + let config = self.config.into_miner()?; + let pow_engine = spec.pow_engine(); + + Ok(MinerArgs { + pow_engine, + config: config.miner, + }) + } + + pub fn import<'m>(self, matches: &ArgMatches<'m>) -> Result { + let consensus = self.consensus()?; + let config = self.config.into_ckb()?; + let format = value_t!(matches.value_of(cli::ARG_FORMAT), Format)?; + let source = value_t!(matches.value_of(cli::ARG_SOURCE), PathBuf)?; + + Ok(ImportArgs { + config, + consensus, + format, + source, + }) + } + + pub fn export<'m>(self, matches: &ArgMatches<'m>) -> Result { + let consensus = self.consensus()?; + let config = self.config.into_ckb()?; + let format = value_t!(matches.value_of(cli::ARG_FORMAT), Format)?; + let target = value_t!(matches.value_of(cli::ARG_TARGET), PathBuf)?; + + Ok(ExportArgs { + config, + consensus, + format, + target, + }) + } + + pub fn init<'m>(matches: &ArgMatches<'m>) -> Result { + let locator = locator_from_matches(matches)?; + let export_specs = matches.is_present(cli::ARG_EXPORT_SPECS); + let list_specs = matches.is_present(cli::ARG_LIST_SPECS); + let force = matches.is_present(cli::ARG_FORCE); + let spec = matches.value_of(cli::ARG_SPEC).unwrap().to_string(); + let rpc_port = matches.value_of(cli::ARG_RPC_PORT).unwrap().to_string(); + let p2p_port = matches.value_of(cli::ARG_P2P_PORT).unwrap().to_string(); + let (log_to_file, log_to_stdout) = match matches.value_of(cli::ARG_LOG_TO) { + Some("file") => (true, false), + Some("stdout") => (false, true), + Some("both") => (true, true), + _ => unreachable!(), + }; + + Ok(InitArgs { + locator, + spec, + rpc_port, + p2p_port, + export_specs, + list_specs, + force, + log_to_file, + log_to_stdout, + }) + } + + fn chain_spec(&self) -> Result { + self.config.chain_spec(&self.resource_locator) + } + + fn consensus(&self) -> Result { + consensus_from_spec(&self.chain_spec()?) + } +} + +fn is_daemon(subcommand_name: &str) -> bool { + match subcommand_name { + cli::CMD_RUN => true, + cli::CMD_MINER => true, + _ => false, + } +} + +fn consensus_from_spec(spec: &ChainSpec) -> Result { + spec.to_consensus().map_err(|err| { + eprintln!("{:?}", err); + ExitCode::Config + }) +} + +fn locator_from_matches<'m>(matches: &ArgMatches<'m>) -> Result { + let config_dir = match matches.value_of(cli::ARG_CONFIG_DIR) { + Some(arg_config_dir) => PathBuf::from(arg_config_dir), + None => ::std::env::current_dir()?, + }; + ResourceLocator::with_root_dir(config_dir).map_err(Into::into) +} diff --git a/src/cli/sentry_config.rs b/src/setup/sentry_config.rs similarity index 91% rename from src/cli/sentry_config.rs rename to src/setup/sentry_config.rs index 073e3207e5..4a59856670 100644 --- a/src/cli/sentry_config.rs +++ b/src/setup/sentry_config.rs @@ -8,8 +8,8 @@ pub struct SentryConfig { } impl SentryConfig { - pub fn init(self) -> sentry::internals::ClientInitGuard { - let guard = sentry::init(&self); + pub fn init(&self) -> sentry::internals::ClientInitGuard { + let guard = sentry::init(self); if guard.is_enabled() { sentry::integrations::panic::register_panic_handler(); info!(target: "sentry", "**Notice**: \ diff --git a/src/subcommand/cli.rs b/src/subcommand/cli.rs new file mode 100644 index 0000000000..bf1bf66e78 --- /dev/null +++ b/src/subcommand/cli.rs @@ -0,0 +1,9 @@ +use crate::setup::ExitCode; +use crypto::secp::Generator; +use numext_fixed_hash::H256; + +pub fn keygen() -> Result<(), ExitCode> { + let result: H256 = Generator::new().random_privkey().into(); + println!("{:#x}", result); + Ok(()) +} diff --git a/src/subcommand/export.rs b/src/subcommand/export.rs new file mode 100644 index 0000000000..48cf159c63 --- /dev/null +++ b/src/subcommand/export.rs @@ -0,0 +1,18 @@ +use crate::setup::{ExitCode, ExportArgs}; +use ckb_db::diskdb::RocksDB; +use ckb_instrument::Export; +use ckb_shared::cachedb::CacheDB; +use ckb_shared::shared::SharedBuilder; + +pub fn export(args: ExportArgs) -> Result<(), ExitCode> { + let shared = SharedBuilder::>::default() + .consensus(args.consensus) + .db(&args.config.db) + .build(); + Export::new(shared, args.format, args.target) + .execute() + .map_err(|err| { + eprintln!("Export error: {:?}", err); + ExitCode::Failure + }) +} diff --git a/src/subcommand/import.rs b/src/subcommand/import.rs new file mode 100644 index 0000000000..191d6d49b5 --- /dev/null +++ b/src/subcommand/import.rs @@ -0,0 +1,25 @@ +use crate::setup::{ExitCode, ImportArgs}; +use ckb_chain::chain::ChainBuilder; +use ckb_db::diskdb::RocksDB; +use ckb_instrument::Import; +use ckb_notify::NotifyService; +use ckb_shared::cachedb::CacheDB; +use ckb_shared::shared::SharedBuilder; + +pub fn import(args: ImportArgs) -> Result<(), ExitCode> { + let shared = SharedBuilder::>::default() + .consensus(args.consensus) + .db(&args.config.db) + .build(); + + let notify = NotifyService::default().start::<&str>(None); + let chain_service = ChainBuilder::new(shared.clone(), notify).build(); + let chain_controller = chain_service.start::<&str>(Some("ImportChainService")); + + Import::new(chain_controller, args.format, args.source) + .execute() + .map_err(|err| { + eprintln!("Import error: {:?}", err); + ExitCode::Failure + }) +} diff --git a/src/subcommand/init.rs b/src/subcommand/init.rs new file mode 100644 index 0000000000..cf214e8f4d --- /dev/null +++ b/src/subcommand/init.rs @@ -0,0 +1,50 @@ +use crate::setup::{ExitCode, InitArgs}; +use ckb_resource::{ + TemplateContext, AVAILABLE_SPECS, CKB_CONFIG_FILE_NAME, MINER_CONFIG_FILE_NAME, + SPECS_RESOURCE_DIR_NAME, +}; + +pub fn init(args: InitArgs) -> Result<(), ExitCode> { + if args.list_specs { + for spec in AVAILABLE_SPECS { + println!("{}", spec); + } + return Ok(()); + } + + let context = TemplateContext { + spec: &args.spec, + rpc_port: &args.rpc_port, + p2p_port: &args.p2p_port, + log_to_file: args.log_to_file, + log_to_stdout: args.log_to_stdout, + }; + + let exported = args.locator.exported(); + if !args.force && exported { + eprintln!("Config files already exists, use --force to overwrite."); + return Err(ExitCode::Failure); + } + + println!( + "{} CKB directory in {}", + if !exported { + "Initialized" + } else { + "Reinitialized" + }, + args.locator.root_dir().display() + ); + + println!("export {}", CKB_CONFIG_FILE_NAME); + args.locator.export_ckb(&context)?; + println!("export {}", MINER_CONFIG_FILE_NAME); + args.locator.export_miner(&context)?; + + if args.export_specs { + println!("export {}", SPECS_RESOURCE_DIR_NAME); + args.locator.export_specs()?; + } + + Ok(()) +} diff --git a/src/subcommand/miner.rs b/src/subcommand/miner.rs new file mode 100644 index 0000000000..9e161f6fe0 --- /dev/null +++ b/src/subcommand/miner.rs @@ -0,0 +1,24 @@ +use crate::setup::{ExitCode, MinerArgs}; +use ckb_miner::{Client, Miner}; +use ckb_util::Mutex; +use crossbeam_channel::unbounded; +use std::sync::Arc; +use std::thread; + +pub fn miner(args: MinerArgs) -> Result<(), ExitCode> { + let (new_work_tx, new_work_rx) = unbounded(); + + let work = Arc::new(Mutex::new(None)); + + let client = Client::new(Arc::clone(&work), new_work_tx, args.config); + + let miner = Miner::new(work, args.pow_engine, new_work_rx, client.clone()); + + thread::Builder::new() + .name("client".to_string()) + .spawn(move || client.poll_block_template()) + .expect("Start client failed!"); + + miner.run(); + Ok(()) +} diff --git a/src/subcommand/mod.rs b/src/subcommand/mod.rs new file mode 100644 index 0000000000..fc49eb694f --- /dev/null +++ b/src/subcommand/mod.rs @@ -0,0 +1,12 @@ +pub mod cli; +mod export; +mod import; +mod init; +mod miner; +mod run; + +pub use self::export::export; +pub use self::import::import; +pub use self::init::init; +pub use self::miner::miner; +pub use self::run::run; diff --git a/src/cli/run_impl.rs b/src/subcommand/run.rs similarity index 76% rename from src/cli/run_impl.rs rename to src/subcommand/run.rs index b132591461..6d5b2d6415 100644 --- a/src/cli/run_impl.rs +++ b/src/subcommand/run.rs @@ -1,5 +1,5 @@ -use crate::helper::wait_for_exit; -use crate::Setup; +use crate::helper::{deadlock_detection, wait_for_exit}; +use crate::setup::{ExitCode, RunArgs}; use ckb_chain::chain::{ChainBuilder, ChainController}; use ckb_db::diskdb::RocksDB; use ckb_miner::BlockAssembler; @@ -11,22 +11,16 @@ use ckb_shared::index::ChainIndex; use ckb_shared::shared::{Shared, SharedBuilder}; use ckb_sync::{NetTimeProtocol, NetworkProtocol, Relayer, Synchronizer}; use ckb_traits::chain_provider::ChainProvider; -use crypto::secp::Generator; use log::info; -use numext_fixed_hash::H256; use std::sync::Arc; -pub fn run(setup: Setup) { - let consensus = setup - .chain_spec - .to_consensus(&setup.configs.chain.spec) - .unwrap(); +pub fn run(args: RunArgs) -> Result<(), ExitCode> { + deadlock_detection(); let shared = SharedBuilder::>::default() - .consensus(consensus) - .db(&setup.configs.db) - .tx_pool_config(setup.configs.tx_pool.clone()) - .txs_verify_cache_size(setup.configs.tx_pool.txs_verify_cache_size) + .consensus(args.consensus) + .db(&args.config.db) + .tx_pool_config(args.config.tx_pool) .build(); let notify = NotifyService::default().start(Some("notify")); @@ -34,11 +28,11 @@ pub fn run(setup: Setup) { let chain_controller = setup_chain(shared.clone(), notify.clone()); info!(target: "main", "chain genesis hash: {:#x}", shared.genesis_hash()); - let block_assembler = BlockAssembler::new(shared.clone(), setup.configs.block_assembler); + let block_assembler = BlockAssembler::new(shared.clone(), args.config.block_assembler); let block_assembler_controller = block_assembler.start(Some("MinerAgent"), ¬ify); let synchronizer = - Synchronizer::new(chain_controller.clone(), shared.clone(), setup.configs.sync); + Synchronizer::new(chain_controller.clone(), shared.clone(), args.config.sync); let relayer = Relayer::new( chain_controller.clone(), @@ -49,7 +43,7 @@ pub fn run(setup: Setup) { let net_time_checker = NetTimeProtocol::default(); let network_state = Arc::new( - NetworkState::from_config(setup.configs.network).expect("Init network state failed"), + NetworkState::from_config(args.config.network).expect("Init network state failed"), ); let protocols = vec![ CKBProtocol::new( @@ -79,7 +73,7 @@ pub fn run(setup: Setup) { .expect("Start network service failed"); let rpc_server = RpcServer::new( - setup.configs.rpc, + args.config.rpc, network_controller, shared, chain_controller, @@ -92,6 +86,8 @@ pub fn run(setup: Setup) { rpc_server.close(); info!(target: "main", "Jsonrpc shutdown"); + + Ok(()) } fn setup_chain( @@ -101,8 +97,3 @@ fn setup_chain( let chain_service = ChainBuilder::new(shared, notify).build(); chain_service.start(Some("ChainService")) } - -pub fn keygen() { - let result: H256 = Generator::new().random_privkey().into(); - println!("{:#x}", result); -} diff --git a/sync/src/relayer/transaction_process.rs b/sync/src/relayer/transaction_process.rs index 1903d76fa8..8101301527 100644 --- a/sync/src/relayer/transaction_process.rs +++ b/sync/src/relayer/transaction_process.rs @@ -73,7 +73,8 @@ where } } Err(PoolError::InvalidTx(TransactionError::UnknownInput)) - | Err(PoolError::InvalidTx(TransactionError::Conflict)) => { + | Err(PoolError::InvalidTx(TransactionError::Conflict)) + | Err(PoolError::Duplicate) => { // this error may occured when peer's tip is different with us, // we can't proof peer is bad so just ignore this debug!(target: "relay", "peer {} relay a conflict or missing input tx: {:?}", self.peer, tx); diff --git a/sync/src/synchronizer/block_fetcher.rs b/sync/src/synchronizer/block_fetcher.rs index 51b852e412..3477fb66f4 100644 --- a/sync/src/synchronizer/block_fetcher.rs +++ b/sync/src/synchronizer/block_fetcher.rs @@ -10,7 +10,7 @@ use ckb_shared::index::ChainIndex; use ckb_traits::ChainProvider; use ckb_util::try_option; use faketime::unix_time_as_millis; -use log::debug; +use log::{debug, trace}; use numext_fixed_hash::H256; use numext_fixed_uint::U256; use std::cmp; @@ -48,7 +48,7 @@ where .or_insert_with(Default::default); if inflight.timestamp < unix_time_as_millis().saturating_sub(BLOCK_DOWNLOAD_TIMEOUT) { - debug!(target: "sync", "[block downloader] inflight block download timeout"); + trace!(target: "sync", "[block downloader] inflight block download timeout"); inflight.clear(); } @@ -123,7 +123,7 @@ where } pub fn fetch(self) -> Option> { - debug!(target: "sync", "[block downloader] BlockFetcher process"); + trace!(target: "sync", "[block downloader] BlockFetcher process"); if self.initial_and_check_inflight() { debug!(target: "sync", "[block downloader] inflight count reach limit"); @@ -133,7 +133,7 @@ where let best_known_header = match self.peer_best_known_header() { Some(best_known_header) => best_known_header, _ => { - debug!(target: "sync", "[block downloader] peer_best_known_header not found peer={}", self.peer); + trace!(target: "sync", "[block downloader] peer_best_known_header not found peer={}", self.peer); return None; } }; diff --git a/sync/src/synchronizer/mod.rs b/sync/src/synchronizer/mod.rs index 8f31860a28..aa452947bc 100644 --- a/sync/src/synchronizer/mod.rs +++ b/sync/src/synchronizer/mod.rs @@ -628,9 +628,7 @@ impl Synchronizer { } } for peer in eviction { - warn!(target: "sync", "timeout eviction peer={}", peer); - // Do not connect this peer in 3 minutes - nc.ban_peer(peer, Duration::from_secs(180)); + info!(target: "sync", "timeout eviction peer={}", peer); nc.disconnect(peer); } } diff --git a/test/Cargo.toml b/test/Cargo.toml index b21b83ff67..e54e8982e0 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -15,7 +15,6 @@ ckb-sync = { path = "../sync" } ckb-protocol = { path = "../protocol"} ckb-util = { path = "../util" } numext-fixed-hash = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"] } -fs_extra = "1.1" tempfile = "3.0" jsonrpc-client-core = "0.5.0" jsonrpc-client-http = "0.5.0" diff --git a/test/fixtures/nodes_template/default.toml b/test/fixtures/nodes_template/default.toml deleted file mode 100644 index e37013e567..0000000000 --- a/test/fixtures/nodes_template/default.toml +++ /dev/null @@ -1,54 +0,0 @@ -data_dir = "default" - -[db] -path = "default/db" - -[chain] -spec = { Local = "spec/integration_test.toml" } - -[logger] -file = "ckb.log" -filter = "info,network=trace,rpc=debug,sync=debug,relay=debug" -color = true - -[network] -path = "default/network" -listen_addresses = ["/ip4/0.0.0.0/tcp/P2P_PORT"] -public_addresses = [] -bootnodes = [] -reserved_peers = [] -reserved_only = false -max_peers = 125 -max_outbound_peers = 30 -ping_interval_secs = 15 -ping_timeout_secs = 20 -connect_outbound_interval_secs = 1 - -[rpc] -listen_address = "0.0.0.0:RPC_PORT" - -max_request_body_size = 10485760 - -modules = ["Net", "Pool", "Miner", "Chain", "IntegrationTest", "Trace"] - -[sync] -verification_level = "Full" -orphan_block_limit = 1024 - -[tx_pool] -max_pool_size = 10000 -max_orphan_size = 10000 -max_proposal_size = 10000 -max_cache_size = 1000 -max_pending_size = 10000 -trace = 100 -txs_verify_cache_size = 100000 - -[block_assembler] -# value is set as always success binary hash -binary_hash = "0x0000000000000000000000000000000000000000000000000000000000000001" -args = [] - -[sentry] -# set to blank to disable sentry error collection -dsn = "" diff --git a/test/fixtures/nodes_template/spec/cells/always_success b/test/fixtures/nodes_template/spec/cells/always_success deleted file mode 100755 index 248e53c5a0..0000000000 Binary files a/test/fixtures/nodes_template/spec/cells/always_success and /dev/null differ diff --git a/test/src/main.rs b/test/src/main.rs index d059a4df04..b3425e941d 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -5,8 +5,7 @@ use std::env; fn main() { let log_config = Config { filter: Some("info".to_owned()), - color: true, - file: None, + ..Default::default() }; let _logger_guard = logger::init(log_config).expect("init Logger"); diff --git a/test/src/node.rs b/test/src/node.rs index cfedb8ee98..7f3fec2d1c 100644 --- a/test/src/node.rs +++ b/test/src/node.rs @@ -6,19 +6,14 @@ use ckb_core::script::Script; use ckb_core::transaction::{CellInput, CellOutput, OutPoint, Transaction, TransactionBuilder}; use ckb_core::BlockNumber; use ckb_util::TryInto; -use fs_extra::dir::{copy, CopyOptions}; use jsonrpc_client_http::{HttpHandle, HttpTransport}; use jsonrpc_types::{BlockTemplate, CellbaseTemplate}; use log::info; use numext_fixed_hash::H256; use rand; -use std::fs::File; -use std::io::{Error, Read, Write}; -use std::path::PathBuf; +use std::io::Error; use std::process::{Child, Command, Stdio}; -const DEFAULT_CONFIG_FILE: &str = "default.toml"; - pub struct Node { pub binary: String, pub dir: String, @@ -54,11 +49,7 @@ impl Node { pub fn start(&mut self) { self.init_config_file().expect("failed to init config file"); let child_process = Command::new(self.binary.to_owned()) - .args(&[ - "run", - "-c", - &format!("{}/{}", self.dir, DEFAULT_CONFIG_FILE), - ]) + .args(&["-C", &self.dir, "run"]) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::inherit()) @@ -240,22 +231,21 @@ impl Node { } fn init_config_file(&self) -> Result<(), Error> { - let mut options = CopyOptions::new(); - options.copy_inside = true; - let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures/nodes_template"); - let dest = PathBuf::from(&self.dir); - copy(source, &dest, &options).expect("failed to copy template"); - - let mut data = String::new(); - { - let mut file = File::open(dest.join(DEFAULT_CONFIG_FILE))?; - file.read_to_string(&mut data)?; - } - let new_data = data - .replace("P2P_PORT", &self.p2p_port.to_string()) - .replace("RPC_PORT", &self.rpc_port.to_string()); - let mut file = File::create(dest.join(DEFAULT_CONFIG_FILE))?; - file.write_all(new_data.as_bytes())?; - Ok(()) + let rpc_port = format!("{}", self.rpc_port).to_string(); + let p2p_port = format!("{}", self.p2p_port).to_string(); + Command::new(self.binary.to_owned()) + .args(&[ + "-C", + &self.dir, + "init", + "--spec", + "integration", + "--rpc-port", + &rpc_port, + "--p2p-port", + &p2p_port, + ]) + .output() + .map(|_| ()) } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 504b766886..c127b8cfc0 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -6,4 +6,4 @@ authors = ["Nervos Core Dev "] edition = "2018" [dependencies] -parking_lot = "0.7" +parking_lot = {version = "0.7", features = ["deadlock_detection"]} diff --git a/util/dir/Cargo.toml b/util/dir/Cargo.toml deleted file mode 100644 index 4aceece87e..0000000000 --- a/util/dir/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "dir" -version = "0.9.0-pre" -license = "MIT" -authors = ["Nervos Core Dev "] -edition = "2018" diff --git a/util/dir/src/lib.rs b/util/dir/src/lib.rs deleted file mode 100644 index 6e8cf10b22..0000000000 --- a/util/dir/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::fs; -use std::path::{Path, PathBuf}; - -#[derive(Debug, PartialEq, Clone)] -pub struct Directories { - pub base: PathBuf, -} - -impl Directories { - pub fn new>(base: P) -> Self { - let base = base.as_ref().to_path_buf(); - Directories { base } - } - - pub fn join>(&self, path: P) -> PathBuf { - let result = self.base.join(path.as_ref()); - fs::create_dir_all(&result).expect("Unable to create dir"); - result - } -} diff --git a/util/logger/src/lib.rs b/util/logger/src/lib.rs index 6469172ab4..dc21e6616e 100644 --- a/util/logger/src/lib.rs +++ b/util/logger/src/lib.rs @@ -37,8 +37,14 @@ impl Logger { } let (sender, receiver) = unbounded(); - let file = config.file; - let enable_color = config.color; + let Config { + color, + file, + log_to_file, + log_to_stdout, + .. + } = config; + let file = if log_to_file { file } else { None }; let tb = thread::Builder::new() .name("LogWriter".to_owned()) @@ -57,16 +63,14 @@ impl Logger { match receiver.recv() { Ok(Message::Record(record)) => { let removed_color = sanitize_color(record.as_ref()); - let output = if enable_color { - record - } else { - removed_color.clone() - }; + let output = if color { record } else { removed_color.clone() }; if let Some(mut file) = file.as_ref() { let _ = file.write_all(removed_color.as_bytes()); let _ = file.write_all(b"\n"); }; - println!("{}", output); + if log_to_stdout { + println!("{}", output); + } } Ok(Message::Terminate) | Err(_) => { break; @@ -93,6 +97,8 @@ pub struct Config { pub filter: Option, pub color: bool, pub file: Option, + pub log_to_file: bool, + pub log_to_stdout: bool, } impl Default for Config { @@ -101,6 +107,8 @@ impl Default for Config { filter: None, color: !cfg!(windows), file: None, + log_to_file: false, + log_to_stdout: true, } } } diff --git a/util/src/lib.rs b/util/src/lib.rs index 7774644263..bf111fca80 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -1,7 +1,9 @@ mod unstable; pub use crate::unstable::{TryFrom, TryInto}; -pub use parking_lot::{Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub use parking_lot::{ + self, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, +}; /// Helper macro for reducing boilerplate code for matching `Option` together /// with early return.