Signet implementation

This commit is contained in:
Karl-Johan Alm 2018-07-24 17:11:13 +09:00 committed by Jorge Timón
parent e195010efd
commit 976257925f
No known key found for this signature in database
GPG key ID: A4F5D141C01A0387
24 changed files with 460 additions and 17 deletions

16
contrib/example.conf Normal file
View file

@ -0,0 +1,16 @@
chain=signet
[signet]
con_genesis_style=signet_old
signet_blockscript=512103e464a9f3070da4d3e0b34ce971ff36f3e07c47a8f4beadf32e8ea7e2afa8a82451ae
signet_siglen=77
# DG seed node
seednode=178.128.221.177
bech32_hrp=sb
pchmessagestart=F0C7706A
pubkeyprefix=125
scriptprefix=87
secretprefix=217
extpubkeyprefix=043587CF
extprvkeyprefix=04358394

75
contrib/signet/issuer/issuer.sh Executable file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
export LC_ALL=C
#
# Issue blocks using a local node at a given interval.
#
if [ $# -lt 3 ]; then
echo "syntax: $0 <min_time> <max_time> <bitcoin-cli path> [<bitcoin-cli args>]" ; exit 1
fi
function log()
{
echo "- $(date +%H:%M:%S): $*"
}
min_time=$1
shift
max_time=$1
shift
bcli=$1
shift
# https://stackoverflow.com/questions/806906/how-do-i-test-if-a-variable-is-a-number-in-bash
re='^[0-9]+$'
if ! [[ $min_time =~ $re ]] ; then
echo "error: min_time $min_time is not a number" ; exit 1
fi
if ! [[ $max_time =~ $re ]] ; then
echo "error: max_time $max_time is not a number" ; exit 1
fi
let randinterval=max_time-min_time
if [ $randinterval -lt 1 ]; then
echo "error: interval min..max must be positive and greater than 0" ; exit 1
fi
if ! [ -e "$bcli" ]; then
which "$bcli" &> /dev/null
if [ $? -ne 0 ]; then
echo "error: unable to find bitcoin binary: $bcli" ; exit 1
fi
fi
echo "- checking node status"
conns=$($bcli "$@" getconnectioncount)
if [ $? -ne 0 ]; then
echo "node error" ; exit 1
fi
if [ $conns -lt 1 ]; then
echo "warning: node is not connected to any other node"
fi
log "node OK with $conns connection(s)"
log "mining in random intervals between $min_time .. $max_time seconds"
log "hit ^C to stop"
while true; do
let rv=$RANDOM%$randinterval
echo -n -e "- $(date +%H:%M:%S): next block in $rv seconds..."
sleep $rv
echo -n -e " [submit]"
blockhash=$($bcli "$@" getnewblockhex true)
if [ $? -ne 0 ]; then
echo "node error; aborting" ; exit 1
fi
echo ""
log "broadcasting block $($bcli "$@" getblockcount) $blockhash to $($bcli "$@" getconnectioncount) peer(s)"
done

View file

@ -405,6 +405,16 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
if (g_solution_blocks && !(s.GetType() & SER_GETHASH)) {
uint256 hash = GetBlockHash();
READWRITE(g_blockheader_payload_map[hash]);
size_t len = GetSizeOfCompactSize(g_blockheader_payload_map[hash].size()) + g_blockheader_payload_map[hash].size();
while (len < g_solution_block_len) {
uint8_t padding = 0;
READWRITE(padding);
len++;
}
}
}
uint256 GetBlockHash() const

View file

