src/sync.h \
src/util.h \
src/timestamps.h \
+ src/hash.h \
src/uint256.h \
src/kernel.h \
src/scrypt.h \
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "addrman.h"
+#include "hash.h"
using namespace std;
{ "getsubsidy", &getsubsidy, true, false },
{ "getmininginfo", &getmininginfo, true, false },
{ "getnewaddress", &getnewaddress, true, false },
- { "getnewpubkey", &getnewpubkey, true, false },
{ "getaccountaddress", &getaccountaddress, true, false },
{ "setaccount", &setaccount, true, false },
{ "getaccount", &getaccount, false, false },
{ "walletlock", &walletlock, true, false },
{ "encryptwallet", &encryptwallet, false, false },
{ "validateaddress", &validateaddress, true, false },
- { "validatepubkey", &validatepubkey, true, false },
{ "getbalance", &getbalance, false, false },
{ "move", &movecmd, false, false },
{ "sendfrom", &sendfrom, false, false },
{ "dumpwallet", &dumpwallet, true, false },
{ "importwallet", &importwallet, false, false },
{ "importprivkey", &importprivkey, false, false },
+ { "importaddress", &importaddress, false, true },
{ "listunspent", &listunspent, false, false },
{ "getrawtransaction", &getrawtransaction, false, false },
{ "createrawtransaction", &createrawtransaction, false, false },
void ThreadRPCServer(void* parg)
{
// Make this thread recognisable as the RPC listener
- RenameThread("bitcoin-rpclist");
+ RenameThread("novacoin-rpclist");
try
{
void ThreadRPCServer3(void* parg)
{
// Make this thread recognisable as the RPC handler
- RenameThread("bitcoin-rpchand");
+ RenameThread("novacoin-rpchand");
{
LOCK(cs_THREAD_RPCHANDLER);
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "importaddress" && n > 2) ConvertTo<bool>(params[2]);
return params;
}
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp);
-extern json_spirit::Value validatepubkey(const json_spirit::Array& params, bool fHelp);
-extern json_spirit::Value getnewpubkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
--- /dev/null
+// Copyright (c) 2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef CHECKQUEUE_H
+#define CHECKQUEUE_H
+
+#include <algorithm>
+#include <vector>
+
+#include <boost/foreach.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/mutex.hpp>
+
+template<typename T> class CCheckQueueControl;
+
+/** Queue for verifications that have to be performed.
+ * The verifications are represented by a type T, which must provide an
+ * operator(), returning a bool.
+ *
+ * One thread (the master) is assumed to push batches of verifications
+ * onto the queue, where they are processed by N-1 worker threads. When
+ * the master is done adding work, it temporarily joins the worker pool
+ * as an N'th worker, until all jobs are done.
+ */
+template<typename T> class CCheckQueue {
+private:
+ // Mutex to protect the inner state
+ boost::mutex mutex;
+
+ // Worker threads block on this when out of work
+ boost::condition_variable condWorker;
+
+ // Master thread blocks on this when out of work
+ boost::condition_variable condMaster;
+
+ // Quit method blocks on this until all workers are gone
+ boost::condition_variable condQuit;
+
+ // The queue of elements to be processed.
+ // As the order of booleans doesn't matter, it is used as a LIFO (stack)
+ std::vector<T> queue;
+
+ // The number of workers (including the master) that are idle.
+ int nIdle;
+
+ // The total number of workers (including the master).
+ int nTotal;
+
+ // The temporary evaluation result.
+ bool fAllOk;
+
+ // Number of verifications that haven't completed yet.
+ // This includes elements that are not anymore in queue, but still in
+ // worker's own batches.
+ unsigned int nTodo;
+
+ // Whether we're shutting down.
+ bool fQuit;
+
+ // The maximum number of elements to be processed in one batch
+ unsigned int nBatchSize;
+
+ // Internal function that does bulk of the verification work.
+ bool Loop(bool fMaster = false) {
+ boost::condition_variable &cond = fMaster ? condMaster : condWorker;
+ std::vector<T> vChecks;
+ vChecks.reserve(nBatchSize);
+ unsigned int nNow = 0;
+ bool fOk = true;
+ do {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ // first do the clean-up of the previous loop run (allowing us to do it in the same critsect)
+ if (nNow) {
+ fAllOk &= fOk;
+ nTodo -= nNow;
+ if (nTodo == 0 && !fMaster)
+ // We processed the last element; inform the master he can exit and return the result
+ condMaster.notify_one();
+ } else {
+ // first iteration
+ nTotal++;
+ }
+ // logically, the do loop starts here
+ while (queue.empty()) {
+ if ((fMaster || fQuit) && nTodo == 0) {
+ nTotal--;
+ if (nTotal==0)
+ condQuit.notify_one();
+ bool fRet = fAllOk;
+ // reset the status for new work later
+ if (fMaster)
+ fAllOk = true;
+ // return the current status
+ return fRet;
+ }
+ nIdle++;
+ cond.wait(lock); // wait
+ nIdle--;
+ }
+ // Decide how many work units to process now.
+ // * Do not try to do everything at once, but aim for increasingly smaller batches so
+ // all workers finish approximately simultaneously.
+ // * Try to account for idle jobs which will instantly start helping.
+ // * Don't do batches smaller than 1 (duh), or larger than nBatchSize.
+ nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1)));
+ vChecks.resize(nNow);
+ for (unsigned int i = 0; i < nNow; i++) {
+ // We want the lock on the mutex to be as short as possible, so swap jobs from the global
+ // queue to the local batch vector instead of copying.
+ vChecks[i].swap(queue.back());
+ queue.pop_back();
+ }
+ // Check whether we need to do work at all
+ fOk = fAllOk;
+ }
+ // execute work
+ BOOST_FOREACH(T &check, vChecks)
+ if (fOk)
+ fOk = check();
+ vChecks.clear();
+ } while(true);
+ }
+
+public:
+ // Create a new check queue
+ CCheckQueue(unsigned int nBatchSizeIn) :
+ nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {}
+
+ // Worker thread
+ void Thread() {
+ Loop();
+ }
+
+ // Wait until execution finishes, and return whether all evaluations where succesful.
+ bool Wait() {
+ return Loop(true);
+ }
+
+ // Add a batch of checks to the queue
+ void Add(std::vector<T> &vChecks) {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ BOOST_FOREACH(T &check, vChecks) {
+ queue.push_back(T());
+ check.swap(queue.back());
+ }
+ nTodo += vChecks.size();
+ if (vChecks.size() == 1)
+ condWorker.notify_one();
+ else if (vChecks.size() > 1)
+ condWorker.notify_all();
+ }
+
+ // Shut the queue down
+ void Quit() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ fQuit = true;
+ // No need to wake the master, as he will quit automatically when all jobs are
+ // done.
+ condWorker.notify_all();
+
+ while (nTotal > 0)
+ condQuit.wait(lock);
+ }
+
+ friend class CCheckQueueControl<T>;
+};
+
+/** RAII-style controller object for a CCheckQueue that guarantees the passed
+ * queue is finished before continuing.
+ */
+template<typename T> class CCheckQueueControl {
+private:
+ CCheckQueue<T> *pqueue;
+ bool fDone;
+
+public:
+ CCheckQueueControl(CCheckQueue<T> *pqueueIn) : pqueue(pqueueIn), fDone(false) {
+ // 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 Wait() {
+ if (pqueue == NULL)
+ return true;
+ bool fRet = pqueue->Wait();
+ fDone = true;
+ return fRet;
+ }
+
+ void Add(std::vector<T> &vChecks) {
+ if (pqueue != NULL)
+ pqueue->Add(vChecks);
+ }
+
+ ~CCheckQueueControl() {
+ if (!fDone)
+ Wait();
+ }
+};
+
+#endif
--- /dev/null
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2013 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_HASH_H
+#define BITCOIN_HASH_H
+
+#include "serialize.h"
+#include "uint256.h"
+#include "version.h"
+
+#include <vector>
+
+#include <openssl/ripemd.h>
+#include <openssl/sha.h>
+
+template<typename T1>
+inline uint256 Hash(const T1 pbegin, const T1 pend)
+{
+ static unsigned char pblank[1];
+ uint256 hash1;
+ SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
+ uint256 hash2;
+ SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
+ return hash2;
+}
+
+class CHashWriter
+{
+private:
+ SHA256_CTX ctx;
+
+public:
+ int nType;
+ int nVersion;
+
+ void Init() {
+ SHA256_Init(&ctx);
+ }
+
+ CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {
+ Init();
+ }
+
+ CHashWriter& write(const char *pch, size_t size) {
+ SHA256_Update(&ctx, pch, size);
+ return (*this);
+ }
+
+ // invalidates the object
+ uint256 GetHash() {
+ uint256 hash1;
+ SHA256_Final((unsigned char*)&hash1, &ctx);
+ uint256 hash2;
+ SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
+ return hash2;
+ }
+
+ template<typename T>
+ CHashWriter& operator<<(const T& obj) {
+ // Serialize to this stream
+ ::Serialize(*this, obj, nType, nVersion);
+ return (*this);
+ }
+};
+
+
+template<typename T1, typename T2>
+inline uint256 Hash(const T1 p1begin, const T1 p1end,
+ const T2 p2begin, const T2 p2end)
+{
+ static unsigned char pblank[1];
+ uint256 hash1;
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
+ SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
+ SHA256_Final((unsigned char*)&hash1, &ctx);
+ uint256 hash2;
+ SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
+ return hash2;
+}
+
+template<typename T1, typename T2, typename T3>
+inline uint256 Hash(const T1 p1begin, const T1 p1end,
+ const T2 p2begin, const T2 p2end,
+ const T3 p3begin, const T3 p3end)
+{
+ static unsigned char pblank[1];
+ uint256 hash1;
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
+ SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
+ SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
+ SHA256_Final((unsigned char*)&hash1, &ctx);
+ uint256 hash2;
+ SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
+ return hash2;
+}
+
+template<typename T>
+uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
+{
+ CHashWriter ss(nType, nVersion);
+ ss << obj;
+ return ss.GetHash();
+}
+
+template<typename T1>
+inline uint160 Hash160(const T1 pbegin, const T1 pend)
+{
+ static unsigned char pblank[1];
+ uint256 hash1;
+ SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
+ uint160 hash2;
+ RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
+ return hash2;
+}
+
+inline uint160 Hash160(const std::vector<unsigned char>& vch)
+{
+ return Hash160(vch.begin(), vch.end());
+}
+
+unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash);
+
+typedef struct
+{
+ SHA512_CTX ctxInner;
+ SHA512_CTX ctxOuter;
+} HMAC_SHA512_CTX;
+
+int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len);
+int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len);
+int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx);
+
+#endif
CClientUIInterface uiInterface;
std::string strWalletFileName;
bool fConfChange;
-bool fEnforceCanonical;
unsigned int nNodeLifespan;
unsigned int nDerivationMethodIndex;
unsigned int nMinerSleep;
static bool fTaken;
// Make this thread recognisable as the shutdown thread
- RenameThread("bitcoin-shutoff");
+ RenameThread("novacoin-shutoff");
bool fFirstThread = false;
{
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
" -confchange " + _("Require a confirmations for change (default: 0)") + "\n" +
- " -enforcecanonical " + _("Enforce transaction scripts to use canonical PUSH operators (default: 1)") + "\n" +
" -upgradewallet " + _("Upgrade wallet to latest format") + "\n" +
" -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" +
" -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" +
" -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" +
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
+ " -par=N " + _("Set the number of script verification threads (1-16, 0=auto, default: 0)") + "\n" +
" -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" +
"\n" + _("Block creation options:") + "\n" +
// ********************************************************* Step 3: parameter-to-internal-flags
+ // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
+ nScriptCheckThreads = GetArg("-par", 0);
+ if (nScriptCheckThreads == 0)
+ nScriptCheckThreads = boost::thread::hardware_concurrency();
+ if (nScriptCheckThreads <= 1)
+ nScriptCheckThreads = 0;
+ else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
+ nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
+
fDebug = GetBoolArg("-debug");
// -debug implies fDebug*
}
fConfChange = GetBoolArg("-confchange", false);
- fEnforceCanonical = GetBoolArg("-enforcecanonical", true);
if (mapArgs.count("-mininput"))
{
if (fDaemon)
fprintf(stdout, "NovaCoin server starting\n");
+ if (nScriptCheckThreads) {
+ printf("Using %u threads for script verification\n", nScriptCheckThreads);
+ for (int i=0; i<nScriptCheckThreads-1; i++)
+ NewThread(ThreadScriptCheck, NULL);
+ }
+
int64 nStart;
// ********************************************************* Step 5: verify database integrity
if (file)
LoadExternalBlockFile(file);
}
- exit(0);
+ StartShutdown();
}
filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
void ThreadIRCSeed(void* parg)
{
// Make this thread recognisable as the IRC seeding thread
- RenameThread("bitcoin-ircseed");
+ RenameThread("novacoin-ircseed");
try
{
return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download
// Verify signature
- if (!VerifySignature(txPrev, tx, 0, true, 0))
+ if (!VerifySignature(txPrev, tx, 0, MANDATORY_SCRIPT_VERIFY_FLAGS, 0))
return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str()));
// Read block header
return ret;
}
+int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) {
+ while (c1len > c2len) {
+ if (*c1)
+ return 1;
+ c1++;
+ c1len--;
+ }
+ while (c2len > c1len) {
+ if (*c2)
+ return -1;
+ c2++;
+ c2len--;
+ }
+ while (c1len > 0) {
+ if (*c1 > *c2)
+ return 1;
+ if (*c2 > *c1)
+ return -1;
+ c1++;
+ c2++;
+ c1len--;
+ }
+ return 0;
+}
+
+// Order of secp256k1's generator minus 1.
+const unsigned char vchMaxModOrder[32] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
+ 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
+ 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
+};
+
+// Half of the order of secp256k1's generator minus 1.
+const unsigned char vchMaxModHalfOrder[32] = {
+ 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D,
+ 0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0
+};
+
+const unsigned char vchZero[0] = {};
+
+
+
void CKey::SetCompressedPubKey()
{
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
return fCompressedPubKey;
}
+bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) {
+ return CompareBigEndian(vch, len, vchZero, 0) > 0 &&
+ CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
+}
+
void CKey::MakeNewKey(bool fCompressed)
{
if (!EC_KEY_generate_key(pkey))
bool CKey::Sign(uint256 hash, std::vector<unsigned char>& vchSig)
{
+ vchSig.clear();
+ ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+ if (sig==NULL)
+ return false;
+ const EC_GROUP *group = EC_KEY_get0_group(pkey);
+ CBigNum order, halforder;
+ EC_GROUP_get_order(group, &order, NULL);
+ BN_rshift1(&halforder, &order);
+ // enforce low S values, by negating the value (modulo the order) if above order/2.
+ if (BN_cmp(sig->s, &halforder) > 0) {
+ BN_sub(sig->s, &order, sig->s);
+ }
unsigned int nSize = ECDSA_size(pkey);
vchSig.resize(nSize); // Make sure it is big enough
- if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey))
- {
+ unsigned char *pos = &vchSig[0];
+ nSize = i2d_ECDSA_SIG(sig, &pos);
+ ECDSA_SIG_free(sig);
+ vchSig.resize(nSize); // Shrink to fit actual size
+ // Testing our new signature
+ if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) {
vchSig.clear();
return false;
}
- vchSig.resize(nSize); // Shrink to fit actual size
return true;
}
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
if (sig==NULL)
return false;
+ const EC_GROUP *group = EC_KEY_get0_group(pkey);
+ CBigNum order, halforder;
+ EC_GROUP_get_order(group, &order, NULL);
+ BN_rshift1(&halforder, &order);
+ // enforce low S values, by negating the value (modulo the order) if above order/2.
+ if (BN_cmp(sig->s, &halforder) > 0) {
+ BN_sub(sig->s, &order, sig->s);
+ }
vchSig.clear();
vchSig.resize(65,0);
int nBitsR = BN_num_bits(sig->r);
#include "allocators.h"
#include "serialize.h"
#include "uint256.h"
-#include "util.h"
+#include "hash.h"
+#include "bignum.h"
#include <openssl/ec.h> // for EC_KEY definition
bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig);
bool IsValid();
+
+ // Check whether an element of a signature (r or s) is valid.
+ static bool CheckSignatureElement(const unsigned char *vch, int len, bool half);
};
#endif
#include "keystore.h"
#include "script.h"
+#include "base58.h"
extern bool fWalletUnlockMintOnly;
return false;
}
+bool CBasicKeyStore::AddWatchOnly(const CTxDestination &dest)
+{
+ LOCK(cs_KeyStore);
+ CKeyID keyID;
+ CBitcoinAddress(dest).GetKeyID(keyID);
+
+ if (HaveKey(keyID))
+ return false;
+
+ setWatchOnly.insert(dest);
+ return true;
+}
+
+bool CBasicKeyStore::HaveWatchOnly(const CTxDestination &dest) const
+{
+ LOCK(cs_KeyStore);
+ return setWatchOnly.count(dest) > 0;
+}
+
bool CCryptoKeyStore::SetCrypted()
{
{
{
{
LOCK(cs_KeyStore);
+
+ CTxDestination address = key.GetPubKey().GetID();
+ if (HaveWatchOnly(address))
+ return false;
+
if (!IsCrypted())
return CBasicKeyStore::AddKey(key);
#include "crypter.h"
#include "sync.h"
#include <boost/signals2/signal.hpp>
+#include <boost/variant.hpp>
class CScript;
+class CNoDestination {
+public:
+ friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; }
+ friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
+};
+
+/** A txout script template with a specific destination. It is either:
+ * CNoDestination: no destination set
+ * CKeyID: TX_PUBKEYHASH destination
+ * CScriptID: TX_SCRIPTHASH destination
+ *
+ * A CTxDestination is the internal data type encoded in a CBitcoinAddress.
+ */
+typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
+
/** A virtual base class for key stores */
class CKeyStore
{
virtual bool HaveCScript(const CScriptID &hash) const =0;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
+ // Support for Watch-only addresses
+ virtual bool AddWatchOnly(const CTxDestination &dest) =0;
+ virtual bool HaveWatchOnly(const CTxDestination &dest) const =0;
+
virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const
{
CKey key;
typedef std::map<CKeyID, std::pair<CSecret, bool> > KeyMap;
typedef std::map<CScriptID, CScript > ScriptMap;
+typedef std::set<CTxDestination> WatchOnlySet;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
protected:
KeyMap mapKeys;
ScriptMap mapScripts;
+ WatchOnlySet setWatchOnly;
public:
bool AddKey(const CKey& key);
virtual bool AddCScript(const CScript& redeemScript);
virtual bool HaveCScript(const CScriptID &hash) const;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
+
+ virtual bool AddWatchOnly(const CTxDestination &dest);
+ virtual bool HaveWatchOnly(const CTxDestination &dest) const;
};
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
#include "net.h"
#include "init.h"
#include "ui_interface.h"
+#include "checkqueue.h"
#include "kernel.h"
#include "zerocoin/Zerocoin.h"
#include <boost/algorithm/string/replace.hpp>
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
+int nScriptCheckThreads = 0;
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
return false;
if (!txin.scriptSig.IsPushOnly())
return false;
- if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) {
+ if (!txin.scriptSig.HasCanonicalPushes()) {
return false;
}
}
if (txout.nValue == 0) {
return false;
}
- if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) {
+ if (!txout.scriptPubKey.HasCanonicalPushes()) {
return false;
}
}
// beside "push data" in the scriptSig the
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0))
return false;
if (whichType == TX_SCRIPTHASH)
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, SIG_SWITCH_TIME < tx.nTime ? STRICT_FLAGS : SOFT_FLAGS))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
return nSigOps;
}
+bool CScriptCheck::operator()() const {
+ const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
+ if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType))
+ return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str());
+ return true;
+}
+
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
+{
+ return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
+}
+
bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+ const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
+
if (!IsCoinBase())
{
int64 nValueIn = 0;
return DoS(100, error("ConnectInputs() : txin values out of range"));
}
+
+ if (pvChecks)
+ pvChecks->reserve(vin.size());
+
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
// before the last blockchain checkpoint. This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint.
- if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
+ if (fScriptChecks)
{
// Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ CScriptCheck check(txPrev, *this, i, flags, 0);
+ if (pvChecks)
{
- // only during transition phase for P2SH: do not invoke anti-DoS code for
- // potentially old clients relaying bad P2SH transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
-
+ pvChecks->push_back(CScriptCheck());
+ check.swap(pvChecks->back());
+ }
+ else if (!check())
+ {
+ if (flags & STRICT_FLAGS)
+ {
+ // Don't trigger DoS code in case of STRICT_FLAGS caused failure.
+ CScriptCheck check(txPrev, *this, i, flags & ~STRICT_FLAGS, 0);
+ if (check())
+ return error("ConnectInputs() : %s strict VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ }
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
}
}
int64 nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT;
if (nReward > nCalculatedReward)
- return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
+ return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
}
else
{
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i, true, 0))
- return error("ConnectInputs() : VerifySignature failed");
+ if (!VerifySignature(txPrev, *this, i, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, 0))
+ return error("ClientConnectInputs() : VerifySignature failed");
///// this is redundant with the mempool.mapNextTx stuff,
///// not sure which I want to get rid of
return true;
}
+static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
+
+void ThreadScriptCheck(void*) {
+ vnThreadsRunning[THREAD_SCRIPTCHECK]++;
+ RenameThread("novacoin-scriptch");
+ scriptcheckqueue.Thread();
+ vnThreadsRunning[THREAD_SCRIPTCHECK]--;
+}
+
+void ThreadScriptCheckQuit() {
+ scriptcheckqueue.Quit();
+}
+
bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in, but skip BlockSig checking
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
// initial block download.
bool fEnforceBIP30 = true; // Always active in NovaCoin
- bool fStrictPayToScriptHash = true; // Always active in NovaCoin
+ bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
//// issue here: it doesn't know the version
unsigned int nTxPos;
nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
map<uint256, CTxIndex> mapQueuedChanges;
+ CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
+
int64 nFees = 0;
int64 nValueIn = 0;
int64 nValueOut = 0;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
return false;
- if (fStrictPayToScriptHash)
- {
- // Add in sigops done by pay-to-script-hash inputs;
- // this is to prevent a "rogue miner" from creating
- // an incredibly-expensive-to-validate block.
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
- if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
- }
+ // Add in sigops done by pay-to-script-hash inputs;
+ // this is to prevent a "rogue miner" from creating
+ // an incredibly-expensive-to-validate block.
+ nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ if (nSigOps > MAX_BLOCK_SIGOPS)
+ return DoS(100, error("ConnectBlock() : too many sigops"));
int64 nTxValueIn = tx.GetValueIn(mapInputs);
int64 nTxValueOut = tx.GetValueOut();
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
- if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ std::vector<CScriptCheck> vChecks;
+ if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fScriptChecks, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, nScriptCheckThreads ? &vChecks : NULL))
return false;
+ control.Add(vChecks);
}
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
}
+ if (!control.Wait())
+ return DoS(100, false);
+
if (IsProofOfWork())
{
int64 nBlockReward = GetProofOfWorkReward(nBits, fProtocol048 ? nFees : 0);
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
+// Maximum number of script-checking threads allowed
+static const int MAX_SCRIPTCHECK_THREADS = 16;
#ifdef USE_UPNP
static const int fHaveUPnP = true;
extern int64 nMinimumInputValue;
extern bool fUseFastIndex;
extern unsigned int nDerivationMethodIndex;
-
-extern bool fEnforceCanonical;
+extern int nScriptCheckThreads;
// Minimum disk space required - used in CheckDiskSpace()
static const uint64 nMinDiskSpace = 52428800;
class CReserveKey;
class CTxDB;
class CTxIndex;
+class CScriptCheck;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
bool SendMessages(CNode* pto, bool fSendTrickle);
bool LoadExternalBlockFile(FILE* fileIn);
+// Run an instance of the script checking thread
+void ThreadScriptCheck(void* parg);
+// Stop the script checking threads
+void ThreadScriptCheckQuit();
+
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
int64 GetProofOfWorkReward(unsigned int nBits, int64 nFees=0);
void StakeMiner(CWallet *pwallet);
void ResendWalletTransactions();
-
-
+bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
@param[in] pindexBlock
@param[in] fBlock true if called from ConnectBlock
@param[in] fMiner true if called from CreateNewBlock
- @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions
+ @param[in] fScriptChecks enable scripts validation?
+ @param[in] flags STRICT_FLAGS script validation flags
+ @param[in] pvChecks NULL If pvChecks is not NULL, script checks are pushed onto it instead of being performed inline.
@return Returns true if all checks succeed
*/
- bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
- std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
+ bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs, std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock,
+ bool fBlock, bool fMiner, bool fScriptChecks=true,
+ unsigned int flags=STRICT_FLAGS, std::vector<CScriptCheck> *pvChecks = NULL);
bool ClientConnectInputs();
bool CheckTransaction() const;
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
};
+/** Closure representing one script verification
+ * Note that this stores references to the spending transaction */
+class CScriptCheck
+{
+private:
+ CScript scriptPubKey;
+ const CTransaction *ptxTo;
+ unsigned int nIn;
+ unsigned int nFlags;
+ int nHashType;
+
+public:
+ CScriptCheck() {}
+ CScriptCheck(const CTransaction& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, int nHashTypeIn) :
+ scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
+ ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { }
+
+ bool operator()() const;
+
+ void swap(CScriptCheck &check) {
+ scriptPubKey.swap(check.scriptPubKey);
+ std::swap(ptxTo, check.ptxTo);
+ std::swap(nIn, check.nIn);
+ std::swap(nFlags, check.nFlags);
+ std::swap(nHashType, check.nHashType);
+ }
+};
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
- if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true, true, MANDATORY_SCRIPT_VERIFY_FLAGS))
continue;
mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
swap(mapTestPool, mapTestPoolTmp);
void ThreadGetMyExternalIP(void* parg)
{
// Make this thread recognisable as the external IP detection thread
- RenameThread("bitcoin-ext-ip");
+ RenameThread("novacoin-ext-ip");
CNetAddr addrLocalHost;
if (GetMyExternalIP(addrLocalHost))
void ThreadSocketHandler(void* parg)
{
// Make this thread recognisable as the networking thread
- RenameThread("bitcoin-net");
+ RenameThread("novacoin-net");
try
{
void ThreadMapPort(void* parg)
{
// Make this thread recognisable as the UPnP thread
- RenameThread("bitcoin-UPnP");
+ RenameThread("novacoin-UPnP");
try
{
void ThreadDNSAddressSeed(void* parg)
{
// Make this thread recognisable as the DNS seeding thread
- RenameThread("bitcoin-dnsseed");
+ RenameThread("novacoin-dnsseed");
try
{
void ThreadDumpAddress(void* parg)
{
// Make this thread recognisable as the address dumping thread
- RenameThread("bitcoin-adrdump");
+ RenameThread("novacoin-adrdump");
try
{
void ThreadOpenConnections(void* parg)
{
// Make this thread recognisable as the connection opening thread
- RenameThread("bitcoin-opencon");
+ RenameThread("novacoin-opencon");
try
{
void ThreadOpenAddedConnections(void* parg)
{
// Make this thread recognisable as the connection opening thread
- RenameThread("bitcoin-opencon");
+ RenameThread("novacoin-opencon");
try
{
void ThreadMessageHandler(void* parg)
{
// Make this thread recognisable as the message handling thread
- RenameThread("bitcoin-msghand");
+ RenameThread("novacoin-msghand");
try
{
void StartNode(void* parg)
{
// Make this thread recognisable as the startup thread
- RenameThread("bitcoin-start");
+ RenameThread("novacoin-start");
if (semOutbound == NULL) {
// initialize semaphore
fShutdown = true;
nTransactionsUpdated++;
int64 nStart = GetTime();
+ {
+ LOCK(cs_main);
+ ThreadScriptCheckQuit();
+ }
if (semOutbound)
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
semOutbound->post();
if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n");
if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n");
if (vnThreadsRunning[THREAD_MINTER] > 0) printf("ThreadStakeMinter still running\n");
- while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
+ if (vnThreadsRunning[THREAD_SCRIPTCHECK] > 0) printf("ThreadScriptCheck still running\n");
+ while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0 || vnThreadsRunning[THREAD_SCRIPTCHECK] > 0)
Sleep(20);
Sleep(50);
DumpAddresses();
#include "netbase.h"
#include "protocol.h"
#include "addrman.h"
+#include "hash.h"
class CRequestTracker;
class CNode;
THREAD_DUMPADDRESS,
THREAD_RPCHANDLER,
THREAD_MINTER,
+ THREAD_SCRIPTCHECK,
THREAD_MAX
};
#include "netbase.h"
#include "util.h"
#include "sync.h"
+#include "hash.h"
#ifndef WIN32
#include <sys/fcntl.h>
</widget>
</item>
<item row="0" column="1">
- <widget class="QLabel" name="labelBalance">
+ <widget class="QLabel" name="labelBalanceTotal">
<property name="font">
<font>
<weight>75</weight>
</widget>
</item>
<item row="1" column="0">
+ <widget class="QLabel" name="labelBalanceWatchOnlyText">
+ <property name="text">
+ <string>Unspendable:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelBalanceWatchOnly">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Your unspendable balance</string>
+ </property>
+ <property name="text">
+ <string notr="true">0 NVC</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Stake:</string>
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="2" column="1">
<widget class="QLabel" name="labelStake">
<property name="font">
<font>
</property>
</widget>
</item>
- <item row="2" column="0">
+ <item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Unconfirmed:</string>
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="3" column="1">
<widget class="QLabel" name="labelUnconfirmed">
<property name="font">
<font>
</property>
</widget>
</item>
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QLabel" name="labelImmatureText">
<property name="text">
<string>Immature:</string>
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="4" column="1">
<widget class="QLabel" name="labelImmature">
<property name="font">
<font>
</property>
</widget>
</item>
- <item row="4" column="0">
+ <item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Number of transactions:</string>
</property>
</widget>
</item>
- <item row="4" column="1">
+ <item row="5" column="1">
<widget class="QLabel" name="labelNumTransactions">
<property name="toolTip">
<string>Total number of transactions in wallet</string>
<translation>Unconfirmed:</translation>
</message>
<message>
+ <location line="-55"/>
+ <source>Unspendable:</source>
+ <translation>Unspendable:</translation>
+ </message>
+ <message>
<location line="-107"/>
<source>Wallet</source>
<translation>Wallet</translation>
<translation>Your current balance</translation>
</message>
<message>
+ <location line="-147"/>
+ <source>Your unspendable balance</source>
+ <translation>Your unspendable balance</translation>
+ </message>
+ <message>
<location line="+58"/>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</source>
<translation>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</translation>
<translation>Незрелые:</translation>
</message>
<message>
+ <location line="+136"/>
+ <source>Unspendable:</source>
+ <translation>Недоступно:</translation>
+ </message>
+ <message>
<location line="+13"/>
<source>Mined balance that has not yet matured</source>
<translation>Баланс добытых монет, который ещё не созрел</translation>
<translation>Ваш текущий баланс</translation>
</message>
<message>
+ <location line="-147"/>
+ <source>Your unspendable balance</source>
+ <translation>Недоступный баланс</translation>
+ </message>
+ <message>
<location line="+58"/>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</source>
<translation>Общая сумма всех транзакций, которые до сих пор не подтверждены, и до сих пор не учитываются в текущем балансе</translation>
OverviewPage::OverviewPage(QWidget *parent) :
QWidget(parent),
ui(new Ui::OverviewPage),
- currentBalance(-1),
+ currentBalanceTotal(-1),
+ currentBalanceWatchOnly(0),
currentStake(0),
currentUnconfirmedBalance(-1),
currentImmatureBalance(-1),
delete ui;
}
-void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
+void OverviewPage::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
{
int unit = model->getOptionsModel()->getDisplayUnit();
- currentBalance = balance;
+ currentBalanceTotal = total;
+ currentBalanceWatchOnly = watchOnly;
currentStake = stake;
currentUnconfirmedBalance = unconfirmedBalance;
currentImmatureBalance = immatureBalance;
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
+ ui->labelBalanceTotal->setText(BitcoinUnits::formatWithUnit(unit, total));
+ ui->labelBalanceWatchOnly->setText(BitcoinUnits::formatWithUnit(unit, watchOnly));
ui->labelStake->setText(BitcoinUnits::formatWithUnit(unit, stake));
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
bool showImmature = immatureBalance != 0;
ui->labelImmature->setVisible(showImmature);
ui->labelImmatureText->setVisible(showImmature);
+
+ // only show watch-only balance if it's non-zero, so as not to complicate things
+ // for users
+ bool showWatchOnly = watchOnly != 0;
+ ui->labelBalanceWatchOnly->setVisible(showWatchOnly);
+ ui->labelBalanceWatchOnlyText->setVisible(showWatchOnly);
}
void OverviewPage::setNumTransactions(int count)
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
- setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
- connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64)));
+ qint64 nTotal=0, nWatchOnly=0;
+ model->getBalance(nTotal, nWatchOnly);
+ setBalance(nTotal, nWatchOnly, model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
+ connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64)));
setNumTransactions(model->getNumTransactions());
connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int)));
{
if(model && model->getOptionsModel())
{
- if(currentBalance != -1)
- setBalance(currentBalance, model->getStake(), currentUnconfirmedBalance, currentImmatureBalance);
+ if(currentBalanceTotal != -1)
+ setBalance(currentBalanceTotal, currentBalanceWatchOnly, model->getStake(), currentUnconfirmedBalance, currentImmatureBalance);
// Update txdelegate->unit with the current unit
txdelegate->unit = model->getOptionsModel()->getDisplayUnit();
void showOutOfSyncWarning(bool fShow);
public slots:
- void setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
void setNumTransactions(int count);
signals:
private:
Ui::OverviewPage *ui;
WalletModel *model;
- qint64 currentBalance;
+ qint64 currentBalanceTotal;
+ qint64 currentBalanceWatchOnly;
qint64 currentStake;
qint64 currentUnconfirmedBalance;
qint64 currentImmatureBalance;
static void ipcThread(void* pArg)
{
// Make this thread recognisable as the GUI-IPC thread
- RenameThread("bitcoin-gui-ipc");
+ RenameThread("novacoin-gui-ipc");
try
{
}
if(model && model->getOptionsModel())
{
- setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
- connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64)));
+ qint64 nTotal=0, nWatchOnly=0;
+ model->getBalance(nTotal, nWatchOnly);
+ setBalance(nTotal, nWatchOnly, model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
+ connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
// Coin Control
if(!ui->lineEditCoinControlChange->hasAcceptableInput() ||
(model && !model->validateAddress(ui->lineEditCoinControlChange->text())))
{
+ CoinControlDialog::coinControl->destChange = CNoDestination();
ui->lineEditCoinControlChange->setValid(false);
valid = false;
}
+ else
+ CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get();
}
for(int i = 0; i < ui->entries->count(); ++i)
return false;
}
-void SendCoinsDialog::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
+void SendCoinsDialog::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
{
Q_UNUSED(stake);
Q_UNUSED(unconfirmedBalance);
return;
int unit = model->getOptionsModel()->getDisplayUnit();
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, total - watchOnly));
}
void SendCoinsDialog::updateDisplayUnit()
if(model && model->getOptionsModel())
{
// Update labelBalance with the current balance and the current unit
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance()));
+ qint64 total=0, watchOnly=0;
+ model->getBalance(total, watchOnly);
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), total - watchOnly));
}
}
void accept();
SendCoinsEntry *addEntry();
void updateRemoveEnabled();
- void setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
void on_addressBookButton_clicked();
void on_pasteButton_clicked();
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
int64 nTime = wtx.GetTxTime();
- int64 nCredit = wtx.GetCredit();
+ int64 nCredit = wtx.GetCredit(false);
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
//
int64 nUnmatured = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
- nUnmatured += wallet->GetCredit(txout);
+ nUnmatured += wallet->GetCredit(txout, false);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, false)) + "<br>";
}
}
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, false)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
{
QList<TransactionRecord> parts;
int64 nTime = wtx.GetTxTime();
- int64 nCredit = wtx.GetCredit(true);
+ int64 nCredit = wtx.GetCredit(false,true);
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash(), hashPrev = 0;
// For generated transactions, determine maturity
if(type == TransactionRecord::Generated)
{
- int64 nCredit = wtx.GetCredit(true);
+ int64 nCredit = wtx.GetCredit(false,true);
if (nCredit == 0)
{
status.maturity = TransactionStatus::Immature;
return wallet->GetBalance();
}
+void WalletModel::getBalance(qint64 &nTotal, qint64 &nWatchOnly) const
+{
+ wallet->GetBalance(nTotal, nWatchOnly);
+}
+
qint64 WalletModel::getUnconfirmedBalance() const
{
return wallet->GetUnconfirmedBalance();
void WalletModel::checkBalanceChanged()
{
- qint64 newBalance = getBalance();
+ qint64 newBalanceTotal=0, newBalanceWatchOnly=0;
+ getBalance(newBalanceTotal, newBalanceWatchOnly);
+
qint64 newStake = getStake();
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
qint64 newImmatureBalance = getImmatureBalance();
- if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
+ if(cachedBalance != newBalanceTotal || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
{
- cachedBalance = newBalance;
+ cachedBalance = newBalanceTotal;
cachedStake = newStake;
cachedUnconfirmedBalance = newUnconfirmedBalance;
cachedImmatureBalance = newImmatureBalance;
- emit balanceChanged(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance);
+ emit balanceChanged(newBalanceTotal, newBalanceWatchOnly, newStake, newUnconfirmedBalance, newImmatureBalance);
}
}
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
- nBalance += out.tx->vout[out.i].nValue;
+ if(out.fSpendable)
+ nBalance += out.tx->vout[out.i].nValue;
if(total > nBalance)
{
BOOST_FOREACH(const COutPoint& outpoint, vOutpoints)
{
if (!wallet->mapWallet.count(outpoint.hash)) continue;
- COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain());
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true);
vOutputs.push_back(out);
}
}
BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins)
{
if (!wallet->mapWallet.count(outpoint.hash)) continue;
- COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain());
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true);
vCoins.push_back(out);
}
while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
{
if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
- cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0);
+ cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true);
}
CTxDestination address;
- if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue;
+ if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
+ continue;
mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out);
}
}
TransactionTableModel *getTransactionTableModel();
qint64 getBalance() const;
+ void getBalance(qint64 &nTotal, qint64 &nWatchOnly) const;
qint64 getStake() const;
qint64 getUnconfirmedBalance() const;
qint64 getImmatureBalance() const;
signals:
// Signal that balance in wallet changed
- void balanceChanged(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void balanceChanged(qint64 total, qint64 watchOnly, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance);
// Number of transactions in wallet changed
void numTransactionsChanged(int count);
{
if (fPrintTransactionDetail)
{
- Object entry;
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << tx;
+ string strHex = HexStr(ssTx.begin(), ssTx.end());
- entry.push_back(Pair("txid", tx.GetHash().GetHex()));
- TxToJSON(tx, 0, entry);
-
- txinfo.push_back(entry);
+ txinfo.push_back(strHex);
}
else
txinfo.push_back(tx.GetHash().GetHex());
return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false);
}
-// ppcoin: get information of sync-checkpoint
+// get information of sync-checkpoint
Value getcheckpoint(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
result.push_back(Pair("height", pindexCheckpoint->nHeight));
result.push_back(Pair("timestamp", DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str()));
+ if (Checkpoints::checkpointMessage.vchSig.size() != 0)
+ {
+ Object msgdata;
+ CUnsignedSyncCheckpoint checkpoint;
+
+ CDataStream sMsg(Checkpoints::checkpointMessage.vchMsg, SER_NETWORK, PROTOCOL_VERSION);
+ sMsg >> checkpoint;
+
+ Object parsed; // message version and data (block hash)
+ parsed.push_back(Pair("version", checkpoint.nVersion));
+ parsed.push_back(Pair("hash", checkpoint.hashCheckpoint.GetHex().c_str()));
+ msgdata.push_back(Pair("parsed", parsed));
+
+ Object raw; // raw checkpoint message data
+ raw.push_back(Pair("data", HexStr(Checkpoints::checkpointMessage.vchMsg).c_str()));
+ raw.push_back(Pair("signature", HexStr(Checkpoints::checkpointMessage.vchSig).c_str()));
+ msgdata.push_back(Pair("raw", raw));
+
+ result.push_back(Pair("data", msgdata));
+ }
+
// Check that the block satisfies synchronized checkpoint
if (CheckpointsMode == Checkpoints::STRICT)
result.push_back(Pair("policy", "strict"));
return Value::null;
}
+Value importaddress(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 3)
+ throw runtime_error(
+ "importaddress <address> [label] [rescan=true]\n"
+ "Adds an address that can be watched as if it were in your wallet but cannot be used to spend.");
+
+ CBitcoinAddress address(params[0].get_str());
+ if (!address.IsValid())
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ CTxDestination dest;
+ dest = address.Get();
+
+ string strLabel = "";
+ if (params.size() > 1)
+ strLabel = params[1].get_str();
+
+ // Whether to perform rescan after import
+ bool fRescan = true;
+ if (params.size() > 2)
+ fRescan = params[2].get_bool();
+
+ {
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+
+ // Don't throw error in case an address is already there
+ if (pwalletMain->HaveWatchOnly(dest))
+ return Value::null;
+
+ pwalletMain->MarkDirty();
+ pwalletMain->SetAddressBookName(dest, strLabel);
+
+ if (!pwalletMain->AddWatchOnly(dest))
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+
+ if (fRescan)
+ {
+ pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
+ pwalletMain->ReacceptWalletTransactions();
+ }
+ }
+
+ return Value::null;
+}
+
Value importwallet(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
}
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
+ if (pk.IsPayToScriptHash())
+ {
+ CTxDestination address;
+ if (ExtractDestination(pk, address))
+ {
+ const CScriptID& hash = boost::get<const CScriptID&>(address);
+ CScript redeemScript;
+ if (pwalletMain->GetCScript(hash, redeemScript))
+ entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
+ }
+ }
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth));
+ entry.push_back(Pair("spendable", out.fSpendable));
results.push_back(entry);
}
{
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
- "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
+ "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
"Sign inputs for raw transaction (serialized, hex-encoded).\n"
"Second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the blockchain.\n"
}
}
+ bool fGivenKeys = false;
+ CBasicKeyStore tempKeystore;
+ if (params.size() > 2 && params[2].type() != null_type)
+ {
+ fGivenKeys = true;
+ Array keys = params[2].get_array();
+ BOOST_FOREACH(Value k, keys)
+ {
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(k.get_str());
+ if (!fGood)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
+ CKey key;
+ bool fCompressed;
+ CSecret secret = vchSecret.GetSecret(fCompressed);
+ key.SetSecret(secret, fCompressed);
+ tempKeystore.AddKey(key);
+ }
+ }
+ else
+ EnsureWalletIsUnlocked();
+
// Add previous txouts given in the RPC call:
if (params.size() > 1 && params[1].type() != null_type)
{
}
else
mapPrevOut[outpoint] = scriptPubKey;
- }
- }
- bool fGivenKeys = false;
- CBasicKeyStore tempKeystore;
- if (params.size() > 2 && params[2].type() != null_type)
- {
- fGivenKeys = true;
- Array keys = params[2].get_array();
- BOOST_FOREACH(Value k, keys)
- {
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(k.get_str());
- if (!fGood)
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
- CKey key;
- bool fCompressed;
- CSecret secret = vchSecret.GetSecret(fCompressed);
- key.SetSecret(secret, fCompressed);
- tempKeystore.AddKey(key);
+ // if redeemScript given and not using the local wallet (private keys
+ // given), add redeemScript to the tempKeystore so it can be signed:
+ Value v = find_value(prevOut, "redeemScript");
+ if (fGivenKeys && scriptPubKey.IsPayToScriptHash())
+ {
+ RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
+ Value v = find_value(prevOut, "redeemScript");
+ if (!(v == Value::null))
+ {
+ vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
+ CScript redeemScript(rsData.begin(), rsData.end());
+ tempKeystore.AddCScript(redeemScript);
+ }
+ }
}
}
- else
- EnsureWalletIsUnlocked();
const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
{
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
- if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
+ if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STRICT_FLAGS, 0))
fComplete = false;
}
proxyType proxy;
GetProxy(NET_IPV4, proxy);
+ int64 nTotal = 0, nWatchOnly = 0;
+ pwalletMain->GetBalance(nTotal, nWatchOnly);
+
Object obj, diff;
obj.push_back(Pair("version", FormatFullVersion()));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
- obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
+ obj.push_back(Pair("balance", ValueFromAmount(nTotal)));
+ obj.push_back(Pair("unspendable", ValueFromAmount(nWatchOnly)));
obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
obj.push_back(Pair("blocks", (int)nBestHeight));
return obj;
}
-
-Value getnewpubkey(const Array& params, bool fHelp)
-{
- if (fHelp || params.size() > 1)
- throw runtime_error(
- "getnewpubkey [account]\n"
- "Returns new public key for coinbase generation.");
-
- // Parse the account first so we don't generate a key if there's an error
- string strAccount;
- if (params.size() > 0)
- strAccount = AccountFromValue(params[0]);
-
- if (!pwalletMain->IsLocked())
- pwalletMain->TopUpKeyPool();
-
- // Generate a new key that is added to wallet
- CPubKey newKey;
- if (!pwalletMain->GetKeyFromPool(newKey, false))
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
- CKeyID keyID = newKey.GetID();
-
- pwalletMain->SetAddressBookName(keyID, strAccount);
- vector<unsigned char> vchPubKey = newKey.Raw();
-
- return HexStr(vchPubKey.begin(), vchPubKey.end());
-}
-
-
Value getnewaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
if (!fCreated)
{
- if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
+ int64 nTotal = 0, nWatchOnly = 0;
+ pwalletMain->GetBalance(nTotal, nWatchOnly);
+
+ if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
}
TxToJSON(wtx, 0, entry);
- int64 nCredit = wtx.GetCredit();
+ int64 nCredit = wtx.GetCredit(false);
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
void ThreadTopUpKeyPool(void* parg)
{
// Make this thread recognisable as the key-topping-up thread
- RenameThread("bitcoin-key-top");
+ RenameThread("novacoin-key-top");
pwalletMain->TopUpKeyPool();
}
void ThreadCleanWalletPassphrase(void* parg)
{
// Make this thread recognisable as the wallet relocking thread
- RenameThread("bitcoin-lock-wa");
+ RenameThread("novacoin-lock-wa");
int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
class DescribeAddressVisitor : public boost::static_visitor<Object>
{
+private:
+ isminetype mine;
public:
- Object operator()(const CNoDestination &dest) const { return Object(); }
+ DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
+ Object operator()(const CNoDestination &dest) const { return Object(); }
Object operator()(const CKeyID &keyID) const {
Object obj;
CPubKey vchPubKey;
pwalletMain->GetPubKey(keyID, vchPubKey);
obj.push_back(Pair("isscript", false));
- obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
- obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
+ if (mine == MINE_SPENDABLE) {
+ pwalletMain->GetPubKey(keyID, vchPubKey);
+ obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
+ obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
+ }
return obj;
}
Object operator()(const CScriptID &scriptID) const {
Object obj;
obj.push_back(Pair("isscript", true));
- CScript subscript;
- pwalletMain->GetCScript(scriptID, subscript);
- std::vector<CTxDestination> addresses;
- txnouttype whichType;
- int nRequired;
- ExtractDestinations(subscript, whichType, addresses, nRequired);
- obj.push_back(Pair("script", GetTxnOutputType(whichType)));
- obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
- Array a;
- BOOST_FOREACH(const CTxDestination& addr, addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
- obj.push_back(Pair("addresses", a));
- if (whichType == TX_MULTISIG)
- obj.push_back(Pair("sigsrequired", nRequired));
+ if (mine == MINE_SPENDABLE) {
+ CScript subscript;
+ pwalletMain->GetCScript(scriptID, subscript);
+ std::vector<CTxDestination> addresses;
+ txnouttype whichType;
+ int nRequired;
+ ExtractDestinations(subscript, whichType, addresses, nRequired);
+ obj.push_back(Pair("script", GetTxnOutputType(whichType)));
+ obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
+ Array a;
+ BOOST_FOREACH(const CTxDestination& addr, addresses)
+ a.push_back(CBitcoinAddress(addr).ToString());
+ obj.push_back(Pair("addresses", a));
+ if (whichType == TX_MULTISIG)
+ obj.push_back(Pair("sigsrequired", nRequired));
+ }
return obj;
}
};
CTxDestination dest = address.Get();
string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress));
- bool fMine = IsMine(*pwalletMain, dest);
- ret.push_back(Pair("ismine", fMine));
- if (fMine) {
- Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
- ret.insert(ret.end(), detail.begin(), detail.end());
- }
- if (pwalletMain->mapAddressBook.count(dest))
- ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
- }
- return ret;
-}
-
-Value validatepubkey(const Array& params, bool fHelp)
-{
- if (fHelp || !params.size() || params.size() > 2)
- throw runtime_error(
- "validatepubkey <novacoinpubkey>\n"
- "Return information about <novacoinpubkey>.");
-
- std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
- CPubKey pubKey(vchPubKey);
-
- bool isValid = pubKey.IsValid();
- bool isCompressed = pubKey.IsCompressed();
- CKeyID keyID = pubKey.GetID();
-
- CBitcoinAddress address;
- address.Set(keyID);
-
- Object ret;
- ret.push_back(Pair("isvalid", isValid));
- if (isValid)
- {
- CTxDestination dest = address.Get();
- string currentAddress = address.ToString();
- ret.push_back(Pair("address", currentAddress));
- bool fMine = IsMine(*pwalletMain, dest);
- ret.push_back(Pair("ismine", fMine));
- ret.push_back(Pair("iscompressed", isCompressed));
- if (fMine) {
- Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
+ isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
+ ret.push_back(Pair("ismine", mine != MINE_NO));
+ if (mine != MINE_NO) {
+ ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
+ Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
ret.insert(ret.end(), detail.begin(), detail.end());
}
if (pwalletMain->mapAddressBook.count(dest))
#include "sync.h"
#include "util.h"
-bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
+bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags);
static const valtype vchFalse(0);
static const valtype vchZero(0);
}
}
-bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
+bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) {
+ if (!(flags & SCRIPT_VERIFY_STRICTENC))
+ return true;
+
+ if (vchPubKey.size() < 33)
+ return error("Non-canonical public key: too short");
+ if (vchPubKey[0] == 0x04) {
+ if (vchPubKey.size() != 65)
+ return error("Non-canonical public key: invalid length for uncompressed key");
+ } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
+ if (vchPubKey.size() != 33)
+ return error("Non-canonical public key: invalid length for compressed key");
+ } else {
+ return error("Non-canonical public key: compressed nor uncompressed");
+ }
+ return true;
+}
+
+bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) {
+ if (!(flags & SCRIPT_VERIFY_STRICTENC))
+ return true;
+
+ // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
+ // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
+ // Where R and S are not negative (their first byte has its highest bit not set), and not
+ // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
+ // in which case a single 0 byte is necessary and even required).
+ if (vchSig.size() < 9)
+ return error("Non-canonical signature: too short");
+ if (vchSig.size() > 73)
+ return error("Non-canonical signature: too long");
+ unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
+ if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
+ return error("Non-canonical signature: unknown hashtype byte");
+ if (vchSig[0] != 0x30)
+ return error("Non-canonical signature: wrong type");
+ if (vchSig[1] != vchSig.size()-3)
+ return error("Non-canonical signature: wrong length marker");
+ unsigned int nLenR = vchSig[3];
+ if (5 + nLenR >= vchSig.size())
+ return error("Non-canonical signature: S length misplaced");
+ unsigned int nLenS = vchSig[5+nLenR];
+ if ((unsigned long)(nLenR+nLenS+7) != vchSig.size())
+ return error("Non-canonical signature: R+S length mismatch");
+
+ const unsigned char *R = &vchSig[4];
+ if (R[-2] != 0x02)
+ return error("Non-canonical signature: R value type mismatch");
+ if (nLenR == 0)
+ return error("Non-canonical signature: R length is zero");
+ if (R[0] & 0x80)
+ return error("Non-canonical signature: R value negative");
+ if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80))
+ return error("Non-canonical signature: R value excessively padded");
+
+ const unsigned char *S = &vchSig[6+nLenR];
+ if (S[-2] != 0x02)
+ return error("Non-canonical signature: S value type mismatch");
+ if (nLenS == 0)
+ return error("Non-canonical signature: S length is zero");
+ if (S[0] & 0x80)
+ return error("Non-canonical signature: S value negative");
+ if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
+ return error("Non-canonical signature: S value excessively padded");
+
+ if (flags & SCRIPT_VERIFY_LOW_S) {
+ if (!CKey::CheckSignatureElement(S, nLenS, true))
+ return error("Non-canonical signature: S value is unnecessarily high");
+ }
+
+ return true;
+}
+
+bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
{
CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin();
return false;
int nOpCount = 0;
-
try
{
while (pc < pend)
//
if (!script.GetOp(pc, opcode, vchPushValue))
return false;
- if (vchPushValue.size() > 520)
+ if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
return false;
if (opcode > OP_16 && ++nOpCount > 201)
return false;
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
- return false;
+ return false; // Disabled opcodes.
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4)
stack.push_back(vchPushValue);
break;
- //
- // Splice ops
- //
- case OP_CAT:
- {
- // (x1 x2 -- out)
- if (stack.size() < 2)
- return false;
- valtype& vch1 = stacktop(-2);
- valtype& vch2 = stacktop(-1);
- vch1.insert(vch1.end(), vch2.begin(), vch2.end());
- popstack(stack);
- if (stacktop(-1).size() > 520)
- return false;
- }
- break;
-
- case OP_SUBSTR:
- {
- // (in begin size -- out)
- if (stack.size() < 3)
- return false;
- valtype& vch = stacktop(-3);
- int nBegin = CastToBigNum(stacktop(-2)).getint();
- int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint();
- if (nBegin < 0 || nEnd < nBegin)
- return false;
- if (nBegin > (int)vch.size())
- nBegin = vch.size();
- if (nEnd > (int)vch.size())
- nEnd = vch.size();
- vch.erase(vch.begin() + nEnd, vch.end());
- vch.erase(vch.begin(), vch.begin() + nBegin);
- popstack(stack);
- popstack(stack);
- }
- break;
-
- case OP_LEFT:
- case OP_RIGHT:
- {
- // (in size -- out)
- if (stack.size() < 2)
- return false;
- valtype& vch = stacktop(-2);
- int nSize = CastToBigNum(stacktop(-1)).getint();
- if (nSize < 0)
- return false;
- if (nSize > (int)vch.size())
- nSize = vch.size();
- if (opcode == OP_LEFT)
- vch.erase(vch.begin() + nSize, vch.end());
- else
- vch.erase(vch.begin(), vch.end() - nSize);
- popstack(stack);
- }
- break;
-
case OP_SIZE:
{
// (in -- in size)
//
// Bitwise logic
//
- case OP_INVERT:
- {
- // (in - out)
- if (stack.size() < 1)
- return false;
- valtype& vch = stacktop(-1);
- for (unsigned int i = 0; i < vch.size(); i++)
- vch[i] = ~vch[i];
- }
- break;
-
- //
- // WARNING: These disabled opcodes exhibit unexpected behavior
- // when used on signed integers due to a bug in MakeSameSize()
- // [see definition of MakeSameSize() above].
- //
- case OP_AND:
- case OP_OR:
- case OP_XOR:
- {
- // (x1 x2 - out)
- if (stack.size() < 2)
- return false;
- valtype& vch1 = stacktop(-2);
- valtype& vch2 = stacktop(-1);
- MakeSameSize(vch1, vch2); // <-- NOT SAFE FOR SIGNED VALUES
- if (opcode == OP_AND)
- {
- for (unsigned int i = 0; i < vch1.size(); i++)
- vch1[i] &= vch2[i];
- }
- else if (opcode == OP_OR)
- {
- for (unsigned int i = 0; i < vch1.size(); i++)
- vch1[i] |= vch2[i];
- }
- else if (opcode == OP_XOR)
- {
- for (unsigned int i = 0; i < vch1.size(); i++)
- vch1[i] ^= vch2[i];
- }
- popstack(stack);
- }
- break;
-
case OP_EQUAL:
case OP_EQUALVERIFY:
//case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
//
case OP_1ADD:
case OP_1SUB:
- case OP_2MUL:
- case OP_2DIV:
case OP_NEGATE:
case OP_ABS:
case OP_NOT:
{
case OP_1ADD: bn += bnOne; break;
case OP_1SUB: bn -= bnOne; break;
- case OP_2MUL: bn <<= 1; break;
- case OP_2DIV: bn >>= 1; break;
case OP_NEGATE: bn = -bn; break;
case OP_ABS: if (bn < bnZero) bn = -bn; break;
case OP_NOT: bn = (bn == bnZero); break;
case OP_ADD:
case OP_SUB:
- case OP_MUL:
- case OP_DIV:
- case OP_MOD:
- case OP_LSHIFT:
- case OP_RSHIFT:
case OP_BOOLAND:
case OP_BOOLOR:
case OP_NUMEQUAL:
bn = bn1 - bn2;
break;
- case OP_MUL:
- if (!BN_mul(&bn, &bn1, &bn2, pctx))
- return false;
- break;
-
- case OP_DIV:
- if (!BN_div(&bn, NULL, &bn1, &bn2, pctx))
- return false;
- break;
-
- case OP_MOD:
- if (!BN_mod(&bn, &bn1, &bn2, pctx))
- return false;
- break;
-
- case OP_LSHIFT:
- if (bn2 < bnZero || bn2 > CBigNum(2048))
- return false;
- bn = bn1 << bn2.getulong();
- break;
-
- case OP_RSHIFT:
- if (bn2 < bnZero || bn2 > CBigNum(2048))
- return false;
- bn = bn1 >> bn2.getulong();
- break;
-
case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break;
case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break;
case OP_NUMEQUAL: bn = (bn1 == bn2); break;
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
- bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+ bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
+ CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
popstack(stack);
popstack(stack);
valtype& vchPubKey = stacktop(-ikey);
// Check signature
- if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType))
- {
+ bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
+ CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
+
+ if (fOk) {
isig++;
nSigsCount--;
}
fSuccess = false;
}
- while (i-- > 0)
+ while (i-- > 1)
popstack(stack);
+
+ // A bug causes CHECKMULTISIG to consume one extra argument
+ // whose contents were not checked in any way.
+ //
+ // Unfortunately this is a potential source of mutability,
+ // so optionally verify it is exactly equal to zero prior
+ // to removing it from the stack.
+ if (stack.size() < 1)
+ return false;
+ if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size())
+ return error("CHECKMULTISIG dummy argument not null");
+ popstack(stack);
+
stack.push_back(fSuccess ? vchTrue : vchFalse);
if (opcode == OP_CHECKMULTISIGVERIFY)
{
private:
// sigdata_type is (signature hash, signature, public key):
- typedef boost::tuple<uint256, std::vector<unsigned char>, std::vector<unsigned char> > sigdata_type;
+ typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey > sigdata_type;
std::set< sigdata_type> setValid;
- CCriticalSection cs_sigcache;
+ boost::shared_mutex cs_sigcache;
public:
bool
- Get(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey)
+ Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
{
- LOCK(cs_sigcache);
+ boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
sigdata_type k(hash, vchSig, pubKey);
std::set<sigdata_type>::iterator mi = setValid.find(k);
return false;
}
- void Set(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey)
+ void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
{
// DoS prevention: limit cache size to less than 10MB
// (~200 bytes per cache entry times 50,000 entries)
int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000);
if (nMaxCacheSize <= 0) return;
- LOCK(cs_sigcache);
+ boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
while (static_cast<int64>(setValid.size()) > nMaxCacheSize)
{
}
};
-bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode,
- const CTransaction& txTo, unsigned int nIn, int nHashType)
+bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode,
+ const CTransaction& txTo, unsigned int nIn, int nHashType, int flags)
{
static CSignatureCache signatureCache;
+ CKey key;
+ if (!key.SetPubKey(vchPubKey))
+ return false;
+ CPubKey pubkey = key.GetPubKey();
+ if (!pubkey.IsValid())
+ return false;
+
// Hash type is one byte tacked on to the end of the signature
if (vchSig.empty())
return false;
uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType);
- if (signatureCache.Get(sighash, vchSig, vchPubKey))
+ if (signatureCache.Get(sighash, vchSig, pubkey))
return true;
- CKey key;
- if (!key.SetPubKey(vchPubKey))
- return false;
-
if (!key.Verify(sighash, vchSig))
return false;
- signatureCache.Set(sighash, vchSig, vchPubKey);
+ if (!(flags & SCRIPT_VERIFY_NOCACHE))
+ signatureCache.Set(sighash, vchSig, pubkey);
+
return true;
}
-
//
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
};
-bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
+isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest)
{
- return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest);
+ if (boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest))
+ return MINE_SPENDABLE;
+ if (keystore.HaveWatchOnly(dest))
+ return MINE_WATCH_ONLY;
+ return MINE_NO;
}
-bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
+isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txnouttype whichType;
- if (!Solver(scriptPubKey, whichType, vSolutions))
- return false;
+ if (!Solver(scriptPubKey, whichType, vSolutions)) {
+ if (keystore.HaveWatchOnly(scriptPubKey.GetID()))
+ return MINE_WATCH_ONLY;
+ return MINE_NO;
+ }
CKeyID keyID;
switch (whichType)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
- return false;
+ break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
- return keystore.HaveKey(keyID);
+ if (keystore.HaveKey(keyID))
+ return MINE_SPENDABLE;
+ if (keystore.HaveWatchOnly(keyID))
+ return MINE_WATCH_ONLY;
+ break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
- return keystore.HaveKey(keyID);
+ if (keystore.HaveKey(keyID))
+ return MINE_SPENDABLE;
+ if (keystore.HaveWatchOnly(keyID))
+ return MINE_WATCH_ONLY;
+ break;
case TX_SCRIPTHASH:
{
+ CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
- if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
- return false;
- return IsMine(keystore, subscript);
+ if (keystore.GetCScript(scriptID, subscript)) {
+ isminetype ret = IsMine(keystore, subscript);
+ if (ret)
+ return ret;
+ }
+ if (keystore.HaveWatchOnly(scriptID))
+ return MINE_WATCH_ONLY;
+ break;
}
case TX_MULTISIG:
{
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
- return HaveKeys(keys, keystore) == keys.size();
+ if (HaveKeys(keys, keystore) == keys.size())
+ return MINE_SPENDABLE;
+ break;
}
}
- return false;
+
+ if (keystore.HaveWatchOnly(scriptPubKey.GetID()))
+ return MINE_WATCH_ONLY;
+ return MINE_NO;
}
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
}
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
- bool fValidatePayToScriptHash, int nHashType)
+ unsigned int flags, int nHashType)
{
vector<vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType))
return false;
- if (fValidatePayToScriptHash)
+ if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType))
return false;
if (stack.empty())
return false;
return false;
// Additional validation for spend-to-script-hash transactions:
- if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash())
+ if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash())
{
if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only
return false; // or validation fails
+ // stackCopy cannot be empty here, because if it was the
+ // P2SH HASH <> EQUAL scriptPubKey would be evaluated with
+ // an empty stack and the EvalScript above would return false.
+ assert(!stackCopy.empty());
+
const valtype& pubKeySerialized = stackCopy.back();
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stackCopy);
- if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType))
+ if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType))
return false;
if (stackCopy.empty())
return false;
return true;
}
-
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType)
{
assert(nIn < txTo.vin.size());
}
// Test solution
- return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, 0);
+ return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STRICT_FLAGS, 0);
}
bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType)
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType);
}
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType)
-{
- assert(nIn < txTo.vin.size());
- const CTxIn& txin = txTo.vin[nIn];
- if (txin.prevout.n >= txFrom.vout.size())
- return false;
- const CTxOut& txout = txFrom.vout[txin.prevout.n];
-
- if (txin.prevout.hash != txFrom.GetHash())
- return false;
-
- return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType);
-}
-
static CScript PushAll(const vector<valtype>& values)
{
CScript result;
if (sigs.count(pubkey))
continue; // Already got a sig for this pubkey
- if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0))
+ if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0))
{
sigs[pubkey] = sig;
break;
Solver(scriptPubKey, txType, vSolutions);
vector<valtype> stack1;
- EvalScript(stack1, scriptSig1, CTransaction(), 0, 0);
+ EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0);
vector<valtype> stack2;
- EvalScript(stack2, scriptSig2, CTransaction(), 0, 0);
+ EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0);
return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2);
}
#include <vector>
#include <boost/foreach.hpp>
-#include <boost/variant.hpp>
#include "keystore.h"
#include "bignum.h"
class CTransaction;
+static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
+
+/** IsMine() return codes */
+enum isminetype
+{
+ MINE_NO = 0,
+ MINE_WATCH_ONLY = 1,
+ MINE_SPENDABLE = 2,
+};
+
/** Signature hash types/flags */
enum
{
SIGHASH_ANYONECANPAY = 0x80,
};
+/** Script verification flags */
+enum
+{
+ SCRIPT_VERIFY_NONE = 0,
+ SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
+ SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
+ SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values in signatures (depends on STRICTENC)
+ SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
+ SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
+};
+
+// Strict verification:
+//
+// * force DER encoding;
+// * force low S;
+// * ensure that CHECKMULTISIG dummy argument is null.
+static const unsigned int STRICT_FORMAT_FLAGS = SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_NULLDUMMY;
+
+// Mandatory script verification flags that all new blocks must comply with for
+// them to be valid. (but old blocks may not comply with) Currently just P2SH,
+// but in the future other flags may be added, such as a soft-fork to enforce
+// strict DER encoding.
+//
+// Failing one of these tests may trigger a DoS ban - see ConnectInputs() for
+// details.
+static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
+
+// Standard script verification flags that standard transactions will comply
+// with. However scripts violating these flags may still be present in valid
+// blocks and we must accept those blocks.
+static const unsigned int STRICT_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | STRICT_FORMAT_FLAGS;
+
+// Soft verifications, no extended signature format checkings
+static const unsigned int SOFT_FLAGS = STRICT_FLAGS & ~STRICT_FORMAT_FLAGS;
enum txnouttype
{
TX_NULL_DATA,
};
-class CNoDestination {
-public:
- friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; }
- friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
-};
-
-/** A txout script template with a specific destination. It is either:
- * * CNoDestination: no destination set
- * * CKeyID: TX_PUBKEYHASH destination
- * * CScriptID: TX_SCRIPTHASH destination
- * A CTxDestination is the internal data type encoded in a CBitcoinAddress
- */
-typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
-
const char* GetTxnOutputType(txnouttype t);
/** Script opcodes */
}
};
+bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags);
+bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags);
-
-
-bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
-bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
-bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination &dest);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
- bool fValidatePayToScriptHash, int nHashType);
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType);
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
// combine them intelligently and return the result.
static const unsigned int FEE_SWITCH_TIME = 1405814400; // Sun, 20 Jul 2014 00:00:00 GMT
static const unsigned int VALIDATION_SWITCH_TIME = 1408492800; // Wed, 20 Aug 2014 00:00:00 GMT
+static const unsigned int SIG_SWITCH_TIME = 1411171200; // Sat, 20 Sep 2014 00:00:00 GMT
#endif
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
-#include <openssl/sha.h>
-#include <openssl/ripemd.h>
-
#include "netbase.h" // for AddTimeData
typedef long long int64;
*/
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
-
-
-
-
-
-
-
-
-template<typename T1>
-inline uint256 Hash(const T1 pbegin, const T1 pend)
-{
- static unsigned char pblank[1];
- uint256 hash1;
- SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
- uint256 hash2;
- SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
- return hash2;
-}
-
-class CHashWriter
-{
-private:
- SHA256_CTX ctx;
-
-public:
- int nType;
- int nVersion;
-
- void Init() {
- SHA256_Init(&ctx);
- }
-
- CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {
- Init();
- }
-
- CHashWriter& write(const char *pch, size_t size) {
- SHA256_Update(&ctx, pch, size);
- return (*this);
- }
-
- // invalidates the object
- uint256 GetHash() {
- uint256 hash1;
- SHA256_Final((unsigned char*)&hash1, &ctx);
- uint256 hash2;
- SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
- return hash2;
- }
-
- template<typename T>
- CHashWriter& operator<<(const T& obj) {
- // Serialize to this stream
- ::Serialize(*this, obj, nType, nVersion);
- return (*this);
- }
-};
-
-
-template<typename T1, typename T2>
-inline uint256 Hash(const T1 p1begin, const T1 p1end,
- const T2 p2begin, const T2 p2end)
-{
- static unsigned char pblank[1];
- uint256 hash1;
- SHA256_CTX ctx;
- SHA256_Init(&ctx);
- SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
- SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
- SHA256_Final((unsigned char*)&hash1, &ctx);
- uint256 hash2;
- SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
- return hash2;
-}
-
-template<typename T1, typename T2, typename T3>
-inline uint256 Hash(const T1 p1begin, const T1 p1end,
- const T2 p2begin, const T2 p2end,
- const T3 p3begin, const T3 p3end)
-{
- static unsigned char pblank[1];
- uint256 hash1;
- SHA256_CTX ctx;
- SHA256_Init(&ctx);
- SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
- SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
- SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
- SHA256_Final((unsigned char*)&hash1, &ctx);
- uint256 hash2;
- SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
- return hash2;
-}
-
-template<typename T>
-uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
-{
- CHashWriter ss(nType, nVersion);
- ss << obj;
- return ss.GetHash();
-}
-
-inline uint160 Hash160(const std::vector<unsigned char>& vch)
-{
- uint256 hash1;
- SHA256(&vch[0], vch.size(), (unsigned char*)&hash1);
- uint160 hash2;
- RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
- return hash2;
-}
-
/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
+bool CWallet::AddWatchOnly(const CTxDestination &dest)
+{
+ if (!CCryptoKeyStore::AddWatchOnly(dest))
+ return false;
+ nTimeFirstKey = 1; // No birthday information for watch-only keys.
+ if (!fFileBacked)
+ return true;
+ return CWalletDB(strWalletFile).WriteWatchOnly(dest);
+}
+
+bool CWallet::LoadWatchOnly(const CTxDestination &dest)
+{
+ printf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str());
+ return CCryptoKeyStore::AddWatchOnly(dest);
+}
+
// ppcoin: optional setting to unlock wallet for block minting only;
// serves to disable the trivial sendmoney when OS account compromised
bool fWalletUnlockMintOnly = false;
printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
- printf("WalletUpdateSpent found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("WalletUpdateSpent found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(false)).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
}
-bool CWallet::IsMine(const CTxIn &txin) const
+isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
- if (IsMine(prev.vout[txin.prevout.n]))
- return true;
+ return IsMine(prev.vout[txin.prevout.n]);
}
}
- return false;
+ return MINE_NO;
}
int64 CWallet::GetDebit(const CTxIn &txin) const
if (GetBlocksToMaturity() > 0)
nGeneratedImmature = pwallet->GetCredit(*this);
else
- nGeneratedMature = GetCredit();
+ nGeneratedMature = GetCredit(false);
return;
}
}
if (fUpdated)
{
- printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(false)).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk();
}
{
const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableCredit();
+ nTotal += pcoin->GetAvailableCredit(false);
}
}
return nTotal;
}
+void CWallet::GetBalance(int64 &nTotal, int64 &nWatchOnly) const
+{
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsTrusted()) {
+ nWatchOnly += pcoin->GetAvailableCredit(true);
+ nTotal += pcoin->GetAvailableCredit(false);
+ }
+ }
+ }
+}
+
int64 CWallet::GetUnconfirmedBalance() const
{
int64 nTotal = 0;
{
const CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || !pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableCredit();
+ nTotal += pcoin->GetAvailableCredit(false);
}
}
return nTotal;
if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0)
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue &&
- (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
- vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
-
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
+ isminetype mine = IsMine(pcoin->vout[i]);
+ if (!(pcoin->IsSpent(i)) && mine != MINE_NO &&
+ pcoin->vout[i].nValue >= nMinimumInputValue &&
+ (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
+ {
+ vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE));
+ }
+ }
}
}
}
if(pcoin->GetDepthInMainChain() < nConf)
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue)
- vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
+ isminetype mine = IsMine(pcoin->vout[i]);
+
+ if (!(pcoin->IsSpent(i)) && mine != MINE_NO && pcoin->vout[i].nValue >= nMinimumInputValue)
+ vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE));
+ }
}
}
}
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
- BOOST_FOREACH(COutput output, vCoins)
+ BOOST_FOREACH(const COutput &output, vCoins)
{
+ if (!output.fSpendable)
+ continue;
+
const CWalletTx *pcoin = output.tx;
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
{
BOOST_FOREACH(const COutput& out, vCoins)
{
+ if(!out.fSpendable)
+ continue;
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
BOOST_FOREACH(COutput output, vCoins)
{
+ if(!output.fSpendable)
+ continue;
const CWalletTx *pcoin = output.tx;
int i = output.i;
if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
- printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(false));
}
if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()];
- printf(" stake: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" stake: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(false));
}
}
bool AddCScript(const CScript& redeemScript);
bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); }
+ // Adds a watch-only address to the store, and saves it to disk.
+ bool AddWatchOnly(const CTxDestination &dest);
+ // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
+ bool LoadWatchOnly(const CTxDestination &dest);
+
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
void ReacceptWalletTransactions();
void ResendWalletTransactions();
int64 GetBalance() const;
+ void GetBalance(int64 &nTotal, int64 &nWatchOnly) const;
int64 GetUnconfirmedBalance() const;
int64 GetImmatureBalance() const;
int64 GetStake() const;
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, int64> GetAddressBalances();
- bool IsMine(const CTxIn& txin) const;
+ isminetype IsMine(const CTxIn& txin) const;
int64 GetDebit(const CTxIn& txin) const;
- bool IsMine(const CTxOut& txout) const
+ isminetype IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
- int64 GetCredit(const CTxOut& txout) const
+ int64 GetCredit(const CTxOut& txout, bool fWatchOnly=false) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
- return (IsMine(txout) ? txout.nValue : 0);
+ isminetype ismine = IsMine(txout);
+ if (fWatchOnly && ismine != MINE_WATCH_ONLY)
+ return 0;
+ return (ismine != MINE_NO ? txout.nValue : 0);
}
bool IsChange(const CTxOut& txout) const;
int64 GetChange(const CTxOut& txout) const
}
return nDebit;
}
- int64 GetCredit(const CTransaction& tx) const
+ int64 GetCredit(const CTransaction& tx, bool fWatchOnly=true) const
{
int64 nCredit = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
- nCredit += GetCredit(txout);
+ if (!fWatchOnly || (fWatchOnly && IsMine(txout) == MINE_WATCH_ONLY))
+ nCredit += GetCredit(txout);
+
if (!MoneyRange(nCredit))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
}
// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
+ mutable bool fWatchOnlyCreditCached;
mutable bool fAvailableCreditCached;
+ mutable bool fAvailableWatchOnlyCreditCached;
mutable bool fChangeCached;
mutable int64 nDebitCached;
mutable int64 nCreditCached;
+ mutable int64 nWatchOnlyCreditCached;
mutable int64 nAvailableCreditCached;
+ mutable int64 nAvailableWatchOnlyCreditCached;
mutable int64 nChangeCached;
CWalletTx()
vfSpent.clear();
fDebitCached = false;
fCreditCached = false;
+ fWatchOnlyCreditCached = false;
fAvailableCreditCached = false;
+ fAvailableWatchOnlyCreditCached = false;
fChangeCached = false;
nDebitCached = 0;
nCreditCached = 0;
+ nWatchOnlyCreditCached = 0;
nAvailableCreditCached = 0;
+ nAvailableWatchOnlyCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
{
vfSpent[i] = true;
fReturn = true;
- fAvailableCreditCached = false;
+ fAvailableCreditCached = fAvailableWatchOnlyCreditCached = false;
}
}
return fReturn;
void MarkDirty()
{
fCreditCached = false;
- fAvailableCreditCached = false;
+ fAvailableCreditCached = fAvailableWatchOnlyCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
if (!vfSpent[nOut])
{
vfSpent[nOut] = true;
- fAvailableCreditCached = false;
+ fAvailableCreditCached = fAvailableWatchOnlyCreditCached = false;
}
}
if (vfSpent[nOut])
{
vfSpent[nOut] = false;
- fAvailableCreditCached = false;
+ fAvailableCreditCached = fAvailableWatchOnlyCreditCached = false;
}
}
return nDebitCached;
}
- int64 GetCredit(bool fUseCache=true) const
+ int64 GetCredit(bool fWatchOnly, bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
// GetBalance can assume transactions in mapWallet won't change
- if (fUseCache && fCreditCached)
- return nCreditCached;
+ if (fUseCache) {
+ if (fWatchOnly && fCreditCached)
+ return nWatchOnlyCreditCached;
+ if (fCreditCached)
+ return nCreditCached;
+ }
+
+ if (fWatchOnly) {
+ nWatchOnlyCreditCached = pwallet->GetCredit(*this, true);
+ fWatchOnlyCreditCached = true;
+
+ return nWatchOnlyCreditCached;
+ }
+
nCreditCached = pwallet->GetCredit(*this);
fCreditCached = true;
+
return nCreditCached;
}
- int64 GetAvailableCredit(bool fUseCache=true) const
+ int64 GetAvailableCredit(bool fWatchOnly, bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
- if (fUseCache && fAvailableCreditCached)
- return nAvailableCreditCached;
+ if (fUseCache) {
+ if (fWatchOnly && fAvailableWatchOnlyCreditCached)
+ return nAvailableWatchOnlyCreditCached;
+
+ if (fAvailableCreditCached)
+ return nAvailableCreditCached;
+ }
int64 nCredit = 0;
for (unsigned int i = 0; i < vout.size(); i++)
if (!IsSpent(i))
{
const CTxOut &txout = vout[i];
- nCredit += pwallet->GetCredit(txout);
+ nCredit += pwallet->GetCredit(txout, fWatchOnly);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
}
- nAvailableCreditCached = nCredit;
- fAvailableCreditCached = true;
+ if (fWatchOnly) {
+ nAvailableWatchOnlyCreditCached = nCredit;
+ fAvailableWatchOnlyCreditCached = true;
+ } else {
+ nAvailableCreditCached = nCredit;
+ fAvailableCreditCached = true;
+ }
return nCredit;
}
const CWalletTx *tx;
int i;
int nDepth;
+ bool fSpendable;
- COutput(const CWalletTx *txIn, int iIn, int nDepthIn)
+ COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn)
{
- tx = txIn; i = iIn; nDepth = nDepthIn;
+ tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn;
}
std::string ToString() const
{
- return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str());
+ return strprintf("COutput(%s, %d, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, fSpendable, nDepth, FormatMoney(tx->vout[i].nValue).c_str());
}
void print() const
wss.fAnyUnordered = true;
}
}
+ else if (strType == "watch")
+ {
+ std::string strAddress;
+ ssKey >> strAddress;
+ char fYes;
+ ssValue >> fYes;
+ if (fYes == '1')
+ pwallet->LoadWatchOnly(CBitcoinAddress(strAddress).Get());
+
+ // Watch-only addresses have no birthday information for now,
+ // so set the wallet birthday to the beginning of time.
+ pwallet->nTimeFirstKey = 1;
+ }
else if (strType == "key" || strType == "wkey")
{
vector<unsigned char> vchPubKey;
void ThreadFlushWalletDB(void* parg)
{
// Make this thread recognisable as the wallet flushing thread
- RenameThread("bitcoin-wallet");
+ RenameThread("novacoin-wallet");
const string& strFile = ((const string*)parg)[0];
static bool fOneThread;
#include "db.h"
#include "base58.h"
+#include "keystore.h"
class CKeyPool;
class CAccount;
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
+ bool WriteWatchOnly(const CTxDestination &dest)
+ {
+ nWalletDBUpdated++;
+ return Write(std::make_pair(std::string("watch"), CBitcoinAddress(dest).ToString()), '1');
+ }
+
bool WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
#include "../serialize.h"
#include "../bignum.h"
-#include "../util.h"
+#include "../hash.h"
#include "Params.h"
#include "Coin.h"
#include "Commitment.h"