return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
- if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift)
+ if (GetBlockTime() > FutureDrift(GetAdjustedTime()))
return error("CheckBlock() : block timestamp too far in the future");
// First transaction must be coinbase, the rest must not be
return DoS(100, error("CheckBlock() : more than one coinbase"));
// Check coinbase timestamp
- if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
+ if (GetBlockTime() > FutureDrift((int64)vtx[0].nTime))
return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
if (IsProofOfStake())
return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
+ if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
return error("AcceptBlock() : block's timestamp is too early");
// Check that all transactions are finalized
return true;
}
-// ppcoin: sign block
-bool CBlock::SignBlock(const CKeyStore& keystore)
+// novacoin: attempt to generate suitable proof-of-stake
+bool CBlock::SignBlock(CWallet& wallet)
{
- vector<valtype> vSolutions;
- txnouttype whichType;
-
- if(!IsProofOfStake())
- {
- for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
- {
- const CTxOut& txout = vtx[0].vout[i];
+ // if we are trying to sign
+ // something except proof-of-stake block template
+ if (!vtx[0].vout[0].IsEmpty())
+ return false;
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- continue;
+ // if we are trying to sign
+ // a complete proof-of-stake block
+ if (IsProofOfStake())
+ return true;
- if (whichType == TX_PUBKEY)
- {
- // Sign
- valtype& vchPubKey = vSolutions[0];
- CKey key;
+ static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- continue;
- if (key.GetPubKey() != vchPubKey)
- continue;
- if(!key.Sign(GetHash(), vchBlockSig))
- continue;
+ CKey key;
+ CTransaction txCoinStake;
+ int64 nSearchTime = txCoinStake.nTime; // search to current time
- return true;
- }
- }
- }
- else
+ if (nSearchTime > nLastCoinStakeSearchTime)
{
- const CTxOut& txout = vtx[1].vout[1];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_PUBKEY)
+ if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key))
{
- // Sign
- valtype& vchPubKey = vSolutions[0];
- CKey key;
-
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- return false;
- if (key.GetPubKey() != vchPubKey)
- return false;
-
- return key.Sign(GetHash(), vchBlockSig);
+ if (txCoinStake.nTime >= max(pindexBest->GetMedianTimePast()+1, PastDrift(pindexBest->GetBlockTime())))
+ {
+ // make sure coinstake would meet timestamp protocol
+ // as it would be the same as the block timestamp
+ vtx[0].nTime = nTime = txCoinStake.nTime;
+ nTime = max(pindexBest->GetMedianTimePast()+1, GetMaxTransactionTime());
+ nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime()));
+
+ // we have to make sure that we have no future timestamps in
+ // our transactions set
+ for (vector<CTransaction>::iterator it = vtx.begin(); it != vtx.end();)
+ if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; }
+
+ vtx.insert(vtx.begin() + 1, txCoinStake);
+ hashMerkleRoot = BuildMerkleTree();
+
+ // append a signature to our block
+ return key.Sign(GetHash(), vchBlockSig);
+ }
}
+ nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
+ nLastCoinStakeSearchTime = nSearchTime;
}
- printf("Sign failed\n");
return false;
}
static const uint256 hashGenesisBlock("0x00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4");
static const uint256 hashGenesisBlockTestNet("0x000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb");
-static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
+
+inline int64 PastDrift(int64 nTime) { return nTime - 2 * 60 * 60; } // up to 2 hours from the past
+inline int64 FutureDrift(int64 nTime) { return nTime + 2 * 60 * 60; } // up to 2 hours from the future
extern libzerocoin::Params* ZCParams;
extern CScript COINBASE_FLAGS;
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const;
bool AcceptBlock();
bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block
- bool SignBlock(const CKeyStore& keystore);
+ bool SignBlock(CWallet& keystore);
bool CheckBlockSignature(bool fProofOfStake) const;
private:
uint256 GetBlockHash() const
{
- if (fUseFastIndex && (nTime < GetAdjustedTime() - 12 * nMaxClockDrift) && blockHash != 0)
+ if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0)
return blockHash;
CBlock block;
-I"$(CURDIR)" \
-I"$(CURDIR)"/obj \
-I"$(DEPSDIR)/boost_1_55_0" \
- -I"$(DEPSDIR)/db-5.3.21/build_unix" \
+ -I"$(DEPSDIR)/db-6.0.20/build_unix" \
-I"$(DEPSDIR)/openssl-1.0.1f/include" \
-I"$(DEPSDIR)"
LIBPATHS= \
-L"$(DEPSDIR)/boost_1_55_0/stage/lib" \
- -L"$(DEPSDIR)/db-5.3.21/build_unix" \
+ -L"$(DEPSDIR)/db-6.0.20/build_unix" \
-L"$(DEPSDIR)/openssl-1.0.1f"
LIBS= \
}
};
-// CreateNewBlock:
-// fProofOfStake: try (best effort) to make a proof-of-stake block
+// CreateNewBlock: create new block (without proof-of-work/proof-of-stake)
CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake)
{
// Create new block
if (mapArgs.count("-mintxfee"))
ParseMoney(mapArgs["-mintxfee"], nMinTxFee);
- // ppcoin: if coinstake available add coinstake tx
- static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
CBlockIndex* pindexPrev = pindexBest;
- if (fProofOfStake) // attempt to find a coinstake
- {
- pblock->nBits = GetNextTargetRequired(pindexPrev, true);
- CTransaction txCoinStake;
- int64 nSearchTime = txCoinStake.nTime; // search to current time
- if (nSearchTime > nLastCoinStakeSearchTime)
- {
- if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake))
- {
- if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - nMaxClockDrift))
- { // make sure coinstake would meet timestamp protocol
- // as it would be the same as the block timestamp
- pblock->vtx[0].nTime = txCoinStake.nTime;
- pblock->vtx.push_back(txCoinStake);
- }
- }
- nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
- nLastCoinStakeSearchTime = nSearchTime;
- }
- }
-
- pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
+ pblock->nBits = GetNextTargetRequired(pindexPrev, fProofOfStake);
// Collect memory pool transactions into the block
int64 nFees = 0;
continue;
// Timestamp limit
- if (tx.nTime > GetAdjustedTime() || (pblock->IsProofOfStake() && tx.nTime > pblock->vtx[1].nTime))
+ if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > pblock->vtx[0].nTime))
continue;
- // ppcoin: simplify transaction fee - allow free = false
+ // Simplify transaction fee - allow free = false
int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
// Skip free transactions if we're past the minimum block size:
if (fDebug && GetBoolArg("-printpriority"))
printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
- if (pblock->IsProofOfWork())
+ if (!fProofOfStake)
pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- if (pblock->IsProofOfStake())
- pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp
pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
- pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
- if (pblock->IsProofOfWork())
+ pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime()));
+ if (!fProofOfStake)
pblock->UpdateTime(pindexPrev);
pblock->nNonce = 0;
}
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
+
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
{
if (fShutdown)
return;
- while (vNodes.empty() || IsInitialBlockDownload())
+
+ while (pwallet->IsLocked())
{
+ strMintWarning = strMintMessage;
Sleep(1000);
if (fShutdown)
return;
}
- while (pwallet->IsLocked())
+ while (vNodes.empty() || IsInitialBlockDownload())
{
- strMintWarning = strMintMessage;
Sleep(1000);
+ if (fShutdown)
+ return;
}
+
strMintWarning = "";
//
return;
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
- if(pblock->IsProofOfStake())
+ // Trying to sign a block
+ if (pblock->SignBlock(*pwallet))
{
- // Trying to sign a block
- if (!pblock->SignBlock(*pwallet))
- {
- strMintWarning = strMintMessage;
- continue;
- }
-
- strMintWarning = "";
+ strMintWarning = _("Stake generation: new block found!");
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckStake(pblock.get(), *pwallet);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
int64 nCredit = wtx.GetCredit(true);
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
- uint256 hash = wtx.GetHash();
+ uint256 hash = wtx.GetHash(), hashPrev = 0;
std::map<std::string, std::string> mapValue = wtx.mapValue;
- if (wtx.IsCoinStake())
- {
- // Stake generation
- parts.append(TransactionRecord(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut()));
- }
- else if (nNet > 0 || wtx.IsCoinBase())
+ if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake())
{
//
// Credit
}
if (wtx.IsCoinBase())
{
- // Generated
+ // Generated (proof-of-work)
+ sub.type = TransactionRecord::Generated;
+ }
+ if (wtx.IsCoinStake())
+ {
+ // Generated (proof-of-stake)
+
+ if (hashPrev == hash)
+ continue; // last coinstake output
+
sub.type = TransactionRecord::Generated;
+ sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit;
+ hashPrev = hash;
}
parts.append(sub);
}
// For generated transactions, determine maturity
- if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint)
+ if(type == TransactionRecord::Generated)
{
int64 nCredit = wtx.GetCredit(true);
if (nCredit == 0)
SendToOther,
RecvWithAddress,
RecvFromOther,
- SendToSelf,
- StakeMint
+ SendToSelf
};
/** Number of confirmation needed for transaction */
status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
break;
}
- if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint)
+
+ if(wtx->type == TransactionRecord::Generated)
{
switch(wtx->status.maturity)
{
return tr("Sent to");
case TransactionRecord::SendToSelf:
return tr("Payment to yourself");
- case TransactionRecord::StakeMint:
case TransactionRecord::Generated:
return tr("Mined");
default:
switch(wtx->type)
{
case TransactionRecord::Generated:
- case TransactionRecord::StakeMint:
return QIcon(":/icons/tx_mined");
case TransactionRecord::RecvWithAddress:
case TransactionRecord::RecvFromOther:
QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
{
- if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint)
+ if(wtx->type == TransactionRecord::Generated)
{
switch(wtx->status.maturity)
{
return QString::fromStdString(rec->getTxID());
case ConfirmedRole:
// Return True if transaction counts for balance
- return rec->status.confirmed && !((rec->type == TransactionRecord::Generated || rec->type == TransactionRecord::StakeMint) &&
- rec->status.maturity != TransactionStatus::Mature);
+ return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && rec->status.maturity != TransactionStatus::Mature);
case FormattedAmountRole:
return formatTxAmount(rec, false);
}
typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
- typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::StakeMint));
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
"getmininginfo\n"
"Returns an object containing mining-related information.");
+ uint64 nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
+ pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);
+
Object obj;
obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize));
obj.push_back(Pair("netstakeweight", GetPoSKernelPS()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
- obj.push_back(Pair("stakeweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_NORMAL)));
- obj.push_back(Pair("minweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_MINWEIGHT)));
- obj.push_back(Pair("maxweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_MAXWEIGHT)));
+
+ obj.push_back(Pair("stakeweight", (uint64_t)nWeight));
+ obj.push_back(Pair("minweight", (uint64_t)nMinWeight));
+ obj.push_back(Pair("maxweight", (uint64_t)nMaxWeight));
+
obj.push_back(Pair("stakeinterest", (uint64_t)GetProofOfStakeReward(0, GetLastBlockIndex(pindexBest, true)->nBits, GetLastBlockIndex(pindexBest, true)->nTime, true)));
obj.push_back(Pair("testnet", fTestNet));
return obj;
}
}
+void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
+{
+ vCoins.clear();
+
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+
+ if (!pcoin->IsFinal())
+ continue;
+
+ 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()));
+ }
+ }
+}
+
static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsigned int> > >vValue, int64 nTotalLower, int64 nTargetValue,
vector<char>& vfBest, int64& nBest, int iterations = 1000)
{
int i = output.i;
+ // Follow the timestamp rules
if (pcoin->nTime > nSpendTime)
- continue; // ppcoin: timestamp must not exceed spend time
+ continue;
int64 n = pcoin->vout[i].nValue;
SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet));
}
+// Select some coins without random shuffle or best subset approximation
+bool CWallet::SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+{
+ vector<COutput> vCoins;
+ AvailableCoinsMinConf(vCoins, nMinConf);
+
+ setCoinsRet.clear();
+ nValueRet = 0;
+
+ BOOST_FOREACH(COutput output, vCoins)
+ {
+ const CWalletTx *pcoin = output.tx;
+ int i = output.i;
+
+ // Stop if we've chosen enough inputs
+ if (nValueRet >= nTargetValue)
+ break;
+
+ // Follow the timestamp rules
+ if (pcoin->nTime > nSpendTime)
+ continue;
+
+ int64 n = pcoin->vout[i].nValue;
+
+ pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
+
+ if (n >= nTargetValue)
+ {
+ // If input value is greater or equal to target then simply insert
+ // it into the current subset and exit
+ setCoinsRet.insert(coin.second);
+ nValueRet += coin.first;
+ break;
+ }
+ else if (n < nTargetValue + CENT)
+ {
+ setCoinsRet.insert(coin.second);
+ nValueRet += coin.first;
+ }
+ }
+
+ return true;
+}
+
bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl)
{
int64 nValue = 0;
}
// NovaCoin: get current stake weight
-uint64 CWallet::GetStakeWeight(const CKeyStore& keystore, enum StakeWeightMode mode)
+bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight)
{
- LOCK2(cs_main, cs_wallet);
-
// Choose coins to use
int64 nBalance = GetBalance();
int64 nReserveBalance = 0;
- uint64 nCoinAge = 0;
if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
{
error("GetStakeWeight : invalid reserve balance amount");
- return 0;
+ return false;
}
if (nBalance <= nReserveBalance)
- return 0;
+ return false;
- set<pair<const CWalletTx*,unsigned int> > setCoins;
vector<const CWalletTx*> vwtxPrev;
+
+/*
+ * TODO: performance comparison
+
+ static set<pair<const CWalletTx*,unsigned int> > setCoins;
+ static uint256 hashPrevBlock;
+ static int64 nValueIn = 0;
+
+ // Cache outputs unless best block changed
+ if (hashPrevBlock != pindexBest->GetBlockHash())
+ {
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, GetAdjustedTime(), nCoinbaseMaturity * 10, setCoins, nValueIn))
+ return false;
+
+ if (setCoins.empty())
+ return false;
+
+ hashPrevBlock == pindexBest->GetBlockHash();
+ }
+*/
+
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
- if (!SelectCoins(nBalance - nReserveBalance, GetTime(), setCoins, nValueIn))
- return 0;
+
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, GetTime(), nCoinbaseMaturity * 10, setCoins, nValueIn))
+ return false;
+
if (setCoins.empty())
- return 0;
+ return false;
+ CTxDB txdb("r");
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CTxDB txdb("r");
CTxIndex txindex;
- 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 (nTime + nStakeMinAge > GetTime())
- continue;
- break;
- case STAKE_MAXWEIGHT:
- // Do not count input that is still less than 90 days old
- if (nTime + nStakeMaxAge > GetTime())
- continue;
- break;
- case STAKE_MINWEIGHT:
- // Count only inputs with suitable age (from 30 to 90 days old)
- if (nTime + nStakeMaxAge < GetTime())
- continue;
- if (nTime + nStakeMinAge > GetTime())
- continue;
- break;
+ LOCK2(cs_main, cs_wallet);
+ if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ continue;
}
- int64 nTimeWeight;
+ unsigned int nTime = pcoin.first->nTime;
// 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);
-
+ int64 nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge);
CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60);
- nCoinAge += bnCoinDayWeight.getuint64();
- }
+ // Count input that is more than 90 days old
+ if (nTime + nStakeMaxAge < GetTime())
+ {
+ nMaxWeight += bnCoinDayWeight.getuint64();
+ nWeight += bnCoinDayWeight.getuint64();
+ }
- if (fDebug && GetBoolArg("-printcoinage"))
- printf("StakeWeight bnCoinDay=%"PRI64d"\n", nCoinAge);
+ // Count input that is more than 30 days old and less than 90 days old
+ if (nTime + nStakeMinAge < GetTime() && nTime + nStakeMaxAge > GetTime())
+ {
+ nMinWeight += bnCoinDayWeight.getuint64();
+ nWeight += bnCoinDayWeight.getuint64();
+ }
+ }
- return nCoinAge;
+ return true;
}
-// ppcoin: create coin stake transaction
-bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew)
+bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew, CKey& key)
{
// The following split & combine thresholds are important to security
// Should not be adjusted if you don't understand the consequences
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
- LOCK2(cs_main, cs_wallet);
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 nBalance = GetBalance();
int64 nReserveBalance = 0;
+
if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
return error("CreateCoinStake : invalid reserve balance amount");
+
if (nBalance <= nReserveBalance)
return false;
- set<pair<const CWalletTx*,unsigned int> > setCoins;
+
vector<const CWalletTx*> vwtxPrev;
+
+/*
+ * TODO: performance comparison
+
+ static set<pair<const CWalletTx*,unsigned int> > setCoins;
+ static uint256 hashPrevBlock;
+ static int64 nValueIn = 0;
+
+ // Cache outputs unless best block changed
+ if (hashPrevBlock != pindexBest->GetBlockHash())
+ {
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
+ return false;
+
+ if (setCoins.empty())
+ return false;
+
+ hashPrevBlock == pindexBest->GetBlockHash();
+ }
+*/
+
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
- if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn))
+
+ // Select coins with suitable depth
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
return false;
+
if (setCoins.empty())
return false;
+
int64 nCredit = 0;
CScript scriptPubKeyKernel;
+ CTxDB txdb("r");
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CTxDB txdb("r");
CTxIndex txindex;
- if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
- continue;
+ {
+ LOCK2(cs_main, cs_wallet);
+ if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ continue;
+ }
// Read block header
CBlock block;
- if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- continue;
+ {
+ LOCK2(cs_main, cs_wallet);
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ continue;
+ }
+
static int nMaxStakeSearchInterval = 60;
if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
continue; // only count coins meeting min age requirement
if (whichType == TX_PUBKEYHASH) // pay to address type
{
// convert to pay to public key type
- CKey key;
if (!keystore.GetKey(uint160(vSolutions[0]), key))
{
if (fDebug && GetBoolArg("-printcoinstake"))
}
scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
}
- else
+ 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);
+ break; // unable to find corresponding public key
+ }
+
+ if (key.GetPubKey() != vchPubKey)
+ {
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : invalid key for kernel type=%d\n", whichType);
+ break; // keys mismatch
+ }
+
scriptPubKeyOut = scriptPubKeyKernel;
+ }
- txNew.nTime -= n;
+ txNew.nTime -= n;
txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
nCredit += pcoin.first->vout[pcoin.second].nValue;
vwtxPrev.push_back(pcoin.first);
break;
}
}
+
if (fKernelFound || fShutdown)
break; // if kernel is found stop searching
}
+
if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
return false;
+
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
// Attempt to add more inputs
vwtxPrev.push_back(pcoin.first);
}
}
+
// Calculate coin age reward
{
uint64 nCoinAge;
FEATURE_LATEST = 60000
};
-/** Stake weight calculation mode */
-enum StakeWeightMode
-{
- STAKE_NORMAL = 0, // all 30+ days old inputs
- STAKE_MAXWEIGHT = 1, // only 90+ days old inputs
- STAKE_MINWEIGHT = 3 // only [30-90] days old inputs
-};
-
/** A key pool entry */
class CKeyPool
{
class CWallet : public CCryptoKeyStore
{
private:
+ bool SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet, const CCoinControl *coinControl=NULL) const;
CWalletDB *pwalletdbEncryption;
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
+ void AvailableCoinsMinConf(std::vector<COutput>& vCoins, int nConf) const;
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const;
bool SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
// keystore implementation
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl *coinControl=NULL);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
- uint64 GetStakeWeight(const CKeyStore& keystore, enum StakeWeightMode mode=STAKE_NORMAL);
- bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew);
+ bool GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight);
+ bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew, CKey& key);
std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);