├── stratum-proxy ├── .gitignore ├── config │ ├── insecure.toml │ ├── secure.toml │ └── gen_keys.sh ├── Cargo.toml ├── src │ ├── util.rs │ ├── lib.rs │ ├── dummy_metrics.rs │ ├── main.rs │ ├── server │ │ └── peer_address.rs │ ├── frontend.rs │ └── error.rs ├── README.md └── tests │ └── utils │ └── mod.rs ├── coins ├── .gitignore ├── Cargo.toml └── bitcoin │ └── Cargo.toml ├── utils-rs ├── scm │ ├── Cargo.toml │ ├── scm │ │ ├── README.md │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── global.rs │ │ │ └── version.rs │ └── scm-macros │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── unvariant │ ├── Cargo.toml │ ├── unvariant │ │ ├── Cargo.toml │ │ └── src │ │ │ └── macro_private.rs │ ├── unvariant-tests │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── tests │ │ │ ├── common │ │ │ ├── mod.rs │ │ │ ├── str_frame.rs │ │ │ └── frame.rs │ │ │ ├── unvariant.rs │ │ │ └── id.rs │ └── unvariant-macros │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ ├── unvariant.rs │ │ └── id.rs ├── metrics │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── logging │ ├── Cargo.toml │ └── tests │ │ ├── panic.rs │ │ └── basic.rs └── async-utils │ ├── Cargo.toml │ └── src │ └── maybe_future.rs ├── noise-proxy ├── .cargo │ └── config ├── config │ ├── sample_config.toml │ └── gen_keys.sh ├── Cargo.toml └── src │ ├── framing.rs │ ├── dummy_metrics.rs │ ├── metrics.rs │ ├── connector.rs │ └── main.rs └── protocols ├── stratum ├── sim │ ├── requirements.txt │ ├── sim_primitives │ │ ├── stratum_v2 │ │ │ ├── states.py │ │ │ ├── __init__.py │ │ │ └── types.py │ │ ├── stratum_v1 │ │ │ ├── proxy.py │ │ │ ├── __init__.py │ │ │ ├── messages.py │ │ │ └── miner.py │ │ ├── __init__.py │ │ ├── mining_params.py │ │ ├── coins.py │ │ ├── network.py │ │ └── hashrate_meter.py │ └── README.md ├── src │ ├── v2 │ │ ├── telemetry.rs │ │ ├── extensions.rs │ │ ├── error.rs │ │ ├── messages │ │ │ └── test.rs │ │ ├── telemetry │ │ │ └── messages.rs │ │ ├── macros.rs │ │ ├── framing │ │ │ └── codec.rs │ │ └── noise │ │ │ └── negotiation.rs │ ├── test_utils.rs │ ├── v1 │ │ ├── error.rs │ │ ├── framing │ │ │ └── codec.rs │ │ ├── test.rs │ │ ├── framing.rs │ │ └── messages │ │ │ └── test.rs │ ├── test_utils │ │ └── common.rs │ ├── lib.rs │ ├── v2.rs │ └── error.rs ├── Cargo.toml ├── README.md └── examples │ ├── initiator.rs │ └── responder.rs └── wire ├── Cargo.toml └── src ├── framing.rs ├── proxy ├── error.rs └── codec.rs ├── lib.rs ├── server.rs └── connection.rs /stratum-proxy/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /coins/.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /coins/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "bitcoin", 5 | ] 6 | -------------------------------------------------------------------------------- /utils-rs/scm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "scm", 5 | "scm-macros", 6 | ] 7 | -------------------------------------------------------------------------------- /noise-proxy/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.arm-unknown-linux-musleabi] 2 | linker = "arm-openwrt-linux-muslgnueabi-gcc" 3 | -------------------------------------------------------------------------------- /utils-rs/unvariant/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "unvariant", 5 | "unvariant-macros", 6 | "unvariant-tests", 7 | ] 8 | -------------------------------------------------------------------------------- /protocols/stratum/sim/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | simpy 3 | matplotlib 4 | hashids 5 | event_bus 6 | colorama 7 | stringcase 8 | git+ssh://git@github.com/braiins/black.git@braiins-codestyle#egg=black 9 | -------------------------------------------------------------------------------- /stratum-proxy/config/insecure.toml: -------------------------------------------------------------------------------- 1 | listen_address = "0.0.0.0:3336" 2 | upstream_address = "stratum.slushpool.com:3333" 3 | insecure = true 4 | certificate_file = "server.cert" 5 | secret_key_file = "server-secret.key" 6 | -------------------------------------------------------------------------------- /noise-proxy/config/sample_config.toml: -------------------------------------------------------------------------------- 1 | listen = "0.0.0.0:3338" 2 | upstream = "eu.stratum.slushpool.com:3333" 3 | certificate = "config/server-noise-static-public.cert" 4 | server_key = "config/server-noise-static-secret.key" 5 | -------------------------------------------------------------------------------- /utils-rs/scm/scm/README.md: -------------------------------------------------------------------------------- 1 | # Software Configuration Management 2 | 3 | This crate gathers functionality related to SCM (https://en.wikipedia.org/wiki 4 | /Software_configuration_management). Currently it provides simple versioning 5 | functionality. 6 | 7 | -------------------------------------------------------------------------------- /stratum-proxy/config/secure.toml: -------------------------------------------------------------------------------- 1 | listen_address = "0.0.0.0:3336" 2 | upstream_address = "eu.stratum.slushpool.com:3333" 3 | insecure = false 4 | certificate_file = "config/server-noise-static-public.cert" 5 | secret_key_file = "config/server-noise-static-secret.key" 6 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-unvariant" 3 | version = "0.1.0" 4 | authors = ["Vojtech Kral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | ii-unvariant-macros = { path = "../unvariant-macros" } 9 | futures = "0.3.7" 10 | pin-project = "1.0.1" 11 | -------------------------------------------------------------------------------- /utils-rs/scm/scm-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-scm-macros" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | proc-macro2 = "1.0.24" 13 | syn = "1.0.48" 14 | quote = "1.0.7" 15 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-unvariant-tests" 3 | version = "0.1.0" 4 | authors = ["Vojtech Kral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | ii-unvariant = { path = "../unvariant" } 9 | 10 | [dev-dependencies] 11 | tokio = { version = "1.2.0", features = ["full"] } 12 | -------------------------------------------------------------------------------- /coins/bitcoin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-bitcoin" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | bitcoin_hashes = "0.9.4" 10 | lazy_static = "1.4.0" 11 | packed_struct = "0.3.1" 12 | packed_struct_codegen = "0.3.1" 13 | primitive-types = "0.7.2" 14 | thiserror = "1.0.24" 15 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-unvariant-macros" 3 | version = "0.1.0" 4 | authors = ["Vojtech Kral "] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | proc-macro2 = "1.0.24" 12 | quote = "1.0.7" 13 | syn = { version = "1.0.48", features = ["full", "visit-mut", "extra-traits"] } 14 | -------------------------------------------------------------------------------- /utils-rs/metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-metrics" 3 | version = "0.2.0" 4 | authors = ["Jakub Trnka "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ii-scm = { path = "../scm/scm" } 11 | prometheus = { version = "0.11", features = ["process"]} 12 | rustc_version = "0.2" 13 | thiserror = { version = "1.0" } 14 | -------------------------------------------------------------------------------- /utils-rs/scm/scm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-scm" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | description = "Utility for handling Software Configuration Management (SCM)" 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | ii-scm-macros = { path = "../scm-macros" } 12 | once_cell = "1.4.1" 13 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v2/states.py: -------------------------------------------------------------------------------- 1 | """Protocol state related classes for Stratum V2 2 | 3 | """ 4 | from sim_primitives.stratum_v2.messages import SetupConnection, SetupConnectionSuccess 5 | 6 | 7 | class ConnectionConfig: 8 | """Stratum V2 connection configurat 9 | 10 | """ 11 | 12 | def __init__(self, setup_req: SetupConnection, setup_resp: SetupConnectionSuccess): 13 | self.req = setup_req 14 | self.resp = setup_resp 15 | -------------------------------------------------------------------------------- /noise-proxy/config/gen_keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ "${PWD##*/}" == "config" ]; then 3 | KPATH="../../protocols/stratum" 4 | TARGET="${PWD}" 5 | else 6 | KPATH="../protocols/stratum" 7 | TARGET="${PWD}/config" 8 | fi 9 | KBIN=ii-stratum-keytool 10 | cd "$KPATH" 11 | cargo build --release --bin $KBIN 12 | cp target/release/$KBIN $TARGET 13 | cd $TARGET 14 | ./$KBIN gen-ca-key 15 | ./$KBIN gen-noise-key 16 | ./$KBIN sign-key --public-key-to-sign server-noise-static-public.key --signing-key ca-ed25519-secret.key 17 | rm $KBIN 18 | -------------------------------------------------------------------------------- /stratum-proxy/config/gen_keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ "${PWD##*/}" == "config" ]; then 3 | KPATH="../../protocols/stratum" 4 | TARGET="${PWD}" 5 | else 6 | KPATH="../protocols/stratum" 7 | TARGET="${PWD}/config" 8 | fi 9 | KBIN=ii-stratum-keytool 10 | cd "$KPATH" 11 | cargo build --release --bin $KBIN 12 | cp target/release/$KBIN $TARGET 13 | cd $TARGET 14 | ./$KBIN gen-ca-key 15 | ./$KBIN gen-noise-key 16 | ./$KBIN sign-key --public-key-to-sign server-noise-static-public.key --signing-key ca-ed25519-secret.key 17 | rm $KBIN 18 | -------------------------------------------------------------------------------- /utils-rs/logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-logging" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | lazy_static = "1.4.0" 10 | slog = { version = "2.7.0", features = ["max_level_trace", "release_max_level_info"] } 11 | slog-term = "2.6.0" 12 | slog-async = "2.5.0" 13 | slog-json = "2.3.0" 14 | slog-envlogger = "2.2.0" # slog-envlogger = { path = "../envlogger" } 15 | slog-atomic = "3.0.0" 16 | 17 | [dev-dependencies] 18 | tempfile = "3.1.0" 19 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v1/proxy.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | from sim_primitives.network import Connection 4 | from sim_primitives.protocol import UpstreamConnectionProcessor 5 | from sim_primitives.proxy import Proxy 6 | 7 | 8 | class V1ToV2Translation(UpstreamConnectionProcessor): 9 | """Processes all messages on 1 connection 10 | 11 | """ 12 | 13 | def _on_invalid_message(self, msg): 14 | pass 15 | 16 | class State(enum.Enum): 17 | pass 18 | 19 | def __init__(self, proxy: Proxy, connection: Connection): 20 | pass 21 | -------------------------------------------------------------------------------- /utils-rs/async-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-async-utils" 3 | version = "0.3.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | futures = "0.3.7" 10 | once_cell = "1.5.2" 11 | pin-project-lite = "0.2.0" 12 | tokio12 = { package = "tokio", version = "1.2.0", features = ["full"], optional = true } 13 | tokio = { version = "0.3.2", features = ["full"], optional = true } 14 | tokio02 = { package = "tokio", version = "0.2.22", features = ["full"], optional = true } 15 | # we need tokio-stream to support stream wrappers for Signals, not in the latest version yet 16 | tokio-stream = { git = "https://github.com/tokio-rs/tokio", rev="6fd06aaeecce21bcf31cbe485fe0060e3f07e983", features = ["default", "signal"] } 17 | 18 | [features] 19 | default = ["tokio12"] 20 | tokio03 = ["tokio"] 21 | -------------------------------------------------------------------------------- /noise-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-noise-proxy" 3 | version = "0.1.0" 4 | authors = ["Braiins"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.33" 11 | bytes = "1.0.1" 12 | structopt = "0.3.20" 13 | clap = "2.33" 14 | futures = "0.3" 15 | ii-async-utils = { path = "../utils-rs/async-utils" } 16 | ii-logging = { path = "../utils-rs/logging" } 17 | ii-metrics = { path = "../utils-rs/metrics", optional = true } 18 | ii-wire = { path = "../protocols/wire" } 19 | ii-stratum = { path = "../protocols/stratum" } 20 | prometheus = { version = "0.11.0", optional = true } 21 | serde = { version = "1.0.123", features = ["derive"] } 22 | thiserror = "1.0.23" 23 | tokio = { version = "1.2.0", features = ["full"] } 24 | tokio-util = { version = "0.6.3", features = ["codec"] } 25 | toml = "0.5.8" 26 | 27 | [features] 28 | prometheus_metrics = ["prometheus", "ii-metrics"] 29 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v1/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v2/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/telemetry.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | pub mod messages; 24 | -------------------------------------------------------------------------------- /protocols/stratum/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | pub mod common; 24 | pub mod v1; 25 | pub mod v2; 26 | -------------------------------------------------------------------------------- /noise-proxy/src/framing.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | // TODO: implement transparent L2 codec for stratum::v2::noise::CompoundCodec 24 | -------------------------------------------------------------------------------- /utils-rs/scm/scm/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | pub mod global; 24 | pub mod version; 25 | 26 | pub use ii_scm_macros::git_hash; 27 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/mining_params.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | """This module gathers mining parameters""" 24 | 25 | diff_1_target = 0xFFFF << 208 26 | -------------------------------------------------------------------------------- /stratum-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-stratum-proxy" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | ii-stratum = { path = "../protocols/stratum" } 10 | ii-wire = { path = "../protocols/wire", features = ["serde"]} 11 | ii-async-utils = { path = "../utils-rs/async-utils" } 12 | ii-logging = { path = "../utils-rs/logging" } 13 | ii-metrics = { path = "../utils-rs/metrics", optional = true} 14 | ii-noise-proxy = { path = "../noise-proxy" } 15 | ii-unvariant = { path = "../utils-rs/unvariant/unvariant" } 16 | ii-scm = { path = "../utils-rs/scm/scm" } 17 | futures = "0.3.7" 18 | async-trait = "0.1.41" 19 | tokio = { version = "1.2.0", features = ["full"] } 20 | tokio-util = { version = "0.6.3", features = ["codec"] } 21 | bytes = "1.0.1" 22 | thiserror = "1.0.21" 23 | anyhow = "1.0.33" 24 | arrayvec = "0.5.2" 25 | clap = "2.33.3" 26 | bitcoin_hashes = "0.9.4" 27 | primitive-types = "0.7.2" 28 | serde = "1.0.117" 29 | serde_json = "1.0.59" 30 | structopt = "0.3.20" 31 | toml = "0.5.7" 32 | prometheus = { version = "0.11", features = ["process"], optional = true } 33 | 34 | [features] 35 | v2json = ["ii-stratum/v2json"] 36 | prometheus_metrics = ["prometheus", "ii-metrics"] 37 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Support / example code used in tests 24 | 25 | mod frame; 26 | pub use frame::*; 27 | 28 | mod str_frame; 29 | pub use str_frame::*; 30 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/extensions.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! This module lists all official extensions 24 | 25 | /// Base protocol covered in the main specification 26 | pub const BASE: u16 = 0x0000; 27 | /// Telemetry extension 28 | pub const TELEMETRY: u16 = 0x0001; 29 | -------------------------------------------------------------------------------- /protocols/wire/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-wire" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | bytes = { version = "1.0.1", optional = true } 10 | bytes06 = { package = "bytes", version = "0.6.0", optional = true } 11 | bytes05 = { package = "bytes", version = "0.5.6", optional = true } 12 | futures = "0.3.7" 13 | tokio = { version = "1.2.0", features = ["full"], optional = true } 14 | tokio-util = { version = "0.6.3", features = ["codec"], optional = true } 15 | tokio03-core = { package = "tokio", version = "0.3.2", features = ["full"], optional = true } 16 | tokio03-util = { package = "tokio-util", version = "0.5.0", features = ["codec"], optional = true } 17 | tokio02-core = { package = "tokio", version = "0.2.22", features = ["full"], optional = true } 18 | tokio02-util = { package = "tokio-util", version = "0.3.1", features = ["codec"], optional = true } 19 | pin-project = "1.0.1" 20 | thiserror = "1.0.21" 21 | serde = { version = "1.0.117", optional = true, features = ["derive"] } 22 | ii-logging = { path = "../../utils-rs/logging" } 23 | 24 | [dev-dependencies] 25 | serde_json = "1.0.59" 26 | 27 | [features] 28 | default = ["tokio12"] 29 | tokio12 = ["tokio", "tokio-util", "bytes"] 30 | tokio03 = ["tokio03-core", "tokio03-util", "bytes06"] 31 | tokio02 = ["tokio02-core", "tokio02-util", "bytes05"] 32 | -------------------------------------------------------------------------------- /utils-rs/metrics/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | mod prometheus_registry; 24 | /// Reexport primitives 25 | pub use prometheus_registry::*; 26 | 27 | #[derive(thiserror::Error, Debug)] 28 | pub enum Error { 29 | #[error("Prometheus metric processing related error: {0}")] 30 | PrometheusError(#[from] prometheus::Error), 31 | } 32 | 33 | pub type Result = std::result::Result; 34 | -------------------------------------------------------------------------------- /protocols/stratum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ii-stratum" 3 | version = "0.1.0" 4 | authors = ["Braiins "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2018" 7 | 8 | [[bin]] 9 | name = "ii-stratum-keytool" 10 | path = "src/keytool.rs" 11 | #test = false 12 | bench = false 13 | 14 | [dependencies] 15 | ii-bitcoin = { path = "../../coins/bitcoin" } 16 | ii-wire = { path = "../wire" } 17 | ii-async-utils = { path = "../../utils-rs/async-utils" } 18 | ii-logging = { path = "../../utils-rs/logging" } 19 | ii-unvariant = { path = "../../utils-rs/unvariant/unvariant"} 20 | futures = "0.3.5" 21 | async-trait = "0.1" 22 | tokio = { version = "1.2.0", features = ["full"] } 23 | tokio-util = { version = "0.6.3", features = ["codec"] } 24 | bytes = "1.0.1" 25 | thiserror = "1.0.21" 26 | anyhow = "1.0.33" 27 | lazy_static = "1.4.0" 28 | serde = { version = "1.0.117", features = ["derive"] } 29 | serde_json = "1.0.59" 30 | serde_repr = "0.1.6" 31 | byteorder = "1.3.4" 32 | hex = "0.4.2" 33 | # Temporarily disabled, see v1 TODO 34 | #serde_tuple = "0.2.2" 35 | packed_struct = "0.3.1" 36 | packed_struct_codegen = "0.3.1" 37 | bitcoin_hashes = "0.9.4" 38 | snow = {version = "0.7.2", features = ["ring-accelerated"]} 39 | primitive-types = "0.7.2" 40 | structopt = "0.3.20" 41 | rand = "0.7.3" 42 | ed25519-dalek = { version = "1.0.1", features = ["serde"] } 43 | x25519-dalek = "1.1.0" 44 | bs58 = { version ="0.3.1", features = ["check"] } 45 | 46 | [dev-dependencies] 47 | byte_string = "1.0.0" 48 | 49 | [features] 50 | v2json = [] 51 | -------------------------------------------------------------------------------- /protocols/stratum/src/v1/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Version 1 errors only 24 | 25 | use thiserror::Error; 26 | 27 | #[derive(Error, Clone, Eq, PartialEq, Debug)] 28 | pub enum Error { 29 | /// Json error. 30 | #[error("JSON error: {0}")] 31 | Json(String), 32 | 33 | #[error("Rpc error: {0}")] 34 | Rpc(String), 35 | 36 | #[error("Subscription error: {0}")] 37 | Subscribe(String), 38 | 39 | #[error("Submit error: {0}")] 40 | Submit(String), 41 | } 42 | -------------------------------------------------------------------------------- /noise-proxy/src/dummy_metrics.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::sync::Arc; 24 | 25 | pub struct MetricsRegistry; 26 | pub struct NoiseProxyMetrics; 27 | 28 | impl NoiseProxyMetrics { 29 | pub fn new() -> (Arc, MetricsRegistry) { 30 | (Arc::new(NoiseProxyMetrics), MetricsRegistry) 31 | } 32 | 33 | pub fn account_successful_tcp_open(&self) {} 34 | 35 | pub fn account_failed_tcp_open(&self) {} 36 | 37 | pub fn account_tcp_close_in_stage(&self, _: &str) {} 38 | } 39 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Version 2 errors only 24 | 25 | use thiserror::Error; 26 | 27 | #[derive(Error, Clone, Eq, PartialEq, Debug)] 28 | pub enum Error { 29 | #[error("Unknown message error: {0}")] 30 | UnknownMessage(String), 31 | 32 | #[error("Channel not operational: {0}")] 33 | ChannelNotOperational(String), 34 | 35 | #[error("Type length is out of the permitted range: {0}, max: {1}")] 36 | DataTypeOverflow(usize, usize), 37 | } 38 | -------------------------------------------------------------------------------- /stratum-proxy/src/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use futures::channel::mpsc; 24 | use std::{convert::TryInto, fmt}; 25 | 26 | /// Converts the response message into a `Frame` and submits it into the 27 | /// specified queue 28 | pub fn submit_message(tx: &mut mpsc::Sender, msg: T) -> Result<(), mpsc::TrySendError> 29 | where 30 | F: Send + Sync + 'static, 31 | T: TryInto, 32 | >::Error: fmt::Debug, 33 | { 34 | let frame = msg 35 | .try_into() 36 | .expect("BUG: Could convert the message to frame"); 37 | tx.try_send(frame) 38 | } 39 | -------------------------------------------------------------------------------- /stratum-proxy/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Stratum proxy library provides functionality for proxying any combination of Stratum V1 and V2 24 | //! protocol version 25 | 26 | // Increase recursion limit as e.g. `select!` macro and other complex macros quickly run out of 27 | // the default recursion limit if more complex statements are used 28 | #![recursion_limit = "256"] 29 | 30 | pub mod error; 31 | pub mod frontend; 32 | #[cfg_attr(not(feature = "prometheus_metrics"), path = "dummy_metrics.rs")] 33 | pub mod metrics; 34 | pub mod server; 35 | pub mod translation; 36 | pub mod util; 37 | -------------------------------------------------------------------------------- /protocols/wire/src/framing.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::fmt::Debug; 24 | 25 | use crate::{tokio, tokio_util}; 26 | 27 | use tokio::io::Error as IOError; 28 | use tokio_util::codec::{Decoder, Encoder}; 29 | 30 | pub trait Framing: 'static { 31 | /// Send message type 32 | type Tx: Send + Sync; 33 | /// Receive message type 34 | type Rx: Send + Sync; 35 | type Error: From; 36 | type Codec: Encoder 37 | + Decoder 38 | + Default 39 | + Unpin 40 | + Send 41 | + Debug 42 | + 'static; 43 | } 44 | -------------------------------------------------------------------------------- /protocols/stratum/src/test_utils/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Miscellaneous constants shared among test utils to generate consistent messages 24 | 25 | pub const BRAIINS_OS_RELEASE: &str = "2019-06-05"; 26 | pub const MINER_SW_SIGNATURE: &str = "Braiins OS 2019-06-05"; 27 | pub const POOL_URL: &str = "stratum.slushpool.com"; 28 | pub const POOL_PORT: usize = 3333; 29 | pub const USER_CREDENTIALS: &str = "braiins.worker0"; 30 | 31 | /// Nonce for the block header of the sample mining job 32 | pub const MINING_WORK_NONCE: u32 = 0x0443c37b; 33 | /// Version for the block header 34 | pub const MINING_WORK_VERSION: u32 = 0x20000000; 35 | /// Ntime for the block header 36 | pub const MINING_WORK_NTIME: u32 = 0x5d10bc0a; 37 | -------------------------------------------------------------------------------- /utils-rs/logging/tests/panic.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Test that setting logger config after using the logger 24 | //! results in a panic. 25 | //! 26 | //! **Warning**: Each logging test needs to be in a separate files 27 | //! due to global LOGGER initialization 28 | 29 | use ii_logging::macros::*; 30 | use ii_logging::{self, LoggingConfig}; 31 | 32 | #[test] 33 | #[should_panic] 34 | fn test_logging_config_too_late() { 35 | // Use silent config 36 | ii_logging::set_logger_config(LoggingConfig::no_logging()); 37 | 38 | // Log something 39 | trace!("This will tirgger LOGGER instantiation"); 40 | 41 | // This should now panic 42 | ii_logging::set_logger_config(LoggingConfig::default()); 43 | } 44 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/coins.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | """Helper module with generic coin algorithms""" 24 | 25 | 26 | class Target: 27 | def __init__(self, target: int, diff_1_target: int): 28 | self.target = target 29 | self.diff_1_target = diff_1_target 30 | 31 | def to_difficulty(self): 32 | """Converts target to difficulty at the network specified by diff_1_target""" 33 | return self.diff_1_target // self.target 34 | 35 | @staticmethod 36 | def from_difficulty(diff, diff_1_target): 37 | """Converts difficulty to target at the network specified by diff_1_target""" 38 | return Target(diff_1_target // diff, diff_1_target) 39 | 40 | def div_by_factor(self, factor: float): 41 | self.target = self.target // factor 42 | 43 | def __str__(self): 44 | return '{}(diff={})'.format(type(self).__name__, self.to_difficulty()) 45 | -------------------------------------------------------------------------------- /protocols/stratum/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | pub mod error; 24 | pub mod payload; 25 | pub mod v1; 26 | pub mod v2; 27 | 28 | pub use error::Result; 29 | 30 | /// Mask for allowed version bits that can be rolled based on BIP320 31 | pub const BIP320_N_VERSION_MASK: u32 = 0x1fffe000; 32 | 33 | /// Maximum number of bits allowed by BIP320_N_VERSION_MASK 34 | pub const BIP320_N_VERSION_MAX_BITS: usize = 16; 35 | 36 | /// Describes protocol and its associated Handler 37 | pub trait Protocol { 38 | type Header; 39 | } 40 | 41 | pub trait AnyPayload: Sync + Send { 42 | /// The payload is serialized to a specified `writer` 43 | fn serialize_to_writer(&self, writer: &mut dyn std::io::Write) -> Result<()>; 44 | } 45 | 46 | // This is here because some test utilities need to be shared between 47 | // both unit and integration tests. 48 | #[doc(hidden)] 49 | pub mod test_utils; 50 | -------------------------------------------------------------------------------- /protocols/wire/src/proxy/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use crate::tokio; 24 | 25 | use thiserror::Error; 26 | 27 | /// Error type for this module 28 | #[derive(Error, Debug)] 29 | pub enum Error { 30 | #[error("Proxy protocol error: {0}")] 31 | Proxy(String), 32 | 33 | #[error("Proxy protocol V2 error: {0}")] 34 | ProxyV2(#[from] crate::proxy::codec::v2::proto::Error), 35 | 36 | #[error("IO error: {0}")] 37 | Io(#[from] tokio::io::Error), 38 | 39 | #[error("Invalid encoding of proxy header: {0}")] 40 | Utf8(#[from] std::str::Utf8Error), 41 | 42 | #[error("Invalid address in proxy header: {0}")] 43 | IPAddress(#[from] std::net::AddrParseError), 44 | 45 | #[error("Invalid port in proxy header: {0}")] 46 | Port(#[from] std::num::ParseIntError), 47 | 48 | #[error("Invalid state: {0}")] 49 | InvalidState(String), 50 | } 51 | 52 | /// Convenient Result type, with our Error included 53 | pub type Result = std::result::Result; 54 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/messages/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use bytes::{BufMut, BytesMut}; 24 | 25 | use super::*; 26 | use crate::test_utils::v2::*; 27 | use crate::AnyPayload; 28 | 29 | #[test] 30 | fn test_deserialize_setup_connection() { 31 | let deserialized = SetupConnection::try_from(SETUP_CONNECTION_SERIALIZED) 32 | .expect("BUG: Deserialization failed"); 33 | 34 | assert_eq!( 35 | deserialized, 36 | build_setup_connection(), 37 | "Deserialization is not correct" 38 | ); 39 | } 40 | 41 | #[test] 42 | fn test_serialize_setup_connection() { 43 | let message = build_setup_connection(); 44 | let mut writer = bytes::BytesMut::new().writer(); 45 | message 46 | .serialize_to_writer(&mut writer) 47 | .expect("BUG: Cannot serialize message"); 48 | let serialized_message = writer.into_inner(); 49 | 50 | // The message has been serialized completely, let's skip the header for now 51 | assert_eq!( 52 | BytesMut::from(&SETUP_CONNECTION_SERIALIZED[..]), 53 | serialized_message 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /stratum-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This proxy demonstrates 1:1 translation of Stratum V2 protocol to Stratum V1. It 4 | listens for downstream Stratum V2 connections and translates the protocol to 5 | upstream Stratum V1 node. 6 | 7 | 8 | # Build 9 | 10 | ## Prerequisites 11 | 12 | - Rust toolchain installed via [rustup](https://rustup.rs/) 13 | 14 | ## Building it 15 | 16 | `cargo build --release` 17 | 18 | ## Running the proxy via cargo 19 | See sample configurations in `config` directory, check if listen_address (proxy socket address) 20 | and upstream_address are correctly set or leave provided defaults. 21 | 22 | If secure mode is required, check that certificate and secret key fiel paths are set correctly. 23 | ### Running insecure Stratum V2 protocol version 24 | 1. run proxy with insecure configuration option: 25 | `cargo run --release -- --conf config/insecure.toml` 26 | 1. configure bosminer pool url to use insecure scheme: 27 | `stratum2+tcp+insecure://` 28 | ### Running secure Stratum V2 protocol version 29 | 1. generate keys and certificates: `bash config/gen_keys.sh`. Generated keys and certificates are stored in 30 | config directory so that their relative path matches default sample configuration for secure mode. 31 | 1. `cargo run --release -- --conf config/secure.toml` 32 | 1. configure bosminer pool url to validate against generated authority_public_key: 33 | 1. `cat config/ca-ed25519-public.key` 34 | 2. -> `{"ed25519_public_key": "ZZ6uJT6kaDRKmJZvUdcYnFoUYv2T4SK5VcB88MVuVVHrJe6rw"}` 35 | 3. `stratum2+tcp:///ZZ6uJT6kaDRKmJZvUdcYnFoUYv2T4SK5VcB88MVuVVHrJe6rw` 36 | 37 | ## Running it directly 38 | `cargo build` command generates binary file `./target/release/ii-stratum-proxy`. 39 | 40 | This file can be run directly, e. g. 41 | 42 | `./target/release/ii-stratum-proxy --conf config/insecure.toml` 43 | 44 | 45 | 46 | # Future Work 47 | 48 | Below is a high level list of areas that still need to be resolved: 49 | 50 | - handle multiple channels on a single downstream connection 51 | - use V2 submission sequence numbers for batch acknowledgement of valid job 52 | solutions 53 | - improve logging 54 | - resolve all TODO's in the sources 55 | -------------------------------------------------------------------------------- /protocols/wire/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | #![allow(clippy::single_component_path_imports)] 23 | 24 | #[cfg(all(feature = "tokio03", feature = "tokio02"))] 25 | compile_error!("You can't use both Tokio 0.3 and 0.2. Note: The `tokio02` feature requires default features to be turned off"); 26 | 27 | #[cfg(feature = "tokio12")] 28 | pub(crate) use tokio; 29 | #[cfg(feature = "tokio12")] 30 | pub(crate) use tokio_util; 31 | 32 | #[cfg(feature = "tokio03")] 33 | pub(crate) use tokio03_core as tokio; 34 | #[cfg(feature = "tokio03")] 35 | pub(crate) use tokio03_util as tokio_util; 36 | 37 | #[cfg(feature = "tokio02")] 38 | pub(crate) use tokio02_core as tokio; 39 | #[cfg(feature = "tokio02")] 40 | pub(crate) use tokio02_util as tokio_util; 41 | 42 | #[cfg(feature = "bytes")] 43 | pub(crate) use bytes; 44 | #[cfg(feature = "bytes05")] 45 | pub(crate) use bytes05 as bytes; 46 | #[cfg(feature = "bytes06")] 47 | pub(crate) use bytes06 as bytes; 48 | 49 | #[macro_use] 50 | extern crate ii_logging; 51 | 52 | mod connection; 53 | pub use connection::*; 54 | 55 | mod server; 56 | pub use server::*; 57 | 58 | mod client; 59 | pub use client::*; 60 | 61 | mod framing; 62 | pub use framing::*; 63 | 64 | pub mod proxy; 65 | -------------------------------------------------------------------------------- /protocols/stratum/sim/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This project is a simulation of the Stratum mining protocol. It currently supports both versions: **Stratum V1** and **Stratum V2**. The intention is to verify the design of **Stratum V2** with regards to translating between both protocol variants. At the same time, the platform can serve as a testbed for various network latency scenarios. 4 | 5 | Last but not least, the idea is to have a reference implementation of both protocols that serves the blueprint specification of the messages. 6 | 7 | 8 | # Features 9 | 10 | - test network latency issues 11 | - complete definition of protocol messages 12 | - pool rejects stale shares since it simulates finding new blocks 13 | - miners simulate finding shares based exponential distribution 14 | - plot results of series of simmulations 15 | 16 | 17 | ## Install 18 | 19 | Simulation requires Python 3.7. 20 | 21 | The easiest way to run the simulation is to use python `virtualenvwrapper` 22 | 23 | 24 | ### The `virtualenvwrapper` way 25 | 26 | ``` 27 | mkvirtualenv --python=/usr/bin/python3.7 stratum-sim 28 | pip install -r ./requirements.txt 29 | ``` 30 | 31 | ## Running Stratum V2 Simulation 32 | 33 | `python ./pool_miner_sim.py --verbose --latency=0.2` 34 | 35 | ## Running Stratum V1 Simulation 36 | 37 | `python ./pool_miner_sim.py --verbose --latency=0.2 --v1` 38 | 39 | ## Running Stratum V2 Miner and V1 Pool using Proxy Simulation 40 | 41 | `python ./pool_miner_sim.py --verbose --latency=0.2 --v2v1` 42 | 43 | ## Simulate V2-V2, V1-V1 and V2-proxy-V1 and plot results into PDF report 44 | 45 | `python ./simulate_and_plot_results.py` 46 | 47 | 48 | # Future Work 49 | 50 | The simulation is far from complete. Currently, it supports the following 51 | scenarios: 52 | 53 | ``` 54 | 2xminer (V1) ----> pool (V1) 55 | 2xminer (V2) ----> pool (V2) 56 | miner (V2) ----> proxy (translating) ---> pool (V1) 57 | ``` 58 | 59 | Example scenarios that need to be to be covered: 60 | 61 | ``` 62 | miner (V1) ----> proxy (V1:V2) ---> pool (V2) 63 | ``` 64 | 65 | The current simulation output is very basic, and we know that it could be extended much further. Below are a few points that could be covered in future iterations: 66 | - implement BDD scenarios using gherkin language to run a full set of simulation scenarios 67 | - provide more advanced statistics with chart plotting 68 | -------------------------------------------------------------------------------- /stratum-proxy/tests/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use futures::prelude::*; 24 | use std::time::Duration; 25 | use tokio::time; 26 | 27 | /// Run an async function/lambda repeatedly with backoff until it 28 | /// returns Ok(...) or until the number of inerations is reached. 29 | /// 30 | /// `start_delay` is the starting timeout in milliseconds, `iterations` 31 | /// is the maximum number of re-tries. The delay is doubled in each iteration. 32 | /// 33 | /// The last `Result` from the callback function is returned, 34 | /// carrying either an Ok value or an error. 35 | /// TODO: review tokio-retry if that would be a suitable implementation instead of a custom one 36 | /// NB. tokio-retry seems to not be updated for tokio 0.2 37 | pub async fn backoff>, F: Fn() -> FT>( 38 | start_delay: u32, 39 | iterations: u32, 40 | f: F, 41 | ) -> Result { 42 | let mut delay = start_delay; 43 | let mut res = f().await; 44 | if res.is_ok() { 45 | return res; 46 | } 47 | 48 | for _ in 0..iterations { 49 | time::sleep(Duration::from_millis(delay as u64)).await; 50 | delay *= 2; 51 | 52 | res = f().await; 53 | if res.is_ok() { 54 | return res; 55 | } 56 | } 57 | 58 | res 59 | } 60 | -------------------------------------------------------------------------------- /protocols/stratum/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This is a Stratum protocol software package that provides: 4 | 5 | - Stratum V1/V2 primitives implemented in Rust 6 | - [Simulator](sim/README.md) used to verify the design of Stratum V2 7 | 8 | ## Stratum Server Certificate Workflow 9 | 10 | Stratum V2 security is based on [noise handshake](https://noiseprotocol.org/noise.html#handshake-patterns), where the static public key of the stratum server is signed by a simple **Certification Authority**. Any stratum client is required to have preconfigured the public key of this certification authority and use it to verify the authenticity of the static public key presented by the server. 11 | 12 | ### Building the Tool 13 | 14 | For security reasons, we recommend using the tool built from sources. 15 | 16 | Setup the Rust toolchain installed via [rustup](https://rustup.rs/). 17 | 18 | ### Workflow 19 | The overall workflow requires: 20 | 21 | #### Generating a **Certification Authority ED25519 keypair** 22 | 23 | This **CA keypair** *MUST* never be deployed to a stratum server and is used 24 | exclusively for signing the server keys: 25 | 26 | ``` 27 | cargo run -- gen-ca-key 28 | ``` 29 | 30 | The resulting keys are in: 31 | - `ca-ed25519-public.key`, and 32 | - `ca-ed25519-secret.key`. 33 | 34 | Keep them safe! 35 | 36 | #### Generating server keypair 37 | 38 | ``` 39 | cargo run -- gen-noise-key 40 | ``` 41 | 42 | The resulting keys are in: 43 | 44 | - `server-noise-static-secret.key`, and 45 | - `server-noise-static-public.key`. 46 | 47 | #### Signing the server public key 48 | 49 | We will sign the public key from the previous step with the **CA Private Key** and produce a certificate that has a specified validity (defaults to 90 days). 50 | 51 | ``` 52 | cargo run -- sign-key --public-key-to-sign server-noise-static-public.key 53 | --signing-key ca-ed25519-secret.key 54 | ``` 55 | 56 | The following files are to be uploaded to the stratum server that provides 57 | stratum v2 (e.g. our ii-stratum-proxy): 58 | 59 | - `server-noise-static-secret.key`, and 60 | - `server-noise-static-public.cert`. 61 | 62 | #### Testing Stratum Client Setup 63 | 64 | In case you decide to run the miner against your own stratum V2 endpoint (e.g. [ii-stratum-proxy](../../stratum-proxy/README.md)) you have to pass it the actual public key of the Pool CA that has been used for signing. 65 | 66 | 67 | ## Running Protocol Test suite 68 | 69 | `cargo test --all` 70 | -------------------------------------------------------------------------------- /protocols/wire/src/server.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! TODO: Remove this module 24 | 25 | use std::net::TcpListener as StdTcpListener; 26 | use std::net::ToSocketAddrs as StdToSocketAddrs; 27 | use std::pin::Pin; 28 | use std::task::{Context, Poll}; 29 | 30 | use crate::tokio; 31 | 32 | use futures::prelude::*; 33 | use futures::ready; 34 | use tokio::net::{TcpListener, TcpStream}; 35 | 36 | #[derive(Debug)] 37 | pub struct Server { 38 | tcp: Option, 39 | } 40 | 41 | impl Server { 42 | pub fn bind(addr: A) -> std::io::Result { 43 | let tcp = StdTcpListener::bind(addr)?; 44 | tcp.set_nonblocking(true)?; 45 | let tcp = Some(TcpListener::from_std(tcp)?); 46 | 47 | Ok(Server { tcp }) 48 | } 49 | 50 | pub fn shutdown(&mut self) { 51 | self.tcp = None; 52 | } 53 | } 54 | 55 | impl Stream for Server { 56 | type Item = std::io::Result; 57 | 58 | // NB. the unused_mut is because of tokio02 where the mut is required 59 | fn poll_next( 60 | #[allow(unused_mut)] mut self: Pin<&mut Self>, 61 | cx: &mut Context, 62 | ) -> Poll> { 63 | if let Some(tcp) = self.tcp.as_mut() { 64 | let (socket, _) = ready!(tcp.poll_accept(cx))?; 65 | Poll::Ready(Some(Ok(socket))) 66 | } else { 67 | Poll::Ready(None) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /stratum-proxy/src/dummy_metrics.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | //! Empty metrics for the case when stratum proxy is compiled with prometheus metrics disabled 23 | 24 | use ii_stratum::v1::rpc::Method; 25 | pub use primitive_types::U256; 26 | use std::time::Instant; 27 | use tokio::time::Duration; 28 | 29 | pub struct ProxyMetrics; 30 | 31 | impl ProxyMetrics { 32 | pub fn account_accepted_share(&self, _target: Option) {} 33 | 34 | pub fn account_rejected_share(&self, _target: Option) {} 35 | 36 | pub fn account_successful_tcp_open(&self) {} 37 | 38 | pub fn account_unsuccessful_tcp_open(&self) {} 39 | 40 | pub fn observe_v1_request_success(&self, _request_method: Method, _duration: Duration) {} 41 | 42 | pub fn observe_v1_request_error(&self, _request_method: Method, _duration: Duration) {} 43 | 44 | pub fn tcp_connection_timer_observe(&self, _timer: Instant) {} 45 | 46 | pub fn tcp_connection_close_ok(&self) {} 47 | 48 | pub fn tcp_connection_close_with_error(&self, _error: &crate::error::Error) {} 49 | 50 | pub fn account_tcp_listener_breakdown(&self) {} 51 | 52 | pub fn accounted_spawn( 53 | self: &std::sync::Arc, 54 | future: T, 55 | ) -> tokio::task::JoinHandle 56 | where 57 | T: std::future::Future + Send + 'static, 58 | T::Output: Send + 'static, 59 | { 60 | tokio::spawn(future) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /utils-rs/logging/tests/basic.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Test of logging basic setup and usage. 24 | //! 25 | //! **Warning**: Each logging test needs to be in a separate files 26 | //! due to global LOGGER initialization 27 | 28 | use std::env; 29 | use std::fs; 30 | 31 | use ii_logging::macros::*; 32 | use ii_logging::{self, Level, LoggingConfig, LoggingTarget, LOGGER}; 33 | 34 | use tempfile::NamedTempFile; 35 | 36 | #[test] 37 | fn test_logging_basic() { 38 | const LOG_MSG: &'static str = "Hello, World!"; 39 | 40 | // Set RUST_LOG to "": Don't let outer environment influence the test 41 | // and test the behaviour if RUST_LOG is empty 42 | env::set_var("RUST_LOG", ""); 43 | 44 | // Create configuration 45 | let temp_file = NamedTempFile::new().expect("Could not create temporary file"); 46 | let config = LoggingConfig { 47 | target: LoggingTarget::File(temp_file.path().into()), 48 | level: Level::Trace, 49 | drain_channel_size: LoggingConfig::ASYNC_LOGGER_DRAIN_CHANNEL_SIZE, 50 | }; 51 | 52 | // Setup logger 53 | ii_logging::set_logger_config(config); 54 | let flush_guard = LOGGER.take_guard(); 55 | 56 | // Log a message and flush logs 57 | trace!("{}", LOG_MSG); 58 | drop(flush_guard); 59 | 60 | // Verify message 61 | let log_contents = fs::read_to_string(temp_file.path()).expect("Could not read back log file"); 62 | assert!(log_contents.find(LOG_MSG).is_some()); 63 | } 64 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/tests/unvariant.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use ii_unvariant::{unvariant, Id}; 24 | 25 | mod common; 26 | use common::*; 27 | 28 | #[test] 29 | fn unvariant_macro() { 30 | let get_value = |frame: Frame| -> Result { 31 | unvariant!(frame { 32 | foo: Foo => Ok(foo.into()), 33 | bar: Bar => Ok(bar.into()), 34 | id: _ => Err(id), 35 | }) 36 | }; 37 | 38 | let res = get_value(Frame::new_foo(9001)); 39 | assert_eq!(res, Ok(9001 + Foo::ID)); 40 | 41 | let res = get_value(Frame::new_bar(false)); 42 | assert_eq!(res, Ok(1 + Bar::ID)); 43 | 44 | let res = get_value(Frame::new_unknown()); 45 | assert_eq!(res, Err(0xff)); 46 | } 47 | 48 | #[test] 49 | fn unvariant_macro_try() { 50 | let get_value = |frame: Frame| -> Result { 51 | unvariant!(try frame { 52 | res: TryFoo => res.map(Into::into), 53 | res: TryBar => res.map(Into::into), 54 | id: _ => Err(id), 55 | }) 56 | }; 57 | 58 | let res = get_value(Frame::new_foo(9001)); 59 | assert_eq!(res, Ok(9001 + TryFoo::ID)); 60 | 61 | let res = get_value(Frame::new_bar(true)); 62 | assert_eq!(res, Ok(2 + TryBar::ID)); 63 | 64 | let res = get_value(Frame::new_foo_bad()); 65 | assert_eq!(res, Err(TryFoo::ID)); 66 | 67 | let res = get_value(Frame::new_bar_bad()); 68 | assert_eq!(res, Err(TryBar::ID)); 69 | 70 | let res = get_value(Frame::new_unknown()); 71 | assert_eq!(res, Err(0xff)); 72 | } 73 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | extern crate proc_macro; 24 | 25 | use proc_macro::TokenStream; 26 | use quote::quote; 27 | use syn::parse_macro_input; 28 | 29 | mod extensions; 30 | mod handler; 31 | mod id; 32 | mod unvariant; 33 | 34 | #[proc_macro_attribute] 35 | pub fn id(args: TokenStream, input: TokenStream) -> TokenStream { 36 | let args = parse_macro_input!(args as id::Args); 37 | let item = parse_macro_input!(input as id::Item); 38 | id::expand(args, item).into() 39 | } 40 | 41 | // Note: We can't use a regular proc_macro here because that can't yet be used 42 | // in an expression context. And so dtolnay's proc_macro_hack is used here instead. 43 | // Cf. https://github.com/rust-lang/rust/issues/54727 44 | // This is to be straightened up once stable rust supports proc_macros in expr context. 45 | #[proc_macro] 46 | pub fn unvariant(input: TokenStream) -> TokenStream { 47 | let item = parse_macro_input!(input as unvariant::Item); 48 | unvariant::expand(item).into() 49 | } 50 | 51 | #[proc_macro_attribute] 52 | pub fn handler(mut args: TokenStream, input: TokenStream) -> TokenStream { 53 | // Hack: args and input are merged here because we need the info 54 | // from args in the impl parser. An `@` token is used as a separator. 55 | let at = quote!(@); 56 | let at: TokenStream = at.into(); 57 | args.extend(at); 58 | args.extend(input); 59 | let input = args; 60 | let item = parse_macro_input!(input as handler::Item); 61 | 62 | handler::expand(item).into() 63 | } 64 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/tests/id.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use ii_unvariant::{id, id_for, Id}; 24 | 25 | #[test] 26 | fn id_macro() { 27 | #[id(0x11)] 28 | struct IdU32; 29 | let id: u32 = IdU32::ID; 30 | assert_eq!(id, 0x11); 31 | 32 | #[id(0x11u8)] 33 | struct IdU8; 34 | let id: u8 = IdU8::ID; 35 | assert_eq!(id, 0x11); 36 | 37 | #[id(0x11u8 type u8)] 38 | struct IdU8Explicit; 39 | let id: u8 = IdU8Explicit::ID; 40 | assert_eq!(id, 0x11); 41 | 42 | #[id(0x11u64)] 43 | struct IdU64; 44 | let id: u64 = IdU64::ID; 45 | assert_eq!(id, 0x11); 46 | 47 | #[id(0x11i64)] 48 | struct IdI64; 49 | let id: i64 = IdI64::ID; 50 | assert_eq!(id, 0x11); 51 | 52 | #[id("ii")] 53 | struct IdStr; 54 | let id: &str = IdStr::ID; 55 | assert_eq!(id, "ii"); 56 | 57 | #[id((3, "ii") type (u8, &'static str))] 58 | struct IdComplex; 59 | let id: (u8, &str) = IdComplex::ID; 60 | assert_eq!(id, (3, "ii")); 61 | 62 | struct IdFor; 63 | id_for!(u8, IdFor => 0x11); 64 | let id: u8 = IdFor::ID; 65 | assert_eq!(id, 0x11); 66 | 67 | struct IdForMulti1; 68 | struct IdForMulti2; 69 | struct IdForMulti3; 70 | id_for!(u8, 71 | IdForMulti1 => 0x11, 72 | IdForMulti2 => 0x12, 73 | IdForMulti3 => 0x13, 74 | ); 75 | let id: u8 = IdForMulti1::ID; 76 | assert_eq!(id, 0x11); 77 | let id: u8 = IdForMulti2::ID; 78 | assert_eq!(id, 0x12); 79 | let id: u8 = IdForMulti3::ID; 80 | assert_eq!(id, 0x13); 81 | } 82 | -------------------------------------------------------------------------------- /protocols/stratum/src/v1/framing/codec.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use bytes::{BufMut, BytesMut}; 24 | use tokio_util::codec::{Decoder, Encoder, LinesCodec}; 25 | 26 | use super::Frame; 27 | use crate::error::Error; 28 | 29 | // FIXME: check bytesmut capacity when encoding (use BytesMut::remaining_mut()) 30 | 31 | /// TODO consider generalizing the codec 32 | #[derive(Debug)] 33 | pub struct Codec(LinesCodec); 34 | 35 | impl Decoder for Codec { 36 | type Item = Frame; 37 | type Error = Error; 38 | 39 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 40 | let frame_str = self.0.decode(src)?; 41 | let mut bytes = match frame_str { 42 | // Note, creating `BytesMut` instance this way creates another copy of the incoming 43 | // data. We would have to implement a custom decode that would buffer the data 44 | // directly in 1BytesMut` 45 | // this copies the frame into the 46 | Some(frame_str) => BytesMut::from(frame_str.as_bytes()), 47 | None => return Ok(None), 48 | }; 49 | Ok(Some(Frame::deserialize(&mut bytes))) 50 | } 51 | } 52 | 53 | impl Encoder for Codec { 54 | type Error = Error; 55 | 56 | fn encode(&mut self, item: Frame, dst: &mut BytesMut) -> Result<(), Self::Error> { 57 | item.serialize(dst)?; 58 | dst.put_u8(b'\n'); 59 | Ok(()) 60 | } 61 | } 62 | 63 | impl Default for Codec { 64 | fn default() -> Self { 65 | // TODO: limit line length with new_with_max_length() ? 66 | // !!!! MUST BE LIMITED!!! 16kB seems about right 67 | Codec(LinesCodec::new()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /noise-proxy/src/metrics.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::sync::Arc; 24 | 25 | use ii_metrics::MetricsRegistry; 26 | use prometheus::IntCounterVec; 27 | 28 | pub struct NoiseProxyMetrics { 29 | tcp_connection_open_total: IntCounterVec, 30 | tcp_connection_close_stage: IntCounterVec, 31 | } 32 | 33 | impl NoiseProxyMetrics { 34 | pub fn new() -> (Arc, MetricsRegistry) { 35 | let registry = MetricsRegistry::default(); 36 | let collector = Self::from_registry(®istry); 37 | (collector, registry) 38 | } 39 | 40 | pub fn from_registry(registry: &MetricsRegistry) -> Arc { 41 | Arc::new(NoiseProxyMetrics { 42 | tcp_connection_open_total: registry.register_generic_counter_vec( 43 | "noise_tcp_connection_open", 44 | "Number of TCP-open events", 45 | &["result"], 46 | ), 47 | tcp_connection_close_stage: registry.register_generic_counter_vec( 48 | "noise_tcp_connection_close", 49 | "Number of TCP-close events", 50 | &["result"], 51 | ), 52 | }) 53 | } 54 | } 55 | 56 | impl NoiseProxyMetrics { 57 | pub fn account_successful_tcp_open(&self) { 58 | self.tcp_connection_open_total 59 | .with_label_values(&["success"]) 60 | .inc(); 61 | } 62 | 63 | pub fn account_failed_tcp_open(&self) { 64 | self.tcp_connection_open_total 65 | .with_label_values(&["fail"]) 66 | .inc(); 67 | } 68 | 69 | pub fn account_tcp_close_in_stage(&self, stage: &str) { 70 | self.tcp_connection_close_stage 71 | .with_label_values(&[stage]) 72 | .inc(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /utils-rs/scm/scm/src/global.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use once_cell::sync::OnceCell; 24 | 25 | use std::default::Default; 26 | 27 | static VERSION: OnceCell = OnceCell::new(); 28 | 29 | #[derive(Clone, Debug)] 30 | pub struct Version { 31 | signature: String, 32 | full: String, 33 | } 34 | 35 | impl Default for Version { 36 | fn default() -> Self { 37 | Self { 38 | signature: "version-not-set".into(), 39 | full: "version-not-set".into(), 40 | } 41 | } 42 | } 43 | 44 | impl Version { 45 | /// Example values: `Version::set("StratumProxy", ii_scm::version_full!().as_str())` 46 | pub fn set(signature: &str, full: &str) { 47 | VERSION 48 | .set(Self { 49 | signature: signature.to_string(), 50 | full: full.to_string(), 51 | }) 52 | .expect("BUG: version is already set"); 53 | } 54 | 55 | /// Try to set version, return resul of operation 56 | pub fn try_set(signature: &str, full: &str) -> Result<(), Self> { 57 | VERSION.set(Self { 58 | signature: signature.to_string(), 59 | full: full.to_string(), 60 | }) 61 | } 62 | 63 | #[inline] 64 | pub fn get() -> &'static Self { 65 | VERSION.get_or_init(Default::default) 66 | } 67 | 68 | #[inline] 69 | pub fn signature() -> &'static String { 70 | &Self::get().signature 71 | } 72 | 73 | #[inline] 74 | pub fn full() -> &'static String { 75 | &Self::get().full 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | 83 | #[test] 84 | fn global_version() { 85 | Version::set("scm", "1"); 86 | 87 | assert_eq!(Version::signature(), "scm"); 88 | assert_eq!(Version::full(), "1"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /utils-rs/scm/scm/src/version.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | /// " -" 24 | #[macro_export] 25 | macro_rules! version_full { 26 | ($($tt:tt)*) => { 27 | format!( 28 | "{} {}-{}", 29 | env!("CARGO_PKG_NAME"), 30 | $crate::version_semantic!(), 31 | $crate::version_git!($($tt)*) 32 | ) 33 | } 34 | } 35 | 36 | #[macro_export] 37 | macro_rules! version_semantic { 38 | () => { 39 | env!("CARGO_PKG_VERSION") 40 | }; 41 | } 42 | 43 | #[macro_export] 44 | macro_rules! version_git { 45 | ($($tt:tt)*) => { 46 | $crate::git_hash!(length = 8, $($tt)*) 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | #[test] 53 | fn full() { 54 | let hash = ii_scm_macros::git_hash!(object = "HEAD", length = 8); 55 | assert_eq!(hash.len(), 8); 56 | assert_eq!(version_full!(), format!("ii-scm 0.1.0-{}", hash)); 57 | } 58 | 59 | #[test] 60 | fn full_args() { 61 | let hash = ii_scm_macros::git_hash!(object = "HEAD", length = 5); 62 | assert_eq!(hash.len(), 5); 63 | assert_eq!( 64 | version_full!(object = "HEAD", length = 5), 65 | format!("ii-scm 0.1.0-{}", hash) 66 | ); 67 | } 68 | 69 | #[test] 70 | fn semantic() { 71 | assert_eq!(version_semantic!(), "0.1.0"); 72 | } 73 | 74 | #[test] 75 | fn git() { 76 | let hash = ii_scm_macros::git_hash!(object = "HEAD", length = 8); 77 | assert_eq!(hash.len(), 8); 78 | assert_eq!(version_git!(), hash); 79 | } 80 | 81 | #[test] 82 | fn git_args() { 83 | let hash = ii_scm_macros::git_hash!(object = "HEAD", length = 3); 84 | assert_eq!(hash.len(), 3); 85 | assert_eq!(version_git!(object = "HEAD", length = 3), hash); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /stratum-proxy/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Simple proxy that translates V2 protocol from clients to V1 protocol and connects to a 24 | //! requested pool 25 | 26 | use anyhow::{Context, Result}; 27 | use structopt::StructOpt; 28 | 29 | use ii_async_utils::HaltHandle; 30 | use ii_logging::macros::*; 31 | use ii_scm::global::Version; 32 | use ii_stratum_proxy::{ 33 | frontend::{Args, Config}, 34 | server::{self, controller::LoggingController, ProxyProtocolConfig}, 35 | }; 36 | 37 | #[tokio::main] 38 | async fn main() -> Result<()> { 39 | Version::set("StratumProxy", ii_scm::version_full!().as_str()); 40 | ii_async_utils::setup_panic_handling(); 41 | 42 | let _logging_controller = LoggingController::new(None); 43 | 44 | let args = Args::from_args(); 45 | 46 | let config_file_string = tokio::fs::read_to_string(args.config_file) 47 | .await 48 | .context("Proxy configuration file couldn't be read.")?; 49 | let config = toml::from_str::(config_file_string.as_str())?; 50 | info!("Starting {}: {}", Version::signature(), Version::full(),); 51 | info!("Config: {:#?}", config); 52 | 53 | let server = server::ProxyServer::listen( 54 | config.listen_address.clone(), 55 | config.upstream_address.clone(), 56 | server::TranslationHandler::new(None), 57 | config.read_security_context().await?, 58 | config 59 | .proxy_protocol_config 60 | .unwrap_or_else(ProxyProtocolConfig::default), 61 | None, 62 | ) 63 | .await 64 | .context("Cannot bind the server")?; 65 | 66 | let halt_handle = HaltHandle::arc(); 67 | halt_handle.spawn_object(server); 68 | halt_handle.ready(); 69 | halt_handle.halt_on_signal(); 70 | halt_handle 71 | .join(Some(std::time::Duration::from_secs(5))) 72 | .await 73 | .map_err(Into::into) 74 | } 75 | -------------------------------------------------------------------------------- /noise-proxy/src/connector.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Implementation of noise initiator 24 | 25 | use ii_stratum::v2; 26 | use ii_stratum::v2::noise::{auth, negotiation, CompoundCodec}; 27 | use tokio::net::TcpStream; 28 | use tokio_util::codec::{Decoder, Encoder, Framed}; 29 | 30 | #[derive(thiserror::Error, Debug)] 31 | #[error("Failed to construct connector: {0}")] 32 | pub struct Error(#[from] ii_stratum::error::Error); 33 | 34 | /// Struct that is able to initiate noise encrypted connection to upstream 35 | /// using some provided L2 codec (stratum v1 or stratum v2) 36 | pub struct Connector { 37 | /// Upstream authority public key that will be used to authenticate the endpoint 38 | upstream_authority_public_key: v2::noise::AuthorityPublicKey, 39 | } 40 | 41 | impl Connector { 42 | pub fn with_key(key: auth::EncodedEd25519PublicKey) -> Self { 43 | Self { 44 | upstream_authority_public_key: key.into_inner(), 45 | } 46 | } 47 | 48 | /// Build framed tcp stream using l2-codec `C` producing frames `F` 49 | pub async fn connect( 50 | self, 51 | connection: TcpStream, 52 | ) -> Result>, Error> 53 | where 54 | C: Default + Decoder + Encoder, 55 | >::Error: Into, 56 | { 57 | let noise_initiator = ii_stratum::v2::noise::Initiator::new( 58 | self.upstream_authority_public_key, 59 | vec![negotiation::EncryptionAlgorithm::AESGCM], 60 | ); 61 | trace!( 62 | "Stratum V2 noise connector: {:?}, {:?}", 63 | connection, 64 | noise_initiator 65 | ); 66 | noise_initiator 67 | .connect_with_codec(connection, |noise_codec| { 68 | CompoundCodec::::new(Some(noise_codec)) 69 | }) 70 | .await 71 | .map_err(Into::into) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /protocols/stratum/examples/initiator.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv4Addr; 2 | 3 | use futures::SinkExt; 4 | use ii_stratum::v2::noise::{ 5 | auth::{SignedPart, SignedPartHeader}, 6 | negotiation::EncryptionAlgorithm, 7 | CompoundCodec, Initiator, StaticKeypair, 8 | }; 9 | use tokio::net::TcpStream; 10 | 11 | // TODO consolidate code between initiator.rs and responder.rs 12 | 13 | const TEST_MESSAGE: &str = "Some short test message"; 14 | const TEST_SERVER_SOCKET: (Ipv4Addr, u16) = (Ipv4Addr::new(127, 0, 0, 1), 13225); 15 | 16 | fn build_deterministic_signed_part_and_auth() -> ( 17 | SignedPart, 18 | ed25519_dalek::Keypair, 19 | StaticKeypair, 20 | ed25519_dalek::Signature, 21 | ) { 22 | let ca_keypair_bytes = [ 23 | 228, 230, 186, 46, 141, 75, 176, 50, 58, 88, 5, 122, 144, 27, 124, 162, 103, 98, 75, 204, 24 | 205, 238, 48, 242, 170, 21, 38, 183, 32, 199, 88, 251, 48, 45, 168, 81, 159, 57, 81, 233, 25 | 0, 127, 137, 160, 19, 132, 253, 60, 188, 136, 48, 64, 180, 215, 118, 149, 61, 223, 246, 26 | 125, 215, 76, 73, 28, 27 | ]; 28 | let server_static_pub = [ 29 | 21, 50, 22, 157, 231, 160, 237, 11, 91, 131, 166, 162, 185, 55, 24, 125, 138, 176, 99, 166, 30 | 20, 161, 157, 57, 177, 241, 215, 0, 51, 13, 150, 31, 31 | ]; 32 | let server_static_priv = [ 33 | 83, 75, 77, 152, 164, 249, 65, 65, 239, 36, 159, 145, 250, 29, 58, 215, 250, 9, 55, 243, 34 | 134, 157, 198, 189, 182, 21, 182, 36, 34, 4, 125, 122, 35 | ]; 36 | 37 | let static_server_keypair = snow::Keypair { 38 | public: server_static_pub.to_vec(), 39 | private: server_static_priv.to_vec(), 40 | }; 41 | let ca_keypair = ed25519_dalek::Keypair::from_bytes(&ca_keypair_bytes) 42 | .expect("BUG: Failed to construct key_pair"); 43 | let signed_part = SignedPart::new( 44 | SignedPartHeader::new(0, u32::MAX), 45 | static_server_keypair.public.clone(), 46 | ca_keypair.public, 47 | ); 48 | let signature = signed_part 49 | .sign_with(&ca_keypair) 50 | .expect("BUG: Failed to sign certificate"); 51 | (signed_part, ca_keypair, static_server_keypair, signature) 52 | } 53 | 54 | #[tokio::main] 55 | async fn main() -> anyhow::Result<()> { 56 | let mut g_cfg = ii_logging::LoggingConfig::for_app(10); 57 | g_cfg.level = ii_logging::Level::Trace; 58 | let _g = ii_logging::setup(g_cfg); 59 | 60 | // Prepare test certificate and a serialized noise message that contains the signature 61 | let (_, authority_keys, _, _) = build_deterministic_signed_part_and_auth(); 62 | let stream = TcpStream::connect(TEST_SERVER_SOCKET).await?; 63 | let initiator = Initiator::new( 64 | authority_keys.public, 65 | vec![EncryptionAlgorithm::ChaChaPoly, EncryptionAlgorithm::AESGCM], 66 | ); 67 | let mut framed = initiator 68 | .connect_with_codec::(stream, |noise| { 69 | CompoundCodec::::new(Some(noise)) 70 | }) 71 | .await?; 72 | framed.send(TEST_MESSAGE).await?; 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v2/types.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | """Protocol specific types""" 24 | import enum 25 | 26 | 27 | class DeviceInfo: 28 | pass 29 | 30 | 31 | class ProtocolType: 32 | MINING_PROTOCOL = 0 33 | JOB_NEGOTIATION_PROTOCOL = 1 34 | TEMPLATE_DISTRIBUTION_PROTOCOL = 2 35 | JOB_DISTRIBUTION_PROTOCOL = 3 36 | 37 | 38 | class MiningChannelType(enum.Enum): 39 | """Stratum V1 mining session follows the state machine below.""" 40 | 41 | # Header only mining/standard 42 | STANDARD = 0 43 | EXTENDED = 1 44 | 45 | 46 | class DownstreamConnectionFlags(enum.Enum): 47 | """Flags provided by downstream node""" 48 | 49 | #: The downstream node requires standard jobs. It doesn’t understand group channels - it is unable to process 50 | #: extended jobs sent to standard channels thru a group channel. 51 | REQUIRES_STANDARD_JOBS = 0 52 | 53 | #: If set, the client notifies the server that it will send SetCustomMiningJob on this connection 54 | REQUIRES_WORK_SELECTION = 1 55 | 56 | #: The client requires version rolling for efficiency or correct operation and the server MUST NOT send jobs 57 | #: which do not allow version rolling. 58 | REQUIRES_VERSION_ROLLING = 2 59 | 60 | 61 | class UpstreamConnectionFlags(enum.Enum): 62 | """Flags provided by upstream node""" 63 | 64 | #: Upstream node will not accept any changes to the version field. Note that if REQUIRES_VERSION_ROLLING was set 65 | #: in the SetupConnection::flags field, this bit MUST NOT be set. Further, if this bit is set, extended jobs MUST 66 | #: NOT indicate support for version rolling. 67 | REQUIRES_FIXED_VERSION = 0 68 | 69 | #: Upstream node will not accept opening of a standard channel. 70 | REQUIRES_EXTENDED_CHANNELS = 1 71 | 72 | 73 | class Hash: 74 | """Hash value doesn't need specific representation within the simulation""" 75 | 76 | pass 77 | 78 | 79 | class MerklePath: 80 | """Merkle path doesn't need specific representation within the simulation""" 81 | 82 | pass 83 | 84 | 85 | class CoinBasePrefix: 86 | pass 87 | 88 | 89 | class CoinBaseSuffix: 90 | pass 91 | -------------------------------------------------------------------------------- /stratum-proxy/src/server/peer_address.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Module contains primitives for deeper peer information tracking 24 | 25 | use ii_wire::proxy::ProxyInfo; 26 | use std::{fmt, net::SocketAddr}; 27 | 28 | /// Downstream peer representation as a direct peer address with optional original peer address 29 | /// known, for example from PROXY protocol. 30 | #[derive(Copy, Clone, Debug)] 31 | pub struct DownstreamPeer { 32 | pub direct_peer: SocketAddr, 33 | /// Track additional information about the peer 34 | pub proxy_info: ii_wire::proxy::ProxyInfo, 35 | } 36 | 37 | impl DownstreamPeer { 38 | pub fn new(direct_peer: SocketAddr) -> Self { 39 | Self { 40 | direct_peer, 41 | proxy_info: Default::default(), 42 | } 43 | } 44 | 45 | pub fn set_proxy_info(&mut self, proxy_info: ProxyInfo) { 46 | self.proxy_info = proxy_info; 47 | } 48 | } 49 | 50 | impl fmt::Display for DownstreamPeer { 51 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 | write!( 53 | f, 54 | "{}({})", 55 | self.direct_peer.to_string(), 56 | self.proxy_info.to_string(), 57 | ) 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::DownstreamPeer; 64 | use ii_wire::proxy::ProxyInfo; 65 | use std::convert::TryFrom; 66 | use std::net::{IpAddr, SocketAddr}; 67 | 68 | #[test] 69 | fn correct_downstream_peer_format() { 70 | let src = SocketAddr::new(IpAddr::from([4, 5, 6, 7]), 4567); 71 | let dst = SocketAddr::new(IpAddr::from([1, 2, 3, 4]), 1234); 72 | let proxy_info = 73 | ProxyInfo::try_from((Some(src), Some(dst))).expect("BUG: cannot produce proxy info"); 74 | 75 | let mut peer = DownstreamPeer::new(SocketAddr::new(IpAddr::from([5, 4, 3, 2]), 5432)); 76 | assert_eq!( 77 | format!("{}", peer), 78 | String::from("5.4.3.2:5432(ProxyInfo[SRC:N/A, DST:N/A])") 79 | ); 80 | peer.set_proxy_info(proxy_info); 81 | assert_eq!( 82 | format!("{}", peer), 83 | String::from("5.4.3.2:5432(ProxyInfo[SRC:4.5.6.7:4567, DST:1.2.3.4:1234])") 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /protocols/stratum/src/v1/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use bytes::BytesMut; 3 | 4 | use ii_unvariant::unvariant; 5 | 6 | use crate::test_utils::v1::*; 7 | use crate::v1::rpc::Rpc; 8 | 9 | /// Test traits that will be used by serded for HexBytes when converting from/to string 10 | #[test] 11 | fn hex_bytes() { 12 | let hex_bytes = HexBytes(vec![0xde, 0xad, 0xbe, 0xef, 0x11, 0x22, 0x33]); 13 | let hex_bytes_str = "deadbeef112233"; 14 | 15 | let checked_hex_bytes_str: String = hex_bytes.clone().into(); 16 | assert_eq!( 17 | hex_bytes_str, checked_hex_bytes_str, 18 | "Mismatched hex bytes strings", 19 | ); 20 | 21 | let checked_hex_bytes = 22 | HexBytes::try_from(hex_bytes_str).expect("BUG: Failed to decode hex bytes"); 23 | 24 | assert_eq!(hex_bytes, checked_hex_bytes, "Mismatched hex bytes values",) 25 | } 26 | 27 | #[test] 28 | fn hex_u32_from_string() { 29 | let hex_bytes_str = "deadbeef"; 30 | let hex_bytes_str_0x = "0xdeadbeef"; 31 | 32 | let le_from_plain = HexU32Le::try_from(hex_bytes_str).expect("BUG: parsing hex string failed"); 33 | let le_from_prefixed = 34 | HexU32Le::try_from(hex_bytes_str_0x).expect("BUG: parsing hex string failed"); 35 | let ref_le = HexU32Le(0xefbeadde); 36 | let be_from_plain = HexU32Be::try_from(hex_bytes_str).expect("BUG: parsing hex string failed"); 37 | let be_from_prefixed = 38 | HexU32Be::try_from(hex_bytes_str_0x).expect("BUG: parsing hex string failed"); 39 | let ref_be = HexU32Be(0xdeadbeef); 40 | assert_eq!(le_from_plain, ref_le); 41 | assert_eq!(le_from_prefixed, ref_le); 42 | assert_eq!(be_from_plain, ref_be); 43 | assert_eq!(be_from_prefixed, ref_be); 44 | } 45 | 46 | #[test] 47 | fn extra_nonce1() { 48 | let expected_enonce1 = ExtraNonce1(HexBytes(vec![0xde, 0xad, 0xbe, 0xef, 0x11, 0x22, 0x33])); 49 | let expected_enonce1_str = r#""deadbeef112233""#; 50 | 51 | let checked_enonce1_str: String = 52 | serde_json::to_string(&expected_enonce1).expect("BUG: Serialization failed"); 53 | assert_eq!( 54 | expected_enonce1_str, checked_enonce1_str, 55 | "Mismatched extranonce 1 strings", 56 | ); 57 | 58 | let checked_enonce1 = 59 | serde_json::from_str(expected_enonce1_str).expect("BUG: Deserialization failed"); 60 | 61 | assert_eq!( 62 | expected_enonce1, checked_enonce1, 63 | "Mismatched extranonce 1 values", 64 | ) 65 | } 66 | 67 | /// This test demonstrates an actual implementation of protocol handler for a set of 68 | /// messsages 69 | #[tokio::test] 70 | async fn message_from_frame() { 71 | for &req in V1_TEST_REQUESTS { 72 | let msg_rpc = Rpc::try_from(Frame::from_serialized_payload(BytesMut::from(req))) 73 | .expect("BUG: Deserialization failed"); 74 | let mut handler = TestIdentityHandler; 75 | handler.handle_v1(msg_rpc).await; 76 | } 77 | } 78 | 79 | #[tokio::test] 80 | async fn deserialize_response_message() { 81 | let fr = Frame::from_serialized_payload(BytesMut::from(MINING_SUBSCRIBE_OK_RESULT_JSON)); 82 | let deserialized = Rpc::try_from(fr).expect("BUG: Deserialization failed"); 83 | 84 | unvariant!(try deserialized { 85 | x: rpc::StratumResult => { 86 | x.expect("BUG: Deserialization failed"); 87 | }, 88 | _x: _ => panic!("BUG: Incorrect message unvariated"), 89 | }); 90 | } 91 | 92 | // add also a separate stratum error test as per above response 93 | -------------------------------------------------------------------------------- /noise-proxy/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::path::{Path, PathBuf}; 24 | 25 | use anyhow::{Context, Result}; 26 | use clap::{App, Arg}; 27 | use futures::TryFutureExt; 28 | use ii_async_utils::HaltHandle; 29 | use ii_logging::macros::*; 30 | use ii_noise_proxy::{metrics::NoiseProxyMetrics, NoiseProxy, SecurityContext}; 31 | use ii_wire::proxy; 32 | use tokio::io::AsyncReadExt; 33 | 34 | #[derive(serde::Deserialize, Debug)] 35 | struct Configuration { 36 | listen: String, 37 | upstream: String, 38 | certificate: PathBuf, 39 | server_key: PathBuf, 40 | } 41 | 42 | #[tokio::main] 43 | async fn main() -> Result<()> { 44 | let _grd = ii_logging::setup_for_app(100); 45 | let matches = App::new("Stratum V1-to-V1 noise proxy") 46 | .arg( 47 | Arg::with_name("config") 48 | .long("conf") 49 | .short("c") 50 | .takes_value(true) 51 | .help("Configuration file path"), 52 | ) 53 | .get_matches(); 54 | 55 | let config_file = matches 56 | .value_of("config") 57 | .context("Missing configuration path")?; 58 | let cfg_string = tokio::fs::File::open(config_file) 59 | .and_then(|mut file_handle| async move { 60 | let mut cfg_str = String::new(); 61 | file_handle 62 | .read_to_string(&mut cfg_str) 63 | .await 64 | .map(|_| cfg_str) 65 | }) 66 | .await?; 67 | 68 | let config = toml::from_str::(&cfg_string)?; 69 | 70 | info!("Running V1 noise proxy: {:#?}", config); 71 | 72 | let cert_path = Path::new(&config.certificate); 73 | let key_path = Path::new(&config.server_key); 74 | let ctx = SecurityContext::read_from_file(cert_path, key_path).await?; 75 | let halt_handle = HaltHandle::arc(); 76 | let (metrics, _) = NoiseProxyMetrics::new(); 77 | let noise_proxy = NoiseProxy::new( 78 | config.listen, 79 | config.upstream, 80 | std::sync::Arc::new(ctx), 81 | proxy::ProtocolConfig::new( 82 | false, 83 | vec![proxy::ProtocolVersion::V1, proxy::ProtocolVersion::V2], 84 | ), 85 | None, 86 | metrics, 87 | ) 88 | .await?; 89 | halt_handle.spawn_object(noise_proxy); 90 | halt_handle.ready(); 91 | halt_handle.clone().halt_on_signal(); 92 | halt_handle 93 | .join(Some(std::time::Duration::from_secs(3))) 94 | .await 95 | .map_err(Into::into) 96 | } 97 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/telemetry/messages.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | #[cfg(not(feature = "v2json"))] 24 | use crate::v2::serialization; 25 | use crate::{ 26 | error::{Error, Result}, 27 | v2::{extensions, framing, types::*, Protocol}, 28 | AnyPayload, 29 | }; 30 | use serde::{Deserialize, Serialize}; 31 | use std::convert::TryFrom; 32 | 33 | use ii_unvariant::{id, Id}; 34 | 35 | /// Generates conversion for telemetry protocol messages (extension 1) 36 | macro_rules! impl_telemetry_message_conversion { 37 | ($message:tt, $is_channel_msg:expr) => { 38 | impl_message_conversion!(extensions::TELEMETRY, $message, $is_channel_msg); 39 | }; 40 | } 41 | 42 | #[id(0x00u8)] 43 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 44 | pub struct OpenTelemetryChannel { 45 | pub req_id: u32, 46 | pub dev_id: Str0_255, 47 | // TODO: consider adding this vendor field that would allow verifying that the device and 48 | // upstream node accepting the telemetry data will exchange compatible telemetry data 49 | // pub telemetry_type: u32, 50 | } 51 | 52 | #[id(0x01u8)] 53 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 54 | pub struct OpenTelemetryChannelSuccess { 55 | pub req_id: u32, 56 | pub channel_id: u32, 57 | } 58 | 59 | #[id(0x02u8)] 60 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 61 | pub struct OpenTelemetryChannelError { 62 | pub req_id: u32, 63 | pub code: Str0_32, 64 | } 65 | 66 | #[id(0x03u8)] 67 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 68 | pub struct SubmitTelemetryData { 69 | pub channel_id: u32, 70 | pub seq_num: u32, 71 | pub telemetry_payload: Bytes0_64k, 72 | } 73 | 74 | #[id(0x04u8)] 75 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 76 | pub struct SubmitTelemetryDataSuccess { 77 | pub channel_id: u32, 78 | pub last_seq_num: u32, 79 | } 80 | 81 | #[id(0x05u8)] 82 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 83 | pub struct SubmitTelemetryDataError { 84 | pub channel_id: u32, 85 | pub seq_num: u32, 86 | pub code: Str0_32, 87 | } 88 | 89 | impl_telemetry_message_conversion!(OpenTelemetryChannel, false); 90 | impl_telemetry_message_conversion!(OpenTelemetryChannelSuccess, false); 91 | impl_telemetry_message_conversion!(OpenTelemetryChannelError, false); 92 | impl_telemetry_message_conversion!(SubmitTelemetryData, false); 93 | impl_telemetry_message_conversion!(SubmitTelemetryDataSuccess, false); 94 | impl_telemetry_message_conversion!(SubmitTelemetryDataError, false); 95 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! All shared public macros for the V2 part of the stack 24 | 25 | /// Generates conversion for a specified protocol message 26 | /// `extension_id` - Identifies the protocol extension 27 | /// `message` - Message identifier (token) 28 | /// `is_channel_msg` - expected boolean value whether the message is a channel message 29 | 30 | #[macro_export] 31 | macro_rules! impl_message_conversion { 32 | ($extension_id:expr, $message:tt, $is_channel_msg:expr) => { 33 | // NOTE: $message and $handler_fn need to be tt because of https://github.com/dtolnay/async-trait/issues/46 34 | 35 | impl TryFrom<$message> for framing::Frame { 36 | type Error = Error; 37 | 38 | /// Prepares a frame for serializing the specified message just in time (the message 39 | /// is treated as a `SerializablePayload`) 40 | fn try_from(m: $message) -> Result { 41 | Ok(framing::Frame::from_serializable_payload( 42 | $is_channel_msg, 43 | $extension_id, 44 | $message::ID as framing::MsgType, 45 | m, 46 | )) 47 | } 48 | } 49 | 50 | impl TryFrom<&[u8]> for $message { 51 | type Error = Error; 52 | 53 | fn try_from(msg: &[u8]) -> Result { 54 | serialization::from_slice(msg).map_err(Into::into) 55 | } 56 | } 57 | 58 | impl TryFrom for $message { 59 | type Error = Error; 60 | 61 | fn try_from(frame: framing::Frame) -> Result { 62 | let (_header, payload) = frame.split(); 63 | let payload = payload.into_bytes_mut()?; 64 | Self::try_from(&payload[..]) 65 | } 66 | } 67 | 68 | impl TryFrom for Box<$message> { 69 | type Error = Error; 70 | 71 | fn try_from(frame: framing::Frame) -> Result> { 72 | $message::try_from(frame).map(Box::new) 73 | } 74 | } 75 | 76 | impl Id for Box<$message> { 77 | const ID: u8 = $message::ID; 78 | } 79 | 80 | /// Each message is a `AnyPayload/SerializablePayload` object that can be serialized into 81 | /// `writer` 82 | impl AnyPayload for $message { 83 | fn serialize_to_writer(&self, writer: &mut dyn std::io::Write) -> Result<()> { 84 | serialization::to_writer(writer, self).map_err(Into::into) 85 | } 86 | } 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Stratum version 2 top level module 24 | pub mod error; 25 | pub mod framing; 26 | #[macro_use] 27 | pub mod macros; 28 | pub mod extensions; 29 | pub mod messages; 30 | pub mod noise; 31 | pub mod serialization; 32 | pub mod telemetry; 33 | pub mod types; 34 | 35 | use tokio::net::TcpStream; 36 | 37 | use futures::prelude::*; 38 | 39 | pub use self::framing::codec::Codec; 40 | pub use self::framing::{Frame, Framing}; 41 | 42 | /// Tcp stream that produces/consumes V2 frames 43 | pub type Framed = tokio_util::codec::Framed>; 44 | 45 | pub trait FramedSink: 46 | Sink<::Tx, Error = ::Error> 47 | + std::marker::Unpin 48 | + std::fmt::Debug 49 | + 'static 50 | { 51 | } 52 | 53 | impl FramedSink for T where 54 | T: Sink<::Tx, Error = ::Error> 55 | + std::marker::Unpin 56 | + std::fmt::Debug 57 | + 'static 58 | { 59 | } 60 | 61 | /// Helper type for outgoing V2 frames when run time support for multiple sink types (e.g. 62 | /// TcpStream, mpsc::Sender etc.) is needed 63 | pub type DynFramedSink = std::pin::Pin< 64 | Box< 65 | dyn Sink<::Tx, Error = ::Error> 66 | + Send 67 | + std::marker::Unpin, 68 | >, 69 | >; 70 | 71 | pub trait FramedStream: 72 | Stream< 73 | Item = std::result::Result< 74 | ::Tx, 75 | ::Error, 76 | >, 77 | > + std::marker::Unpin 78 | + 'static 79 | { 80 | } 81 | 82 | impl FramedStream for T where 83 | T: Stream< 84 | Item = std::result::Result< 85 | ::Tx, 86 | ::Error, 87 | >, 88 | > + std::marker::Unpin 89 | + 'static 90 | { 91 | } 92 | 93 | /// Helper type for incoming V2 frames when run time support for multiple sources (e.g. 94 | /// TcpStream, mpsc::Receiver etc.) is needed 95 | pub type DynFramedStream = std::pin::Pin< 96 | Box< 97 | dyn Stream< 98 | Item = std::result::Result< 99 | ::Rx, 100 | ::Error, 101 | >, 102 | > + Send, 103 | >, 104 | >; 105 | 106 | /// Protocol associates a custom handler with it 107 | pub struct Protocol; 108 | impl crate::Protocol for Protocol { 109 | type Header = framing::Header; 110 | } 111 | 112 | #[cfg(test)] 113 | mod test; 114 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/tests/common/str_frame.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::convert::TryFrom; 24 | 25 | use ii_unvariant::{id, GetId, /* HandleFuture, */ Id}; 26 | 27 | /// Similar to `Frame` but contains a string reference 28 | /// and has string reference ID type. 29 | /// This type is for testing of both 30 | /// a) passing a variant by reference, and 31 | /// b) the variant having a lifetime generic argument. 32 | pub struct StrFrame<'a>(pub &'a str, pub bool); 33 | 34 | /// When passing variants by reference, we need to implement 35 | /// `GetId` on the reference type instead: 36 | impl<'a> GetId for StrFrame<'a> { 37 | type Id = &'a str; 38 | 39 | fn get_id(&self) -> &'a str { 40 | self.0.as_ref() 41 | } 42 | } 43 | 44 | #[id("foo")] 45 | pub struct StrFoo; 46 | 47 | impl<'a, 'b> From<&'a StrFrame<'b>> for StrFoo { 48 | fn from(frame: &'a StrFrame<'b>) -> Self { 49 | assert_eq!(frame.get_id(), Self::ID); 50 | StrFoo 51 | } 52 | } 53 | impl<'a> From<&'a str> for StrFoo { 54 | fn from(frame: &'a str) -> Self { 55 | assert_eq!(frame.get_id(), Self::ID); 56 | StrFoo 57 | } 58 | } 59 | 60 | #[id("bar")] 61 | pub struct StrBar; 62 | 63 | impl<'a, 'b> From<&'a StrFrame<'b>> for StrBar { 64 | fn from(frame: &'a StrFrame<'b>) -> Self { 65 | assert_eq!(frame.get_id(), Self::ID); 66 | StrBar 67 | } 68 | } 69 | impl<'a> From<&'a str> for StrBar { 70 | fn from(frame: &'a str) -> Self { 71 | assert_eq!(frame.get_id(), Self::ID); 72 | StrBar 73 | } 74 | } 75 | 76 | #[derive(PartialEq, Debug)] 77 | pub struct TryStrFrameError; 78 | 79 | impl From for bool { 80 | fn from(_: TryStrFrameError) -> Self { 81 | false 82 | } 83 | } 84 | 85 | #[id("foo")] 86 | pub struct TryStrFoo; 87 | 88 | impl<'a, 'b> TryFrom<&'a StrFrame<'b>> for TryStrFoo { 89 | type Error = TryStrFrameError; 90 | 91 | fn try_from(frame: &'a StrFrame<'b>) -> Result { 92 | assert_eq!(frame.get_id(), Self::ID); 93 | if frame.1 { 94 | Ok(TryStrFoo) 95 | } else { 96 | Err(TryStrFrameError) 97 | } 98 | } 99 | } 100 | 101 | #[id("bar")] 102 | pub struct TryStrBar; 103 | 104 | impl<'a, 'b> TryFrom<&'a StrFrame<'b>> for TryStrBar { 105 | type Error = TryStrFrameError; 106 | 107 | fn try_from(frame: &'a StrFrame<'b>) -> Result { 108 | assert_eq!(frame.get_id(), Self::ID); 109 | if frame.1 { 110 | Ok(TryStrBar) 111 | } else { 112 | Err(TryStrFrameError) 113 | } 114 | } 115 | } 116 | 117 | pub trait TryStrFrameHandler { 118 | fn handle<'a, 'b>(&mut self, variant: &'a StrFrame<'b>) -> Result<(), bool>; 119 | } 120 | -------------------------------------------------------------------------------- /protocols/stratum/examples/responder.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv4Addr; 2 | 3 | use bytes::Bytes; 4 | use futures::StreamExt; 5 | use ii_stratum::v2::noise::{ 6 | auth, 7 | auth::{SignedPart, SignedPartHeader}, 8 | negotiation::EncryptionAlgorithm, 9 | CompoundCodec, Responder, StaticKeypair, 10 | }; 11 | use tokio::net::TcpListener; 12 | 13 | // TODO consolidate code between initiator.rs and responder.rs 14 | 15 | const TEST_MESSAGE: &str = "Some short test message"; 16 | const TEST_SERVER_SOCKET: (Ipv4Addr, u16) = (Ipv4Addr::new(127, 0, 0, 1), 13225); 17 | 18 | fn build_deterministic_signed_part_and_auth() -> ( 19 | SignedPart, 20 | ed25519_dalek::Keypair, 21 | StaticKeypair, 22 | ed25519_dalek::Signature, 23 | ) { 24 | let ca_keypair_bytes = [ 25 | 228, 230, 186, 46, 141, 75, 176, 50, 58, 88, 5, 122, 144, 27, 124, 162, 103, 98, 75, 204, 26 | 205, 238, 48, 242, 170, 21, 38, 183, 32, 199, 88, 251, 48, 45, 168, 81, 159, 57, 81, 233, 27 | 0, 127, 137, 160, 19, 132, 253, 60, 188, 136, 48, 64, 180, 215, 118, 149, 61, 223, 246, 28 | 125, 215, 76, 73, 28, 29 | ]; 30 | let server_static_pub = [ 31 | 21, 50, 22, 157, 231, 160, 237, 11, 91, 131, 166, 162, 185, 55, 24, 125, 138, 176, 99, 166, 32 | 20, 161, 157, 57, 177, 241, 215, 0, 51, 13, 150, 31, 33 | ]; 34 | let server_static_priv = [ 35 | 83, 75, 77, 152, 164, 249, 65, 65, 239, 36, 159, 145, 250, 29, 58, 215, 250, 9, 55, 243, 36 | 134, 157, 198, 189, 182, 21, 182, 36, 34, 4, 125, 122, 37 | ]; 38 | 39 | let static_server_keypair = snow::Keypair { 40 | public: server_static_pub.to_vec(), 41 | private: server_static_priv.to_vec(), 42 | }; 43 | let ca_keypair = ed25519_dalek::Keypair::from_bytes(&ca_keypair_bytes) 44 | .expect("BUG: Failed to construct key_pair"); 45 | let signed_part = SignedPart::new( 46 | SignedPartHeader::new(0, u32::MAX), 47 | static_server_keypair.public.clone(), 48 | ca_keypair.public, 49 | ); 50 | let signature = signed_part 51 | .sign_with(&ca_keypair) 52 | .expect("BUG: Failed to sign certificate"); 53 | (signed_part, ca_keypair, static_server_keypair, signature) 54 | } 55 | 56 | fn build_signed_message(signed_part: SignedPart, signature: ed25519_dalek::Signature) -> Bytes { 57 | let certificate = auth::Certificate::new(signed_part, signature); 58 | certificate 59 | .build_noise_message() 60 | .serialize_to_bytes_mut() 61 | .expect("BUG: Cannot serialize signature noise message") 62 | .freeze() 63 | } 64 | 65 | #[tokio::main] 66 | async fn main() -> anyhow::Result<()> { 67 | let mut g_cfg = ii_logging::LoggingConfig::for_app(10); 68 | g_cfg.level = ii_logging::Level::Trace; 69 | let _g = ii_logging::setup(g_cfg); 70 | // Prepare test certificate and a serialized noise message that contains the signature 71 | let (signed_part, _, static_keypair, sig) = build_deterministic_signed_part_and_auth(); 72 | let signature_noise_message = build_signed_message(signed_part, sig); 73 | let tcp_listener = TcpListener::bind(TEST_SERVER_SOCKET).await?; 74 | let responder = Responder::new( 75 | &static_keypair, 76 | signature_noise_message, 77 | vec![EncryptionAlgorithm::ChaChaPoly, EncryptionAlgorithm::AESGCM], 78 | ); 79 | let (downstream, _) = tcp_listener.accept().await?; 80 | let mut framed = responder 81 | .accept_with_codec::(downstream, |noise| { 82 | CompoundCodec::::new(Some(noise)) 83 | }) 84 | .await?; 85 | 86 | let msg = framed 87 | .next() 88 | .await 89 | .expect("BUG: Failed to receive test message")?; 90 | 91 | if TEST_MESSAGE == msg { 92 | Ok(()) 93 | } else { 94 | anyhow::bail!( 95 | "BUG: Expected ({:x?}) and decoded ({:x?}) messages don't match", 96 | TEST_MESSAGE, 97 | msg 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v1/messages.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | """Stratum V1 messages.""" 24 | from sim_primitives.protocol import Message 25 | 26 | 27 | class Configure(Message): 28 | def __init__(self, req_id, extensions, extension_params): 29 | self.extensions = extensions 30 | self.extension_params = extension_params 31 | super().__init__(req_id) 32 | 33 | 34 | class ConfigureResponse(Message): 35 | def __init__(self, req_id, extensions: list, extension_params: dict): 36 | self.extensions = extensions 37 | self.extension_params = extension_params 38 | super().__init__(req_id) 39 | 40 | 41 | class Authorize(Message): 42 | def __init__(self, req_id, user_name, password): 43 | self.user_name = user_name 44 | self.password = password 45 | super().__init__(req_id) 46 | 47 | 48 | class Subscribe(Message): 49 | def __init__(self, req_id, signature, extranonce1, url): 50 | self.signature = signature 51 | self.extranonce1 = extranonce1 52 | self.url = url 53 | super().__init__(req_id) 54 | 55 | 56 | class SubscribeResponse(Message): 57 | def __init__(self, req_id, subscription_ids, extranonce1, extranonce2_size): 58 | self.subscription_ids = subscription_ids 59 | self.extranonce1 = extranonce1 60 | self.extranonce2_size = extranonce2_size 61 | super().__init__(req_id) 62 | 63 | 64 | class SetDifficulty(Message): 65 | def __init__(self, diff): 66 | self.diff = diff 67 | super().__init__() 68 | 69 | 70 | class Submit(Message): 71 | def __init__(self, req_id, user_name, job_id, extranonce2, time, nonce): 72 | self.user_name = user_name 73 | self.job_id = job_id 74 | self.extranonce2 = extranonce2 75 | self.time = time 76 | self.nonce = nonce 77 | super().__init__(req_id) 78 | 79 | 80 | class Notify(Message): 81 | def __init__( 82 | self, 83 | job_id, 84 | prev_hash, 85 | coin_base_1, 86 | coin_base_2, 87 | merkle_branch, 88 | version, 89 | bits, 90 | time, 91 | clean_jobs, 92 | ): 93 | self.job_id = job_id 94 | self.prev_hash = prev_hash 95 | self.coin_base_1 = coin_base_1 96 | self.coin_base_2 = coin_base_2 97 | self.merkle_branch = merkle_branch 98 | self.version = version 99 | self.bits = bits 100 | self.time = time 101 | self.clean_jobs = clean_jobs 102 | super().__init__() 103 | 104 | 105 | class Reconnect(Message): 106 | def __init__(self, hostname, port, wait_time): 107 | self.hostname = hostname 108 | self.port = port 109 | self.wait_time = wait_time 110 | super().__init__() 111 | 112 | 113 | class OkResult(Message): 114 | pass 115 | 116 | 117 | class ErrorResult(Message): 118 | def __init__(self, req_id, code, msg): 119 | self.code = code 120 | self.msg = msg 121 | super().__init__(req_id) 122 | -------------------------------------------------------------------------------- /stratum-proxy/src/frontend.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use serde::Deserialize; 24 | use std::path::PathBuf; 25 | use std::sync::Arc; 26 | use structopt::StructOpt; 27 | 28 | use ii_noise_proxy::SecurityContext; 29 | use ii_scm::global::Version; 30 | use ii_wire::Address; 31 | 32 | use crate::error::{Error, Result}; 33 | use crate::server::ProxyProtocolConfig; 34 | 35 | #[derive(Debug, StructOpt)] 36 | #[structopt(name = Version::signature().as_str(), version = Version::full().as_str())] 37 | pub struct Args { 38 | #[structopt(short = "c", long = "conf", help("Path to configuration file"))] 39 | pub config_file: PathBuf, 40 | } 41 | 42 | // TODO: Write Deserizlize manually in order to report errors and validate config more properly 43 | // if only one of the certificate or server-private-key config line is present, serde treates it as 44 | // if none were present and doesn't return error 45 | #[derive(Debug, Deserialize)] 46 | #[serde(deny_unknown_fields)] 47 | pub struct Config { 48 | pub listen_address: Address, 49 | pub upstream_address: Address, 50 | #[serde(default)] // Default for bool is "false" 51 | pub insecure: bool, 52 | #[serde(flatten)] 53 | pub key_and_cert_files: Option, 54 | pub proxy_protocol_config: Option, 55 | } 56 | 57 | #[derive(Debug, Deserialize)] 58 | pub struct KeyAndCertFiles { 59 | certificate_file: PathBuf, 60 | secret_key_file: PathBuf, 61 | } 62 | 63 | impl Default for Config { 64 | fn default() -> Self { 65 | Self { 66 | listen_address: Address("0.0.0.0".to_owned(), 3336), 67 | upstream_address: Address("stratum.slushpool.com".to_owned(), 3333), 68 | insecure: true, 69 | key_and_cert_files: None, 70 | proxy_protocol_config: None, 71 | } 72 | } 73 | } 74 | 75 | impl Config { 76 | /// Read certificates for current configuration and return: 77 | /// - `None` if file path configurations are missing and/or `insecure == true` option 78 | /// - SecurityContext `Some(SecurityContext)` if files are valid and `insecure == false` 79 | /// - `Error` otherwise 80 | pub async fn read_security_context(&self) -> Result>> { 81 | if self.insecure { 82 | Ok(None) 83 | } else if let Some(key_and_cert_files) = self.key_and_cert_files.as_ref() { 84 | let ctx_result = SecurityContext::read_from_file( 85 | key_and_cert_files.certificate_file.as_path(), 86 | key_and_cert_files.secret_key_file.as_path(), 87 | ) 88 | .await 89 | .map_err(|e| Error::InvalidFile(format!("Failed to read certificate and key: {}", e))) 90 | .map(Arc::new); 91 | if let Ok(ctx) = ctx_result.as_ref() { 92 | ctx.validate_by_time(std::time::SystemTime::now)?; 93 | } 94 | ctx_result.map(Some) 95 | } else { 96 | Err(Error::InvalidFile( 97 | "Certificate and key files are missing".to_owned(), 98 | )) 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/network.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | import random 24 | from abc import ABC, abstractmethod 25 | 26 | import numpy as np 27 | import simpy 28 | from hashids import Hashids 29 | 30 | 31 | def gen_uid(env): 32 | hashids = Hashids() 33 | return hashids.encode(int(env.now * 16), random.randint(0, 16777216)) 34 | 35 | 36 | class AcceptingConnection(ABC): 37 | @abstractmethod 38 | def connect_in(self, connection): 39 | pass 40 | 41 | @abstractmethod 42 | def disconnect(self, connection): 43 | pass 44 | 45 | 46 | class ConnectionStore: 47 | """This class represents the propagation network connection.""" 48 | 49 | def __init__(self, env, mean_latency, latency_stddev_percent): 50 | self.env = env 51 | self.mean_latency = mean_latency 52 | self.latency_stddev = 0.01 * latency_stddev_percent * mean_latency 53 | self.store = simpy.Store(env) 54 | 55 | def latency(self): 56 | if self.latency_stddev < 0.00001: 57 | delay = self.mean_latency 58 | else: 59 | delay = np.random.normal(self.mean_latency, self.latency_stddev) 60 | yield self.env.timeout(delay) 61 | 62 | def put(self, value): 63 | self.store.put(value) 64 | 65 | def get(self): 66 | value = yield self.store.get() 67 | yield self.env.process(self.latency()) 68 | return value 69 | 70 | 71 | class Connection: 72 | def __init__(self, env, port: str, mean_latency=0.01, latency_stddev_percent=10): 73 | self.uid = gen_uid(env) 74 | self.env = env 75 | self.port = port 76 | self.mean_latency = mean_latency 77 | self.latency_stddev_percent = latency_stddev_percent 78 | # Connection directions are from client prospective 79 | # Outgoing - client will store messages into the outgoing store, 80 | # while server will pickup the messages from the outgoing store 81 | self.outgoing = ConnectionStore(env, mean_latency, latency_stddev_percent) 82 | # Incoming - vice versa 83 | self.incoming = ConnectionStore(env, mean_latency, latency_stddev_percent) 84 | self.conn_target = None 85 | 86 | def connect_to(self, conn_target): 87 | conn_target.connect_in(self) 88 | self.conn_target = conn_target 89 | 90 | def disconnect(self): 91 | # TODO: Review whether to use assert's or RuntimeErrors in simulation 92 | if self.conn_target is None: 93 | raise RuntimeError('Not connected') 94 | self.conn_target.disconnect(self) 95 | self.conn_target = None 96 | 97 | def is_connected(self): 98 | return self.conn_target is not None 99 | 100 | 101 | class ConnectionFactory: 102 | def __init__(self, env, port: str, mean_latency=0.01, latency_stddev_percent=10): 103 | self.env = env 104 | self.port = port 105 | self.mean_latency = mean_latency 106 | self.latency_stddev_percent = latency_stddev_percent 107 | 108 | def create_connection(self): 109 | return Connection( 110 | self.env, 111 | self.port, 112 | mean_latency=self.mean_latency, 113 | latency_stddev_percent=self.latency_stddev_percent, 114 | ) 115 | -------------------------------------------------------------------------------- /protocols/stratum/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Module that represents stratum protocol errors 24 | 25 | use std::{self, fmt, io}; 26 | use thiserror::Error; 27 | 28 | #[derive(Error, Debug)] //TODO: We lost Clone PartialEq and Eq, is this important? 29 | pub enum Error { 30 | /// Input/Output error. 31 | #[error("I/O error: {0}")] 32 | Io(#[from] io::Error), 33 | 34 | #[error("Handshake error: {0}")] 35 | Handshake(String), 36 | 37 | /// Line Codec error. 38 | #[error("Lines Codec error: {0}")] 39 | LinesCodec(#[from] tokio_util::codec::LinesCodecError), 40 | 41 | /// Errors emitted by serde 42 | #[error("Serde JSON: {0}")] 43 | Serde(#[from] serde_json::error::Error), 44 | 45 | /// General error used for more specific . 46 | #[error("General error: {0}")] 47 | General(String), 48 | 49 | /// Unexpected version of something. 50 | #[error("Unexpected {0} version: {1}, expected: {2}")] 51 | UnexpectedVersion(String, String, String), 52 | 53 | #[error("Noise handshake error: {0}")] 54 | Noise(String), 55 | 56 | #[error("Noise protocol error: {0}")] 57 | NoiseProtocol(#[from] snow::error::Error), 58 | 59 | #[error("Noise signature error: {0}")] 60 | NoiseSignature(#[from] ed25519_dalek::SignatureError), 61 | 62 | #[error("Noise base58 error: {0}")] 63 | NoiseEncoding(#[from] bs58::decode::Error), 64 | 65 | /// Stratum version 1 error 66 | #[error("V1 error: {0}")] 67 | V1(#[from] super::v1::error::Error), 68 | 69 | /// Stratum version 2 error 70 | #[error("V2 error: {0}")] 71 | V2(#[from] super::v2::error::Error), 72 | 73 | /// Stratum version 2 serialization error 74 | #[error("V2 serialization error: {0}")] 75 | V2Serialization(#[from] super::v2::serialization::Error), 76 | 77 | /// Hex Decode error 78 | #[error("Hex value decoding error: {0}")] 79 | HexDecode(#[from] hex::FromHexError), 80 | 81 | /// Invalid Bitcoin hash 82 | #[error("Invalid Bitcoin hash: {0}")] 83 | BitcoinHash(#[from] bitcoin_hashes::hex::Error), 84 | 85 | /// Timeout error 86 | #[error("Timeout error: {0}")] 87 | Timeout(#[from] tokio::time::error::Elapsed), 88 | 89 | /// Format error 90 | #[error("Format error: {0}")] 91 | Format(#[from] fmt::Error), 92 | 93 | /// Utf8 error 94 | #[error("Error decoding UTF-8 string: {0}")] 95 | Utf8(#[from] std::str::Utf8Error), 96 | } 97 | 98 | impl From<&str> for Error { 99 | fn from(info: &str) -> Self { 100 | Error::General(info.to_string()) 101 | } 102 | } 103 | 104 | impl From for Error { 105 | fn from(info: String) -> Self { 106 | Error::General(info) 107 | } 108 | } 109 | 110 | /// A specialized `Result` type bound to [`Error`]. 111 | pub type Result = std::result::Result; 112 | 113 | #[cfg(test)] 114 | mod test { 115 | use super::super::v2::error::Error as V2Error; 116 | use super::*; 117 | 118 | #[test] 119 | fn test_error_display_with_inner_error() { 120 | let inner_msg = "Usak is unknown"; 121 | let inner = V2Error::UnknownMessage(inner_msg.into()); 122 | let err = Error::V2(inner); 123 | let msg = err.to_string(); 124 | assert!(msg.contains(inner_msg)); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/framing/codec.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use bytes::BytesMut; 24 | use tokio_util::codec::length_delimited::{self, LengthDelimitedCodec}; 25 | use tokio_util::codec::{Decoder, Encoder}; 26 | 27 | use super::{Frame, Header}; 28 | use crate::error::Error; 29 | 30 | #[derive(Debug)] 31 | pub struct Codec { 32 | inner: LengthDelimitedCodec, 33 | } 34 | 35 | impl Codec { 36 | pub fn new() -> Self { 37 | // TODO: limit frame size with max_frame_length() ? 38 | // Note: LengthDelimitedCodec is a bit tricky to coerce into 39 | // including the header in the final mesasge. 40 | // .num_skip(0) tells it to not skip the header, 41 | // but then .length_adjustment() needs to be set to the header size 42 | // because normally the 'length' is the size of part after the 'length' field. 43 | Self { 44 | inner: length_delimited::Builder::new() 45 | .little_endian() 46 | .length_field_offset(Header::LEN_OFFSET) 47 | .length_field_length(Header::LEN_SIZE) 48 | .num_skip(0) 49 | // Actual header length is not counted in the length field 50 | .length_adjustment(Header::SIZE as isize) 51 | .new_codec(), 52 | } 53 | } 54 | } 55 | 56 | impl Default for Codec { 57 | fn default() -> Self { 58 | Self::new() 59 | } 60 | } 61 | 62 | impl Decoder for Codec { 63 | type Item = Frame; 64 | type Error = Error; 65 | 66 | fn decode( 67 | &mut self, 68 | src: &mut BytesMut, 69 | ) -> std::result::Result, Self::Error> { 70 | let stratum_bytes = self.inner.decode(src)?; 71 | 72 | let mut bytes = match stratum_bytes { 73 | Some(bytes) => bytes, 74 | None => return Ok(None), 75 | }; 76 | Ok(Some(Frame::deserialize(&mut bytes))) 77 | } 78 | } 79 | 80 | impl Encoder for Codec { 81 | type Error = Error; 82 | 83 | fn encode(&mut self, item: Frame, dst: &mut BytesMut) -> std::result::Result<(), Self::Error> { 84 | item.serialize(dst) 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod test { 90 | use super::*; 91 | 92 | #[test] 93 | fn test_codec_no_noise() { 94 | let mut codec = Codec::default(); 95 | let mut payload = BytesMut::new(); 96 | payload.extend_from_slice(&[1, 2, 3, 4]); 97 | let expected_frame = Frame::from_serialized_payload(false, 0, 0x16, payload.clone()); 98 | 99 | // This is currently due to the fact that Frame doesn't support cloning 100 | let expected_frame_copy = Frame::from_serialized_payload(false, 0, 0x16, payload); 101 | 102 | let mut buffer = BytesMut::new(); 103 | codec 104 | .encode(expected_frame, &mut buffer) 105 | .expect("BUG: Codec failed to encode message"); 106 | 107 | let decoded_frame = codec 108 | .decode(&mut buffer) 109 | .expect("BUG: Codec failed to decode message") 110 | .expect("BUG: No frame provided"); 111 | 112 | assert_eq!( 113 | expected_frame_copy, decoded_frame, 114 | "BUG: Expected ({:x?}) and decoded ({:x?}) frames don't match", 115 | expected_frame_copy, decoded_frame 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /protocols/stratum/src/v1/framing.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! This module defines framing of Stratum V1 messages 24 | 25 | pub mod codec; 26 | 27 | use bytes::{BufMut, BytesMut}; 28 | 29 | use super::Protocol; 30 | use crate::error::{Error, Result}; 31 | use crate::payload::Payload; 32 | use crate::v2::noise; 33 | use crate::AnyPayload; 34 | 35 | /// Protocol frame consists solely from the payload 36 | #[derive(Debug, PartialEq)] 37 | pub struct Frame(Payload); 38 | 39 | impl Frame { 40 | /// TODO use this constant 41 | pub const MAX_FRAME_LENGTH: usize = 16384; 42 | 43 | /// Builds a frame from `src`. No copying occurs as `BytesMut` allows us splitting off 44 | /// the payload part. 45 | pub(crate) fn deserialize(src: &mut BytesMut) -> Self { 46 | Self::from_serialized_payload(src.split()) 47 | } 48 | 49 | pub fn from_serialized_payload(payload: BytesMut) -> Self { 50 | Self(Payload::from(payload)) 51 | } 52 | 53 | pub fn from_serializable_payload(payload: T) -> Self 54 | // TODO review the static lifetime 55 | where 56 | T: 'static + AnyPayload, 57 | { 58 | Self(Payload::LazyBytes(Box::new(payload))) 59 | } 60 | 61 | /// Serializes a frame into a specified `dst` buffer. The method either copies the already 62 | /// serialized payload into the buffer or runs the on-demand serializer of the payload. 63 | pub fn serialize(&self, dst: &mut BytesMut) -> Result<()> { 64 | // TODO reserve a reasonable chunk in the buffer - make it a constant 65 | dst.reserve(128); 66 | let mut payload_writer = dst.split().writer(); 67 | 68 | self.0.serialize_to_writer(&mut payload_writer)?; 69 | 70 | // No copying occurs here as the buffer has been originally split off of `dst` 71 | dst.unsplit(payload_writer.into_inner()); 72 | 73 | Ok(()) 74 | } 75 | 76 | /// Consumes the frame providing its payload 77 | pub fn into_inner(self) -> Payload { 78 | self.0 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod test { 84 | use super::*; 85 | 86 | #[test] 87 | fn test_frame_from_serializable_payload() { 88 | const EXPECTED_FRAME_BYTES: &[u8] = &[0xde, 0xad, 0xbe, 0xef, 0x0a]; 89 | struct TestPayload; 90 | 91 | impl AnyPayload for TestPayload { 92 | fn serialize_to_writer(&self, writer: &mut dyn std::io::Write) -> Result<()> { 93 | writer.write_all(&EXPECTED_FRAME_BYTES)?; 94 | Ok(()) 95 | } 96 | } 97 | let frame = Frame::from_serializable_payload(TestPayload); 98 | 99 | let mut dst_frame_bytes = BytesMut::new(); 100 | assert!(frame.serialize(&mut dst_frame_bytes).is_ok()); 101 | assert_eq!( 102 | BytesMut::from(EXPECTED_FRAME_BYTES), 103 | dst_frame_bytes, 104 | "Frames don't match, expected: {:x?}, generated: {:x?}", 105 | EXPECTED_FRAME_BYTES, 106 | // convert to vec as it has same Debug representation 107 | dst_frame_bytes.to_vec() 108 | ); 109 | } 110 | } 111 | 112 | #[derive(Debug)] 113 | pub struct Framing; 114 | 115 | impl ii_wire::Framing for Framing { 116 | type Tx = Frame; 117 | type Rx = Frame; 118 | type Error = Error; 119 | type Codec = noise::CompoundCodec; 120 | } 121 | -------------------------------------------------------------------------------- /protocols/wire/src/connection.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::io; 24 | use std::net::SocketAddr; 25 | use std::pin::Pin; 26 | use std::task::{Context, Poll}; 27 | 28 | use crate::{tokio, tokio_util}; 29 | 30 | use futures::prelude::*; 31 | use pin_project::pin_project; 32 | use tokio::net::{TcpStream, ToSocketAddrs}; 33 | use tokio_util::codec::{Framed, FramedParts}; 34 | 35 | use crate::framing::Framing; 36 | 37 | #[pin_project] 38 | #[derive(Debug)] 39 | pub struct Connection { 40 | #[pin] 41 | pub framed_stream: Framed, 42 | } 43 | 44 | impl Connection { 45 | /// Create a new `Connection` from an existing TCP stream 46 | pub fn new(stream: TcpStream) -> Self { 47 | let framed_stream = Framed::new(stream, F::Codec::default()); 48 | 49 | Self { framed_stream } 50 | } 51 | 52 | /// Create a new `Connection` from `FramedParts`. 53 | /// 54 | /// It can be used on previously framed stream to change to new codec 55 | pub fn new_from_parts(parts: FramedParts) -> Self { 56 | let mut new_parts = FramedParts::new(parts.io, F::Codec::default()); 57 | new_parts.read_buf = parts.read_buf; 58 | new_parts.write_buf = parts.write_buf; 59 | let framed_stream = Framed::from_parts(new_parts); 60 | Self { framed_stream } 61 | } 62 | 63 | pub fn codec_mut(&mut self) -> &mut F::Codec { 64 | self.framed_stream.codec_mut() 65 | } 66 | 67 | /// Connects to a remote address `addr` and creates two halves 68 | /// which perfom full message serialization / desrialization 69 | pub async fn connect(addr: A) -> Result { 70 | let stream = TcpStream::connect(addr).await?; 71 | Ok(Connection::new(stream)) 72 | } 73 | 74 | pub fn local_addr(&self) -> Result { 75 | self.framed_stream.get_ref().local_addr() 76 | } 77 | 78 | pub fn peer_addr(&self) -> Result { 79 | self.framed_stream.get_ref().peer_addr() 80 | } 81 | 82 | pub fn into_inner(self) -> Framed { 83 | self.framed_stream 84 | } 85 | } 86 | 87 | impl From for Connection { 88 | fn from(stream: TcpStream) -> Self { 89 | Self::new(stream) 90 | } 91 | } 92 | 93 | impl Stream for Connection { 94 | type Item = Result; 95 | 96 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 97 | self.project().framed_stream.poll_next(cx) 98 | } 99 | } 100 | 101 | impl Sink for Connection { 102 | type Error = F::Error; 103 | 104 | fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 105 | self.project().framed_stream.poll_ready(cx) 106 | } 107 | 108 | fn start_send(self: Pin<&mut Self>, item: F::Tx) -> Result<(), Self::Error> { 109 | self.project().framed_stream.start_send(item) 110 | } 111 | 112 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 113 | self.project().framed_stream.poll_flush(cx) 114 | } 115 | 116 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 117 | self.project().framed_stream.poll_close(cx) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant/src/macro_private.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! These are types / functions that the macro-generated code 24 | //! needs to function. They need to be made public in order 25 | //! for the generated code to use them, but 'logically' they are private. 26 | //! Please don't use them other than through the macro code. 27 | 28 | use std::future::Future; 29 | use std::marker::PhantomData; 30 | use std::pin::Pin; 31 | use std::task::{Context, Poll}; 32 | 33 | use pin_project::pin_project; 34 | 35 | use super::{AsyncHandler, GetId}; 36 | 37 | /// Don't use this type directly, it's not part of the public API. 38 | /// 39 | /// A filter future that can reset to take a new input. 40 | /// Kind of like lockstep Sink + Stream. 41 | #[doc(hidden)] 42 | pub trait FilterFuture: Future { 43 | fn input(self: Pin<&mut Self>, value: T); 44 | } 45 | 46 | /// Don't use this type directly, it's not part of the public API. 47 | #[doc(hidden)] 48 | #[pin_project] 49 | pub struct HandlerFilter { 50 | handler: H, 51 | handle_fn: Hf, 52 | variant: Option, 53 | #[pin] 54 | state: Option, 55 | _marker: PhantomData O>, 56 | } 57 | 58 | impl HandlerFilter 59 | where 60 | F: Future, 61 | Hf: 'static + Fn(*mut H, T) -> F, 62 | { 63 | /// Don't use this function, it's not part of the public API. 64 | pub fn __new(handler: H, handle_fn: Hf) -> Self { 65 | Self { 66 | handler, 67 | handle_fn, 68 | variant: None, 69 | state: None, 70 | _marker: PhantomData, 71 | } 72 | } 73 | } 74 | 75 | impl Future for HandlerFilter 76 | where 77 | F: Future, 78 | Hf: 'static + Fn(*mut H, T) -> F, 79 | { 80 | type Output = O; 81 | 82 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 83 | let mut this = self.project(); 84 | 85 | if this.state.is_none() { 86 | // No future in self.state, generate a new one: 87 | let variant = this 88 | .variant 89 | .take() 90 | .expect("BUG: unvariant: HandlerFilter: Neither future nor variant in state"); 91 | let ft = (this.handle_fn)(this.handler as *mut _, variant); 92 | this.state.set(Some(ft)); 93 | } 94 | 95 | // At this point there must be a future in self.state 96 | this.state 97 | .as_mut() 98 | .as_pin_mut() 99 | .expect("BUG: unvariant: HandlerFilter: No Future in state") 100 | .poll(cx) 101 | } 102 | } 103 | 104 | impl FilterFuture for HandlerFilter 105 | where 106 | F: Future, 107 | Hf: 'static + Fn(*mut H, T) -> F, 108 | { 109 | fn input(self: Pin<&mut Self>, variant: T) { 110 | let mut this = self.project(); 111 | this.state.set(None); 112 | *this.variant = Some(variant); 113 | } 114 | } 115 | 116 | impl AsyncHandler 117 | where 118 | T: GetId, 119 | { 120 | #[doc(hidden)] 121 | /// Do not use this function, it's not a part of the public API. 122 | pub fn __new(ff: FF) -> Self 123 | where 124 | FF: FilterFuture + 'static, 125 | { 126 | let ff = Box::pin(ff); 127 | Self { ff } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-macros/src/unvariant.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use proc_macro2::TokenStream; 24 | use quote::{quote, ToTokens}; 25 | use syn::parse::{Parse, ParseStream}; 26 | use syn::punctuated::Punctuated; 27 | use syn::{braced, Expr, Ident, Result, Token, Type}; 28 | 29 | pub enum ArmPattern { 30 | Type(Type), 31 | CatchAll, 32 | } 33 | 34 | pub struct Arm { 35 | ident: Ident, 36 | pat: ArmPattern, 37 | body: Expr, 38 | } 39 | 40 | impl Parse for Arm { 41 | fn parse(input: ParseStream) -> Result { 42 | let ident: Ident = input.parse()?; 43 | input.parse::()?; 44 | 45 | let pat = if input.peek(Token![_]) { 46 | input.parse::()?; 47 | ArmPattern::CatchAll 48 | } else { 49 | let ty: Type = input.parse()?; 50 | ArmPattern::Type(ty) 51 | }; 52 | 53 | let _arrow: Token![=>] = input.parse()?; 54 | let body: Expr = input.parse()?; 55 | 56 | Ok(Self { ident, pat, body }) 57 | } 58 | } 59 | 60 | pub struct Item { 61 | variant: Ident, 62 | use_try: bool, 63 | arms: Punctuated, 64 | } 65 | 66 | impl Parse for Item { 67 | fn parse(input: ParseStream) -> Result { 68 | let use_try = input.peek(Token![try]); 69 | if use_try { 70 | input.parse::()?; 71 | } 72 | 73 | let variant: Ident = input.parse()?; 74 | let content; 75 | braced!(content in input); 76 | 77 | let arms = content.parse_terminated(Arm::parse)?; 78 | 79 | Ok(Self { 80 | variant, 81 | use_try, 82 | arms, 83 | }) 84 | } 85 | } 86 | 87 | struct ArmTokenizer<'a> { 88 | arm: &'a Arm, 89 | item: &'a Item, 90 | } 91 | 92 | impl<'a> ToTokens for ArmTokenizer<'a> { 93 | fn to_tokens(&self, tokens: &mut TokenStream) { 94 | let ident = &self.arm.ident; 95 | let variant = &self.item.variant; 96 | let body = &self.arm.body; 97 | 98 | let tt = match &self.arm.pat { 99 | ArmPattern::Type(ty) => { 100 | if self.item.use_try { 101 | quote!(<#ty as ::ii_unvariant::Id<_>>::ID => { 102 | let #ident = <#ty as ::core::convert::TryFrom::<_>>::try_from(#variant); #body 103 | }) 104 | } else { 105 | quote!(<#ty as ::ii_unvariant::Id<_>>::ID => { 106 | let #ident = <#ty as ::core::convert::From::<_>>::from(#variant); #body 107 | }) 108 | } 109 | } 110 | ArmPattern::CatchAll => { 111 | quote!(__unvariant_catch_all_id => { let #ident = __unvariant_catch_all_id; #body }) 112 | } 113 | }; 114 | 115 | tokens.extend(tt); 116 | } 117 | } 118 | 119 | impl ToTokens for Item { 120 | fn to_tokens(&self, tokens: &mut TokenStream) { 121 | let variant = &self.variant; 122 | let arms: Punctuated = self 123 | .arms 124 | .iter() 125 | .map(|arm| ArmTokenizer { arm, item: self }) 126 | .collect(); 127 | 128 | tokens.extend(quote!( 129 | match ::ii_unvariant::GetId::get_id(&#variant) { 130 | #arms 131 | } 132 | )); 133 | } 134 | } 135 | 136 | pub fn expand(item: Item) -> TokenStream { 137 | quote!(#item) 138 | } 139 | -------------------------------------------------------------------------------- /utils-rs/async-utils/src/maybe_future.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::future::Future; 24 | use std::pin::Pin; 25 | use std::task::{Context, Poll}; 26 | 27 | use pin_project_lite::pin_project; 28 | 29 | pin_project! { 30 | #[doc = "Object representing a possible future with `Output=T` or a result directly. 31 | Internally a boxed future is used to seal the returning future type. 32 | 33 | The main intention is to use this future in async trait context when 34 | a called function can but mustn't have to need to be async. 35 | Traditionally a user has to pay for the async call by boxing the result 36 | future for all calls. `MaybeFuture` allows to pay the allocation price 37 | only when it is needed."] 38 | #[must_use = "futures do nothing unless you `.await` or poll them"] 39 | pub struct MaybeFuture { 40 | #[pin] 41 | inner: MaybeFutureInner 42 | } 43 | } 44 | 45 | pin_project! { 46 | #[doc = "Internal implementation of `MaybeFuture`. This is non-public type, 47 | preventing invalid construction."] 48 | #[project = InnerProjection] 49 | enum MaybeFutureInner { 50 | Future { #[pin] future: Pin + Send>> }, 51 | Ready { value: Option }, 52 | } 53 | } 54 | 55 | impl MaybeFuture { 56 | pub fn future + Send + 'static>(fut: F) -> Self { 57 | let inner = MaybeFutureInner::Future { 58 | future: Box::pin(fut), 59 | }; 60 | Self { inner } 61 | } 62 | pub fn result(val: T) -> Self { 63 | let inner = MaybeFutureInner::Ready { value: Some(val) }; 64 | Self { inner } 65 | } 66 | } 67 | 68 | impl Future for MaybeFuture { 69 | type Output = T; 70 | 71 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 72 | // Peel off the Pin layers: 73 | let proj = self.project().inner.project(); 74 | 75 | // Forward to future or yield value immediately: 76 | match proj { 77 | InnerProjection::Future { future } => future.poll(cx), 78 | InnerProjection::Ready { value } => value 79 | .take() 80 | .expect("BUG: MaybeFuture polled after yielding Ready") 81 | .into(), 82 | } 83 | } 84 | } 85 | 86 | #[macro_export] 87 | macro_rules! maybe { 88 | ($expr:expr) => { 89 | match $expr { 90 | ::std::result::Result::Ok(val) => val, 91 | ::std::result::Result::Err(err) => { 92 | return $crate::MaybeFuture::result(::std::result::Result::Err(err.into())); 93 | } 94 | } 95 | }; 96 | } 97 | 98 | #[cfg(test)] 99 | mod test { 100 | use super::*; 101 | 102 | use futures::future; 103 | #[cfg(feature = "tokio02")] 104 | use tokio02 as tokio; 105 | #[cfg(feature = "tokio12")] 106 | use tokio12 as tokio; 107 | 108 | #[tokio::test] 109 | async fn maybe_future() { 110 | // Test with a Future: 111 | let mut i = 0u32; 112 | let ft = future::poll_fn(move |cx| { 113 | if i < 42 { 114 | i += 1; 115 | cx.waker().wake_by_ref(); 116 | Poll::Pending 117 | } else { 118 | Poll::Ready(i) 119 | } 120 | }); 121 | 122 | let maybe = MaybeFuture::future(ft); 123 | assert_eq!(maybe.await, 42); 124 | 125 | // Test with an immediate value: 126 | let maybe = MaybeFuture::result(42); 127 | assert_eq!(maybe.await, 42); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /protocols/wire/src/proxy/codec.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use super::error::{Error, Result}; 24 | use ii_logging::slog::{Record, Serializer, KV}; 25 | use std::convert::TryFrom; 26 | use std::fmt; 27 | use std::net::SocketAddr; 28 | 29 | pub mod v1; 30 | pub mod v2; 31 | 32 | pub(crate) const MAX_HEADER_SIZE: usize = 536; 33 | 34 | /// Type of transport 35 | #[derive(PartialEq, Eq, Debug, Clone, Copy)] 36 | pub enum SocketType { 37 | /// TCP/IP V4 38 | Ipv4, 39 | /// TCP/IP V6 40 | Ipv6, 41 | /// Transport protocol in unknown 42 | Unknown, 43 | } 44 | 45 | /// Contains information from PROXY protocol 46 | #[derive(PartialEq, Eq, Debug, Clone, Copy)] 47 | pub struct ProxyInfo { 48 | /// Type of transport 49 | pub socket_type: SocketType, 50 | /// Original source address passed in PROXY protocol 51 | pub original_source: Option, 52 | /// Original destination address passed in PROXY protocol 53 | pub original_destination: Option, 54 | } 55 | 56 | impl Default for ProxyInfo { 57 | fn default() -> Self { 58 | Self { 59 | socket_type: SocketType::Unknown, 60 | original_source: Default::default(), 61 | original_destination: Default::default(), 62 | } 63 | } 64 | } 65 | 66 | impl TryFrom<(Option, Option)> for ProxyInfo { 67 | type Error = Error; 68 | fn try_from(addrs: (Option, Option)) -> Result { 69 | match (addrs.0, addrs.1) { 70 | (s @ Some(SocketAddr::V4(_)), d @ Some(SocketAddr::V4(_))) => Ok(ProxyInfo { 71 | socket_type: SocketType::Ipv4, 72 | original_source: s, 73 | original_destination: d, 74 | }), 75 | 76 | (s @ Some(SocketAddr::V6(_)), d @ Some(SocketAddr::V6(_))) => Ok(ProxyInfo { 77 | socket_type: SocketType::Ipv6, 78 | original_source: s, 79 | original_destination: d, 80 | }), 81 | 82 | (None, None) => Ok(ProxyInfo { 83 | socket_type: SocketType::Unknown, 84 | original_source: None, 85 | original_destination: None, 86 | }), 87 | 88 | _ => Err(Error::Proxy( 89 | "Inconsistent source and destination addresses".into(), 90 | )), 91 | } 92 | } 93 | } 94 | 95 | impl fmt::Display for ProxyInfo { 96 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 97 | write!( 98 | f, 99 | "ProxyInfo[SRC:{}, DST:{}]", 100 | self.original_source 101 | .map_or_else(|| "N/A".to_string(), |s| s.to_string()), 102 | self.original_destination 103 | .map_or_else(|| "N/A".to_string(), |s| s.to_string()) 104 | ) 105 | } 106 | } 107 | 108 | impl KV for ProxyInfo { 109 | fn serialize( 110 | &self, 111 | _record: &Record<'_>, 112 | serializer: &mut dyn Serializer, 113 | ) -> ii_logging::slog::Result { 114 | const DST_KEY: &str = "PROXY_DST"; 115 | const SRC_KEY: &str = "PROXY_SRC"; 116 | if let Some(src) = self.original_source { 117 | serializer.emit_str(SRC_KEY, &src.to_string())?; 118 | } else { 119 | serializer.emit_none(SRC_KEY)?; 120 | } 121 | 122 | if let Some(dst) = self.original_destination { 123 | serializer.emit_str(DST_KEY, &dst.to_string())?; 124 | } else { 125 | serializer.emit_none(DST_KEY)?; 126 | } 127 | Ok(()) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /protocols/stratum/src/v1/messages/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::str::FromStr; 24 | 25 | use super::*; 26 | use crate::test_utils::v1::*; 27 | use crate::v1::rpc::Rpc; 28 | use serde_json::Value; 29 | 30 | #[test] 31 | fn set_version_mask_serialize() { 32 | let msg = SetVersionMask { 33 | mask: VersionMask(HexU32Be(0xdeadbeef)), 34 | }; 35 | 36 | let msg_json = serde_json::to_string(&msg).expect("BUG: Failed to serialize SetVersionMask"); 37 | assert_eq!(msg_json, "[\"deadbeef\"]"); 38 | let msg_des: SetVersionMask = 39 | serde_json::from_str(&msg_json).expect("BUG: Failed to deserialize SetVersionMask"); 40 | assert_eq!(msg_des, msg); 41 | 42 | let payload = msg.try_into().expect("BUG: failed to build RPC payload"); 43 | let lhs = rpc::Request { 44 | id: Some(1), 45 | payload, 46 | }; 47 | let lhs = serde_json::to_value(lhs).expect("BUG: failed to serialize RPC payload"); 48 | 49 | let rhs = r#"{"id": 1, "method": "mining.set_version_mask", "params": ["deadbeef"]}"#; 50 | let rhs: Value = 51 | serde_json::from_str(&rhs).expect("BUG: failed to deserialize static JSON string"); 52 | assert_eq!(lhs, rhs); 53 | } 54 | 55 | #[test] 56 | fn test_build_subscribe_from_rpc_request() { 57 | if let Rpc::Request(subscribe_req) = build_subscribe_request_frame() { 58 | let expected_subscribe = build_subscribe(); 59 | let subscribe = Subscribe::try_from(subscribe_req).expect("BUG: Conversion failed"); 60 | 61 | assert_eq!(expected_subscribe, subscribe, "Subscribe request mismatch"); 62 | } else { 63 | panic!("BUG: Request expected"); 64 | } 65 | } 66 | 67 | #[test] 68 | fn test_build_subscribe_good_result_from_response() { 69 | if let Rpc::Response(subscribe_resp) = build_subscribe_ok_response_message() { 70 | let expected_subscribe_result = build_subscribe_ok_result(); 71 | let subscribe_result = 72 | SubscribeResult::try_from(subscribe_resp).expect("BUG: Conversion failed"); 73 | 74 | assert_eq!( 75 | expected_subscribe_result, subscribe_result, 76 | "Subscribe result mismatch" 77 | ); 78 | } else { 79 | panic!("BUG: Response expected, the test needs to be fixed") 80 | } 81 | } 82 | 83 | #[test] 84 | fn test_build_subscribe_good_result_json() { 85 | let expected_subscribe_result = build_subscribe_ok_result(); 86 | match Rpc::from_str(MINING_SUBSCRIBE_OK_RESULT_JSON).expect("BUG: Cannot prepare test result") { 87 | Rpc::Response(resp) => { 88 | let subscribe_result = SubscribeResult::try_from(resp).expect("BUG: Conversion failed"); 89 | assert_eq!( 90 | expected_subscribe_result, subscribe_result, 91 | "Subscribe result mismatch" 92 | ); 93 | } 94 | Rpc::Request(req) => { 95 | panic!("Received request ({:?} instead of response", req); 96 | } // Rpc::Unrecognized(value) => { 97 | // panic!("Unrecongized message: {}", value); 98 | // } 99 | } 100 | } 101 | 102 | #[test] 103 | #[should_panic] 104 | fn test_subscribe_malformed_result_json() { 105 | match Rpc::from_str(MINING_SUBSCRIBE_MALFORMED_RESULT_JSON) 106 | .expect("BUG: Cannot prepare test result") 107 | { 108 | // This match arm should fail thus causing the test to pass 109 | Rpc::Response(resp) => { 110 | let _subscribe_result = 111 | SubscribeResult::try_from(resp).expect("BUG: Conversion failed"); 112 | } 113 | // This match arm should not execute, if it does it is a bug and the test wouldn't panic 114 | // and would show up as failed 115 | Rpc::Request(_) => (), 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-tests/tests/common/frame.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use std::convert::TryFrom; 24 | 25 | use ii_unvariant::{id, GetId, Id}; 26 | 27 | /// A simple "network frame" example. 28 | /// It contains a one-byte header followed by arbitrary data. 29 | /// The header is an ID of the contained type. 30 | /// The type of data is either `Foo` or `Bar`, 31 | /// `Foo` is basically a `u32`, `Bar` is a one-byte `bool` value. 32 | pub struct Frame(Box<[u8]>); 33 | 34 | /// Implementing `GetId` for the `Frame` lets the unvariant macros know 35 | /// how to get specific type ID out of the variant type `Frame`. 36 | impl GetId for Frame { 37 | type Id = u32; 38 | 39 | fn get_id(&self) -> u32 { 40 | self.0[0] as _ 41 | } 42 | } 43 | 44 | // Utilities: various ctors 45 | impl Frame { 46 | pub fn new_foo(value: u32) -> Self { 47 | let mut bytes = Vec::with_capacity(5); 48 | bytes.push(Foo::ID as u8); 49 | bytes.extend(&value.to_le_bytes()); 50 | Self(bytes.into()) 51 | } 52 | 53 | pub fn new_foo_bad() -> Self { 54 | Self(vec![1].into()) 55 | } 56 | 57 | pub fn new_bar(value: bool) -> Self { 58 | let bytes = vec![Bar::ID as u8, value as u8]; 59 | Self(bytes.into()) 60 | } 61 | 62 | pub fn new_bar_bad() -> Self { 63 | Self(vec![2].into()) 64 | } 65 | 66 | pub fn new_unknown() -> Self { 67 | Self(vec![0xff].into()) 68 | } 69 | } 70 | 71 | /// The `Foo` "message" type, annotated with an ID. 72 | /// The annotation generates an Id trait implementation. 73 | #[id(1)] 74 | pub struct Foo(u32); 75 | 76 | impl From for Foo { 77 | fn from(frame: Frame) -> Self { 78 | From::from(&frame) 79 | } 80 | } 81 | 82 | impl<'a> From<&'a Frame> for Foo { 83 | fn from(frame: &'a Frame) -> Self { 84 | assert_eq!(frame.get_id(), Self::ID); 85 | let mut num = [0; 4]; 86 | num.copy_from_slice(&frame.0[1..5]); 87 | Self(u32::from_le_bytes(num)) 88 | } 89 | } 90 | 91 | /// A `From` implementation is required so that the macro code 92 | /// knows how to create this type from the generic `Frame` type. 93 | /// This is the non-failing use-case, for fallible example see `TryFoo` below. 94 | impl From for u32 { 95 | fn from(foo: Foo) -> u32 { 96 | foo.0 + Foo::ID 97 | } 98 | } 99 | 100 | /// The `Bar` "message" type, annotated with an ID. 101 | #[id(2)] 102 | pub struct Bar(bool); 103 | 104 | impl From for Bar { 105 | fn from(frame: Frame) -> Self { 106 | From::from(&frame) 107 | } 108 | } 109 | 110 | impl<'a> From<&'a Frame> for Bar { 111 | fn from(frame: &'a Frame) -> Self { 112 | assert_eq!(frame.get_id(), Self::ID); 113 | Self(frame.0[1] == 1) 114 | } 115 | } 116 | 117 | impl From for u32 { 118 | fn from(bar: Bar) -> u32 { 119 | bar.0 as u32 + Bar::ID + 1 120 | } 121 | } 122 | 123 | /// The `TryFoo` type is the same as `Foo`, except that it implements 124 | /// `TryFrom` instead of simple `From` to demonstrate 125 | /// usage of the macro's `try` use-case. 126 | #[id(1)] 127 | pub struct TryFoo(u32); 128 | 129 | impl TryFrom for TryFoo { 130 | type Error = u32; 131 | 132 | fn try_from(frame: Frame) -> Result { 133 | assert_eq!(frame.get_id(), Self::ID); 134 | let mut num = [0; 4]; 135 | num.copy_from_slice(frame.0.get(1..5).ok_or(Self::ID)?); 136 | Ok(Self(u32::from_le_bytes(num))) 137 | } 138 | } 139 | 140 | impl From for u32 { 141 | fn from(foo: TryFoo) -> u32 { 142 | foo.0 + TryFoo::ID 143 | } 144 | } 145 | 146 | // Ditto, cf. `TryFoo` 147 | #[id(2)] 148 | pub struct TryBar(bool); 149 | 150 | impl TryFrom for TryBar { 151 | type Error = u32; 152 | 153 | fn try_from(frame: Frame) -> Result { 154 | assert_eq!(frame.get_id(), Self::ID); 155 | let byte = *frame.0.get(1).ok_or(Self::ID)?; 156 | Ok(Self(byte == 1)) 157 | } 158 | } 159 | 160 | impl From for u32 { 161 | fn from(bar: TryBar) -> u32 { 162 | bar.0 as u32 + TryBar::ID + 1 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/hashrate_meter.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | """ 24 | this class estimates miner speed from reported shares 25 | implemented using rolling time window 26 | the HashrateMeter.roll method is called automatically each 5 seconds by default (granularity = 5) 27 | """ 28 | import numpy as np 29 | import simpy 30 | 31 | 32 | class HashrateMeter(object): 33 | def __init__( 34 | self, 35 | env: simpy.Environment, 36 | window_size: int = 60, 37 | granularity: int = 5, 38 | auto_hold_threshold=None, 39 | ): 40 | self.env = env 41 | self.time_started = 0 42 | self.window_size = window_size 43 | self.granularity = granularity 44 | self.pow_buffer = np.zeros(self.window_size // self.granularity) 45 | self.submit_buffer = np.zeros(self.window_size // self.granularity) 46 | self.frozen_time_buffer = np.zeros(self.window_size // self.granularity) 47 | self.roll_proc = env.process(self.roll()) 48 | self.auto_hold_threshold = auto_hold_threshold 49 | self.on_hold = False 50 | self.put_on_hold_proc = None 51 | 52 | def reset(self, time_started): 53 | self.pow_buffer = np.zeros(self.window_size // self.granularity) 54 | self.submit_buffer = np.zeros(self.window_size // self.granularity) 55 | self.frozen_time_buffer = np.zeros(self.window_size // self.granularity) 56 | self.time_started = time_started 57 | if self.put_on_hold_proc: 58 | self.put_on_hold_proc.interrupt() # terminate the current auto-on-hold process if exists 59 | 60 | def roll(self): 61 | while True: 62 | try: 63 | yield self.env.timeout(self.granularity) 64 | if not self.on_hold: 65 | self.pow_buffer = np.roll(self.pow_buffer, 1) 66 | self.pow_buffer[0] = 0 67 | self.submit_buffer = np.roll(self.submit_buffer, 1) 68 | self.submit_buffer[0] = 0 69 | self.frozen_time_buffer = np.roll(self.frozen_time_buffer, 1) 70 | self.frozen_time_buffer[0] = 0 71 | else: 72 | self.frozen_time_buffer[0] += self.granularity 73 | except simpy.Interrupt: 74 | break 75 | 76 | def on_hold_after_timeout(self): 77 | try: 78 | yield self.env.timeout(self.auto_hold_threshold) 79 | self.on_hold = True 80 | self.put_on_hold_proc = None 81 | except simpy.Interrupt: 82 | pass # do nothing 83 | 84 | def measure(self, share_diff: int): 85 | """Account for the shares 86 | 87 | TODO: consider changing the interface to accept the difficulty target directly 88 | """ 89 | self.pow_buffer[0] += share_diff 90 | self.submit_buffer[0] += 1 91 | self.on_hold = False # reset frozen status whenever a share is submitted 92 | if self.auto_hold_threshold: 93 | if self.put_on_hold_proc: 94 | self.put_on_hold_proc.interrupt() # terminate the current auto-on-hold process if exists 95 | self.put_on_hold_proc = self.env.process( 96 | self.on_hold_after_timeout() 97 | ) # will trigger after the threshold 98 | 99 | def get_speed(self): 100 | total_time_held = np.sum(self.frozen_time_buffer) 101 | time_elapsed = self.env.now - self.time_started - total_time_held 102 | if time_elapsed > self.window_size: 103 | time_elapsed = self.window_size 104 | total_work = np.sum(self.pow_buffer) 105 | if time_elapsed < 1 or total_work == 0: 106 | return None 107 | 108 | return total_work * 4.294967296 / time_elapsed 109 | 110 | def get_submit_per_secs(self): 111 | total_time_held = np.sum(self.frozen_time_buffer) 112 | time_elapsed = self.env.now - self.time_started - total_time_held 113 | if time_elapsed < 1: 114 | return None 115 | elif time_elapsed > self.window_size: 116 | time_elapsed = self.window_size 117 | return np.sum(self.submit_buffer) / time_elapsed 118 | 119 | def is_on_hold(self): 120 | return self.on_hold 121 | 122 | def terminate(self): 123 | self.roll_proc.interrupt() 124 | if self.put_on_hold_proc: 125 | self.put_on_hold_proc.interrupt() # terminate the current auto-on-hold process if exists 126 | -------------------------------------------------------------------------------- /utils-rs/scm/scm-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | extern crate proc_macro; 24 | 25 | use proc_macro::TokenStream; 26 | use proc_macro2::Span; 27 | use quote::quote; 28 | use syn::parse::{Parse, ParseStream}; 29 | use syn::parse_macro_input; 30 | 31 | use std::io::{Error, ErrorKind, Result}; 32 | use std::process::Command; 33 | 34 | macro_rules! error { 35 | ($($args:tt)*) => { 36 | syn::Error::new(Span::call_site(), format!($($args)*)) 37 | }; 38 | } 39 | 40 | #[derive(Default)] 41 | struct GitHashInput { 42 | object: Option, 43 | length: Option, 44 | } 45 | 46 | impl Parse for GitHashInput { 47 | fn parse(input: ParseStream) -> syn::Result { 48 | let mut result = GitHashInput::default(); 49 | 50 | while !input.is_empty() { 51 | let arg: syn::Ident = input.parse()?; 52 | let _: syn::token::Eq = input.parse()?; 53 | match arg.to_string().as_str() { 54 | "object" => { 55 | let name: syn::LitStr = input.parse()?; 56 | result.object.replace(name.value()); 57 | } 58 | "length" => { 59 | let value: syn::LitInt = input.parse()?; 60 | result.length.replace(value.base10_parse()?); 61 | } 62 | name => { 63 | return Err(error!("Unexpected argument `{}`", name)); 64 | } 65 | } 66 | if !input.is_empty() { 67 | let _: syn::token::Comma = input.parse()?; 68 | } 69 | } 70 | 71 | Ok(result) 72 | } 73 | } 74 | 75 | fn get_git_hash( 76 | git_executable_path: &str, 77 | default_var: &str, 78 | input: GitHashInput, 79 | ) -> Result { 80 | let object = input.object.as_deref().unwrap_or("HEAD"); 81 | Command::new(git_executable_path) 82 | .arg("rev-parse") 83 | .arg(object) 84 | .output() 85 | .and_then(|output| { 86 | if output.status.success() { 87 | let output = String::from_utf8_lossy(&output.stdout); 88 | let mut hash = output.trim_end().to_string(); 89 | if let Some(length) = input.length { 90 | hash.truncate(length); 91 | } 92 | Ok(hash) 93 | } else { 94 | Err(Error::new( 95 | ErrorKind::Other, 96 | "Invalid git revision requested".to_string(), 97 | )) 98 | } 99 | }) 100 | .or_else(|_| { 101 | std::env::var(default_var).map_err(|e| Error::new(ErrorKind::Other, e.to_string())) 102 | }) 103 | } 104 | 105 | fn impl_git_hash(input: GitHashInput) -> syn::Result { 106 | match get_git_hash("git", "GIT_HASH", input) { 107 | Ok(hash) => Ok(quote!(#hash)), 108 | Err(e) => Err(error!("{}", e)), 109 | } 110 | } 111 | 112 | #[proc_macro] 113 | pub fn git_hash(input: TokenStream) -> TokenStream { 114 | let args = parse_macro_input!(input as GitHashInput); 115 | 116 | let tokens = match impl_git_hash(args) { 117 | Ok(x) => x, 118 | Err(e) => e.to_compile_error(), 119 | }; 120 | 121 | TokenStream::from(tokens) 122 | } 123 | 124 | #[cfg(test)] 125 | mod tests { 126 | use super::get_git_hash; 127 | use crate::GitHashInput; 128 | 129 | #[test] 130 | fn failing_git_command() { 131 | get_git_hash( 132 | "git", 133 | "CARGO_PKG_NAME", 134 | GitHashInput { 135 | object: Some("HEAD".to_string()), 136 | length: None, 137 | }, 138 | ) 139 | .expect("BUG: Failed to extract revision number from git"); 140 | 141 | get_git_hash( 142 | "some-non-existing-command", 143 | "CARGO_PKG_NAME", 144 | GitHashInput { 145 | object: Some("HEAD".to_string()), 146 | length: None, 147 | }, 148 | ) 149 | .expect("BUG: Failed to extract revision number from variable"); 150 | 151 | get_git_hash( 152 | "some-non-existing-command", 153 | "SOME_NONEXISTING_VARIABLE", 154 | GitHashInput { 155 | object: Some("HEAD".to_string()), 156 | length: None, 157 | }, 158 | ) 159 | .expect_err("BUG: Failed to fail to extract version"); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /utils-rs/unvariant/unvariant-macros/src/id.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | use proc_macro2::TokenStream; 24 | use quote::{quote, ToTokens}; 25 | use syn::parse::discouraged::Speculative; 26 | use syn::parse::{Parse, ParseStream}; 27 | use syn::{parse_str, Expr, Generics, Ident, ItemEnum, ItemStruct, Lit, Result, Token, Type}; 28 | 29 | pub struct Args { 30 | pub id: Expr, 31 | pub ty: Type, 32 | } 33 | 34 | impl Args { 35 | fn int_suffix_to_type(suffix: &str) -> Option { 36 | if suffix.is_empty() { 37 | Some(Type::Verbatim(quote!(u32))) 38 | } else { 39 | parse_str(suffix).ok() 40 | } 41 | } 42 | 43 | fn float_suffix_to_type(suffix: &str) -> Option { 44 | if suffix.is_empty() { 45 | Some(Type::Verbatim(quote!(f32))) 46 | } else { 47 | parse_str(suffix).ok() 48 | } 49 | } 50 | 51 | fn infer_type(id: &Expr) -> Option { 52 | match &id { 53 | Expr::Lit(lit) => { 54 | let ty = match &lit.lit { 55 | Lit::Int(i) => return Self::int_suffix_to_type(i.suffix()), 56 | Lit::Str(_) => quote!(&'static str), 57 | Lit::ByteStr(_) => quote!(&'static [u8]), 58 | Lit::Byte(_) => quote!(u8), 59 | Lit::Char(_) => quote!(char), 60 | Lit::Float(f) => return Self::float_suffix_to_type(f.suffix()), 61 | Lit::Bool(_) => quote!(bool), 62 | Lit::Verbatim(_) => return None, 63 | }; 64 | 65 | Some(Type::Verbatim(ty)) 66 | } 67 | _ => None, 68 | } 69 | } 70 | } 71 | 72 | impl Parse for Args { 73 | fn parse(input: ParseStream) -> Result { 74 | let id: Expr = input.parse()?; 75 | 76 | if input.is_empty() { 77 | let ty = match Args::infer_type(&id) { 78 | Some(ty) => ty, 79 | None => { 80 | return Err(input.error("Could not infer ID type. Please specify it explicitly using the #[id( type )] syntax.")) 81 | } 82 | }; 83 | 84 | Ok(Args { id, ty }) 85 | } else { 86 | input.parse::()?; 87 | let ty: Type = input.parse()?; 88 | 89 | Ok(Args { id, ty }) 90 | } 91 | } 92 | } 93 | 94 | pub enum Item { 95 | Struct(ItemStruct), 96 | Enum(ItemEnum), 97 | } 98 | 99 | impl Item { 100 | fn decl(&self) -> (&Ident, &Generics) { 101 | match self { 102 | Self::Struct(s) => (&s.ident, &s.generics), 103 | Self::Enum(e) => (&e.ident, &e.generics), 104 | } 105 | } 106 | } 107 | 108 | impl Parse for Item { 109 | fn parse(input: ParseStream) -> Result { 110 | let fork = input.fork(); 111 | 112 | // Try to first parse a struct in a speculative manner. 113 | // If that fails, go back (drop fork) and try parsing an anum. 114 | // This might be somewhat inefficient for enums or erroneous usages, 115 | // but there isn't really any other way - we can't just peek for a `struct` token, 116 | // because there might be an arbitrary number of arbitrarily complex 117 | // attributes in front of the `struct` token. 118 | // Note that doc comments also generate attributes. 119 | 120 | if let Ok(item_struct) = fork.parse::() { 121 | input.advance_to(&fork); 122 | Ok(Item::Struct(item_struct)) 123 | } else if let Ok(item_struct) = input.parse::() { 124 | Ok(Item::Enum(item_struct)) 125 | } else { 126 | Err(input.error("#[id(..)] may only be used with structs and enums")) 127 | } 128 | } 129 | } 130 | 131 | impl ToTokens for Item { 132 | fn to_tokens(&self, tokens: &mut TokenStream) { 133 | match self { 134 | Item::Struct(item) => item.to_tokens(tokens), 135 | Item::Enum(item) => item.to_tokens(tokens), 136 | } 137 | } 138 | } 139 | 140 | pub fn expand(args: Args, item: Item) -> TokenStream { 141 | let (id, ty) = (args.id, args.ty); 142 | let (name, generics) = item.decl(); 143 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 144 | 145 | quote!( 146 | #item 147 | 148 | impl #impl_generics ::ii_unvariant::Id<#ty> for #name #ty_generics #where_clause { 149 | const ID: #ty = #id; 150 | } 151 | ) 152 | } 153 | -------------------------------------------------------------------------------- /protocols/stratum/src/v2/noise/negotiation.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! This module provides functions for a negotiation step before the noise handshake phase. 24 | //! Currently used to negotiate the encryption algorithm that will be used during the snow 25 | //! communication. 26 | 27 | use crate::v2::types::*; 28 | use serde::{Deserialize, Serialize}; 29 | use serde_repr::{Deserialize_repr, Serialize_repr}; 30 | use snow::{params::NoiseParams, Builder}; 31 | 32 | /// Builds noise params given a certain EncryptionAlgorithm 33 | pub struct NoiseParamsBuilder { 34 | params: NoiseParams, 35 | } 36 | 37 | impl NoiseParamsBuilder { 38 | pub fn new(chosen_algorithm: EncryptionAlgorithm) -> Self { 39 | Self { 40 | params: format!("Noise_NX_25519_{:?}_BLAKE2s", chosen_algorithm) 41 | .parse() 42 | .expect("BUG: cannot parse noise parameters"), 43 | } 44 | } 45 | 46 | pub fn get_builder<'a>(self) -> Builder<'a> { 47 | // Initialize our initiator using a builder. 48 | Builder::new(self.params) 49 | } 50 | } 51 | 52 | const MAGIC: u32 = u32::from_le_bytes(*b"STR2"); 53 | 54 | /// Negotiation prologue; if initiator and responder prologue don't match the entire negotiation 55 | /// fails. 56 | /// Made of the initiator message (the list of algorithms) and the responder message (the 57 | /// algorithm chosen). If both of them are None, no negotiation happened. 58 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 59 | pub struct Prologue { 60 | pub initiator_msg: Option, 61 | pub responder_msg: Option, 62 | } 63 | 64 | impl Default for Prologue { 65 | fn default() -> Self { 66 | Self { 67 | initiator_msg: None, 68 | responder_msg: None, 69 | } 70 | } 71 | } 72 | 73 | #[derive(Serialize_repr, Deserialize_repr, Clone, Debug, PartialEq)] 74 | #[repr(u32)] 75 | pub enum EncryptionAlgorithm { 76 | AESGCM = u32::from_le_bytes(*b"AESG"), 77 | ChaChaPoly = u32::from_le_bytes(*b"CHCH"), 78 | } 79 | 80 | /// Message used for negotiation of the encryption algorithm 81 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 82 | pub struct NegotiationMessage { 83 | magic: u32, 84 | pub encryption_algos: Seq0_255, 85 | } 86 | 87 | impl NegotiationMessage { 88 | pub fn new(encryption_algos: Vec) -> Self { 89 | Self { 90 | magic: MAGIC, 91 | encryption_algos: Seq0_255::try_from(encryption_algos) 92 | .expect("BUG: cannot convert EncryptionAlgorithm vector"), 93 | } 94 | } 95 | } 96 | 97 | /// Holds encryption negotiation params, such as the Prologue and the algorithm chosen. 98 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 99 | pub struct EncryptionNegotiation { 100 | pub chosen_algorithm: EncryptionAlgorithm, 101 | pub prologue: Prologue, 102 | } 103 | 104 | impl EncryptionNegotiation { 105 | pub fn new(prologue: Prologue, chosen_algorithm: EncryptionAlgorithm) -> Self { 106 | EncryptionNegotiation { 107 | chosen_algorithm, 108 | prologue, 109 | } 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod test { 115 | use super::*; 116 | use crate::v2; 117 | 118 | #[test] 119 | fn test_negotiation_message() { 120 | let negotiation_message = NegotiationMessage::new(vec![ 121 | EncryptionAlgorithm::AESGCM, 122 | EncryptionAlgorithm::ChaChaPoly, 123 | ]); 124 | let mut serialized_negotiation_message = Vec::new(); 125 | serialized_negotiation_message.extend_from_slice(b"STR2"); // magic bytes 126 | serialized_negotiation_message.push(2); // number of algorithms provided 127 | serialized_negotiation_message.extend_from_slice(b"AESG"); // AESGCM 128 | serialized_negotiation_message.extend_from_slice(b"CHCH"); // ChaChaPoly 129 | 130 | let v2_serialized_negotiation_message = v2::serialization::to_vec(&negotiation_message) 131 | .expect("BUG: can't serialize negotiation_message"); 132 | assert_eq!( 133 | serialized_negotiation_message, 134 | v2_serialized_negotiation_message 135 | ); 136 | 137 | let v2_deserialized_negotiation_message = 138 | v2::serialization::from_slice(&v2_serialized_negotiation_message) 139 | .expect("BUG: can't deserialize negotiation_message"); 140 | assert_eq!(negotiation_message, v2_deserialized_negotiation_message); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /protocols/stratum/sim/sim_primitives/stratum_v1/miner.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Braiins Systems s.r.o. 2 | # 3 | # This file is part of Braiins Open-Source Initiative (BOSI). 4 | # 5 | # BOSI is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Please, keep in mind that we may also license BOSI or any part thereof 19 | # under a proprietary license. For more information on the terms and conditions 20 | # of such proprietary license or if you have any other questions, please 21 | # contact us at opensource@braiins.com. 22 | 23 | import enum 24 | 25 | from sim_primitives.miner import Miner 26 | from sim_primitives.network import Connection 27 | from sim_primitives.pool import MiningJob 28 | from sim_primitives.protocol import DownstreamConnectionProcessor 29 | from sim_primitives.stratum_v1.messages import ( 30 | Configure, 31 | Authorize, 32 | Subscribe, 33 | SubscribeResponse, 34 | SetDifficulty, 35 | Submit, 36 | Notify, 37 | OkResult, 38 | ErrorResult, 39 | ) 40 | 41 | 42 | class MinerV1(DownstreamConnectionProcessor): 43 | class States(enum.Enum): 44 | INIT = enum.auto() 45 | AUTHORIZED = enum.auto() 46 | AUTHORIZED_AND_SUBSCRIBED = enum.auto() 47 | SUBSCRIBED = enum.auto() 48 | RUNNING = enum.auto() 49 | 50 | def __init__(self, miner: Miner, connection: Connection): 51 | self.miner = miner 52 | self.state = self.States.INIT 53 | self.session = None 54 | self.desired_submits_per_sec = 0.3 55 | self.default_difficulty = self.miner.device_information.get('speed_ghps') / ( 56 | 4.294_967_296 * self.desired_submits_per_sec 57 | ) 58 | super().__init__(miner.name, miner.env, miner.bus, connection) 59 | self.setup() 60 | 61 | def setup(self): 62 | self.session = self.miner.new_mining_session(self.default_difficulty) 63 | self.session.run() 64 | 65 | auth_req = Authorize(req_id=None, user_name='some_miner', password='x') 66 | self.send_request(auth_req) 67 | 68 | sbscr_req = Subscribe( 69 | req_id=None, signature='some_signature', extranonce1=None, url='some_url' 70 | ) 71 | self.send_request(sbscr_req) 72 | 73 | def submit_mining_solution(self, job: MiningJob): 74 | submit_req = Submit( 75 | req_id=None, 76 | user_name=self.miner.name, 77 | job_id=job.uid, 78 | extranonce2=None, 79 | time=self.miner.env.now, 80 | nonce=None, 81 | ) 82 | self.send_request(submit_req) 83 | 84 | def visit_ok_result(self, msg): 85 | req = self.request_registry.pop(msg.req_id) 86 | if not req: 87 | self._on_invalid_message(msg) 88 | return 89 | if isinstance(req, Authorize): 90 | if self.state == self.States.INIT: 91 | self.state = self.States.AUTHORIZED 92 | elif self.state == self.States.SUBSCRIBED: 93 | self.state = self.States.AUTHORIZED_AND_SUBSCRIBED 94 | self._emit_protocol_msg_on_bus('Connection authorized', msg) 95 | 96 | def visit_error_result(self, msg): 97 | req = self.request_registry.pop(msg.req_id) 98 | if req: 99 | self._emit_protocol_msg_on_bus( 100 | "Error code {}, '{}' for request".format(msg.code, msg.msg), req 101 | ) 102 | 103 | def visit_subscribe_response(self, msg): 104 | if not self.request_registry.pop(msg.req_id): 105 | self._on_invalid_message(msg) 106 | return 107 | if self.state == self.States.INIT: 108 | self.state = self.States.SUBSCRIBED 109 | elif self.state == self.States.AUTHORIZED: 110 | self.state = self.States.AUTHORIZED_AND_SUBSCRIBED 111 | self._emit_protocol_msg_on_bus('Connection subscribed', msg) 112 | 113 | def visit_notify(self, msg): 114 | if self._allowed_to_mine: 115 | self.state = self.States.RUNNING 116 | job = self.session.new_mining_job(job_uid=msg.job_id) 117 | self.miner.mine_on_new_job(job, flush_any_pending_work=msg.clean_jobs) 118 | else: 119 | self._emit_protocol_msg_on_bus('Miner not fully initialized', msg) 120 | 121 | def visit_set_difficulty(self, msg): 122 | self.session.set_target(msg.diff) 123 | self._emit_protocol_msg_on_bus('Difficulty updated', msg) 124 | 125 | def visit_reconnect(self, msg): 126 | self._emit_protocol_msg_on_bus( 127 | 'Reconnect received, waiting {} seconds'.format(msg.wait_time), msg 128 | ) 129 | 130 | def disconnect_and_reconnect(): 131 | target = self.connection.conn_target 132 | self.session.terminate() 133 | self.state = self.States.INIT 134 | self.miner.set_is_mining(False) 135 | self.connection.disconnect() 136 | yield self.env.timeout(msg.wait_time) 137 | self.setup() 138 | self.connection.connect_to(target) 139 | self.miner.set_is_mining(True) 140 | self._emit_protocol_msg_on_bus('Miner reconnected', msg) 141 | 142 | self.env.process(disconnect_and_reconnect()) 143 | 144 | @property 145 | def _allowed_to_mine(self): 146 | return self.state in ( 147 | self.States.RUNNING, 148 | self.States.AUTHORIZED_AND_SUBSCRIBED, 149 | self.States.SUBSCRIBED, 150 | ) 151 | 152 | def _on_invalid_message(self, msg): 153 | self._emit_protocol_msg_on_bus('Received invalid message', msg) 154 | -------------------------------------------------------------------------------- /stratum-proxy/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Braiins Systems s.r.o. 2 | // 3 | // This file is part of Braiins Open-Source Initiative (BOSI). 4 | // 5 | // BOSI is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | // Please, keep in mind that we may also license BOSI or any part thereof 19 | // under a proprietary license. For more information on the terms and conditions 20 | // of such proprietary license or if you have any other questions, please 21 | // contact us at opensource@braiins.com. 22 | 23 | //! Module that represents custom stratum proxy errors 24 | 25 | use futures::channel::mpsc; 26 | use std::error::Error as StdError; 27 | use std::io; 28 | use thiserror::Error; 29 | 30 | use ii_wire::proxy::error::Error as ProxyError; 31 | 32 | #[derive(Error, Debug)] 33 | pub enum DownstreamError { 34 | #[error("Error on sending downstream: {0}")] 35 | SendError(String), 36 | #[error("Early network error before any protocol communication started")] 37 | EarlyIo(std::io::Error), 38 | #[error("PROXY protocol error: {0}")] 39 | ProxyProtocol(ProxyError), 40 | #[error("Stratum error: {0}")] 41 | Stratum(ii_stratum::error::Error), 42 | #[error("Timeout error: {0}")] 43 | Timeout(tokio::time::error::Elapsed), 44 | } 45 | 46 | #[derive(Error, Debug)] 47 | pub enum UpstreamError { 48 | #[error("Error on sending upstream: {0}")] 49 | SendError(String), 50 | #[error("IO error: {0}")] 51 | Io(std::io::Error), 52 | #[error("PROXY protocol error: {0}")] 53 | ProxyProtocol(ProxyError), 54 | #[error("Stratum error: {0}")] 55 | Stratum(ii_stratum::error::Error), 56 | #[error("Timeout error: {0}")] 57 | Timeout(tokio::time::error::Elapsed), 58 | } 59 | 60 | impl From> for UpstreamError { 61 | fn from(e: mpsc::TrySendError) -> Self { 62 | UpstreamError::SendError(e.into_send_error().to_string()) 63 | } 64 | } 65 | 66 | impl From> for DownstreamError { 67 | fn from(e: mpsc::TrySendError) -> Self { 68 | DownstreamError::SendError(e.into_send_error().to_string()) 69 | } 70 | } 71 | 72 | #[derive(Error, Debug)] 73 | pub enum V2ProtocolError { 74 | #[error("V2 Setup Connection error: {0}")] 75 | SetupConnection(String), 76 | #[error("V2 Open Mining Channel error: {0}")] 77 | OpenMiningChannel(String), 78 | #[error("V2 Other non-specified Error: {0}")] 79 | Other(String), 80 | } 81 | 82 | impl V2ProtocolError { 83 | pub fn setup_connection(val: T) -> Self { 84 | Self::SetupConnection(val.to_string()) 85 | } 86 | pub fn open_mining_channel(val: T) -> Self { 87 | Self::OpenMiningChannel(val.to_string()) 88 | } 89 | pub fn other(val: T) -> Self { 90 | Self::Other(val.to_string()) 91 | } 92 | } 93 | 94 | #[derive(Error, Debug)] 95 | pub enum Error { 96 | #[error("Failed to resolved host: {0}")] 97 | HostNameError(String), 98 | 99 | /// General error used for more specific errors. 100 | #[error("General error: {0}")] 101 | General(String), 102 | 103 | #[error("General error: {0} with metrics label: {1}")] 104 | GeneralWithMetricsLabel(String, &'static str), 105 | 106 | /// Stratum protocol error. 107 | #[error("Stratum error: {0}")] 108 | Stratum(#[from] ii_stratum::error::Error), 109 | 110 | /// Bitcoin Hashes error. 111 | #[error("Bitcoin Hashes error: {0}")] 112 | BitcoinHashes(#[from] bitcoin_hashes::error::Error), 113 | 114 | /// Timeout error. 115 | #[error("Timeout error: {0}")] 116 | Timeout(/*#[from]*/ tokio::time::error::Elapsed), 117 | 118 | /// Utf8 error 119 | #[error("Error decoding UTF-8 string: {0}")] 120 | Utf8(#[from] std::str::Utf8Error), 121 | 122 | /// Json Error 123 | #[error("JSON error: {0}")] 124 | Json(#[from] serde_json::Error), 125 | 126 | /// Connection Attempt Error 127 | #[error("Client connection attempt error: {0}")] 128 | ClientAttempt(#[from] ii_wire::AttemptError), 129 | 130 | /// File content error 131 | #[error("Invalid content of key/certificate file: {0}")] 132 | InvalidFile(String), 133 | 134 | /// Prometheus metrics error. 135 | #[cfg(feature = "prometheus_metrics")] 136 | #[error("Metrics error: {0}")] 137 | Metrics(#[from] ii_metrics::Error), 138 | 139 | /// Errors when communicatin with downstream node 140 | #[error("Downstream error: {0}")] 141 | Downstream(#[from] DownstreamError), 142 | 143 | /// Errors when communicating with upstream node 144 | #[error("Downstream error: {0}")] 145 | Upstream(#[from] UpstreamError), 146 | 147 | /// Stratum protocol state error 148 | #[error("Stratum V2 protocol state related error: {0}")] 149 | Protocol(V2ProtocolError), 150 | 151 | #[error("I/O error: {0}")] 152 | Io(std::io::Error), 153 | 154 | #[error("Noise security error: {0}")] 155 | Noise(#[from] ii_noise_proxy::Error), 156 | } 157 | 158 | impl From for Error { 159 | fn from(val: V2ProtocolError) -> Self { 160 | Self::Protocol(val) 161 | } 162 | } 163 | 164 | impl From> for Error 165 | where 166 | T: Send + Sync + 'static, 167 | { 168 | fn from(e: futures::channel::mpsc::TrySendError) -> Self { 169 | Error::Io(io::Error::new(io::ErrorKind::Other, e)) 170 | } 171 | } 172 | 173 | impl From<&str> for Error { 174 | fn from(msg: &str) -> Self { 175 | Error::General(msg.to_string()) 176 | } 177 | } 178 | 179 | impl From for Error { 180 | fn from(msg: String) -> Self { 181 | Error::General(msg) 182 | } 183 | } 184 | 185 | /// A specialized `Result` type bound to [`Error`]. 186 | pub type Result = std::result::Result; 187 | --------------------------------------------------------------------------------