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,27 @@ 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
+ if merkle_path_len > 0 :
810
+ transactions = [BitcoinRawTransaction (ZEROED_32 , ZEROED_32 , b'' )] * (2 ** merkle_path_len - 1 )
811
+ merkle_path = tuple (build_merkle_path_for_coinbase ([t .txid for t in transactions ]))
812
+ else :
813
+ transactions = []
814
+ merkle_path = tuple ()
815
+ return cls (
816
+ version = 0 ,
817
+ previous_block_hash = ZEROED_32 ,
818
+ coinbase_value = 0 ,
819
+ target = ZEROED_32 ,
820
+ min_time = 0 ,
821
+ size_limit = 0 ,
822
+ bits = ZEROED_4 ,
823
+ height = 0 ,
824
+ transactions = transactions ,
825
+ merkle_path = merkle_path ,
826
+ )
827
+
802
828
@classmethod
803
829
def from_dict (cls , params : dict ) -> 'BitcoinCoordJob' :
804
830
r""" Convert from dict of the properties returned from Bitcoin RPC.
@@ -970,7 +996,7 @@ def make_coinbase_transaction(self, hathor_block_hash: bytes, payback_script_bit
970
996
if self .witness_commitment is not None :
971
997
segwit_output = BitcoinTransactionOutput (0 , self .witness_commitment )
972
998
outputs .append (segwit_output )
973
- coinbase_input .script_witness .append (b' \0 ' * 32 )
999
+ coinbase_input .script_witness .append (ZEROED_32 )
974
1000
975
1001
# append now because segwit presence may change this
976
1002
inputs .append (coinbase_input )
@@ -1112,10 +1138,11 @@ class MergedMiningCoordinator:
1112
1138
MAX_XNONCE1 = 2 ** XNONCE1_SIZE - 1
1113
1139
MAX_RECONNECT_BACKOFF = 30
1114
1140
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 ):
1141
+ def __init__ (self , bitcoin_rpc : IBitcoinRPC | None , hathor_client : IHathorClient ,
1142
+ payback_address_bitcoin : str | None , payback_address_hathor : str | None ,
1143
+ address_from_login : bool = True , min_difficulty : int | None = None ,
1144
+ sequential_xnonce1 : bool = False , rng : Random | None = None ,
1145
+ dummy_merkle_path_len : int | None = None ):
1119
1146
self .log = logger .new ()
1120
1147
if rng is None :
1121
1148
rng = Random ()
@@ -1146,6 +1173,7 @@ def __init__(self, bitcoin_rpc: IBitcoinRPC, hathor_client: IHathorClient,
1146
1173
self .started_at = 0.0
1147
1174
self .strip_all_transactions = False
1148
1175
self .strip_segwit_transactions = False
1176
+ self .dummy_merkle_path_len = dummy_merkle_path_len or 0
1149
1177
1150
1178
@property
1151
1179
def uptime (self ) -> float :
@@ -1187,21 +1215,24 @@ async def start(self) -> None:
1187
1215
"""
1188
1216
loop = asyncio .get_event_loop ()
1189
1217
self .started_at = time .time ()
1190
- self .update_bitcoin_block_task = loop .create_task (self .update_bitcoin_block ())
1218
+ if self .bitcoin_rpc is not None :
1219
+ self .update_bitcoin_block_task = loop .create_task (self .update_bitcoin_block ())
1220
+ else :
1221
+ self .bitcoin_coord_job = BitcoinCoordJob .zeroed (self .dummy_merkle_path_len )
1191
1222
self .update_hathor_block_task = loop .create_task (self .update_hathor_block ())
1192
1223
1193
1224
async def stop (self ) -> None :
1194
1225
""" Stops the client, interrupting mining processes, stoping supervisor loop, and sending finished jobs.
1195
1226
"""
1196
- assert self .update_bitcoin_block_task is not None
1197
- self .update_bitcoin_block_task .cancel ()
1227
+ finals = []
1228
+ if self .update_bitcoin_block_task is not None :
1229
+ self .update_bitcoin_block_task .cancel ()
1230
+ finals .append (self .update_bitcoin_block_task )
1198
1231
assert self .update_hathor_block_task is not None
1199
1232
self .update_hathor_block_task .cancel ()
1233
+ finals .append (self .update_hathor_block_task )
1200
1234
try :
1201
- await asyncio .gather (
1202
- self .update_bitcoin_block_task ,
1203
- self .update_hathor_block_task ,
1204
- )
1235
+ await asyncio .gather (* finals )
1205
1236
except asyncio .CancelledError :
1206
1237
pass
1207
1238
except Exception :
@@ -1212,6 +1243,7 @@ async def stop(self) -> None:
1212
1243
async def update_bitcoin_block (self ) -> None :
1213
1244
""" Task that continuously polls block templates from bitcoin.get_block_template
1214
1245
"""
1246
+ assert self .bitcoin_rpc is not None
1215
1247
backoff = 1
1216
1248
longpoll_id = None
1217
1249
while True :
@@ -1350,13 +1382,14 @@ async def update_merged_block(self) -> None:
1350
1382
merkle_root = build_merkle_root (list (tx .txid for tx in block_proposal .transactions ))
1351
1383
if merkle_root != block_proposal .header .merkle_root :
1352
1384
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' )
1385
+ if self .bitcoin_rpc is not None :
1386
+ error = await self .bitcoin_rpc .verify_block_proposal (block = bytes (block_proposal ))
1387
+ if error is not None :
1388
+ self .log .warn ('proposed block is invalid, skipping update' , error = error )
1389
+ return
1390
+ self .next_merged_job = merged_job
1391
+ self .update_jobs ()
1392
+ self .log .debug ('merged job updated' )
1360
1393
1361
1394
def status (self ) -> dict [Any , Any ]:
1362
1395
""" Build status dict with useful metrics for use in MM Status API.
0 commit comments