Merge pull request #4192 from UdjinM6/backports-0.17-pr32

Backports 0.17 pr32
This commit is contained in:
UdjinM6 2021-06-06 23:42:04 +03:00 committed by GitHub
commit eea77e9642
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 599 additions and 129 deletions

View File

@ -107,9 +107,10 @@ BITCOIN_TESTS =\
test/txindex_tests.cpp \
test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
test/versionbits_tests.cpp \
test/uint256_tests.cpp \
test/util_tests.cpp
test/util_tests.cpp \
test/validation_block_tests.cpp \
test/versionbits_tests.cpp
if ENABLE_WALLET
BITCOIN_TESTS += \

View File

@ -111,9 +111,9 @@ public:
class ConsolePrinter : public Printer
{
public:
void header();
void result(const State& state);
void footer();
void header() override;
void result(const State& state) override;
void footer() override;
};
// creates box plot with plotly.js
@ -121,9 +121,9 @@ class PlotlyPrinter : public Printer
{
public:
PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height);
void header();
void result(const State& state);
void footer();
void header() override;
void result(const State& state) override;
void footer() override;
private:
std::string m_plotly_url;

View File

@ -523,7 +523,7 @@ public:
chainTxData = ChainTxData{
1617874573, // * UNIX timestamp of last known number of transactions (Block 1450962)
34709765, // * total number of transactions between genesis and that timestamp
// (the tx=... number in the SetBestChain debug.log lines)
// (the tx=... number in the ChainStateFlushed debug.log lines)
0.3 // * estimated number of transactions per second after that timestamp
};
}
@ -703,7 +703,7 @@ public:
chainTxData = ChainTxData{
1617874832, // * UNIX timestamp of last known number of transactions (Block 477483)
4926985, // * total number of transactions between genesis and that timestamp
// (the tx=... number in the SetBestChain debug.log lines)
// (the tx=... number in the ChainStateFlushed debug.log lines)
0.01 // * estimated number of transactions per second after that timestamp
};

View File

@ -192,7 +192,7 @@ void TxIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const C
}
}
void TxIndex::SetBestChain(const CBlockLocator& locator)
void TxIndex::ChainStateFlushed(const CBlockLocator& locator)
{
if (!m_synced) {
return;
@ -211,7 +211,7 @@ void TxIndex::SetBestChain(const CBlockLocator& locator)
return;
}
// This checks that SetBestChain callbacks are received after BlockConnected. The check may fail
// This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
// immediately after the the sync thread catches up and sets m_synced. Consider the case where
// there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
// backlog even after the sync thread has caught up to the new chain tip. In this unlikely
@ -268,7 +268,9 @@ bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRe
CBlockHeader header;
try {
file >> header;
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) {
return error("%s: fseek(...) failed", __func__);
}
file >> tx;
} catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s", __func__, e.what());

View File

@ -55,7 +55,7 @@ protected:
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
const std::vector<CTransactionRef>& txn_conflicted) override;
void SetBestChain(const CBlockLocator& locator) override;
void ChainStateFlushed(const CBlockLocator& locator) override;
public:
/// Constructs the TxIndex, which becomes available to be queried.

View File

@ -312,7 +312,7 @@ void PrepareShutdown()
fFeeEstimatesInitialized = false;
}
// FlushStateToDisk generates a SetBestChain callback, which we should avoid missing
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
FlushStateToDisk();
}

View File

@ -308,7 +308,11 @@ void BCLog::Logger::ShrinkDebugFile()
{
// Restart the file with some of the end
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
fseek(file, -((long)vch.size()), SEEK_END);
if (fseek(file, -((long)vch.size()), SEEK_END)) {
LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
fclose(file);
return;
}
int nBytes = fread(vch.data(), 1, vch.size(), file);
fclose(file);

View File

@ -22,6 +22,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 2000000;
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
/** The maximum size for transactions we're willing to relay/mine */
static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
/** The minimum size for transactions we're willing to relay/mine (1 empty scriptSig input + 1 P2PKH output = 85 bytes) */
static const unsigned int MIN_STANDARD_TX_SIZE = 85;
/** Maximum number of signature check operations in an IsStandard() P2SH script */
static const unsigned int MAX_P2SH_SIGOPS = 15;
/** The maximum number of sigops we're willing to relay/mine in a single tx */
@ -52,7 +54,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
SCRIPT_VERIFY_LOW_S |
SCRIPT_ENABLE_DIP0020_OPCODES;
SCRIPT_ENABLE_DIP0020_OPCODES |
SCRIPT_VERIFY_CONST_SCRIPTCODE;
/** For convenience, standard but not mandatory verify flags. */
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;

View File

@ -76,8 +76,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
return false;
}
} else {
// Non-canonical public key: neither compressed nor uncompressed
return false;
// Non-canonical public key: neither compressed nor uncompressed
return false;
}
return true;
}
bool static IsCompressedPubKey(const valtype &vchPubKey) {
if (vchPubKey.size() != 33) {
// Non-canonical public key: invalid length for compressed key
return false;
}
if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) {
// Non-canonical public key: invalid prefix for compressed key
return false;
}
return true;
}
@ -202,8 +214,8 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
return true;
}
bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
return true;
@ -328,6 +340,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
opcode == OP_RSHIFT)
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
// With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR is rejected even in an unexecuted branch
if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR);
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
return set_error(serror, SCRIPT_ERR_MINIMALDATA);
@ -944,6 +960,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
case OP_CODESEPARATOR:
{
// If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected,
// even in an unexecuted branch (this is checked above the opcode case statement).
// Hash starts after the code separator
pbegincodehash = pc;
}
@ -964,10 +983,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Drop the signature, since there's no way for a signature to sign itself
if (sigversion == SigVersion::BASE) {
FindAndDelete(scriptCode, CScript(vchSig));
int found = FindAndDelete(scriptCode, CScript(vchSig));
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
return false;
}
@ -1000,7 +1021,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
valtype &vchMessage = stacktop(-2);
valtype &vchPubKey = stacktop(-1);
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
// serror is set
return false;
}
@ -1071,7 +1092,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
valtype& vchSig = stacktop(-isig-k);
if (sigversion == SigVersion::BASE) {
FindAndDelete(scriptCode, CScript(vchSig));
int found = FindAndDelete(scriptCode, CScript(vchSig));
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
}
@ -1084,7 +1107,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Note how this makes the exact order of pubkey/signature evaluation
// distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
// See the script_(in)valid tests for details.
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
// serror is set
return false;
}

View File

@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -98,6 +98,10 @@ enum
// Enable the opcodes listed in DIP0020 (OP_CAT, OP_AND, OP_OR, OP_XOR, OP_DIV, OP_MOD, OP_SPLIT, OP_BIN2NUM, OP_NUM2BIN, OP_CHECKDATASIG, OP_CHECKDATASIGVERIFY).
SCRIPT_ENABLE_DIP0020_OPCODES = (1U << 15),
// Making OP_CODESEPARATOR and FindAndDelete fail
//
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);

View File

@ -13,7 +13,26 @@
typedef std::vector<unsigned char> valtype;
static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
namespace {
/**
* This is an enum that tracks the execution context of a script, similar to
* SigVersion in script/interpreter. It is separate however because we want to
* distinguish between top-level scriptPubKey execution and P2SH redeemScript
* execution (a distinction that has no impact on consensus rules).
*/
enum class IsMineSigVersion
{
TOP = 0, //! scriptPubKey execution
P2SH = 1, //! P2SH redeemScript
};
bool PermitsUncompressed(IsMineSigVersion sigversion)
{
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
}
bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
{
for (const valtype& pubkey : pubkeys) {
CKeyID keyID = CPubKey(pubkey).GetID();
@ -22,13 +41,7 @@ static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keyst
return true;
}
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
{
CScript script = GetScriptForDestination(dest);
return IsMine(keystore, script);
}
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion)
{
std::vector<valtype> vSolutions;
txnouttype whichType;
@ -46,11 +59,22 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
isInvalid = true;
return ISMINE_NO;
}
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
if (!PermitsUncompressed(sigversion)) {
CPubKey pubkey;
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
isInvalid = true;
return ISMINE_NO;
}
}
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
@ -59,20 +83,31 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
isminetype ret = IsMine(keystore, subscript);
if (ret == ISMINE_SPENDABLE)
isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH);
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
break;
}
case TX_MULTISIG:
{
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
if (sigversion == IsMineSigVersion::TOP) break;
// Only consider transactions "mine" if we own ALL the
// keys involved. Multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
if (!PermitsUncompressed(sigversion)) {
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i].size() != 33) {
isInvalid = true;
return ISMINE_NO;
}
}
}
if (HaveKeys(keys, keystore))
return ISMINE_SPENDABLE;
break;
@ -86,3 +121,22 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
}
return ISMINE_NO;
}
} // namespace
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid)
{
return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP);
}
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
{
bool isInvalid = false;
return IsMine(keystore, scriptPubKey, isInvalid);
}
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
{
CScript script = GetScriptForDestination(dest);
return IsMine(keystore, script);
}

