X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fscript.cpp;h=6e25fb376a0674f9474fe4653c57c872e7b00a5c;hb=eff0cd3a467d638908b77afc0c50a0a760a351a1;hp=ccb19c388e933efee2faf8711560bfe557a4c475;hpb=23e7583a8c9a0dcee9cbbf3be8bfc453298773f0;p=novacoin.git diff --git a/src/script.cpp b/src/script.cpp index ccb19c3..6e25fb3 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -2,24 +2,19 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include - -using namespace std; -using namespace boost; #include "script.h" #include "keystore.h" #include "bignum.h" #include "key.h" #include "main.h" +#include "sync.h" #include "util.h" -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); - +using namespace std; +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); -typedef vector valtype; static const valtype vchFalse(0); static const valtype vchZero(0); static const valtype vchTrue(1, 1); @@ -53,12 +48,29 @@ bool CastToBool(const valtype& vch) return false; } +// +// WARNING: This does not work as expected for signed integers; the sign-bit +// is left in place as the integer is zero-extended. The correct behavior +// would be to move the most significant bit of the last byte during the +// resize process. MakeSameSize() is currently only used by the disabled +// opcodes OP_AND, OP_OR, and OP_XOR. +// void MakeSameSize(valtype& vch1, valtype& vch2) { // Lengthen the shorter one if (vch1.size() < vch2.size()) + // PATCH: + // +unsigned char msb = vch1[vch1.size()-1]; + // +vch1[vch1.size()-1] &= 0x7f; + // vch1.resize(vch2.size(), 0); + // +vch1[vch1.size()-1] = msb; vch1.resize(vch2.size(), 0); if (vch2.size() < vch1.size()) + // PATCH: + // +unsigned char msb = vch2[vch2.size()-1]; + // +vch2[vch2.size()-1] &= 0x7f; + // vch2.resize(vch1.size(), 0); + // +vch2[vch2.size()-1] = msb; vch2.resize(vch1.size(), 0); } @@ -84,9 +96,11 @@ const char* GetTxnOutputType(txnouttype t) { case TX_NONSTANDARD: return "nonstandard"; case TX_PUBKEY: return "pubkey"; + case TX_PUBKEY_DROP: return "pubkeydrop"; case TX_PUBKEYHASH: return "pubkeyhash"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; } return NULL; } @@ -131,6 +145,8 @@ const char* GetOpName(opcodetype opcode) case OP_ENDIF : return "OP_ENDIF"; case OP_VERIFY : return "OP_VERIFY"; case OP_RETURN : return "OP_RETURN"; + case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; + case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; // stack ops case OP_TOALTSTACK : return "OP_TOALTSTACK"; @@ -213,8 +229,6 @@ const char* GetOpName(opcodetype opcode) // expanson case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; - case OP_NOP3 : return "OP_NOP3"; case OP_NOP4 : return "OP_NOP4"; case OP_NOP5 : return "OP_NOP5"; case OP_NOP6 : return "OP_NOP6"; @@ -228,6 +242,7 @@ const char* GetOpName(opcodetype opcode) // template matching params case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; + case OP_SMALLDATA : return "OP_SMALLDATA"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: @@ -235,12 +250,174 @@ const char* GetOpName(opcodetype opcode) } } -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + + if (vchPubKey.size() < 33) + return error("Non-canonical public key: too short"); + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) + return error("Non-canonical public key: invalid length for uncompressed key"); + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) + return error("Non-canonical public key: invalid length for compressed key"); + } else { + return error("Non-canonical public key: compressed nor uncompressed"); + } + return true; +} + +bool IsDERSignature(const valtype &vchSig, bool fWithHashType, bool fCheckLow) { + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <02> <02> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (vchSig.size() < 9) + return error("Non-canonical signature: too short"); + if (vchSig.size() > 73) + return error("Non-canonical signature: too long"); + if (vchSig[0] != 0x30) + return error("Non-canonical signature: wrong type"); + if (vchSig[1] != vchSig.size() - (fWithHashType ? 3 : 2)) + return error("Non-canonical signature: wrong length marker"); + if (fWithHashType) { + unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) + return error("Non-canonical signature: unknown hashtype byte"); + } + unsigned int nLenR = vchSig[3]; + if (5 + nLenR >= vchSig.size()) + return error("Non-canonical signature: S length misplaced"); + unsigned int nLenS = vchSig[5+nLenR]; + if ((nLenR + nLenS + (fWithHashType ? 7 : 6)) != vchSig.size()) + return error("Non-canonical signature: R+S length mismatch"); + + const unsigned char *R = &vchSig[4]; + if (R[-2] != 0x02) + return error("Non-canonical signature: R value type mismatch"); + if (nLenR == 0) + return error("Non-canonical signature: R length is zero"); + if (R[0] & 0x80) + return error("Non-canonical signature: R value negative"); + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + return error("Non-canonical signature: R value excessively padded"); + + const unsigned char *S = &vchSig[6+nLenR]; + if (S[-2] != 0x02) + return error("Non-canonical signature: S value type mismatch"); + if (nLenS == 0) + return error("Non-canonical signature: S length is zero"); + if (S[0] & 0x80) + return error("Non-canonical signature: S value negative"); + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + return error("Non-canonical signature: S value excessively padded"); + + if (fCheckLow) { + unsigned int nLenR = vchSig[3]; + unsigned int nLenS = vchSig[5+nLenR]; + const unsigned char *S = &vchSig[6+nLenR]; + // If the S value is above the order of the curve divided by two, its + // complement modulo the order could have been used instead, which is + // one byte shorter when encoded correctly. + if (!CKey::CheckSignatureElement(S, nLenS, true)) + return error("Non-canonical signature: S value is unnecessarily high"); + } + + return true; +} + +bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + + return IsDERSignature(vchSig, true, (flags & SCRIPT_VERIFY_LOW_S) != 0); +} + +bool CheckLockTime(const int64_t& nLockTime, const CTransaction &txTo, unsigned int nIn) +{ + // There are two kinds of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // unless the type of nLockTime being tested is the same as + // the nLockTime in the transaction. + if (!( + (txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime > (int64_t)txTo.nLockTime) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (SEQUENCE_FINAL == txTo.vin[nIn].nSequence) + return false; + + return true; +} + +bool CheckSequence(const int64_t& nSequence, const CTransaction &txTo, unsigned int nIn) +{ + // Relative lock times are supported by comparing the passed + // in operand to the sequence number of the input. + const int64_t txToSequence = (int64_t)txTo.vin[nIn].nSequence; + + // Sequence numbers with their most significant bit set are not + // consensus constrained. Testing that the transaction's sequence + // number do not have this bit set prevents using this property + // to get around a CHECKSEQUENCEVERIFY check. + if (txToSequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) + return false; + + // Mask off any bits that do not have consensus-enforced meaning + // before doing the integer comparisons + const uint32_t nLockTimeMask = SEQUENCE_LOCKTIME_TYPE_FLAG | SEQUENCE_LOCKTIME_MASK; + const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; + const int64_t nSequenceMasked = nSequence & nLockTimeMask; + + // There are two kinds of nSequence: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // + // We want to compare apples to apples, so fail the script + // unless the type of nSequenceMasked being tested is the same as + // the nSequenceMasked in the transaction. + if (!( + (txToSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG) || + (txToSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG) + )) { + return false; + } + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nSequenceMasked > txToSequenceMasked) + return false; + + return true; +} + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, uint32_t nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; - CScript::const_iterator pc = script.begin(); - CScript::const_iterator pend = script.end(); - CScript::const_iterator pbegincodehash = script.begin(); + auto pc = script.begin(); + auto pend = script.end(); + auto pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; vector vfExec; @@ -249,7 +426,6 @@ bool EvalScript(vector >& stack, const CScript& script, co return false; int nOpCount = 0; - try { while (pc < pend) @@ -261,7 +437,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // if (!script.GetOp(pc, opcode, vchPushValue)) return false; - if (vchPushValue.size() > 520) + if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) return false; if (opcode > OP_16 && ++nOpCount > 201) return false; @@ -281,9 +457,9 @@ bool EvalScript(vector >& stack, const CScript& script, co opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) - return false; + return false; // Disabled opcodes. - if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + if (fExec && opcode <= OP_PUSHDATA4) stack.push_back(vchPushValue); else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) switch (opcode) @@ -310,7 +486,7 @@ bool EvalScript(vector >& stack, const CScript& script, co case OP_16: { // ( -- value) - CBigNum bn((int)opcode - (int)(OP_1 - 1)); + CBigNum bn(opcode - (OP_1 - 1)); stack.push_back(bn.getvch()); } break; @@ -320,7 +496,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // Control // case OP_NOP: - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP1: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: break; @@ -333,7 +509,7 @@ bool EvalScript(vector >& stack, const CScript& script, co { if (stack.size() < 1) return false; - valtype& vch = stacktop(-1); + auto& vch = stacktop(-1); fValue = CastToBool(vch); if (opcode == OP_NOTIF) fValue = !fValue; @@ -379,6 +555,67 @@ bool EvalScript(vector >& stack, const CScript& script, co } break; + case OP_CHECKLOCKTIMEVERIFY: + { + // CHECKLOCKTIMEVERIFY + // + // (nLockTime -- nLockTime) + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // treat as a NOP2 if not enabled + break; + } + + if (stack.size() < 1) + return false; + + auto nLockTime = CastToBigNum(stacktop(-1)); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) + return false; + + // Actually compare the specified lock time with the transaction. + if (!CheckLockTime(nLockTime.getuint64(), txTo, nIn)) + return false; + + break; + } + + case OP_CHECKSEQUENCEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { + // treat as a NOP3 not enabled + break; + } + + if (stack.size() < 1) + return false; + + // nSequence, like nLockTime, is a 32-bit unsigned integer + // field. See the comment in CHECKLOCKTIMEVERIFY regarding + // 5-byte numeric operands. + auto nSequence = CastToBigNum(stacktop(-1)); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKSEQUENCEVERIFY. + if (nSequence < 0) + return false; + + // To provide for future soft-fork extensibility, if the + // operand has the disabled lock-time flag set, + // CHECKSEQUENCEVERIFY behaves as a NOP. + if ((nSequence.getint32() & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + break; + + // Compare the specified sequence number with the input. + if (!CheckSequence(nSequence.getuint64(), txTo, nIn)) + return false; + + break; + } // // Stack ops @@ -416,8 +653,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x1 x2 x1 x2) if (stack.size() < 2) return false; - valtype vch1 = stacktop(-2); - valtype vch2 = stacktop(-1); + auto vch1 = stacktop(-2); + auto vch2 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); } @@ -428,9 +665,9 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (stack.size() < 3) return false; - valtype vch1 = stacktop(-3); - valtype vch2 = stacktop(-2); - valtype vch3 = stacktop(-1); + auto vch1 = stacktop(-3); + auto vch2 = stacktop(-2); + auto vch3 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); stack.push_back(vch3); @@ -442,8 +679,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (stack.size() < 4) return false; - valtype vch1 = stacktop(-4); - valtype vch2 = stacktop(-3); + auto vch1 = stacktop(-4); + auto vch2 = stacktop(-3); stack.push_back(vch1); stack.push_back(vch2); } @@ -454,8 +691,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (stack.size() < 6) return false; - valtype vch1 = stacktop(-6); - valtype vch2 = stacktop(-5); + auto vch1 = stacktop(-6); + auto vch2 = stacktop(-5); stack.erase(stack.end()-6, stack.end()-4); stack.push_back(vch1); stack.push_back(vch2); @@ -477,7 +714,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x - 0 | x x) if (stack.size() < 1) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); if (CastToBool(vch)) stack.push_back(vch); } @@ -486,7 +723,7 @@ bool EvalScript(vector >& stack, const CScript& script, co case OP_DEPTH: { // -- stacksize - CBigNum bn(stack.size()); + CBigNum bn((uint16_t) stack.size()); stack.push_back(bn.getvch()); } break; @@ -505,7 +742,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x -- x x) if (stack.size() < 1) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); stack.push_back(vch); } break; @@ -524,7 +761,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x1 x2 x1) if (stack.size() < 2) return false; - valtype vch = stacktop(-2); + auto vch = stacktop(-2); stack.push_back(vch); } break; @@ -536,11 +773,11 @@ bool EvalScript(vector >& stack, const CScript& script, co // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) if (stack.size() < 2) return false; - int n = CastToBigNum(stacktop(-1)).getint(); + int n = CastToBigNum(stacktop(-1)).getint32(); popstack(stack); if (n < 0 || n >= (int)stack.size()) return false; - valtype vch = stacktop(-n-1); + auto vch = stacktop(-n-1); if (opcode == OP_ROLL) stack.erase(stack.end()-n-1); stack.push_back(vch); @@ -573,76 +810,18 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x2 x1 x2) if (stack.size() < 2) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); stack.insert(stack.end()-2, vch); } break; - // - // Splice ops - // - case OP_CAT: - { - // (x1 x2 -- out) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - vch1.insert(vch1.end(), vch2.begin(), vch2.end()); - popstack(stack); - if (stacktop(-1).size() > 520) - return false; - } - break; - - case OP_SUBSTR: - { - // (in begin size -- out) - if (stack.size() < 3) - return false; - valtype& vch = stacktop(-3); - int nBegin = CastToBigNum(stacktop(-2)).getint(); - int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint(); - if (nBegin < 0 || nEnd < nBegin) - return false; - if (nBegin > (int)vch.size()) - nBegin = vch.size(); - if (nEnd > (int)vch.size()) - nEnd = vch.size(); - vch.erase(vch.begin() + nEnd, vch.end()); - vch.erase(vch.begin(), vch.begin() + nBegin); - popstack(stack); - popstack(stack); - } - break; - - case OP_LEFT: - case OP_RIGHT: - { - // (in size -- out) - if (stack.size() < 2) - return false; - valtype& vch = stacktop(-2); - int nSize = CastToBigNum(stacktop(-1)).getint(); - if (nSize < 0) - return false; - if (nSize > (int)vch.size()) - nSize = vch.size(); - if (opcode == OP_LEFT) - vch.erase(vch.begin() + nSize, vch.end()); - else - vch.erase(vch.begin(), vch.end() - nSize); - popstack(stack); - } - break; - case OP_SIZE: { // (in -- in size) if (stack.size() < 1) return false; - CBigNum bn(stacktop(-1).size()); + CBigNum bn((uint16_t) stacktop(-1).size()); stack.push_back(bn.getvch()); } break; @@ -651,46 +830,6 @@ bool EvalScript(vector >& stack, const CScript& script, co // // Bitwise logic // - case OP_INVERT: - { - // (in - out) - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - for (unsigned int i = 0; i < vch.size(); i++) - vch[i] = ~vch[i]; - } - break; - - case OP_AND: - case OP_OR: - case OP_XOR: - { - // (x1 x2 - out) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - MakeSameSize(vch1, vch2); - if (opcode == OP_AND) - { - for (unsigned int i = 0; i < vch1.size(); i++) - vch1[i] &= vch2[i]; - } - else if (opcode == OP_OR) - { - for (unsigned int i = 0; i < vch1.size(); i++) - vch1[i] |= vch2[i]; - } - else if (opcode == OP_XOR) - { - for (unsigned int i = 0; i < vch1.size(); i++) - vch1[i] ^= vch2[i]; - } - popstack(stack); - } - break; - case OP_EQUAL: case OP_EQUALVERIFY: //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL @@ -698,8 +837,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 - bool) if (stack.size() < 2) return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); + auto& vch1 = stacktop(-2); + auto& vch2 = stacktop(-1); bool fEqual = (vch1 == vch2); // OP_NOTEQUAL is disabled because it would be too easy to say // something like n != 1 and have some wiseguy pass in 1 with extra @@ -725,8 +864,6 @@ bool EvalScript(vector >& stack, const CScript& script, co // case OP_1ADD: case OP_1SUB: - case OP_2MUL: - case OP_2DIV: case OP_NEGATE: case OP_ABS: case OP_NOT: @@ -735,13 +872,11 @@ bool EvalScript(vector >& stack, const CScript& script, co // (in -- out) if (stack.size() < 1) return false; - CBigNum bn = CastToBigNum(stacktop(-1)); + auto bn = CastToBigNum(stacktop(-1)); switch (opcode) { case OP_1ADD: bn += bnOne; break; case OP_1SUB: bn -= bnOne; break; - case OP_2MUL: bn <<= 1; break; - case OP_2DIV: bn >>= 1; break; case OP_NEGATE: bn = -bn; break; case OP_ABS: if (bn < bnZero) bn = -bn; break; case OP_NOT: bn = (bn == bnZero); break; @@ -755,11 +890,6 @@ bool EvalScript(vector >& stack, const CScript& script, co case OP_ADD: case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_MOD: - case OP_LSHIFT: - case OP_RSHIFT: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: @@ -775,8 +905,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- out) if (stack.size() < 2) return false; - CBigNum bn1 = CastToBigNum(stacktop(-2)); - CBigNum bn2 = CastToBigNum(stacktop(-1)); + auto bn1 = CastToBigNum(stacktop(-2)); + auto bn2 = CastToBigNum(stacktop(-1)); CBigNum bn; switch (opcode) { @@ -788,33 +918,6 @@ bool EvalScript(vector >& stack, const CScript& script, co bn = bn1 - bn2; break; - case OP_MUL: - if (!BN_mul(&bn, &bn1, &bn2, pctx)) - return false; - break; - - case OP_DIV: - if (!BN_div(&bn, NULL, &bn1, &bn2, pctx)) - return false; - break; - - case OP_MOD: - if (!BN_mod(&bn, &bn1, &bn2, pctx)) - return false; - break; - - case OP_LSHIFT: - if (bn2 < bnZero || bn2 > CBigNum(2048)) - return false; - bn = bn1 << bn2.getulong(); - break; - - case OP_RSHIFT: - if (bn2 < bnZero || bn2 > CBigNum(2048)) - return false; - bn = bn1 >> bn2.getulong(); - break; - case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; case OP_NUMEQUAL: bn = (bn1 == bn2); break; @@ -847,9 +950,9 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x min max -- out) if (stack.size() < 3) return false; - CBigNum bn1 = CastToBigNum(stacktop(-3)); - CBigNum bn2 = CastToBigNum(stacktop(-2)); - CBigNum bn3 = CastToBigNum(stacktop(-1)); + auto bn1 = CastToBigNum(stacktop(-3)); + auto bn2 = CastToBigNum(stacktop(-2)); + auto bn3 = CastToBigNum(stacktop(-1)); bool fValue = (bn2 <= bn1 && bn1 < bn3); popstack(stack); popstack(stack); @@ -871,7 +974,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (in -- hash) if (stack.size() < 1) return false; - valtype& vch = stacktop(-1); + auto& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) RIPEMD160(&vch[0], vch.size(), &vchHash[0]); @@ -881,12 +984,12 @@ bool EvalScript(vector >& stack, const CScript& script, co SHA256(&vch[0], vch.size(), &vchHash[0]); else if (opcode == OP_HASH160) { - uint160 hash160 = Hash160(vch); + auto hash160 = Hash160(vch); memcpy(&vchHash[0], &hash160, sizeof(hash160)); } else if (opcode == OP_HASH256) { - uint256 hash = Hash(vch.begin(), vch.end()); + auto hash = Hash(vch.begin(), vch.end()); memcpy(&vchHash[0], &hash, sizeof(hash)); } popstack(stack); @@ -908,8 +1011,8 @@ bool EvalScript(vector >& stack, const CScript& script, co if (stack.size() < 2) return false; - valtype& vchSig = stacktop(-2); - valtype& vchPubKey = stacktop(-1); + auto& vchSig = stacktop(-2); + auto& vchPubKey = stacktop(-1); ////// debug print //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); @@ -921,7 +1024,8 @@ 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)); - bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); popstack(stack); popstack(stack); @@ -942,10 +1046,10 @@ bool EvalScript(vector >& stack, const CScript& script, co // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) int i = 1; - if (stack.size() < i) + if ((int)stack.size() < i) return false; - int nKeysCount = CastToBigNum(stacktop(-i)).getint(); + int nKeysCount = CastToBigNum(stacktop(-i)).getint32(); if (nKeysCount < 0 || nKeysCount > 20) return false; nOpCount += nKeysCount; @@ -953,15 +1057,15 @@ bool EvalScript(vector >& stack, const CScript& script, co return false; int ikey = ++i; i += nKeysCount; - if (stack.size() < i) + if ((int)stack.size() < i) return false; - int nSigsCount = CastToBigNum(stacktop(-i)).getint(); + int nSigsCount = CastToBigNum(stacktop(-i)).getint32(); if (nSigsCount < 0 || nSigsCount > nKeysCount) return false; int isig = ++i; i += nSigsCount; - if (stack.size() < i) + if ((int)stack.size() < i) return false; // Subset of script starting at the most recent codeseparator @@ -970,19 +1074,21 @@ bool EvalScript(vector >& stack, const CScript& script, co // Drop the signatures, since there's no way for a signature to sign itself for (int k = 0; k < nSigsCount; k++) { - valtype& vchSig = stacktop(-isig-k); + auto& vchSig = stacktop(-isig-k); scriptCode.FindAndDelete(CScript(vchSig)); } bool fSuccess = true; while (fSuccess && nSigsCount > 0) { - valtype& vchSig = stacktop(-isig); - valtype& vchPubKey = stacktop(-ikey); + auto& vchSig = stacktop(-isig); + auto& vchPubKey = stacktop(-ikey); // Check signature - if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) - { + bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + if (fOk) { isig++; nSigsCount--; } @@ -995,8 +1101,21 @@ bool EvalScript(vector >& stack, const CScript& script, co fSuccess = false; } - while (i-- > 0) + while (i-- > 1) popstack(stack); + + // A bug causes CHECKMULTISIG to consume one extra argument + // whose contents were not checked in any way. + // + // Unfortunately this is a potential source of mutability, + // so optionally verify it is exactly equal to zero prior + // to removing it from the stack. + if (stack.size() < 1) + return false; + if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size()) + return error("CHECKMULTISIG dummy argument not null"); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); if (opcode == OP_CHECKMULTISIGVERIFY) @@ -1038,11 +1157,11 @@ bool EvalScript(vector >& stack, const CScript& script, co -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, uint32_t nIn, int nHashType) { if (nIn >= txTo.vin.size()) { - printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + printf("ERROR: SignatureHash() : nIn=%" PRIu32 " out of range\n", nIn); return 1; } CTransaction txTmp(txTo); @@ -1069,11 +1188,11 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int } else if ((nHashType & 0x1f) == SIGHASH_SINGLE) { - // Only lockin the txout payee at same index as txin - unsigned int nOut = nIn; + // Only lock-in the txout payee at same index as txin + uint32_t nOut = nIn; if (nOut >= txTmp.vout.size()) { - printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + printf("ERROR: SignatureHash() : nOut=%" PRIu32 " out of range\n", nOut); return 1; } txTmp.vout.resize(nOut+1); @@ -1109,45 +1228,43 @@ class CSignatureCache { private: // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, std::vector > sigdata_type; - std::set< sigdata_type> setValid; - CCriticalSection cs_sigcache; + typedef tuple, CPubKey > sigdata_type; + set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; public: bool - Get(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) { - LOCK(cs_sigcache); + boost::shared_lock lock(cs_sigcache); sigdata_type k(hash, vchSig, pubKey); - std::set::iterator mi = setValid.find(k); + auto mi = setValid.find(k); if (mi != setValid.end()) return true; return false; } - void - Set(uint256 hash, const std::vector& vchSig, const std::vector& pubKey) + void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) { // DoS prevention: limit cache size to less than 10MB // (~200 bytes per cache entry times 50,000 entries) // Since there are a maximum of 20,000 signature operations per block // 50,000 is a reasonable default. - int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); if (nMaxCacheSize <= 0) return; - LOCK(cs_sigcache); + boost::shared_lock lock(cs_sigcache); - while (static_cast(setValid.size()) > nMaxCacheSize) + while (static_cast(setValid.size()) > nMaxCacheSize) { // Evict a random entry. Random because that helps // foil would-be DoS attackers who might try to pre-generate // and re-use a set of valid signatures just-slightly-greater // than our cache size. - uint256 randomHash = GetRandHash(); + auto randomHash = GetRandHash(); std::vector unused; - std::set::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + auto it = setValid.lower_bound(sigdata_type(randomHash, unused, unused)); if (it == setValid.end()) it = setValid.begin(); setValid.erase(*it); @@ -1158,11 +1275,15 @@ public: } }; -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, - const CTransaction& txTo, unsigned int nIn, int nHashType) +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) { static CSignatureCache signatureCache; + CPubKey pubkey(vchPubKey); + if (!pubkey.IsValid()) + return false; + // Hash type is one byte tacked on to the end of the signature if (vchSig.empty()) return false; @@ -1172,30 +1293,21 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc return false; vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + auto sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - if (signatureCache.Get(sighash, vchSig, vchPubKey)) + if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - CKey key; - if (!key.SetPubKey(vchPubKey)) + if (!pubkey.Verify(sighash, vchSig)) return false; - if (!key.Verify(sighash, vchSig)) - return false; + if (!(flags & SCRIPT_VERIFY_NOCACHE)) + signatureCache.Set(sighash, vchSig, pubkey); - signatureCache.Set(sighash, vchSig, vchPubKey); return true; } - - - - - - - // // Return public keys or hashes from scriptPubKey, for 'standard' transaction types. // @@ -1206,15 +1318,23 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { + typeRet = TX_NULL_DATA; + return true; + } + // Scan templates - const CScript& script1 = scriptPubKey; - BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) + const auto& script1 = scriptPubKey; + for(const auto& tplate : mTemplates) { - const CScript& script2 = tplate.second; + const auto& script2 = tplate.second; vSolutionsRet.clear(); opcodetype opcode1, opcode2; vector vch1, vch2; // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - loop + auto pc1 = script1.begin(); + auto pc2 = script2.begin(); + for ( ; ; ) { if (pc1 == script1.end() && pc2 == script2.end()) { @@ -1247,8 +1377,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector n || vSolutionsRet.size()-2 != n) return false; } @@ -1271,7 +1401,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector 1024) + break; + } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly @@ -1311,7 +1461,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +bool SignR(const CPubKey& pubKey, const CPubKey& R, const CKeyStore& keystore, const uint256& hash, int nHashType, CScript& scriptSigRet) +{ + CKey key; + if (!keystore.CreatePrivKey(pubKey, R, key)) + return false; + + vector vchSig; + if (!key.Sign(hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} + +bool SignN(const vector& multisigdata, const CKeyStore& keystore, const uint256& hash, int nHashType, CScript& scriptSigRet) { int nSigned = 0; int nRequired = multisigdata.front()[0]; - for (vector::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++) + for (uint32_t i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) { - const valtype& pubkey = *it; - CBitcoinAddress address; - address.SetPubKey(pubkey); - if (Sign1(address, keystore, hash, nHashType, scriptSigRet)) - { + const auto& pubkey = multisigdata[i]; + auto keyID = CPubKey(pubkey).GetID(); + if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) ++nSigned; - if (nSigned == nRequired) break; - } } return nSigned==nRequired; } @@ -1348,9 +1509,9 @@ bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint2 // Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. // Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), // unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. -// Returns false if scriptPubKey could not be completely satisified. +// Returns false if scriptPubKey could not be completely satisfied. // -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, const uint256& hash, int nHashType, CScript& scriptSigRet, txnouttype& whichTypeRet) { scriptSigRet.clear(); @@ -1359,22 +1520,29 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; - CBitcoinAddress address; + CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: - address.SetPubKey(vSolutions[0]); - return Sign1(address, keystore, hash, nHashType, scriptSigRet); + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEY_DROP: + { + auto key = CPubKey(vSolutions[0]); + auto R = CPubKey(vSolutions[1]); + return SignR(key, R, keystore, hash, nHashType, scriptSigRet); + } case TX_PUBKEYHASH: - address.SetHash160(uint160(vSolutions[0])); - if (!Sign1(address, keystore, hash, nHashType, scriptSigRet)) + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) return false; else { - valtype vch; - keystore.GetPubKey(address, vch); + CPubKey vch; + keystore.GetPubKey(keyID, vch); scriptSigRet << vch; } return true; @@ -1394,7 +1562,10 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector vSolutions; - txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1432,41 +1602,79 @@ bool IsStandard(const CScript& scriptPubKey) unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) { - unsigned int nResult = 0; - BOOST_FOREACH(const valtype& pubkey, pubkeys) + uint32_t nResult = 0; + for(const auto& pubkey : pubkeys) { - CBitcoinAddress address; - address.SetPubKey(pubkey); - if (keystore.HaveKey(address)) + auto keyID = CPubKey(pubkey).GetID(); + if (keystore.HaveKey(keyID)) ++nResult; } return nResult; } -bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) + +class CKeyStoreIsMineVisitor : public boost::static_visitor +{ +private: + const CKeyStore *keystore; +public: + CKeyStoreIsMineVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { } + bool operator()(const CNoDestination &dest) const { return false; } + bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); } + bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } +}; + +isminetype IsMine(const CKeyStore &keystore, const CBitcoinAddress& dest) +{ + CScript script; + script.SetAddress(dest); + return IsMine(keystore, script); +} + +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey)) + return MINE_WATCH_ONLY; + return MINE_NO; + } - CBitcoinAddress address; + CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: - return false; + case TX_NULL_DATA: + break; case TX_PUBKEY: - address.SetPubKey(vSolutions[0]); - return keystore.HaveKey(address); + keyID = CPubKey(vSolutions[0]).GetID(); + if (keystore.HaveKey(keyID)) + return MINE_SPENDABLE; + break; + case TX_PUBKEY_DROP: + { + auto key = CPubKey(vSolutions[0]); + auto R = CPubKey(vSolutions[1]); + if (keystore.CheckOwnership(key, R)) + return MINE_SPENDABLE; + } + break; case TX_PUBKEYHASH: - address.SetHash160(uint160(vSolutions[0])); - return keystore.HaveKey(address); + keyID = CKeyID(uint160(vSolutions[0])); + if (keystore.HaveKey(keyID)) + return MINE_SPENDABLE; + break; case TX_SCRIPTHASH: { + auto scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; - if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) - return false; - return IsMine(keystore, subscript); + if (keystore.GetCScript(scriptID, subscript)) { + auto ret = IsMine(keystore, subscript); + if (ret == MINE_SPENDABLE) + return ret; + } + break; } case TX_MULTISIG: { @@ -1476,13 +1684,44 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - return HaveKeys(keys, keystore) == keys.size(); + if (HaveKeys(keys, keystore) == keys.size()) + return MINE_SPENDABLE; + break; } } + + if (keystore.HaveWatchOnly(scriptPubKey)) + return MINE_WATCH_ONLY; + return MINE_NO; +} + +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + addressRet = CPubKey(vSolutions[0]).GetID(); + return true; + } + else if (whichType == TX_PUBKEYHASH) + { + addressRet = CKeyID(uint160(vSolutions[0])); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet = CScriptID(uint160(vSolutions[0])); + return true; + } + // Multisig txns have more than one address... return false; } -bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet) +bool ExtractAddress(const CKeyStore &keystore, const CScript& scriptPubKey, CBitcoinAddress& addressRet) { vector vSolutions; txnouttype whichType; @@ -1491,66 +1730,115 @@ bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet) if (whichType == TX_PUBKEY) { - addressRet.SetPubKey(vSolutions[0]); + addressRet = CBitcoinAddress(CPubKey(vSolutions[0]).GetID()); + return true; + } + if (whichType == TX_PUBKEY_DROP) + { + // Pay-to-Pubkey-R + CMalleableKeyView view; + if (!keystore.CheckOwnership(CPubKey(vSolutions[0]), CPubKey(vSolutions[1]), view)) + return false; + + addressRet = CBitcoinAddress(view.GetMalleablePubKey()); return true; } else if (whichType == TX_PUBKEYHASH) { - addressRet.SetHash160(uint160(vSolutions[0])); + addressRet = CBitcoinAddress(CKeyID(uint160(vSolutions[0]))); return true; } else if (whichType == TX_SCRIPTHASH) { - addressRet.SetScriptHash160(uint160(vSolutions[0])); + addressRet = CBitcoinAddress(CScriptID(uint160(vSolutions[0]))); return true; } // Multisig txns have more than one address... return false; } -bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) +class CAffectedKeysVisitor : public boost::static_visitor { +private: + const CKeyStore &keystore; + CAffectedKeysVisitor& operator=(CAffectedKeysVisitor const&); + std::vector &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + for(const CTxDestination &dest : vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + + +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector &vKeys) { + CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); +} + +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) { addressRet.clear(); typeRet = TX_NONSTANDARD; vector vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; + if (typeRet == TX_NULL_DATA) + { + nRequiredRet = 0; + return true; + } if (typeRet == TX_MULTISIG) { nRequiredRet = vSolutions.front()[0]; for (unsigned int i = 1; i < vSolutions.size()-1; i++) { - CBitcoinAddress address; - address.SetPubKey(vSolutions[i]); + CTxDestination address = CPubKey(vSolutions[i]).GetID(); addressRet.push_back(address); } } else { nRequiredRet = 1; - CBitcoinAddress address; - if (typeRet == TX_PUBKEYHASH) - address.SetHash160(uint160(vSolutions.front())); - else if (typeRet == TX_SCRIPTHASH) - address.SetScriptHash160(uint160(vSolutions.front())); - else if (typeRet == TX_PUBKEY) - address.SetPubKey(vSolutions.front()); + if (typeRet == TX_PUBKEY_DROP) + return true; + CTxDestination address; + if (!ExtractDestination(scriptPubKey, address)) + return false; addressRet.push_back(address); } return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, uint32_t nIn, unsigned int flags, int nHashType) { vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) return false; - if (fValidatePayToScriptHash) + if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) return false; if (stack.empty()) return false; @@ -1559,16 +1847,21 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return false; // Additional validation for spend-to-script-hash transactions: - if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash()) + if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only return false; // or validation fails - const valtype& pubKeySerialized = stackCopy.back(); + // stackCopy cannot be empty here, because if it was the + // P2SH HASH <> EQUAL scriptPubKey would be evaluated with + // an empty stack and the EvalScript above would return false. + assert(!stackCopy.empty()); + + const auto& pubKeySerialized = stackCopy.back(); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType)) + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) return false; if (stackCopy.empty()) return false; @@ -1578,20 +1871,17 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return true; } - -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, uint32_t nIn, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; + auto& txin = txTo.vin[nIn]; // Leave out the signature from the hash, since a signature can't sign itself. // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(txout.scriptPubKey, txTo, nIn, nHashType); + auto hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); txnouttype whichType; - if (!Solver(keystore, txout.scriptPubKey, hash, nHashType, txin.scriptSig, whichType)) + if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) return false; if (whichType == TX_SCRIPTHASH) @@ -1599,48 +1889,400 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans // Solver returns the subscript that need to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = txin.scriptSig; + auto subscript = txin.scriptSig; // Recompute txn hash using subscript in place of scriptPubKey: - uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + auto hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + txnouttype subType; - if (!Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType)) - return false; - if (subType == TX_SCRIPTHASH) - return false; - txin.scriptSig << static_cast(subscript); // Append serialized subscript + bool fSolved = Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + // Append serialized subscript whether or not it is completely signed: + txin.scriptSig << static_cast(subscript); + if (!fSolved) return false; } // Test solution - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, true, 0)) - return false; + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STRICT_FLAGS, 0); +} - return true; +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, uint32_t nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + auto& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + assert(txin.prevout.hash == txFrom.GetHash()); + const auto& txout = txFrom.vout[txin.prevout.n]; + + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); +} + +static CScript PushAll(const vector& values) +{ + CScript result; + for(const auto& v : values) + result << v; + return result; } +static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + // Combine all the signatures we've got: + set allsigs; + for(const auto& v : sigs1) + { + if (!v.empty()) + allsigs.insert(v); + } + for(const auto& v : sigs2) + { + if (!v.empty()) + allsigs.insert(v); + } + + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(vSolutions.size() > 1); + auto nSigsRequired = (uint32_t)vSolutions.front()[0]; + auto nPubKeys = (uint32_t)(vSolutions.size()-2); + + map sigs; + for(const auto& sig : allsigs) + { + for (unsigned int i = 0; i < nPubKeys; i++) + { + const auto& pubkey = vSolutions[i+1]; + if (sigs.count(pubkey)) + continue; // Already got a sig for this pubkey + + if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) + { + sigs[pubkey] = sig; + break; + } + } + } + // Now build a merged CScript: + uint32_t nSigsHave = 0; + CScript result; result << OP_0; // pop-one-too-many workaround + for (uint32_t i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) + { + if (sigs.count(vSolutions[i+1])) + { + result << sigs[vSolutions[i+1]]; + ++nSigsHave; + } + } + // Fill any missing with OP_0: + for (auto i = nSigsHave; i < nSigsRequired; i++) + result << OP_0; + + return result; +} -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType) +static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const txnouttype txType, const vector& vSolutions, + vector& sigs1, vector& sigs2) { - assert(nIn < txTo.vin.size()); - const CTxIn& txin = txTo.vin[nIn]; - if (txin.prevout.n >= txFrom.vout.size()) - return false; - const CTxOut& txout = txFrom.vout[txin.prevout.n]; + switch (txType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.size() >= sigs2.size()) + return PushAll(sigs1); + return PushAll(sigs2); + case TX_PUBKEY: + case TX_PUBKEY_DROP: + case TX_PUBKEYHASH: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.empty() || sigs1[0].empty()) + return PushAll(sigs2); + return PushAll(sigs1); + case TX_SCRIPTHASH: + if (sigs1.empty() || sigs1.back().empty()) + return PushAll(sigs2); + else if (sigs2.empty() || sigs2.back().empty()) + return PushAll(sigs1); + else + { + // Recur to combine: + auto spk = sigs1.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.pop_back(); + sigs2.pop_back(); + auto result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + result << spk; + return result; + } + case TX_MULTISIG: + return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); + } + + return CScript(); +} + +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, uint32_t nIn, + const CScript& scriptSig1, const CScript& scriptSig2) +{ + txnouttype txType; + vector > vSolutions; + Solver(scriptPubKey, txType, vSolutions); + + vector stack1; + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + vector stack2; + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + + return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); +} + +// +//CScript +// + +CScript& CScript::push_int64(int64_t n) +{ + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back((uint8_t)n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; +} + +CScript& CScript::push_uint64(uint64_t n) +{ + if (n >= 1 && n <= 16) + { + push_back((uint8_t)n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; +} + +CScript& CScript::operator+=(const CScript& b) +{ + insert(end(), b.begin(), b.end()); + return *this; +} + +CScript& CScript::operator<<(opcodetype opcode) +{ + insert(end(), opcode); + return *this; +} + +CScript& CScript::operator<<(const uint160& b) +{ + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; +} + +CScript& CScript::operator<<(const uint256& b) +{ + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; +} + +CScript& CScript::operator<<(const CPubKey& key) +{ + std::vector vchKey(key.begin(), key.end()); + return (*this) << vchKey; +} + +CScript& CScript::operator<<(const CBigNum& b) +{ + *this << b.getvch(); + return *this; +} - if (txin.prevout.hash != txFrom.GetHash()) +CScript& CScript::operator<<(const std::vector& b) +{ + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + uint16_t nSize = (uint16_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + uint32_t nSize = (uint32_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return *this; +} + +CScript& CScript::operator<<(const CScript& b) +{ + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(!"Warning: Pushing a CScript onto a CScript with << is probably not intended, use + to concatenate!"); + return *this; +} + +bool CScript::GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) +{ + // Wrapper so it can be called with either iterator or const_iterator + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, &vchRet); + pc = begin() + (pc2 - begin()); + return fRet; +} + +bool CScript::GetOp(iterator& pc, opcodetype& opcodeRet) +{ + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, NULL); + pc = begin() + (pc2 - begin()); + return fRet; +} + +bool CScript::GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const +{ + return GetOp2(pc, opcodeRet, &vchRet); +} + +bool CScript::GetOp(const_iterator& pc, opcodetype& opcodeRet) const +{ + return GetOp2(pc, opcodeRet, NULL); +} + +bool CScript::GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const +{ + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end()) return false; - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType)) + // Read instruction + if (end() - pc < 1) return false; + uint32_t opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + uint32_t nSize = OP_0; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end() - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end() - pc < 2) + return false; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end() - pc < 4) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (end() - pc < 0 || (uint32_t)(end() - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + opcodeRet = (opcodetype)opcode; return true; } +int CScript::DecodeOP_N(opcodetype opcode) +{ + if (opcode == OP_0) + return 0; + assert(opcode >= OP_1 && opcode <= OP_16); + return (opcode - (OP_1 - 1)); +} + +opcodetype CScript::EncodeOP_N(int n) +{ + assert(n >= 0 && n <= 16); + if (n == 0) + return OP_0; + return (opcodetype)(OP_1+n-1); +} + +int CScript::FindAndDelete(const CScript& b) +{ + int nFound = 0; + if (b.empty()) + return nFound; + CScript result; + iterator pc = begin(), pc2 = begin(); + opcodetype opcode; + do + { + result.insert(result.end(), pc2, pc); + while (static_cast(end() - pc) >= b.size() && equal(b.begin(), b.end(), pc)) + { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } + while (GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end()); + *this = result; + } + return nFound; +} + +int CScript::Find(opcodetype op) const +{ + int nFound = 0; + opcodetype opcode; + for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) + if (opcode == op) + ++nFound; + return nFound; +} + unsigned int CScript::GetSigOpCount(bool fAccurate) const { - unsigned int n = 0; - const_iterator pc = begin(); - opcodetype lastOpcode = OP_INVALIDOPCODE; + uint32_t n = 0; + auto pc = begin(); + auto lastOpcode = OP_INVALIDOPCODE; while (pc < end()) { opcodetype opcode; @@ -1668,7 +2310,7 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const // This is a pay-to-script-hash scriptPubKey; // get the last item that the scriptSig // pushes onto the stack: - const_iterator pc = scriptSig.begin(); + auto pc = scriptSig.begin(); vector data; while (pc < scriptSig.end()) { @@ -1679,7 +2321,7 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const return 0; } - /// ... and return it's opcount: + /// ... and return its opcount: CScript subscript(data.begin(), data.end()); return subscript.GetSigOpCount(true); } @@ -1693,29 +2335,145 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } -void CScript::SetBitcoinAddress(const CBitcoinAddress& address) +bool CScript::IsPushOnly(const_iterator pc) const +{ + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; +} + +// Called by CTransaction::IsStandard and P2SH VerifyScript (which makes it consensus-critical). +bool CScript::IsPushOnly() const +{ + return this->IsPushOnly(begin()); +} + +bool CScript::HasCanonicalPushes() const +{ + auto pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector data; + if (!GetOp(pc, opcode, data)) + return false; + if (opcode > OP_16) + continue; + if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) + // Could have used an OP_n code, rather than a 1-byte push. + return false; + if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) + // Could have used an OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) + // Could have used an OP_PUSHDATA2. + return false; + } + return true; +} + +class CScriptVisitor : public boost::static_visitor +{ +private: + CScript *script; +public: + CScriptVisitor(CScript *scriptin) { script = scriptin; } + + bool operator()(const CNoDestination &dest) const { + script->clear(); + return false; + } + + bool operator()(const CKeyID &keyID) const { + script->clear(); + *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + *script << OP_HASH160 << scriptID << OP_EQUAL; + return true; + } +}; + +void CScript::SetDestination(const CTxDestination& dest) +{ + boost::apply_visitor(CScriptVisitor(this), dest); +} + +void CScript::SetAddress(const CBitcoinAddress& dest) { this->clear(); - if (address.IsScript()) - *this << OP_HASH160 << address.GetHash160() << OP_EQUAL; - else - *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; + if (dest.IsScript()) + *this << OP_HASH160 << dest.GetData() << OP_EQUAL; + else if (dest.IsPubKey()) + *this << OP_DUP << OP_HASH160 << dest.GetData() << OP_EQUALVERIFY << OP_CHECKSIG; + else if (dest.IsPair()) { + // Pubkey pair address, going to generate + // new one-time public key. + CMalleablePubKey mpk; + if (!mpk.setvch(dest.GetData())) + return; + CPubKey R, pubKeyVariant; + mpk.GetVariant(R, pubKeyVariant); + *this << pubKeyVariant << R << OP_DROP << OP_CHECKSIG; + } } -void CScript::SetMultisig(int nRequired, const std::vector& keys) +void CScript::SetMultisig(int nRequired, const std::vector& keys) { this->clear(); *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) - *this << key.GetPubKey(); - *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; + for(const auto& key : keys) + *this << key; + *this << EncodeOP_N((int)(keys.size())) << OP_CHECKMULTISIG; } -void CScript::SetPayToScriptHash(const CScript& subscript) +void CScript::PrintHex() const { - assert(!subscript.empty()); - uint160 subscriptHash = Hash160(subscript); - this->clear(); - *this << OP_HASH160 << subscriptHash << OP_EQUAL; + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); +} + +std::string CScript::ToString(bool fShort) const +{ + std::string str; + opcodetype opcode; + std::vector vch; + const_iterator pc = begin(); + while (pc < end()) + { + if (!str.empty()) + str += " "; + if (!GetOp(pc, opcode, vch)) + { + str += "[error]"; + return str; + } + if (opcode <= OP_PUSHDATA4) + str += fShort? ValueString(vch).substr(0, 10) : ValueString(vch); + else + str += GetOpName(opcode); + } + return str; +} + +void CScript::print() const +{ + printf("%s\n", ToString().c_str()); +} + +CScriptID CScript::GetID() const +{ + return CScriptID(Hash160(*this)); }