From 1d554b1d45f6ab5f65adc66ef44a258b92f75663 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Feb 2015 10:42:01 -0800 Subject: [PATCH 1/9] Raise version of created blocks, and enforce DERSIG in mempool --- src/core.h | 2 +- src/script.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core.h b/src/core.h index 6a0286f9d..b020a365e 100644 --- a/src/core.h +++ b/src/core.h @@ -364,7 +364,7 @@ class CBlockHeader { public: // header - static const int CURRENT_VERSION=2; + static const int CURRENT_VERSION=3; int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/script.h b/src/script.h index 99724f895..c3bca80cf 100644 --- a/src/script.h +++ b/src/script.h @@ -208,6 +208,7 @@ static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_NULLDUMMY | + SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S; // For convenience, standard but not mandatory verify flags. From cdb81cec3fe154abb78a025a8f5fabe18dd0fbb3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 5 Feb 2015 21:26:15 -0800 Subject: [PATCH 2/9] Backport of some of BIP66's tests --- src/test/data/script_invalid.json | 44 +++++++++++++++++++++++- src/test/data/script_valid.json | 56 ++++++++++++++++++++++++++++++- src/test/script_tests.cpp | 14 ++++++-- 3 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 6b7d8372b..e5ed07d34 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -354,5 +354,47 @@ ["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], ["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"], -["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"] +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"], + +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Overly long signature is incorrectly encoded for DERSIG"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Missing S is incorrectly encoded for DERSIG"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "S with invalid S length is incorrectly encoded for DERSIG"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer R is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer S is incorrectly encoded for DERSIG"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Zero-length R is incorrectly encoded for DERSIG"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "Zero-length S is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Negative S is incorrectly encoded for DERSIG"], + +[ + "0x47 0x30440220003040725f724b0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with bad sig with too much R padding" +], +[ + "0x47 0x30440220003040725f724a0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with too much R padding" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 2, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 6, with DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 10, with DERSIG" +], + +["0x00", "'00' EQUAL", "Basic OP_0 execution"] ] diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 65b2f13b5..b0b3835c7 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -500,5 +500,59 @@ ["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", "0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", -"Basic PUSHDATA1 signedness check"] +"Basic PUSHDATA1 signedness check"], + +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is correctly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is correctly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is correctly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is correctly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is correctly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is correctly encoded"], + +[ + "0x47 0x30440220003040725f724b0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with bad sig with too much R padding but no DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 4, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 4, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 6, without DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 10, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 12, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 12, with DERSIG" +], + +["0x00", "SIZE 0 EQUAL", "Basic OP_0 execution"] ] diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 29d518d70..0875f6129 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -144,8 +144,13 @@ BOOST_AUTO_TEST_CASE(script_valid) string scriptPubKeyString = test[1].get_str(); CScript scriptPubKey = ParseScript(scriptPubKeyString); + int flagsNow = flags; + if (test.size() > 3 && ("," + test[2].get_str() + ",").find(",DERSIG,") != string::npos) { + flagsNow |= SCRIPT_VERIFY_DERSIG; + } + CTransaction tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flagsNow, SIGHASH_NONE), strTest); } } @@ -168,8 +173,13 @@ BOOST_AUTO_TEST_CASE(script_invalid) string scriptPubKeyString = test[1].get_str(); CScript scriptPubKey = ParseScript(scriptPubKeyString); + int flagsNow = flags; + if (test.size() > 3 && ("," + test[2].get_str() + ",").find(",DERSIG,") != string::npos) { + flagsNow |= SCRIPT_VERIFY_DERSIG; + } + CTransaction tx; - BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flagsNow, SIGHASH_NONE), strTest); } } From 53a49c0246d265f9ce9a74b5d04448d37a692b21 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 5 Feb 2015 20:56:10 -0800 Subject: [PATCH 3/9] Implement BIP 66 validation rules and switchover logic --- src/main.cpp | 39 +++++++++++++++------- src/script.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/script.h | 2 +- 3 files changed, 116 insertions(+), 13 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9d1eca948..ba397a59e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1966,6 +1966,12 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C unsigned int flags = SCRIPT_VERIFY_NOCACHE | (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + if (block.nVersion >= 3 && + ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100)))) { + flags |= SCRIPT_VERIFY_DERSIG; + } + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2755,21 +2761,30 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, REJECT_INVALID, "bad-txns-nonfinal"); } - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (block.nVersion >= 2) - { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) + // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 3) { - CScript expect = CScript() << nHeight; - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || - !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - pindex->nStatus |= BLOCK_FAILED_VALID; - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), - REJECT_INVALID, "bad-cb-height"); + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 950, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 75, 100))) + { + return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block"), + REJECT_OBSOLETE, "bad-version"); } } + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (block.nVersion >= 2) + { + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) + CScript expect = CScript() << nHeight; + if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { + pindex->nStatus |= BLOCK_FAILED_VALID; + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), + REJECT_INVALID, "bad-cb-height"); + } + } } // Write block to history file diff --git a/src/script.cpp b/src/script.cpp index db0724b27..2cbdaefc9 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -297,6 +297,86 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return true; } +// BIP 66 defined signature encoding check. This largely overlaps with +// IsCanonicalSignature above, but lacks hashtype constraints, and uses the +// exact implementation code from BIP 66. +bool static IsValidSignatureEncoding(const std::vector &sig) { + // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] + // * total-length: 1-byte length descriptor of everything that follows, + // excluding the sighash byte. + // * R-length: 1-byte length descriptor of the R value that follows. + // * R: arbitrary-length big-endian encoded R value. It must use the shortest + // possible encoding for a positive integers (which means no null bytes at + // the start, except a single one when the next byte has its highest bit set). + // * S-length: 1-byte length descriptor of the S value that follows. + // * S: arbitrary-length big-endian encoded S value. The same rules apply. + // * sighash: 1-byte value indicating what data is hashed (not part of the DER + // signature) + + // Minimum and maximum size constraints. + if (sig.size() < 9) return false; + if (sig.size() > 73) return false; + + // A signature is of type 0x30 (compound). + if (sig[0] != 0x30) return false; + + // Make sure the length covers the entire signature. + if (sig[1] != sig.size() - 3) return false; + + // Extract the length of the R element. + unsigned int lenR = sig[3]; + + // Make sure the length of the S element is still inside the signature. + if (5 + lenR >= sig.size()) return false; + + // Extract the length of the S element. + unsigned int lenS = sig[5 + lenR]; + + // Verify that the length of the signature matches the sum of the length + // of the elements. + if ((size_t)(lenR + lenS + 7) != sig.size()) return false; + + // Check whether the R element is an integer. + if (sig[2] != 0x02) return false; + + // Zero-length integers are not allowed for R. + if (lenR == 0) return false; + + // Negative numbers are not allowed for R. + if (sig[4] & 0x80) return false; + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false; + + // Check whether the S element is an integer. + if (sig[lenR + 4] != 0x02) return false; + + // Zero-length integers are not allowed for S. + if (lenS == 0) return false; + + // Negative numbers are not allowed for S. + if (sig[lenR + 6] & 0x80) return false; + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false; + + return true; +} + +bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) { + // 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) { + return true; + } + if ((flags & SCRIPT_VERIFY_DERSIG) != 0 && !IsValidSignatureEncoding(vchSig)) { + return false; + } + return true; +} + bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CScript::const_iterator pc = script.begin(); @@ -849,6 +929,10 @@ bool EvalScript(vector >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); @@ -909,6 +993,10 @@ bool EvalScript(vector >& stack, const CScript& script, co valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + // Check signature bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); diff --git a/src/script.h b/src/script.h index c3bca80cf..737755d8a 100644 --- a/src/script.h +++ b/src/script.h @@ -190,7 +190,7 @@ enum SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values ( Date: Tue, 28 Jul 2015 19:02:55 +0100 Subject: [PATCH 4/9] Correct BIP 66 patches --- src/main.cpp | 41 +++++++++++++++++++++-------------------- src/script.h | 1 + 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ba397a59e..b85236f8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2761,29 +2761,30 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, REJECT_INVALID, "bad-txns-nonfinal"); } - // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3) + // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 3) + { + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 950, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 75, 100))) { - if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 950, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 75, 100))) - { - return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block"), - REJECT_OBSOLETE, "bad-version"); - } + return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block"), + REJECT_OBSOLETE, "bad-version"); } - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (block.nVersion >= 2) + } + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (block.nVersion >= 2) + { + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) - CScript expect = CScript() << nHeight; - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || - !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - pindex->nStatus |= BLOCK_FAILED_VALID; - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), - REJECT_INVALID, "bad-cb-height"); - } + CScript expect = CScript() << nHeight; + if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { + pindex->nStatus |= BLOCK_FAILED_VALID; + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), + REJECT_INVALID, "bad-cb-height"); + } } } diff --git a/src/script.h b/src/script.h index 737755d8a..0fd6634f4 100644 --- a/src/script.h +++ b/src/script.h @@ -191,6 +191,7 @@ enum SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values ( Date: Fri, 7 Aug 2015 14:02:34 +0200 Subject: [PATCH 5/9] Implement [CBlock|CBlockIndex]::GetBaseVersion() - nVersion & 0xff to easily compare versions without aux data - change implementations checking nVersion throughout main.ccp --- src/core.h | 7 +++++++ src/main.cpp | 12 ++++++------ src/main.h | 6 ++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/core.h b/src/core.h index b020a365e..ae1e463c7 100644 --- a/src/core.h +++ b/src/core.h @@ -33,6 +33,7 @@ static const int BLOCK_VERSION_DEFAULT = (1 << 0); static const int BLOCK_VERSION_AUXPOW = (1 << 8); static const int BLOCK_VERSION_CHAIN_START = (1 << 16); static const int BLOCK_VERSION_CHAIN_END = (1 << 30); +static const int BLOCK_VERSION_BASE_MASK = 0x000000ff; // DogeCoin aux chain ID = 0x0062 (98) static const int AUXPOW_CHAIN_ID = 0x0062; @@ -396,6 +397,12 @@ public: return nVersion / BLOCK_VERSION_CHAIN_START; } + // base block version without auxpow chain + int GetBaseVersion() const + { + return nVersion & BLOCK_VERSION_BASE_MASK; + } + void SetAuxPow(CAuxPow* pow); void SetNull() diff --git a/src/main.cpp b/src/main.cpp index b85236f8c..0f1ce5493 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1966,7 +1966,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C unsigned int flags = SCRIPT_VERIFY_NOCACHE | (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); - if (block.nVersion >= 3 && + if (block.GetBaseVersion() >= 3 && ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100)))) { flags |= SCRIPT_VERIFY_DERSIG; @@ -2137,7 +2137,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION && !IsAuxPowVersion(pindex->nVersion)) + if (pindex->GetBaseVersion() > CBlock::CURRENT_VERSION) ++nUpgraded; pindex = pindex->pprev; } @@ -2713,7 +2713,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2) + if (block.GetBaseVersion() < 2) { if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) @@ -2762,7 +2762,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3) + if (block.GetBaseVersion() < 3) { if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 950, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 75, 100))) @@ -2772,7 +2772,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (block.nVersion >= 2) + if (block.GetBaseVersion() >= 2) { // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || @@ -2826,7 +2826,7 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns unsigned int nFound = 0; for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) { - if (pstart->nVersion >= minVersion) + if (pstart->GetBaseVersion() >= minVersion) ++nFound; pstart = pstart->pprev; } diff --git a/src/main.h b/src/main.h index 211d1b32f..bf11cf361 100644 --- a/src/main.h +++ b/src/main.h @@ -928,6 +928,12 @@ public: } return false; } + + // base block version without auxpow chain + int GetBaseVersion() const + { + return nVersion & BLOCK_VERSION_BASE_MASK; + } }; From e1a900b1bc7bff625e8f97116c0f878f139c9056 Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Fri, 7 Aug 2015 15:50:24 +0200 Subject: [PATCH 6/9] enable CBlockIndex::IsSuperMajority This has been disabled since 1.5 for unclear reasons, but we need it for the bip66 softfork in 1.10 that is backported here. This commit re-enables the logic. --- src/main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0f1ce5493..d9810842b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2820,8 +2820,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) { - // Dogecoin: temporarily disable v2 block lockin until we are ready for v2 transition - return false; unsigned int nFound = 0; for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) From 320234c0e5d594f9205c4780fff33a10590d21f4 Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Fri, 7 Aug 2015 15:57:51 +0200 Subject: [PATCH 7/9] Reject version 1 blocks with version 3 supermajority Because CBlockIndex::IsSuperMajority was disabled since 1.5, we cannot enforce version 1 block rejection on the live chains with a v2 supermajority. Instead, we piggyback the version 3 supermajority switch to also enforce the version 2 softfork and all is good. --- src/main.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d9810842b..f34b037d8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2713,10 +2713,12 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + // Dogecoin: reject ONLY if block.nVersion=3 has a supermajority because CBlockIndex::IsSuperMajority + // was hard-disabled until now if (block.GetBaseVersion() < 2) { - if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 950, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 75, 100))) { return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), REJECT_OBSOLETE, "bad-version"); @@ -2772,11 +2774,13 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + // Dogecoin: reject ONLY if block.nVersion=3 has a supermajority because CBlockIndex::IsSuperMajority + // was hard-disabled until now if (block.GetBaseVersion() >= 2) { // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100))) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || From 288fc013bab3d16b827de6bcc0be077e8cff7f6a Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Mon, 10 Aug 2015 10:45:16 +0200 Subject: [PATCH 8/9] Use the same time-based constraints as in 1.10 Backport of #1225 to 1.8.x, sets: - majority window to 2000 blocks on mainnet, 1000 on testnet - enforcement triggers to 1500 on mainnet, 501 on testnet - reject triggers to 1900 on mainnet, 750 on testnet --- src/main.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f34b037d8..1b51e4968 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1967,8 +1967,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); if (block.GetBaseVersion() >= 3 && - ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100)))) { + ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 1500, 2000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 501, 1000)))) { flags |= SCRIPT_VERIFY_DERSIG; } @@ -2717,8 +2717,8 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex // was hard-disabled until now if (block.GetBaseVersion() < 2) { - if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 950, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 75, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 1900, 2000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 750, 1000))) { return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), REJECT_OBSOLETE, "bad-version"); @@ -2766,8 +2766,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: if (block.GetBaseVersion() < 3) { - if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 950, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 75, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 1900, 2000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000))) { return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block"), REJECT_OBSOLETE, "bad-version"); @@ -2779,8 +2779,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (block.GetBaseVersion() >= 2) { // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 1500, 2000)) || + (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 501, 1000))) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || From bc4aebb8a76839132352d3af8ca6053811fe0656 Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Mon, 10 Aug 2015 13:41:55 +0200 Subject: [PATCH 9/9] Only test supermajority for block rejects against v3 blocks Since v2 and v1 blocks will be rejected at exactly the same time, only check for supermajority once to save 2000 iterations over blockheaders per block. Also moves the version check to AcceptBlockHeader() rather than AcceptBlock() to be able to reject early on. --- src/main.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1b51e4968..5cf8d744c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2712,17 +2712,13 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex if (pcheckpoint && nHeight < pcheckpoint->nHeight) return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - // Dogecoin: reject ONLY if block.nVersion=3 has a supermajority because CBlockIndex::IsSuperMajority - // was hard-disabled until now - if (block.GetBaseVersion() < 2) - { + // Reject block.nVersion<3 blocks when 95% (75% on testnet) of the network has upgraded + // Dogecoin: reject v2 and v1 blocks at the same time, only check once + if (block.GetBaseVersion() < 3) { if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 1900, 2000)) || (TestNet() && CBlockIndex::IsSuperMajority(3, pindexPrev, 750, 1000))) - { - return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), + return state.Invalid(error("AcceptBlock() : rejected nVersion<3 block"), REJECT_OBSOLETE, "bad-version"); - } } } @@ -2763,16 +2759,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, REJECT_INVALID, "bad-txns-nonfinal"); } - // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.GetBaseVersion() < 3) - { - if ((!TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 1900, 2000)) || - (TestNet() && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000))) - { - return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block"), - REJECT_OBSOLETE, "bad-version"); - } - } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height // Dogecoin: reject ONLY if block.nVersion=3 has a supermajority because CBlockIndex::IsSuperMajority // was hard-disabled until now