@ -428,6 +428,10 @@ class CCustomParams : public CRegTestParams {
consensus.nMinimumChainWork = uint256S(args.GetArg("-con_nminimumchainwork", "0x0"));
consensus.defaultAssumeValid = uint256S(args.GetArg("-con_defaultassumevalid", "0x00"));
consensus.blockscript = ParseHex(args.GetArg("-signet_blockscript", ""));
g_solution_blocks = !consensus.blockscript.empty();
g_solution_block_len = consensus.siglen = args.GetArg("-signet_siglen", 77);
nPruneAfterHeight = (uint64_t)args.GetArg("-npruneafterheight", nPruneAfterHeight);
fDefaultConsistencyChecks = args.GetBoolArg("-fdefaultconsistencychecks", fDefaultConsistencyChecks);
fMineBlocksOnDemand = args.GetBoolArg("-fmineblocksondemand", fMineBlocksOnDemand);
@ -464,15 +468,29 @@ class CCustomParams : public CRegTestParams {
{
if (consensus.genesis_style == "regtest2_style") {
// Same style as in https://github.com/bitcoin/bitcoin/pull/8994
assert(consensus.blockscript.empty() && "consensus.blockscript is for signets");
genesis = CreateGenesisBlock(strNetworkID.c_str(), CScript(OP_TRUE), 1296688602, 2, 0x207fffff, 1, 50 * COIN);
} else if (consensus.genesis_style == "default_style") {
CHashWriter h(SER_DISK, 0);
h << strNetworkID;
if (!consensus.blockscript.empty()) {
h << consensus.blockscript << consensus.siglen;
}
uint256 hash = h.GetHash();
CScript coinbase_sig = CScript() << std::vector<uint8_t>(hash.begin(), hash.end());
genesis = CreateGenesisBlock(coinbase_sig, CScript(OP_TRUE), 1296688602, 2, 0x207fffff, 1, 50 * COIN);
} else if (consensus.genesis_style == "signet_old") {
// Same style as in https://github.com/kallewoof/bitcoin/pull/5
assert(!consensus.blockscript.empty() && "Signets require consensus.blockscript");
CHashWriter h(SER_DISK, 0);
h << consensus.blockscript << consensus.siglen;
uint256 hash = h.GetHash();
CScript coinbase_sig = CScript() << std::vector<uint8_t>(hash.begin(), hash.end());
CScript genesis_out = CScript() << OP_RETURN;
genesis = CreateGenesisBlock(coinbase_sig, genesis_out, 1534313275, 0, 0x1d00ffff, 1, 50 * COIN);
} else {
throw std::runtime_error(strprintf("%s: Unknown consensus.genesis_style %s.", __func__, consensus.genesis_style));
}

View file

@ -21,9 +21,11 @@ void SetupChainParamsBaseOptions()
gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-testnet", "Use the test chain", false, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-con_genesis_style=<style>", "Use genesis style <style> (default: default_style). Allowed values: default_style, regtest2_style", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-con_genesis_style=<style>", "Use genesis style <style> (default: default_style). Allowed values: default_style, regtest2_style, signet_old", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest or custom only)", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-seednode=<ip>", "Use specified node as seed node. This option can be specified multiple times to connect to multiple nodes. (custom only)", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-signet_blockscript", "Blocks must satisfy the given script to be considered valid instead of using pow. If empty, and by default, it is ignored. (custom only)", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-signet_siglen", "The length of the signature must be exactly this long (padded to this length, if shorter). All block headers in this network are of length 80 + this value (custom only)", true, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;

View file

@ -76,7 +76,11 @@ struct Params {
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
uint256 nMinimumChainWork;
uint256 defaultAssumeValid;
std::vector<uint8_t> blockscript;
uint32_t siglen;
};
} // namespace Consensus
#endif // BITCOIN_CONSENSUS_PARAMS_H

View file

@ -55,6 +55,9 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
BlockAssembler::Options::Options() {
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
// Make room for the signature in the block header, if this is a signet block
if (g_solution_blocks) nBlockMaxWeight -= g_solution_block_len;
}
BlockAssembler::BlockAssembler(const CChainParams& params, const Options& options) : chainparams(params)

View file

@ -14,6 +14,7 @@
#include <util.h>
#include <utilstrencodings.h>
unsigned int GetStandardScriptVerifyFlags() { return STANDARD_SCRIPT_VERIFY_FLAGS; }
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{

View file

@ -68,6 +68,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
SCRIPT_VERIFY_CONST_SCRIPTCODE;
unsigned int GetStandardScriptVerifyFlags();
/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;

View file

@ -9,6 +9,9 @@
#include <chain.h>
#include <primitives/block.h>
#include <uint256.h>
#include <script/interpreter.h>
unsigned int GetStandardScriptVerifyFlags();
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
@ -71,8 +74,17 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF
return bnNew.GetCompact();
}
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
bool CheckProofOfWork(const uint256& hash, unsigned int nBits, const Consensus::Params& params)
{
if (g_solution_blocks) {
if (hash == params.hashGenesisBlock) return true;
SimpleSignatureChecker bsc(hash);
const auto& payload = g_blockheader_payload_map.at(hash);
CScript solution = CScript(payload.begin(), payload.end());
CScript challenge = CScript(params.blockscript.begin(), params.blockscript.end());
return VerifyScript(solution, challenge, nullptr, GetStandardScriptVerifyFlags(), bsc);
}
bool fNegative;
bool fOverflow;
arith_uint256 bnTarget;

View file

@ -18,6 +18,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
bool CheckProofOfWork(const uint256& hash, unsigned int nBits, const Consensus::Params&);
#endif // BITCOIN_POW_H

View file

@ -10,6 +10,10 @@
#include <utilstrencodings.h>
#include <crypto/common.h>
bool g_solution_blocks = false;
size_t g_solution_block_len = 0;
std::map<uint256,std::vector<uint8_t>> g_blockheader_payload_map;
uint256 CBlockHeader::GetHash() const
{
return SerializeHash(*this);

View file

@ -10,6 +10,23 @@
#include <serialize.h>
#include <uint256.h>
/**
* If true, block headers contain a payload equal to a Bitcoin Script solution
* to a signet challenge as defined in the chain params.
*/
extern bool g_solution_blocks;
/**
* If non-zero, defines an enforced size requirement for block header payloads.
* It requires that all blocks are of size 80 + (this value) bytes.
*/
extern size_t g_solution_block_len;
/**
* Contains a mapping of hash to signature data for each block header
* in signet networks.
*/
extern std::map<uint256,std::vector<uint8_t>> g_blockheader_payload_map;
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
@ -43,6 +60,15 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
if (g_solution_blocks && !(s.GetType() & SER_GETHASH)) {
READWRITE(g_blockheader_payload_map[GetHash()]);
size_t len = GetSizeOfCompactSize(g_blockheader_payload_map[GetHash()].size()) + g_blockheader_payload_map[GetHash()].size();
while (len < g_solution_block_len) {
uint8_t padding = 0;
READWRITE(padding);
len++;
}
}
}
void SetNull()

View file

@ -115,16 +115,16 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
{
AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
result.pushKV("hash", block.GetHash().GetHex());
int confirmations = -1;
// Only report confirmations if the block is on the main chain
if (chainActive.Contains(blockindex))
if (blockindex && chainActive.Contains(blockindex))
confirmations = chainActive.Height() - blockindex->nHeight + 1;
result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
result.pushKV("height", blockindex->nHeight);
if (blockindex) result.pushKV("height", blockindex->nHeight);
result.pushKV("version", block.nVersion);
result.pushKV("versionHex", strprintf("%08x", block.nVersion));
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
@ -142,18 +142,23 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
}
result.pushKV("tx", txs);
result.pushKV("time", block.GetBlockTime());
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
if (blockindex) result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
result.pushKV("nonce", (uint64_t)block.nNonce);
result.pushKV("bits", strprintf("%08x", block.nBits));
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (blockindex) result.pushKV("difficulty", GetDifficulty(blockindex));
if (blockindex) result.pushKV("chainwork", blockindex->nChainWork.GetHex());
if (blockindex) result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (blockindex->pprev)
if (blockindex && blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
if (blockindex) {
CBlockIndex *pnext = chainActive.Next(blockindex);
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
}
if (g_solution_blocks && g_blockheader_payload_map.count(block.GetHash())) {
result.pushKV("signet-solution", HexStr(g_blockheader_payload_map.at(block.GetHash())));
}
return result;
}

View file

@ -55,6 +55,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getblockhash", 0, "height" },
{ "getnewblockhex", 0, "broadcast" },
{ "waitforblockheight", 0, "height" },
{ "waitforblockheight", 1, "timeout" },
{ "waitforblock", 1, "timeout" },

View file

@ -695,7 +695,7 @@ protected:
}
};
static UniValue submitblock(const JSONRPCRequest& request)
UniValue submitblock(const JSONRPCRequest& request)
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
@ -725,6 +725,11 @@ static UniValue submitblock(const JSONRPCRequest& request)
}
uint256 hash = block.GetHash();
if (!request.params[1].isNull()) {
g_blockheader_payload_map[hash] = ParseHex(request.params[1].get_str());
}
{
LOCK(cs_main);
const CBlockIndex* pindex = LookupBlockIndex(hash);
@ -942,7 +947,6 @@ static const CRPCCommand commands[] =
{ "mining", "getblocktemplate", &getblocktemplate, {"template_request"} },
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
{ "hidden", "estimatefee", &estimatefee, {} },

View file

@ -1416,6 +1416,22 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
bool SimpleSignatureChecker::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
return false;
// Hash type is one byte tacked on to the end of the signature
std::vector<unsigned char> vchSig(vchSigIn);
if (vchSig.empty())
return false;
// int nHashType = vchSig.back();
vchSig.pop_back();
return pubkey.Verify(hash, vchSig);
}
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
std::vector<std::vector<unsigned char> > stack;

