X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fscript.cpp;h=ad31b76feedac081b3a8378018b4be950a797783;hp=00c9990b8fe01e5f0b08fc689e09212e03c79726;hb=9e58e0a8ca28b15a4bfa677f5b23891972db40fd;hpb=faf705a42a05197d89abfc31672ced94d268767f diff --git a/src/script.cpp b/src/script.cpp index 00c9990..ad31b76 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,8 +1,9 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include using namespace std; using namespace boost; @@ -12,12 +13,11 @@ using namespace boost; #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); +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); @@ -51,12 +51,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); } @@ -82,9 +99,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; } @@ -129,6 +148,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"; @@ -211,8 +232,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"; @@ -226,6 +245,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: @@ -233,7 +253,169 @@ 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, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -247,7 +429,6 @@ bool EvalScript(vector >& stack, const CScript& script, co return false; int nOpCount = 0; - try { while (pc < pend) @@ -259,7 +440,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; @@ -279,7 +460,7 @@ 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) stack.push_back(vchPushValue); @@ -318,7 +499,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; @@ -377,6 +558,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; + + CBigNum 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. + CBigNum 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 @@ -484,7 +726,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; @@ -534,9 +776,9 @@ 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 >= stack.size()) + if (n < 0 || n >= (int)stack.size()) return false; valtype vch = stacktop(-n-1); if (opcode == OP_ROLL) @@ -577,70 +819,12 @@ bool EvalScript(vector >& stack, const CScript& script, co 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 > vch.size()) - nBegin = vch.size(); - if (nEnd > 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 > 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; @@ -649,46 +833,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 @@ -723,8 +867,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: @@ -738,8 +880,6 @@ bool EvalScript(vector >& stack, const CScript& script, co { 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; @@ -753,11 +893,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: @@ -786,33 +921,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; @@ -919,7 +1027,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); @@ -939,11 +1048,11 @@ bool EvalScript(vector >& stack, const CScript& script, co { // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - unsigned int i = 1; - if (stack.size() < i) + int i = 1; + 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; @@ -951,15 +1060,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 @@ -979,8 +1088,10 @@ bool EvalScript(vector >& stack, const CScript& script, co valtype& 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--; } @@ -993,8 +1104,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) @@ -1067,7 +1191,7 @@ 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 + // Only lock-in the txout payee at same index as txin unsigned int nOut = nIn; if (nOut >= txTmp.vout.size()) { @@ -1099,11 +1223,69 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int } -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, - const CTransaction& txTo, unsigned int nIn, int nHashType) +// Valid signature cache, to avoid doing expensive ECDSA signature checking +// twice for every transaction (once when accepted into memory pool, and +// again when accepted into the block chain) + +class CSignatureCache { - CKey key; - if (!key.SetPubKey(vchPubKey)) +private: + // sigdata_type is (signature hash, signature, public key): + typedef boost::tuple, CPubKey > sigdata_type; + std::set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; + +public: + bool + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + boost::shared_lock lock(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + 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_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + boost::shared_lock lock(cs_sigcache); + + 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(); + std::vector unused; + std::set::iterator it = + setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + if (it == setValid.end()) + it = setValid.begin(); + setValid.erase(*it); + } + + sigdata_type k(hash, vchSig, pubKey); + setValid.insert(k); + } +}; + +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 @@ -1115,15 +1297,19 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc return false; vchSig.pop_back(); - return key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig); -} - - - + uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + if (signatureCache.Get(sighash, vchSig, pubkey)) + return true; + if (!pubkey.Verify(sighash, vchSig)) + return false; + if (!(flags & SCRIPT_VERIFY_NOCACHE)) + signatureCache.Set(sighash, vchSig, pubkey); + return true; +} // @@ -1138,13 +1324,21 @@ 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) @@ -1168,7 +1372,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector 1024) + break; + } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly @@ -1241,7 +1465,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 (unsigned int 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 valtype& pubkey = multisigdata[i]; + CKeyID keyID = CPubKey(pubkey).GetID(); + if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) ++nSigned; - if (nSigned == nRequired) break; - } } return nSigned==nRequired; } @@ -1278,9 +1513,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(); @@ -1289,22 +1524,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: + { + CPubKey key = CPubKey(vSolutions[0]); + CPubKey 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; @@ -1324,7 +1566,10 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector vSolutions; - txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1360,43 +1604,89 @@ bool IsStandard(const CScript& scriptPubKey) } -int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) { - int nResult = 0; + unsigned int nResult = 0; BOOST_FOREACH(const valtype& pubkey, pubkeys) { - CBitcoinAddress address; - address.SetPubKey(pubkey); - if (keystore.HaveKey(address)) + CKeyID 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 CTxDestination& dest) +{ + CScript script; + script.SetDestination(dest); + return IsMine(keystore, script); +}*/ + +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: + { + CPubKey key = CPubKey(vSolutions[0]); + CPubKey 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: { + CScriptID 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)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == MINE_SPENDABLE) + return ret; + } + break; } case TX_MULTISIG: { @@ -1406,13 +1696,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; @@ -1421,51 +1742,101 @@ 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)) { + BOOST_FOREACH(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); } @@ -1473,14 +1844,14 @@ bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, 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; @@ -1489,16 +1860,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 + // 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 valtype& 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; @@ -1508,20 +1884,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, unsigned int 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]; // 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); + uint256 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) @@ -1533,42 +1906,157 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans // Recompute txn hash using subscript in place of scriptPubKey: uint256 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 true; + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STRICT_FLAGS, 0); } - -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); - const CTxIn& txin = txTo.vin[nIn]; - if (txin.prevout.n >= txFrom.vout.size()) - return false; + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + assert(txin.prevout.hash == txFrom.GetHash()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - if (txin.prevout.hash != txFrom.GetHash()) - return false; + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); +} - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType)) - return false; +static CScript PushAll(const vector& values) +{ + CScript result; + BOOST_FOREACH(const valtype& v, values) + result << v; + return result; +} - return true; +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; + BOOST_FOREACH(const valtype& v, sigs1) + { + if (!v.empty()) + allsigs.insert(v); + } + BOOST_FOREACH(const valtype& v, sigs2) + { + if (!v.empty()) + allsigs.insert(v); + } + + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(vSolutions.size() > 1); + unsigned int nSigsRequired = vSolutions.front()[0]; + unsigned int nPubKeys = (unsigned int)(vSolutions.size()-2); + map sigs; + BOOST_FOREACH(const valtype& sig, allsigs) + { + for (unsigned int i = 0; i < nPubKeys; i++) + { + const valtype& 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: + unsigned int nSigsHave = 0; + CScript result; result << OP_0; // pop-one-too-many workaround + for (unsigned int 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 (unsigned int i = nSigsHave; i < nSigsRequired; i++) + result << OP_0; + + return result; +} + +static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const txnouttype txType, const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + 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: + valtype spk = sigs1.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.pop_back(); + sigs2.pop_back(); + CScript 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, unsigned int 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); } -int CScript::GetSigOpCount(bool fAccurate) const +unsigned int CScript::GetSigOpCount(bool fAccurate) const { - int n = 0; + unsigned int n = 0; const_iterator pc = begin(); opcodetype lastOpcode = OP_INVALIDOPCODE; while (pc < end()) @@ -1590,7 +2078,7 @@ int CScript::GetSigOpCount(bool fAccurate) const return n; } -int CScript::GetSigOpCount(const CScript& scriptSig) const +unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const { if (!IsPayToScriptHash()) return GetSigOpCount(true); @@ -1609,7 +2097,7 @@ 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); } @@ -1623,29 +2111,88 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } -void CScript::SetBitcoinAddress(const CBitcoinAddress& address) +bool CScript::HasCanonicalPushes() const { - this->clear(); - if (address.IsScript()) - *this << OP_HASH160 << address.GetHash160() << OP_EQUAL; - else - *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; + const_iterator 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; } -void CScript::SetMultisig(int nRequired, const std::vector& keys) +class CScriptVisitor : public boost::static_visitor { - this->clear(); +private: + CScript *script; +public: + CScriptVisitor(CScript *scriptin) { script = scriptin; } - *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) - *this << key.GetPubKey(); - *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; + 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::SetPayToScriptHash(const CScript& subscript) +void CScript::SetAddress(const CBitcoinAddress& dest) { - assert(!subscript.empty()); - uint160 subscriptHash = Hash160(subscript); this->clear(); - *this << OP_HASH160 << subscriptHash << OP_EQUAL; + 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) +{ + this->clear(); + + *this << EncodeOP_N(nRequired); + BOOST_FOREACH(const CPubKey& key, keys) + *this << key; + *this << EncodeOP_N((int)(keys.size())) << OP_CHECKMULTISIG; }