diff --git a/src/compat.h b/src/compat.h index 3f61a9964..25aa6d89b 100644 --- a/src/compat.h +++ b/src/compat.h @@ -28,6 +28,7 @@ #endif #if MAC_OSX +#undef MSG_NOSIGNAL // undef prior to redefinition eliminates warnings #define MSG_NOSIGNAL SO_NOSIGPIPE #endif diff --git a/src/script.cpp b/src/script.cpp index 2fea7a7d5..adeeffa57 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -31,7 +31,6 @@ static const CBigNum bnFalse(0); static const CBigNum bnTrue(1); static const size_t nMaxNumSize = 4; - CBigNum CastToBigNum(const valtype& vch) { if (vch.size() > nMaxNumSize) @@ -1119,27 +1118,40 @@ bool CheckSig(vector vchSig, const vector &vchPubK +typedef struct { + txnouttype txType; + CScript *tScript; +} SScriptPairRec; - - -// -// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. -// bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) { - // Templates - static map mTemplates; - if (mTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); - - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); - - // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); - } + static SScriptPairRec *sTemplates = NULL; + SScriptPairRec *curTemplate; + + if (sTemplates == NULL) { + CScript *tScript; + + sTemplates = (SScriptPairRec *)malloc(sizeof(SScriptPairRec) * 4); + + // order templates such that most common transaction types are checked first + tScript = new CScript(); + *tScript << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG; + sTemplates[0].txType = TX_PUBKEYHASH; + sTemplates[0].tScript = tScript; + + tScript = new CScript(); + *tScript << OP_PUBKEY << OP_CHECKSIG; + sTemplates[1].txType = TX_PUBKEY; + sTemplates[1].tScript = tScript; + + tScript = new CScript(); + *tScript << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG; + sTemplates[2].txType = TX_MULTISIG; + sTemplates[2].tScript = tScript; + + sTemplates[3].txType = (txnouttype)-1; + sTemplates[3].tScript = NULL; + } // Shortcut for pay-to-script-hash, which are more constrained than the other types: // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL @@ -1150,26 +1162,29 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vectortScript != NULL) { + const CScript *testScript = curTemplate->tScript; + opcodetype opcode1, opcode2; + vector vch1; - opcodetype opcode1, opcode2; - vector vch1, vch2; + vSolutionsRet.clear(); // Compare CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); + CScript::const_iterator pc2 = testScript->begin(); + CScript::const_iterator end1 = script1.end(); + CScript::const_iterator end2 = testScript->end(); + loop { - if (pc1 == script1.end() && pc2 == script2.end()) + if (pc1 == end1 && pc2 == end2) { // Found a match - typeRet = tplate.first; + typeRet = curTemplate->txType; if (typeRet == TX_MULTISIG) { // Additional checks for TX_MULTISIG: @@ -1180,9 +1195,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vectorGetOp2(pc2, opcode2, NULL)) // templates push no data, no need to get vch break; // Template matching opcodes: @@ -1191,10 +1206,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector= 33 && vch1.size() <= 120) { vSolutionsRet.push_back(vch1); - if (!script1.GetOp(pc1, opcode1, vch1)) + if (!script1.GetOp2(pc1, opcode1, &vch1)) break; } - if (!script2.GetOp(pc2, opcode2, vch2)) + if (!testScript->GetOp2(pc2, opcode2, NULL)) break; // Normal situation is to fall through // to other if/else statements @@ -1223,16 +1238,18 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector vSolutions; + txnouttype whichType; + bool hasDestination = false; + + *outIsMine = false; + + if (Solver(scriptPubKey, whichType, vSolutions)) { + CKeyID keyID; + + switch (whichType) { + case TX_NONSTANDARD: + break; + + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + addressRet = keyID; + *outIsMine = keystore.HaveKey(keyID); + hasDestination = true; + break; + + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + addressRet = keyID; + *outIsMine = keystore.HaveKey(keyID); + hasDestination = true; + break; + + case TX_SCRIPTHASH: { + CScript subscript; + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + + addressRet = scriptID; + hasDestination = true; + + if (keystore.GetCScript(scriptID, subscript)) + *outIsMine = IsMine(keystore, subscript); + } + break; + + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + *outIsMine = HaveKeys(keys, keystore) == keys.size(); + } + } + } + + return hasDestination; +} + bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) { addressRet.clear(); diff --git a/src/script.h b/src/script.h index d736c2cb1..a174fcd96 100644 --- a/src/script.h +++ b/src/script.h @@ -426,14 +426,14 @@ public: bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const { + const_iterator scriptEnd = end(); + opcodeRet = OP_INVALIDOPCODE; if (pvchRet) pvchRet->clear(); - if (pc >= end()) - return false; // Read instruction - if (end() - pc < 1) + if (scriptEnd - pc < 1) return false; unsigned int opcode = *pc++; @@ -447,13 +447,13 @@ public: } else if (opcode == OP_PUSHDATA1) { - if (end() - pc < 1) + if (scriptEnd - pc < 1) return false; nSize = *pc++; } else if (opcode == OP_PUSHDATA2) { - if (end() - pc < 2) + if (scriptEnd - pc < 2) return false; nSize = 0; memcpy(&nSize, &pc[0], 2); @@ -461,12 +461,12 @@ public: } else if (opcode == OP_PUSHDATA4) { - if (end() - pc < 4) + if (scriptEnd - pc < 4) return false; memcpy(&nSize, &pc[0], 4); pc += 4; } - if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) + if (scriptEnd - pc < 0 || (unsigned int)(scriptEnd - pc) < nSize) return false; if (pvchRet) pvchRet->assign(pc, pc + nSize); @@ -676,6 +676,7 @@ bool IsStandard(const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); +bool ExtractDestinationAndMine(const CKeyStore &keystore, const CScript& scriptPubKey, CTxDestination& addressRet, bool *outMine); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); diff --git a/src/wallet.cpp b/src/wallet.cpp index 1984e36c6..d30e6e720 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -570,6 +570,17 @@ int64 CWallet::GetDebit(const CTxIn &txin) const return 0; } +bool CWallet::HasAddress(const CTxDestination &txDest) const +{ + bool hasAddress = false; + + LOCK(cs_wallet); + if (mapAddressBook.count(txDest)) + hasAddress = true; + + return hasAddress; +} + bool CWallet::IsChange(const CTxOut& txout) const { CTxDestination address; @@ -638,6 +649,8 @@ int CWalletTx::GetRequestCount() const void CWalletTx::GetAmounts(list >& listReceived, list >& listSent, int64& nFee, string& strSentAccount) const { + CTxDestination address; + nFee = 0; listReceived.clear(); listSent.clear(); @@ -651,28 +664,43 @@ void CWalletTx::GetAmounts(list >& listReceived, nFee = nDebit - nValueOut; } + if (!fMineCached) + vfMine.resize(vout.size()); + // Sent/received. - BOOST_FOREACH(const CTxOut& txout, vout) - { - CTxDestination address; - vector vchPubKey; - if (!ExtractDestination(txout.scriptPubKey, address)) - { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); + for (unsigned int i = 0; i < vout.size(); i++) { + const CTxOut &txout = vout[i]; + bool isMine = false; + bool warnUnkownTX = false; + + if (!fMineCached) { + address = CNoDestination(); + warnUnkownTX = !ExtractDestinationAndMine(*pwallet, txout.scriptPubKey, address, &isMine); + vfMine[i] = isMine; + } + else { + if (vfMine[i]) { + isMine = true; // already know this is ours, just fetch address + address = CNoDestination(); + warnUnkownTX = !ExtractDestination(txout.scriptPubKey, address); + } + } + if (warnUnkownTX) { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); } - // Don't report 'change' txouts - if (nDebit > 0 && pwallet->IsChange(txout)) - continue; + if (nDebit > 0) { + if (isMine && !pwallet->HasAddress(address)) // Don't report 'change' txouts + continue; - if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); + } - if (pwallet->IsMine(txout)) + if (isMine) listReceived.push_back(make_pair(address, txout.nValue)); } + fMineCached = true; } void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived, diff --git a/src/wallet.h b/src/wallet.h index 551f81568..6fdbdf82f 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -213,6 +213,7 @@ public: throw std::runtime_error("CWallet::GetCredit() : value out of range"); return (IsMine(txout) ? txout.nValue : 0); } + bool HasAddress(const CTxDestination &txDest) const; bool IsChange(const CTxOut& txout) const; int64 GetChange(const CTxOut& txout) const { @@ -383,6 +384,8 @@ public: int64 nOrderPos; // position in ordered transaction list // memory only + mutable std::vector vfMine; // which outputs are mine + mutable bool fMineCached; mutable bool fDebitCached; mutable bool fCreditCached; mutable bool fImmatureCreditCached; @@ -426,6 +429,8 @@ public: fFromMe = false; strFromAccount.clear(); vfSpent.clear(); + vfMine.clear(); + fMineCached = false; fDebitCached = false; fCreditCached = false; fImmatureCreditCached = false; @@ -519,6 +524,8 @@ public: // make sure balances are recalculated void MarkDirty() { + vfMine.clear(); + fMineCached = false; fCreditCached = false; fAvailableCreditCached = false; fDebitCached = false;