View file

@ -162,6 +162,17 @@ public:
virtual ~BaseSignatureChecker() {}
};
class SimpleSignatureChecker : public BaseSignatureChecker
{
private:
uint256 hash;
public:
const uint256& GetHash() const { return hash; }
SimpleSignatureChecker(const uint256& hash_in) : hash(hash_in) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
};
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{

View file

@ -32,6 +32,15 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
return true;
}
bool SimpleSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
if (!provider.GetKey(keyid, key)) return false;
if (!key.Sign(checker.GetHash(), vchSig)) return false;
vchSig.push_back((unsigned char)SIGHASH_ALL);
return true;
}
static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
if (provider.GetCScript(scriptid, script)) {

View file

@ -66,6 +66,16 @@ public:
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
};
class SimpleSignatureCreator : public BaseSignatureCreator
{
SimpleSignatureChecker checker;
public:
SimpleSignatureCreator(const uint256& hashIn) : BaseSignatureCreator(), checker(hashIn) {};
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
/** A signature creator for transactions. */
class MutableTransactionSignatureCreator : public BaseSignatureCreator {
const CMutableTransaction* txTo;

View file

@ -275,7 +275,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pindexNew->nTx = diskindex.nTx;
const uint256 block_hash = pindexNew->GetBlockHash();
if (!CheckProofOfWork(block_hash, pindexNew->nBits, consensusParams) &&
// Block index guts do not include the payload, so we cannot check the POW for
// signets here
if (!g_solution_blocks &&
!CheckProofOfWork(block_hash, pindexNew->nBits, consensusParams) &&
block_hash != consensusParams.hashGenesisBlock) {
return error("%s: CheckProofOfWork: %s, %s", __func__, block_hash.ToString(), pindexNew->ToString());
}

View file

@ -10,6 +10,7 @@
#include <httpserver.h>
#include <validation.h>
#include <key_io.h>
#include <miner.h>
#include <net.h>
#include <outputtype.h>
#include <policy/feerate.h>
@ -3879,6 +3880,187 @@ static UniValue bumpfee(const JSONRPCRequest& request)
return result;
}
void SignBlockHashWithWallet(const uint256& hash, CWallet* const pwallet)
{
SignatureData solution_in;
if (g_blockheader_payload_map.count(hash) && g_blockheader_payload_map.at(hash).size()) {
solution_in = SignatureData(CScript(g_blockheader_payload_map[hash].begin(), g_blockheader_payload_map[hash].end()));
}
CScript blockscript(Params().GetConsensus().blockscript.begin(), Params().GetConsensus().blockscript.end());
size_t siglen = gArgs.GetArg("-signet_siglen", 0);
// sign until we have a sufficiently small signature, or until we run out of tries
bool res;
size_t smallest = 9999;
std::vector<uint8_t> sigdata;
size_t overhead = GetSizeOfCompactSize(siglen);
for (size_t i = 0; i < 1000; ++i) {
SignatureData solution(solution_in);
res = ProduceSignature(*pwallet, SimpleSignatureCreator(hash), blockscript, solution);
if (!res) {
throw JSONRPCError(RPC_VERIFY_ERROR, "could not produce a signature -- do you have the private key(s)?");
}
if (solution.scriptSig.size() < smallest) {
smallest = solution.scriptSig.size();
if (!siglen || smallest + overhead <= siglen) {
sigdata = std::vector<uint8_t>(solution.scriptSig.begin(), solution.scriptSig.end());
break;
}
}
}
if (siglen && sigdata.size() == 0) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("unable to produce a signature of size <= %zu (smallest found was %zu)", siglen, smallest + overhead));
}
g_blockheader_payload_map[hash] = sigdata;
}
UniValue signblock(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!g_solution_blocks) {
throw std::runtime_error(
"signblock can only be used with signet networks"
);
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"signblock \"blockhex\"\n"
"\nSigns a block proposal, checking that it would be accepted first\n"
"\nArguments:\n"
"1. \"blockhex\" (string, required) The hex-encoded block from getnewblockhex\n"
"\nResult\n"
" sig (hex) The signature\n"
"\nExamples:\n"
+ HelpExampleCli("signblock", "0000002018c6f2f913f9902aeab...5ca501f77be96de63f609010000000000000000015100000000")
);
}
CBlock block;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}
LOCK(cs_main);
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
throw JSONRPCError(RPC_VERIFY_ERROR, "already have block");
}
CBlockIndex* const pindexPrev = chainActive.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash()) {
throw JSONRPCError(RPC_VERIFY_ERROR, "proposal was not based on our best chain");
}
CValidationState state;
if (!TestBlockValidity(state, Params(), block, pindexPrev, false, true) || !state.IsValid()) {
std::string strRejectReason = state.GetRejectReason();
if (strRejectReason.empty()) {
throw JSONRPCError(RPC_VERIFY_ERROR, state.IsInvalid() ? "Block proposal was invalid" : "Error checking block proposal");
}
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
}
SignBlockHashWithWallet(hash, pwallet);
return HexStr(g_blockheader_payload_map.at(hash));
}
UniValue getnewblockhex(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getnewblockhex ( broadcast )\n"
"\nGets hex representation of a proposed, unmined new block, optionally\n"
"signing and broadcasting it to the network.\n"
"\nArguments:\n"
"1. broadcast (boolean, optional, default=false) Sign and broadcast the block immediately, returning the blockhash\n"
"\nResult (for broadcast=false)\n"
"blockhex (hex) The block hex\n"
"\nResult (for broadcast=true)\n"
"blockhash (hash) The block hash\n"
"\nExamples:\n"
+ HelpExampleCli("getnewblockhex", "")
+ HelpExampleCli("getnewblockhex", "true")
);
}
bool broadcast = !request.params[0].isNull() && request.params[0].get_bool();
std::shared_ptr<CReserveScript> coinbase_script;
pwallet->GetScriptForMining(coinbase_script);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbase_script) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
//throw an error if no script was provided
if (coinbase_script->reserveScript.empty()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
}
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbase_script->reserveScript));
{
// IncrementExtraNonce sets coinbase flags and builds merkle tree
LOCK(cs_main);
unsigned int nExtraNonce = 0;
IncrementExtraNonce(&pblocktemplate->block, chainActive.Tip(), nExtraNonce);
}
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblocktemplate->block;
if (broadcast) {
// attempt to sign
uint256 hash = pblocktemplate->block.GetHash();
SignBlockHashWithWallet(hash, pwallet);
// and submit
UniValue submitblock(const JSONRPCRequest& request);
UniValue params(UniValue::VARR);
params.push_back(HexStr(ssBlock.begin(), ssBlock.end()));
params.push_back(HexStr(g_blockheader_payload_map.at(hash).begin(), g_blockheader_payload_map.at(hash).end()));
JSONRPCRequest req;
req.params = params;
submitblock(req);
return HexStr(hash);
}
return HexStr(ssBlock.begin(), ssBlock.end());
}
UniValue GenerateSignetBlocks(int count)
{
UniValue params(UniValue::VARR);
UniValue t(UniValue::VBOOL);
t.setBool(true);
params.push_back(t);
JSONRPCRequest req;
req.params = params;
UniValue blockHashes(UniValue::VARR);
for (int i = 0; i < count; ++i) {
blockHashes.push_back(getnewblockhex(req));
}
return blockHashes;
}
UniValue generate(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@ -3905,6 +4087,11 @@ UniValue generate(const JSONRPCRequest& request)
}
int num_generate = request.params[0].get_int();
if (g_solution_blocks) {
return GenerateSignetBlocks(num_generate);
}
uint64_t max_tries = 1000000;
if (!request.params[1].isNull()) {
max_tries = request.params[1].get_int();
@ -4838,6 +5025,10 @@ static const CRPCCommand commands[] =
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
/** Signet mining */
{ "wallet", "signblock", &signblock, {"blockhex"} },
{ "wallet", "getnewblockhex", &getnewblockhex, {"broadcast"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)

View file

@ -28,6 +28,25 @@ GENESIS_ARGS_MAP = [
],
},
{
'memo': 'default_style with blockscript',
'genesis': 'ac6b1de55f8cb2cffc12c0cab0036d0966a6142fd5f70d0d0ecd96b56f4cb1b6',
'args': [
'-con_genesis_style=default_style',
'-signet_blockscript=512103e464a9f3070da4d3e0b34ce971ff36f3e07c47a8f4beadf32e8ea7e2afa8a82451ae',
],
},
{
'memo': 'signet_old',
'genesis': '7cbf2772cb0e53345b021f34d17b30de42a8952c982b0812e4caca7529009ca5',
# TODO FIXME Should be
# 'genesis': '22861f488a5c6cb033a843e476581a8abf5b82a34926babfde1241ed97ba268e',
'args': [
'-con_genesis_style=signet_old',
'-signet_blockscript=512103e464a9f3070da4d3e0b34ce971ff36f3e07c47a8f4beadf32e8ea7e2afa8a82451ae',
],
},
]
class GenesisHashTest(BitcoinTestFramework):

View file

@ -54,6 +54,7 @@ MAGIC_BYTES = {
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
"testnet3": b"\x0b\x11\x09\x07", # testnet3
"regtest": b"\xfa\xbf\xb5\xda", # regtest
"signet": b"\xf0\xc7\x70\x6a", # signet
}