Blackcoin Lore

This commit is contained in:
janko33bd
2017-05-30 21:33:31 +02:00
parent 597c9b42e5
commit 2fdd12b2ea
141 changed files with 4385 additions and 3872 deletions

View File

@@ -16,6 +16,7 @@
#include "main.h"
#include "net.h"
#include "policy/policy.h"
#include "pos.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/script.h"
@@ -40,6 +41,9 @@ unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
static int64_t GetStakeCombineThreshold() { return 500 * COIN; }
static int64_t GetStakeSplitThreshold() { return 2 * GetStakeCombineThreshold(); }
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
* Override with -mintxfee
@@ -53,6 +57,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CAmount nReserveBalance = 0;
CAmount nMinimumInputValue = 0;
/** @defgroup mapWallet
*
@@ -491,6 +497,304 @@ void CWallet::AddToSpends(const uint256& wtxid)
AddToSpends(txin.prevout, wtxid);
}
void CWallet::AvailableCoinsForStaking(std::vector<COutput>& vCoins) const
{
vCoins.clear();
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const uint256& wtxid = it->first;
const CWalletTx* pcoin = &(*it).second;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < 1)
continue;
if (nDepth < nStakeMinConfirmations)
continue;
if (pcoin->GetBlocksToMaturity() > 0)
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
isminetype mine = IsMine(pcoin->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0))
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
(mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO));
}
}
}
}
// Select some coins without random shuffle or best subset approximation
bool CWallet::SelectCoinsForStaking(CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
vector<COutput> vCoins;
AvailableCoinsForStaking(vCoins);
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;
int64_t n = pcoin->vout[i].nValue;
pair<int64_t,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 CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, int64_t nTime, const COutPoint& prevout, int64_t* pBlockTime)
{
uint256 hashProofOfStake, targetProofOfStake;
CTransaction txPrev;
CDiskTxPos txindex;
if (!ReadFromDisk(txPrev, txindex, *pblocktree, prevout))
return false;
// Read block header
CBlock block;
const CDiskBlockPos& pos = CDiskBlockPos(txindex.nFile, txindex.nPos);
if (!ReadBlockFromDisk(block, pos, Params().GetConsensus()))
return false;
int nDepth;
if (IsConfirmedInNPrevBlocks(txindex, pindexPrev, nStakeMinConfirmations - 1, nDepth))
return false;
if (pBlockTime)
*pBlockTime = block.GetBlockTime();
LogPrintf("looking for coinstake for real \n");
return CheckStakeKernelHash(pindexPrev, nBits, new CCoins(txPrev, pindexPrev->nHeight), prevout, nTime);
}
// miner's coin stake reward
int64_t GetProofOfStakeReward(const CBlockIndex* pindexPrev, int64_t nCoinAge, int64_t nFees)
{
int64_t nSubsidy;
if (Params().GetConsensus().IsProtocolV3(pindexPrev->nTime))
nSubsidy = COIN * 3 / 2;
else
nSubsidy = nCoinAge * 1 * CENT * 33 / (365 * 33 + 8);
LogPrint("creation", "GetProofOfStakeReward(): create=%s nCoinAge=%d\n", FormatMoney(nSubsidy), nCoinAge);
return nSubsidy + nFees;
}
bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, CAmount& nFees, CMutableTransaction& tx, CKey& key)
{
CBlockIndex* pindexPrev = pindexBestHeader;
arith_uint256 bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
struct CMutableTransaction txNew(tx);
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
CAmount nBalance = GetBalance();
if (nBalance <= nReserveBalance)
return false;
vector<const CWalletTx*> vwtxPrev;
set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
// Select coins with suitable depth
CAmount nTargetValue = nBalance - nReserveBalance;
if (!SelectCoinsForStaking(nTargetValue, setCoins, nValueIn))
return false;
if (setCoins.empty())
return false;
int64_t nCredit = 0;
CScript scriptPubKeyKernel;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& pcoin, setCoins)
{
static int nMaxStakeSearchInterval = 60;
bool fKernelFound = false;
for (unsigned int n=0; n<min(nSearchInterval,(int64_t)nMaxStakeSearchInterval) && !fKernelFound && pindexPrev == pindexBestHeader; n++)
{
boost::this_thread::interruption_point();
// Search backward in time from the given txNew timestamp
// Search nSearchInterval seconds back up to nMaxStakeSearchInterval
COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second);
int64_t nBlockTime;
//LogPrintf("looking for coinstake \n");
if (CheckKernel(pindexPrev, nBits, txNew.nTime - n, prevoutStake, &nBlockTime))
{
// Found a kernel
LogPrint("coinstake", "CreateCoinStake : kernel found\n");
vector<vector<unsigned char> > vSolutions;
txnouttype whichType;
CScript scriptPubKeyOut;
scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey;
if (!Solver(scriptPubKeyKernel, whichType, vSolutions))
{
LogPrint("coinstake", "CreateCoinStake : failed to parse kernel\n");
break;
}
LogPrint("coinstake", "CreateCoinStake : parsed kernel type=%d\n", whichType);
if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
{
LogPrint("coinstake", "CreateCoinStake : no support for kernel type=%d\n", whichType);
break; // 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))
{
LogPrint("coinstake", "CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
break; // unable to find corresponding public key
}
scriptPubKeyOut << key.GetPubKey().getvch() << OP_CHECKSIG;
}
if (whichType == TX_PUBKEY)
{
if (!keystore.GetKey(Hash160(vSolutions[0]), key))
{
LogPrint("coinstake", "CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
break; // unable to find corresponding public key
}
if (key.GetPubKey() != vSolutions[0])
{
LogPrint("coinstake", "CreateCoinStake : invalid key for kernel type=%d\n", whichType);
break; // keys mismatch
}
scriptPubKeyOut = scriptPubKeyKernel;
}
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);
txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
LogPrint("coinstake", "CreateCoinStake : added kernel type=%d\n", whichType);
fKernelFound = true;
break;
}
}
if (fKernelFound)
break; // if kernel is found stop searching
}
if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
return false;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& pcoin, setCoins)
{
// 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)
{
// Stop adding more inputs if already too many inputs
if (txNew.vin.size() >= 10)
break;
// Stop adding inputs if reached reserve limit
if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance)
break;
// Do not add additional significant input
if (pcoin.first->vout[pcoin.second].nValue >= GetStakeCombineThreshold())
continue;
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
{
uint64_t nCoinAge;
if (!GetCoinAge(txNew, *pblocktree, pindexPrev, nCoinAge))
return error("CreateCoinStake : failed to calculate coin age");
int64_t nReward = GetProofOfStakeReward(pindexPrev, nCoinAge, nFees);
if (nReward <= 0)
return false;
nCredit += nReward;
}
if (nCredit >= GetStakeSplitThreshold())
txNew.vout.push_back(CTxOut(0, txNew.vout[1].scriptPubKey)); //split stake
// Set output amount
if (txNew.vout.size() == 3)
{
txNew.vout[1].nValue = (nCredit / 2 / CENT) * CENT;
txNew.vout[2].nValue = nCredit - txNew.vout[1].nValue;
}
else
txNew.vout[1].nValue = nCredit;
// Sign
int nIn = 0;
BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
{
if (!SignSignature(*this, *pcoin, txNew, nIn++, SIGHASH_ALL))
return error("CreateCoinStake : failed to sign coinstake");
}
// Limit size
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_STANDARD_TX_SIZE)
return error("CreateCoinStake : exceeded coinstake size limit");
// Successfully generated coinstake
tx = CTransaction(txNew);
return true;
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
if (IsCrypted())
@@ -1051,7 +1355,7 @@ int CWalletTx::GetRequestCount() const
int nRequests = -1;
{
LOCK(pwallet->cs_wallet);
if (IsCoinBase())
if (IsCoinBase() || IsCoinStake())
{
// Generated block
if (!hashUnset())
@@ -1120,7 +1424,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
// In either case, we need to get the destination address
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
if (!ExtractDestination(txout.scriptPubKey, address)&& !txout.IsUnspendable())
{
LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString());
@@ -1244,7 +1548,7 @@ void CWallet::ReacceptWalletTransactions()
int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
if (!wtx.IsCoinBase() && !wtx.IsCoinStake() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
}
}
@@ -1262,7 +1566,7 @@ void CWallet::ReacceptWalletTransactions()
bool CWalletTx::RelayWalletTransaction()
{
assert(pwallet->GetBroadcastTransactions());
if (!IsCoinBase())
if (!IsCoinBase() && !IsCoinStake())
{
if (GetDepthInMainChain() == 0 && !isAbandoned()) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
@@ -1319,7 +1623,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
CAmount CWalletTx::GetCredit(const isminefilter& filter) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
int64_t credit = 0;
@@ -1351,7 +1655,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
{
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
@@ -1363,13 +1667,27 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}
CAmount CWalletTx::GetImmatureStakeCredit(bool fUseCache) const
{
if (IsCoinStake() && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureStakeCreditCached)
return nImmatureStakeCreditCached;
nImmatureStakeCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fImmatureStakeCreditCached = true;
return nImmatureStakeCreditCached;
}
return 0;
}
CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
{
if (pwallet == 0)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
if (fUseCache && fAvailableCreditCached)
@@ -1395,7 +1713,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
{
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0 && IsInMainChain())
{
if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached;
@@ -1413,7 +1731,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
if (fUseCache && fAvailableWatchCreditCached)
@@ -1597,6 +1915,20 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}
CAmount CWallet::GetStakeBalance() const
{
CAmount nTotal = 0;
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
nTotal += pcoin->GetImmatureStakeCredit();
}
}
return nTotal;
}
CAmount CWallet::GetWatchOnlyBalance() const
{
CAmount nTotal = 0;
@@ -1659,7 +1991,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (fOnlyConfirmed && !pcoin->IsTrusted())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
@@ -1891,7 +2223,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
}
bool res = nTargetValue <= nValueFromPresetInputs ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 10, vCoins, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
@@ -1996,6 +2328,8 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
txNew.nLockTime = chainActive.Height();
txNew.nTime = GetAdjustedTime();
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
@@ -2322,6 +2656,8 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
// But always obey the maximum
if (nFeeNeeded > maxTxFee)
nFeeNeeded = maxTxFee;
if(nFeeNeeded < DEFAULT_TRANSACTION_FEE)
nFeeNeeded = DEFAULT_TRANSACTION_FEE;
return nFeeNeeded;
}
@@ -2586,7 +2922,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
@@ -2831,6 +3167,38 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
}
}
uint64_t CWallet::GetStakeWeight() const
{
// Choose coins to use
CAmount nBalance = GetBalance();
if (nBalance <= nReserveBalance)
return 0;
vector<const CWalletTx*> vwtxPrev;
set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
CAmount nTargetValue = nBalance - nReserveBalance;
if (!SelectCoinsForStaking(nTargetValue, setCoins, nValueIn))
return 0;
if (setCoins.empty())
return 0;
uint64_t nWeight = 0;
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
if (pcoin.first->GetDepthInMainChain() >= nStakeMinConfirmations)
nWeight += pcoin.first->vout[pcoin.second].nValue;
}
return nWeight;
}
/** @} */ // end of Actions
class CAffectedKeysVisitor : public boost::static_visitor<void> {
@@ -3027,7 +3395,7 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
int CMerkleTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
if (!(IsCoinBase() || IsCoinStake()))
return 0;
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
}