View File

@ -28,6 +28,12 @@ enum isminetype
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
* and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
* different SIGVERSION may have different network rules. Currently there is no use of isInvalid but it could be
* used in the future. See https://github.com/bitcoin/bitcoin/pull/8499 (segwit policy limits) as an example.
*/
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid);
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);

View File

@ -86,6 +86,10 @@ const char* ScriptErrorString(const ScriptError serror)
return "Public key is neither compressed or uncompressed";
case SCRIPT_ERR_CLEANSTACK:
return "Extra items left on stack after execution";
case SCRIPT_ERR_OP_CODESEPARATOR:
return "Using OP_CODESEPARATOR";
case SCRIPT_ERR_SIG_FINDANDDELETE:
return "Signature is found in scriptCode";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;

View File

@ -64,6 +64,10 @@ typedef enum ScriptError_t
/* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
/* Constant scriptCode */
SCRIPT_ERR_OP_CODESEPARATOR,
SCRIPT_ERR_SIG_FINDANDDELETE,
SCRIPT_ERR_ERROR_COUNT
} ScriptError;

View File

@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

View File

@ -23,7 +23,7 @@ class CScriptID : public uint160
{
public:
CScriptID() : uint160() {}
CScriptID(const CScript& in);
explicit CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
};

View File

@ -255,5 +255,53 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
["SCRIPT_VERIFY_CONST_SCRIPTCODE tests"],
["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"],
[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]],
"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["CODESEPARATOR in an unexecuted IF block is still invalid"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["CODESEPARATOR in an executed IF block is invalid"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["Using CHECKSIG with singatures in scriptSigs will trigger FindAndDelete, which is invalid"],
[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["OP_CODESEPARATOR in scriptSig is invalid"],
[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["Again, FindAndDelete() in scriptSig"],
[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
["FindAndDelete() in redeemScript"],
[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]],
"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH,CONST_SCRIPTCODE"],
["FindAndDelete() in bare CHECKMULTISIG"],
[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH,CONST_SCRIPTCODE"],
["Make diffs cleaner by leaving a comment here without comma at the end"]
]

View File

@ -12,12 +12,6 @@
#include <boost/test/unit_test.hpp>
static isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid)
{
isInvalid = false;
return IsMine(keystore, scriptPubKey);
}
BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
@ -454,7 +448,14 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
keystore.AddKey(keys[1]);
result = IsMine(keystore, scriptPubKey, isInvalid);
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
BOOST_CHECK(!isInvalid);
// Keystore has 2/2 keys and the script
keystore.AddCScript(scriptPubKey);
result = IsMine(keystore, scriptPubKey, isInvalid);
BOOST_CHECK_EQUAL(result, ISMINE_NO);
BOOST_CHECK(!isInvalid);
}

View File

@ -93,6 +93,8 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_INVALID_NUMBER_RANGE, "INVALID_NUMBER_RANGE"},
{SCRIPT_ERR_DIV_BY_ZERO, "DIV_BY_ZERO"},
{SCRIPT_ERR_MOD_BY_ZERO, "MOD_BY_ZERO"},
{SCRIPT_ERR_OP_CODESEPARATOR, "OP_CODESEPARATOR"},
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
};
static const char *FormatScriptError(ScriptError_t err)

