mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Signet implementation
This commit is contained in:
parent
540bf8aacc
commit
5143d5ddc7
20 changed files with 493 additions and 26 deletions
73
contrib/signet/issuer/issuer.sh
Executable file
73
contrib/signet/issuer/issuer.sh
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
#!/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.
|
||||
|
||||
#
|
||||
# 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 [ 1 ]; 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
|
||||
|
|
@ -13,14 +13,15 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include <chainparamsseeds.h>
|
||||
#include <hash.h>
|
||||
|
||||
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
||||
static CBlock CreateGenesisBlock(const CScript& coinbase_sig, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
||||
{
|
||||
CMutableTransaction txNew;
|
||||
txNew.nVersion = 1;
|
||||
txNew.vin.resize(1);
|
||||
txNew.vout.resize(1);
|
||||
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
|
||||
txNew.vin[0].scriptSig = coinbase_sig;
|
||||
txNew.vout[0].nValue = genesisReward;
|
||||
txNew.vout[0].scriptPubKey = genesisOutputScript;
|
||||
|
||||
|
|
@ -35,6 +36,12 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
|
|||
return genesis;
|
||||
}
|
||||
|
||||
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
||||
{
|
||||
CScript coinbase_sig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
|
||||
return CreateGenesisBlock(coinbase_sig, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the genesis block. Note that the output of its generation
|
||||
* transaction cannot be spent since it did not originally exist in the
|
||||
|
|
@ -274,6 +281,89 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SigNet version 2018-07-20
|
||||
*/
|
||||
class SigNetParams : public CChainParams {
|
||||
public:
|
||||
SigNetParams(const ChainParamArgs& args) {
|
||||
for (const auto& a : args) {
|
||||
for (const auto& b : a.second) {
|
||||
printf("%s : %s\n", a.first.c_str(), b.c_str());
|
||||
}
|
||||
}
|
||||
if (!args.count("signet_blockscript") || !args.count("signet_siglen") || !args.count("signet_seednode")) {
|
||||
throw std::runtime_error(strprintf("%s: Signet requires -signet_blockscript, -signet_siglen, and -signet_seednode provided.", __func__));
|
||||
}
|
||||
if (args.at("signet_blockscript").size() != 1) {
|
||||
throw std::runtime_error(strprintf("%s: -signet_blockscript cannot be multiple values.", __func__));
|
||||
}
|
||||
if (args.at("signet_siglen").size() != 1) {
|
||||
throw std::runtime_error(strprintf("%s: -signet_siglen cannot be multiple values.", __func__));
|
||||
}
|
||||
|
||||
strNetworkID = "signet";
|
||||
consensus.blockscript = ParseHex(args.at("signet_blockscript")[0]);
|
||||
g_solution_block_len = consensus.siglen = atoi(args.at("signet_siglen")[0]);
|
||||
consensus.signature_pow = true;
|
||||
consensus.nSubsidyHalvingInterval = 210000;
|
||||
consensus.BIP34Height = 1;
|
||||
consensus.BIP65Height = 1;
|
||||
consensus.BIP66Height = 1;
|
||||
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
||||
consensus.nPowTargetSpacing = 10 * 60;
|
||||
consensus.fPowAllowMinDifficultyBlocks = false;
|
||||
consensus.fPowNoRetargeting = false;
|
||||
consensus.nRuleChangeActivationThreshold = 1916;
|
||||
consensus.nMinerConfirmationWindow = 2016;
|
||||
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// Deployment of BIP68, BIP112, and BIP113.
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// Deployment of SegWit (BIP141, BIP143, and BIP147)
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
pchMessageStart[0] = 0xf0;
|
||||
pchMessageStart[1] = 0xc7;
|
||||
pchMessageStart[2] = 0x70;
|
||||
pchMessageStart[3] = 0x6a;
|
||||
nDefaultPort = 38333;
|
||||
nPruneAfterHeight = 1000;
|
||||
|
||||
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);
|
||||
consensus.hashGenesisBlock = genesis.GetHash();
|
||||
|
||||
vFixedSeeds.clear();
|
||||
vSeeds.clear();
|
||||
vSeeds = args.at("signet_seednode");
|
||||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>{125};
|
||||
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>{87};
|
||||
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>{217};
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||
|
||||
bech32_hrp = "sb";
|
||||
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = false;
|
||||
fMineBlocksOnDemand = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Regression test
|
||||
*/
|
||||
|
|
@ -361,7 +451,7 @@ const CChainParams &Params() {
|
|||
return *globalChainParams;
|
||||
}
|
||||
|
||||
std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain)
|
||||
std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain, const ChainParamArgs& args)
|
||||
{
|
||||
if (chain == CBaseChainParams::MAIN)
|
||||
return std::unique_ptr<CChainParams>(new CMainParams());
|
||||
|
|
@ -369,13 +459,32 @@ std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain)
|
|||
return std::unique_ptr<CChainParams>(new CTestNetParams());
|
||||
else if (chain == CBaseChainParams::REGTEST)
|
||||
return std::unique_ptr<CChainParams>(new CRegTestParams());
|
||||
else if (chain == CBaseChainParams::SIGNET) {
|
||||
g_solution_blocks = true;
|
||||
return std::unique_ptr<CChainParams>(new SigNetParams(args));
|
||||
}
|
||||
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
|
||||
}
|
||||
|
||||
void SelectParams(const std::string& network)
|
||||
{
|
||||
ChainParamArgs chain_args;
|
||||
if (gArgs.IsArgSet("-signet_blockscript") && gArgs.IsArgSet("-signet_siglen")) {
|
||||
chain_args["signet_blockscript"] = gArgs.GetArgs("-signet_blockscript");
|
||||
chain_args["signet_siglen"] = gArgs.GetArgs("-signet_siglen");
|
||||
if (gArgs.IsArgSet("-signet_seednode")) {
|
||||
chain_args["signet_seednode"] = gArgs.GetArgs("-signet_seednode");
|
||||
}
|
||||
} else {
|
||||
chain_args["signet_blockscript"].push_back("512103e464a9f3070da4d3e0b34ce971ff36f3e07c47a8f4beadf32e8ea7e2afa8a82451ae");
|
||||
chain_args["signet_siglen"].push_back("77");
|
||||
if (!gArgs.IsArgSet("-signet_seednode")) {
|
||||
chain_args["signet_seednode"].push_back("178.128.221.177"); // DG seed node
|
||||
}
|
||||
}
|
||||
|
||||
SelectBaseParams(network);
|
||||
globalChainParams = CreateChainParams(network);
|
||||
globalChainParams = CreateChainParams(network, chain_args);
|
||||
}
|
||||
|
||||
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
|
||||
|
|
|
|||
|
|
@ -102,12 +102,14 @@ protected:
|
|||
bool m_fallback_fee_enabled;
|
||||
};
|
||||
|
||||
typedef std::map<std::string,std::vector<std::string>> ChainParamArgs;
|
||||
|
||||
/**
|
||||
* Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
|
||||
* @returns a CChainParams* of the chosen chain.
|
||||
* @throws a std::runtime_error if the chain is not supported.
|
||||
*/
|
||||
std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain);
|
||||
std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain, const ChainParamArgs& args=ChainParamArgs());
|
||||
|
||||
/**
|
||||
* Return the currently selected parameters. This won't change after app
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
const std::string CBaseChainParams::MAIN = "main";
|
||||
const std::string CBaseChainParams::TESTNET = "test";
|
||||
const std::string CBaseChainParams::SIGNET = "sig";
|
||||
const std::string CBaseChainParams::REGTEST = "regtest";
|
||||
|
||||
void SetupChainParamsBaseOptions()
|
||||
|
|
@ -20,6 +21,10 @@ 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("-signet", "Use the signet chain. Note that the network is defined by the signet_blockscript and signet_siglen parameters", false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-signet_blockscript", "Blocks must satisfy the given script to be considered valid (only for -signet networks)", false, 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", false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-signet_seednode", "Specify a seed node for the signet network (may be used multiple times to specify multiple seed nodes)", false, OptionsCategory::CHAINPARAMS);
|
||||
}
|
||||
|
||||
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
|
||||
|
|
@ -38,8 +43,9 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain
|
|||
return MakeUnique<CBaseChainParams>("testnet3", 18332);
|
||||
else if (chain == CBaseChainParams::REGTEST)
|
||||
return MakeUnique<CBaseChainParams>("regtest", 18443);
|
||||
else
|
||||
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
|
||||
else if (chain == CBaseChainParams::SIGNET)
|
||||
return MakeUnique<CBaseChainParams>("signet", 38332);
|
||||
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
|
||||
}
|
||||
|
||||
void SelectBaseParams(const std::string& chain)
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
class CBaseChainParams
|
||||
{
|
||||
public:
|
||||
/** BIP70 chain name strings (main, test or regtest) */
|
||||
/** BIP70 chain name strings (main, test (, sigtest) or regtest) */
|
||||
static const std::string MAIN;
|
||||
static const std::string TESTNET;
|
||||
static const std::string SIGNET;
|
||||
static const std::string REGTEST;
|
||||
|
||||
const std::string& DataDir() const { return strDataDir; }
|
||||
|
|
|
|||
|
|
@ -75,7 +75,12 @@ struct Params {
|
|||
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
|
||||
uint256 nMinimumChainWork;
|
||||
uint256 defaultAssumeValid;
|
||||
|
||||
bool signature_pow{false};
|
||||
std::vector<uint8_t> blockscript;
|
||||
uint32_t siglen;
|
||||
};
|
||||
|
||||
} // namespace Consensus
|
||||
|
||||
#endif // BITCOIN_CONSENSUS_PARAMS_H
|
||||
|
|
|
|||
13
src/pow.cpp
13
src/pow.cpp
|
|
@ -9,6 +9,8 @@
|
|||
#include <chain.h>
|
||||
#include <primitives/block.h>
|
||||
#include <uint256.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <policy/policy.h>
|
||||
|
||||
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
|
||||
{
|
||||
|
|
@ -71,8 +73,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;
|
||||
BlockSignatureChecker 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, STANDARD_SCRIPT_VERIFY_FLAGS, 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
|
||||
|
|
@ -30,6 +47,7 @@ public:
|
|||
|
||||
CBlockHeader()
|
||||
{
|
||||
nVersion = 0;
|
||||
SetNull();
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +61,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()
|
||||
|
|
|
|||
|
|
@ -114,16 +114,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());
|
||||
|
|
@ -141,18 +141,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" },
|
||||
|
|
|
|||
|
|
@ -694,7 +694,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) {
|
||||
|
|
@ -724,6 +724,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);
|
||||
|
|
@ -978,7 +983,6 @@ static const CRPCCommand commands[] =
|
|||
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
|
||||
{ "mining", "submitheader", &submitheader, {"hexdata"} },
|
||||
|
||||
|
||||
{ "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 BlockSignatureChecker::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 BlockSignatureChecker : public BaseSignatureChecker
|
||||
{
|
||||
private:
|
||||
uint256 hash;
|
||||
|
||||
public:
|
||||
const uint256& GetHash() const { return hash; }
|
||||
BlockSignatureChecker(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 BlockSignatureCreator::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 BlockSignatureCreator : public BaseSignatureCreator
|
||||
{
|
||||
BlockSignatureChecker checker;
|
||||
|
||||
public:
|
||||
BlockSignatureCreator(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;
|
||||
|
|
|
|||
|
|
@ -274,7 +274,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
|
|||
pindexNew->nStatus = diskindex.nStatus;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
|
||||
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams))
|
||||
// Block index guts do not include the payload, so we cannot check the POW for
|
||||
// signets here
|
||||
if (!consensusParams.signature_pow && !CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams))
|
||||
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
|
||||
|
||||
pcursor->Next();
|
||||
|
|
|
|||
|
|
@ -950,13 +950,16 @@ std::string ArgsManager::GetChainName() const
|
|||
{
|
||||
bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
|
||||
bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
|
||||
bool signet = ArgsManagerHelper::GetNetBoolArg(*this, "-signet");
|
||||
|
||||
if (fTestNet && fRegTest)
|
||||
throw std::runtime_error("Invalid combination of -regtest and -testnet.");
|
||||
if (fTestNet + fRegTest + signet > 1)
|
||||
throw std::runtime_error("Invalid combination of -regtest, -testnet, and -signet.");
|
||||
if (fRegTest)
|
||||
return CBaseChainParams::REGTEST;
|
||||
if (fTestNet)
|
||||
return CBaseChainParams::TESTNET;
|
||||
if (signet)
|
||||
return CBaseChainParams::SIGNET;
|
||||
return CBaseChainParams::MAIN;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3887,6 +3887,100 @@ 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, BlockSignatureCreator(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 generate(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
|
|
@ -3934,6 +4028,76 @@ UniValue generate(const JSONRPCRequest& request)
|
|||
return generateBlocks(coinbase_script, num_generate, max_tries, true);
|
||||
}
|
||||
|
||||
#include <miner.h>
|
||||
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;
|
||||
return submitblock(req);
|
||||
}
|
||||
|
||||
return HexStr(ssBlock.begin(), ssBlock.end());
|
||||
}
|
||||
|
||||
UniValue rescanblockchain(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
|
|
@ -4839,6 +5003,10 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
|
||||
{ "wallet", "setlabel", &setlabel, {"address","label"} },
|
||||
|
||||
// sigtest mining
|
||||
{ "wallet", "signblock", &signblock, {"blockhex"} },
|
||||
{ "mining", "getnewblockhex", &getnewblockhex, {"broadcast"} },
|
||||
|
||||
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue