Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 211 additions & 94 deletions roles/Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion roles/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [
"translator",
"jd-client",
"jd-server"
]
, "template-provider-role"]

[profile.dev]
# Required by super_safe_lock
Expand Down
35 changes: 35 additions & 0 deletions roles/template-provider-role/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "template-provider-role"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html


[lib]
name = "template_provider_sv2"
path = "src/lib.rs"

[dependencies]
clap = { version = "4.5.37", features = ["derive"] }
binary_sv2 = { path = "../../protocols/v2/binary-sv2" }
buffer_sv2 = { path = "../../utils/buffer" }
codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2"] }
network_helpers_sv2 = { path = "../roles-utils/network-helpers" }
noise_sv2 = { path = "../../protocols/v2/noise-sv2" }
roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" }
async-channel = "1.5.1"
tracing = "0.1.41"
stratum-common = { version = "2.0.0", path = "../../common", features = ["constants"] }
tracing-subscriber = "0.3.19"
tokio = { version = "1.44.2", features = ["full"] }
key-utils = { path = "../../utils/key-utils" }
capnp = "0.20.0"
capnp-rpc = "0.20.0"
tokio-util = { version = "0.7.15", features = ["compat"] }
futures = "0.3.31"

[build-dependencies]
capnpc = "0.20.0"
regex = "1.11.1"

43 changes: 43 additions & 0 deletions roles/template-provider-role/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::{
env,
path::{Path, PathBuf},
};

fn main() {
println!("cargo:rerun-if-changed=capnp");

let out_dir = PathBuf::from(env::var("OUT_DIR").expect("Missing OUT_DIR"));
let capnp_dir = Path::new("capnp");

let schemas = [
"common.capnp",
"echo.capnp",
"init.capnp",
"mining.capnp",
"proxy.capnp",
];
let mut cmd = capnpc::CompilerCommand::new();
cmd.src_prefix(capnp_dir).output_path(&out_dir);
for schema in &schemas {
cmd.file(capnp_dir.join(schema));
}
cmd.run().expect("capnpc compilation failed");

// Too much hassle look into it later.
// let re = Regex::new(r"crate::(\w+_capnp)").unwrap();

// for schema in &schemas {
// let module_name = schema.strip_suffix(".capnp").unwrap();
// let file_name = format!("{}_capnp.rs", module_name);
// let generated_file = out_dir.join(&file_name);

// let content = fs::read_to_string(&generated_file)
// .unwrap_or_else(|e| panic!("Failed to read {}: {}", generated_file.display(), e));

// // Patch all references like `crate::proxy_capnp::...` → `crate::capnp::proxy_capnp::...`
// let patched = re.replace_all(&content, "crate::capnp::$1");

// fs::write(&generated_file, patched.as_ref())
// .unwrap_or_else(|e| panic!("Failed to write {}: {}", generated_file.display(), e));
// }
}
18 changes: 18 additions & 0 deletions roles/template-provider-role/capnp/common.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2024 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

@0xcd2c6232cb484a28;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");

# using Proxy = import "/mp/proxy.capnp";
# $Proxy.includeTypes("ipc/capnp/common-types.h");

using Proxy = import "proxy.capnp";

struct BlockRef $Proxy.wrap("interfaces::BlockRef") {
hash @0 :Data;
height @1 :Int32;
}
18 changes: 18 additions & 0 deletions roles/template-provider-role/capnp/echo.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

@0x888b4f7f51e691f7;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");

# using Proxy = import "/mp/proxy.capnp";
# $Proxy.include("interfaces/echo.h");
# $Proxy.includeTypes("ipc/capnp/echo-types.h");
using Proxy = import "proxy.capnp";

interface Echo $Proxy.wrap("interfaces::Echo") {
destroy @0 (context :Proxy.Context) -> ();
echo @1 (context :Proxy.Context, echo: Text) -> (result :Text);
}
24 changes: 24 additions & 0 deletions roles/template-provider-role/capnp/init.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

@0xf2c5cfa319406aa6;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");

# using Proxy = import "/mp/proxy.capnp";
# $Proxy.include("interfaces/echo.h");
# $Proxy.include("interfaces/init.h");
# $Proxy.include("interfaces/mining.h");
# $Proxy.includeTypes("ipc/capnp/init-types.h");