View File

@ -53,6 +53,12 @@ FastRandomContext insecure_rand_ctx(insecure_rand_seed);
extern bool fPrintToConsole;
extern void noui_connect();
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
os << num.ToString();
return os;
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
: m_path_root(fs::temp_directory_path() / "test_dash" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{

View File

@ -161,4 +161,7 @@ private:
const std::string m_reason;
};
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
std::ostream& operator<<(std::ostream& os, const uint256& num);
#endif

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -46,6 +46,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int) SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int) SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
{std::string("DIP0020_OPCODES"), (unsigned int) SCRIPT_ENABLE_DIP0020_OPCODES},
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
};
unsigned int ParseScriptFlags(std::string strFlags)

View File

@ -0,0 +1,180 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <chainparams.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <miner.h>
#include <pow.h>
#include <random.h>
#include <test/test_dash.h>
#include <validation.h>
#include <validationinterface.h>
struct RegtestingSetup : public TestingSetup {
RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};
BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup)
struct TestSubscriber : public CValidationInterface {
uint256 m_expected_tip;
TestSubscriber(uint256 tip) : m_expected_tip(tip) {}
void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override
{
BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
}
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, const std::vector<CTransactionRef>& txnConflicted) override
{
BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
m_expected_tip = block->GetHash();
}
void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindexDisconnected) override
{
BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
BOOST_CHECK_EQUAL(m_expected_tip, pindexDisconnected->GetBlockHash());
m_expected_tip = block->hashPrevBlock;
}
};
std::shared_ptr<CBlock> Block(const uint256& prev_hash)
{
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
CScript pubKey;
pubKey << i++ << OP_TRUE;
auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
return pblock;
}
std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
++(pblock->nNonce);
}
return pblock;
}
// construct a valid block
const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
{
return FinalizeBlock(Block(prev_hash));
}
// construct an invalid block (but with a valid header)
const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
{
auto pblock = Block(prev_hash);
CMutableTransaction coinbase_spend;
coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]);
CTransactionRef tx = MakeTransactionRef(coinbase_spend);
pblock->vtx.push_back(tx);
auto ret = FinalizeBlock(pblock);
return ret;
}
void BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
{
if (height <= 0 || blocks.size() >= max_size) return;
bool gen_invalid = GetRand(100) < invalid_rate;
bool gen_fork = GetRand(100) < branch_rate;
const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
blocks.push_back(pblock);
if (!gen_invalid) {
BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
}
if (gen_fork) {
blocks.push_back(GoodBlock(root));
BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
}
}
BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
{
// build a large-ish chain that's likely to have some forks
std::vector<std::shared_ptr<const CBlock>> blocks;
while (blocks.size() < 50) {
blocks.clear();
BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
}
bool ignored;
CValidationState state;
std::vector<CBlockHeader> headers;
std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });
// Process all the headers so we understand the toplogy of the chain
BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params()));
// Connect the genesis block and drain any outstanding events
ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored);
SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
initial_tip = chainActive.Tip();
}
TestSubscriber sub(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub);
// create a bunch of threads that repeatedly process a block generated above at random
// this will create parallelism and randomness inside validation - the ValidationInterface
// will subscribe to events generated during block validation and assert on ordering invariance
boost::thread_group threads;
for (int i = 0; i < 10; i++) {
threads.create_thread([&blocks]() {
bool ignored;
for (int i = 0; i < 1000; i++) {
auto block = blocks[GetRand(blocks.size() - 1)];
ProcessNewBlock(Params(), block, true, &ignored);
}
// to make sure that eventually we process the full chain - do it here
for (auto block : blocks) {
if (block->vtx.size() == 1) {
bool processed = ProcessNewBlock(Params(), block, true, &ignored);
assert(processed);
}
}
});
}
threads.join_all();
while (GetMainSignals().CallbacksPending() > 0) {
MilliSleep(100);
}
UnregisterValidationInterface(&sub);
BOOST_CHECK_EQUAL(sub.m_expected_tip, chainActive.Tip()->GetBlockHash());
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -1024,7 +1024,9 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
// Fallback version
// TODO: just write one byte per block
static const char buf[65536] = {};
fseek(file, offset, SEEK_SET);
if (fseek(file, offset, SEEK_SET)) {
return;
}
while (length > 0) {
unsigned int now = 65536;
if (length < now)

View File

@ -153,6 +153,12 @@ private:
*/
std::set<CBlockIndex*> m_failed_blocks;
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
*/
CCriticalSection m_cs_chainstate;
public:
CChain chainActive;
BlockMap mapBlockIndex;
@ -658,6 +664,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
// Do not work on transactions that are too small.
// A transaction with 1 empty scriptSig input and 1 P2PKH output has size of 85 bytes.
// Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) < MIN_STANDARD_TX_SIZE)
return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
@ -2450,13 +2462,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
LOCK(cs_main);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
static int64_t nLastSetChain = 0;
std::set<int> setFilesToPrune;
bool fFlushForPrune = false;
bool fDoFullFlush = false;
int64_t nNow = 0;
bool full_flush_completed = false;
try {
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@ -2473,7 +2484,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
}
}
nNow = GetTimeMicros();
int64_t nNow = GetTimeMicros();
// Avoid writing/flushing immediately after startup.
if (nLastWrite == 0) {
nLastWrite = nNow;
@ -2481,9 +2492,6 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (nLastFlush == 0) {
nLastFlush = nNow;
}
if (nLastSetChain == 0) {
nLastSetChain = nNow;
}
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
cacheSize += evoDb->GetMemoryUsage();
@ -2544,12 +2552,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return AbortNode(state, "Failed to commit EvoDB");
}
nLastFlush = nNow;
full_flush_completed = true;
}
}
if (fDoFullFlush || ((mode == FlushStateMode::ALWAYS || mode == FlushStateMode::PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
GetMainSignals().SetBestChain(chainActive.GetLocator());
nLastSetChain = nNow;
GetMainSignals().ChainStateFlushed(chainActive.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@ -2927,6 +2935,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
@ -3044,10 +3053,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// sanely for performance or correctness!
AssertLockNotHeld(cs_main);
// make sure that no matter what, only one thread is executing ActivateBestChain. This avoids a race condition when
// validation signals are invoked, which might result in out-of-order execution.
static CCriticalSection cs_activateBestChain;
LOCK(cs_activateBestChain);
// ABC maintains a fair degree of expensive-to-calculate internal state
// because this function periodically releases cs_main so that it does not lock up other threads for too long
// during large connects - and to allow for e.g. the callback queue to drain
// we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
LOCK(m_cs_chainstate);
boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
@ -3067,47 +3077,54 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
SyncWithValidationInterfaceQueue();
}
const CBlockIndex *pindexFork;
bool fInitialDownload;
{
LOCK(cs_main);
ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
CBlockIndex* starting_tip = chainActive.Tip();
bool blocks_connected = false;
do {
// We absolutely may not unlock cs_main until we've made forward progress
// (with the exception of shutdown due to hardware issues, low disk space, etc).
ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == nullptr) {
pindexMostWork = FindMostWorkChain();
}
if (pindexMostWork == nullptr) {
pindexMostWork = FindMostWorkChain();
}
// Whether we have anything to do at all.
if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip())
return true;
// Whether we have anything to do at all.
if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
break;
}
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
return false;
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
return false;
blocks_connected = true;
if (fInvalidFound) {
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
pindexNewTip = chainActive.Tip();
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
if (fInvalidFound) {
// Wipe cache, we may need another branch now.
pindexMostWork = nullptr;
}
pindexNewTip = chainActive.Tip();
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
} while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
if (!blocks_connected) return true;
const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
GetMainSignals().SynchronousUpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
// Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
// Notify ValidationInterface subscribers
GetMainSignals().SynchronousUpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
// Always notify the UI if a new block tip was connected
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
}
}

