mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
Signet implementation
This commit is contained in:
parent
e195010efd
commit
976257925f
24 changed files with 460 additions and 17 deletions
16
contrib/example.conf
Normal file
16
contrib/example.conf
Normal 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
75
contrib/signet/issuer/issuer.sh
Executable 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
|
||||
10
src/chain.h
10
src/chain.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
14
src/pow.cpp
14
src/pow.cpp
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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, {} },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue