diff --git a/.gitignore b/.gitignore index d2844c8..73210e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -bin/ +bin/* dex/target crank/target .idea* diff --git a/Cargo.lock b/Cargo.lock index c929ba0..ad81b23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" +[[package]] +name = "bstr" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" +dependencies = [ + "memchr", +] + [[package]] name = "buf_redux" version = "0.8.4" @@ -809,6 +818,7 @@ checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -831,6 +841,27 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +dependencies = [ + "futures 0.1.29", + "num_cpus", +] + +[[package]] +name = "futures-executor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.5" @@ -870,6 +901,8 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ + "futures 0.1.29", + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -930,6 +963,37 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +[[package]] +name = "globset" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "h2" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +dependencies = [ + "byteorder", + "bytes 0.4.12", + "fnv", + "futures 0.1.29", + "http 0.1.21", + "indexmap", + "log", + "slab", + "string", + "tokio-io", +] + [[package]] name = "h2" version = "0.2.6" @@ -941,7 +1005,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.1", "indexmap", "slab", "tokio 0.2.22", @@ -965,7 +1029,7 @@ dependencies = [ "bitflags", "bytes 0.5.6", "headers-core", - "http", + "http 0.2.1", "mime", "sha-1 0.8.2", "time", @@ -977,7 +1041,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.1", ] [[package]] @@ -1025,6 +1089,17 @@ dependencies = [ "digest 0.8.1", ] +[[package]] +name = "http" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +dependencies = [ + "bytes 0.4.12", + "fnv", + "itoa", +] + [[package]] name = "http" version = "0.2.1" @@ -1036,6 +1111,18 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "http 0.1.21", + "tokio-buf", +] + [[package]] name = "http-body" version = "0.3.1" @@ -1043,7 +1130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http", + "http 0.2.1", ] [[package]] @@ -1061,6 +1148,36 @@ dependencies = [ "quick-error", ] +[[package]] +name = "hyper" +version = "0.12.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "futures-cpupool", + "h2 0.1.26", + "http 0.1.21", + "http-body 0.1.0", + "httparse", + "iovec", + "itoa", + "log", + "net2", + "rustc_version", + "time", + "tokio 0.1.22", + "tokio-buf", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "want 0.2.0", +] + [[package]] name = "hyper" version = "0.13.7" @@ -1071,9 +1188,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.2.6", + "http 0.2.1", + "http-body 0.3.1", "httparse", "itoa", "pin-project", @@ -1082,7 +1199,7 @@ dependencies = [ "tokio 0.2.22", "tower-service", "tracing", - "want", + "want 0.3.0", ] [[package]] @@ -1093,7 +1210,7 @@ checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" dependencies = [ "bytes 0.5.6", "futures-util", - "hyper", + "hyper 0.13.7", "log", "rustls", "tokio 0.2.22", @@ -1101,6 +1218,17 @@ dependencies = [ "webpki", ] +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.2.0" @@ -1197,6 +1325,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc-client-transports" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f7b1cdf66312002e15682a24430728bd13036c641163c016bc53fb686a7c2d" +dependencies = [ + "failure", + "futures 0.1.29", + "jsonrpc-core 15.0.0", + "jsonrpc-pubsub", + "log", + "serde", + "serde_json", + "url 1.7.2", +] + [[package]] name = "jsonrpc-core" version = "14.2.0" @@ -1210,6 +1354,84 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpc-core" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b12567a31d48588a65b6cf870081e6ba1d7b2ae353977cb9820d512e69c70" +dependencies = [ + "futures 0.1.29", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-core-client" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d175ca0cf77439b5495612bf216c650807d252d665b4b70ab2eebd895a88fac1" +dependencies = [ + "jsonrpc-client-transports", +] + +[[package]] +name = "jsonrpc-derive" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2cc6ea7f785232d9ca8786a44e9fa698f92149dcdc1acc4aa1fc69c4993d79e" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.21", + "quote 1.0.7", + "syn 1.0.40", +] + +[[package]] +name = "jsonrpc-http-server" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9996b26c0c7a59626d0ed6c5ec8bf06218e62ce1474bd2849f9b9fd38a0158c0" +dependencies = [ + "hyper 0.12.35", + "jsonrpc-core 15.0.0", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.10.2", + "unicase", +] + +[[package]] +name = "jsonrpc-pubsub" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f389c5cd1f3db258a99296892c21047e21ae73ff4c0e2d39650ea86fe994b4c7" +dependencies = [ + "jsonrpc-core 15.0.0", + "log", + "parking_lot 0.10.2", + "rand 0.7.3", + "serde", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c623e1895d0d9110cb0ea7736cfff13191ff52335ad33b21bd5c775ea98b27af" +dependencies = [ + "bytes 0.4.12", + "globset", + "jsonrpc-core 15.0.0", + "lazy_static", + "log", + "tokio 0.1.22", + "tokio-codec", + "unicase", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1687,6 +1909,12 @@ dependencies = [ "crypto-mac", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -2055,7 +2283,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", + "thread_local 1.0.1", ] [[package]] @@ -2084,9 +2312,9 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.1", + "http-body 0.3.1", + "hyper 0.13.7", "hyper-rustls", "ipnet", "js-sys", @@ -2094,7 +2322,7 @@ dependencies = [ "log", "mime", "mime_guess", - "percent-encoding", + "percent-encoding 2.1.0", "pin-project-lite", "rustls", "serde", @@ -2102,7 +2330,7 @@ dependencies = [ "serde_urlencoded", "tokio 0.2.22", "tokio-rustls", - "url", + "url 2.1.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2335,7 +2563,7 @@ dependencies = [ "dtoa", "itoa", "serde", - "url", + "url 2.1.1", ] [[package]] @@ -2360,6 +2588,94 @@ dependencies = [ "solana-client-gen", ] +[[package]] +name = "serum-node" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 3.0.0-beta.2", + "crossbeam", + "futures 0.3.5", + "serum-common", + "serum-node-context", + "serum-node-crank", + "serum-node-json-rpc", + "serum-node-logging", + "serum-node-registry", + "tokio 0.2.22", +] + +[[package]] +name = "serum-node-context" +version = "0.1.0" +dependencies = [ + "clap 3.0.0-beta.2", + "serum-common", +] + +[[package]] +name = "serum-node-crank" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 3.0.0-beta.2", + "crank", + "crossbeam", + "futures 0.3.5", + "serde", + "serum-node-context", + "serum-node-logging", + "solana-sdk", + "tokio 0.2.22", +] + +[[package]] +name = "serum-node-json-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 3.0.0-beta.2", + "futures 0.3.5", + "jsonrpc-core 15.0.0", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-http-server", + "num_enum", + "serum-node-crank", + "serum-node-logging", + "serum-node-registry", + "thiserror", + "tokio 0.2.22", +] + +[[package]] +name = "serum-node-logging" +version = "0.1.0" +dependencies = [ + "clap 3.0.0-beta.2", + "lazy_static", + "slog 2.5.2", + "slog-async", + "slog-json", + "slog-stream", + "slog-term", +] + +[[package]] +name = "serum-node-registry" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 3.0.0-beta.2", + "crossbeam", + "futures 0.3.5", + "serde", + "serum-node-context", + "serum-node-logging", + "solana-sdk", + "tokio 0.2.22", +] + [[package]] name = "serum-registry" version = "0.1.0" @@ -2473,6 +2789,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "slog" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07aa15818e194222ef5b814aec86d47da20d93360c068b2c5f5ef64d9347fbdf" + [[package]] name = "slog" version = "2.5.2" @@ -2486,9 +2808,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b3336ce47ce2f96673499fc07eb85e3472727b9a7a2959964b002c2ce8fbbb" dependencies = [ "crossbeam-channel", - "slog", + "slog 2.5.2", "take_mut", - "thread_local", + "thread_local 1.0.1", +] + +[[package]] +name = "slog-extra" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511581f4dd1dc90e4eca99b60be8a692d9c975e8757558aa774f16007d27492a" +dependencies = [ + "slog 1.7.1", + "thread_local 0.3.6", +] + +[[package]] +name = "slog-json" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" +dependencies = [ + "chrono", + "serde", + "serde_json", + "slog 2.5.2", ] [[package]] @@ -2498,7 +2842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae939ed7d169eed9699f4f5cd440f046f5dc5dfc27c19e3cd311619594c175e0" dependencies = [ "regex", - "slog", + "slog 2.5.2", ] [[package]] @@ -2509,7 +2853,7 @@ checksum = "7c44c89dd8b0ae4537d1ae318353eaf7840b4869c536e31c41e963d1ea523ee6" dependencies = [ "arc-swap", "lazy_static", - "slog", + "slog 2.5.2", ] [[package]] @@ -2520,10 +2864,21 @@ checksum = "be4d87903baf655da2d82bc3ac3f7ef43868c58bf712b3a661fda72009304c23" dependencies = [ "crossbeam", "log", - "slog", + "slog 2.5.2", "slog-scope", ] +[[package]] +name = "slog-stream" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fac4af71007ddb7338f771e059a46051f18d1454d8ac556f234a0573e719daa" +dependencies = [ + "slog 1.7.1", + "slog-extra", + "thread_local 0.3.6", +] + [[package]] name = "slog-term" version = "2.6.0" @@ -2532,9 +2887,9 @@ checksum = "bab1d807cf71129b05ce36914e1dbb6fbfbdecaf686301cb457f4fa967f9f5b6" dependencies = [ "atty", "chrono", - "slog", + "slog 2.5.2", "term", - "thread_local", + "thread_local 1.0.1", ] [[package]] @@ -2547,7 +2902,7 @@ dependencies = [ "libflate", "regex", "serde", - "slog", + "slog 2.5.2", "slog-async", "slog-kvfilter", "slog-scope", @@ -2619,7 +2974,7 @@ dependencies = [ "solana-sdk", "thiserror", "tiny-bip39", - "url", + "url 2.1.1", ] [[package]] @@ -2631,7 +2986,7 @@ dependencies = [ "bincode", "bs58", "indicatif", - "jsonrpc-core", + "jsonrpc-core 14.2.0", "log", "rayon", "reqwest", @@ -2645,7 +3000,7 @@ dependencies = [ "solana-vote-program", "thiserror", "tungstenite 0.10.1", - "url", + "url 2.1.1", ] [[package]] @@ -2746,7 +3101,7 @@ dependencies = [ "solana-version", "tokio 0.1.22", "tokio-codec", - "url", + "url 2.1.1", ] [[package]] @@ -2766,7 +3121,7 @@ dependencies = [ "semver", "solana-sdk", "thiserror", - "url", + "url 2.1.1", ] [[package]] @@ -2949,6 +3304,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" +dependencies = [ + "bytes 0.4.12", +] + [[package]] name = "strsim" version = "0.8.0" @@ -3103,6 +3467,15 @@ dependencies = [ "syn 1.0.40", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -3202,6 +3575,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio-buf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" +dependencies = [ + "bytes 0.4.12", + "either", + "futures 0.1.29", +] + [[package]] name = "tokio-codec" version = "0.1.2" @@ -3489,14 +3873,14 @@ dependencies = [ "base64 0.11.0", "byteorder", "bytes 0.5.6", - "http", + "http 0.2.1", "httparse", "input_buffer", "log", "native-tls", "rand 0.7.3", "sha-1 0.8.2", - "url", + "url 2.1.1", "utf-8", ] @@ -3509,13 +3893,13 @@ dependencies = [ "base64 0.12.3", "byteorder", "bytes 0.5.6", - "http", + "http 0.2.1", "httparse", "input_buffer", "log", "rand 0.7.3", "sha-1 0.9.1", - "url", + "url 2.1.1", "utf-8", ] @@ -3591,15 +3975,26 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + [[package]] name = "url" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ - "idna", + "idna 0.2.0", "matches", - "percent-encoding", + "percent-encoding 2.1.0", ] [[package]] @@ -3638,6 +4033,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "want" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" +dependencies = [ + "futures 0.1.29", + "log", + "try-lock", +] + [[package]] name = "want" version = "0.3.0" @@ -3657,8 +4063,8 @@ dependencies = [ "bytes 0.5.6", "futures 0.3.5", "headers", - "http", - "hyper", + "http 0.2.1", + "hyper 0.13.7", "log", "mime", "mime_guess", diff --git a/Cargo.toml b/Cargo.toml index c4dc96a..f72cef1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,16 @@ members = [ "crank", "safe", "registry", + "node", + "node/json-rpc", + "node/logging", + "node/crank", + "node/registry", + "node/context", ] exclude = [ "dex", "registry/program", "rewards/constant", + "safe/program", ] diff --git a/common/src/client/mod.rs b/common/src/client/mod.rs index d02d668..6f4c5be 100644 --- a/common/src/client/mod.rs +++ b/common/src/client/mod.rs @@ -3,7 +3,7 @@ use std::str::FromStr; pub mod rpc; -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum Cluster { Testnet, Mainnet, diff --git a/crank/Cargo.toml b/crank/Cargo.toml index d622ce4..e79afdd 100644 --- a/crank/Cargo.toml +++ b/crank/Cargo.toml @@ -3,7 +3,9 @@ name = "crank" version = "0.2.0" edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "crank" +path = "src/bin/main.rs" [dependencies] serum_dex = { path = "../dex", features = ["client"] } diff --git a/crank/src/bin/main.rs b/crank/src/bin/main.rs new file mode 100644 index 0000000..2433a20 --- /dev/null +++ b/crank/src/bin/main.rs @@ -0,0 +1,8 @@ +use anyhow::Result; +use clap::Clap; +use crank::Opts; + +fn main() -> Result<()> { + let opts = Opts::parse(); + crank::start(opts) +} diff --git a/crank/src/main.rs b/crank/src/lib.rs similarity index 99% rename from crank/src/main.rs rename to crank/src/lib.rs index dff7724..761250c 100644 --- a/crank/src/main.rs +++ b/crank/src/lib.rs @@ -59,15 +59,15 @@ fn read_keypair_file(s: &str) -> Result { } #[derive(Clap, Debug)] -struct Opts { +pub struct Opts { #[clap(default_value = "mainnet")] - cluster: Cluster, + pub cluster: Cluster, #[clap(subcommand)] - command: Command, + pub command: Command, } #[derive(Clap, Debug)] -enum Command { +pub enum Command { Genesis { #[clap(long, short)] payer: String, @@ -198,9 +198,7 @@ impl Opts { } } -fn main() -> Result<()> { - let opts = Opts::parse(); - +pub fn start(opts: Opts) -> Result<()> { let client = opts.client(); match opts.command { diff --git a/node/Cargo.toml b/node/Cargo.toml new file mode 100644 index 0000000..b4d85eb --- /dev/null +++ b/node/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "serum-node" +version = "0.1.0" +description = "Serum Node" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[[bin]] +name = "serum-node" +path = "src/bin/main.rs" + +[features] +trace = ["serum-node-logging/trace"] +default = [] +strict = [] +rpc = [] + +[dependencies] +serum-common = { path = "../common" } +serum-node-json-rpc = { path = "./json-rpc" } +serum-node-logging = { path = "./logging" } +serum-node-crank = { path = "./crank" } +serum-node-registry = { path = "./registry" } +serum-node-context = { path = "./context" } +tokio = { version = "0.2.22", features = ["sync"] } +futures = "0.3" +anyhow = "1.0.32" +crossbeam = "0.7.3" +clap = "3.0.0-beta.1" diff --git a/node/context/Cargo.toml b/node/context/Cargo.toml new file mode 100644 index 0000000..b321fb1 --- /dev/null +++ b/node/context/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "serum-node-context" +version = "0.1.0" +description = "Serum Node Context" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[dependencies] +serum-common = { path = "../../common" } +clap = "3.0.0-beta.1" diff --git a/node/context/src/lib.rs b/node/context/src/lib.rs new file mode 100644 index 0000000..7eee8b2 --- /dev/null +++ b/node/context/src/lib.rs @@ -0,0 +1,13 @@ +use clap::Clap; +use serum_common::client::Cluster; + +#[derive(Clone, Debug, Clap)] +pub struct Context { + /// Solana cluster to communicate with. + #[clap(long, default_value = "localnet")] + pub cluster: Cluster, + + /// Path to the node's wallet [optional]. + #[clap(long)] + pub wallet: Option, +} diff --git a/node/crank/Cargo.toml b/node/crank/Cargo.toml new file mode 100644 index 0000000..82932ba --- /dev/null +++ b/node/crank/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "serum-node-crank" +version = "0.1.0" +description = "Serum Node Crank" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[dependencies] +serum-node-logging = { path = "../logging" } +serum-node-context = { path = "../context" } +crank = { path = "../../crank", version = "0.2" } +solana-sdk = "1.3.9" +tokio = "0.2.22" +futures = "0.3" +anyhow = "1.0.32" +serde = { version = "1.0", features = ["derive"] } +crossbeam = "0.7.3" +clap = "3.0.0-beta.1" \ No newline at end of file diff --git a/node/crank/src/api.rs b/node/crank/src/api.rs new file mode 100644 index 0000000..c514b0a --- /dev/null +++ b/node/crank/src/api.rs @@ -0,0 +1,11 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; + +pub fn health() -> Result { + Ok(HealthResponse { ok: true }) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HealthResponse { + pub ok: bool, +} diff --git a/node/crank/src/dispatch.rs b/node/crank/src/dispatch.rs new file mode 100644 index 0000000..c4bfffb --- /dev/null +++ b/node/crank/src/dispatch.rs @@ -0,0 +1,47 @@ +use crate::api::{self, HealthResponse}; +use anyhow::Result; +use crossbeam::sync::WaitGroup; +use futures::channel::{mpsc, oneshot}; +use futures::future; +use futures::StreamExt; +use serde::{Deserialize, Serialize}; +use serum_node_logging::{error, info}; + +pub async fn dispatch(rpc_recv: Receiver, start_wg: WaitGroup) { + let logger = serum_node_logging::get_logger("crank"); + info!(logger, "Starting crank api dispatch"); + + drop(start_wg); + + rpc_recv + .for_each( + move |(req, resp_ch): (Request, oneshot::Sender>)| { + info!(logger, "Dispatch request {:?}", req); + + let resp = { + match req { + Request::Health => api::health().map(|r| Response::Health(r)), + } + }; + + if let Err(e) = resp_ch.send(resp) { + error!(logger, "Unable to send api response: {:?}", e); + } + + future::ready(()) + }, + ) + .await; +} + +pub type Sender = mpsc::Sender<(Request, oneshot::Sender>)>; +pub type Receiver = mpsc::Receiver<(Request, oneshot::Sender>)>; + +#[derive(Debug)] +pub enum Request { + Health, +} +#[derive(Debug, Serialize, Deserialize)] +pub enum Response { + Health(HealthResponse), +} diff --git a/node/crank/src/lib.rs b/node/crank/src/lib.rs new file mode 100644 index 0000000..d8daf33 --- /dev/null +++ b/node/crank/src/lib.rs @@ -0,0 +1,37 @@ +extern crate crank as serum_crank; + +use anyhow::Result; +use crossbeam::sync::WaitGroup; +use serum_node_context::Context; +use tokio::runtime::{Builder, Runtime}; + +mod api; +mod dispatch; + +// Re-export. +pub use api::HealthResponse; +pub use dispatch::*; +pub use serum_crank::Command; + +pub struct StartRequest { + pub rpc: Receiver, + pub start_wg: WaitGroup, +} + +pub fn start(req: StartRequest) -> Runtime { + let runtime = Builder::new() + .thread_name("crank") + .threaded_scheduler() + .enable_all() + .build() + .expect("Failed to start crank runtime."); + runtime.handle().spawn(dispatch(req.rpc, req.start_wg)); + runtime +} + +pub fn run_cmd(ctx: &Context, cmd: Command) -> Result<()> { + serum_crank::start(serum_crank::Opts { + cluster: ctx.cluster.clone(), + command: cmd, + }) +} diff --git a/node/json-rpc/Cargo.toml b/node/json-rpc/Cargo.toml new file mode 100644 index 0000000..e1f9250 --- /dev/null +++ b/node/json-rpc/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "serum-node-json-rpc" +version = "0.1.0" +description = "Serum Node JSON RPC" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[dependencies] +serum-node-logging = { path = "../logging" } +serum-node-crank = { path = "../crank" } +serum-node-registry = { path = "../registry" } +jsonrpc-core = "15.0.0" +jsonrpc-derive = "15.0.0" +jsonrpc-http-server = "15.0.0" +jsonrpc-core-client = "15.0.0" +tokio = "0.2.22" +futures = { version = "0.3", features = ["compat"] } +anyhow = "1.0.32" +num_enum = "0.5.0" +thiserror = "1.0.20" +clap = "3.0.0-beta.1" diff --git a/node/json-rpc/src/handlers/api.rs b/node/json-rpc/src/handlers/api.rs new file mode 100644 index 0000000..510c637 --- /dev/null +++ b/node/json-rpc/src/handlers/api.rs @@ -0,0 +1,109 @@ +use crate::handlers::api_trait; +use crate::handlers::FutureResult; +use futures::channel::oneshot; +use futures::future::TryFutureExt; +use jsonrpc_core::Error as RpcError; +use serum_node_crank::{ + HealthResponse as CrankHealthResponse, Request as CrankRequest, Response as CrankResponse, +}; +use serum_node_logging::{trace, Logger}; +use serum_node_registry::{ + HealthResponse as RegistryHealthResponse, Request as RegistryRequest, + Response as RegistryResponse, +}; +use std::convert::Into; + +pub struct Api { + logger: Logger, + crank: serum_node_crank::Sender, + registry: serum_node_registry::Sender, +} + +impl Api { + pub fn new( + logger: Logger, + crank: serum_node_crank::Sender, + registry: serum_node_registry::Sender, + ) -> Self { + Self { + logger, + crank, + registry, + } + } +} + +impl api_trait::Api for Api { + fn crank_health(&self) -> FutureResult { + trace!(self.logger, "serum_startCrank"); + + // Send request to the crank. + let fut = { + let mut crank = self.crank.clone(); + async move { + let (tx, rx) = oneshot::channel(); + crank + .try_send((CrankRequest::Health, tx)) + .map_err(Into::into) + .map_err(jsonrpc_error)?; + + let resp = rx + .await + .map_err(Into::into) + .map_err(jsonrpc_error)? + .map_err(jsonrpc_error)?; + + match resp { + CrankResponse::Health(r) => Ok(r), + } + } + }; + + // Convert to pre-async/await future. + let rpc_fut = Box::pin(fut).compat(); + + // Response. + Box::new(rpc_fut) + } + + fn registry_health(&self) -> FutureResult { + trace!(self.logger, "serum_createEntity"); + + // Send request to the registry. + let fut = { + let mut registry = self.registry.clone(); + async move { + let (tx, rx) = oneshot::channel(); + registry + .try_send((RegistryRequest::Health, tx)) + .map_err(Into::into) + .map_err(jsonrpc_error)?; + + let resp = rx + .await + .map_err(Into::into) + .map_err(jsonrpc_error)? + .map_err(jsonrpc_error)?; + + match resp { + RegistryResponse::Health(r) => Ok(r), + } + } + }; + + // Convert to pre-async/await future. + let rpc_fut = Box::pin(fut).compat(); + + // Response. + Box::new(rpc_fut) + } +} + +/// Constructs a JSON-RPC error from a string message, with error code -32603. +pub fn jsonrpc_error(err: anyhow::Error) -> RpcError { + RpcError { + code: jsonrpc_core::ErrorCode::InternalError, + message: format!("{}", err), + data: None, + } +} diff --git a/node/json-rpc/src/handlers/api_trait.rs b/node/json-rpc/src/handlers/api_trait.rs new file mode 100644 index 0000000..1089434 --- /dev/null +++ b/node/json-rpc/src/handlers/api_trait.rs @@ -0,0 +1,14 @@ +use crate::handlers::FutureResult; +use jsonrpc_derive::rpc; +use serum_node_crank::HealthResponse as CrankHealthResponse; +use serum_node_registry::HealthResponse as RegistryHealthResponse; + +/// Api defines the JSON-RPC interface. Handlers must implement this trait. +#[rpc] +pub trait Api { + #[rpc(name = "serum_crankHealth")] + fn crank_health(&self) -> FutureResult; + + #[rpc(name = "serum_registryHealth")] + fn registry_health(&self) -> FutureResult; +} diff --git a/node/json-rpc/src/handlers/mod.rs b/node/json-rpc/src/handlers/mod.rs new file mode 100644 index 0000000..3fcecaf --- /dev/null +++ b/node/json-rpc/src/handlers/mod.rs @@ -0,0 +1,19 @@ +use api::Api; +use api_trait::Api as ApiTrait; +use serum_node_logging::Logger; + +mod api; +pub(crate) mod api_trait; + +pub type FutureResult = + Box + Send>; + +pub fn build( + logger: Logger, + crank: serum_node_crank::Sender, + registry: serum_node_registry::Sender, +) -> jsonrpc_core::IoHandler { + let mut io = jsonrpc_core::IoHandler::new(); + io.extend_with(Api::new(logger, crank, registry).to_delegate()); + io +} diff --git a/node/json-rpc/src/lib.rs b/node/json-rpc/src/lib.rs new file mode 100644 index 0000000..1a6b274 --- /dev/null +++ b/node/json-rpc/src/lib.rs @@ -0,0 +1,41 @@ +use clap::Clap; +use jsonrpc_http_server::{Server, ServerBuilder}; +use serum_node_logging::info; + +mod handlers; + +pub struct StartRequest { + pub cfg: Config, + pub crank: serum_node_crank::Sender, + pub registry: serum_node_registry::Sender, +} + +#[derive(Debug, Clap)] +pub struct Config { + /// HTTP port for the JSON RPC server. + #[clap(long = "json.http.port", default_value = "8080")] + pub http_port: u16, + /// Number of threads for the JSON RPC server. + #[clap(long = "json.http.threads", default_value = "3")] + pub http_threads: u16, +} + +pub fn start(req: StartRequest) -> JsonRpc { + let url = format!("127.0.0.1:{}", req.cfg.http_port); + + let logger = serum_node_logging::get_logger("json-rpc"); + info!(logger, "Starting JSON-RPC server at {}", url); + + let handlers = handlers::build(logger, req.crank, req.registry); + + let _server = ServerBuilder::new(handlers) + .threads(req.cfg.http_threads as usize) + .start_http(&url.parse().unwrap()) + .unwrap(); + + JsonRpc { _server } +} + +pub struct JsonRpc { + _server: Server, +} diff --git a/node/logging/Cargo.toml b/node/logging/Cargo.toml new file mode 100644 index 0000000..ab010a6 --- /dev/null +++ b/node/logging/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "serum-node-logging" +version = "0.1.0" +description = "Serum Node Logging" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[features] +trace = ["slog/max_level_trace", "slog/release_max_level_warn"] +default = [] + +[dependencies] +slog = "2.5.2" +slog-term = "2" +slog-json = "2.3.0" +slog-async = "2" +slog-stream = "1.2.1" +lazy_static = "1.4.0" +clap = "3.0.0-beta.1" diff --git a/node/logging/src/lib.rs b/node/logging/src/lib.rs new file mode 100644 index 0000000..81c1f2c --- /dev/null +++ b/node/logging/src/lib.rs @@ -0,0 +1,62 @@ +use clap::Clap; +use lazy_static::lazy_static; +use slog::{self, Drain}; +use std::sync::Mutex; + +pub use slog::{debug, error, info, trace, warn}; + +lazy_static! { + static ref LOGGER: Mutex> = Mutex::new(None); +} + +#[derive(Debug, Clap)] +pub struct Config { + /// Log level. + #[clap(long = "log.level", default_value = "info")] + pub level: String, + /// Log format. + #[clap(long = "log.format", default_value = "pretty")] + pub format: String, +} + +pub type Logger = slog::Logger; + +/// Start initializes the logger. +pub fn start(cfg: Config) { + let format_drain = match cfg.format.as_str() { + "json" => { + let drain = slog_json::Json::default(std::io::stderr()).fuse(); + slog_async::Async::default(drain) + } + _ => { + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + slog_async::Async::default(drain) + } + }; + let level_drain = { + let level = match cfg.level.as_str() { + "trace" => slog::Level::Trace, + "debug" => slog::Level::Debug, + "info" => slog::Level::Info, + "warning" => slog::Level::Warning, + "error" => slog::Level::Error, + _ => slog::Level::Info, + }; + slog::LevelFilter::new(format_drain, level).fuse() + }; + LOGGER + .lock() + .unwrap() + .get_or_insert(slog::Logger::root(level_drain, slog::o!())); +} + +/// `start` must be called before `get_logger`. +pub fn get_logger(module: &'static str) -> slog::Logger { + LOGGER + .lock() + .unwrap() + .as_ref() + .unwrap() + .new(slog::o!("module" => module)) +} diff --git a/node/registry/Cargo.toml b/node/registry/Cargo.toml new file mode 100644 index 0000000..265dfe7 --- /dev/null +++ b/node/registry/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "serum-node-registry" +version = "0.1.0" +description = "Serum Node Registry" +repository = "https://github.com/project-serum/serum-dex" +edition = "2018" + +[dependencies] +serum-node-logging = { path = "../logging" } +serum-node-context = { path = "../context" } +tokio = "0.2.22" +futures = "0.3" +anyhow = "1.0.32" +serde = { version = "1.0", features = ["derive"] } +solana-sdk = { version = "=1.3.9", default-features = false } +crossbeam = "0.7.3" +clap = "3.0.0-beta.1" diff --git a/node/registry/src/api.rs b/node/registry/src/api.rs new file mode 100644 index 0000000..c514b0a --- /dev/null +++ b/node/registry/src/api.rs @@ -0,0 +1,11 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; + +pub fn health() -> Result { + Ok(HealthResponse { ok: true }) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HealthResponse { + pub ok: bool, +} diff --git a/node/registry/src/dispatch.rs b/node/registry/src/dispatch.rs new file mode 100644 index 0000000..ed67a0a --- /dev/null +++ b/node/registry/src/dispatch.rs @@ -0,0 +1,47 @@ +use crate::api::{self, HealthResponse}; +use anyhow::Result; +use crossbeam::sync::WaitGroup; +use futures::channel::{mpsc, oneshot}; +use futures::future; +use futures::StreamExt; +use serde::{Deserialize, Serialize}; +use serum_node_logging::{error, info}; + +pub(crate) async fn dispatch(rpc_recv: Receiver, start_wg: WaitGroup) { + let logger = serum_node_logging::get_logger("registry"); + info!(logger, "Starting registry api dispatch"); + + drop(start_wg); + + rpc_recv + .for_each( + move |(req, resp_chan): (Request, oneshot::Sender>)| { + info!(logger, "Dispatch request {:?}", req); + + let resp = { + match req { + Request::Health => api::health().map(|r| Response::Health(r)), + } + }; + + if let Err(e) = resp_chan.send(resp) { + error!(logger, "Unable to send api response: {:?}", e); + } + + future::ready(()) + }, + ) + .await; +} + +pub type Sender = mpsc::Sender<(Request, oneshot::Sender>)>; +pub type Receiver = mpsc::Receiver<(Request, oneshot::Sender>)>; + +#[derive(Debug)] +pub enum Request { + Health, +} +#[derive(Debug, Serialize, Deserialize)] +pub enum Response { + Health(HealthResponse), +} diff --git a/node/registry/src/lib.rs b/node/registry/src/lib.rs new file mode 100644 index 0000000..6ac5962 --- /dev/null +++ b/node/registry/src/lib.rs @@ -0,0 +1,42 @@ +//! serum-node-registry defines the internal registry node service. + +use anyhow::Result; +use clap::Clap; +use crossbeam::sync::WaitGroup; +use serum_node_context::Context; +use solana_sdk::pubkey::Pubkey; +use tokio::runtime::{Builder, Runtime}; + +mod api; +mod dispatch; + +pub use api::HealthResponse; +pub use dispatch::*; + +#[derive(Debug, Clap)] +pub struct Command { + /// Program id of the deployed on-chain registry + #[clap(long = "program-id")] + pub program_id: Option, +} + +pub struct StartRequest { + pub rpc: Receiver, + pub start_wg: WaitGroup, +} + +pub fn start(req: StartRequest) -> Runtime { + let runtime = Builder::new() + .thread_name("registry") + .threaded_scheduler() + .enable_all() + .build() + .expect("Failed to start registry runtime."); + runtime.handle().spawn(dispatch(req.rpc, req.start_wg)); + runtime +} + +pub fn run_cmd(_ctx: &Context, _cmd: Command) -> Result<()> { + // todo + Ok(()) +} diff --git a/node/src/bin/main.rs b/node/src/bin/main.rs new file mode 100644 index 0000000..5fd4ff5 --- /dev/null +++ b/node/src/bin/main.rs @@ -0,0 +1,8 @@ +use clap::Clap; +use serum_node::Config; + +fn main() { + let cfg = Config::parse(); + let handle = serum_node::start(cfg); + handle.park(); +} diff --git a/node/src/lib.rs b/node/src/lib.rs new file mode 100644 index 0000000..409c7a9 --- /dev/null +++ b/node/src/lib.rs @@ -0,0 +1,135 @@ +#![cfg_attr(feature = "strict", deny(warnings))] + +use anyhow::Result; +use clap::Clap; +use crossbeam::sync::WaitGroup; +use futures::channel::mpsc; +use serum_node_context::Context; +use serum_node_logging::info; +use serum_node_logging::{error, trace}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tokio::runtime::Runtime; + +/// start is the entrypoint to running a node. +pub fn start(cfg: Config) -> Handle { + // Start logging. + serum_node_logging::start(cfg.logging); + let logger = serum_node_logging::get_logger("node"); + + // Start the services, if needed. + let _services = { + if !cfg.json { + None + } else { + info!(logger, "Node starting 🚀"); + + // Channels to relay requests from the RPC server to internal services. + let crank_chan_size = 4; + let (crank_sender, crank_receiver) = mpsc::channel(crank_chan_size); + let registry_chan_size = 1024; + let (registry_sender, registry_receiver) = mpsc::channel(registry_chan_size); + + // Start JSON-RPC server. + let _json_rpc = serum_node_json_rpc::start(serum_node_json_rpc::StartRequest { + cfg: cfg.json_rpc, + crank: crank_sender, + registry: registry_sender, + }); + + info!(logger, "Starting internal api services"); + + // WaitGroup to block thread until all async services are ready. + let start_wg = WaitGroup::new(); + + // Start crank service. + let _crank = serum_node_crank::start(serum_node_crank::StartRequest { + rpc: crank_receiver, + start_wg: start_wg.clone(), + }); + + // Start registry service. + let _registry = serum_node_registry::start(serum_node_registry::StartRequest { + rpc: registry_receiver, + start_wg: start_wg.clone(), + }); + + start_wg.wait(); + + info!(logger, "Service setup complete"); + + Some(Services { + _json_rpc, + _crank, + _registry, + }) + } + }; + + // Run the command, if given. + if let Some(cmd) = cfg.cmd { + trace!(logger, "Executing command: {:?}", cmd); + if let Err(e) = run_cmd(&cfg.context, cmd) { + error!(logger, "Command failed with error: {}", e.to_string()); + std::process::exit(1); + } + } + + Handle { _services } +} + +fn run_cmd(ctx: &Context, cmd: Command) -> Result<()> { + match cmd { + Command::Crank(crank_cmd) => serum_node_crank::run_cmd(ctx, crank_cmd), + Command::Registry(reg_cmd) => serum_node_registry::run_cmd(ctx, reg_cmd), + } +} + +#[derive(Debug, Clap)] +#[clap(name = "serum-node", about = "A Serum node")] +pub struct Config { + #[clap(flatten)] + pub logging: serum_node_logging::Config, + + #[clap(flatten)] + pub json_rpc: serum_node_json_rpc::Config, + + /// Enables the JSON RPC server if set. Defaults to off. + #[clap(long)] + pub json: bool, + + #[clap(flatten)] + pub context: Context, + + #[clap(subcommand)] + pub cmd: Option, +} + +#[derive(Debug, Clap)] +pub enum Command { + /// Runs the crank client utility + Crank(serum_node_crank::Command), + /// Communicates with a Serum registry. + Registry(serum_node_registry::Command), +} + +pub struct Handle { + _services: Option, +} + +struct Services { + _json_rpc: serum_node_json_rpc::JsonRpc, + _crank: Runtime, + _registry: Runtime, +} + +impl Handle { + pub fn park(&self) { + if self._services.is_some() { + let term = Arc::new(AtomicBool::new(false)); + while !term.load(Ordering::Acquire) { + std::thread::park(); + } + } + } +}