View File

@ -26,7 +26,7 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex* pindexDisconnected)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &, MemPoolRemovalReason)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@ -93,7 +93,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->NotifyTransactionLock.connect(boost::bind(&CValidationInterface::NotifyTransactionLock, pwalletIn, _1, _2));
g_signals.m_internals->NotifyChainLock.connect(boost::bind(&CValidationInterface::NotifyChainLock, pwalletIn, _1, _2));
g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1, _2));
g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
@ -107,7 +107,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->NotifyChainLock.disconnect(boost::bind(&CValidationInterface::NotifyChainLock, pwalletIn, _1, _2));
g_signals.m_internals->NotifyTransactionLock.disconnect(boost::bind(&CValidationInterface::NotifyTransactionLock, pwalletIn, _1, _2));
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1, _2));
@ -132,7 +132,7 @@ void UnregisterAllValidationInterfaces() {
}
g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots();
g_signals.m_internals->SetBestChain.disconnect_all_slots();
g_signals.m_internals->ChainStateFlushed.disconnect_all_slots();
g_signals.m_internals->NotifyTransactionLock.disconnect_all_slots();
g_signals.m_internals->NotifyChainLock.disconnect_all_slots();
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
@ -205,9 +205,9 @@ void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock
});
}
void CMainSignals::SetBestChain(const CBlockLocator &locator) {
void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
m_internals->m_schedulerClient.AddToProcessQueue([locator, this] {
m_internals->SetBestChain(locator);
m_internals->ChainStateFlushed(locator);
});
}

