stratum

package module
v0.0.0-...-57f08b7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2021 License: MIT Imports: 19 Imported by: 0

README

go-stratum-client

Stratum client implemented in Go (WIP) forked from https://github.com/gurupras/go-stratum-client
A huge thanks to gurupras for writing and releasing the original project

This is an extremely primitive stratum client implemented in Golang. Most of the code was implemented by going through the sources of cpuminer-multi and xmrig.

What can it do?

  • Connect to pool
  • Authenticate
  • Receive jobs
  • Submit results
  • Keepalive

How to use

To connect and authorize

import (
  "os"

  stratum "github.com/andtheysay/go-stratum-client"
)

sc := stratum.New()

if err := sc.Connect(poolURL); err != nil {
  handleError(err)
  os.Exit(1)
}

if err := sc.Authorize(poolUsername, poolPassword); err != nil {
  handleError(err)
  os.Exit(1)
}

For a more complete example take a look at the go-cryptonight-miner repo, in particular xmrig_cpuminer.go

Testing

To run the tests create a test-config.yaml with the pool information

pool:
username:
pass:

Then run the command

go test -run ''

To run a mock stratum server you can use the mock_server binary from the releases in this repo

First download the binary for your platform and give it the required permissions

wget https://github.com/andtheysay/go-stratum-client/releases/download/1.0/mock_server-linux-amd64
chmod +x mock_server-linux-amd64

Then run the mock server. You can specify which port to run on with the --port or -p flag

./mock_server-linux-amd64 --port=8888

What's pending

  • Full coverage of the stratum API
  • Provide an easier to understand example to showcase how to use the library

andtheysay's changes

  • Use go modules
  • Replace logrus with zap because I like zap more
  • Replace json with json-iterator to encode json quicker
  • Use andtheysay's fork of set. There are no functional changes, I just wanted to backup the repo because it is in archived mode
  • Updated the README to reflect my changes
  • Updated the README to explain how to use the library

Can I help?