using Echo = import "echo.capnp";
using Mining = import "mining.capnp";
using Proxy = import "proxy.capnp";

interface Init $Proxy.wrap("interfaces::Init") {
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
makeMining @2 (context :Proxy.Context) -> (result :Mining.Mining);
}
57 changes: 57 additions & 0 deletions roles/template-provider-role/capnp/mining.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) 2024 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

@0xc77d03df6a41b505;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");

using Common = import "common.capnp";
# using Proxy = import "/mp/proxy.capnp";
# $Proxy.include("interfaces/mining.h");
# $Proxy.includeTypes("ipc/capnp/mining-types.h");
using Proxy = import "proxy.capnp";

interface Mining $Proxy.wrap("interfaces::Mining") {
isTestChain @0 (context :Proxy.Context) -> (result: Bool);
isInitialBlockDownload @1 (context :Proxy.Context) -> (result: Bool);
getTip @2 (context :Proxy.Context) -> (result: Common.BlockRef, hasResult: Bool);
waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64) -> (result: Common.BlockRef);
createNewBlock @4 (options: BlockCreateOptions) -> (result: BlockTemplate);
}

interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
destroy @0 (context :Proxy.Context) -> ();
getBlockHeader @1 (context: Proxy.Context) -> (result: Data);
getBlock @2 (context: Proxy.Context) -> (result: Data);
getTxFees @3 (context: Proxy.Context) -> (result: List(Int64));
getTxSigops @4 (context: Proxy.Context) -> (result: List(Int64));
getCoinbaseTx @5 (context: Proxy.Context) -> (result: Data);
getCoinbaseCommitment @6 (context: Proxy.Context) -> (result: Data);
getWitnessCommitmentIndex @7 (context: Proxy.Context) -> (result: Int32);
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
submitSolution @9 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool);
waitNext @10 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate);
}

struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {
useMempool @0 :Bool $Proxy.name("use_mempool");
blockReservedWeight @1 :UInt64 $Proxy.name("block_reserved_weight");
coinbaseOutputMaxAdditionalSigops @2 :UInt64 $Proxy.name("coinbase_output_max_additional_sigops");
}

struct BlockWaitOptions $Proxy.wrap("node::BlockWaitOptions") {
timeout @0 : Float64 $Proxy.name("timeout");
feeThreshold @1 : Int64 $Proxy.name("fee_threshold");
}

# Note: serialization of the BlockValidationState C++ type is somewhat fragile
# and using the struct can be awkward. It would be good if testBlockValidity
# method were changed to return validity information in a simpler format.
struct BlockValidationState {
mode @0 :Int32;
result @1 :Int32;
rejectReason @2 :Text;
debugMessage @3 :Text;
}
65 changes: 65 additions & 0 deletions roles/template-provider-role/capnp/proxy.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright (c) 2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

@0xcc316e3f71a040fb;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("mp");

annotation include(file): Text;
annotation includeTypes(file): Text;
# Extra include paths to add to generated files.

annotation wrap(interface, struct): Text;
# Wrap capnp interface generating ProxyClient / ProxyServer C++ classes that
# forward calls to a C++ interface with same methods and parameters. Text
# string should be the name of the C++ interface.
# If applied to struct rather than an interface, this will generate a ProxyType
# struct with get methods for introspection and copying fields between C++ and
# capnp structs.

annotation count(param, struct, interface): Int32;
# Indicate how many C++ method parameters there are corresponding to one capnp
# parameter (default is 1). If not 1, multiple C++ method arguments will be
# condensed into a single capnp parameter by the client and then expanded by
# the server by CustomReadField/CustomBuildField overloads which need to be
# provided separately. An example would be a capnp Text parameter initialized
# from C++ char* and size arguments. Can be 0 to fill an implicit capnp
# parameter from client or server side context. If annotation is applied to an
# interface or struct type it will apply to all parameters of that type.

annotation exception(param): Text;
# Indicate that a result parameter corresponds to a C++ exception. Text string
# should be the name of a C++ exception type that the generated server class
# will catch and the client class will rethrow.

annotation name(field, method): Text;
# Name of the C++ method or field corresponding to a capnp method or field.

annotation skip(field): Void;
# Synonym for count(0).