View File

@ -87,7 +87,11 @@ protected:
*/
~CValidationInterface() = default;
/**
* Notifies listeners of updated block chain tip
* Notifies listeners when the block chain tip advances.
*
* When multiple blocks are connected at once, UpdatedBlockTip will be called on the final tip
* but may not be called on every intermediate tip. If the latter behavior is desired,
* subscribe to BlockConnected() instead.
*
* Called on a background thread.
*/
@ -136,9 +140,20 @@ protected:
/**
* Notifies listeners of the new active block chain on-disk.
*
* Prior to this callback, any updates are not guaranteed to persist on disk
* (ie clients need to handle shutdown/restart safety by being able to
* understand when some updates were lost due to unclean shutdown).
*
* When this callback is invoked, the validation changes done by any prior
* callback are guaranteed to exist on disk and survive a restart, including
* an unclean shutdown.
*
* Provides a locator describing the best chain, which is likely useful for
* storing current state on disk in client DBs.
*
* Called on a background thread.
*/
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
/** Tells listeners to broadcast their data. */
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
@ -198,7 +213,7 @@ public:
void NotifyInstantSendDoubleSpendAttempt(const CTransactionRef &currentTx, const CTransactionRef &previousTx);
void NotifyRecoveredSig(const std::shared_ptr<const llmq::CRecoveredSig> &sig);
void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff);
void SetBestChain(const CBlockLocator &);
void ChainStateFlushed(const CBlockLocator &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);

View File