Yes! Please send me PRs that can fix bugs, clean up the implementation and improve it.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	AUTH_RESPONSE_STR_1    = `{"id":1,"jsonrpc":"2.0","error":null,"result":{"id":"8bc409ea-7ca7-4073-a596-31b1c8fb9335","job":{"blob":"0101c1ef8dd405b9a2d3278fbc35ba876422c82a94dd7befec695f42848d55cab06a96e31a34b300000000b1940501101bd2f22938ce55f25ddf3c584ab6915453073f3c09a5967df6966204","job_id":"Js5ps3OcKxJUCiEjtIz54ImuNMmA","target":"b88d0600","id":"8bc409ea-7ca7-4073-a596-31b1c8fb9335"},"status":"OK"}}` + "\n"
	AUTH_RESPONSE_STR_2    = `{"id":1,"jsonrpc":"2.0","error":null,"result":{"id":"8bc409ea-7ca7-4073-a596-31b1c8fb9335","job":{"blob":"0505efcfdccb0506180897d587b02f9c97037e66ea638990b2b3a0efab7bab0bff4e3f3dfe1c7d00000000a6788e66eb9b82325f95fc7a2007d3fed7152a3590366cc2a9577dcadf3544a804","job_id":"Js5ps3OcKxJUCiEjtIz54ImuNMmA","target":"e4a63d00","id":"8bc409eb-7ca7-4073-a596-31b1c8fb9335"},"status":"OK"}}` + "\n"
	AUTH_RESPONSE_RESULT_2 = "960A7A3A1826B0AA70E8043FFE7B9E23EE2E028BBA75F3D7557CCDFF9C7F1A00"
	AUTH_RESPONSE_STR_3    = `{"id":1,"jsonrpc":"2.0","error":null,"result":{"id":"8bc409ea-7ca7-4073-a596-31b1c8fb9335","job":{"blob":"0707dde5cdd4058e415b279f8e448abc8cf5c97e5768770ad1f2699f932be50511a0925afa9df5000000007d39a83b02d50b39b6ee526555b9bef8d249e03e5871cf3e7a07fba548db00f303","job_id":"K8itLau43TcUF0oqQiq3P3rwuWRZ","target":"5c351400","id":"bd27ab2c-8b59-4486-94f2-fd0b49d173a0"},"status":"OK"}}` + "\n"
	AUTH_RESPONSE_RESULT_3 = `8dac154677f0b053b4fdf2a18fba93e45a0009805a37cd108f0fcee00b0d0600`
	AUTH_RESPONSE_STR_4    = `{"id":1,"jsonrpc":"2.0","error":null,"result":{"id":"73292077-4cb3-4d26-80ec-93d3805c448f","job":{"blob":"0707efeccdd405566a7baee167953d5d3754d82087011228d2bdb36ac0b2798ebb8a264ada03aa00000000041a4cfc113c9be9ce1d7214298837b250a5ea70396dd166681d6dbaa1eb9b2703","job_id":"FF0V3jespRJxKE+aNV/rAFXL6YXu","target":"9bc42000","id":"73292077-4cb3-4d26-80ec-93d3805c448f"},"status":"OK"}}` + "\n"
	AUTH_RESPONSE_RESULT_4 = `46cfea7d7afe4739d517783e3b4b78a48fe173288cd2926a774cc71c94881b00`
	TEST_JOB_STR_1         = `{"jsonrpc":"2.0","method":"job","params":{"blob":"0101dab597d4059fdcc43a65bca7d58238708e97dbe59f21030314c55278c42cbb9ae13ac2e44b00000000b0d68bd268662790c0aae0e79bbdd6c4fd6dabf11485415239930936708e38df07","job_id":"jhUv6SY9RB0Pv+QzyfoZ9sg0Yg1d","target":"877d0200","id":"7ec63ee3-21ae-45ee-abd7-fc44c01508e7"}}` + "\n"
	TEST_JOB_STR_2         = `{"jsonrpc":"2.0","method":"job","params":{"blob":"0101c08affd405a2c58d548dc8b0ad847ef2f9714c55c23f6071256529e6573cda8260d4dc674b000000004485013199e5a45b6873d8dbca3c410d634210c8ff08cc5dd3aae805c85964b801","job_id":"2RnPDwXHTUl5yaCyvFaAgbnJ5MO7","target":"9e0f0800","id":"56bc1d76-0a4a-485a-b77f-c044af2e6df4"}}` + "\n"
	TEST_JOB_STR_3         = `{"jsonrpc":"2.0","method":"job","params":{"blob":"0101c08affd405a2c58d548dc8b0ad847ef2f9714c55c23f6071256529e6573cda8260d4dc674b000000004761695040784182c8a14a3dd204070f23704009e6045415315159e83e4b147b01","job_id":"P7FkijwsW3aQEYobjOHSg1seWFU8","target":"134e0100","id":"e5cf2a03-5072-43b4-b715-5ae94ef1e3bb"}}` + "\n"
	TEST_JOB_STR_4         = `{"jsonrpc":"2.0","method":"job","params":{"blob":"0101c08affd405a2c58d548dc8b0ad847ef2f9714c55c23f6071256529e6573cda8260d4dc674b00000000084ded521877d16cd47290ca02edac705302c367583dc3a71de3a245e76298f501","job_id":"s2reLql9Nqek+80xMNlz8DSwV8pw","target":"df4e0100","id":"e5cf2a03-5072-43b4-b715-5ae94ef1e3bb"}}` + "\n"
	TEST_JOB_STR_5         = `{"jsonrpc":"2.0","method":"job","params":{"blob":"0101c08affd405a2c58d548dc8b0ad847ef2f9714c55c23f6071256529e6573cda8260d4dc674b000000003226efc9748eba339df68d8545bcda69734cc5f2b97860c025f5543e7eaa764b01","job_id":"8A4/5aRUJLkUlyZCCU+yoKVMAS3q","target":"08080800","id":"4aac1d76-0a4a-485a-b77f-c044af2e6df4"}}` + "\n"
	RESULT_JOB_STR_5       = `39916040d5d411d5edcb1a1f90cf133b6dc7c0be05c39b526c3312cadcd40000`
	TEST_JOB_STR           = TEST_JOB_STR_5
	RESULT_JOB_STR         = RESULT_JOB_STR_5

	TestAuthResponses = []string{AUTH_RESPONSE_STR_1, AUTH_RESPONSE_STR_2, AUTH_RESPONSE_STR_3, AUTH_RESPONSE_STR_4}
	TestJobs          = []string{TEST_JOB_STR_1, TEST_JOB_STR_2, TEST_JOB_STR_3, TEST_JOB_STR_4, TEST_JOB_STR_5}
)
View Source
var (
	KeepAliveDuration time.Duration = 60 * time.Second
)

Functions

func BinToHex

func BinToHex(bin []byte) (string, error)

func BinToStr

func BinToStr(bin []byte) string

func HexToBin

func HexToBin(hex string, size int) ([]byte, error)

func MyCaller

func MyCaller() string

MyCaller returns the caller of the function that called it Used for debugging stratum locks Taken from https://stackoverflow.com/a/35213181/1761555

func WorkCopy

func WorkCopy(dest *Work, src *Work)

Types

type ClientRequest

type ClientRequest struct {
	Conn    net.Conn
	Request *Request
}

type Request

type Request struct {
	MessageID    interface{} `json:"id"`
	RemoteMethod string      `json:"method"`
	Parameters   interface{} `json:"params"`
}

func NewRequest

func NewRequest(id int, method string, args interface{}) *Request

func (*Request) JsonRPCString

func (r *Request) JsonRPCString() (string, error)

type Response

type Response struct {
	MessageID interface{}            `json:"id"`
	Result    map[string]interface{} `json:"result"`
	Error     *StratumError          `json:"error"`
}

func OkResponse

func OkResponse(r *Request) (*Response, error)

OkResponse generates a response with the following format: {"id": "<request.MessageID>", "error": null, "result": {"status": "OK"}}

func ParseResponse

func ParseResponse(b []byte) (*Response, error)

func (*Response) String

func (r *Response) String() string

type StratumContext