interface ThreadMap $count(0) {
# Interface letting clients control which thread a method call should
# execute on. Clients create and name threads and pass the thread handle as
# a call parameter.
makeThread @0 (name :Text) -> (result :Thread);
}

interface Thread {
# Thread handle returned by makeThread corresponding to one server thread.

getName @0 () -> (result: Text);
}

struct Context $count(0) {
# Execution context passed as a parameter from the client class to the server class.

thread @0 : Thread;
# Handle of the server thread the current method call should execute on.

callbackThread @1 : Thread;
# Handle of the client thread that is calling the current method, and that
# any callbacks made by the server thread should be made on.
}
110 changes: 110 additions & 0 deletions roles/template-provider-role/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::{
convert::From,
fmt::Debug,
sync::{MutexGuard, PoisonError},
};

use roles_logic_sv2::parsers::Mining;

#[derive(std::fmt::Debug)]
pub enum TPError {
Io(std::io::Error),
ChannelSend(Box<dyn std::marker::Send + Debug>),
ChannelRecv(async_channel::RecvError),
BinarySv2(binary_sv2::Error),
Codec(codec_sv2::Error),
Noise(noise_sv2::Error),
RolesLogic(roles_logic_sv2::Error),
Framing(codec_sv2::framing_sv2::Error),
PoisonLock(String),
Custom(String),
Sv2ProtocolError((u32, Mining<'static>)),
}

impl std::fmt::Display for TPError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use TPError::*;
match self {
Io(ref e) => write!(f, "I/O error: `{:?}", e),
ChannelSend(ref e) => write!(f, "Channel send failed: `{:?}`", e),
ChannelRecv(ref e) => write!(f, "Channel recv failed: `{:?}`", e),
BinarySv2(ref e) => write!(f, "Binary SV2 error: `{:?}`", e),
Codec(ref e) => write!(f, "Codec SV2 error: `{:?}", e),
Framing(ref e) => write!(f, "Framing SV2 error: `{:?}`", e),
Noise(ref e) => write!(f, "Noise SV2 error: `{:?}", e),
RolesLogic(ref e) => write!(f, "Roles Logic SV2 error: `{:?}`", e),
PoisonLock(ref e) => write!(f, "Poison lock: {:?}", e),
Custom(ref e) => write!(f, "Custom SV2 error: `{:?}`", e),
Sv2ProtocolError(ref e) => {
write!(f, "Received Sv2 Protocol Error from upstream: `{:?}`", e)
}
}
}
}

pub type TPResult<T> = Result<T, TPError>;

impl From<std::io::Error> for TPError {
fn from(e: std::io::Error) -> TPError {
TPError::Io(e)
}
}

impl From<async_channel::RecvError> for TPError {
fn from(e: async_channel::RecvError) -> TPError {
TPError::ChannelRecv(e)
}
}

impl From<binary_sv2::Error> for TPError {
fn from(e: binary_sv2::Error) -> TPError {
TPError::BinarySv2(e)
}
}

impl From<codec_sv2::Error> for TPError {
fn from(e: codec_sv2::Error) -> TPError {
TPError::Codec(e)
}
}

impl From<noise_sv2::Error> for TPError {
fn from(e: noise_sv2::Error) -> TPError {
TPError::Noise(e)
}
}

impl From<roles_logic_sv2::Error> for TPError {
fn from(e: roles_logic_sv2::Error) -> TPError {
TPError::RolesLogic(e)
}
}

impl<T: 'static + std::marker::Send + Debug> From<async_channel::SendError<T>> for TPError {
fn from(e: async_channel::SendError<T>) -> TPError {
TPError::ChannelSend(Box::new(e))
}
}

impl From<String> for TPError {
fn from(e: String) -> TPError {
TPError::Custom(e)
}
}
impl From<codec_sv2::framing_sv2::Error> for TPError {
fn from(e: codec_sv2::framing_sv2::Error) -> TPError {
TPError::Framing(e)
}
}

impl<T> From<PoisonError<MutexGuard<'_, T>>> for TPError {
fn from(e: PoisonError<MutexGuard<T>>) -> TPError {
TPError::PoisonLock(e.to_string())
}
}

impl From<(u32, Mining<'static>)> for TPError {
fn from(e: (u32, Mining<'static>)) -> Self {
TPError::Sv2ProtocolError(e)
}
}
Loading