@ -198,10 +198,11 @@ static void ImportScript(CWallet * const pwallet, const CScript& script, const s
}
if (isRedeemScript) {
if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
const CScriptID id(script);
if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
ImportAddress(pwallet, CScriptID(script), strLabel);
ImportAddress(pwallet, id, strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@ -578,7 +579,8 @@ UniValue importwallet(const JSONRPCRequest& request)
} else if(IsHex(vstr[0])) {
std::vector<unsigned char> vData(ParseHex(vstr[0]));
CScript script = CScript(vData.begin(), vData.end());
if (pwallet->HaveCScript(script)) {
CScriptID id(script);
if (pwallet->HaveCScript(id)) {
LogPrintf("Skipping import of %s (script already present)\n", vstr[0]);
continue;
}
@ -589,7 +591,7 @@ UniValue importwallet(const JSONRPCRequest& request)
}
int64_t birth_time = DecodeDumpTime(vstr[1]);
if (birth_time > 0) {
pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time;
pwallet->m_script_metadata[id].nCreateTime = birth_time;
nTimeBegin = std::min(nTimeBegin, birth_time);
}
}
@ -1093,12 +1095,12 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) {
CScriptID redeem_id(redeemScript);
if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
CTxDestination redeem_dest = CScriptID(redeemScript);
CScript redeemDestination = GetScriptForDestination(redeem_dest);
CScript redeemDestination = GetScriptForDestination(redeem_id);
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");

View File

@ -627,7 +627,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
}
void CWallet::SetBestChain(const CBlockLocator& loc)
void CWallet::ChainStateFlushed(const CBlockLocator& loc)
{
WalletBatch batch(*database);
batch.WriteBestBlock(loc);
@ -5093,7 +5093,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
return error(_("Unable to generate initial keys"));
}
walletInstance->SetBestChain(chainActive.GetLocator());
walletInstance->ChainStateFlushed(chainActive.GetLocator());
// Try to create wallet backup right after new wallet was created
std::string strBackupWarning;
@ -5240,7 +5240,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
}
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->SetBestChain(chainActive.GetLocator());
walletInstance->ChainStateFlushed(chainActive.GetLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1

View File

@ -1120,7 +1120,7 @@ public:
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void SetBestChain(const CBlockLocator& loc) override;
void ChainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(bool& fFirstRunRet);
void AutoLockMasternodeCollaterals();

View File

@ -81,7 +81,7 @@ class BIP68Test(BitcoinTestFramework):
tx2.nVersion = 2
sequence_value = sequence_value & 0x7fffffff
tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))]
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), CScript([b'a' * 35]))]
tx2.rehash()
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
@ -223,7 +223,7 @@ class BIP68Test(BitcoinTestFramework):
tx = CTransaction()
tx.nVersion = 2
tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)]
tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee*COIN), CScript([b'a']))]
tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), CScript([b'a' * 35]))]
tx.rehash()
if (orig_tx.hash in node.getrawmempool()):
@ -351,7 +351,7 @@ class BIP68Test(BitcoinTestFramework):
tx3 = CTransaction()
tx3.nVersion = 2
tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)]
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), CScript([b'a' * 35]))]
tx3.rehash()
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))

View File

@ -31,6 +31,7 @@ from test_framework.script import (
OP_ELSE,
OP_ENDIF,
OP_EQUAL,
OP_DROP,
OP_FALSE,
OP_HASH160,
OP_IF,
@ -1219,7 +1220,7 @@ class FullBlockTest(BitcoinTestFramework):
block.vtx.extend(tx_list)
# this is a little handier to use than the version in blocktools.py
def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])):
def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])):
return create_transaction(spend_tx, n, b"", value, script)
# sign a transaction, using the key we know about

View File

@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.messages import COutPoint, CTransaction, CTxIn, CTxOut, ToHex
from test_framework.mininode import COIN
from test_framework.script import CScript, OP_CAT
from test_framework.script import CScript, OP_CAT, OP_DROP, OP_TRUE
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, get_bip9_status, satoshi_round
@ -47,7 +47,7 @@ class DIP0020ActivationTest(BitcoinTestFramework):
value = int(value - self.relayfee * COIN)
tx0 = CTransaction()
tx0.vin.append(CTxIn(COutPoint(int(txid, 16), 0)))
tx0.vout.append(CTxOut(value, CScript([])))
tx0.vout.append(CTxOut(value, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx0.rehash()
tx0_hex = ToHex(tx0)

View File

@ -9,7 +9,8 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.blocktools import create_block, create_coinbase
from test_framework.script import CScript, OP_TRUE
from test_framework.script import CScript, OP_TRUE, OP_DROP
# TestP2PConn: A peer we use to send messages to dashd, and store responses.
class TestP2PConn(P2PInterface):
@ -375,7 +376,7 @@ class CompactBlocksTest(BitcoinTestFramework):
for i in range(num_transactions):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE])))
tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.rehash()
utxo = [tx.sha256, 0, tx.vout[0].nValue]
block.vtx.append(tx)

View File

