Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FreshEyes] Extend signetchallenge to set target block spacing #14

Open
wants to merge 2 commits into
base: bitcoin-fresheyes-staging-master-29365
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions contrib/signet/miner
Original file line number Diff line number Diff line change
Expand Up @@ -225,15 +225,15 @@ def seconds_to_hms(s):
out = "-" + out
return out

def next_block_delta(last_nbits, last_hash, ultimate_target, do_poisson, max_interval):
def next_block_delta(last_nbits, last_hash, ultimate_target, do_poisson, max_interval, target_spacing):
# strategy:
# 1) work out how far off our desired target we are
# 2) cap it to a factor of 4 since that's the best we can do in a single retarget period
# 3) use that to work out the desired average interval in this retarget period
# 4) if doing poisson, use the last hash to pick a uniformly random number in [0,1), and work out a random multiplier to vary the average by
# 5) cap the resulting interval between 1 second and 1 hour to avoid extremes

INTERVAL = 600.0*2016/2015 # 10 minutes, adjusted for the off-by-one bug
INTERVAL = float(target_spacing)*2016/2015 # 10 minutes, adjusted for the off-by-one bug

current_target = nbits_to_target(last_nbits)
retarget_factor = ultimate_target / current_target
Expand Down Expand Up @@ -308,8 +308,9 @@ def do_generate(args):
return 1
my_blocks = (start-1, stop, total)

if args.max_interval < 960:
logging.error("--max-interval must be at least 960 (16 minutes)")
max_interval_limit = args.target_spacing * 16 / 10
if args.max_interval < max_interval_limit:
logging.error("--max-interval must be at least %d (%f minutes)" % (max_interval_limit, max_interval_limit/60))
return 1

ultimate_target = nbits_to_target(int(args.nbits,16))
Expand All @@ -328,7 +329,7 @@ def do_generate(args):
if lastheader is None:
lastheader = bestheader["hash"]
elif bestheader["hash"] != lastheader:
next_delta = next_block_delta(int(bestheader["bits"], 16), bestheader["hash"], ultimate_target, args.poisson, args.max_interval)
next_delta = next_block_delta(int(bestheader["bits"], 16), bestheader["hash"], ultimate_target, args.poisson, args.max_interval, args.target_spacing)
next_delta += bestheader["time"] - time.time()
next_is_mine = next_block_is_mine(bestheader["hash"], my_blocks)
logging.info("Received new block at height %d; next in %s (%s)", bestheader["height"], seconds_to_hms(next_delta), ("mine" if next_is_mine else "backup"))
Expand All @@ -342,14 +343,14 @@ def do_generate(args):
action_time = now
is_mine = True
elif bestheader["height"] == 0:
time_delta = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson, args.max_interval)
time_delta = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson, args.max_interval, args.target_spacing)
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 = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson, args.max_interval)
time_delta = next_block_delta(int(bestheader["bits"], 16), bci["bestblockhash"], ultimate_target, args.poisson, args.max_interval, args.target_spacing)
mine_time = bestheader["time"] + time_delta

is_mine = next_block_is_mine(bci["bestblockhash"], my_blocks)
Expand Down Expand Up @@ -423,7 +424,7 @@ def do_generate(args):
# report
bstr = "block" if is_mine else "backup block"

next_delta = next_block_delta(block.nBits, block.hash, ultimate_target, args.poisson, args.max_interval)
next_delta = next_block_delta(block.nBits, block.hash, ultimate_target, args.poisson, args.max_interval, args.target_spacing)
next_delta += block.nTime - time.time()
next_is_mine = next_block_is_mine(block.hash, my_blocks)

Expand Down Expand Up @@ -502,6 +503,7 @@ def main():
generate.add_argument("--backup-delay", default=300, type=int, help="Seconds to delay before mining blocks reserved for other miners (default=300)")
generate.add_argument("--standby-delay", default=0, type=int, help="Seconds to delay before mining blocks (default=0)")
generate.add_argument("--max-interval", default=1800, type=int, help="Maximum interblock interval (seconds)")
generate.add_argument("--target-spacing", default=600, type=int, help="Target interval between blocks (seconds), property of the network (default 600)")

calibrate = cmds.add_parser("calibrate", help="Calibrate difficulty")
calibrate.set_defaults(fn=do_calibrate)
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ BITCOIN_TESTS =\
test/blockmanager_tests.cpp \
test/bloom_tests.cpp \
test/bswap_tests.cpp \
test/chainparams_tests.cpp \
test/checkqueue_tests.cpp \
test/coins_tests.cpp \
test/coinstatsindex_tests.cpp \
Expand Down
81 changes: 80 additions & 1 deletion src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <consensus/params.h>
#include <deploymentinfo.h>
#include <logging.h>
#include <script/script.h>
#include <tinyformat.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
Expand All @@ -21,6 +22,80 @@
#include <stdexcept>
#include <vector>

