#include "main.h"
using namespace std;
-
-
-bool fCoinsDataActual;
+extern int64_t nReserveBalance;
//////////////////////////////////////////////////////////////////////////////
//
return key.GetPubKey();
}
+CMalleableKeyView CWallet::GenerateNewMalleableKey()
+{
+ RandAddSeedPerfmon();
+
+ // Compressed public keys were introduced in version 0.6.0
+ SetMinVersion(FEATURE_MALLKEY);
+
+ CMalleableKey mKey;
+ mKey.MakeNewKeys();
+ const CMalleableKeyView &keyView(mKey);
+
+ // Create new metadata
+ int64_t nCreationTime = GetTime();
+ mapMalleableKeyMetadata[keyView] = CKeyMetadata(nCreationTime);
+ if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
+ nTimeFirstKey = nCreationTime;
+
+ if (!AddMalleableKey(mKey))
+ throw std::runtime_error("CWallet::GenerateNewMalleableKey() : AddMalleableKey failed");
+ return CMalleableKeyView(mKey);
+}
+
bool CWallet::AddKey(const CKey& key)
{
CPubKey pubkey = key.GetPubKey();
return true;
}
+bool CWallet::AddMalleableKey(const CMalleableKey& mKey)
+{
+ CMalleableKeyView keyView = CMalleableKeyView(mKey);
+ CSecret vchSecretH = mKey.GetSecretH();
+ if (!CCryptoKeyStore::AddMalleableKey(keyView, vchSecretH))
+ return false;
+ if (!fFileBacked)
+ return true;
+ if (!IsCrypted())
+ return CWalletDB(strWalletFile).WriteMalleableKey(keyView, vchSecretH, mapMalleableKeyMetadata[keyView]);
+ return true;
+}
+
+bool CWallet::AddCryptedMalleableKey(const CMalleableKeyView& keyView, const std::vector<unsigned char> &vchCryptedSecretH)
+{
+ if (!CCryptoKeyStore::AddCryptedMalleableKey(keyView, vchCryptedSecretH))
+ return false;
+
+ if (!fFileBacked)
+ return true;
+
+ {
+ LOCK(cs_wallet);
+ if (pwalletdbEncryption)
+ return pwalletdbEncryption->WriteCryptedMalleableKey(keyView, vchCryptedSecretH, mapMalleableKeyMetadata[keyView]);
+ else
+ return CWalletDB(strWalletFile).WriteCryptedMalleableKey(keyView, vchCryptedSecretH, mapMalleableKeyMetadata[keyView]);
+ }
+
+ return true;
+}
+
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return true;
}
+bool CWallet::LoadMalleableKeyMetadata(const CMalleableKeyView &keyView, const CKeyMetadata &metadata)
+{
+ if (metadata.nCreateTime && (!nTimeFirstKey || metadata.nCreateTime < nTimeFirstKey))
+ nTimeFirstKey = metadata.nCreateTime;
+
+ mapMalleableKeyMetadata[keyView] = metadata;
+ return true;
+}
+
bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
{
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
- pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
+ double nFirstMultiplier = 1e2 / (GetTimeMillis() - nStartTime);
+ pMasterKey.second.nDeriveIterations = (uint32_t)(pMasterKey.second.nDeriveIterations *nFirstMultiplier);
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
- pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+ double nSecondMultiplier = 1e2 / (GetTimeMillis() - nStartTime);
+ pMasterKey.second.nDeriveIterations = (uint32_t)((pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * nSecondMultiplier) / 2);
if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000;
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
+ int64_t nDivider = GetTimeMillis() - nStartTime;
+ kMasterKey.nDeriveIterations = (uint32_t)(25e5 / (double)(nDivider));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+ double nMultiplier = 1e2 / (GetTimeMillis() - nStartTime);
+ kMasterKey.nDeriveIterations = (uint32_t)((kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * nMultiplier) / 2);
if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000;
mi++;
}
+ MalleableKeyMap::const_iterator mi2 = mapMalleableKeys.begin();
+ while (mi2 != mapMalleableKeys.end())
+ {
+ const CSecret &vchSecretH = mi2->second;
+ const CMalleableKeyView &keyView = mi2->first;
+ pwalletdbDecryption->EraseCryptedMalleableKey(keyView);
+ pwalletdbDecryption->WriteMalleableKey(keyView, vchSecretH, mapMalleableKeyMetadata[keyView]);
+ mi2++;
+ }
+
// Erase master keys
MasterKeyMap::const_iterator mk = mapMasterKeys.begin();
while (mk != mapMasterKeys.end())
uint256 hash = tx.GetHash();
{
LOCK(cs_wallet);
- bool fExisted = mapWallet.count(hash);
+ bool fExisted = mapWallet.count(hash) != 0;
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
RelayWalletTransaction(txdb);
}
-void CWallet::ResendWalletTransactions()
+void CWallet::ResendWalletTransactions(bool fForceResend)
{
- // Do this infrequently and randomly to avoid giving away
- // that these are our transactions.
- static int64_t nNextTime = GetRand(GetTime() + 30 * 60);
- if (GetTime() < nNextTime)
- return;
- bool fFirst = (nNextTime == 0);
- nNextTime = GetTime() + GetRand(30 * 60);
- if (fFirst)
- return;
+ if (!fForceResend) {
+ // Do this infrequently and randomly to avoid giving away
+ // that these are our transactions.
+ static int64_t nNextTime = GetRand(GetTime() + 30 * 60);
+ if (GetTime() < nNextTime)
+ return;
+ bool fFirst = (nNextTime == 0);
+ nNextTime = GetTime() + GetRand(30 * 60);
+ if (fFirst)
+ return;
- // Only do it if there's been a new block since last time
- static int64_t nLastTime = 0;
- if (nTimeBestReceived < nLastTime)
- return;
- nLastTime = GetTime();
+ // Only do it if there's been a new block since last time
+ static int64_t nLastTime = 0;
+ if (nTimeBestReceived < nLastTime)
+ return;
+ nLastTime = GetTime();
+ }
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
CWalletTx& wtx = item.second;
// Don't rebroadcast until it's had plenty of time that
// it should have gotten in already by now.
- if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60)
+ if (fForceResend || nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60)
mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
}
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
CScript scriptChange;
// coin control: send change to custom address
- if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
- scriptChange.SetDestination(coinControl->destChange);
+ if (coinControl && coinControl->destChange.IsValid())
+ scriptChange.SetAddress(coinControl->destChange);
// no coin control: send change to newly generated address
else
void CWallet::GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight)
{
- int64_t nTimeWeight = GetWeight(nTime, (int64_t)GetTime());
+ int64_t nTimeWeight = GetWeight(nTime, GetTime());
// If time weight is lower or equal to zero then weight is zero.
if (nTimeWeight <= 0)
return;
}
- CBigNum bnCoinDayWeight = CBigNum(nValue) * nTimeWeight / COIN / (24 * 60 * 60);
+ CBigNum bnCoinDayWeight = CBigNum(nValue) * nTimeWeight / COIN / nOneDay;
nWeight = bnCoinDayWeight.getuint64();
}
-
-// NovaCoin: get current stake miner statistics
-void CWallet::GetStakeStats(float &nKernelsRate, float &nCoinDaysRate)
-{
- static uint64_t nLastKernels = 0, nLastCoinDays = 0;
- static float nLastKernelsRate = 0, nLastCoinDaysRate = 0;
- static int64_t nLastTime = GetTime();
-
- if (nKernelsTried < nLastKernels)
- {
- nLastKernels = 0;
- nLastCoinDays = 0;
-
- nLastTime = GetTime();
- }
-
- int64_t nInterval = GetTime() - nLastTime;
- //if (nKernelsTried > 1000 && nInterval > 5)
- if (nInterval > 10)
- {
- nKernelsRate = nLastKernelsRate = ( nKernelsTried - nLastKernels ) / (float) nInterval;
- nCoinDaysRate = nLastCoinDaysRate = ( nCoinDaysTried - nLastCoinDays ) / (float) nInterval;
-
- nLastKernels = nKernelsTried;
- nLastCoinDays = nCoinDaysTried;
- nLastTime = GetTime();
- }
- else
- {
- nKernelsRate = nLastKernelsRate;
- nCoinDaysRate = nLastCoinDaysRate;
- }
-}
-
bool CWallet::MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nOutputValue, list<uint256>& listMerged)
{
int64_t nBalance = GetBalance();
return true;
}
-
-bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, uint32_t nSearchInterval, CTransaction& txNew, CKey& key)
+bool CWallet::CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nGenerationTime, uint32_t nBits, CTransaction &txNew, CKey& key)
{
- // The following combine threshold is important to security
- // Should not be adjusted if you don't understand the consequences
- int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3;
-
- CBigNum bnTargetPerCoinDay;
- bnTargetPerCoinDay.SetCompact(nBits);
-
- txNew.vin.clear();
- txNew.vout.clear();
-
- // Mark coin stake transaction
- CScript scriptEmpty;
- scriptEmpty.clear();
- txNew.vout.push_back(CTxOut(0, scriptEmpty));
-
- // Choose coins to use
- int64_t nBalance = GetBalance();
- int64_t nReserveBalance = 0;
+ CWalletTx wtx;
+ if (!GetTransaction(hashTx, wtx))
+ return error("Transaction %s is not found\n", hashTx.GetHex().c_str());
- if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
- return error("CreateCoinStake : invalid reserve balance amount");
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ CScript scriptPubKeyOut;
+ CScript scriptPubKeyKernel = wtx.vout[nOut].scriptPubKey;
+ if (!Solver(scriptPubKeyKernel, whichType, vSolutions))
+ return error("CreateCoinStake : failed to parse kernel\n");
- if (nBalance <= nReserveBalance)
- return false;
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : parsed kernel type=%d\n", whichType);
- vector<const CWalletTx*> vwtxPrev;
+ if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
+ return error("CreateCoinStake : no support for kernel type=%d\n", whichType);
- CTxDB txdb("r");
+ if (whichType == TX_PUBKEYHASH) // pay to address type
{
- LOCK2(cs_main, cs_wallet);
- // Cache outputs unless best block or wallet transaction set changed
- if (!fCoinsDataActual)
- {
- mapMeta.clear();
- int64_t nValueIn = 0;
- CoinsSet setCoins;
- if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
- return false;
-
- if (setCoins.empty())
- return false;
-
- {
- CTxIndex txindex;
- CBlock block;
- for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++)
- {
- // Load transaction index item
- if (!txdb.ReadTxIndex(pcoin->first->GetHash(), txindex))
- continue;
-
- // Read block header
- if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- continue;
+ // convert to pay to public key type
+ if (!GetKey(uint160(vSolutions[0]), key))
+ return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
- uint64_t nStakeModifier = 0;
- if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
- continue;
-
- // Add meta record
- // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier))
- mapMeta[make_pair(pcoin->first->GetHash(), pcoin->second)] = make_pair(make_pair(txindex, *pcoin), make_pair(block, nStakeModifier));
-
- if (fDebug)
- printf("Load coin: %s\n", pcoin->first->GetHash().GetHex().c_str());
- }
- }
+ scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
+ }
+ if (whichType == TX_PUBKEY)
+ {
+ valtype& vchPubKey = vSolutions[0];
+ if (!GetKey(Hash160(vchPubKey), key))
+ return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
+ if (key.GetPubKey() != vchPubKey)
+ return error("CreateCoinStake : invalid key for kernel type=%d\n", whichType); // keys mismatch
+ scriptPubKeyOut = scriptPubKeyKernel;
+ }
- if (fDebug)
- printf("Stake miner: %" PRIszu " meta items loaded for %" PRIszu " coins\n", mapMeta.size(), setCoins.size());
+ // The following combine threshold is important to security
+ // Should not be adjusted if you don't understand the consequences
+ int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3;
- fCoinsDataActual = true;
- nKernelsTried = 0;
- nCoinDaysTried = 0;
- }
- }
+ int64_t nBalance = GetBalance();
+ int64_t nCredit = wtx.vout[nOut].nValue;
- int64_t nCredit = 0;
- CScript scriptPubKeyKernel;
+ txNew.vin.clear();
+ txNew.vout.clear();
- unsigned int nTimeTx, nBlockTime;
- COutPoint prevoutStake;
- CoinsSet::value_type kernelcoin;
+ // List of constake dependencies
+ vector<const CWalletTx*> vwtxPrev;
+ vwtxPrev.push_back(&wtx);
- if (ScanForStakeKernelHash(mapMeta, nBits, txNew.nTime, nSearchInterval, kernelcoin, nTimeTx, nBlockTime, nKernelsTried, nCoinDaysTried))
- {
- // Found a kernel
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : kernel found\n");
- vector<valtype> vSolutions;
- txnouttype whichType;
- CScript scriptPubKeyOut;
- scriptPubKeyKernel = kernelcoin.first->vout[kernelcoin.second].scriptPubKey;
- if (!Solver(scriptPubKeyKernel, whichType, vSolutions))
- {
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : failed to parse kernel\n");
- return false;
- }
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : parsed kernel type=%d\n", whichType);
- if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
- {
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : no support for kernel type=%d\n", whichType);
- return false; // only support pay to public key and pay to address
- }
- if (whichType == TX_PUBKEYHASH) // pay to address type
- {
- // convert to pay to public key type
- if (!keystore.GetKey(uint160(vSolutions[0]), key))
- {
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
- return false; // unable to find corresponding public key
- }
- scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
- }
- if (whichType == TX_PUBKEY)
- {
- valtype& vchPubKey = vSolutions[0];
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- {
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
- return false; // unable to find corresponding public key
- }
- if (key.GetPubKey() != vchPubKey)
- {
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : invalid key for kernel type=%d\n", whichType);
- return false; // keys mismatch
- }
+ // Set generation time, and kernel input
+ txNew.nTime = nGenerationTime;
+ txNew.vin.push_back(CTxIn(hashTx, nOut));
- scriptPubKeyOut = scriptPubKeyKernel;
- }
+ // Mark coin stake transaction with empty vout[0]
+ CScript scriptEmpty;
+ scriptEmpty.clear();
+ txNew.vout.push_back(CTxOut(0, scriptEmpty));
- txNew.nTime = nTimeTx;
- txNew.vin.push_back(CTxIn(kernelcoin.first->GetHash(), kernelcoin.second));
- nCredit += kernelcoin.first->vout[kernelcoin.second].nValue;
- vwtxPrev.push_back(kernelcoin.first);
- txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : added kernel type=%d\n", whichType);
- if (GetWeight((int64_t)nBlockTime, (int64_t)txNew.nTime) < nStakeMaxAge)
- txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake
- if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : added kernel type=%d\n", whichType);
- }
+ int64_t nValueIn = 0;
+ CoinsSet setCoins;
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nGenerationTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
+ return false;
- if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
+ if (setCoins.empty())
return false;
- // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier))
- for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++)
+ bool fDontSplitCoins = false;
+ if (GetWeight((int64_t)wtx.nTime, (int64_t)nGenerationTime) == nStakeMaxAge)
{
- // Get coin
- CoinsSet::value_type pcoin = meta_item->second.first.second;
+ // Only one output for old kernel inputs
+ txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
- // Attempt to add more inputs
- // Only add coins of the same key/address as kernel
- if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey))
- && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
+ // Iterate through set of (wtx*, nout) in order to find some additional inputs for our new coinstake transaction.
+ //
+ // * Value is higher than 0.01 NVC;
+ // * Only add inputs of the same key/address as kernel;
+ // * Input hash and kernel parent hash should be different.
+ for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++)
{
- int64_t nTimeWeight = GetWeight((int64_t)pcoin.first->nTime, (int64_t)txNew.nTime);
-
// Stop adding more inputs if already too many inputs
if (txNew.vin.size() >= 100)
break;
if (nCredit > nCombineThreshold)
break;
// Stop adding inputs if reached reserve limit
- if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance)
+ if (nCredit + pcoin->first->vout[pcoin->second].nValue > nBalance - nReserveBalance)
break;
- // Do not add additional significant input
- if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold)
- continue;
+
+ int64_t nTimeWeight = GetWeight((int64_t)pcoin->first->nTime, (int64_t)nGenerationTime);
+
// Do not add input that is still too young
if (nTimeWeight < nStakeMaxAge)
continue;
+ // Do not add input if key/address is not the same as kernel
+ if (pcoin->first->vout[pcoin->second].scriptPubKey != scriptPubKeyKernel && pcoin->first->vout[pcoin->second].scriptPubKey != txNew.vout[1].scriptPubKey)
+ continue;
+ // Do not add input if parents are the same
+ if (pcoin->first->GetHash() != txNew.vin[0].prevout.hash)
+ continue;
+ // Do not add additional significant input
+ if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold)
+ continue;
- txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
- nCredit += pcoin.first->vout[pcoin.second].nValue;
- vwtxPrev.push_back(pcoin.first);
+ txNew.vin.push_back(CTxIn(pcoin->first->GetHash(), pcoin->second));
+ nCredit += pcoin->first->vout[pcoin->second].nValue;
+ vwtxPrev.push_back(pcoin->first);
}
- }
- // Calculate coin age reward
+ fDontSplitCoins = true;
+ }
+ else
{
- uint64_t nCoinAge;
- CTxDB txdb("r");
- if (!txNew.GetCoinAge(txdb, nCoinAge))
- return error("CreateCoinStake : failed to calculate coin age");
-
- int64_t nReward = GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
- // Refuse to create mint that has zero or negative reward
- if(nReward <= 0)
- return false;
-
- nCredit += nReward;
+ int64_t nSplitThreshold = GetArg("-splitthreshold", nCombineThreshold);
+
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : nSplitThreshold=%" PRId64 "\n", nSplitThreshold);
+
+ if (nCredit > nSplitThreshold)
+ {
+ // Split stake input if credit is lower than combine threshold and maximum weight isn't reached yet
+ txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
+ txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
+
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : splitting coinstake\n");
+ }
+ else
+ {
+ txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
+ fDontSplitCoins = true;
+ }
}
+ // Calculate coin age reward
+ uint64_t nCoinAge;
+ CTxDB txdb("r");
+ if (!txNew.GetCoinAge(txdb, nCoinAge))
+ return error("CreateCoinStake : failed to calculate coin age\n");
+ nCredit += GetProofOfStakeReward(nCoinAge, nBits, nGenerationTime);
+
int64_t nMinFee = 0;
while (true)
{
// Set output amount
- if (txNew.vout.size() == 3)
+ if (fDontSplitCoins)
+ txNew.vout[1].nValue = nCredit - nMinFee;
+ else
{
txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT;
txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue;
}
- else
- txNew.vout[1].nValue = nCredit - nMinFee;
// Sign
int nIn = 0;
BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
{
if (!SignSignature(*this, *pcoin, txNew, nIn++))
- return error("CreateCoinStake : failed to sign coinstake");
+ return error("CreateCoinStake : failed to sign coinstake\n");
}
// Limit size
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE_GEN/5)
- return error("CreateCoinStake : exceeded coinstake size limit");
+ return error("CreateCoinStake : exceeded coinstake size limit\n");
// Check enough fee is paid
if (nMinFee < txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT)
}
}
- // Successfully generated coinstake
+ // Successfully created coinstake
return true;
}
-
// Call after CreateTransaction unless you want to abort
bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
AddToWallet(wtxNew);
// Mark old coins as spent
- set<CWalletTx*> setCoins;
BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee)
{
+ // Check amount
+ if (nValue <= 0)
+ return _("Invalid amount");
+ if (nValue + nTransactionFee > GetBalance())
+ return _("Insufficient funds");
+
CReserveKey reservekey(this);
int64_t nFeeRequired;
return "";
}
-
-
-string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee)
-{
- // Check amount
- if (nValue <= 0)
- return _("Invalid amount");
- if (nValue + nTransactionFee > GetBalance())
- return _("Insufficient funds");
-
- // Parse Bitcoin address
- CScript scriptPubKey;
- scriptPubKey.SetDestination(address);
-
- return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee);
-}
-
-
-
-
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
if (!fFileBacked)
{
std::map<CTxDestination, std::string>::iterator mi = mapAddressBook.find(address);
mapAddressBook[address] = strName;
- NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
+ NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != MINE_NO, (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
if (!fFileBacked)
return false;
return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
bool CWallet::DelAddressBookName(const CTxDestination& address)
{
mapAddressBook.erase(address);
- NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != MINE_NO, CT_DELETED);
if (!fFileBacked)
return false;
return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
{
if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
{
- printf("FixSpentCoins found lost coin %sppc %s[%d], %s\n",
+ printf("FixSpentCoins found lost coin %sppc %s[%u], %s\n",
FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
nMismatchFound++;
nBalanceInQuestion += pcoin->vout[n].nValue;
}
else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
{
- printf("FixSpentCoins found spent coin %sppc %s[%d], %s\n",
+ printf("FixSpentCoins found spent coin %sppc %s[%u], %s\n",
FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
nMismatchFound++;
nBalanceInQuestion += pcoin->vout[n].nValue;
}
}
-void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
- mapKeyBirth.clear();
+void CWallet::GetAddresses(std::map<CBitcoinAddress, int64_t> &mapAddresses) const {
+ mapAddresses.clear();
// get birth times for keys with metadata
- for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++)
- if (it->second.nCreateTime)
- mapKeyBirth[it->first] = it->second.nCreateTime;
-
- // map in which we'll infer heights of other keys
- CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin
- std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;
- std::set<CKeyID> setKeys;
- GetKeys(setKeys);
- BOOST_FOREACH(const CKeyID &keyid, setKeys) {
- if (mapKeyBirth.count(keyid) == 0)
- mapKeyFirstBlock[keyid] = pindexMax;
- }
- setKeys.clear();
-
- // if there are no such keys, we're done
- if (mapKeyFirstBlock.empty())
- return;
+ for (std::map<CMalleableKeyView, CKeyMetadata>::const_iterator it = mapMalleableKeyMetadata.begin(); it != mapMalleableKeyMetadata.end(); it++) {
+ CBitcoinAddress addr(it->first.GetMalleablePubKey());
+ mapAddresses[addr] = it->second.nCreateTime ? it->second.nCreateTime : 0;
+ }
+
+ for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) {
+ CBitcoinAddress addr(it->first);
+ mapAddresses[addr] = it->second.nCreateTime ? it->second.nCreateTime : 0;
+ }
- // find first block that affects those keys, if there are any left
- std::vector<CKeyID> vAffected;
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {
// iterate over all wallet transactions...
const CWalletTx &wtx = (*it).second;
- std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
- if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) {
- // ... which are already in a block
- int nHeight = blit->second->nHeight;
- BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
- // iterate over all their outputs
- ::ExtractAffectedKeys(*this, txout.scriptPubKey, vAffected);
- BOOST_FOREACH(const CKeyID &keyid, vAffected) {
- // ... and all their affected keys
- std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
- if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
- rit->second = blit->second;
+ if (wtx.hashBlock == 0)
+ continue; // skip unconfirmed transactions
+
+ for(std::vector<CTxOut>::const_iterator it2 = wtx.vout.begin(); it2 != wtx.vout.end(); it2++) {
+ const CTxOut &out = (*it2);
+ // iterate over all their outputs
+ CBitcoinAddress addressRet;
+ if (const_cast<CWallet*>(this)->ExtractAddress(out.scriptPubKey, addressRet)) {
+ if (mapAddresses.find(addressRet) != mapAddresses.end() && (mapAddresses[addressRet] == 0 || mapAddresses[addressRet] > wtx.nTime))
+ mapAddresses[addressRet] = wtx.nTime;
+ }
+ else {
+ // multisig output affects more than one key
+ std::vector<CKeyID> vAffected;
+ ::ExtractAffectedKeys(*this, out.scriptPubKey, vAffected);
+
+ for(std::vector<CKeyID>::const_iterator it3 = vAffected.begin(); it3 != vAffected.end(); it3++) {
+ CBitcoinAddress addrAffected(*it3);
+ if (mapAddresses.find(addrAffected) != mapAddresses.end() && (mapAddresses[addrAffected] == 0 || mapAddresses[addrAffected] > wtx.nTime))
+ mapAddresses[addrAffected] = wtx.nTime;
}
vAffected.clear();
}
}
}
-
- // Extract block timestamps for those keys
- for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
- mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
}
void CWallet::ClearOrphans()
for(list<uint256>::const_iterator it = orphans.begin(); it != orphans.end(); ++it)
EraseFromWallet(*it);
}
+
+bool CWallet::ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet)
+{
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
+ return false;
+
+ if (whichType == TX_PUBKEY)
+ {
+ addressRet = CBitcoinAddress(CPubKey(vSolutions[0]).GetID());
+ return true;
+ }
+ if (whichType == TX_PUBKEY_DROP)
+ {
+ // Pay-to-Pubkey-R
+ CMalleableKeyView view;
+ if (!CheckOwnership(CPubKey(vSolutions[0]), CPubKey(vSolutions[1]), view))
+ return false;
+
+ addressRet = CBitcoinAddress(view.GetMalleablePubKey());
+ return true;
+ }
+ else if (whichType == TX_PUBKEYHASH)
+ {
+ addressRet = CBitcoinAddress(CKeyID(uint160(vSolutions[0])));
+ return true;
+ }
+ else if (whichType == TX_SCRIPTHASH)
+ {
+ addressRet = CBitcoinAddress(CScriptID(uint160(vSolutions[0])));
+ return true;
+ }
+ // Multisig txns have more than one address...
+ return false;
+}