diff options
Diffstat (limited to 'src')
45 files changed, 1346 insertions, 823 deletions
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 01ace1e2b2..c1622cf5d3 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -436,7 +436,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) BOOST_FOREACH(const CTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i))) + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) fComplete = false; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d32d96c56b..1fd3b01681 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -156,11 +156,11 @@ public: vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); - base58Prefixes[PUBKEY_ADDRESS] = boost::assign::list_of(0); - base58Prefixes[SCRIPT_ADDRESS] = boost::assign::list_of(5); - base58Prefixes[SECRET_KEY] = boost::assign::list_of(128); - base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E); - base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4); + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >(); convertSeed6(vFixedSeeds, pnSeed6_main, ARRAYLEN(pnSeed6_main)); @@ -214,11 +214,11 @@ public: vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); - base58Prefixes[PUBKEY_ADDRESS] = boost::assign::list_of(111); - base58Prefixes[SCRIPT_ADDRESS] = boost::assign::list_of(196); - base58Prefixes[SECRET_KEY] = boost::assign::list_of(239); - base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF); - base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94); + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); + base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >(); convertSeed6(vFixedSeeds, pnSeed6_test, ARRAYLEN(pnSeed6_test)); diff --git a/src/checkqueue.h b/src/checkqueue.h index 7ca825c16f..6f6b97e3a7 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -161,7 +161,12 @@ public: { } - friend class CCheckQueueControl<T>; + bool IsIdle() + { + boost::unique_lock<boost::mutex> lock(mutex); + return (nTotal == nIdle && nTodo == 0 && fAllOk == true); + } + }; /** @@ -180,9 +185,8 @@ public: { // passed queue is supposed to be unused, or NULL if (pqueue != NULL) { - assert(pqueue->nTotal == pqueue->nIdle); - assert(pqueue->nTodo == 0); - assert(pqueue->fAllOk == true); + bool isIdle = pqueue->IsIdle(); + assert(isIdle); } } diff --git a/src/crypter.cpp b/src/crypter.cpp index 00f7f7f1bd..75d84dbf13 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -102,7 +102,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM } -bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) +static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); @@ -112,7 +112,7 @@ bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vch return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); } -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); @@ -122,6 +122,19 @@ bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); } +static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) +{ + CKeyingMaterial vchSecret; + if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + + if (vchSecret.size() != 32) + return false; + + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + return key.VerifyPubKey(vchPubKey); +} + bool CCryptoKeyStore::SetCrypted() { LOCK(cs_KeyStore); @@ -161,20 +174,8 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - { - keyFail = true; - break; - } - if (vchSecret.size() != 32) - { - keyFail = true; - break; - } CKey key; - key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - if (key.GetPubKey() != vchPubKey) + if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key)) { keyFail = true; break; @@ -243,13 +244,7 @@ bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - return true; + return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); } } return false; diff --git a/src/crypter.h b/src/crypter.h index 7b4c2f2613..cbaf1562f0 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -107,9 +107,6 @@ public: } }; -bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); -bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); - /** Keystore which keeps the private keys encrypted. * It derives from the basic key store, which is used if no encryption is active. */ diff --git a/src/init.cpp b/src/init.cpp index c2af23139c..c8f32d8bfd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -234,6 +234,26 @@ bool static Bind(const CService &addr, unsigned int flags) { return true; } +void OnRPCStopped() +{ + cvBlockChange.notify_all(); + LogPrint("rpc", "RPC stopped.\n"); +} + +void OnRPCPreCommand(const CRPCCommand& cmd) +{ +#ifdef ENABLE_WALLET + if (cmd.reqWallet && !pwalletMain) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); +#endif + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && + !cmd.okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); +} + std::string HelpMessage(HelpMessageMode mode) { // When adding new options to the categories, please keep and ensure alphabetical ordering. @@ -802,6 +822,8 @@ bool AppInit2(boost::thread_group& threadGroup) if (fServer) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); + RPCServer::OnStopped(&OnRPCStopped); + RPCServer::OnPreCommand(&OnRPCPreCommand); StartRPCThreads(); } diff --git a/src/keystore.cpp b/src/keystore.cpp index 22cd08f30c..7531737e04 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -7,8 +7,6 @@ #include "crypter.h" #include "key.h" -#include "script/script.h" -#include "script/standard.h" #include "util.h" #include <boost/foreach.hpp> diff --git a/src/keystore.h b/src/keystore.h index 6655264d72..4a4b6d20af 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -8,14 +8,13 @@ #include "key.h" #include "pubkey.h" +#include "script/script.h" +#include "script/standard.h" #include "sync.h" #include <boost/signals2/signal.hpp> #include <boost/variant.hpp> -class CScript; -class CScriptID; - /** A virtual base class for key stores */ class CKeyStore { diff --git a/src/main.cpp b/src/main.cpp index f43d8c58b6..43e5a2d024 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -904,7 +904,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee) + bool* pfMissingInputs, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1063,8 +1063,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa dFreeCount += nSize; } - if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: insane fees %s, %d > %d", + if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) + return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); @@ -1422,7 +1422,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; @@ -1764,6 +1764,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; + // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: + if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + flags |= SCRIPT_VERIFY_DERSIG; + } + CBlockUndo blockundo; CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2601,6 +2606,13 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta REJECT_OBSOLETE, "bad-version"); } + // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, Params().RejectBlockOutdatedMajority())) + { + return state.Invalid(error("%s : rejected nVersion=2 block", __func__), + REJECT_OBSOLETE, "bad-version"); + } + return true; } @@ -3438,7 +3450,7 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); - LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id); + LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -4273,7 +4285,7 @@ bool ProcessMessages(CNode* pfrom) // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", msg.hdr.GetCommand(), pfrom->id); + LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; } @@ -4282,7 +4294,7 @@ bool ProcessMessages(CNode* pfrom) CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { - LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", hdr.GetCommand(), pfrom->id); + LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; } string strCommand = hdr.GetCommand(); @@ -4298,7 +4310,7 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand, nMessageSize, nChecksum, hdr.nChecksum); + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4315,12 +4327,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", strCommand, nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), nMessageSize, e.what()); } else { @@ -4337,7 +4349,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id); + LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); break; } @@ -4534,12 +4546,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } - // In case there is a block that has been in flight from this peer for (1 + 0.5 * N) times the block interval + // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (2 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); pto->fDisconnect = true; } diff --git a/src/main.h b/src/main.h index a7360d2f70..936cd43e99 100644 --- a/src/main.h +++ b/src/main.h @@ -205,7 +205,7 @@ void FlushStateToDisk(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee=false); + bool* pfMissingInputs, bool fRejectAbsurdFee=false); struct CNodeStateStats { diff --git a/src/net.cpp b/src/net.cpp index e4ab9d706d..3c3666615e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1971,7 +1971,7 @@ void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSen ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); ssSend << CMessageHeader(pszCommand, 0); - LogPrint("net", "sending: %s ", pszCommand); + LogPrint("net", "sending: %s ", SanitizeString(pszCommand)); } void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) diff --git a/src/primitives/block.h b/src/primitives/block.h index d77ab162e0..c7ed6f723a 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -24,7 +24,7 @@ class CBlockHeader { public: // header - static const int32_t CURRENT_VERSION=2; + static const int32_t CURRENT_VERSION=3; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 4c00aca985..d31a1e018b 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -278,7 +278,6 @@ void BitcoinAmountField::setValue(const CAmount& value) void BitcoinAmountField::setReadOnly(bool fReadOnly) { amount->setReadOnly(fReadOnly); - unit->setEnabled(!fReadOnly); } void BitcoinAmountField::unitChanged(int idx) diff --git a/src/qt/forms/helpmessagedialog.ui b/src/qt/forms/helpmessagedialog.ui index 37008f047e..dc7df9d6c8 100644 --- a/src/qt/forms/helpmessagedialog.ui +++ b/src/qt/forms/helpmessagedialog.ui @@ -14,6 +14,69 @@ <string notr="true">Bitcoin Core - Command-line options</string> </property> <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>12</number> + </property> + <property name="topMargin"> + <number>12</number> + </property> + <property name="rightMargin"> + <number>12</number> + </property> + <property name="bottomMargin"> + <number>12</number> + </property> + <item> + <layout class="QVBoxLayout" name="verticalLayoutLogo" stretch="0,0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>4</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="aboutLogo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Ignored"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>100</height> + </size> + </property> + <property name="pixmap"> + <pixmap resource="../bitcoin.qrc">:/icons/bitcoin</pixmap> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + </layout> + </item> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> @@ -25,6 +88,9 @@ </item> <item> <widget class="QScrollArea" name="scrollArea"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> <property name="verticalScrollBarPolicy"> <enum>Qt::ScrollBarAlwaysOn</enum> </property> @@ -57,6 +123,22 @@ </widget> </item> <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>4</width> + <height>4</height> + </size> + </property> + </spacer> + </item> + <item> <widget class="QDialogButtonBox" name="okButton"> <property name="orientation"> <enum>Qt::Horizontal</enum> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9e8743d805..5aef2d7539 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -526,8 +526,8 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn msgParams.first = tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); msgParams.second = CClientUIInterface::MSG_ERROR; break; - case WalletModel::InsaneFee: - msgParams.first = tr("A fee higher than %1 is considered an insanely high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), 10000000)); + case WalletModel::AbsurdFee: + msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), 10000000)); break; case WalletModel::PaymentRequestExpired: msgParams.first = tr("Payment request expired!"); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 4ef42b927e..63dd6efb52 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -137,6 +137,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : ui->helpMessage->moveCursor(QTextCursor::Start); ui->scrollArea->setVisible(false); + ui->aboutLogo->setVisible(false); } } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 4a7abf999c..79f5191fc0 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -279,9 +279,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return TransactionCreationFailed; } - // reject insane fee > 0.1 bitcoin + // reject absurdly high fee > 0.1 bitcoin if (nFeeRequired > 10000000) - return InsaneFee; + return AbsurdFee; } return SendCoinsReturn(OK); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9916d11f93..4a9a12beaa 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -111,7 +111,7 @@ public: DuplicateAddress, TransactionCreationFailed, // Error returned when wallet is still locked TransactionCommitFailed, - InsaneFee, + AbsurdFee, PaymentRequestExpired }; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index cfc559d198..293d6d5619 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -105,6 +105,7 @@ Value getblockcount(const Array& params, bool fHelp) + HelpExampleRpc("getblockcount", "") ); + LOCK(cs_main); return chainActive.Height(); } @@ -121,6 +122,7 @@ Value getbestblockhash(const Array& params, bool fHelp) + HelpExampleRpc("getbestblockhash", "") ); + LOCK(cs_main); return chainActive.Tip()->GetBlockHash().GetHex(); } @@ -137,6 +139,7 @@ Value getdifficulty(const Array& params, bool fHelp) + HelpExampleRpc("getdifficulty", "") ); + LOCK(cs_main); return GetDifficulty(); } @@ -173,6 +176,8 @@ Value getrawmempool(const Array& params, bool fHelp) + HelpExampleRpc("getrawmempool", "true") ); + LOCK(cs_main); + bool fVerbose = false; if (params.size() > 0) fVerbose = params[0].get_bool(); @@ -233,6 +238,8 @@ Value getblockhash(const Array& params, bool fHelp) + HelpExampleRpc("getblockhash", "1000") ); + LOCK(cs_main); + int nHeight = params[0].get_int(); if (nHeight < 0 || nHeight > chainActive.Height()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -277,6 +284,8 @@ Value getblock(const Array& params, bool fHelp) + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") ); + LOCK(cs_main); + std::string strHash = params[0].get_str(); uint256 hash(uint256S(strHash)); @@ -326,6 +335,8 @@ Value gettxoutsetinfo(const Array& params, bool fHelp) + HelpExampleRpc("gettxoutsetinfo", "") ); + LOCK(cs_main); + Object ret; CCoinsStats stats; @@ -380,6 +391,8 @@ Value gettxout(const Array& params, bool fHelp) + HelpExampleRpc("gettxout", "\"txid\", 1") ); + LOCK(cs_main); + Object ret; std::string strHash = params[0].get_str(); @@ -436,6 +449,8 @@ Value verifychain(const Array& params, bool fHelp) + HelpExampleRpc("verifychain", "") ); + LOCK(cs_main); + int nCheckLevel = GetArg("-checklevel", 3); int nCheckDepth = GetArg("-checkblocks", 288); if (params.size() > 0) @@ -467,6 +482,8 @@ Value getblockchaininfo(const Array& params, bool fHelp) + HelpExampleRpc("getblockchaininfo", "") ); + LOCK(cs_main); + Object obj; obj.push_back(Pair("chain", Params().NetworkIDString())); obj.push_back(Pair("blocks", (int)chainActive.Height())); @@ -526,6 +543,8 @@ Value getchaintips(const Array& params, bool fHelp) + HelpExampleRpc("getchaintips", "") ); + LOCK(cs_main); + /* Build up a list of chain tips. We start with the list of all known blocks, and successively remove blocks that appear as pprev of another block. */ diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 8666779cc1..b9c92a06c5 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -91,6 +91,8 @@ Value importprivkey(const Array& params, bool fHelp) + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); string strSecret = params[0].get_str(); @@ -158,6 +160,8 @@ Value importaddress(const Array& params, bool fHelp) + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + CScript script; CBitcoinAddress address(params[0].get_str()); @@ -223,6 +227,8 @@ Value importwallet(const Array& params, bool fHelp) + HelpExampleRpc("importwallet", "\"test\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); ifstream file; @@ -322,6 +328,8 @@ Value dumpprivkey(const Array& params, bool fHelp) + HelpExampleRpc("dumpprivkey", "\"myaddress\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); string strAddress = params[0].get_str(); @@ -351,6 +359,8 @@ Value dumpwallet(const Array& params, bool fHelp) + HelpExampleRpc("dumpwallet", "\"test\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); ofstream file; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index f1c4016575..e3ae5cff42 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -88,6 +88,7 @@ Value getnetworkhashps(const Array& params, bool fHelp) + HelpExampleRpc("getnetworkhashps", "") ); + LOCK(cs_main); return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); } @@ -107,6 +108,7 @@ Value getgenerate(const Array& params, bool fHelp) + HelpExampleRpc("getgenerate", "") ); + LOCK(cs_main); return GetBoolArg("-gen", false); } @@ -200,7 +202,6 @@ Value setgenerate(const Array& params, bool fHelp) return Value::null; } - #endif @@ -228,6 +229,9 @@ Value getmininginfo(const Array& params, bool fHelp) + HelpExampleRpc("getmininginfo", "") ); + + LOCK(cs_main); + Object obj; obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); @@ -268,8 +272,9 @@ Value prioritisetransaction(const Array& params, bool fHelp) + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") ); - uint256 hash = ParseHashStr(params[0].get_str(), "txid"); + LOCK(cs_main); + uint256 hash = ParseHashStr(params[0].get_str(), "txid"); CAmount nAmount = params[2].get_int64(); mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); @@ -358,6 +363,8 @@ Value getblocktemplate(const Array& params, bool fHelp) + HelpExampleRpc("getblocktemplate", "") ); + LOCK(cs_main); + std::string strMode = "template"; Value lpval = Value::null; if (params.size() > 0) @@ -439,10 +446,6 @@ Value getblocktemplate(const Array& params, bool fHelp) } // Release the wallet and main lock while waiting -#ifdef ENABLE_WALLET - if(pwalletMain) - LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet); -#endif LEAVE_CRITICAL_SECTION(cs_main); { checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); @@ -460,10 +463,6 @@ Value getblocktemplate(const Array& params, bool fHelp) } } ENTER_CRITICAL_SECTION(cs_main); -#ifdef ENABLE_WALLET - if(pwalletMain) - ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet); -#endif if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 3d647a0d2d..8d260b1cc9 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -69,6 +69,12 @@ Value getinfo(const Array& params, bool fHelp) + HelpExampleRpc("getinfo", "") ); +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + proxyType proxy; GetProxy(NET_IPV4, proxy); @@ -172,6 +178,12 @@ Value validateaddress(const Array& params, bool fHelp) + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") ); +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + CBitcoinAddress address(params[0].get_str()); bool isValid = address.IsValid(); @@ -329,6 +341,8 @@ Value verifymessage(const Array& params, bool fHelp) + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") ); + LOCK(cs_main); + string strAddress = params[0].get_str(); string strSign = params[1].get_str(); string strMessage = params[2].get_str(); @@ -372,6 +386,8 @@ Value setmocktime(const Array& params, bool fHelp) if (!Params().MineBlocksOnDemand()) throw runtime_error("setmocktime for regression testing (-regtest mode) only"); + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(int_type)); SetMockTime(params[0].get_int64()); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index f0fadb5987..6306fd4406 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -34,7 +34,8 @@ Value getconnectioncount(const Array& params, bool fHelp) + HelpExampleRpc("getconnectioncount", "") ); - LOCK(cs_vNodes); + LOCK2(cs_main, cs_vNodes); + return (int)vNodes.size(); } @@ -52,7 +53,8 @@ Value ping(const Array& params, bool fHelp) ); // Request that each node send a ping during next message processing pass - LOCK(cs_vNodes); + LOCK2(cs_main, cs_vNodes); + BOOST_FOREACH(CNode* pNode, vNodes) { pNode->fPingQueued = true; } @@ -113,6 +115,8 @@ Value getpeerinfo(const Array& params, bool fHelp) + HelpExampleRpc("getpeerinfo", "") ); + LOCK(cs_main); + vector<CNodeStats> vstats; CopyNodeStats(vstats); @@ -411,6 +415,8 @@ Value getnetworkinfo(const Array& params, bool fHelp) + HelpExampleRpc("getnetworkinfo", "") ); + LOCK(cs_main); + Object obj; obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("subversion", diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 2b108bf588..4a079f5c81 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -169,6 +169,8 @@ Value getrawtransaction(const Array& params, bool fHelp) + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") ); + LOCK(cs_main); + uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; @@ -256,6 +258,7 @@ Value listunspent(const Array& params, bool fHelp) Array results; vector<COutput> vecOutputs; assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false); BOOST_FOREACH(const COutput& out, vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) @@ -334,6 +337,7 @@ Value createrawtransaction(const Array& params, bool fHelp) + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") ); + LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type)); Array inputs = params[0].get_array(); @@ -428,6 +432,7 @@ Value decoderawtransaction(const Array& params, bool fHelp) + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") ); + LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(str_type)); CTransaction tx; @@ -466,6 +471,7 @@ Value decodescript(const Array& params, bool fHelp) + HelpExampleRpc("decodescript", "\"hexstring\"") ); + LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(str_type)); Object r; @@ -532,6 +538,11 @@ Value signrawtransaction(const Array& params, bool fHelp) + HelpExampleRpc("signrawtransaction", "\"myhex\"") ); +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif RPCTypeCheck(params, boost::assign::list_of(str_type)(array_type)(array_type)(str_type), true); vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); @@ -591,7 +602,7 @@ Value signrawtransaction(const Array& params, bool fHelp) } } #ifdef ENABLE_WALLET - else + else if (pwalletMain) EnsureWalletIsUnlocked(); #endif @@ -688,7 +699,7 @@ Value signrawtransaction(const Array& params, bool fHelp) BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i))) + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) fComplete = false; } @@ -722,6 +733,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); + LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(str_type)(bool_type)); // parse hex string from parameter diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index e4f23d56d2..20e9252d7e 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -7,9 +7,11 @@ #include "base58.h" #include "init.h" -#include "main.h" +#include "random.h" +#include "sync.h" #include "ui_interface.h" #include "util.h" +#include "utilstrencodings.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif @@ -23,11 +25,13 @@ #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> #include <boost/shared_ptr.hpp> +#include <boost/signals2/signal.hpp> #include <boost/thread.hpp> #include "json/json_spirit_writer_template.h" using namespace boost::asio; using namespace json_spirit; +using namespace RPCServer; using namespace std; static std::string strRPCUserColonPass; @@ -46,6 +50,34 @@ static boost::asio::io_service::work *rpc_dummy_work = NULL; static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors; +static struct CRPCSignals +{ + boost::signals2::signal<void ()> Started; + boost::signals2::signal<void ()> Stopped; + boost::signals2::signal<void (const CRPCCommand&)> PreCommand; + boost::signals2::signal<void (const CRPCCommand&)> PostCommand; +} g_rpcSignals; + +void RPCServer::OnStarted(boost::function<void ()> slot) +{ + g_rpcSignals.Started.connect(slot); +} + +void RPCServer::OnStopped(boost::function<void ()> slot) +{ + g_rpcSignals.Stopped.connect(slot); +} + +void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot) +{ + g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); +} + +void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot) +{ + g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); +} + void RPCTypeCheck(const Array& params, const list<Value_type>& typesExpected, bool fAllowNull) @@ -239,112 +271,110 @@ Value stop(const Array& params, bool fHelp) * Call Table */ static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafeMode threadSafe reqWallet - // --------------------- ------------------------ ----------------------- ---------- ---------- --------- +{ // category name actor (function) okSafeMode reqWallet + // --------------------- ------------------------ ----------------------- ---------- --------- /* Overall control/query calls */ - { "control", "getinfo", &getinfo, true, false, false }, /* uses wallet if enabled */ - { "control", "help", &help, true, true, false }, - { "control", "stop", &stop, true, true, false }, + { "control", "getinfo", &getinfo, true, false }, /* uses wallet if enabled */ + { "control", "help", &help, true, false }, + { "control", "stop", &stop, true, false }, /* P2P networking */ - { "network", "getnetworkinfo", &getnetworkinfo, true, false, false }, - { "network", "addnode", &addnode, true, true, false }, - { "network", "getaddednodeinfo", &getaddednodeinfo, true, true, false }, - { "network", "getconnectioncount", &getconnectioncount, true, false, false }, - { "network", "getnettotals", &getnettotals, true, true, false }, - { "network", "getpeerinfo", &getpeerinfo, true, false, false }, - { "network", "ping", &ping, true, false, false }, + { "network", "getnetworkinfo", &getnetworkinfo, true, false }, + { "network", "addnode", &addnode, true, false }, + { "network", "getaddednodeinfo", &getaddednodeinfo, true, false }, + { "network", "getconnectioncount", &getconnectioncount, true, false }, + { "network", "getnettotals", &getnettotals, true, false }, + { "network", "getpeerinfo", &getpeerinfo, true, false }, + { "network", "ping", &ping, true, false }, /* Block chain and UTXO */ - { "blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false }, - { "blockchain", "getbestblockhash", &getbestblockhash, true, false, false }, - { "blockchain", "getblockcount", &getblockcount, true, false, false }, - { "blockchain", "getblock", &getblock, true, false, false }, - { "blockchain", "getblockhash", &getblockhash, true, false, false }, - { "blockchain", "getchaintips", &getchaintips, true, false, false }, - { "blockchain", "getdifficulty", &getdifficulty, true, false, false }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true, true, false }, - { "blockchain", "getrawmempool", &getrawmempool, true, false, false }, - { "blockchain", "gettxout", &gettxout, true, false, false }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, - { "blockchain", "verifychain", &verifychain, true, false, false }, - { "blockchain", "invalidateblock", &invalidateblock, true, true, false }, - { "blockchain", "reconsiderblock", &reconsiderblock, true, true, false }, + { "blockchain", "getblockchaininfo", &getblockchaininfo, true, false }, + { "blockchain", "getbestblockhash", &getbestblockhash, true, false }, + { "blockchain", "getblockcount", &getblockcount, true, false }, + { "blockchain", "getblock", &getblock, true, false }, + { "blockchain", "getblockhash", &getblockhash, true, false }, + { "blockchain", "getchaintips", &getchaintips, true, false }, + { "blockchain", "getdifficulty", &getdifficulty, true, false }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, true, false }, + { "blockchain", "getrawmempool", &getrawmempool, true, false }, + { "blockchain", "gettxout", &gettxout, true, false }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false }, + { "blockchain", "verifychain", &verifychain, true, false }, /* Mining */ - { "mining", "getblocktemplate", &getblocktemplate, true, false, false }, - { "mining", "getmininginfo", &getmininginfo, true, false, false }, - { "mining", "getnetworkhashps", &getnetworkhashps, true, false, false }, - { "mining", "prioritisetransaction", &prioritisetransaction, true, false, false }, - { "mining", "submitblock", &submitblock, true, true, false }, + { "mining", "getblocktemplate", &getblocktemplate, true, false }, + { "mining", "getmininginfo", &getmininginfo, true, false }, + { "mining", "getnetworkhashps", &getnetworkhashps, true, false }, + { "mining", "prioritisetransaction", &prioritisetransaction, true, false }, + { "mining", "submitblock", &submitblock, true, false }, #ifdef ENABLE_WALLET /* Coin generation */ - { "generating", "getgenerate", &getgenerate, true, false, false }, - { "generating", "setgenerate", &setgenerate, true, true, false }, + { "generating", "getgenerate", &getgenerate, true, false }, + { "generating", "setgenerate", &setgenerate, true, false }, #endif /* Raw transactions */ - { "rawtransactions", "createrawtransaction", &createrawtransaction, true, false, false }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false, false }, - { "rawtransactions", "decodescript", &decodescript, true, false, false }, - { "rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ + { "rawtransactions", "createrawtransaction", &createrawtransaction, true, false }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false }, + { "rawtransactions", "decodescript", &decodescript, true, false }, + { "rawtransactions", "getrawtransaction", &getrawtransaction, true, false }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, false, false }, /* uses wallet if enabled */ /* Utility functions */ - { "util", "createmultisig", &createmultisig, true, true , false }, - { "util", "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ - { "util", "verifymessage", &verifymessage, true, false, false }, - { "util", "estimatefee", &estimatefee, true, true, false }, - { "util", "estimatepriority", &estimatepriority, true, true, false }, + { "util", "createmultisig", &createmultisig, true, false }, + { "util", "validateaddress", &validateaddress, true, false }, /* uses wallet if enabled */ + { "util", "verifymessage", &verifymessage, true, false }, + { "util", "estimatefee", &estimatefee, true, false }, + { "util", "estimatepriority", &estimatepriority, true, false }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true, true, false }, - { "hidden", "reconsiderblock", &reconsiderblock, true, true, false }, - { "hidden", "setmocktime", &setmocktime, true, false, false }, + { "hidden", "invalidateblock", &invalidateblock, true, false }, + { "hidden", "reconsiderblock", &reconsiderblock, true, false }, + { "hidden", "setmocktime", &setmocktime, true, false }, #ifdef ENABLE_WALLET /* Wallet */ - { "wallet", "addmultisigaddress", &addmultisigaddress, true, false, true }, - { "wallet", "backupwallet", &backupwallet, true, false, true }, - { "wallet", "dumpprivkey", &dumpprivkey, true, false, true }, - { "wallet", "dumpwallet", &dumpwallet, true, false, true }, - { "wallet", "encryptwallet", &encryptwallet, true, false, true }, - { "wallet", "getaccountaddress", &getaccountaddress, true, false, true }, - { "wallet", "getaccount", &getaccount, true, false, true }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, - { "wallet", "getbalance", &getbalance, false, false, true }, - { "wallet", "getnewaddress", &getnewaddress, true, false, true }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, false, true }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, - { "wallet", "gettransaction", &gettransaction, false, false, true }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, - { "wallet", "getwalletinfo", &getwalletinfo, false, false, true }, - { "wallet", "importprivkey", &importprivkey, true, false, true }, - { "wallet", "importwallet", &importwallet, true, false, true }, - { "wallet", "importaddress", &importaddress, true, false, true }, - { "wallet", "keypoolrefill", &keypoolrefill, true, false, true }, - { "wallet", "listaccounts", &listaccounts, false, false, true }, - { "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true }, - { "wallet", "listlockunspent", &listlockunspent, false, false, true }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, - { "wallet", "listsinceblock", &listsinceblock, false, false, true }, - { "wallet", "listtransactions", &listtransactions, false, false, true }, - { "wallet", "listunspent", &listunspent, false, false, true }, - { "wallet", "lockunspent", &lockunspent, true, false, true }, - { "wallet", "move", &movecmd, false, false, true }, - { "wallet", "sendfrom", &sendfrom, false, false, true }, - { "wallet", "sendmany", &sendmany, false, false, true }, - { "wallet", "sendtoaddress", &sendtoaddress, false, false, true }, - { "wallet", "setaccount", &setaccount, true, false, true }, - { "wallet", "settxfee", &settxfee, true, false, true }, - { "wallet", "signmessage", &signmessage, true, false, true }, - { "wallet", "walletlock", &walletlock, true, false, true }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, false, true }, - { "wallet", "walletpassphrase", &walletpassphrase, true, false, true }, + { "wallet", "addmultisigaddress", &addmultisigaddress, true, true }, + { "wallet", "backupwallet", &backupwallet, true, true }, + { "wallet", "dumpprivkey", &dumpprivkey, true, true }, + { "wallet", "dumpwallet", &dumpwallet, true, true }, + { "wallet", "encryptwallet", &encryptwallet, true, true }, + { "wallet", "getaccountaddress", &getaccountaddress, true, true }, + { "wallet", "getaccount", &getaccount, true, true }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, true }, + { "wallet", "getbalance", &getbalance, false, true }, + { "wallet", "getnewaddress", &getnewaddress, true, true }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, true }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, true }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, true }, + { "wallet", "gettransaction", &gettransaction, false, true }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, true }, + { "wallet", "getwalletinfo", &getwalletinfo, false, true }, + { "wallet", "importprivkey", &importprivkey, true, true }, + { "wallet", "importwallet", &importwallet, true, true }, + { "wallet", "importaddress", &importaddress, true, true }, + { "wallet", "keypoolrefill", &keypoolrefill, true, true }, + { "wallet", "listaccounts", &listaccounts, false, true }, + { "wallet", "listaddressgroupings", &listaddressgroupings, false, true }, + { "wallet", "listlockunspent", &listlockunspent, false, true }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, true }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, true }, + { "wallet", "listsinceblock", &listsinceblock, false, true }, + { "wallet", "listtransactions", &listtransactions, false, true }, + { "wallet", "listunspent", &listunspent, false, true }, + { "wallet", "lockunspent", &lockunspent, true, true }, + { "wallet", "move", &movecmd, false, true }, + { "wallet", "sendfrom", &sendfrom, false, true }, + { "wallet", "sendmany", &sendmany, false, true }, + { "wallet", "sendtoaddress", &sendtoaddress, false, true }, + { "wallet", "setaccount", &setaccount, true, true }, + { "wallet", "settxfee", &settxfee, true, true }, + { "wallet", "signmessage", &signmessage, true, true }, + { "wallet", "walletlock", &walletlock, true, true }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, true }, + { "wallet", "walletpassphrase", &walletpassphrase, true, true }, #endif // ENABLE_WALLET }; @@ -693,6 +723,7 @@ void StartRPCThreads() for (int i = 0; i < GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); fRPCRunning = true; + g_rpcSignals.Started(); } void StartDummyRPCThread() @@ -735,7 +766,7 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); - cvBlockChange.notify_all(); + g_rpcSignals.Stopped(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); delete rpc_dummy_work; rpc_dummy_work = NULL; @@ -818,7 +849,7 @@ void JSONRequest::parse(const Value& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getblocktemplate") - LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod); + LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params Value valParams = find_value(request, "params"); @@ -978,45 +1009,20 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); -#ifdef ENABLE_WALLET - if (pcmd->reqWallet && !pwalletMain) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); -#endif - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && - !pcmd->okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + g_rpcSignals.PreCommand(*pcmd); try { // Execute - Value result; - { - if (pcmd->threadSafe) - result = pcmd->actor(params, false); -#ifdef ENABLE_WALLET - else if (!pwalletMain) { - LOCK(cs_main); - result = pcmd->actor(params, false); - } else { - LOCK2(cs_main, pwalletMain->cs_wallet); - result = pcmd->actor(params, false); - } -#else // ENABLE_WALLET - else { - LOCK(cs_main); - result = pcmd->actor(params, false); - } -#endif // !ENABLE_WALLET - } - return result; + return pcmd->actor(params, false); } catch (const std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } + + g_rpcSignals.PostCommand(*pcmd); } std::string HelpExampleCli(string methodname, string args){ diff --git a/src/rpcserver.h b/src/rpcserver.h index 1b94b758f2..f63438ecb8 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -19,6 +19,16 @@ #include "json/json_spirit_utils.h" #include "json/json_spirit_writer_template.h" +class CRPCCommand; + +namespace RPCServer +{ + void OnStarted(boost::function<void ()> slot); + void OnStopped(boost::function<void ()> slot); + void OnPreCommand(boost::function<void (const CRPCCommand&)> slot); + void OnPostCommand(boost::function<void (const CRPCCommand&)> slot); +} + class CBlockIndex; class CNetAddr; @@ -88,7 +98,6 @@ public: std::string name; rpcfn_type actor; bool okSafeMode; - bool threadSafe; bool reqWallet; }; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 28371771a9..1afc3c910e 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -90,6 +90,8 @@ Value getnewaddress(const Array& params, bool fHelp) + HelpExampleRpc("getnewaddress", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // Parse the account first so we don't generate a key if there's an error string strAccount; if (params.size() > 0) @@ -164,13 +166,14 @@ Value getaccountaddress(const Array& params, bool fHelp) + HelpExampleRpc("getaccountaddress", "\"myaccount\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // Parse the account first so we don't generate a key if there's an error string strAccount = AccountFromValue(params[0]); Value ret; ret = GetAccountAddress(strAccount).ToString(); - return ret; } @@ -189,6 +192,8 @@ Value getrawchangeaddress(const Array& params, bool fHelp) + HelpExampleRpc("getrawchangeaddress", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); @@ -219,11 +224,12 @@ Value setaccount(const Array& params, bool fHelp) + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - string strAccount; if (params.size() > 1) strAccount = AccountFromValue(params[1]); @@ -262,6 +268,8 @@ Value getaccount(const Array& params, bool fHelp) + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); @@ -292,6 +300,8 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strAccount = AccountFromValue(params[0]); // Find all addresses that have the given account @@ -363,6 +373,8 @@ Value sendtoaddress(const Array& params, bool fHelp) + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); @@ -409,6 +421,8 @@ Value listaddressgroupings(const Array& params, bool fHelp) + HelpExampleRpc("listaddressgroupings", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + Array jsonGroupings; map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) @@ -454,6 +468,8 @@ Value signmessage(const Array& params, bool fHelp) + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); string strAddress = params[0].get_str(); @@ -504,6 +520,8 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // Bitcoin address CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); if (!address.IsValid()) @@ -557,6 +575,8 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // Minimum confirmations int nMinDepth = 1; if (params.size() > 1) @@ -643,6 +663,8 @@ Value getbalance(const Array& params, bool fHelp) + HelpExampleRpc("getbalance", "\"*\", 6") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (params.size() == 0) return ValueFromAmount(pwalletMain->GetBalance()); @@ -695,6 +717,9 @@ Value getunconfirmedbalance(const Array ¶ms, bool fHelp) throw runtime_error( "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); + + LOCK2(cs_main, pwalletMain->cs_wallet); + return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); } @@ -721,6 +746,8 @@ Value movecmd(const Array& params, bool fHelp) + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); CAmount nAmount = AmountFromValue(params[2]); @@ -793,6 +820,8 @@ Value sendfrom(const Array& params, bool fHelp) + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strAccount = AccountFromValue(params[0]); CBitcoinAddress address(params[1].get_str()); if (!address.IsValid()) @@ -850,6 +879,8 @@ Value sendmany(const Array& params, bool fHelp) + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strAccount = AccountFromValue(params[0]); Object sendTo = params[1].get_obj(); int nMinDepth = 1; @@ -935,6 +966,8 @@ Value addmultisigaddress(const Array& params, bool fHelp) throw runtime_error(msg); } + LOCK2(cs_main, pwalletMain->cs_wallet); + string strAccount; if (params.size() > 2) strAccount = AccountFromValue(params[2]); @@ -1110,6 +1143,8 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) + HelpExampleRpc("listreceivedbyaddress", "6, true, true") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + return ListReceived(params, false); } @@ -1141,6 +1176,8 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) + HelpExampleRpc("listreceivedbyaccount", "6, true, true") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + return ListReceived(params, true); } @@ -1292,6 +1329,8 @@ Value listtransactions(const Array& params, bool fHelp) + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strAccount = "*"; if (params.size() > 0) strAccount = params[0].get_str(); @@ -1372,6 +1411,8 @@ Value listaccounts(const Array& params, bool fHelp) + HelpExampleRpc("listaccounts", "6") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + int nMinDepth = 1; if (params.size() > 0) nMinDepth = params[0].get_int(); @@ -1460,6 +1501,8 @@ Value listsinceblock(const Array& params, bool fHelp) + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + CBlockIndex *pindex = NULL; int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -1546,6 +1589,8 @@ Value gettransaction(const Array& params, bool fHelp) + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + uint256 hash; hash.SetHex(params[0].get_str()); @@ -1594,6 +1639,8 @@ Value backupwallet(const Array& params, bool fHelp) + HelpExampleRpc("backupwallet", "\"backup.dat\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + string strDest = params[0].get_str(); if (!BackupWallet(*pwalletMain, strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); @@ -1616,6 +1663,8 @@ Value keypoolrefill(const Array& params, bool fHelp) + HelpExampleRpc("keypoolrefill", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool unsigned int kpSize = 0; if (params.size() > 0) { @@ -1663,6 +1712,8 @@ Value walletpassphrase(const Array& params, bool fHelp) + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (fHelp) return true; if (!pwalletMain->IsCrypted()) @@ -1710,6 +1761,8 @@ Value walletpassphrasechange(const Array& params, bool fHelp) + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (fHelp) return true; if (!pwalletMain->IsCrypted()) @@ -1756,6 +1809,8 @@ Value walletlock(const Array& params, bool fHelp) + HelpExampleRpc("walletlock", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (fHelp) return true; if (!pwalletMain->IsCrypted()) @@ -1797,6 +1852,8 @@ Value encryptwallet(const Array& params, bool fHelp) + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (fHelp) return true; if (pwalletMain->IsCrypted()) @@ -1861,6 +1918,8 @@ Value lockunspent(const Array& params, bool fHelp) + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + if (params.size() == 1) RPCTypeCheck(params, boost::assign::list_of(bool_type)); else @@ -1930,6 +1989,8 @@ Value listlockunspent(const Array& params, bool fHelp) + HelpExampleRpc("listlockunspent", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + vector<COutPoint> vOutpts; pwalletMain->ListLockedCoins(vOutpts); @@ -1961,6 +2022,8 @@ Value settxfee(const Array& params, bool fHelp) + HelpExampleRpc("settxfee", "0.00001") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + // Amount CAmount nAmount = 0; if (params[0].get_real() != 0.0) @@ -1992,6 +2055,8 @@ Value getwalletinfo(const Array& params, bool fHelp) + HelpExampleRpc("getwalletinfo", "") ); + LOCK2(cs_main, pwalletMain->cs_wallet); + Object obj; obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index c8dd54a747..b0d5faaf77 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -78,7 +78,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, SignatureChecker(tx, nIn), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 9d9c266435..0320577797 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -46,6 +46,7 @@ enum { bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance }; /// Returns 1 if the input nIn of the serialized transaction pointed to by diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0cee1a0116..84a7432fdb 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -93,76 +93,76 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { * in which case a single 0 byte is necessary and even required). * * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + * + * This function is consensus-critical since BIP66. */ -bool static IsDERSignature(const valtype &vchSig) { +bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) { + // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] + // * total-length: 1-byte length descriptor of everything that follows, + // excluding the sighash byte. + // * R-length: 1-byte length descriptor of the R value that follows. + // * R: arbitrary-length big-endian encoded R value. It must use the shortest + // possible encoding for a positive integers (which means no null bytes at + // the start, except a single one when the next byte has its highest bit set). + // * S-length: 1-byte length descriptor of the S value that follows. + // * S: arbitrary-length big-endian encoded S value. The same rules apply. + // * sighash: 1-byte value indicating what data is hashed (not part of the DER + // signature) - if (vchSig.size() < 9) { - // Non-canonical signature: too short - return false; - } - if (vchSig.size() > 73) { - // Non-canonical signature: too long - return false; - } - if (vchSig[0] != 0x30) { - // Non-canonical signature: wrong type - return false; - } - if (vchSig[1] != vchSig.size()-3) { - // Non-canonical signature: wrong length marker - return false; - } - unsigned int nLenR = vchSig[3]; - if (5 + nLenR >= vchSig.size()) { - // Non-canonical signature: S length misplaced - return false; - } - unsigned int nLenS = vchSig[5+nLenR]; - if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) { - // Non-canonical signature: R+S length mismatch - return false; - } + // Minimum and maximum size constraints. + if (sig.size() < 9) return false; + if (sig.size() > 73) return false; - const unsigned char *R = &vchSig[4]; - if (R[-2] != 0x02) { - // Non-canonical signature: R value type mismatch - return false; - } - if (nLenR == 0) { - // Non-canonical signature: R length is zero - return false; - } - if (R[0] & 0x80) { - // Non-canonical signature: R value negative - return false; - } - if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) { - // Non-canonical signature: R value excessively padded - return false; - } + // A signature is of type 0x30 (compound). + if (sig[0] != 0x30) return false; + + // Make sure the length covers the entire signature. + if (sig[1] != sig.size() - 3) return false; + + // Extract the length of the R element. + unsigned int lenR = sig[3]; + + // Make sure the length of the S element is still inside the signature. + if (5 + lenR >= sig.size()) return false; + + // Extract the length of the S element. + unsigned int lenS = sig[5 + lenR]; + + // Verify that the length of the signature matches the sum of the length + // of the elements. + if ((size_t)(lenR + lenS + 7) != sig.size()) return false; + + // Check whether the R element is an integer. + if (sig[2] != 0x02) return false; + + // Zero-length integers are not allowed for R. + if (lenR == 0) return false; + + // Negative numbers are not allowed for R. + if (sig[4] & 0x80) return false; + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false; + + // Check whether the S element is an integer. + if (sig[lenR + 4] != 0x02) return false; + + // Zero-length integers are not allowed for S. + if (lenS == 0) return false; + + // Negative numbers are not allowed for S. + if (sig[lenR + 6] & 0x80) return false; + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false; - const unsigned char *S = &vchSig[6+nLenR]; - if (S[-2] != 0x02) { - // Non-canonical signature: S value type mismatch - return false; - } - if (nLenS == 0) { - // Non-canonical signature: S length is zero - return false; - } - if (S[0] & 0x80) { - // Non-canonical signature: S value negative - return false; - } - if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) { - // Non-canonical signature: S value excessively padded - return false; - } return true; } bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { - if (!IsDERSignature(vchSig)) { + if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } unsigned int nLenR = vchSig[3]; @@ -194,7 +194,7 @@ bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, Sc if (vchSig.size() == 0) { return true; } - if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) { + if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) { // serror is set @@ -1058,12 +1058,12 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -bool SignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { return pubkey.Verify(sighash, vchSig); } -bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode) const +bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1076,7 +1076,7 @@ bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vec int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType); if (!VerifySignature(vchSig, pubkey, sighash)) return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 8bf379ed89..fc64438f68 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -7,6 +7,7 @@ #define BITCOIN_SCRIPT_INTERPRETER_H #include "script_error.h" +#include "primitives/transaction.h" #include <vector> #include <stdint.h> @@ -90,20 +91,29 @@ public: virtual ~BaseSignatureChecker() {} }; -class SignatureChecker : public BaseSignatureChecker +class TransactionSignatureChecker : public BaseSignatureChecker { private: - const CTransaction& txTo; + const CTransaction* txTo; unsigned int nIn; protected: virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - SignatureChecker(const CTransaction& txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const; }; +class MutableTransactionSignatureChecker : public TransactionSignatureChecker +{ +private: + const CTransaction txTo; + +public: + MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn) : TransactionSignatureChecker(&txTo, nInIn), txTo(*txToIn) {} +}; + bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 75ecdb563d..099b4ad0e3 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -74,14 +74,14 @@ public: } -bool CachingSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { static CSignatureCache signatureCache; if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - if (!SignatureChecker::VerifySignature(vchSig, pubkey, sighash)) + if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; if (store) diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 3bd11caff5..b299038daa 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -12,13 +12,13 @@ class CPubKey; -class CachingSignatureChecker : public SignatureChecker +class CachingTransactionSignatureChecker : public TransactionSignatureChecker { private: bool store; public: - CachingSignatureChecker(const CTransaction& txToIn, unsigned int nInIn, bool storeIn=true) : SignatureChecker(txToIn, nInIn), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {} bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index adddd4ec78..14119f7e2c 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -123,7 +123,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(txTo, nIn)); + return VerifyScript(txin.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&txTo, nIn)); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -174,7 +174,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (SignatureChecker(txTo, nIn).CheckSig(sig, pubkey, scriptPubKey)) + if (TransactionSignatureChecker(&txTo, nIn).CheckSig(sig, pubkey, scriptPubKey)) { sigs[pubkey] = sig; break; diff --git a/src/script/standard.h b/src/script/standard.h index ac80193773..a8b0acc981 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -25,7 +25,7 @@ public: CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 40; //! bytes +static const unsigned int MAX_OP_RETURN_RELAY = 80; //! bytes extern unsigned nMaxDatacarrierBytes; /** @@ -45,6 +45,7 @@ static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; * blocks and we must accept those blocks. */ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | + SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_MINIMALDATA | SCRIPT_VERIFY_NULLDUMMY | diff --git a/src/sync.h b/src/sync.h index 7891e41560..27e80e813b 100644 --- a/src/sync.h +++ b/src/sync.h @@ -142,6 +142,17 @@ public: Enter(pszName, pszFile, nLine); } + CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) + { + if (!pmutexIn) return; + + lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock); + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + ~CMutexLock() { if (lock.owns_lock()) diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 9a8fe1ee88..a67c157aff 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -510,6 +510,16 @@ "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but first signature invalid." ], +["Increase DERSIG test coverage"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Overly long signature is incorrectly encoded for DERSIG"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Missing S is incorrectly encoded for DERSIG"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "S with invalid S length is incorrectly encoded for DERSIG"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer R is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer S is incorrectly encoded for DERSIG"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Zero-length R is incorrectly encoded for DERSIG"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "Zero-length S is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Negative S is incorrectly encoded for DERSIG"], + ["Automatically generated test cases"], [ "0x47 0x3044022053205076a7bb12d2db3162a2d97d8197631f829b065948b7019b15482af819a902204328dcc02c994ca086b1226d0d5f1674d23cfae0d846143df812b81cab3391e801", @@ -590,6 +600,102 @@ "P2PK NOT with too much R padding" ], [ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 1, with DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 2, without DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 2, with DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 3, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 3, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 5, without DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 5, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 6, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x304402200b3d0b0375bb15c14620afa4aa10ae90a0d6a046ce217bc20fe0bc1ced68c1b802204b550acab90ae6d3478057c9ad24f9df743815b799b6449dd7e7f6d3bc6e274c01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 7, with DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 8, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 8, with DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 9, without DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 9, with DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 10, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 11, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 11, with DERSIG" +], +[ "0x49 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef05101", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", "LOW_S", diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index a187401cd4..fb81fcb1f5 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -688,6 +688,16 @@ "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature." ], +["Increase test coverage for DERSIG"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is correctly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is correctly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is correctly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is correctly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is correctly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is correctly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is correctly encoded"], + ["Automatically generated test cases"], [ "0x47 0x3044022053205076a7bb13d2db3162a2d97d8197631f829b065948b7019b15482af819a902204328dcc02c994ca086b1226d0d5f1674d23cfae0d846143df812b81cab3391e801", @@ -756,6 +766,54 @@ "P2PK NOT with bad sig with too much R padding but no DERSIG" ], [ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 1, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 4, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 4, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 6, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x304402200b3d0b0375bb15c14620afa4aa10ae90a0d6a046ce217bc20fe0bc1ced68c1b802204b550acab90ae6d3478057c9ad24f9df743815b799b6449dd7e7f6d3bc6e274c01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 7, without DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 10, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 12, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 12, with DERSIG" +], +[ "0x49 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef05101", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", "", diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9501169943..2168a5fef1 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -82,20 +82,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.assign(1,key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err)); + BOOST_CHECK(VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 1: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.assign(1,key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 2: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -106,18 +106,18 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) { - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err)); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); @@ -129,12 +129,12 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) { - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 77e70022df..94f2ce1a29 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -43,7 +43,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, SignatureChecker(txTo, 0), &err); + return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 4db8942fa1..6092afd782 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -93,7 +93,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bo ScriptError err; CMutableTransaction tx = BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)); CMutableTransaction tx2 = tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, SignatureChecker(tx, 0), &err) == expect, message); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, MutableTransactionSignatureChecker(&tx, 0), &err) == expect, message); BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message); #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); @@ -406,6 +406,79 @@ BOOST_AUTO_TEST_CASE(script_build) "P2PK NOT with too much R padding", SCRIPT_VERIFY_DERSIG ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); + good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 1, without DERSIG", 0 + ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 1, with DERSIG", SCRIPT_VERIFY_DERSIG + ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 2, without DERSIG", 0 + ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 2, with DERSIG", SCRIPT_VERIFY_DERSIG + ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 3, without DERSIG", 0 + ).Num(0)); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 3, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0)); + good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 4, without DERSIG", 0 + ).Num(0)); + good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 4, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0)); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 5, without DERSIG", 0 + ).Num(1)); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, + "BIP66 example 5, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(1)); + good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 6, without DERSIG", 0 + ).Num(1)); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, + "BIP66 example 6, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(1)); + good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 7, without DERSIG", 0 + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 7, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 8, without DERSIG", 0 + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 8, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 9, without DERSIG", 0 + ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 9, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 10, without DERSIG", 0 + ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 10, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 11, without DERSIG", 0 + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); + bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, + "BIP66 example 11, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); + good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 12, without DERSIG", 0 + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); + good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, + "BIP66 example 12, with DERSIG", SCRIPT_VERIFY_DERSIG + ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); + good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG, "P2PK with high S but no LOW_S", 0 ).PushSig(keys.key2, SIGHASH_ALL, 32, 33)); @@ -682,18 +755,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -715,54 +788,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 96134cd72e..52adfea992 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, SignatureChecker(tx, i), &err), + verify_flags, TransactionSignatureChecker(&tx, i), &err), strTest); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, SignatureChecker(tx, i), &err); + verify_flags, TransactionSignatureChecker(&tx, i), &err); } BOOST_CHECK_MESSAGE(!fValid, strTest); BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); @@ -347,12 +347,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); - // 40-byte TX_NULL_DATA (standard) - t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + // 80-byte TX_NULL_DATA (standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); BOOST_CHECK(IsStandardTx(t, reason)); - // 41-byte TX_NULL_DATA (non-standard) - t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); + // 81-byte TX_NULL_DATA (non-standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); BOOST_CHECK(!IsStandardTx(t, reason)); // TX_NULL_DATA w/o PUSHDATA diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1c5778abed..d829ec228d 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -282,21 +282,21 @@ BOOST_AUTO_TEST_CASE(strprintf_numbers) { int64_t s64t = -9223372036854775807LL; /* signed 64 bit test value */ uint64_t u64t = 18446744073709551615ULL; /* unsigned 64 bit test value */ - BOOST_CHECK(strprintf("%s %d %s", B, s64t, E) == B" -9223372036854775807 "E); - BOOST_CHECK(strprintf("%s %u %s", B, u64t, E) == B" 18446744073709551615 "E); - BOOST_CHECK(strprintf("%s %x %s", B, u64t, E) == B" ffffffffffffffff "E); + BOOST_CHECK(strprintf("%s %d %s", B, s64t, E) == B" -9223372036854775807 " E); + BOOST_CHECK(strprintf("%s %u %s", B, u64t, E) == B" 18446744073709551615 " E); + BOOST_CHECK(strprintf("%s %x %s", B, u64t, E) == B" ffffffffffffffff " E); size_t st = 12345678; /* unsigned size_t test value */ ssize_t sst = -12345678; /* signed size_t test value */ - BOOST_CHECK(strprintf("%s %d %s", B, sst, E) == B" -12345678 "E); - BOOST_CHECK(strprintf("%s %u %s", B, st, E) == B" 12345678 "E); - BOOST_CHECK(strprintf("%s %x %s", B, st, E) == B" bc614e "E); + BOOST_CHECK(strprintf("%s %d %s", B, sst, E) == B" -12345678 " E); + BOOST_CHECK(strprintf("%s %u %s", B, st, E) == B" 12345678 " E); + BOOST_CHECK(strprintf("%s %x %s", B, st, E) == B" bc614e " E); ptrdiff_t pt = 87654321; /* positive ptrdiff_t test value */ ptrdiff_t spt = -87654321; /* negative ptrdiff_t test value */ - BOOST_CHECK(strprintf("%s %d %s", B, spt, E) == B" -87654321 "E); - BOOST_CHECK(strprintf("%s %u %s", B, pt, E) == B" 87654321 "E); - BOOST_CHECK(strprintf("%s %x %s", B, pt, E) == B" 5397fb1 "E); + BOOST_CHECK(strprintf("%s %d %s", B, spt, E) == B" -87654321 " E); + BOOST_CHECK(strprintf("%s %u %s", B, pt, E) == B" 87654321 " E); + BOOST_CHECK(strprintf("%s %x %s", B, pt, E) == B" 5397fb1 " E); } #undef B #undef E diff --git a/src/wallet.cpp b/src/wallet.cpp index d66a4465c5..b51c4d4b14 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1015,6 +1015,193 @@ set<uint256> CWalletTx::GetConflicts() const return result; } +CAmount CWalletTx::GetDebit(const isminefilter& filter) const +{ + if (vin.empty()) + return 0; + + CAmount debit = 0; + if(filter & ISMINE_SPENDABLE) + { + if (fDebitCached) + debit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + fDebitCached = true; + debit += nDebitCached; + } + } + if(filter & ISMINE_WATCH_ONLY) + { + if(fWatchDebitCached) + debit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + fWatchDebitCached = true; + debit += nWatchDebitCached; + } + } + return debit; +} + +CAmount CWalletTx::GetCredit(const isminefilter& filter) const +{ + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + int64_t credit = 0; + if (filter & ISMINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & ISMINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; +} + +CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + CAmount nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; +} + +bool CWalletTx::IsTrusted() const +{ + // Quick answer in most cases + if (!IsFinalTx(*this)) + return false; + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; +} + void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -1445,10 +1632,14 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; - //The priority after the next block (depth+1) is used instead of the current, + //The coin age after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. - dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; } CAmount nChange = nValueIn - nValue - nFeeRet; @@ -2365,9 +2556,9 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee) +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); } diff --git a/src/wallet.h b/src/wallet.h index aaff00b716..6ed87d1e68 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -103,6 +103,329 @@ public: StringMap destdata; }; + +typedef std::map<std::string, std::string> mapValue_t; + + +static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) + { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"].c_str()); +} + + +static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = i64tostr(nOrderPos); +} + +struct COutputEntry +{ + CTxDestination destination; + CAmount amount; + int vout; +}; + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx : public CTransaction +{ +private: + int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const; + +public: + uint256 hashBlock; + std::vector<uint256> vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = uint256(); + nIndex = -1; + fMerkleVerified = false; + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*(CTransaction*)this); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + } + + int SetMerkleBranch(const CBlock& block); + + + /** + * Return depth of transaction in blockchain: + * -1 : not in blockchain, and not in memory pool (conflicted transaction) + * 0 : in memory pool, waiting to be included in a block + * >=1 : this many blocks deep in the main chain + */ + int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectAbsurdFee=true); +}; + +/** + * A transaction with a bunch of additional info that only the owner cares about. + * It includes any unrecorded transactions needed to link it back to the block chain. + */ +class CWalletTx : public CMerkleTx +{ +private: + const CWallet* pwallet; + +public: + mapValue_t mapValue; + std::vector<std::pair<std::string, std::string> > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; //! time received by this node + unsigned int nTimeSmart; + char fFromMe; + std::string strFromAccount; + int64_t nOrderPos; //! position in ordered transaction list + + // memory only + mutable bool fDebitCached; + mutable bool fCreditCached; + mutable bool fImmatureCreditCached; + mutable bool fAvailableCreditCached; + mutable bool fWatchDebitCached; + mutable bool fWatchCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; + mutable bool fChangeCached; + mutable CAmount nDebitCached; + mutable CAmount nCreditCached; + mutable CAmount nImmatureCreditCached; + mutable CAmount nAvailableCreditCached; + mutable CAmount nWatchDebitCached; + mutable CAmount nWatchCreditCached; + mutable CAmount nImmatureWatchCreditCached; + mutable CAmount nAvailableWatchCreditCached; + mutable CAmount nChangeCached; + + CWalletTx() + { + Init(NULL); + } + + CWalletTx(const CWallet* pwalletIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + void Init(const CWallet* pwalletIn) + { + pwallet = pwalletIn; + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + fDebitCached = false; + fCreditCached = false; + fImmatureCreditCached = false; + fAvailableCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nImmatureCreditCached = 0; + nAvailableCreditCached = 0; + nWatchDebitCached = 0; + nWatchCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + if (ser_action.ForRead()) + Init(NULL); + char fSpent = false; + + if (!ser_action.ForRead()) + { + mapValue["fromaccount"] = strFromAccount; + + WriteOrderPos(nOrderPos, mapValue); + + if (nTimeSmart) + mapValue["timesmart"] = strprintf("%u", nTimeSmart); + } + + READWRITE(*(CMerkleTx*)this); + std::vector<CMerkleTx> vUnused; //! Used to be vtxPrev + READWRITE(vUnused); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (ser_action.ForRead()) + { + strFromAccount = mapValue["fromaccount"]; + + ReadOrderPos(nOrderPos, mapValue); + + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; + } + + mapValue.erase("fromaccount"); + mapValue.erase("version"); + mapValue.erase("spent"); + mapValue.erase("n"); + mapValue.erase("timesmart"); + } + + //! make sure balances are recalculated + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void BindWallet(CWallet *pwalletIn) + { + pwallet = pwalletIn; + MarkDirty(); + } + + //! filter decides which addresses will count towards the debit + CAmount GetDebit(const isminefilter& filter) const; + CAmount GetCredit(const isminefilter& filter) const; + CAmount GetImmatureCredit(bool fUseCache=true) const; + CAmount GetAvailableCredit(bool fUseCache=true) const; + CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetChange() const; + + void GetAmounts(std::list<COutputEntry>& listReceived, + std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const; + + void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, + CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; + + bool IsFromMe(const isminefilter& filter) const + { + return (GetDebit(filter) > 0); + } + + bool IsTrusted() const; + + bool WriteToDisk(CWalletDB *pwalletdb); + + int64_t GetTxTime() const; + int GetRequestCount() const; + + void RelayWalletTransaction(); + + std::set<uint256> GetConflicts() const; +}; + + + + +class COutput +{ +public: + const CWalletTx *tx; + int i; + int nDepth; + bool fSpendable; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) + { + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; + } + + std::string ToString() const; +}; + + + + +/** Private key that includes an expiration date in case it never gets used. */ +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64_t nTimeCreated; + int64_t nTimeExpires; + std::string strComment; + //! todo: add something to note what created it (user, getnewaddress, change) + //! maybe should have a map<string, string> property map + + CWalletKey(int64_t nExpires=0); + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(LIMITED_STRING(strComment, 65536)); + } +}; + + + /** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. @@ -464,508 +787,6 @@ public: }; -typedef std::map<std::string, std::string> mapValue_t; - - -static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) -{ - if (!mapValue.count("n")) - { - nOrderPos = -1; // TODO: calculate elsewhere - return; - } - nOrderPos = atoi64(mapValue["n"].c_str()); -} - - -static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) -{ - if (nOrderPos == -1) - return; - mapValue["n"] = i64tostr(nOrderPos); -} - -struct COutputEntry -{ - CTxDestination destination; - CAmount amount; - int vout; -}; - -/** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx : public CTransaction -{ -private: - int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const; - -public: - uint256 hashBlock; - std::vector<uint256> vMerkleBranch; - int nIndex; - - // memory only - mutable bool fMerkleVerified; - - - CMerkleTx() - { - Init(); - } - - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) - { - Init(); - } - - void Init() - { - hashBlock = uint256(); - nIndex = -1; - fMerkleVerified = false; - } - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*(CTransaction*)this); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - } - - int SetMerkleBranch(const CBlock& block); - - - /** - * Return depth of transaction in blockchain: - * -1 : not in blockchain, and not in memory pool (conflicted transaction) - * 0 : in memory pool, waiting to be included in a block - * >=1 : this many blocks deep in the main chain - */ - int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; - int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } - int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true); -}; - -/** - * A transaction with a bunch of additional info that only the owner cares about. - * It includes any unrecorded transactions needed to link it back to the block chain. - */ -class CWalletTx : public CMerkleTx -{ -private: - const CWallet* pwallet; - -public: - mapValue_t mapValue; - std::vector<std::pair<std::string, std::string> > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; //! time received by this node - unsigned int nTimeSmart; - char fFromMe; - std::string strFromAccount; - int64_t nOrderPos; //! position in ordered transaction list - - // memory only - mutable bool fDebitCached; - mutable bool fCreditCached; - mutable bool fImmatureCreditCached; - mutable bool fAvailableCreditCached; - mutable bool fWatchDebitCached; - mutable bool fWatchCreditCached; - mutable bool fImmatureWatchCreditCached; - mutable bool fAvailableWatchCreditCached; - mutable bool fChangeCached; - mutable CAmount nDebitCached; - mutable CAmount nCreditCached; - mutable CAmount nImmatureCreditCached; - mutable CAmount nAvailableCreditCached; - mutable CAmount nWatchDebitCached; - mutable CAmount nWatchCreditCached; - mutable CAmount nImmatureWatchCreditCached; - mutable CAmount nAvailableWatchCreditCached; - mutable CAmount nChangeCached; - - CWalletTx() - { - Init(NULL); - } - - CWalletTx(const CWallet* pwalletIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - void Init(const CWallet* pwalletIn) - { - pwallet = pwalletIn; - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - nTimeSmart = 0; - fFromMe = false; - strFromAccount.clear(); - fDebitCached = false; - fCreditCached = false; - fImmatureCreditCached = false; - fAvailableCreditCached = false; - fWatchDebitCached = false; - fWatchCreditCached = false; - fImmatureWatchCreditCached = false; - fAvailableWatchCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nImmatureCreditCached = 0; - nAvailableCreditCached = 0; - nWatchDebitCached = 0; - nWatchCreditCached = 0; - nAvailableWatchCreditCached = 0; - nImmatureWatchCreditCached = 0; - nChangeCached = 0; - nOrderPos = -1; - } - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if (ser_action.ForRead()) - Init(NULL); - char fSpent = false; - - if (!ser_action.ForRead()) - { - mapValue["fromaccount"] = strFromAccount; - - WriteOrderPos(nOrderPos, mapValue); - - if (nTimeSmart) - mapValue["timesmart"] = strprintf("%u", nTimeSmart); - } - - READWRITE(*(CMerkleTx*)this); - std::vector<CMerkleTx> vUnused; //! Used to be vtxPrev - READWRITE(vUnused); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (ser_action.ForRead()) - { - strFromAccount = mapValue["fromaccount"]; - - ReadOrderPos(nOrderPos, mapValue); - - nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; - } - - mapValue.erase("fromaccount"); - mapValue.erase("version"); - mapValue.erase("spent"); - mapValue.erase("n"); - mapValue.erase("timesmart"); - } - - //! make sure balances are recalculated - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fWatchDebitCached = false; - fWatchCreditCached = false; - fAvailableWatchCreditCached = false; - fImmatureWatchCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } - - void BindWallet(CWallet *pwalletIn) - { - pwallet = pwalletIn; - MarkDirty(); - } - - //! filter decides which addresses will count towards the debit - CAmount GetDebit(const isminefilter& filter) const - { - if (vin.empty()) - return 0; - - CAmount debit = 0; - if(filter & ISMINE_SPENDABLE) - { - if (fDebitCached) - debit += nDebitCached; - else - { - nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); - fDebitCached = true; - debit += nDebitCached; - } - } - if(filter & ISMINE_WATCH_ONLY) - { - if(fWatchDebitCached) - debit += nWatchDebitCached; - else - { - nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); - fWatchDebitCached = true; - debit += nWatchDebitCached; - } - } - return debit; - } - - CAmount GetCredit(const isminefilter& filter) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - int64_t credit = 0; - if (filter & ISMINE_SPENDABLE) - { - // GetBalance can assume transactions in mapWallet won't change - if (fCreditCached) - credit += nCreditCached; - else - { - nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); - fCreditCached = true; - credit += nCreditCached; - } - } - if (filter & ISMINE_WATCH_ONLY) - { - if (fWatchCreditCached) - credit += nWatchCreditCached; - else - { - nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); - fWatchCreditCached = true; - credit += nWatchCreditCached; - } - } - return credit; - } - - CAmount GetImmatureCredit(bool fUseCache=true) const - { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) - { - if (fUseCache && fImmatureCreditCached) - return nImmatureCreditCached; - nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); - fImmatureCreditCached = true; - return nImmatureCreditCached; - } - - return 0; - } - - CAmount GetAvailableCredit(bool fUseCache=true) const - { - if (pwallet == 0) - return 0; - - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; - - CAmount nCredit = 0; - uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < vout.size(); i++) - { - if (!pwallet->IsSpent(hashTx, i)) - { - const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit(): value out of range"); - } - } - - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; - return nCredit; - } - - CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const - { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) - { - if (fUseCache && fImmatureWatchCreditCached) - return nImmatureWatchCreditCached; - nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); - fImmatureWatchCreditCached = true; - return nImmatureWatchCreditCached; - } - - return 0; - } - - CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const - { - if (pwallet == 0) - return 0; - - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableWatchCreditCached) - return nAvailableWatchCreditCached; - - CAmount nCredit = 0; - for (unsigned int i = 0; i < vout.size(); i++) - { - if (!pwallet->IsSpent(GetHash(), i)) - { - const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit(): value out of range"); - } - } - - nAvailableWatchCreditCached = nCredit; - fAvailableWatchCreditCached = true; - return nCredit; - } - - CAmount GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = pwallet->GetChange(*this); - fChangeCached = true; - return nChangeCached; - } - - void GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const; - - void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, - CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; - - bool IsFromMe(const isminefilter& filter) const - { - return (GetDebit(filter) > 0); - } - - bool IsTrusted() const - { - // Quick answer in most cases - if (!IsFinalTx(*this)) - return false; - int nDepth = GetDepthInMainChain(); - if (nDepth >= 1) - return true; - if (nDepth < 0) - return false; - if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit - return false; - - // Trusted if all inputs are from us and are in the mempool: - BOOST_FOREACH(const CTxIn& txin, vin) - { - // Transactions not sent by us: not trusted - const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); - if (parent == NULL) - return false; - const CTxOut& parentOut = parent->vout[txin.prevout.n]; - if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) - return false; - } - return true; - } - - bool WriteToDisk(CWalletDB *pwalletdb); - - int64_t GetTxTime() const; - int GetRequestCount() const; - - void RelayWalletTransaction(); - - std::set<uint256> GetConflicts() const; -}; - - - - -class COutput -{ -public: - const CWalletTx *tx; - int i; - int nDepth; - bool fSpendable; - - COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) - { - tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; - } - - std::string ToString() const; -}; - - - - -/** Private key that includes an expiration date in case it never gets used. */ -class CWalletKey -{ -public: - CPrivKey vchPrivKey; - int64_t nTimeCreated; - int64_t nTimeExpires; - std::string strComment; - //! todo: add something to note what created it (user, getnewaddress, change) - //! maybe should have a map<string, string> property map - - CWalletKey(int64_t nExpires=0); - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPrivKey); - READWRITE(nTimeCreated); - READWRITE(nTimeExpires); - READWRITE(LIMITED_STRING(strComment, 65536)); - } -}; - - - - - - /** * Account information. * Stored in wallet with key "acc"+string account name. |