void ParseWrappedSignetChallenge(const std::vector<uint8_t>& wrappedChallenge, std::vector<uint8_t>& outParams, std::vector<uint8_t>& outChallenge) {
if (wrappedChallenge.empty() || wrappedChallenge[0] != OP_RETURN) {
// Not a wrapped challenge.
outChallenge = wrappedChallenge;
return;
}

std::vector<uint8_t> params;
std::vector<uint8_t> challenge;

const CScript script(wrappedChallenge.begin(), wrappedChallenge.end());
CScript::const_iterator it = script.begin(), itend = script.end();
int i;
for (i = 0; it != itend; i++) {
if (i > 2) {
throw std::runtime_error("too many operations in wrapped challenge, must be 3.");
}
std::vector<unsigned char> push_data;
opcodetype opcode;
if (!script.GetOp(it, opcode, push_data)) {
throw std::runtime_error(strprintf("failed to parse operation %d in wrapped challenge script.", i));
}
if (i == 0) {
// OP_RETURN.
continue;
}
if (opcode != OP_PUSHDATA1 && opcode != OP_PUSHDATA2 && opcode != OP_PUSHDATA4) {
throw std::runtime_error(strprintf("operation %d of wrapped challenge script must be a PUSHDATA opcode, got 0x%02x.", i, opcode));
}
if (i == 1) {
params.swap(push_data);
} else if (i == 2) {
challenge.swap(push_data);
}
}
if (i != 3) {
throw std::runtime_error(strprintf("too few operations in wrapped challenge, must be 3, got %d.", i));
}

outParams.swap(params);
outChallenge.swap(challenge);
}

void ParseSignetParams(const std::vector<uint8_t>& params, CChainParams::SigNetOptions& options) {
if (params.empty()) {
return;
}

// The format of params is extendable in case more fields are added in the future.
// It is encoded as a concatenation of (field_id, value) tuples, protobuf style.
// Currently there is only one field defined: pow_target_spacing, whose field_id
// is 0x01 and the lendth of encoding is 8 (int64_t). So valid values of params are:
// - empty string (checked in if block above),
// - 0x01 followed by 8 bytes of pow_target_spacing (9 bytes in total).
// If length is not 0 and not 9, the value can not be parsed.

if (params.size() != 1 + 8) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 authors commented here with:

  • comment link https://github.com/bitcoin/bitcoin/pull/29365#discussion_r1487797038 at 2024/02/13, 12:52:10 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/29365#discussion_r1487877269 at 2024/02/13, 13:41:36 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/29365#discussion_r1496917274 at 2024/02/21, 05:05:13 UTC
  • comment link https://github.com/bitcoin/bitcoin/pull/29365#discussion_r1497768481 at 2024/02/21, 15:17:22 UTC.

throw std::runtime_error(strprintf("signet params must have length %d, got %d.", 1+8, params.size()));
}
if (params[0] != 0x01) {
throw std::runtime_error(strprintf("signet params[0] must be 0x01, got 0x%02x.", params[0]));
}
// Parse little-endian 64 bit number to uint8_t.
const uint8_t* bytes = &params[1];
const uint64_t value = uint64_t(bytes[0]) | uint64_t(bytes[1])<<8 | uint64_t(bytes[2])<<16 | uint64_t(bytes[3])<<24 |
uint64_t(bytes[4])<<32 | uint64_t(bytes[5])<<40 | uint64_t(bytes[6])<<48 | uint64_t(bytes[7])<<56;
auto pow_target_spacing = int64_t(value);
if (pow_target_spacing <= 0) {
throw std::runtime_error("signet param pow_target_spacing <= 0.");
}

options.pow_target_spacing = pow_target_spacing;
}

void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options)
{
if (args.IsArgSet("-signetseednode")) {
Expand All @@ -35,7 +110,11 @@ void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& option
if (!val) {
throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0]));
}
options.challenge.emplace(*val);
std::vector<unsigned char> params;
std::vector<unsigned char> challenge;
ParseWrappedSignetChallenge(*val, params, challenge);
ParseSignetParams(params, options);
options.challenge.emplace(challenge);
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,26 @@ const CChainParams &Params();
*/
void SelectParams(const ChainType chain);

/**
* Extracts signet params and signet challenge from wrapped signet challenge.
* Format of wrapped signet challenge is:
* If the challenge is in the form "OP_RETURN PUSHDATA<params> PUSHDATA<actual challenge>",
* If the input challenge does not start with OP_RETURN,
* sets outParams="" and outChallenge=input.
* If the input challenge starts with OP_RETURN, but does not satisfy the format,
* throws an exception.
*/
void ParseWrappedSignetChallenge(const std::vector<uint8_t>& wrappedChallenge, std::vector<uint8_t>& outParams, std::vector<uint8_t>& outChallenge);

/**
* Parses signet options.
* The format currently supports only setting pow_target_spacing, but
* can be extended in the future.
* Possible values:
* - Empty (then do nothing)
* - 0x01 (pow_target_spacing as int64_t little endian) => set pow_target_spacing.
* If the format is wrong, throws an exception.
*/
void ParseSignetParams(const std::vector<uint8_t>& params, CChainParams::SigNetOptions& options);

#endif // BITCOIN_CHAINPARAMS_H
2 changes: 1 addition & 1 deletion src/chainparamsbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge); in case -signetchallenge is in the form of 'OP_RETURN PUSHDATA<params> PUSHDATA<actual challenge>', then <actual challenge> is used as a challenge and <params> is used to set parameters of signet; currently the only supported parameter is target spacing, the format of <params> to set it is 01<8 bytes value of target spacing, seconds, little endian>", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
}

Expand Down
2 changes: 1 addition & 1 deletion src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class SigNetParams : public CChainParams {
consensus.CSVHeight = 1;
consensus.SegwitHeight = 1;
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
consensus.nPowTargetSpacing = options.pow_target_spacing;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An author commented here with:

  • comment link https://github.com/bitcoin/bitcoin/pull/29365#discussion_r1479616697 at 2024/02/06, 11:22:01 UTC.

consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
Expand Down
1 change: 1 addition & 0 deletions src/kernel/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class CChainParams
struct SigNetOptions {
std::optional<std::vector<uint8_t>> challenge{};
std::optional<std::vector<std::string>> seeds{};
int64_t pow_target_spacing{10 * 60};
};

/**
Expand Down
Loading