13
13
14
14
from lib .hash import sha256 , hash_to_str
15
15
from lib .jsonrpc import JSONSession , RPCError , JSONRPCv2 , JSONRPC
16
+ import lib .util as util
16
17
from server .daemon import DaemonError
17
18
import server .version as version
18
19
@@ -35,7 +36,6 @@ def __init__(self, controller, kind):
35
36
self .daemon = self .bp .daemon
36
37
self .client = 'unknown'
37
38
self .client_version = (1 , )
38
- self .protocol_version = '1.0'
39
39
self .anon_logs = self .env .anon_logs
40
40
self .last_delay = 0
41
41
self .txs_sent = 0
@@ -113,19 +113,7 @@ def __init__(self, *args, **kwargs):
113
113
self .hashX_subs = {}
114
114
self .mempool_statuses = {}
115
115
self .chunk_indices = []
116
- self .electrumx_handlers = {
117
- 'blockchain.address.subscribe' : self .address_subscribe ,
118
- 'blockchain.block.get_chunk' : self .block_get_chunk ,
119
- 'blockchain.headers.subscribe' : self .headers_subscribe ,
120
- 'blockchain.numblocks.subscribe' : self .numblocks_subscribe ,
121
- 'blockchain.script_hash.subscribe' : self .script_hash_subscribe ,
122
- 'blockchain.transaction.broadcast' : self .transaction_broadcast ,
123
- 'server.add_peer' : self .add_peer ,
124
- 'server.banner' : self .banner ,
125
- 'server.features' : self .server_features ,
126
- 'server.peers.subscribe' : self .peers_subscribe ,
127
- 'server.version' : self .server_version ,
128
- }
116
+ self .set_protocol_handlers (None )
129
117
130
118
def sub_count (self ):
131
119
return len (self .hashX_subs )
@@ -164,7 +152,7 @@ async def notify(self, height, touched):
164
152
165
153
for alias_status in changed :
166
154
if len (alias_status [0 ]) == 64 :
167
- method = 'blockchain.script_hash .subscribe'
155
+ method = 'blockchain.scripthash .subscribe'
168
156
else :
169
157
method = 'blockchain.address.subscribe'
170
158
pairs .append ((method , alias_status ))
@@ -247,12 +235,12 @@ async def address_subscribe(self, address):
247
235
hashX = self .controller .address_to_hashX (address )
248
236
return await self .hashX_subscribe (hashX , address )
249
237
250
- async def script_hash_subscribe (self , script_hash ):
238
+ async def scripthash_subscribe (self , scripthash ):
251
239
'''Subscribe to a script hash.
252
240
253
- script_hash : the SHA256 hash of the script to subscribe to'''
254
- hashX = self .controller .script_hash_to_hashX ( script_hash )
255
- return await self .hashX_subscribe (hashX , script_hash )
241
+ scripthash : the SHA256 hash of the script to subscribe to'''
242
+ hashX = self .controller .scripthash_to_hashX ( scripthash )
243
+ return await self .hashX_subscribe (hashX , scripthash )
256
244
257
245
def server_features (self ):
258
246
'''Returns a dictionary of server features.'''
@@ -333,47 +321,98 @@ def server_version(self, client_name=None, protocol_version=None):
333
321
in self .client .split ('.' ))
334
322
except Exception :
335
323
pass
336
- if protocol_version is not None :
337
- self .protocol_version = protocol_version
324
+
325
+ self .log_info ('protocol version {} requested' .format (protocol_version ))
326
+ self .set_protocol_handlers (protocol_version )
327
+
338
328
return version .VERSION
339
329
340
330
async def transaction_broadcast (self , raw_tx ):
341
331
'''Broadcast a raw transaction to the network.
342
332
343
333
raw_tx: the raw transaction as a hexadecimal string'''
344
- # An ugly API: current Electrum clients only pass the raw
345
- # transaction in hex and expect error messages to be returned in
346
- # the result field. And the server shouldn't be doing the client's
347
- # user interface job here.
334
+ # This returns errors as JSON RPC errors, as is natural
348
335
try :
349
336
tx_hash = await self .daemon .sendrawtransaction ([raw_tx ])
350
337
self .txs_sent += 1
351
338
self .log_info ('sent tx: {}' .format (tx_hash ))
352
339
self .controller .sent_tx (tx_hash )
353
340
return tx_hash
354
341
except DaemonError as e :
355
- error = e .args [ 0 ]
342
+ error , = e .args
356
343
message = error ['message' ]
357
344
self .log_info ('sendrawtransaction: {}' .format (message ),
358
345
throttle = True )
346
+ raise RPCError ('the transaction was rejected by network rules.'
347
+ '\n \n {}\n [{}]' .format (message , raw_tx ))
348
+
349
+ async def transaction_broadcast_1_0 (self , raw_tx ):
350
+ '''Broadcast a raw transaction to the network.
351
+
352
+ raw_tx: the raw transaction as a hexadecimal string'''
353
+ # An ugly API: current Electrum clients only pass the raw
354
+ # transaction in hex and expect error messages to be returned in
355
+ # the result field. And the server shouldn't be doing the client's
356
+ # user interface job here.
357
+ try :
358
+ return await self .transaction_broadcast (raw_tx )
359
+ except RPCError as e :
360
+ message , = e .args
359
361
if 'non-mandatory-script-verify-flag' in message :
360
- return (
362
+ message = (
361
363
'Your client produced a transaction that is not accepted '
362
364
'by the network any more. Please upgrade to Electrum '
363
365
'2.5.1 or newer.'
364
366
)
365
367
366
- return (
367
- 'The transaction was rejected by network rules. ({})\n [{}]'
368
- .format (message , raw_tx )
369
- )
368
+ return message
369
+
370
+ def set_protocol_handlers (self , version_str ):
371
+ controller = self .controller
372
+ if version_str is None :
373
+ version_str = version .PROTOCOL_MIN
374
+ ptuple = util .protocol_tuple (version_str )
375
+ # Disconnect if requested protocol version in unsupported
376
+ if (ptuple < util .protocol_tuple (version .PROTOCOL_MIN )
377
+ or ptuple > util .protocol_tuple (version .PROTOCOL_MAX )):
378
+ self .log_info ('unsupported protocol version {}'
379
+ .format (version_str ))
380
+ raise RPCError ('unsupported protocol version: {}'
381
+ .format (version_str ), JSONRPC .FATAL_ERROR )
382
+
383
+ handlers = {
384
+ 'blockchain.address.subscribe' : self .address_subscribe ,
385
+ 'blockchain.block.get_chunk' : self .block_get_chunk ,
386
+ 'blockchain.headers.subscribe' : self .headers_subscribe ,
387
+ 'blockchain.numblocks.subscribe' : self .numblocks_subscribe ,
388
+ 'blockchain.transaction.broadcast' : self .transaction_broadcast_1_0 ,
389
+ 'blockchain.transaction.get' : controller .transaction_get_1_0 ,
390
+ 'server.add_peer' : self .add_peer ,
391
+ 'server.banner' : self .banner ,
392
+ 'server.features' : self .server_features ,
393
+ 'server.peers.subscribe' : self .peers_subscribe ,
394
+ 'server.version' : self .server_version ,
395
+ }
396
+
397
+ handlers .update (controller .electrumx_handlers )
398
+
399
+ if ptuple >= (1 , 1 ):
400
+ # Remove deprecated methods
401
+ del handlers ['blockchain.address.get_proof' ]
402
+ del handlers ['blockchain.numblocks.subscribe' ]
403
+ del handlers ['blockchain.utxo.get_address' ]
404
+ # Add new handlers
405
+ handlers .update ({
406
+ 'blockchain.scripthash.subscribe' : self .scripthash_subscribe ,
407
+ 'blockchain.transaction.broadcast' : self .transaction_broadcast ,
408
+ 'blockchain.transaction.get' : controller .transaction_get ,
409
+ })
410
+
411
+ self .electrumx_handlers = handlers
370
412
371
413
def request_handler (self , method ):
372
414
'''Return the async handler for the given request method.'''
373
- handler = self .electrumx_handlers .get (method )
374
- if not handler :
375
- handler = self .controller .electrumx_handlers .get (method )
376
- return handler
415
+ return self .electrumx_handlers .get (method )
377
416
378
417
379
418
class LocalRPC (SessionBase ):
0 commit comments