From 85c5c0bea9d45e93a9fb20988457480798d68637 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 1 Oct 2022 13:15:19 +1000 Subject: [PATCH] signet/miner: add Generate.next_block_time function --- contrib/signet/miner | 107 +++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/contrib/signet/miner b/contrib/signet/miner index b8fe83494378d..84ff9cb83aa82 100755 --- a/contrib/signet/miner +++ b/contrib/signet/miner @@ -213,13 +213,17 @@ class Generate: INTERVAL = 600.0*2016/2015 # 10 minutes, adjusted for the off-by-one bug - def __init__(self, multiminer=None, ultimate_target=None, poisson=False, max_interval=1800): + def __init__(self, multiminer=None, ultimate_target=None, poisson=False, max_interval=1800, + standby_delay=0, backup_delay=0, set_block_time=None): if multiminer is None: multiminer = (0, 1, 1) (self.multi_low, self.multi_high, self.multi_period) = multiminer self.ultimate_target = ultimate_target self.poisson = poisson self.max_interval = max_interval + self.standby_delay = standby_delay + self.backup_delay = backup_delay + self.set_block_time = set_block_time def next_block_delta(self, last_nbits, last_hash): # strategy: @@ -250,6 +254,42 @@ class Generate: det_rand = int(last_hash[-16:-8], 16) return self.multi_low <= (det_rand % self.multi_period) < self.multi_high + def next_block_time(self, now, bestheader, is_first_block): + if self.set_block_time is not None: + logging.debug("Setting start time to %d", self.set_block_time) + self.mine_time = self.set_block_time + self.action_time = now + self.is_mine = True + elif bestheader["height"] == 0: + time_delta = self.INTERVAL * 100 # plenty of time to mine 100 blocks + logging.info("Backdating time for first block to %d minutes ago" % (time_delta/60)) + self.mine_time = now - time_delta + self.action_time = now + self.is_mine = True + else: + time_delta = self.next_block_delta(int(bestheader["bits"], 16), bestheader["hash"]) + self.mine_time = bestheader["time"] + time_delta + + self.is_mine = self.next_block_is_mine(bestheader["hash"]) + + self.action_time = self.mine_time + if not self.is_mine: + self.action_time += self.backup_delay + + if self.standby_delay > 0: + self.action_time += self.standby_delay + elif is_first_block: + # for non-standby, always mine immediately on startup, + # even if the next block shouldn't be ours + self.action_time = now + + # don't want fractional times so round down + self.mine_time = int(self.mine_time) + self.action_time = int(self.action_time) + + # can't mine a block 2h in the future; 1h55m for some safety + self.action_time = max(self.action_time, self.mine_time - 6900) + def do_generate(args): if args.max_blocks is not None: if args.ongoing: @@ -308,7 +348,8 @@ def do_generate(args): ultimate_target = nbits_to_target(int(args.nbits,16)) - gen = Generate(multiminer=my_blocks, ultimate_target=ultimate_target, poisson=args.poisson, max_interval=args.max_interval) + gen = Generate(multiminer=my_blocks, ultimate_target=ultimate_target, poisson=args.poisson, max_interval=args.max_interval, + standby_delay=args.standby_delay, backup_delay=args.backup_delay, set_block_time=args.set_block_time) mined_blocks = 0 bestheader = {"hash": None} @@ -332,52 +373,18 @@ def do_generate(args): # when is the next block due to be mined? now = time.time() - if args.set_block_time is not None: - logging.debug("Setting start time to %d", args.set_block_time) - mine_time = args.set_block_time - action_time = now - is_mine = True - elif bestheader["height"] == 0: - time_delta = gen.next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"]) - time_delta *= 100 # 100 blocks - logging.info("Backdating time for first block to %d minutes ago" % (time_delta/60)) - mine_time = now - time_delta - action_time = now - is_mine = True - else: - time_delta = gen.next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"]) - mine_time = bestheader["time"] + time_delta - - is_mine = gen.next_block_is_mine(bci["bestblockhash"]) - - action_time = mine_time - if not is_mine: - action_time += args.backup_delay - - if args.standby_delay > 0: - action_time += args.standby_delay - elif mined_blocks == 0: - # for non-standby, always mine immediately on startup, - # even if the next block shouldn't be ours - action_time = now - - # don't want fractional times so round down - mine_time = int(mine_time) - action_time = int(action_time) - - # can't mine a block 2h in the future; 1h55m for some safety - action_time = max(action_time, mine_time - 6900) + gen.next_block_time(now, bestheader, (mined_blocks == 0)) # ready to go? otherwise sleep and check for new block - if now < action_time: - sleep_for = min(action_time - now, 60) - if mine_time < now: + if now < gen.action_time: + sleep_for = min(gen.action_time - now, 60) + if gen.mine_time < now: # someone else might have mined the block, # so check frequently, so we don't end up late # mining the next block if it's ours sleep_for = min(20, sleep_for) - minestr = "mine" if is_mine else "backup" - logging.debug("Sleeping for %s, next block due in %s (%s)" % (seconds_to_hms(sleep_for), seconds_to_hms(mine_time - now), minestr)) + minestr = "mine" if gen.is_mine else "backup" + logging.debug("Sleeping for %s, next block due in %s (%s)" % (seconds_to_hms(sleep_for), seconds_to_hms(gen.mine_time - now), minestr)) time.sleep(sleep_for) continue @@ -390,20 +397,20 @@ def do_generate(args): logging.debug("GBT template: %s", tmpl) - if tmpl["mintime"] > mine_time: - logging.info("Updating block time from %d to %d", mine_time, tmpl["mintime"]) - mine_time = tmpl["mintime"] - if mine_time > now: - logging.error("GBT mintime is in the future: %d is %d seconds later than %d", mine_time, (mine_time-now), now) + if tmpl["mintime"] > gen.mine_time: + logging.info("Updating block time from %d to %d", gen.mine_time, tmpl["mintime"]) + gen.mine_time = tmpl["mintime"] + if gen.mine_time > now: + logging.error("GBT mintime is in the future: %d is %d seconds later than %d", gen.mine_time, (gen.mine_time-now), now) return 1 # address for reward reward_addr, reward_spk = get_reward_addr_spk(args, tmpl["height"]) # mine block - logging.debug("Mining block delta=%s start=%s mine=%s", seconds_to_hms(mine_time-bestheader["time"]), mine_time, is_mine) + logging.debug("Mining block delta=%s start=%s mine=%s", seconds_to_hms(gen.mine_time-bestheader["time"]), gen.mine_time, gen.is_mine) mined_blocks += 1 - psbt = generate_psbt(tmpl, reward_spk, blocktime=mine_time) + psbt = generate_psbt(tmpl, reward_spk, blocktime=gen.mine_time) input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8') psbt_signed = json.loads(args.bcli("-stdin", "walletprocesspsbt", input=input_stream)) if not psbt_signed.get("complete",False): @@ -417,7 +424,7 @@ def do_generate(args): r = args.bcli("-stdin", "submitblock", input=block.serialize().hex().encode('utf8')) # report - bstr = "block" if is_mine else "backup block" + bstr = "block" if gen.is_mine else "backup block" next_delta = gen.next_block_delta(block.nBits, block.hash) next_delta += block.nTime - time.time()