stratum-mining / stratum

stratum
https://stratumprotocol.org
Other
204 stars 121 forks source link

high-level SRI APIs for external consumption #702

Open plebhash opened 8 months ago

plebhash commented 8 months ago

SRI stands for Stratum Reference Implementation, and is meant to act as the building blocks for the Bitcoin mining infrastructure.

Now imagine someone wants to write a SV2-enabled pool based on SRI. There are infinite different directions they could go. Right now, SRI has implementation for roles crates, which do include a "demo" pool. But this is just a dummy project which only exists for the purpose of showcasing how to wire the low-level libraries together (e.g.: crates under protocols, commons and utils).

If someone wants to leverage the low-level libs while writing a pool from scratch under a different architecture, that is already doable. We believe these low-level crate libs are relatively mature, which is why they are all set as v1.0.0. And they can use this demo pool as a reference. This is also likely a path to most pools that already have some codebase, but only wish to integrate SV2 low level crates in a modular way (most likely via FFI).

But putting low-level libraries together implies heavy-lifting. That could be time-consuming and somewhat prohibitive to small teams that wish to leverage Rust to write a pool from scratch. That is why it is also desirable that SRI provide high-level libraries with building blocks for custom implementations on the application level.

These building blocks would be opinionated with regards to threading model, error handling, socket handling and many other general aspects of software architecture. But the pool team could still implement their own opinionated strategies for coinbase management and share accounting, without necessarilty spending thousands of man-hours on wiring together the low-level APIs.

plebhash commented 8 months ago

Here is a proposed structure for the repo:

stratum
├── bin
│   ├── Cargo.toml (workspace)
│   ├── jd-client
│   ├── jd-server
│   ├── mining-proxy
│   ├── pool
│   ├── test-utils
│   └── translator
└── lib
    ├── Cargo.toml (workspace)
    ├── examples
    │   ├── interop-cpp
    │   ├── interop-cpp-no-cargo
    │   ├── ping-pong-with-noise
    │   ├── ping-pong-without-noise
    │   ├── sv1-client-and-server
    │   ├── sv2-proxy
    │   └── template-provider-test
    ├── protocols
    │   ├── fuzz-tests
    │   ├── v1
    │   └── v2
    │       ├── binary-sv2
    │       ├── codec-sv2
    │       ├── const-sv2
    │       ├── framing-sv2
    │       ├── noise-sv2
    │       ├── roles-logic-sv2
    │       ├── subprotocols
    │       └── sv2-ffi
    └── roles
        ├── jd-client
        ├── jd-server
        ├── mining-proxy
        ├── pool
        └── translator

However I'm not sure where to place the following directories:

maybe @Fi3 has some suggestions?

Fi3 commented 8 months ago

If we are going to separete workspaces to have differnet policies for external dependecies. More permissive in roles and less in in protocols. This part of the tree:

   └── roles
        ├── jd-client
        ├── jd-server
        ├── mining-proxy
        ├── pool
        └── translator

Should stay in the bin workspaces, cause we will import things here, where for the lib workspace we can likely reduce everything to: (1) serde (under a feature flag), (2) few other crypto thing for noise as well under a feature flag and (3) bitcoin-rust that we assume safe. In the roles we want to use more "fancy" things.

Another option could be to have roles as now*, but they are crates that have 2 entry-point one main and one lib so we can keep them together. Not sure what option make more sense btw.

About other directories:

Btw I excepect that finding the optimal solution will require some try and a lot of discussion, this are juts very raw guildelines.

* that we could just call roles workspace
* that we could just call protocols or core workspace
* not separating lib a bin crate
plebhash commented 8 months ago

@Fi3 what is the plan for removing vendored/rust-jsonrpc?

it seems only jd-server uses it.

I see two alternatives:

is there some other alternative?

Fi3 commented 8 months ago

@lorbax is implementing a reduced bitcoin rpc library based on hyper.

plebhash commented 8 months ago

ok, I started splitting the root workspace into three separate new workspaces:

I'm leaving test, benches and examples living as separate entities without being part of any workspace.

you can check the modifications here: https://github.com/plebhash/stratum/commit/c1d8c61d0a1cf08e51a6be4cc7a3389aa172611e


@Fi3 how about moving the main.rs implementations from roles/* into examples?

protocols and roles would live as separate workspaces and still follow the dependency policies you mentioned above (permissive for roles, strict for protocols), but both would be 100% libs.

if the user wants to know how to use the APIs to implement a specific role, they can look into examples for reference. The advantage of this project layout would be making it explicitly clear that SRI allows for custom decisions on e.g.: opinionated pool design.

What is still not clear to me is whether this would make sense for every role, or only few ones like pool. Or in other words: is there room for custom implementations of proxy, translator and jdc/jds?

Fi3 commented 8 months ago

There is room for custom implementation on every roles, sv2 spec say only what a role main task it but there a lot of ways to achieve it, not talk to about endless kinds of translators and general proxies. Examples right now contains very trivial examples about how to use the libraries. Roles are supposed to be prod ready software used by pool and miners, not sure if they should fit in the same category. Another issue with it is that you increase the development feedback time (I guess one of the most important metric). If you have 3 work-spaces like that a -> b -> c and you work on a but you need to change something in c, in order to have the changes in a you need to: (1) change and compile c (2) clean and compile b (3) clean and compile a. So adding I'm general against adding an hop in something that we modify frequently if there is no a very good reason. Last point was true last time that I checked maybe now is not anymore like that.

plebhash commented 8 months ago

ok undersood, we should keep all roles crates as bin+lib.


If you have 3 work-spaces like that a -> b -> c and you work on a but you need to change something in c, in order to have the changes in a you need to: (1) change and compile c (2) clean and compile b (3) clean and compile a.

this comment confused me a bit.

is this an argument against having protocols, roles and utils as separate workspaces?

I'm confused because on a previous comment you also said:

utils we have thing that are used by crates under protocols and things that are used by crates under roles, and thing like the message generator that is used by no one. I think that make sense to put them in their workspace. Now the MG is not included in then main workspace for compile issues, I think that have one workspace for utils would solve this.

that was the main motivation for creating a separate workspace for utils.

Fi3 commented 8 months ago

what i mean is that having roles libraries and roles binaries in 2 separate workspace have disadvantage one of that could be the time that a developer have to wait between modifying something in the code and seeing the effects of it. in the case of the "core" part of SRI this is counterbalanced by the fact that we can use 2 different policy of what we import in work-spaces.

plebhash commented 8 months ago

what i mean is that having roles libraries and roles binaries in 2 separate workspace ...

as stated in https://github.com/stratum-mining/stratum/issues/702#issuecomment-1865317310, I'm currently splitting workspaces in terms of:

and not lib + bin. I guess I should also update the original issue description to reflect that, apologies for the confusion there.

disadvantage one of that could be the time that a developer have to wait between modifying something in the code and seeing the effects of it.

I don't think this really happens in practice. Here's some instructions so you can see it for yourself:

  1. check out the workspace-split branch of my fork
  2. build the pool_sv2 executable:
    $ cd roles/pool
    $ cargo build
  3. apply some change to a dependency of pool_sv from the other workspace. For example, rename binary_sv2:
    --- a/protocols/v2/binary-sv2/binary-sv2/Cargo.toml
    +++ b/protocols/v2/binary-sv2/binary-sv2/Cargo.toml
    @@ -1,5 +1,5 @@
    [package]
    -name = "binary_sv2"
    +name = "binary_sv2_with_a_different_name"
    version = "0.1.6"
  4. build again:
    $ cargo build
    error: no matching package named `binary_sv2` found
    location searched: /home/bear/develop/stratum/protocols/v2/binary-sv2/binary-sv2

this means the developer can simply rebuild whatever roles crate they're working on, while making direct modifications to some protocols dependency crate, and never having to rebuild protocols as an extra step

or am I missing something? if that is the case, could you please give a practical example of some specific workflow where having this specific workspace split (roles + protocols + utils, not bin + lib) would become prohibitive?

Fi3 commented 8 months ago

Not sure i will double check changing the dep name invalidate the cache i guess

Fi3 commented 8 months ago

ok I double checked changing the code of something in a different workspace without changing the name of the crate and cargo see it so no issue here.