71
71
# PROPAGATION_FAILED = {'code': 33, 'message': 'Solution propagation failed'}
72
72
DUPLICATE_SOLUTION = {'code' : 34 , 'message' : 'Solution already submitted' }
73
73
74
+ ZEROED_4 : bytes = b'\0 ' * 4
75
+ ZEROED_32 : bytes = b'\0 ' * 32
76
+
74
77
75
78
class HathorCoordJob (NamedTuple ):
76
79
""" Data class used to send a job's work to Hathor Stratum.
@@ -690,6 +693,8 @@ async def submit_to_bitcoin(self, job: SingleMinerJob, work: SingleMinerWork) ->
690
693
""" Submit work to Bitcoin RPC.
691
694
"""
692
695
bitcoin_rpc = self .coordinator .bitcoin_rpc
696
+ if bitcoin_rpc is None :
697
+ return
693
698
# bitcoin_block = job.build_bitcoin_block(work) # XXX: too expensive for now
694
699
bitcoin_block_header = job .build_bitcoin_block_header (work )
695
700
block_hash = Hash (bitcoin_block_header .hash )
@@ -799,6 +804,21 @@ class BitcoinCoordJob(NamedTuple):
799
804
witness_commitment : Optional [bytes ] = None
800
805
append_to_input : bool = True
801
806
807
+ @classmethod
808
+ def zeroed (cls , merkle_path_len : int = 0 ) -> 'BitcoinCoordJob' :
809
+ return cls (
810
+ version = 0 ,
811
+ previous_block_hash = ZEROED_32 ,
812
+ coinbase_value = 0 ,
813
+ target = ZEROED_32 ,
814
+ min_time = 0 ,
815
+ size_limit = 0 ,
816
+ bits = ZEROED_4 ,
817
+ height = 0 ,
818
+ transactions = [],
819
+ merkle_path = tuple ([ZEROED_32 ] * merkle_path_len ),
820
+ )
821
+
802
822
@classmethod
803
823
def from_dict (cls , params : dict ) -> 'BitcoinCoordJob' :
804
824
r""" Convert from dict of the properties returned from Bitcoin RPC.
@@ -970,7 +990,7 @@ def make_coinbase_transaction(self, hathor_block_hash: bytes, payback_script_bit
970
990
if self .witness_commitment is not None :
971
991
segwit_output = BitcoinTransactionOutput (0 , self .witness_commitment )
972
992
outputs .append (segwit_output )
973
- coinbase_input .script_witness .append (b' \0 ' * 32 )
993
+ coinbase_input .script_witness .append (ZEROED_32 )
974
994
975
995
# append now because segwit presence may change this
976
996
inputs .append (coinbase_input )
@@ -1112,10 +1132,11 @@ class MergedMiningCoordinator:
1112
1132
MAX_XNONCE1 = 2 ** XNONCE1_SIZE - 1
1113
1133
MAX_RECONNECT_BACKOFF = 30
1114
1134
1115
- def __init__ (self , bitcoin_rpc : IBitcoinRPC , hathor_client : IHathorClient ,
1116
- payback_address_bitcoin : Optional [str ], payback_address_hathor : Optional [str ],
1117
- address_from_login : bool = True , min_difficulty : Optional [int ] = None ,
1118
- sequential_xnonce1 : bool = False , rng : Optional [Random ] = None ):
1135
+ def __init__ (self , bitcoin_rpc : IBitcoinRPC | None , hathor_client : IHathorClient ,
1136
+ payback_address_bitcoin : str | None , payback_address_hathor : str | None ,
1137
+ address_from_login : bool = True , min_difficulty : int | None = None ,
1138
+ sequential_xnonce1 : bool = False , rng : Random | None = None ,
1139
+ dummy_merkle_path_len : int | None = None ):
1119
1140
self .log = logger .new ()
1120
1141
if rng is None :
1121
1142
rng = Random ()
@@ -1146,6 +1167,7 @@ def __init__(self, bitcoin_rpc: IBitcoinRPC, hathor_client: IHathorClient,
1146
1167
self .started_at = 0.0
1147
1168
self .strip_all_transactions = False
1148
1169
self .strip_segwit_transactions = False
1170
+ self .dummy_merkle_path_len = dummy_merkle_path_len or 0
1149
1171
1150
1172
@property
1151
1173
def uptime (self ) -> float :
@@ -1187,7 +1209,10 @@ async def start(self) -> None:
1187
1209
"""
1188
1210
loop = asyncio .get_event_loop ()
1189
1211
self .started_at = time .time ()
1190
- self .update_bitcoin_block_task = loop .create_task (self .update_bitcoin_block ())
1212
+ if self .bitcoin_rpc is not None :
1213
+ self .update_bitcoin_block_task = loop .create_task (self .update_bitcoin_block ())
1214
+ else :
1215
+ self .bitcoin_coord_job = BitcoinCoordJob .zeroed (self .dummy_merkle_path_len )
1191
1216
self .update_hathor_block_task = loop .create_task (self .update_hathor_block ())
1192
1217
1193
1218
async def stop (self ) -> None :
@@ -1212,6 +1237,7 @@ async def stop(self) -> None:
1212
1237
async def update_bitcoin_block (self ) -> None :
1213
1238
""" Task that continuously polls block templates from bitcoin.get_block_template
1214
1239
"""
1240
+ assert self .bitcoin_rpc is not None
1215
1241
backoff = 1
1216
1242
longpoll_id = None
1217
1243
while True :
@@ -1350,13 +1376,14 @@ async def update_merged_block(self) -> None:
1350
1376
merkle_root = build_merkle_root (list (tx .txid for tx in block_proposal .transactions ))
1351
1377
if merkle_root != block_proposal .header .merkle_root :
1352
1378
self .log .warn ('bad merkle root' , expected = merkle_root .hex (), got = block_proposal .header .merkle_root .hex ())
1353
- error = await self .bitcoin_rpc .verify_block_proposal (block = bytes (block_proposal ))
1354
- if error is not None :
1355
- self .log .warn ('proposed block is invalid, skipping update' , error = error )
1356
- else :
1357
- self .next_merged_job = merged_job
1358
- self .update_jobs ()
1359
- self .log .debug ('merged job updated' )
1379
+ if self .bitcoin_rpc is not None :
1380
+ error = await self .bitcoin_rpc .verify_block_proposal (block = bytes (block_proposal ))
1381
+ if error is not None :
1382
+ self .log .warn ('proposed block is invalid, skipping update' , error = error )
1383
+ return
1384
+ self .next_merged_job = merged_job
1385
+ self .update_jobs ()
1386
+ self .log .debug ('merged job updated' )
1360
1387
1361
1388
def status (self ) -> dict [Any , Any ]:
1362
1389
""" Build status dict with useful metrics for use in MM Status API.
0 commit comments