X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=3c28c0a38971f445109605bf15a3b6c2ecee9e2a;hb=c0e8991ed1b482d0dab75f86e415fc63d1104d68;hp=998857777fdd5be2ec9cc1f5374f2121bd6f1a13;hpb=5d2c41f9d678971c9b6fff9de6348a56d925720a;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index 9988577..3c28c0a 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -3,12 +3,15 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "txdb.h" #include "wallet.h" #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" #include "base58.h" #include "kernel.h" +#include "coincontrol.h" +#include using namespace std; extern int nStakeMaxAge; @@ -246,7 +249,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); - CMasterKey kMasterKey; + CMasterKey kMasterKey(nDerivationMethodIndex); RandAddSeedPerfmon(); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); @@ -501,6 +504,16 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } return true; } @@ -892,7 +905,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) { uint256 hash = tx.GetHash(); if (!txdb.ContainsTx(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + RelayTransaction((CTransaction)tx, hash); } } if (!(IsCoinBase() || IsCoinStake())) @@ -901,7 +914,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) if (!txdb.ContainsTx(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); - RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + RelayTransaction((CTransaction)*this, hash); } } } @@ -1014,7 +1027,7 @@ int64 CWallet::GetImmatureBalance() const } // populate vCoins with vector of spendable COutputs -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); @@ -1037,8 +1050,10 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) + 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())); + } } } @@ -1213,20 +1228,28 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in return true; } -bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const { vector vCoins; - AvailableCoins(vCoins); + AvailableCoins(vCoins, true, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet)); } - - - -bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) @@ -1246,7 +1269,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW CTxDB txdb("r"); { nFeeRet = nTransactionFee; - loop + while (true) { wtxNew.vin.clear(); wtxNew.vout.clear(); @@ -1261,7 +1284,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Choose coins to use set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn)) + if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { @@ -1289,22 +1312,30 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW if (nChange > 0) { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey = reservekey.GetReservedKey(); - // assert(mapKeys.count(vchPubKey)); - // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; - scriptChange.SetDestination(vchPubKey.GetID()); + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey = reservekey.GetReservedKey(); + + scriptChange.SetDestination(vchPubKey.GetID()); + } // Insert change txn at random position: vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); @@ -1331,7 +1362,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); + int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND, nBytes); if (nFeeRet < max(nPayFee, nMinFee)) { @@ -1350,15 +1381,15 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl); } -// NovaCoin: get current stake generation power -uint64 CWallet::GetStakeMintPower(const CKeyStore& keystore, enum StakeWeightMode mode) +// NovaCoin: get current stake weight +uint64 CWallet::GetStakeWeight(const CKeyStore& keystore, enum StakeWeightMode mode) { LOCK2(cs_main, cs_wallet); @@ -1369,7 +1400,7 @@ uint64 CWallet::GetStakeMintPower(const CKeyStore& keystore, enum StakeWeightMod if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) { - error("CreateCoinStake : invalid reserve balance amount"); + error("GetStakeWeight : invalid reserve balance amount"); return 0; } @@ -1391,41 +1422,53 @@ uint64 CWallet::GetStakeMintPower(const CKeyStore& keystore, enum StakeWeightMod if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; + unsigned int nTime = pcoin.first->nTime; + switch(mode) { case STAKE_NORMAL: // Do not count input that is still less than 30 days old - if (pcoin.first->nTime + nStakeMinAge > GetTime()) + if (nTime + nStakeMinAge > GetTime()) continue; break; case STAKE_MAXWEIGHT: // Do not count input that is still less than 90 days old - if (pcoin.first->nTime + nStakeMaxAge > GetTime()) + if (nTime + nStakeMaxAge > GetTime()) continue; break; case STAKE_MINWEIGHT: // Count only inputs with suitable age (from 30 to 90 days old) - if (pcoin.first->nTime + nStakeMaxAge < GetTime()) + if (nTime + nStakeMaxAge < GetTime()) continue; - if (pcoin.first->nTime + nStakeMinAge > GetTime()) - continue; - break; - case STAKE_BELOWMIN: - // Count only inputs with suitable age (less than 30 days old) - if (pcoin.first->nTime + nStakeMinAge < GetTime()) + if (nTime + nStakeMinAge > GetTime()) continue; break; } - CBigNum bnCentSecond = CBigNum(pcoin.first->vout[pcoin.second].nValue) * (GetTime()-pcoin.first->nTime) / CENT; - CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); + int64 nTimeWeight; + // Kernel hash weight starts from 0 at the 30-day min age + // this change increases active coins participating the hash and helps + // to secure the network when proof-of-stake difficulty is low + // + if(fTestNet || (STAKEWEIGHT_SWITCH_TIME < nTime)) + { + // New rule since 01 Jan 2014: Maximum TimeWeight is 90 days. + nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge); + } + else + { + // Current rule: Maximum TimeWeight is 60 days. + nTimeWeight = min((int64)GetTime() - nTime, (int64)nStakeMaxAge) - nStakeMinAge; + } + + CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); - nCoinAge += bnCoinDay.getuint64(); + nCoinAge += bnCoinDayWeight.getuint64(); } if (fDebug && GetBoolArg("-printcoinage")) - printf("StakePower bnCoinDay=%"PRI64d"\n", nCoinAge); + printf("StakeWeight bnCoinDay=%"PRI64d"\n", nCoinAge); return nCoinAge; } @@ -1484,9 +1527,9 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int { // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval - uint256 hashProofOfStake = 0; + uint256 hashProofOfStake = 0, targetProofOfStake = 0; COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); - if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake)) + if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake, targetProofOfStake)) { // Found a kernel if (fDebug && GetBoolArg("-printcoinstake")) @@ -1579,7 +1622,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int } int64 nMinFee = 0; - loop + while (true) { // Set output amount if (txNew.vout.size() == 3) @@ -1857,7 +1900,7 @@ bool CWallet::NewKeyPool() return true; } -bool CWallet::TopUpKeyPool() +bool CWallet::TopUpKeyPool(unsigned int nSize) { { LOCK(cs_wallet); @@ -1868,7 +1911,12 @@ bool CWallet::TopUpKeyPool() CWalletDB walletdb(strWalletFile); // Top up key pool - unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL); + unsigned int nTargetSize; + if (nSize > 0) + nTargetSize = nSize; + else + nTargetSize = max(GetArg("-keypool", 100), 0LL); + while (setKeyPool.size() < (nTargetSize + 1)) { int64 nEnd = 1;