type StratumContext struct {
	net.Conn
	sync.Mutex

	SessionID         string
	KeepAliveDuration time.Duration
	Work              *Work

	LastSubmittedWork *Work
	// contains filtered or unexported fields
}

func New

func New() *StratumContext

func (*StratumContext) Authorize

func (sc *StratumContext) Authorize(username, password string) error

func (*StratumContext) Call

func (sc *StratumContext) Call(serviceMethod string, args interface{}) (*Request, error)

Call issues a JSONRPC request for the specified serviceMethod. It works by calling CallLocked while holding the StratumContext lock

func (*StratumContext) CallLocked

func (sc *StratumContext) CallLocked(serviceMethod string, args interface{}) (*Request, error)

CallLocked issues a JSONRPC request for the specified serviceMethod. The StratumContext lock is expected to be held by the caller

func (*StratumContext) Connect

func (sc *StratumContext) Connect(addr string) error

func (*StratumContext) GetJob

func (sc *StratumContext) GetJob() error

func (*StratumContext) Lock

func (sc *StratumContext) Lock()

func (*StratumContext) NotifyNewWork

func (sc *StratumContext) NotifyNewWork(work *Work)

func (*StratumContext) NotifyResponse

func (sc *StratumContext) NotifyResponse(response *Response)

func (*StratumContext) NotifySubmit

func (sc *StratumContext) NotifySubmit(data interface{})

func (*StratumContext) ReadJSON

func (sc *StratumContext) ReadJSON() (map[string]interface{}, error)

func (*StratumContext) ReadLine

func (sc *StratumContext) ReadLine() (string, error)

func (*StratumContext) ReadResponse

func (sc *StratumContext) ReadResponse() (*Response, error)

func (*StratumContext) Reconnect

func (sc *StratumContext) Reconnect()

func (*StratumContext) RegisterResponseListener

func (sc *StratumContext) RegisterResponseListener(rChan chan *Response)

func (*StratumContext) RegisterSubmitListener

func (sc *StratumContext) RegisterSubmitListener(sChan chan interface{})

func (*StratumContext) RegisterWorkListener

func (sc *StratumContext) RegisterWorkListener(workChan chan *Work)

func (*StratumContext) RunHandleMessages

func (sc *StratumContext) RunHandleMessages()

func (*StratumContext) RunKeepAlive

func (sc *StratumContext) RunKeepAlive()

func (*StratumContext) SubmitWork

func (sc *StratumContext) SubmitWork(work *Work, hash string) error

func (*StratumContext) Unlock

func (sc *StratumContext) Unlock()

type StratumError

type StratumError struct {
	Code      StratumErrorCode `json:"code"`
	Message   string           `json:"message"`
	Traceback interface{}      `json:"traceback"`
}

func (*StratumError) Error

func (se *StratumError) Error() string

type StratumErrorCode

type StratumErrorCode int
const (
	STRATUM_ERROR_UNKNOWN                StratumErrorCode = -1
	STRATUM_ERROR_SERVICE                StratumErrorCode = -2
	STRATUM_ERROR_METHOD                 StratumErrorCode = -3
	STRATUM_ERROR_FEE_REQUIRED           StratumErrorCode = -10
	STRATUM_ERROR_SIGNATURE_REQUIRED     StratumErrorCode = -20
	STRATUM_ERROR_SIGNATURE_UNAVAILABLE  StratumErrorCode = -21
	STRATUM_ERROR_UNKNOWN_SIGNATURE_TYPE StratumErrorCode = -22
	STRATUM_ERROR_BAD_SIGNATURE          StratumErrorCode = -23
)

type StratumOnWorkHandler

type StratumOnWorkHandler func(work *Work)

type TestServer

type TestServer struct {
	*stoppablenetlistener.StoppableNetListener
	RequestChan chan *ClientRequest
	EventChan   chan interface{}
	// contains filtered or unexported fields
}

func NewTestServer

func NewTestServer(port int) (*TestServer, error)

NewTestServer creates a new test server on the specified port. Throws an error if it was unable to bind to the specified port.

func (*TestServer) RandomAuthResponse

func (ts *TestServer) RandomAuthResponse() (*Response, error)

RandomAuthResponse sends back one of the hard-coded response strings consisting of a successful result and a job

func (*TestServer) RandomJob

func (ts *TestServer) RandomJob() (*Request, error)

RandomJob doesn't really generate random jobs. Has a list of jobs that it iterates over in order

type TsErrorEvent

type TsErrorEvent struct {
	*ClientRequest
	// contains filtered or unexported fields
}

type TsMessageEvent

type TsMessageEvent struct {
	Method string
	*ClientRequest
	DefaultResponse *Response
}

type Work

type Work struct {
	Data       WorkData
	Target     uint64 `json:"target"`
	JobID      string `json:"job_id"`
	NoncePtr   *uint32
	Difficulty float64 `json:"difficulty"`
	XNonce2    string
	Size       int
}

func NewWork

func NewWork() *Work

func ParseWork

func ParseWork(args map[string]interface{}) (*Work, error)

func ParseWorkFromResponse

func ParseWorkFromResponse(r *Response) (*Work, error)

type WorkData

type WorkData []byte

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL