// 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 <boost/algorithm/string/replace.hpp>
using namespace std;
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);
{
uint256 hash = tx.GetHash();
if (!txdb.ContainsTx(hash))
- RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
+ RelayTransaction((CTransaction)tx, hash);
}
}
if (!(IsCoinBase() || IsCoinStake()))
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);
}
}
}
}
// populate vCoins with vector of spendable COutputs
-void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed) const
+void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
{
vCoins.clear();
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()));
+
}
}
}
return true;
}
-bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const
{
vector<COutput> 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<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
+bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl)
{
int64 nValue = 0;
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
CTxDB txdb("r");
{
nFeeRet = nTransactionFee;
- loop
+ while (true)
{
wtxNew.vin.clear();
wtxNew.vout.clear();
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > 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)
{
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<CNoDestination>(&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<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
// 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))
{
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<CScript, int64> > 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);
if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
{
- error("GetStakeMintPower : invalid reserve balance amount");
+ error("GetStakeWeight : invalid reserve balance amount");
return 0;
}
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())
- continue;
- if (pcoin.first->nTime + nStakeMinAge > GetTime())
+ if (nTime + nStakeMaxAge < 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
+ //
+ // Maximum TimeWeight is 90 days.
+ nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge);
- nCoinAge += bnCoinDay.getuint64();
+ CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60);
+
+ nCoinAge += bnCoinDayWeight.getuint64();
}
if (fDebug && GetBoolArg("-printcoinage"))
- printf("StakePower bnCoinDay=%"PRI64d"\n", nCoinAge);
+ printf("StakeWeight bnCoinDay=%"PRI64d"\n", nCoinAge);
return nCoinAge;
}
{
// 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"))
}
int64 nMinFee = 0;
- loop
+ while (true)
{
// Set output amount
if (txNew.vout.size() == 3)
return true;
}
-bool CWallet::TopUpKeyPool()
+bool CWallet::TopUpKeyPool(unsigned int nSize)
{
{
LOCK(cs_wallet);
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;