@ -81,7 +81,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# Transaction will be rejected with code 16 (REJECT_INVALID)
# and we get disconnected immediately
self.log.info('Test a transaction that is rejected')
tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN)
tx1 = create_transaction(block1.vtx[0], 0, b'\x64' * 35, 50 * COIN)
node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True)
# Make two p2p connections to provide the node with orphans
@ -114,32 +114,33 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# Create a root transaction that we withold until all dependend transactions
# are sent out and in the orphan cache
SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
tx_withhold = CTransaction()
tx_withhold.vin.append(CTxIn(outpoint=COutPoint(base_tx, 0)))
tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=b'\x51'))
tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
tx_withhold.calc_sha256()
# Our first orphan tx with some outputs to create further orphan txs
tx_orphan_1 = CTransaction()
tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=b'\x51')] * 3
tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
tx_orphan_1.calc_sha256()
# A valid transaction with low fee
tx_orphan_2_no_fee = CTransaction()
tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=b'\x51'))
tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
# A valid transaction with sufficient fee
tx_orphan_2_valid = CTransaction()
tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=b'\x51'))
tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
tx_orphan_2_valid.calc_sha256()
# An invalid transaction with negative fee
tx_orphan_2_invalid = CTransaction()
tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=b'\x51'))
tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
self.log.info('Send the orphans ... ')
# Send valid orphan txs from p2ps[0]

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
# Copyright (c) 2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# address.py
#
# This file encodes and decodes BASE58 P2PKH and P2SH addresses
#
from .script import hash256, hash160, CScript
from .util import bytes_to_hex_str, hex_str_to_bytes
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def byte_to_base58(b, version):
result = ''
str = bytes_to_hex_str(b)
str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
str += checksum[:8]
value = int('0x'+str,0)
while value > 0:
result = chars[value % 58] + result
value //= 58
while (str[:2] == '00'):
result = chars[0] + result
str = str[2:]
return result
# TODO: def base58_decode
def keyhash_to_p2pkh(hash, main = False):
assert (len(hash) == 20)
version = 76 if main else 140
return byte_to_base58(hash, version)
def scripthash_to_p2sh(hash, main = False):
assert (len(hash) == 20)
version = 16 if main else 19
return byte_to_base58(hash, version)
def key_to_p2pkh(key, main = False):
key = check_key(key)
return keyhash_to_p2pkh(hash160(key), main)
def script_to_p2sh(script, main = False):
script = check_script(script)
return scripthash_to_p2sh(hash160(script), main)
def check_key(key):
if (type(key) is str):
key = hex_str_to_bytes(key) # Assuming this is hex string
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
return key
assert(False)
def check_script(script):
if (type(script) is str):
script = hex_str_to_bytes(script) # Assuming this is hex string
if (type(script) is bytes or type(script) is CScript):
return script
assert(False)

View File

@ -70,6 +70,9 @@ ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
# this specifies the curve used with ECDSA.
NID_secp256k1 = 714 # from openssl/obj_mac.h
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
def _check_result(val, func, args):
if val == 0:
@ -142,7 +145,7 @@ class CECKey():
r = self.get_raw_ecdh_key(other_pubkey)
return kdf(r)
def sign(self, hash):
def sign(self, hash, low_s = True):
# FIXME: need unit tests for below cases
if not isinstance(hash, bytes):
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
@ -154,7 +157,25 @@ class CECKey():
mb_sig = ctypes.create_string_buffer(sig_size0.value)
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
assert 1 == result
return mb_sig.raw[:sig_size0.value]
assert mb_sig.raw[0] == 0x30
assert mb_sig.raw[1] == sig_size0.value - 2
total_size = mb_sig.raw[1]
assert mb_sig.raw[2] == 2
r_size = mb_sig.raw[3]
assert mb_sig.raw[4 + r_size] == 2
s_size = mb_sig.raw[5 + r_size]
s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
return mb_sig.raw[:sig_size0.value]
else:
low_s_value = SECP256K1_ORDER - s_value
low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
low_s_bytes = low_s_bytes[1:]
new_s_size = len(low_s_bytes)
new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
def verify(self, hash, sig):
"""Verify a DER signature"""