From 94dbbbe6dc3ab1a1180608b083eea794113d0464 Mon Sep 17 00:00:00 2001 From: slush0 Date: Tue, 19 May 2015 19:38:54 +0200 Subject: [PATCH] Stub of binary stratum implementation --- build_pb.sh | 8 + client2.py | 2 +- protob/stratum.proto | 71 ++++++ stratum/protobuf_mapping.py | 46 ++++ stratum/protocol_binary.py | 356 ++++++++++++++++++++++++++ stratum/socket_transport.py | 3 +- stratum/stratum_pb2.py | 495 ++++++++++++++++++++++++++++++++++++ 7 files changed, 979 insertions(+), 2 deletions(-) create mode 100755 build_pb.sh create mode 100644 protob/stratum.proto create mode 100644 stratum/protobuf_mapping.py create mode 100644 stratum/protocol_binary.py create mode 100644 stratum/stratum_pb2.py diff --git a/build_pb.sh b/build_pb.sh new file mode 100755 index 0000000..967c130 --- /dev/null +++ b/build_pb.sh @@ -0,0 +1,8 @@ +#!/bin/bash +CURDIR=$(pwd) + +cd $CURDIR/protob + +for i in stratum ; do + protoc --python_out=../stratum/ -I/usr/include -I. $i.proto +done diff --git a/client2.py b/client2.py index 02794e5..4cfdf22 100644 --- a/client2.py +++ b/client2.py @@ -27,7 +27,7 @@ s = time.time() for x in range(n): - r = urllib2.Request('http://california.stratum.bitcoin.cz:8000', data, headers) + r = urllib2.Request('http://stratum.bitcoin.cz:3333', data, headers) # r = urllib2.Request('http://localhost:8000', data, headers) resp = urllib2.urlopen(r) diff --git a/protob/stratum.proto b/protob/stratum.proto new file mode 100644 index 0000000..66eb40c --- /dev/null +++ b/protob/stratum.proto @@ -0,0 +1,71 @@ +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.stratum.protobuf"; +option java_outer_classname = "StratumProtobuf"; + +import "google/protobuf/descriptor.proto"; + +extend google.protobuf.EnumValueOptions { + optional string json = 50002; +} + +/** + * Mapping between Stratum wire identifier (uint) and a protobuf message + */ +enum MessageType { + MessageType_Subscribe = 10 [(json)='mining.subscribe']; + MessageType_Authorize = 11 [(json)='mining.authorize']; + MessageType_Notify = 12 [(json)='mining.notify']; + MessageType_Submit = 13 [(json)='mining.submit']; + MessageType_SetDifficulty = 20 [(json)='mining.set_difficulty']; + MessageType_Reconnect = 21 [(json)='client.reconnect']; + MessageType_GetVersion = 22 [(json)='client.get_version']; + MessageType_Version = 23; +} + +message Subscribe { + required uint32 id = 1; +} + +message Authorize { + required uint32 id = 1; + required bytes worker_name = 2; + optional bytes password = 3; +} + +message Notify { + optional bytes job_id = 1; + required bytes prevhash = 2; + required bytes coinbase1 = 3; + optional bytes coinbase2 = 4; + repeated bytes merkle_branch = 5; + required bytes version = 6; + required bytes nbits = 7; + required bytes ntime = 8; + optional bool clean_jobs = 9; +} + +message Submit { + required uint32 id = 1; + optional bytes worker_name = 2; + optional bytes job_id = 3; + required bytes extranonce2 = 4; + required bytes ntime = 5; + required bytes nonce = 6; +} + +message SetDifficulty { + required uint64 difficulty = 1; +} + +message Reconnect { + optional bytes hostname = 1; + optional int32 port = 2; + optional int32 wait = 3; +} + +message GetVersion { + required uint32 id = 1; +} + +message Version {} + diff --git a/stratum/protobuf_mapping.py b/stratum/protobuf_mapping.py new file mode 100644 index 0000000..d941285 --- /dev/null +++ b/stratum/protobuf_mapping.py @@ -0,0 +1,46 @@ +import stratum_pb2 as proto + +map_type_to_class = {} +map_class_to_type = {} + +map_class_to_json = {} +map_json_to_class = {} + +def build_map(): + for msg_type, i in proto.MessageType.items(): + msg_name = msg_type.replace('MessageType_', '') + msg_class = getattr(proto, msg_name) + + map_type_to_class[i] = msg_class + map_class_to_type[msg_class] = i + + json_method = proto.MessageType.DESCRIPTOR.values_by_number[i].GetOptions().Extensions[proto.json] + + map_class_to_json[msg_class] = json_method + map_json_to_class[json_method] = msg_class + +def get_type(msg): + return map_class_to_type[msg.__class__] + +def get_class(t): + return map_type_to_class[t] + +def get_class_by_json(j): + return map_json_to_class[j] + +def get_json_by_class(msg): + return map_class_to_json[msg.__class__] + +def check_missing(): + from google.protobuf import reflection + + types = [getattr(proto, item) for item in dir(proto) + if issubclass(getattr(proto, item).__class__, reflection.GeneratedProtocolMessageType)] + + missing = list(set(types) - set(map_type_to_class.values())) + + if len(missing): + raise Exception("Following protobuf messages are not defined in mapping: %s" % missing) + +build_map() +check_missing() diff --git a/stratum/protocol_binary.py b/stratum/protocol_binary.py new file mode 100644 index 0000000..366abd8 --- /dev/null +++ b/stratum/protocol_binary.py @@ -0,0 +1,356 @@ +import struct +import time +import socket + +from twisted.protocols.basic import IntNStringReceiver +from twisted.internet import defer, error +from twisted.python.failure import Failure + +# import services +import stats +import signature +import custom_exceptions +import connection_registry +import settings + +import stratum_pb2 as protob +import protobuf_mapping + +import logger +log = logger.get_logger('protocol-binary') + +class RequestCounter(object): + def __init__(self): + self.on_finish = defer.Deferred() + self.counter = 0 + + def set_count(self, cnt): + self.counter = cnt + + def decrease(self): + self.counter -= 1 + if self.counter <= 0: + self.finish() + + def finish(self): + if not self.on_finish.called: + self.on_finish.callback(True) + +def obj2protob(method, data): + res = protobuf_mapping.get_class_by_json(method)() + + if method == 'mining.subscribe': + pass + else: + raise Exception("Unknown object type") + + return res + +def protob2obj(msg): + method = protobuf_mapping.get_json_by_class(msg) + res = '' + return method, res + +class ProtocolBinary(IntNStringReceiver): + structFormat = "!I" + prefixLength = struct.calcsize(structFormat) + + def _get_id(self): + self.request_id += 1 + return self.request_id + + def _get_ip(self): + return self.proxied_ip or self.transport.getPeer().host + + def get_ident(self): + # Get global unique ID of connection + return "%s:%s" % (self.proxied_ip or self.transport.getPeer().host, "%x" % id(self)) + + def get_session(self): + return self.session + + def connectionMade(self): + try: + self.transport.setTcpNoDelay(True) + self.transport.setTcpKeepAlive(True) + self.transport.socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 120) # Seconds before sending keepalive probes + self.transport.socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 1) # Interval in seconds between keepalive probes + self.transport.socket.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 5) # Failed keepalive probles before declaring other end dead + except: + # Supported only by the socket transport, + # but there's really no better place in code to trigger this. + pass + + # Read settings.TCP_PROXY_PROTOCOL documentation + self.expect_tcp_proxy_protocol_header = self.factory.__dict__.get('tcp_proxy_protocol_enable', False) + self.proxied_ip = None # IP obtained from TCP proxy protocol + + self.request_id = 0 + self.lookup_table = {} + self.event_handler = self.factory.event_handler() + self.on_disconnect = defer.Deferred() + self.on_finish = None # Will point to defer which is called + # once all client requests are processed + + # Initiate connection session + self.session = {} + + stats.PeerStats.client_connected(self._get_ip()) + log.debug("Connected %s" % self.transport.getPeer().host) + connection_registry.ConnectionRegistry.add_connection(self) + + def transport_write(self, data): + '''Overwrite this if transport needs some extra care about data written + to the socket, like adding message format in websocket.''' + try: + self.transport.write(data) + except AttributeError: + # Transport is disconnected + pass + + def connectionLost(self, reason): + if self.on_disconnect != None and not self.on_disconnect.called: + self.on_disconnect.callback(self) + self.on_disconnect = None + + stats.PeerStats.client_disconnected(self._get_ip()) + connection_registry.ConnectionRegistry.remove_connection(self) + self.transport = None # Fixes memory leak (cyclic reference) + + def writeRequest(self, payload, is_notification=False): + if not is_notification: + request_id = self._get_id() + payload.id = request_id + + serialized = payload.SerializeToString() + self.transport_write("%s\n" % serialized) + return request_id + + def writeResponse(self, payload): + serialized = payload.SerializeToString() + + + def writeJsonRequest(self, method, params, is_notification=False): + payload = obj2protob(method, params) + return self.writeRequest(payload) + + def writeJsonResponse(self, data, message_id, use_signature=False, sign_method='', sign_params=[]): + if use_signature: + raise NotImplementedError() + + else: + serialized = json.dumps({'id': message_id, 'result': data, 'error': None}) + + if self.factory.debug: + log.debug("< %s" % serialized) + + self.transport_write("%s\n" % serialized) + + def writeJsonError(self, code, message, traceback, message_id, use_signature=False, sign_method='', sign_params=[]): + if use_signature: + serialized = signature.jsonrpc_dumps_sign(self.factory.signing_key, self.factory.signing_id, False, \ + message_id, sign_method, sign_params, None, (code, message, traceback)) + else: + serialized = json.dumps({'id': message_id, 'result': None, 'error': (code, message, traceback)}) + + self.transport_write("%s\n" % serialized) + + def writeGeneralError(self, message, code=-1): + log.error(message) + return self.writeJsonError(code, message, None, None) + + def process_response(self, data, message_id, sign_method, sign_params, request_counter): + self.writeJsonResponse(data.result, message_id, data.sign, sign_method, sign_params) + request_counter.decrease() + + + def process_failure(self, failure, message_id, sign_method, sign_params, request_counter): + if not isinstance(failure.value, custom_exceptions.ServiceException): + # All handled exceptions should inherit from ServiceException class. + # Throwing other exception class means that it is unhandled error + # and we should log it. + log.exception(failure) + + sign = False + code = getattr(failure.value, 'code', -1) + + # if isinstance(failure.value, services.ResultObject): + # # Strip ResultObject + # sign = failure.value.sign + # failure.value = failure.value.result + + if message_id != None: + # Other party doesn't care of error state for notifications + if settings.DEBUG: + tb = failure.getBriefTraceback() + else: + tb = None + self.writeJsonError(code, failure.getErrorMessage(), tb, message_id, sign, sign_method, sign_params) + + request_counter.decrease() + + def dataReceived(self, data, request_counter=None): + '''Original code from Twisted, hacked for request_counter proxying. + request_counter is hack for HTTP transport, didn't found cleaner solution how + to indicate end of request processing in asynchronous manner. + + TODO: This would deserve some unit test to be sure that future twisted versions + will work nicely with this.''' + + if request_counter == None: + request_counter = RequestCounter() + + lines = (self._buffer + data).split(self.delimiter) + self._buffer = lines.pop(-1) + request_counter.set_count(len(lines)) + self.on_finish = request_counter.on_finish + + for line in lines: + if self.transport.disconnecting: + request_counter.finish() + return + if len(line) > self.MAX_LENGTH: + request_counter.finish() + return self.lineLengthExceeded(line) + else: + try: + self.lineReceived(line, request_counter) + except Exception as exc: + request_counter.finish() + # log.exception("Processing of message failed") + log.warning("Failed message: %s from %s" % (str(exc), self._get_ip())) + return error.ConnectionLost('Processing of message failed') + + if len(self._buffer) > self.MAX_LENGTH: + request_counter.finish() + return self.lineLengthExceeded(self._buffer) + + def lineReceived(self, line, request_counter): + if self.expect_tcp_proxy_protocol_header: + # This flag may be set only for TCP transport AND when TCP_PROXY_PROTOCOL + # is enabled in server config. Then we expect the first line of the stream + # may contain proxy metadata. + + # We don't expect this header during this session anymore + self.expect_tcp_proxy_protocol_header = False + + if line.startswith('PROXY'): + self.proxied_ip = line.split()[2] + + # Let's process next line + request_counter.decrease() + return + + try: + message = json.loads(line) + except: + # self.writeGeneralError("Cannot decode message '%s'" % line) + request_counter.finish() + raise custom_exceptions.ProtocolException("Cannot decode message '%s'" % line.strip()) + + if self.factory.debug: + log.debug("> %s" % message) + + msg_id = message.get('id', 0) + msg_method = message.get('method') + msg_params = message.get('params') + msg_result = message.get('result') + msg_error = message.get('error') + + if msg_method: + # It's a RPC call or notification + try: + result = self.event_handler._handle_event(msg_method, msg_params, connection_ref=self) + if result == None and msg_id != None: + # event handler must return Deferred object or raise an exception for RPC request + raise custom_exceptions.MethodNotFoundException("Event handler cannot process method '%s'" % msg_method) + except: + failure = Failure() + self.process_failure(failure, msg_id, msg_method, msg_params, request_counter) + + else: + if msg_id == None: + # It's notification, don't expect the response + request_counter.decrease() + else: + # It's a RPC call + result.addCallback(self.process_response, msg_id, msg_method, msg_params, request_counter) + result.addErrback(self.process_failure, msg_id, msg_method, msg_params, request_counter) + + elif msg_id: + # It's a RPC response + # Perform lookup to the table of waiting requests. + request_counter.decrease() + + try: + meta = self.lookup_table[msg_id] + del self.lookup_table[msg_id] + except KeyError: + # When deferred object for given message ID isn't found, it's an error + raise custom_exceptions.ProtocolException("Lookup for deferred object for message ID '%s' failed." % msg_id) + + # If there's an error, handle it as errback + # If both result and error are null, handle it as a success with blank result + if msg_error != None: + meta['defer'].errback(custom_exceptions.RemoteServiceException(msg_error[0], msg_error[1], msg_error[2])) + else: + meta['defer'].callback(msg_result) + + else: + request_counter.decrease() + raise custom_exceptions.ProtocolException("Cannot handle message '%s'" % line) + + def rpc(self, method, params, is_notification=False): + ''' + This method performs remote RPC call. + + If method should expect an response, it store + request ID to lookup table and wait for corresponding + response message. + ''' + + request_id = self.writeJsonRequest(method, params, is_notification) + + if is_notification: + return + + d = defer.Deferred() + self.lookup_table[request_id] = {'defer': d, 'method': method, 'params': params} + return d + +class ClientProtocolBinary(ProtocolBinary): + def connectionMade(self): + ProtocolBinary.connectionMade(self) + self.factory.client = self + + if self.factory.timeout_handler: + self.factory.timeout_handler.cancel() + self.factory.timeout_handler = None + + if isinstance(getattr(self.factory, 'after_connect', None), list): + log.debug("Resuming connection: %s" % self.factory.after_connect) + for cmd in self.factory.after_connect: + self.rpc(cmd[0], cmd[1]) + + if not self.factory.on_connect.called: + d = self.factory.on_connect + self.factory.on_connect = defer.Deferred() + d.callback(self.factory) + + + # d = self.rpc('node.get_peers', []) + # d.addCallback(self.factory.add_peers) + + def connectionLost(self, reason): + self.factory.client = None + + if self.factory.timeout_handler: + self.factory.timeout_handler.cancel() + self.factory.timeout_handler = None + + if not self.factory.on_disconnect.called: + d = self.factory.on_disconnect + self.factory.on_disconnect = defer.Deferred() + d.callback(self.factory) + + Protocol.connectionLost(self, reason) diff --git a/stratum/socket_transport.py b/stratum/socket_transport.py index 0484552..7336d1e 100644 --- a/stratum/socket_transport.py +++ b/stratum/socket_transport.py @@ -5,6 +5,7 @@ import socksclient import custom_exceptions from protocol import Protocol, ClientProtocol +from protocol_binary import ProtocolBinary from event_handler import GenericEventHandler import logger @@ -21,7 +22,7 @@ def __init__(self, debug=False, signing_key=None, signing_id=None, event_handler self.signing_key = signing_key self.signing_id = signing_id self.event_handler = event_handler - self.protocol = Protocol + self.protocol = ProtocolBinary # Read settings.TCP_PROXY_PROTOCOL documentation self.tcp_proxy_protocol_enable = tcp_proxy_protocol_enable diff --git a/stratum/stratum_pb2.py b/stratum/stratum_pb2.py new file mode 100644 index 0000000..22742a5 --- /dev/null +++ b/stratum/stratum_pb2.py @@ -0,0 +1,495 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stratum.proto + +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + + +import google.protobuf.descriptor_pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='stratum.proto', + package='', + serialized_pb='\n\rstratum.proto\x1a google/protobuf/descriptor.proto\"\x17\n\tSubscribe\x12\n\n\x02id\x18\x01 \x02(\r\">\n\tAuthorize\x12\n\n\x02id\x18\x01 \x02(\r\x12\x13\n\x0bworker_name\x18\x02 \x02(\x0c\x12\x10\n\x08password\x18\x03 \x01(\x0c\"\xaa\x01\n\x06Notify\x12\x0e\n\x06job_id\x18\x01 \x01(\x0c\x12\x10\n\x08prevhash\x18\x02 \x02(\x0c\x12\x11\n\tcoinbase1\x18\x03 \x02(\x0c\x12\x11\n\tcoinbase2\x18\x04 \x01(\x0c\x12\x15\n\rmerkle_branch\x18\x05 \x03(\x0c\x12\x0f\n\x07version\x18\x06 \x02(\x0c\x12\r\n\x05nbits\x18\x07 \x02(\x0c\x12\r\n\x05ntime\x18\x08 \x02(\x0c\x12\x12\n\nclean_jobs\x18\t \x01(\x08\"l\n\x06Submit\x12\n\n\x02id\x18\x01 \x02(\r\x12\x13\n\x0bworker_name\x18\x02 \x01(\x0c\x12\x0e\n\x06job_id\x18\x03 \x01(\x0c\x12\x13\n\x0b\x65xtranonce2\x18\x04 \x02(\x0c\x12\r\n\x05ntime\x18\x05 \x02(\x0c\x12\r\n\x05nonce\x18\x06 \x02(\x0c\"#\n\rSetDifficulty\x12\x12\n\ndifficulty\x18\x01 \x02(\x04\"9\n\tReconnect\x12\x10\n\x08hostname\x18\x01 \x01(\x0c\x12\x0c\n\x04port\x18\x02 \x01(\x05\x12\x0c\n\x04wait\x18\x03 \x01(\x05\"\x18\n\nGetVersion\x12\n\n\x02id\x18\x01 \x02(\r\"\t\n\x07Version*\xfd\x02\n\x0bMessageType\x12/\n\x15MessageType_Subscribe\x10\n\x1a\x14\x92\xb5\x18\x10mining.subscribe\x12/\n\x15MessageType_Authorize\x10\x0b\x1a\x14\x92\xb5\x18\x10mining.authorize\x12)\n\x12MessageType_Notify\x10\x0c\x1a\x11\x92\xb5\x18\rmining.notify\x12)\n\x12MessageType_Submit\x10\r\x1a\x11\x92\xb5\x18\rmining.submit\x12\x38\n\x19MessageType_SetDifficulty\x10\x14\x1a\x19\x92\xb5\x18\x15mining.set_difficulty\x12/\n\x15MessageType_Reconnect\x10\x15\x1a\x14\x92\xb5\x18\x10\x63lient.reconnect\x12\x32\n\x16MessageType_GetVersion\x10\x16\x1a\x16\x92\xb5\x18\x12\x63lient.get_version\x12\x17\n\x13MessageType_Version\x10\x17:1\n\x04json\x12!.google.protobuf.EnumValueOptions\x18\xd2\x86\x03 \x01(\tB3\n com.satoshilabs.stratum.protobufB\x0fStratumProtobuf') + +_MESSAGETYPE = _descriptor.EnumDescriptor( + name='MessageType', + full_name='MessageType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='MessageType_Subscribe', index=0, number=10, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020mining.subscribe'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_Authorize', index=1, number=11, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020mining.authorize'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_Notify', index=2, number=12, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\rmining.notify'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_Submit', index=3, number=13, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\rmining.submit'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_SetDifficulty', index=4, number=20, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\025mining.set_difficulty'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_Reconnect', index=5, number=21, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020client.reconnect'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_GetVersion', index=6, number=22, + options=_descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\022client.get_version'), + type=None), + _descriptor.EnumValueDescriptor( + name='MessageType_Version', index=7, number=23, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=557, + serialized_end=938, +) + +MessageType = enum_type_wrapper.EnumTypeWrapper(_MESSAGETYPE) +MessageType_Subscribe = 10 +MessageType_Authorize = 11 +MessageType_Notify = 12 +MessageType_Submit = 13 +MessageType_SetDifficulty = 20 +MessageType_Reconnect = 21 +MessageType_GetVersion = 22 +MessageType_Version = 23 + +JSON_FIELD_NUMBER = 50002 +json = _descriptor.FieldDescriptor( + name='json', full_name='json', index=0, + number=50002, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=True, extension_scope=None, + options=None) + + +_SUBSCRIBE = _descriptor.Descriptor( + name='Subscribe', + full_name='Subscribe', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Subscribe.id', index=0, + number=1, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=51, + serialized_end=74, +) + + +_AUTHORIZE = _descriptor.Descriptor( + name='Authorize', + full_name='Authorize', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Authorize.id', index=0, + number=1, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='worker_name', full_name='Authorize.worker_name', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='password', full_name='Authorize.password', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=76, + serialized_end=138, +) + + +_NOTIFY = _descriptor.Descriptor( + name='Notify', + full_name='Notify', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='job_id', full_name='Notify.job_id', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='prevhash', full_name='Notify.prevhash', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='coinbase1', full_name='Notify.coinbase1', index=2, + number=3, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='coinbase2', full_name='Notify.coinbase2', index=3, + number=4, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='merkle_branch', full_name='Notify.merkle_branch', index=4, + number=5, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='version', full_name='Notify.version', index=5, + number=6, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='nbits', full_name='Notify.nbits', index=6, + number=7, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='ntime', full_name='Notify.ntime', index=7, + number=8, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='clean_jobs', full_name='Notify.clean_jobs', index=8, + number=9, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=141, + serialized_end=311, +) + + +_SUBMIT = _descriptor.Descriptor( + name='Submit', + full_name='Submit', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Submit.id', index=0, + number=1, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='worker_name', full_name='Submit.worker_name', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='job_id', full_name='Submit.job_id', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='extranonce2', full_name='Submit.extranonce2', index=3, + number=4, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='ntime', full_name='Submit.ntime', index=4, + number=5, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='nonce', full_name='Submit.nonce', index=5, + number=6, type=12, cpp_type=9, label=2, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=313, + serialized_end=421, +) + + +_SETDIFFICULTY = _descriptor.Descriptor( + name='SetDifficulty', + full_name='SetDifficulty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='difficulty', full_name='SetDifficulty.difficulty', index=0, + number=1, type=4, cpp_type=4, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=423, + serialized_end=458, +) + + +_RECONNECT = _descriptor.Descriptor( + name='Reconnect', + full_name='Reconnect', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='hostname', full_name='Reconnect.hostname', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value="", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='port', full_name='Reconnect.port', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='wait', full_name='Reconnect.wait', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=460, + serialized_end=517, +) + + +_GETVERSION = _descriptor.Descriptor( + name='GetVersion', + full_name='GetVersion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='GetVersion.id', index=0, + number=1, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=519, + serialized_end=543, +) + + +_VERSION = _descriptor.Descriptor( + name='Version', + full_name='Version', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=545, + serialized_end=554, +) + +DESCRIPTOR.message_types_by_name['Subscribe'] = _SUBSCRIBE +DESCRIPTOR.message_types_by_name['Authorize'] = _AUTHORIZE +DESCRIPTOR.message_types_by_name['Notify'] = _NOTIFY +DESCRIPTOR.message_types_by_name['Submit'] = _SUBMIT +DESCRIPTOR.message_types_by_name['SetDifficulty'] = _SETDIFFICULTY +DESCRIPTOR.message_types_by_name['Reconnect'] = _RECONNECT +DESCRIPTOR.message_types_by_name['GetVersion'] = _GETVERSION +DESCRIPTOR.message_types_by_name['Version'] = _VERSION + +class Subscribe(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _SUBSCRIBE + + # @@protoc_insertion_point(class_scope:Subscribe) + +class Authorize(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _AUTHORIZE + + # @@protoc_insertion_point(class_scope:Authorize) + +class Notify(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _NOTIFY + + # @@protoc_insertion_point(class_scope:Notify) + +class Submit(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _SUBMIT + + # @@protoc_insertion_point(class_scope:Submit) + +class SetDifficulty(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _SETDIFFICULTY + + # @@protoc_insertion_point(class_scope:SetDifficulty) + +class Reconnect(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _RECONNECT + + # @@protoc_insertion_point(class_scope:Reconnect) + +class GetVersion(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _GETVERSION + + # @@protoc_insertion_point(class_scope:GetVersion) + +class Version(_message.Message): + __metaclass__ = _reflection.GeneratedProtocolMessageType + DESCRIPTOR = _VERSION + + # @@protoc_insertion_point(class_scope:Version) + +google.protobuf.descriptor_pb2.EnumValueOptions.RegisterExtension(json) + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n com.satoshilabs.stratum.protobufB\017StratumProtobuf') +_MESSAGETYPE.values_by_name["MessageType_Subscribe"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_Subscribe"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020mining.subscribe') +_MESSAGETYPE.values_by_name["MessageType_Authorize"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_Authorize"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020mining.authorize') +_MESSAGETYPE.values_by_name["MessageType_Notify"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_Notify"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\rmining.notify') +_MESSAGETYPE.values_by_name["MessageType_Submit"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_Submit"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\rmining.submit') +_MESSAGETYPE.values_by_name["MessageType_SetDifficulty"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_SetDifficulty"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\025mining.set_difficulty') +_MESSAGETYPE.values_by_name["MessageType_Reconnect"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_Reconnect"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\020client.reconnect') +_MESSAGETYPE.values_by_name["MessageType_GetVersion"].has_options = True +_MESSAGETYPE.values_by_name["MessageType_GetVersion"]._options = _descriptor._ParseOptions(descriptor_pb2.EnumValueOptions(), '\222\265\030\022client.get_version') +# @@protoc_insertion_point(module_scope)