Stratum Protocol

Messages are newline ("\n") terminated json strings sent over plain TCP with optional TLS encryption. URI scheme is stratum+tcp for plain TCP and stratum+ssl or stratum+tls if TLS encryption is used (for some reason "ssl" is more prevalent than "tls" as URI scheme despite in fact TLS is actually used).

Establishing a stratum connection

After establishing a TCP connection (and optionally TLS) the first message is sent from the Client and must be of type mining.subscribe:

Mining.subscribe

Request from client:

{
  "id": 1,
  "method": "mining.subscribe",
  "params": [
    "MinerName/1.0.0",           # user agent / version
    "<suggested session id>"     # optional suggested session Id
  ]
}

Response from pool:

{
  "id": 1,                 # same as "id" in the request
  "result": [
    [
      [
        "mining.notify",
        "extra1hex"        # session ID, often just equal to the extra nonce 1
      ]
    ],
    "extra1hex",           # extranonce 1 in hex
    2                      # size of extranonce2
  ],
  "error": null
}

Mining authorize

The next message is again send from the client and must have "method" field equal to "mining.authorize":

Request from client:

{
  "id": 2, 
  "method": "mining.authorize",
  "params": [
    "address.worker",  # user name, convention is to specify address and worker id
    "password"         # password
  ]
}

Response from pool:

{
  "id": 2,        # same "id" as in the request
  "result": true,
  "error": null
}

Now the connection is established. The pool n

Events pushed from pool server to clients

There are two kind of events that sent from the pool: "mining.notify" and "mining.set_difficulty".

Mining.notify

{
  "id": null,
  "method": "mining.notify",
  "params": [
    "jobId",         # jobId has to be sent back on submission
    "prevHash",      # hex encoded previous hash in block header
    "merklePrefix",  # hex encoded prefix of 
    "version",       # hex encoded block version
    "nbits",         # hex encoded nbits, difficulty target
    "ntime",         # hex encoded ntime, timestamp in block header
    false            # clean? If yes, discard old jobs.
  ]
}

Notable differences from Bitcoin's Stratum protocol:

This method has only 7 parameters instead of 9 parameters in Bitcoin's stratum protocol. Bitcoin's stratum protocol's third (coinbase 1) and fourth (coinbase 2) parameters are not necessary in Warthog. Furthermore instead of Bitcoin's Merkle branches parameter we have a Merkle prefix parameter. Merkle root is computed as follows:

Merkle root computation

Merkle root is sha256(merklePrefix + extranonce1 + extranonce2). Miners can choose extranonce2 arbitrarily (with length specified by pool) to generate different Merkle roots.

Header structure

The parameters sent in a "mining.notify" event and a 4-byte nonce selected by the miner can be used to form a header to be hashed by the miner as follows:

Bytes Meaning
1-32 prevHash
33-36 nbits
37-68 Merkle root (controlled by miner, see above)
69-72 version
73-76 ntime
77-80 nonce (chosen by miner)

Mining.set_difficulty

{
  "id": null,
  "method": "mining.set_difficulty",
  "params": [
    100000     # difficulty
  ]
}

Notable differences from Bitcoin's Stratum protocol

In contrast to Bitcoin's stratum protocol the target is just the inverse of the difficulty. In Bitcoin there is an additional factor of 2^32 involved for historical reasons. Since Warthog was written from scratch, it not carry this historical burden. This means the miner must meet the target 1/difficulty to mine a share.

Events pushed from miner to pool

Mining.submit

When the miner has found nonce and extranonce2 such that the block is

{
  "id": 4,
  "method": "mining.submit",
  "params": [
    "jobId",       # jobId from mining.notify
    "0000",        # extranonce2 hex
    "a5b378fe",    # time hex (in shifupool equal to ntime from mining.notify)
    "a28a04a2"     # nonce hex
  ]
}

Pool will reply with

{
  "id": 4,        # same "id" as in the request
  "result": true,
  "error": null
}

or report error.

Error reporting

Pool response must specify request id in the "id" field. Errors are reported by setting "result" to null and specifying error details in an array of size 3 (code, message, additional info) in the "error" field.

{
  "id": 3,
  "result": null,
  "error": [
    1,              # error code
    "error msg",    # error message
    null            # additional error information
  ]
}

01/21/2024 6:07 PM Every coin has its own stratum specification and I changed just very little. Basically we do not have coinbases but mining.notify looks like this:

{
  "id": null,
  "method": "mining.notify",
  "params": [
    "jobId",         # jobId has to be sent back on submission
    "prevHash",      # previous hash in block header
    "merklePrefix",  # prefix of 
    2,               # version
    "nbits",         # nbits, difficulty target
    "ntime",         # ntime, timestamp in block header
    false            # clean? If yes, discard old jobs.
  ]
}