mirror of https://github.com/bitcoin/bitcoin.git
Compare commits
24 Commits
2aa354d442
...
0031aa65c0
Author | SHA1 | Date |
---|---|---|
|
0031aa65c0 | |
|
b510893d00 | |
|
ec5841888d | |
|
d735e2e9b3 | |
|
de1dc6b47b | |
|
3635d62f5a | |
|
2e09d66fbb | |
|
156927903d | |
|
e1a1b14c93 | |
|
93a70a42d3 | |
|
6de8051263 | |
|
46135d90ea | |
|
771978952a | |
|
7ae0497eef | |
|
6d409d5970 | |
|
f046286c0c | |
|
652424ad16 | |
|
417437eb01 | |
|
3cbbcb66ef | |
|
bddcadee82 | |
|
a5ead122fe | |
|
4577fb2b1e | |
|
a3986935f0 | |
|
5db8cd2d37 |
|
@ -211,11 +211,16 @@ jobs:
|
|||
steps:
|
||||
- *CHECKOUT
|
||||
|
||||
- name: Configure Developer Command Prompt for Microsoft Visual C++
|
||||
# Using microsoft/setup-msbuild is not enough.
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
- &SET_UP_VS
|
||||
name: Set up VS Developer Prompt
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
$installationPath = & $vswherePath -latest -property installationPath
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | foreach-object {
|
||||
$name, $value = $_ -split '=', 2
|
||||
echo "$name=$value" >> $env:GITHUB_ENV
|
||||
}
|
||||
|
||||
- name: Get tool information
|
||||
shell: pwsh
|
||||
|
@ -263,14 +268,26 @@ jobs:
|
|||
run: |
|
||||
cmake --build . -j $NUMBER_OF_PROCESSORS --config Release
|
||||
|
||||
- name: Get bitcoind manifest
|
||||
- name: Check executable manifests
|
||||
if: matrix.job-type == 'standard'
|
||||
working-directory: build
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -out:bitcoind.manifest
|
||||
cat bitcoind.manifest
|
||||
echo
|
||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -validate_manifest
|
||||
mt.exe -nologo -inputresource:bin\Release\bitcoind.exe -out:bitcoind.manifest
|
||||
Get-Content bitcoind.manifest
|
||||
|
||||
Get-ChildItem -Filter "bin\Release\*.exe" | ForEach-Object {
|
||||
$exeName = $_.Name
|
||||
|
||||
# Skip as they currently do not have manifests
|
||||
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe" -or $exeName -eq "test_bitcoin-qt.exe") {
|
||||
Write-Host "Skipping $exeName (no manifest present)"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Checking $exeName"
|
||||
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||
}
|
||||
|
||||
- name: Run test suite
|
||||
if: matrix.job-type == 'standard'
|
||||
|
@ -370,19 +387,26 @@ jobs:
|
|||
- name: Run bitcoind.exe
|
||||
run: ./bin/bitcoind.exe -version
|
||||
|
||||
- name: Find mt.exe tool
|
||||
shell: pwsh
|
||||
run: |
|
||||
$sdk_dir = (Get-ItemProperty 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots' -Name KitsRoot10).KitsRoot10
|
||||
$sdk_latest = (Get-ChildItem "$sdk_dir\bin" -Directory | Where-Object { $_.Name -match '^\d+\.\d+\.\d+\.\d+$' } | Sort-Object Name -Descending | Select-Object -First 1).Name
|
||||
"MT_EXE=${sdk_dir}bin\${sdk_latest}\x64\mt.exe" >> $env:GITHUB_ENV
|
||||
- *SET_UP_VS
|
||||
|
||||
- name: Get bitcoind manifest
|
||||
shell: pwsh
|
||||
- name: Check executable manifests
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
||||
mt.exe -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
||||
Get-Content bitcoind.manifest
|
||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -validate_manifest
|
||||
|
||||
Get-ChildItem -Filter "bin\*.exe" | ForEach-Object {
|
||||
$exeName = $_.Name
|
||||
|
||||
# Skip as they currently do not have manifests
|
||||
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe") {
|
||||
Write-Host "Skipping $exeName (no manifest present)"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Checking $exeName"
|
||||
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||
}
|
||||
|
||||
- name: Run unit tests
|
||||
# Can't use ctest here like other jobs as we don't have a CMake build tree.
|
||||
|
|
|
@ -36,9 +36,8 @@ define fetch_file_inner
|
|||
endef
|
||||
|
||||
define fetch_file
|
||||
( test -f $$($(1)_source_dir)/$(4) || \
|
||||
( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5))))
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5)))
|
||||
endef
|
||||
|
||||
# Shell script to create a source tarball in $(1)_source from local directory
|
||||
|
@ -109,7 +108,7 @@ $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/
|
|||
$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources)
|
||||
|
||||
#stamps
|
||||
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash
|
||||
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_version)-$($(1)_sha256_hash).hash
|
||||
$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted
|
||||
$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed
|
||||
$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned
|
||||
|
@ -247,7 +246,6 @@ endif
|
|||
$($(1)_fetched):
|
||||
mkdir -p $$(@D) $(SOURCES_PATH)
|
||||
rm -f $$@
|
||||
touch $$@
|
||||
cd $$(@D); $($(1)_fetch_cmds)
|
||||
cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);)
|
||||
touch $$@
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package=qrencode
|
||||
$(package)_version=4.1.1
|
||||
$(package)_download_path=https://fukuchi.org/works/qrencode/
|
||||
$(package)_download_path=https://github.com/fukuchi/libqrencode/archive/refs/tags/
|
||||
$(package)_download_file=v$($(package)_version).tar.gz
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=da448ed4f52aba6bcb0cd48cac0dd51b8692bccc4cd127431402fca6f8171e8e
|
||||
$(package)_sha256_hash=5385bc1b8c2f20f3b91d258bf8ccc8cf62023935df2d2676b5b67049f31a049c
|
||||
$(package)_patches=cmake_fixups.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
|
|
|
@ -24,7 +24,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
|
|||
{
|
||||
ECC_Context ecc_context{};
|
||||
|
||||
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const int witnessversion = 0;
|
||||
|
||||
// Key pair.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <chain.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/check.h>
|
||||
#include <util/time.h>
|
||||
|
||||
std::string CBlockFileInfo::ToString() const
|
||||
|
@ -158,18 +159,26 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr
|
|||
/** Find the last common ancestor two blocks have.
|
||||
* Both pa and pb must be non-nullptr. */
|
||||
const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
|
||||
// First rewind to the last common height (the forking point cannot be past one of the two).
|
||||
if (pa->nHeight > pb->nHeight) {
|
||||
pa = pa->GetAncestor(pb->nHeight);
|
||||
} else if (pb->nHeight > pa->nHeight) {
|
||||
pb = pb->GetAncestor(pa->nHeight);
|
||||
}
|
||||
|
||||
while (pa != pb && pa && pb) {
|
||||
while (pa != pb) {
|
||||
// Jump back until pa and pb have a common "skip" ancestor.
|
||||
while (pa->pskip != pb->pskip) {
|
||||
// This logic relies on the property that equal-height blocks have equal-height skip
|
||||
// pointers.
|
||||
Assume(pa->nHeight == pb->nHeight);
|
||||
Assume(pa->pskip->nHeight == pb->pskip->nHeight);
|
||||
pa = pa->pskip;
|
||||
pb = pb->pskip;
|
||||
}
|
||||
// At this point, pa and pb are different, but have equal pskip. The forking point lies in
|
||||
// between pa/pb on the one end, and pa->pskip/pb->pskip on the other end.
|
||||
pa = pa->pprev;
|
||||
pb = pb->pprev;
|
||||
}
|
||||
|
||||
// Eventually all chain branches meet at the genesis block.
|
||||
assert(pa == pb);
|
||||
return pa;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef BITCOIN_CONSENSUS_PARAMS_H
|
||||
#define BITCOIN_CONSENSUS_PARAMS_H
|
||||
|
||||
#include <script/verify_flags.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <array>
|
||||
|
@ -89,7 +90,7 @@ struct Params {
|
|||
* - buried in the chain, and
|
||||
* - fail if the default script verify flags are applied.
|
||||
*/
|
||||
std::map<uint256, uint32_t> script_flag_exceptions;
|
||||
std::map<uint256, script_verify_flags> script_flag_exceptions;
|
||||
/** Block height and hash at which BIP34 becomes active */
|
||||
int BIP34Height;
|
||||
uint256 BIP34Hash;
|
||||
|
|
|
@ -140,7 +140,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
|||
return nSigOps;
|
||||
}
|
||||
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags)
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags)
|
||||
{
|
||||
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_CONSENSUS_TX_VERIFY_H
|
||||
|
||||
#include <consensus/amount.h>
|
||||
#include <script/verify_flags.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
@ -52,7 +53,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma
|
|||
* @param[in] flags Script verification flags
|
||||
* @return Total signature operation cost of tx
|
||||
*/
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags);
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags);
|
||||
|
||||
/**
|
||||
* Check if transaction is final and can be included in a block with the
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
#include <consensus/params.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
struct VBDeploymentInfo {
|
||||
/** Deployment name */
|
||||
|
|
|
@ -98,7 +98,7 @@ static constexpr unsigned int MAX_DUST_OUTPUTS_PER_TX{1};
|
|||
* Note that this does not affect consensus validity; see GetBlockScriptFlags()
|
||||
* for that.
|
||||
*/
|
||||
static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||
static constexpr script_verify_flags MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||
SCRIPT_VERIFY_DERSIG |
|
||||
SCRIPT_VERIFY_NULLDUMMY |
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
||||
|
@ -112,7 +112,7 @@ static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
|||
* the additional (non-mandatory) rules here, to improve forwards and
|
||||
* backwards compatibility.
|
||||
*/
|
||||
static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
||||
static constexpr script_verify_flags STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
||||
SCRIPT_VERIFY_STRICTENC |
|
||||
SCRIPT_VERIFY_MINIMALDATA |
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
|
||||
|
@ -128,7 +128,7 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI
|
|||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
|
||||
|
||||
/** 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};
|
||||
static constexpr script_verify_flags STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
|
||||
|
||||
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
|
||||
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
|
||||
|
|
|
@ -88,6 +88,14 @@ UniValue WriteUTXOSnapshot(
|
|||
const fs::path& temppath,
|
||||
const std::function<void()>& interruption_point = {});
|
||||
|
||||
UniValue CreateRolledBackUTXOSnapshot(
|
||||
NodeContext& node,
|
||||
Chainstate& chainstate,
|
||||
const CBlockIndex* target,
|
||||
AutoFile&& afile,
|
||||
const fs::path& path,
|
||||
const fs::path& tmppath);
|
||||
|
||||
/* Calculate the difficulty for a given block index.
|
||||
*/
|
||||
double GetDifficulty(const CBlockIndex& blockindex)
|
||||
|
@ -1469,6 +1477,9 @@ RPCHelpMan getdeploymentinfo()
|
|||
RPCResult::Type::OBJ, "", "", {
|
||||
{RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
|
||||
{RPCResult::Type::NUM, "height", "requested block height (or tip)"},
|
||||
{RPCResult::Type::ARR, "script_flags", "script verify flags for the block", {
|
||||
{RPCResult::Type::STR, "flag", "a script verify flag"},
|
||||
}},
|
||||
{RPCResult::Type::OBJ_DYN, "deployments", "", {
|
||||
{RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
|
||||
}},
|
||||
|
@ -1495,6 +1506,12 @@ RPCHelpMan getdeploymentinfo()
|
|||
UniValue deploymentinfo(UniValue::VOBJ);
|
||||
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
|
||||
deploymentinfo.pushKV("height", blockindex->nHeight);
|
||||
{
|
||||
const auto flagnames = GetScriptFlagNames(GetBlockScriptFlags(*blockindex, chainman));
|
||||
UniValue uv_flagnames(UniValue::VARR);
|
||||
uv_flagnames.push_backV(flagnames.begin(), flagnames.end());
|
||||
deploymentinfo.pushKV("script_flags", uv_flagnames);
|
||||
}
|
||||
deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
|
||||
return deploymentinfo;
|
||||
},
|
||||
|
@ -2974,42 +2991,6 @@ static RPCHelpMan getblockfilter()
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* RAII class that disables the network in its constructor and enables it in its
|
||||
* destructor.
|
||||
*/
|
||||
class NetworkDisable
|
||||
{
|
||||
CConnman& m_connman;
|
||||
public:
|
||||
NetworkDisable(CConnman& connman) : m_connman(connman) {
|
||||
m_connman.SetNetworkActive(false);
|
||||
if (m_connman.GetNetworkActive()) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Network activity could not be suspended.");
|
||||
}
|
||||
};
|
||||
~NetworkDisable() {
|
||||
m_connman.SetNetworkActive(true);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII class that temporarily rolls back the local chain in it's constructor
|
||||
* and rolls it forward again in it's destructor.
|
||||
*/
|
||||
class TemporaryRollback
|
||||
{
|
||||
ChainstateManager& m_chainman;
|
||||
const CBlockIndex& m_invalidate_index;
|
||||
public:
|
||||
TemporaryRollback(ChainstateManager& chainman, const CBlockIndex& index) : m_chainman(chainman), m_invalidate_index(index) {
|
||||
InvalidateBlock(m_chainman, m_invalidate_index.GetBlockHash());
|
||||
};
|
||||
~TemporaryRollback() {
|
||||
ReconsiderBlock(m_chainman, m_invalidate_index.GetBlockHash());
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the UTXO set to a file for loading elsewhere.
|
||||
*
|
||||
|
@ -3020,9 +3001,8 @@ static RPCHelpMan dumptxoutset()
|
|||
return RPCHelpMan{
|
||||
"dumptxoutset",
|
||||
"Write the serialized UTXO set to a file. This can be used in loadtxoutset afterwards if this snapshot height is supported in the chainparams as well.\n\n"
|
||||
"Unless the \"latest\" type is requested, the node will roll back to the requested height and network activity will be suspended during this process. "
|
||||
"Because of this it is discouraged to interact with the node in any other way during the execution of this call to avoid inconsistent results and race conditions, particularly RPCs that interact with blockstorage.\n\n"
|
||||
"This call may take several minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
|
||||
"This creates a temporary UTXO database when rolling back, keeping the main chain intact. Should the node experience an unclean shutdown the temporary database may need to be removed from the datadir manually.\n\n"
|
||||
"This call may take several minutes for deep rollbacks. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
|
||||
{
|
||||
{"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
|
||||
{"type", RPCArg::Type::STR, RPCArg::Default(""), "The type of snapshot to create. Can be \"latest\" to create a snapshot of the current UTXO set or \"rollback\" to temporarily roll back the state of the node to a historical block before creating the snapshot of a historical UTXO set. This parameter can be omitted if a separate \"rollback\" named parameter is specified indicating the height or hash of a specific historical block. If \"rollback\" is specified and separate \"rollback\" named parameter is not specified, this will roll back to the latest valid snapshot block that can currently be loaded with loadtxoutset."},
|
||||
|
@ -3094,16 +3074,13 @@ static RPCHelpMan dumptxoutset()
|
|||
"Couldn't open file " + temppath.utf8string() + " for writing.");
|
||||
}
|
||||
|
||||
CConnman& connman = EnsureConnman(node);
|
||||
const CBlockIndex* invalidate_index{nullptr};
|
||||
std::optional<NetworkDisable> disable_network;
|
||||
std::optional<TemporaryRollback> temporary_rollback;
|
||||
|
||||
// If the user wants to dump the txoutset of the current tip, we don't have
|
||||
// to roll back at all
|
||||
if (target_index != tip) {
|
||||
// If the node is running in pruned mode we ensure all necessary block
|
||||
// data is available before starting to roll back.
|
||||
UniValue result;
|
||||
Chainstate& chainstate{node.chainman->ActiveChainstate()};
|
||||
if (target_index == tip) {
|
||||
// Dump the txoutset of the current tip
|
||||
result = CreateUTXOSnapshot(node, chainstate, std::move(afile), path, temppath);
|
||||
} else {
|
||||
// Check pruning constraints before attempting rollback
|
||||
if (node.chainman->m_blockman.IsPruneMode()) {
|
||||
LOCK(node.chainman->GetMutex());
|
||||
const CBlockIndex* current_tip{node.chainman->ActiveChain().Tip()};
|
||||
|
@ -3113,56 +3090,14 @@ static RPCHelpMan dumptxoutset()
|
|||
}
|
||||
}
|
||||
|
||||
// Suspend network activity for the duration of the process when we are
|
||||
// rolling back the chain to get a utxo set from a past height. We do
|
||||
// this so we don't punish peers that send us that send us data that
|
||||
// seems wrong in this temporary state. For example a normal new block
|
||||
// would be classified as a block connecting an invalid block.
|
||||
// Skip if the network is already disabled because this
|
||||
// automatically re-enables the network activity at the end of the
|
||||
// process which may not be what the user wants.
|
||||
if (connman.GetNetworkActive()) {
|
||||
disable_network.emplace(connman);
|
||||
}
|
||||
|
||||
invalidate_index = WITH_LOCK(::cs_main, return node.chainman->ActiveChain().Next(target_index));
|
||||
temporary_rollback.emplace(*node.chainman, *invalidate_index);
|
||||
result = CreateRolledBackUTXOSnapshot(node,
|
||||
chainstate,
|
||||
target_index,
|
||||
std::move(afile),
|
||||
path,
|
||||
temppath);
|
||||
}
|
||||
|
||||
Chainstate* chainstate;
|
||||
std::unique_ptr<CCoinsViewCursor> cursor;
|
||||
CCoinsStats stats;
|
||||
{
|
||||
// Lock the chainstate before calling PrepareUtxoSnapshot, to be able
|
||||
// to get a UTXO database cursor while the chain is pointing at the
|
||||
// target block. After that, release the lock while calling
|
||||
// WriteUTXOSnapshot. The cursor will remain valid and be used by
|
||||
// WriteUTXOSnapshot to write a consistent snapshot even if the
|
||||
// chainstate changes.
|
||||
LOCK(node.chainman->GetMutex());
|
||||
chainstate = &node.chainman->ActiveChainstate();
|
||||
// In case there is any issue with a block being read from disk we need
|
||||
// to stop here, otherwise the dump could still be created for the wrong
|
||||
// height.
|
||||
// The new tip could also not be the target block if we have a stale
|
||||
// sister block of invalidate_index. This block (or a descendant) would
|
||||
// be activated as the new tip and we would not get to new_tip_index.
|
||||
if (target_index != chainstate->m_chain.Tip()) {
|
||||
LogWarning("dumptxoutset failed to roll back to requested height, reverting to tip.\n");
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Could not roll back to requested height.");
|
||||
} else {
|
||||
std::tie(cursor, stats, tip) = PrepareUTXOSnapshot(*chainstate, node.rpc_interruption_point);
|
||||
}
|
||||
}
|
||||
|
||||
UniValue result = WriteUTXOSnapshot(*chainstate,
|
||||
cursor.get(),
|
||||
&stats,
|
||||
tip,
|
||||
std::move(afile),
|
||||
path,
|
||||
temppath,
|
||||
node.rpc_interruption_point);
|
||||
fs::rename(temppath, path);
|
||||
|
||||
result.pushKV("path", path.utf8string());
|
||||
|
@ -3171,6 +3106,152 @@ static RPCHelpMan dumptxoutset()
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* RAII class that creates a temporary database directory in its constructor
|
||||
* and removes it in its destructor.
|
||||
*/
|
||||
class TemporaryUTXODatabase
|
||||
{
|
||||
fs::path m_path;
|
||||
public:
|
||||
TemporaryUTXODatabase(const fs::path& path) : m_path(path) {
|
||||
fs::create_directories(m_path);
|
||||
}
|
||||
~TemporaryUTXODatabase() {
|
||||
try {
|
||||
fs::remove_all(m_path);
|
||||
} catch (...) {
|
||||
LogInfo("Failed to clean up temporary UTXO database at %s, please remove it manually.",
|
||||
fs::PathToString(m_path));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UniValue CreateRolledBackUTXOSnapshot(
|
||||
NodeContext& node,
|
||||
Chainstate& chainstate,
|
||||
const CBlockIndex* target,
|
||||
AutoFile&& afile,
|
||||
const fs::path& path,
|
||||
const fs::path& tmppath)
|
||||
{
|
||||
// Create a temporary leveldb to store the UTXO set that is being rolled back
|
||||
std::string temp_db_name{strprintf("temp_utxo_%d", target->nHeight)};
|
||||
fs::path temp_db_path{fsbridge::AbsPathJoin(tmppath.parent_path(), fs::u8path(temp_db_name))};
|
||||
TemporaryUTXODatabase temp_db_cleaner{temp_db_path};
|
||||
|
||||
// Create temporary database
|
||||
DBParams db_params{
|
||||
.path = temp_db_path,
|
||||
.cache_bytes = size_t(1) << 24, // 16MB cache
|
||||
.memory_only = false,
|
||||
.wipe_data = true,
|
||||
.obfuscate = false,
|
||||
.options = DBOptions{}
|
||||
};
|
||||
|
||||
std::unique_ptr<CCoinsViewDB> temp_db = std::make_unique<CCoinsViewDB>(
|
||||
std::move(db_params),
|
||||
CoinsViewOptions{}
|
||||
);
|
||||
|
||||
LogInfo("Copying current UTXO set to temporary database.");
|
||||
{
|
||||
WITH_LOCK(::cs_main, chainstate.ForceFlushStateToDisk());
|
||||
CCoinsViewCache temp_cache(temp_db.get());
|
||||
temp_cache.SetBestBlock(chainstate.m_chain.Tip()->GetBlockHash());
|
||||
|
||||
std::unique_ptr<CCoinsViewCursor> cursor;
|
||||
WITH_LOCK(::cs_main, cursor = chainstate.CoinsDB().Cursor());
|
||||
|
||||
size_t coins_count = 0;
|
||||
while (cursor->Valid()) {
|
||||
node.rpc_interruption_point();
|
||||
|
||||
COutPoint key;
|
||||
Coin coin;
|
||||
if (cursor->GetKey(key) && cursor->GetValue(coin)) {
|
||||
temp_cache.AddCoin(key, std::move(coin), false);
|
||||
coins_count++;
|
||||
|
||||
// Log every 10M coins (optimized for mainnet)
|
||||
if (coins_count % 10'000'000 == 0) {
|
||||
LogInfo("Copying UTXO set: %uM coins copied.", coins_count / 1'000'000);
|
||||
}
|
||||
|
||||
// Flush periodically
|
||||
if (coins_count % 100'000 == 0) {
|
||||
temp_cache.Flush();
|
||||
}
|
||||
}
|
||||
cursor->Next();
|
||||
}
|
||||
|
||||
temp_cache.Flush();
|
||||
LogInfo("UTXO set copy complete: %u coins total", coins_count);
|
||||
}
|
||||
|
||||
LogInfo("Rolling back from height %d to %d", chainstate.m_chain.Tip()->nHeight, target->nHeight);
|
||||
|
||||
const CBlockIndex* block_index{chainstate.m_chain.Tip()};
|
||||
CCoinsViewCache rollback_cache(temp_db.get());
|
||||
rollback_cache.SetBestBlock(block_index->GetBlockHash());
|
||||
size_t blocks_processed = 0;
|
||||
DisconnectResult res;
|
||||
|
||||
while (block_index->nHeight > target->nHeight) {
|
||||
node.rpc_interruption_point();
|
||||
|
||||
CBlock block;
|
||||
if (!node.chainman->m_blockman.ReadBlock(block, *block_index)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR,
|
||||
strprintf("Failed to read block at height %d", block_index->nHeight));
|
||||
}
|
||||
|
||||
WITH_LOCK(::cs_main, res = chainstate.DisconnectBlock(block, block_index, rollback_cache));
|
||||
if (res == DISCONNECT_FAILED) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR,
|
||||
strprintf("Failed to roll back block at height %d", block_index->nHeight));
|
||||
}
|
||||
|
||||
blocks_processed++;
|
||||
if (blocks_processed % 500 == 0) {
|
||||
LogInfo("Rolled back %d blocks.", blocks_processed);
|
||||
rollback_cache.Flush();
|
||||
}
|
||||
|
||||
block_index = block_index->pprev;
|
||||
}
|
||||
|
||||
rollback_cache.SetBestBlock(target->GetBlockHash());
|
||||
rollback_cache.Flush();
|
||||
|
||||
LogInfo("Rollback complete. Computing UTXO statistics for created txoutset dump.");
|
||||
std::optional<CCoinsStats> maybe_stats = GetUTXOStats(temp_db.get(),
|
||||
chainstate.m_blockman,
|
||||
CoinStatsHashType::HASH_SERIALIZED,
|
||||
node.rpc_interruption_point);
|
||||
|
||||
if (!maybe_stats) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to compute UTXO statistics");
|
||||
}
|
||||
|
||||
std::unique_ptr<CCoinsViewCursor> pcursor{temp_db->Cursor()};
|
||||
if (!pcursor) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to create UTXO cursor");
|
||||
}
|
||||
|
||||
LogInfo("Writing snapshot to disk.");
|
||||
return WriteUTXOSnapshot(chainstate,
|
||||
pcursor.get(),
|
||||
&(*maybe_stats),
|
||||
target,
|
||||
std::move(afile),
|
||||
path,
|
||||
tmppath,
|
||||
node.rpc_interruption_point);
|
||||
}
|
||||
|
||||
std::tuple<std::unique_ptr<CCoinsViewCursor>, CCoinsStats, const CBlockIndex*>
|
||||
PrepareUTXOSnapshot(
|
||||
Chainstate& chainstate,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <crypto/sha256.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/script.h>
|
||||
#include <tinyformat.h>
|
||||
#include <uint256.h>
|
||||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
@ -197,7 +198,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror) {
|
||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||
if (vchSig.size() == 0) {
|
||||
|
@ -214,7 +215,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
|||
return true;
|
||||
}
|
||||
|
||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, script_verify_flags flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
|
||||
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
||||
}
|
||||
|
@ -317,7 +318,7 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
{
|
||||
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
|
||||
|
||||
|
@ -343,7 +344,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
assert(sigversion == SigVersion::TAPSCRIPT);
|
||||
|
||||
|
@ -388,7 +389,7 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr
|
|||
* A return value of false means the script fails entirely. When true is returned, the
|
||||
* success variable indicates whether the signature check itself succeeded.
|
||||
*/
|
||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
switch (sigversion) {
|
||||
case SigVersion::BASE:
|
||||
|
@ -403,7 +404,7 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
|
|||
assert(false);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
static const CScriptNum bnZero(0);
|
||||
static const CScriptNum bnOne(1);
|
||||
|
@ -1233,7 +1234,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
return set_success(serror);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
{
|
||||
ScriptExecutionData execdata;
|
||||
return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
|
||||
|
@ -1824,7 +1825,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
|
|||
template class GenericTransactionSignatureChecker<CTransaction>;
|
||||
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
||||
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, script_verify_flags flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
|
||||
|
||||
|
@ -1909,7 +1910,7 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
|
|||
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
||||
}
|
||||
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||
{
|
||||
CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
|
||||
std::span stack{witness.stack};
|
||||
|
@ -1994,7 +1995,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
|
||||
}
|
||||
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
{
|
||||
static const CScriptWitness emptyWitness;
|
||||
if (witness == nullptr) {
|
||||
|
@ -2131,7 +2132,7 @@ size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& wi
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags)
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags)
|
||||
{
|
||||
static const CScriptWitness witnessEmpty;
|
||||
|
||||
|
@ -2161,3 +2162,48 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
|
||||
const std::map<std::string, script_verify_flag_name> g_verify_flag_names{
|
||||
FLAG_NAME(P2SH),
|
||||
FLAG_NAME(STRICTENC),
|
||||
FLAG_NAME(DERSIG),
|
||||
FLAG_NAME(LOW_S),
|
||||
FLAG_NAME(SIGPUSHONLY),
|
||||
FLAG_NAME(MINIMALDATA),
|
||||
FLAG_NAME(NULLDUMMY),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
|
||||
FLAG_NAME(CLEANSTACK),
|
||||
FLAG_NAME(MINIMALIF),
|
||||
FLAG_NAME(NULLFAIL),
|
||||
FLAG_NAME(CHECKLOCKTIMEVERIFY),
|
||||
FLAG_NAME(CHECKSEQUENCEVERIFY),
|
||||
FLAG_NAME(WITNESS),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
|
||||
FLAG_NAME(WITNESS_PUBKEYTYPE),
|
||||
FLAG_NAME(CONST_SCRIPTCODE),
|
||||
FLAG_NAME(TAPROOT),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
|
||||
FLAG_NAME(DISCOURAGE_OP_SUCCESS),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
|
||||
};
|
||||
#undef FLAG_NAME
|
||||
|
||||
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
if (flags == SCRIPT_VERIFY_NONE) {
|
||||
return res;
|
||||
}
|
||||
script_verify_flags leftover = flags;
|
||||
for (const auto& [name, flag] : g_verify_flag_names) {
|
||||
if ((flags & flag) != 0) {
|
||||
res.push_back(name);
|
||||
leftover &= ~flag;
|
||||
}
|
||||
}
|
||||
if (leftover != 0) {
|
||||
res.push_back(strprintf("0x%08x", leftover.as_int()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <hash.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/script_error.h> // IWYU pragma: export
|
||||
#include <script/verify_flags.h> // IWYU pragma: export
|
||||
#include <span.h>
|
||||
#include <uint256.h>
|
||||
|
||||
|
@ -42,35 +43,36 @@ enum
|
|||
* All flags are intended to be soft forks: the set of acceptable scripts under
|
||||
* flags (A | B) is a subset of the acceptable scripts under flag (A).
|
||||
*/
|
||||
enum : uint32_t {
|
||||
SCRIPT_VERIFY_NONE = 0,
|
||||
|
||||
static constexpr script_verify_flags SCRIPT_VERIFY_NONE{0};
|
||||
|
||||
enum class script_verify_flag_name : uint8_t {
|
||||
// Evaluate P2SH subscripts (BIP16).
|
||||
SCRIPT_VERIFY_P2SH = (1U << 0),
|
||||
SCRIPT_VERIFY_P2SH,
|
||||
|
||||
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||
// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
||||
// (not used or intended as a consensus rule).
|
||||
SCRIPT_VERIFY_STRICTENC = (1U << 1),
|
||||
SCRIPT_VERIFY_STRICTENC,
|
||||
|
||||
// Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
|
||||
SCRIPT_VERIFY_DERSIG = (1U << 2),
|
||||
SCRIPT_VERIFY_DERSIG,
|
||||
|
||||
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||
// (BIP62 rule 5).
|
||||
SCRIPT_VERIFY_LOW_S = (1U << 3),
|
||||
SCRIPT_VERIFY_LOW_S,
|
||||
|
||||
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
|
||||
SCRIPT_VERIFY_NULLDUMMY = (1U << 4),
|
||||
SCRIPT_VERIFY_NULLDUMMY,
|
||||
|
||||
// Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
|
||||
SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5),
|
||||
SCRIPT_VERIFY_SIGPUSHONLY,
|
||||
|
||||
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
||||
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
||||
// any other push causes the script to fail (BIP62 rule 3).
|
||||
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
||||
SCRIPT_VERIFY_MINIMALDATA = (1U << 6),
|
||||
SCRIPT_VERIFY_MINIMALDATA,
|
||||
|
||||
// Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||
//
|
||||
|
@ -82,7 +84,7 @@ enum : uint32_t {
|
|||
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
||||
// NOPs that have associated forks to give them new meaning (CLTV, CSV)
|
||||
// are not subject to this rule.
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
|
||||
|
||||
// Require that only a single stack element remains after evaluation. This changes the success criterion from
|
||||
// "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
||||
|
@ -91,64 +93,72 @@ enum : uint32_t {
|
|||
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
|
||||
// Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
|
||||
// consensus rules. It is automatic there and does not need this flag.
|
||||
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
|
||||
SCRIPT_VERIFY_CLEANSTACK,
|
||||
|
||||
// Verify CHECKLOCKTIMEVERIFY
|
||||
//
|
||||
// See BIP65 for details.
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
||||
|
||||
// support CHECKSEQUENCEVERIFY opcode
|
||||
//
|
||||
// See BIP112 for details
|
||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10),
|
||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
||||
|
||||
// Support segregated witness
|
||||
//
|
||||
SCRIPT_VERIFY_WITNESS = (1U << 11),
|
||||
SCRIPT_VERIFY_WITNESS,
|
||||
|
||||
// Making v1-v16 witness program non-standard
|
||||
//
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
|
||||
|
||||
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
||||
//
|
||||
// Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
|
||||
// rules. It is automatic there and does not depend on this flag.
|
||||
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
|
||||
SCRIPT_VERIFY_MINIMALIF,
|
||||
|
||||
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
|
||||
//
|
||||
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
|
||||
SCRIPT_VERIFY_NULLFAIL,
|
||||
|
||||
// Public keys in segregated witness scripts must be compressed
|
||||
//
|
||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
|
||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
|
||||
|
||||
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
||||
//
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE,
|
||||
|
||||
// Taproot/Tapscript validation (BIPs 341 & 342)
|
||||
//
|
||||
SCRIPT_VERIFY_TAPROOT = (1U << 17),
|
||||
SCRIPT_VERIFY_TAPROOT,
|
||||
|
||||
// Making unknown Taproot leaf versions non-standard
|
||||
//
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
|
||||
|
||||
// Making unknown OP_SUCCESS non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS,
|
||||
|
||||
// Making unknown public key versions (in BIP 342 scripts) non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
|
||||
|
||||
// Constants to point to the highest flag in use. Add new flags above this line.
|
||||
//
|
||||
SCRIPT_VERIFY_END_MARKER
|
||||
};
|
||||
using enum script_verify_flag_name;
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
static constexpr int MAX_SCRIPT_VERIFY_FLAGS_BITS = static_cast<int>(SCRIPT_VERIFY_END_MARKER);
|
||||
|
||||
// assert there is still a spare bit
|
||||
static_assert(0 < MAX_SCRIPT_VERIFY_FLAGS_BITS && MAX_SCRIPT_VERIFY_FLAGS_BITS <= 63);
|
||||
|
||||
static constexpr script_verify_flags::value_type MAX_SCRIPT_VERIFY_FLAGS = ((script_verify_flags::value_type{1} << MAX_SCRIPT_VERIFY_FLAGS_BITS) - 1);
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror);
|
||||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
|
@ -363,12 +373,16 @@ uint256 ComputeTapbranchHash(std::span<const unsigned char> a, std::span<const u
|
|||
* Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
|
||||
uint256 ComputeTaprootMerkleRoot(std::span<const unsigned char> control, const uint256& tapleaf_hash);
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags);
|
||||
|
||||
int FindAndDelete(CScript& script, const CScript& b);
|
||||
|
||||
extern const std::map<std::string, script_verify_flag_name> g_verify_flag_names;
|
||||
|
||||
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||
#define BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
enum class script_verify_flag_name : uint8_t;
|
||||
|
||||
class script_verify_flags
|
||||
{
|
||||
public:
|
||||
using value_type = uint64_t;
|
||||
|
||||
consteval script_verify_flags() = default;
|
||||
|
||||
// also allow construction with hard-coded 0 (but not other integers)
|
||||
consteval explicit(false) script_verify_flags(value_type f) : m_value{f} { if (f != 0) throw 0; }
|
||||
|
||||
// implicit construction from a hard-coded SCRIPT_VERIFY_* constant is also okay
|
||||
constexpr explicit(false) script_verify_flags(script_verify_flag_name f) : m_value{value_type{1} << static_cast<uint8_t>(f)} { }
|
||||
|
||||
// rule of 5
|
||||
constexpr script_verify_flags(const script_verify_flags&) = default;
|
||||
constexpr script_verify_flags(script_verify_flags&&) = default;
|
||||
constexpr script_verify_flags& operator=(const script_verify_flags&) = default;
|
||||
constexpr script_verify_flags& operator=(script_verify_flags&&) = default;
|
||||
constexpr ~script_verify_flags() = default;
|
||||
|
||||
// integer conversion needs to be very explicit
|
||||
static constexpr script_verify_flags from_int(value_type f) { script_verify_flags r; r.m_value = f; return r; }
|
||||
constexpr value_type as_int() const { return m_value; }
|
||||
|
||||
// bitwise operations
|
||||
constexpr script_verify_flags operator~() const { return from_int(~m_value); }
|
||||
friend constexpr script_verify_flags operator|(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value | b.m_value); }
|
||||
friend constexpr script_verify_flags operator&(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value & b.m_value); }
|
||||
|
||||
// in-place bitwise operations
|
||||
constexpr script_verify_flags& operator|=(script_verify_flags vf) { m_value |= vf.m_value; return *this; }
|
||||
constexpr script_verify_flags& operator&=(script_verify_flags vf) { m_value &= vf.m_value; return *this; }
|
||||
|
||||
// tests
|
||||
constexpr explicit operator bool() const { return m_value != 0; }
|
||||
constexpr bool operator==(script_verify_flags other) const { return m_value == other.m_value; }
|
||||
|
||||
/** Compare two script_verify_flags. <, >, <=, and >= are auto-generated from this. */
|
||||
friend constexpr std::strong_ordering operator<=>(const script_verify_flags& a, const script_verify_flags& b) noexcept
|
||||
{
|
||||
return a.m_value <=> b.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type m_value{0}; // default value is SCRIPT_VERIFY_NONE
|
||||
};
|
||||
|
||||
inline constexpr script_verify_flags operator~(script_verify_flag_name f)
|
||||
{
|
||||
return ~script_verify_flags{f};
|
||||
}
|
||||
|
||||
inline constexpr script_verify_flags operator|(script_verify_flag_name f1, script_verify_flag_name f2)
|
||||
{
|
||||
return script_verify_flags{f1} | f2;
|
||||
}
|
||||
|
||||
#endif // BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
|
||||
|
||||
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
||||
static constexpr script_verify_flags BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
||||
static bool FetchAndClearCommitmentSection(const std::span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ add_executable(test_bitcoin
|
|||
bloom_tests.cpp
|
||||
bswap_tests.cpp
|
||||
caches_tests.cpp
|
||||
chain_tests.cpp
|
||||
chainstate_write_tests.cpp
|
||||
checkqueue_tests.cpp
|
||||
cluster_linearize_tests.cpp
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <chain.h>
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(chain_tests, BasicTestingSetup)
|
||||
|
||||
namespace {
|
||||
|
||||
const CBlockIndex* NaiveGetAncestor(const CBlockIndex* a, int height)
|
||||
{
|
||||
while (a->nHeight > height) {
|
||||
a = a->pprev;
|
||||
}
|
||||
BOOST_REQUIRE_EQUAL(a->nHeight, height);
|
||||
return a;
|
||||
}
|
||||
|
||||
const CBlockIndex* NaiveLastCommonAncestor(const CBlockIndex* a, const CBlockIndex* b)
|
||||
{
|
||||
while (a->nHeight > b->nHeight) {
|
||||
a = a->pprev;
|
||||
}
|
||||
while (b->nHeight > a->nHeight) {
|
||||
b = b->pprev;
|
||||
}
|
||||
while (a != b) {
|
||||
BOOST_REQUIRE_EQUAL(a->nHeight, b->nHeight);
|
||||
a = a->pprev;
|
||||
b = b->pprev;
|
||||
}
|
||||
BOOST_REQUIRE_EQUAL(a, b);
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(chain_test)
|
||||
{
|
||||
FastRandomContext ctx;
|
||||
std::vector<std::unique_ptr<CBlockIndex>> block_index;
|
||||
// Run 10 iterations of the whole test.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
block_index.clear();
|
||||
// Create genesis block.
|
||||
auto genesis = std::make_unique<CBlockIndex>();
|
||||
genesis->nHeight = 0;
|
||||
block_index.push_back(std::move(genesis));
|
||||
// Create 10000 more blocks.
|
||||
for (int b = 0; b < 10000; ++b) {
|
||||
auto new_index = std::make_unique<CBlockIndex>();
|
||||
// 95% of blocks build on top of the last block; the others fork off randomly.
|
||||
if (ctx.randrange(20) != 0) {
|
||||
new_index->pprev = block_index.back().get();
|
||||
} else {
|
||||
new_index->pprev = block_index[ctx.randrange(block_index.size())].get();
|
||||
}
|
||||
new_index->nHeight = new_index->pprev->nHeight + 1;
|
||||
new_index->BuildSkip();
|
||||
block_index.push_back(std::move(new_index));
|
||||
}
|
||||
// Run 10000 random GetAncestor queries.
|
||||
for (int q = 0; q < 10000; ++q) {
|
||||
const CBlockIndex* block = block_index[ctx.randrange(block_index.size())].get();
|
||||
unsigned height = ctx.randrange<unsigned>(block->nHeight + 1);
|
||||
const CBlockIndex* result = block->GetAncestor(height);
|
||||
BOOST_CHECK(result == NaiveGetAncestor(block, height));
|
||||
}
|
||||
// Run 10000 random LastCommonAncestor queries.
|
||||
for (int q = 0; q < 10000; ++q) {
|
||||
const CBlockIndex* block1 = block_index[ctx.randrange(block_index.size())].get();
|
||||
const CBlockIndex* block2 = block_index[ctx.randrange(block_index.size())].get();
|
||||
const CBlockIndex* result = LastCommonAncestor(block1, block2);
|
||||
BOOST_CHECK(result == NaiveLastCommonAncestor(block1, block2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -288,7 +288,7 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend
|
|||
// consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
|
||||
return;
|
||||
}
|
||||
const auto flags{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
|
||||
// Avoid:
|
||||
// script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
FUZZ_TARGET(eval_script)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
const std::vector<uint8_t> script_bytes = [&] {
|
||||
if (fuzzed_data_provider.remaining_bytes() != 0) {
|
||||
return fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
|
||||
|
|
|
@ -118,8 +118,8 @@ FUZZ_TARGET(script, .init = initialize_script)
|
|||
(void)FindAndDelete(script_mut, *other_script);
|
||||
}
|
||||
const std::vector<std::string> random_string_vector = ConsumeRandomLengthStringVector(fuzzed_data_provider);
|
||||
const uint32_t u32{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
const uint32_t flags{u32 | SCRIPT_VERIFY_P2SH};
|
||||
const auto flags_rand{fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>()};
|
||||
const auto flags = script_verify_flags::from_int(flags_rand) | SCRIPT_VERIFY_P2SH;
|
||||
{
|
||||
CScriptWitness wit;
|
||||
for (const auto& s : random_string_vector) {
|
||||
|
|
|
@ -90,22 +90,22 @@ CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
|||
return scriptwitness;
|
||||
}
|
||||
|
||||
const std::map<std::string, unsigned int> FLAG_NAMES = {
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
const std::map<std::string, script_verify_flag_name> FLAG_NAMES = {
|
||||
{std::string("P2SH"), SCRIPT_VERIFY_P2SH},
|
||||
{std::string("DERSIG"), SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("NULLDUMMY"), SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("TAPROOT"), SCRIPT_VERIFY_TAPROOT},
|
||||
};
|
||||
|
||||
std::vector<unsigned int> AllFlags()
|
||||
std::vector<script_verify_flags> AllFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
std::vector<script_verify_flags> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
script_verify_flags flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
@ -125,13 +125,13 @@ std::vector<unsigned int> AllFlags()
|
|||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<unsigned int> ALL_FLAGS = AllFlags();
|
||||
const std::vector<script_verify_flags> ALL_FLAGS = AllFlags();
|
||||
|
||||
unsigned int ParseScriptFlags(const std::string& str)
|
||||
script_verify_flags ParseScriptFlags(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return 0;
|
||||
|
||||
unsigned int flags = 0;
|
||||
script_verify_flags flags = 0;
|
||||
std::vector<std::string> words = SplitString(str, ',');
|
||||
|
||||
for (const std::string& word : words) {
|
||||
|
@ -153,7 +153,7 @@ void Test(const std::string& str)
|
|||
if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
|
||||
size_t idx = test["index"].getInt<int64_t>();
|
||||
if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
|
||||
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
script_verify_flags test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
bool final = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
static DataStream& operator>>(DataStream& ds, script_verify_flags& f)
|
||||
{
|
||||
script_verify_flags::value_type n{0};
|
||||
ds >> n;
|
||||
f = script_verify_flags::from_int(n);
|
||||
assert(n == f.as_int());
|
||||
return ds;
|
||||
}
|
||||
|
||||
FUZZ_TARGET(script_flags)
|
||||
{
|
||||
if (buffer.size() > 100'000) return;
|
||||
|
@ -22,12 +31,14 @@ FUZZ_TARGET(script_flags)
|
|||
try {
|
||||
const CTransaction tx(deserialize, TX_WITH_WITNESS, ds);
|
||||
|
||||
unsigned int verify_flags;
|
||||
script_verify_flags verify_flags;
|
||||
ds >> verify_flags;
|
||||
|
||||
assert(verify_flags == script_verify_flags::from_int(verify_flags.as_int()));
|
||||
|
||||
if (!IsValidFlagCombination(verify_flags)) return;
|
||||
|
||||
unsigned int fuzzed_flags;
|
||||
script_verify_flags fuzzed_flags;
|
||||
ds >> fuzzed_flags;
|
||||
|
||||
std::vector<CTxOut> spent_outputs;
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
FUZZ_TARGET(signature_checker)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
const SigVersion sig_version = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0});
|
||||
const auto script_1{ConsumeScript(fuzzed_data_provider)};
|
||||
const auto script_2{ConsumeScript(fuzzed_data_provider)};
|
||||
|
|
|
@ -38,7 +38,7 @@ sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const
|
|||
|
||||
BOOST_AUTO_TEST_CASE(multisig_verify)
|
||||
{
|
||||
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
script_verify_flags flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
|
||||
ScriptError err;
|
||||
CKey key[4];
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include <univalue.h>
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags);
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(script_assets_tests)
|
||||
|
||||
|
@ -71,12 +71,12 @@ static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
|||
return scriptwitness;
|
||||
}
|
||||
|
||||
static std::vector<unsigned int> AllConsensusFlags()
|
||||
static std::vector<script_verify_flags> AllConsensusFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
std::vector<script_verify_flags> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
script_verify_flags flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
@ -97,7 +97,7 @@ static std::vector<unsigned int> AllConsensusFlags()
|
|||
}
|
||||
|
||||
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
|
||||
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
|
||||
static const std::vector<script_verify_flags> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
|
||||
|
||||
static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
|
|||
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
|
||||
BOOST_CHECK(prevouts.size() == mtx.vin.size());
|
||||
size_t idx = test["index"].getInt<int64_t>();
|
||||
uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())};
|
||||
script_verify_flags test_flags{ParseScriptFlags(test["flags"].get_str())};
|
||||
bool fin = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <core_io.h>
|
||||
#include <key.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <test/util/transaction_utils.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
|
@ -38,10 +40,9 @@
|
|||
|
||||
using namespace util::hex_literals;
|
||||
|
||||
static const unsigned int gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
static const script_verify_flags gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags);
|
||||
std::string FormatScriptFlags(unsigned int flags);
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags);
|
||||
|
||||
struct ScriptErrorDesc
|
||||
{
|
||||
|
@ -95,6 +96,11 @@ static ScriptErrorDesc script_errors[]={
|
|||
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
|
||||
};
|
||||
|
||||
static std::string FormatScriptFlags(script_verify_flags flags)
|
||||
{
|
||||
return util::Join(GetScriptFlagNames(flags), ",");
|
||||
}
|
||||
|
||||
static std::string FormatScriptError(ScriptError_t err)
|
||||
{
|
||||
for (const auto& se : script_errors)
|
||||
|
@ -114,7 +120,7 @@ static ScriptError_t ParseScriptError(const std::string& name)
|
|||
}
|
||||
|
||||
struct ScriptTest : BasicTestingSetup {
|
||||
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, uint32_t flags, const std::string& message, int scriptError, CAmount nValue = 0)
|
||||
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, script_verify_flags flags, const std::string& message, int scriptError, CAmount nValue = 0)
|
||||
{
|
||||
bool expect = (scriptError == SCRIPT_ERR_OK);
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK) {
|
||||
|
@ -128,13 +134,13 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
|
|||
BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
|
||||
|
||||
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint32_t extra_flags(m_rng.randbits(16));
|
||||
uint32_t combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
script_verify_flags extra_flags = script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS));
|
||||
script_verify_flags combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
|
||||
// Weed out some invalid flag combinations.
|
||||
if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
|
||||
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags.as_int()));
|
||||
}
|
||||
}
|
||||
}; // struct ScriptTest
|
||||
|
@ -226,7 +232,7 @@ private:
|
|||
bool havePush{false};
|
||||
std::vector<unsigned char> push;
|
||||
std::string comment;
|
||||
uint32_t flags;
|
||||
script_verify_flags flags;
|
||||
int scriptError{SCRIPT_ERR_OK};
|
||||
CAmount nValue;
|
||||
|
||||
|
@ -246,7 +252,7 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
|
||||
TestBuilder(const CScript& script_, const std::string& comment_, script_verify_flags flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
|
||||
{
|
||||
CScript scriptPubKey = script;
|
||||
if (wm == WitnessMode::PKH) {
|
||||
|
@ -963,7 +969,7 @@ BOOST_AUTO_TEST_CASE(script_json_test)
|
|||
} else {
|
||||
scriptPubKey = ParseScript(scriptPubKeyString);
|
||||
}
|
||||
unsigned int scriptflags = ParseScriptFlags(test[pos++].get_str());
|
||||
script_verify_flags scriptflags = ParseScriptFlags(test[pos++].get_str());
|
||||
int scriptError = ParseScriptError(test[pos++].get_str());
|
||||
|
||||
DoTest(scriptPubKey, scriptSig, witness, scriptflags, strTest, scriptError, nValue);
|
||||
|
@ -1706,4 +1712,15 @@ BOOST_AUTO_TEST_CASE(compute_tapleaf)
|
|||
BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, std::span(script)), tlc2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(formatscriptflags)
|
||||
{
|
||||
// quick check that FormatScriptFlags reports any unknown/unexpected bits
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH), "P2SH");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_TAPROOT), "P2SH,TAPROOT");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | script_verify_flags::from_int(1u<<31)), "P2SH,0x80000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int(1u<<27)), "TAPROOT,0x08000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int((1u<<28) | (1ull<<58))), "TAPROOT,0x400000010000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(script_verify_flags::from_int(1u<<26)), "0x04000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
|
|||
* Verifies script execution of the zeroth scriptPubKey of tx output and
|
||||
* zeroth scriptSig and witness of tx input.
|
||||
*/
|
||||
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, uint32_t flags)
|
||||
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, script_verify_flags flags)
|
||||
{
|
||||
ScriptError error;
|
||||
CTransaction inputi(input);
|
||||
|
@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
|
|||
CKey key = GenerateRandomKey();
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
// Default flags
|
||||
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
|
||||
// Multisig script (legacy counting)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <primitives/transaction_identifier.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
|
@ -49,41 +50,21 @@ typedef std::vector<unsigned char> valtype;
|
|||
static CFeeRate g_dust{DUST_RELAY_TX_FEE};
|
||||
static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG};
|
||||
|
||||
static std::map<std::string, unsigned int> mapFlagNames = {
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S},
|
||||
{std::string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY},
|
||||
{std::string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA},
|
||||
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
|
||||
{std::string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK},
|
||||
{std::string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF},
|
||||
{std::string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
|
||||
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
|
||||
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
|
||||
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
|
||||
};
|
||||
static const std::map<std::string, script_verify_flag_name>& mapFlagNames = g_verify_flag_names;
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags)
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags)
|
||||
{
|
||||
unsigned int flags = SCRIPT_VERIFY_NONE;
|
||||
script_verify_flags flags = SCRIPT_VERIFY_NONE;
|
||||
if (strFlags.empty() || strFlags == "NONE") return flags;
|
||||
|
||||
std::vector<std::string> words = SplitString(strFlags, ',');
|
||||
for (const std::string& word : words)
|
||||
{
|
||||
if (!mapFlagNames.count(word))
|
||||
if (!mapFlagNames.count(word)) {
|
||||
BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
|
||||
flags |= mapFlagNames[word];
|
||||
continue;
|
||||
}
|
||||
flags |= mapFlagNames.at(word);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
@ -91,34 +72,18 @@ unsigned int ParseScriptFlags(std::string strFlags)
|
|||
// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
|
||||
bool CheckMapFlagNames()
|
||||
{
|
||||
unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
|
||||
script_verify_flags standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
|
||||
for (const auto& pair : mapFlagNames) {
|
||||
standard_flags_missing &= ~(pair.second);
|
||||
}
|
||||
return standard_flags_missing == 0;
|
||||
}
|
||||
|
||||
std::string FormatScriptFlags(unsigned int flags)
|
||||
{
|
||||
if (flags == SCRIPT_VERIFY_NONE) {
|
||||
return "";
|
||||
}
|
||||
std::string ret;
|
||||
std::map<std::string, unsigned int>::const_iterator it = mapFlagNames.begin();
|
||||
while (it != mapFlagNames.end()) {
|
||||
if (flags & it->second) {
|
||||
ret += it->first + ",";
|
||||
}
|
||||
it++;
|
||||
}
|
||||
return ret.substr(0, ret.size() - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the input scripts of a transaction are valid/invalid as expected.
|
||||
*/
|
||||
bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
|
||||
const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags,
|
||||
const std::map<COutPoint, int64_t>& map_prevout_values, script_verify_flags flags,
|
||||
const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
|
||||
{
|
||||
bool tx_valid = true;
|
||||
|
@ -152,18 +117,18 @@ bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>&
|
|||
* CLEANSTACK must be used WITNESS and P2SH
|
||||
*/
|
||||
|
||||
unsigned int TrimFlags(unsigned int flags)
|
||||
script_verify_flags TrimFlags(script_verify_flags flags)
|
||||
{
|
||||
// WITNESS requires P2SH
|
||||
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS;
|
||||
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~SCRIPT_VERIFY_WITNESS;
|
||||
|
||||
// CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
|
||||
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK;
|
||||
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~SCRIPT_VERIFY_CLEANSTACK;
|
||||
Assert(IsValidFlagCombination(flags));
|
||||
return flags;
|
||||
}
|
||||
|
||||
unsigned int FillFlags(unsigned int flags)
|
||||
script_verify_flags FillFlags(script_verify_flags flags)
|
||||
{
|
||||
// CLEANSTACK implies WITNESS
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
|
||||
|
@ -178,11 +143,11 @@ unsigned int FillFlags(unsigned int flags)
|
|||
// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
|
||||
// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
|
||||
// Assumes that mapFlagNames contains all script verify flags.
|
||||
std::set<unsigned int> ExcludeIndividualFlags(unsigned int flags)
|
||||
std::set<script_verify_flags> ExcludeIndividualFlags(script_verify_flags flags)
|
||||
{
|
||||
std::set<unsigned int> flags_combos;
|
||||
std::set<script_verify_flags> flags_combos;
|
||||
for (const auto& pair : mapFlagNames) {
|
||||
const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second));
|
||||
script_verify_flags flags_excluding_one = TrimFlags(flags & ~(pair.second));
|
||||
if (flags != flags_excluding_one) {
|
||||
flags_combos.insert(flags_excluding_one);
|
||||
}
|
||||
|
@ -247,7 +212,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
BOOST_CHECK(state.IsValid());
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
||||
if (~verify_flags != FillFlags(~verify_flags)) {
|
||||
|
@ -260,14 +225,14 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
// Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
|
||||
for (const auto& [name, flag] : mapFlagNames) {
|
||||
// Removing individual flags
|
||||
unsigned int flags = TrimFlags(~(verify_flags | flag));
|
||||
script_verify_flags flags = TrimFlags(~(verify_flags | flag));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
|
||||
BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest);
|
||||
}
|
||||
// Removing random combinations of flags
|
||||
flags = TrimFlags(~(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size())));
|
||||
flags = TrimFlags(~(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS))));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
|
||||
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags) << ": " << strTest);
|
||||
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags.as_int()) << ": " << strTest);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,7 +302,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
}
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
||||
if (verify_flags != FillFlags(verify_flags)) {
|
||||
|
@ -350,13 +315,13 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
|
||||
// Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
|
||||
for (const auto& [name, flag] : mapFlagNames) {
|
||||
unsigned int flags = FillFlags(verify_flags | flag);
|
||||
script_verify_flags flags = FillFlags(verify_flags | flag);
|
||||
// Adding individual flags
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
|
||||
BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest);
|
||||
}
|
||||
// Adding random combinations of flags
|
||||
flags = FillFlags(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size()));
|
||||
flags = FillFlags(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS)));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
|
||||
BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest);
|
||||
}
|
||||
|
@ -488,7 +453,7 @@ static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const
|
|||
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
|
||||
}
|
||||
|
||||
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, uint32_t flags, bool success)
|
||||
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, script_verify_flags flags, bool success)
|
||||
{
|
||||
ScriptError error;
|
||||
CTransaction inputi(input);
|
||||
|
|
|
@ -21,7 +21,7 @@ struct Dersig100Setup : public TestChain100Setup {
|
|||
};
|
||||
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
@ -120,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
|
|||
// should fail.
|
||||
// Capture this interaction with the upgraded_nop argument: set it when evaluating
|
||||
// any script flag that is implemented as an upgraded NOP code.
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, script_verify_flags failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
PrecomputedTransactionData txdata;
|
||||
|
||||
|
@ -130,7 +130,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
|
|||
TxValidationState state;
|
||||
|
||||
// Randomly selects flag combinations
|
||||
uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1);
|
||||
script_verify_flags test_flags = script_verify_flags::from_int(insecure_rand.randrange(MAX_SCRIPT_VERIFY_FLAGS));
|
||||
|
||||
// Filter out incompatible flag choices
|
||||
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <script/interpreter.h>
|
||||
#include <test/util/script.h>
|
||||
|
||||
bool IsValidFlagCombination(unsigned flags)
|
||||
bool IsValidFlagCombination(script_verify_flags flags)
|
||||
{
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false;
|
||||
if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <crypto/sha256.h>
|
||||
#include <script/script.h>
|
||||
#include <script/verify_flags.h>
|
||||
|
||||
static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
|
||||
static const CScript P2WSH_OP_TRUE{
|
||||
|
@ -31,6 +32,6 @@ static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_ca
|
|||
static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
|
||||
|
||||
/** Flags that are not forbidden by an assert in script validation */
|
||||
bool IsValidFlagCombination(unsigned flags);
|
||||
bool IsValidFlagCombination(script_verify_flags flags);
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_SCRIPT_H
|
||||
|
|
|
@ -139,7 +139,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
|
|||
}
|
||||
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks = nullptr)
|
||||
|
@ -262,9 +262,6 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
|
|||
return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time});
|
||||
}
|
||||
|
||||
// Returns the script flags which should be checked for a given block
|
||||
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
|
||||
|
||||
static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
|
||||
{
|
||||
|
@ -398,7 +395,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
|
|||
* */
|
||||
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& view, const CTxMemPool& pool,
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
script_verify_flags flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
ValidationCache& validation_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
|
||||
{
|
||||
|
@ -1251,7 +1248,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
const CTransaction& tx = *ws.m_ptx;
|
||||
TxValidationState& state = ws.m_state;
|
||||
|
||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
constexpr script_verify_flags scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
|
||||
// Check input scripts and signatures.
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
|
@ -1290,7 +1287,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
// There is a similar check in CreateNewBlock() to prevent creating
|
||||
// invalid blocks (using TestBlockValidity), however allowing such
|
||||
// transactions into the mempool can be exploited as a DoS attack.
|
||||
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
script_verify_flags currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
|
||||
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
|
||||
|
@ -2098,7 +2095,7 @@ std::optional<std::pair<ScriptError, std::string>> CScriptCheck::operator()() {
|
|||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
|
||||
ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR};
|
||||
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
|
||||
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, m_flags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n);
|
||||
|
@ -2142,7 +2139,7 @@ ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, cons
|
|||
* Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp
|
||||
*/
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks)
|
||||
|
@ -2330,7 +2327,7 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
|
|||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||
}
|
||||
|
||||
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
|
||||
script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
|
||||
{
|
||||
const Consensus::Params& consensusparams = chainman.GetConsensus();
|
||||
|
||||
|
@ -2342,7 +2339,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
|
|||
// mainnet.
|
||||
// For simplicity, always leave P2SH+WITNESS+TAPROOT on except for the two
|
||||
// violating blocks.
|
||||
uint32_t flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
|
||||
script_verify_flags flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
|
||||
const auto it{consensusparams.script_flag_exceptions.find(*Assert(block_index.phashBlock))};
|
||||
if (it != consensusparams.script_flag_exceptions.end()) {
|
||||
flags = it->second;
|
||||
|
@ -2556,7 +2553,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
}
|
||||
|
||||
// Get the script flags for this block
|
||||
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
|
||||
script_verify_flags flags{GetBlockScriptFlags(*pindex, m_chainman)};
|
||||
|
||||
const auto time_2{SteadyClock::now()};
|
||||
m_chainman.time_forks += time_2 - time_1;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <script/verify_flags.h>
|
||||
#include <sync.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h>
|
||||
|
@ -336,14 +337,14 @@ private:
|
|||
CTxOut m_tx_out;
|
||||
const CTransaction *ptxTo;
|
||||
unsigned int nIn;
|
||||
unsigned int nFlags;
|
||||
script_verify_flags m_flags;
|
||||
bool cacheStore;
|
||||
PrecomputedTransactionData *txdata;
|
||||
SignatureCache* m_signature_cache;
|
||||
|
||||
public:
|
||||
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
|
||||
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, script_verify_flags flags, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), m_flags(flags), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
|
||||
|
||||
CScriptCheck(const CScriptCheck&) = delete;
|
||||
CScriptCheck& operator=(const CScriptCheck&) = delete;
|
||||
|
@ -1365,4 +1366,7 @@ bool IsBIP30Repeat(const CBlockIndex& block_index);
|
|||
/** Identifies blocks which coinbase output was subsequently overwritten in the UTXO set (see BIP30) */
|
||||
bool IsBIP30Unspendable(const uint256& block_hash, int block_height);
|
||||
|
||||
// Returns the script flags which should be checked for a given block
|
||||
script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
|
||||
|
||||
#endif // BITCOIN_VALIDATION_H
|
||||
|
|
|
@ -213,6 +213,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
assert_equal(gdi_result, {
|
||||
"hash": blockhash,
|
||||
"height": height,
|
||||
"script_flags": ["CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DERSIG","NULLDUMMY","P2SH","TAPROOT","WITNESS"],
|
||||
"deployments": {
|
||||
'bip34': {'type': 'buried', 'active': True, 'height': 2},
|
||||
'bip66': {'type': 'buried', 'active': True, 'height': 3},
|
||||
|
|
|
@ -19,16 +19,29 @@ class DumptxoutsetTest(BitcoinTestFramework):
|
|||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def check_expected_network(self, node, active):
|
||||
rev_file = node.blocks_path / "rev00000.dat"
|
||||
bogus_file = node.blocks_path / "bogus.dat"
|
||||
rev_file.rename(bogus_file)
|
||||
assert_raises_rpc_error(
|
||||
-1, 'Could not roll back to requested height.', node.dumptxoutset, 'utxos.dat', rollback=99)
|
||||
assert_equal(node.getnetworkinfo()['networkactive'], active)
|
||||
def test_dumptxoutset_with_fork(self):
|
||||
node = self.nodes[0]
|
||||
tip = node.getbestblockhash()
|
||||
target_height = node.getblockcount() - 10
|
||||
target_hash = node.getblockhash(target_height)
|
||||
|
||||
# Cleanup
|
||||
bogus_file.rename(rev_file)
|
||||
# Create a fork of two blocks at the target height
|
||||
invalid_block = node.getblockhash(target_height + 1)
|
||||
node.invalidateblock(invalid_block)
|
||||
# Reset mocktime to not regenerate the same blockhash
|
||||
node.setmocktime(0)
|
||||
self.generate(node, 2)
|
||||
|
||||
# Move back on to actual main chain
|
||||
node.reconsiderblock(invalid_block)
|
||||
self.wait_until(lambda: node.getbestblockhash() == tip)
|
||||
|
||||
# Use dumptxoutset at the forked height
|
||||
out = node.dumptxoutset("txoutset_fork.dat", "rollback", {"rollback": target_height})
|
||||
|
||||
# Verify the snapshot was created at the target height and not the fork tip
|
||||
assert_equal(out['base_height'], target_height)
|
||||
assert_equal(out['base_hash'], target_hash)
|
||||
|
||||
def run_test(self):
|
||||
"""Test a trivial usage of the dumptxoutset RPC command."""
|
||||
|
@ -71,13 +84,8 @@ class DumptxoutsetTest(BitcoinTestFramework):
|
|||
assert_raises_rpc_error(
|
||||
-8, 'Invalid snapshot type "bogus" specified. Please specify "rollback" or "latest"', node.dumptxoutset, 'utxos.dat', "bogus")
|
||||
|
||||
self.log.info("Test that dumptxoutset failure does not leave the network activity suspended when it was on previously")
|
||||
self.check_expected_network(node, True)
|
||||
|
||||
self.log.info("Test that dumptxoutset failure leaves the network activity suspended when it was off")
|
||||
node.setnetworkactive(False)
|
||||
self.check_expected_network(node, False)
|
||||
node.setnetworkactive(True)
|
||||
self.log.info("Testing dumptxoutset with chain fork at target height")
|
||||
self.test_dumptxoutset_with_fork()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue