int nLimitProcessors = 1;
int fMinimizeToTray = true;
int fMinimizeOnClose = true;
+#ifdef USE_UPNP
+#if USE_UPNP
+int fUseUPnP = true;
+#else
+int fUseUPnP = false;
+#endif
+#endif
+
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
- if (wtxIn.fSpent && wtxIn.fSpent != wtx.fSpent)
- {
- wtx.fSpent = wtxIn.fSpent;
- fUpdated = true;
- }
+ fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
}
//// debug print
if (txout.scriptPubKey == scriptDefaultKey)
{
CWalletDB walletdb;
- vchDefaultKey = walletdb.GetKeyFromKeyPool();
+ vchDefaultKey = GetKeyFromKeyPool();
walletdb.WriteDefaultKey(vchDefaultKey);
walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
}
return true;
}
-bool AddToWalletIfMine(const CTransaction& tx, const CBlock* pblock)
+bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false)
{
- if (tx.IsMine() || mapWallet.count(tx.GetHash()))
+ uint256 hash = tx.GetHash();
+ bool fExisted = mapWallet.count(hash);
+ if (fExisted && !fUpdate) return false;
+ if (fExisted || tx.IsMine() || tx.IsFromMe())
{
CWalletTx wtx(tx);
// Get merkle branch if transaction was found in a block
wtx.SetMerkleBranch(pblock);
return AddToWallet(wtx);
}
- return true;
+ return false;
}
bool EraseFromWallet(uint256 hash)
if (mi != mapWallet.end())
{
CWalletTx& wtx = (*mi).second;
- if (!wtx.fSpent && wtx.vout[prevout.n].IsMine())
+ if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine())
{
printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
- wtx.fSpent = true;
+ wtx.MarkSpent(prevout.n);
wtx.WriteToDisk();
vWalletUpdated.push_back(prevout.hash);
}
//////////////////////////////////////////////////////////////////////////////
//
-// CTransaction
+// CTransaction and CTxIndex
//
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
return nRequests;
}
+void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<string, int64> >& listReceived,
+ list<pair<string, int64> >& listSent, int64& nFee, string& strSentAccount) const
+{
+ nGeneratedImmature = nGeneratedMature = nFee = 0;
+ listReceived.clear();
+ listSent.clear();
+ strSentAccount = strFromAccount;
+
+ if (IsCoinBase())
+ {
+ if (GetBlocksToMaturity() > 0)
+ nGeneratedImmature = CTransaction::GetCredit();
+ else
+ nGeneratedMature = GetCredit();
+ return;
+ }
+
+ // Compute fee:
+ int64 nDebit = GetDebit();
+ if (nDebit > 0) // debit>0 means we signed/sent this transaction
+ {
+ int64 nValueOut = GetValueOut();
+ nFee = nDebit - nValueOut;
+ }
+
+ // Sent/received. Standard client will never generate a send-to-multiple-recipients,
+ // but non-standard clients might (so return a list of address/amount pairs)
+ foreach(const CTxOut& txout, vout)
+ {
+ string address;
+ uint160 hash160;
+ vector<unsigned char> vchPubKey;
+ if (ExtractHash160(txout.scriptPubKey, hash160))
+ address = Hash160ToAddress(hash160);
+ else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey))
+ address = PubKeyToAddress(vchPubKey);
+ else
+ {
+ printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
+ this->GetHash().ToString().c_str());
+ address = " unknown ";
+ }
+
+ // Don't report 'change' txouts
+ if (nDebit > 0 && txout.IsChange())
+ continue;
+
+ if (nDebit > 0)
+ listSent.push_back(make_pair(address, txout.nValue));
+
+ if (txout.IsMine())
+ listReceived.push_back(make_pair(address, txout.nValue));
+ }
+
+}
+
+void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
+ int64& nSent, int64& nFee) const
+{
+ nGenerated = nReceived = nSent = nFee = 0;
+
+ int64 allGeneratedImmature, allGeneratedMature, allFee;
+ allGeneratedImmature = allGeneratedMature = allFee = 0;
+ string strSentAccount;
+ list<pair<string, int64> > listReceived;
+ list<pair<string, int64> > listSent;
+ GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
+
+ if (strAccount == "")
+ nGenerated = allGeneratedMature;
+ if (strAccount == strSentAccount)
+ {
+ foreach(const PAIRTYPE(string,int64)& s, listSent)
+ nSent += s.second;
+ nFee = allFee;
+ }
+ CRITICAL_BLOCK(cs_mapAddressBook)
+ {
+ foreach(const PAIRTYPE(string,int64)& r, listReceived)
+ {
+ if (mapAddressBook.count(r.first))
+ {
+ if (mapAddressBook[r.first] == strAccount)
+ {
+ nReceived += r.second;
+ }
+ }
+ else if (strAccount.empty())
+ {
+ nReceived += r.second;
+ }
+ }
+ }
+}
if ((int64)nLockTime > INT_MAX)
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
- // Rather not work on nonstandard transactions
- if (GetSigOpCount() > 2 || ::GetSerializeSize(*this, SER_NETWORK) < 100)
+ // Safety limits
+ unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
+ // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
+ // attacks disallow transactions with more than one SigOp per 34 bytes.
+ // 34 bytes because a TxOut is:
+ // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
+ if (GetSigOpCount() > nSize / 34 || nSize < 100)
return error("AcceptToMemoryPool() : nonstandard transaction");
+ // Rather not work on nonstandard transactions
+ if (!IsStandard())
+ return error("AcceptToMemoryPool() : nonstandard transaction type");
+
// Do we already have it?
uint256 hash = GetHash();
CRITICAL_BLOCK(cs_mapTransactions)
}
}
- // Check against previous transactions
- map<uint256, CTxIndex> mapUnused;
- int64 nFees = 0;
- if (fCheckInputs && !ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
+ if (fCheckInputs)
{
- if (pfMissingInputs)
- *pfMissingInputs = true;
- return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
+ // Check against previous transactions
+ map<uint256, CTxIndex> mapUnused;
+ int64 nFees = 0;
+ if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
+ {
+ if (pfMissingInputs)
+ *pfMissingInputs = true;
+ return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
+ }
+
+ // Don't accept it if it can't get into a block
+ if (nFees < GetMinFee(1000))
+ return error("AcceptToMemoryPool() : not enough fees");
+
+ // Continuously rate-limit free transactions
+ // This mitigates 'penny-flooding' -- sending thousands of free transactions just to
+ // be annoying or make other's transactions take longer to confirm.
+ if (nFees < CENT)
+ {
+ static CCriticalSection cs;
+ static double dFreeCount;
+ static int64 nLastTime;
+ int64 nNow = GetTime();
+
+ CRITICAL_BLOCK(cs)
+ {
+ // Use an exponentially decaying ~10-minute window:
+ dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
+ nLastTime = nNow;
+ // -limitfreerelay unit is thousand-bytes-per-minute
+ // At default rate it would take over a month to fill 1GB
+ if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe())
+ return error("AcceptToMemoryPool() : free transaction rejected by rate limiter");
+ if (fDebug)
+ printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
+ dFreeCount += nSize;
+ }
+ }
}
// Store transaction in memory
return false;
}
+int ScanForWalletTransactions(CBlockIndex* pindexStart)
+{
+ int ret = 0;
+
+ CBlockIndex* pindex = pindexStart;
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ while (pindex)
+ {
+ CBlock block;
+ block.ReadFromDisk(pindex, true);
+ foreach(CTransaction& tx, block.vtx)
+ {
+ if (AddToWalletIfInvolvingMe(tx, &block))
+ ret++;
+ }
+ pindex = pindex->pnext;
+ }
+ }
+ return ret;
+}
+
void ReacceptWalletTransactions()
{
CTxDB txdb("r");
- CRITICAL_BLOCK(cs_mapWallet)
+ bool fRepeat = true;
+ while (fRepeat) CRITICAL_BLOCK(cs_mapWallet)
{
+ fRepeat = false;
+ vector<CDiskTxPos> vMissingTx;
foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
- if (wtx.fSpent && wtx.IsCoinBase())
+ if (wtx.IsCoinBase() && wtx.IsSpent(0))
continue;
CTxIndex txindex;
+ bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
{
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
- if (!wtx.fSpent)
+ if (txindex.vSpent.size() != wtx.vout.size())
{
- if (txindex.vSpent.size() != wtx.vout.size())
- {
- printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
+ printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
+ continue;
+ }
+ for (int i = 0; i < txindex.vSpent.size(); i++)
+ {
+ if (wtx.IsSpent(i))
continue;
- }
- for (int i = 0; i < txindex.vSpent.size(); i++)
+ if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
{
- if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
- {
- printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
- wtx.fSpent = true;
- wtx.WriteToDisk();
- break;
- }
+ wtx.MarkSpent(i);
+ fUpdated = true;
+ vMissingTx.push_back(txindex.vSpent[i]);
}
}
+ if (fUpdated)
+ {
+ printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ wtx.MarkDirty();
+ wtx.WriteToDisk();
+ }
}
else
{
wtx.AcceptWalletTransaction(txdb, false);
}
}
+ if (!vMissingTx.empty())
+ {
+ // TODO: optimize this to scan just part of the block chain?
+ if (ScanForWalletTransactions(pindexGenesisBlock))
+ fRepeat = true; // Found missing transactions: re-do Reaccept.
+ }
}
}
}
}
+int CTxIndex::GetDepthInMainChain() const
+{
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false))
+ return 0;
+ // Find the block in the index
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
+ if (mi == mapBlockIndex.end())
+ return 0;
+ CBlockIndex* pindex = (*mi).second;
+ if (!pindex || !pindex->IsInMainChain())
+ return 0;
+ return 1 + nBestHeight - pindex->nHeight;
+}
+
bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions)
{
+ if (!fReadTransactions)
+ {
+ *this = pindex->GetBlockHeader();
+ return true;
+ }
if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions))
return false;
if (GetHash() != pindex->GetBlockHash())
bool IsInitialBlockDownload()
{
- if (pindexBest == NULL || (!fTestNet && nBestHeight < 74000))
+ if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000))
return true;
static int64 nLastUpdate;
static CBlockIndex* pindexLastBest;
// Watch for transactions paying to me
foreach(CTransaction& tx, vtx)
- AddToWalletIfMine(tx, this);
+ AddToWalletIfInvolvingMe(tx, this, true);
return true;
}
-
-
bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
printf("REORGANIZE\n");
}
}
+ // Update best block in wallet (so we can detect restored wallets)
+ if (!IsInitialBlockDownload())
+ {
+ CWalletDB walletdb;
+ const CBlockLocator locator(pindexNew);
+ if (!walletdb.WriteBestBlock(locator))
+ return error("SetBestChain() : WriteWalletBest failed");
+ }
+
// New best block
hashBestChain = hash;
pindexBest = pindexNew;
pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork();
CTxDB txdb;
+ txdb.TxnBegin();
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
+ if (!txdb.TxnCommit())
+ return false;
// New best
if (pindexNew->bnChainWork > bnBestChainWork)
// Check that the block chain matches the known block chain up to a checkpoint
if (!fTestNet)
- if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) ||
- (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) ||
- (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) ||
- (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) ||
- (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")))
+ if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) ||
+ (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) ||
+ (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) ||
+ (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) ||
+ (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) ||
+ (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) ||
+ (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553")))
return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
return error("AcceptBlock() : out of disk space");
- unsigned int nFile;
- unsigned int nBlockPos;
- if (!WriteToDisk(!fClient, nFile, nBlockPos))
+ unsigned int nFile = -1;
+ unsigned int nBlockPos = 0;
+ if (!WriteToDisk(nFile, nBlockPos))
return error("AcceptBlock() : WriteToDisk failed");
if (!AddToBlockIndex(nFile, nBlockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
if (hashBestChain == hash)
CRITICAL_BLOCK(cs_vNodes)
foreach(CNode* pnode, vNodes)
- if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 55000))
+ if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
return true;
// Preliminary checks
if (!pblock->CheckBlock())
- {
- delete pblock;
return error("ProcessBlock() : CheckBlock FAILED");
- }
// If don't already have its previous block, shunt it off to holding area until we get it
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
- mapOrphanBlocks.insert(make_pair(hash, pblock));
- mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
+ CBlock* pblock2 = new CBlock(*pblock);
+ mapOrphanBlocks.insert(make_pair(hash, pblock2));
+ mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
// Ask this guy to fill in what we're missing
if (pfrom)
- pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock));
+ pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
return true;
}
// Store to disk
if (!pblock->AcceptBlock())
- {
- delete pblock;
return error("ProcessBlock() : AcceptBlock FAILED");
- }
- delete pblock;
// Recursively process any orphan blocks that depended on this one
vector<uint256> vWorkQueue;
{
if (fTestNet)
{
- hashGenesisBlock = uint256("0x0000000224b1593e3ff16a0e3b61285bbc393a39f78c8aa48c456142671f7110");
+ hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
if (fTestNet)
{
- block.nTime = 1279232055;
+ block.nTime = 1296688602;
block.nBits = 0x1d07fff8;
- block.nNonce = 81622180;
+ block.nNonce = 384568319;
}
//// debug print
// Start new block file
unsigned int nFile;
unsigned int nBlockPos;
- if (!block.WriteToDisk(!fClient, nFile, nBlockPos))
+ if (!block.WriteToDisk(nFile, nBlockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
int nPriority = 0;
string strStatusBar;
string strRPC;
- if (mapArgs.count("-testsafemode"))
+ if (GetBoolArg("-testsafemode"))
strRPC = "test";
// Misc warnings like out of disk space and clock is wrong
{
nPriority = alert.nPriority;
strStatusBar = alert.strStatusBar;
- strRPC = alert.strRPCError;
}
}
}
return true;
}
+ // Be shy and don't send version until we hear
+ if (pfrom->fInbound)
+ pfrom->PushVersion();
+
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
- if (pfrom->fClient)
- {
- pfrom->vSend.nType |= SER_BLOCKHEADERONLY;
- pfrom->vRecv.nType |= SER_BLOCKHEADERONLY;
- }
AddTimeData(pfrom->addr.ip, nTime);
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
if (mi != mapBlockIndex.end())
{
- //// could optimize this to send header straight from blockindex for client
CBlock block;
- block.ReadFromDisk((*mi).second, !pfrom->fClient);
+ block.ReadFromDisk((*mi).second);
pfrom->PushMessage("block", block);
// Trigger them to send a getblocks request for the next batch of inventory
uint256 hashStop;
vRecv >> locator >> hashStop;
- // Find the first block the caller has in the main chain
+ // Find the last block the caller has in the main chain
CBlockIndex* pindex = locator.GetBlockIndex();
// Send the rest of the chain
}
+ else if (strCommand == "getheaders")
+ {
+ CBlockLocator locator;
+ uint256 hashStop;
+ vRecv >> locator >> hashStop;
+
+ CBlockIndex* pindex = NULL;
+ if (locator.IsNull())
+ {
+ // If locator is null, return the hashStop block
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop);
+ if (mi == mapBlockIndex.end())
+ return true;
+ pindex = (*mi).second;
+ }
+ else
+ {
+ // Find the last block the caller has in the main chain
+ pindex = locator.GetBlockIndex();
+ if (pindex)
+ pindex = pindex->pnext;
+ }
+
+ vector<CBlock> vHeaders;
+ int nLimit = 2000 + locator.GetDistanceBack();
+ printf("getheaders %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit);
+ for (; pindex; pindex = pindex->pnext)
+ {
+ vHeaders.push_back(pindex->GetBlockHeader());
+ if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
+ break;
+ }
+ pfrom->PushMessage("headers", vHeaders);
+ }
+
+
else if (strCommand == "tx")
{
vector<uint256> vWorkQueue;
bool fMissingInputs = false;
if (tx.AcceptToMemoryPool(true, &fMissingInputs))
{
- AddToWalletIfMine(tx, NULL);
+ AddToWalletIfInvolvingMe(tx, NULL, true);
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
if (tx.AcceptToMemoryPool(true))
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
- AddToWalletIfMine(tx, NULL);
+ AddToWalletIfInvolvingMe(tx, NULL, true);
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
else if (strCommand == "block")
{
- auto_ptr<CBlock> pblock(new CBlock);
- vRecv >> *pblock;
+ CBlock block;
+ vRecv >> block;
- //// debug print
- printf("received block %s\n", pblock->GetHash().ToString().substr(0,20).c_str());
- // pblock->print();
+ printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str());
+ // block.print();
- CInv inv(MSG_BLOCK, pblock->GetHash());
+ CInv inv(MSG_BLOCK, block.GetHash());
pfrom->AddInventoryKnown(inv);
- if (ProcessBlock(pfrom, pblock.release()))
+ if (ProcessBlock(pfrom, &block))
mapAlreadyAskedFor.erase(inv);
}
else if (strCommand == "checkorder")
{
uint256 hashReply;
- CWalletTx order;
- vRecv >> hashReply >> order;
+ vRecv >> hashReply;
- if (!mapArgs.count("-allowreceivebyip") || mapArgs["-allowreceivebyip"] == "0")
+ if (!GetBoolArg("-allowreceivebyip"))
{
pfrom->PushMessage("reply", hashReply, (int)2, string(""));
return true;
}
+ CWalletTx order;
+ vRecv >> order;
+
/// we have a chance to check the order here
// Keep giving the same key to the same ip until they use it
if (!mapReuseKey.count(pfrom->addr.ip))
- mapReuseKey[pfrom->addr.ip] = CWalletDB().GetKeyFromKeyPool();
+ mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool();
// Send back approval of order and pubkey to use
CScript scriptPubKey;
else if (strCommand == "submitorder")
{
uint256 hashReply;
- CWalletTx wtxNew;
- vRecv >> hashReply >> wtxNew;
- wtxNew.fFromMe = false;
+ vRecv >> hashReply;
- if (!mapArgs.count("-allowreceivebyip") || mapArgs["-allowreceivebyip"] == "0")
+ if (!GetBoolArg("-allowreceivebyip"))
{
pfrom->PushMessage("reply", hashReply, (int)2);
return true;
}
+ CWalletTx wtxNew;
+ vRecv >> wtxNew;
+ wtxNew.fFromMe = false;
+
// Broadcast
if (!wtxNew.AcceptWalletTransaction())
{
"mov %%ecx, %1;" // ecx into c
:"=r"(a),"=r"(c) /* output */
:"r"(in) /* input */
- :"%eax","%ecx" /* clobbered register */
+ :"%eax","%ebx","%ecx","%edx" /* clobbered register */
);
aret = a;
cret = c;
// ScanHash scans nonces looking for a hash with at least some zero bits.
// It operates on big endian data. Caller does the byte reversing.
// All input buffers are 16-byte aligned. nNonce is usually preserved
-// between calls, but periodically or if nNonce is above 0xff000000,
+// between calls, but periodically or if nNonce is 0xffff0000 or above,
// the block is rebuilt and nNonce starts over at zero.
//
-unsigned int ScanHash_CryptoPP(char* pmidstate, char* pblock, char* phash1, char* phash, unsigned int& nHashesDone)
+unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
{
- unsigned int& nNonce = *(unsigned int*)(pblock + 12);
+ unsigned int& nNonce = *(unsigned int*)(pdata + 12);
for (;;)
{
// Crypto++ SHA-256
- // Hash pblock using pmidstate as the starting state into
+ // Hash pdata using pmidstate as the starting state into
// preformatted buffer phash1, then hash phash1 into phash
nNonce++;
- SHA256Transform(phash1, pblock, pmidstate);
+ SHA256Transform(phash1, pdata, pmidstate);
SHA256Transform(phash, phash1, pSHA256InitState);
// Return the nonce if the hash has at least some zero bits,
};
+CBlock* CreateNewBlock(CReserveKey& reservekey)
+{
+ CBlockIndex* pindexPrev = pindexBest;
+
+ // Create new block
+ auto_ptr<CBlock> pblock(new CBlock());
+ if (!pblock.get())
+ return NULL;
+
+ // Create coinbase tx
+ CTransaction txNew;
+ txNew.vin.resize(1);
+ txNew.vin[0].prevout.SetNull();
+ txNew.vout.resize(1);
+ txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
+
+ // Add our coinbase tx as first transaction
+ pblock->vtx.push_back(txNew);
+
+ // Collect memory pool transactions into the block
+ int64 nFees = 0;
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_mapTransactions)
+ {
+ CTxDB txdb("r");
+
+ // Priority order to process transactions
+ list<COrphan> vOrphan; // list memory doesn't move
+ map<uint256, vector<COrphan*> > mapDependers;
+ multimap<double, CTransaction*> mapPriority;
+ for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi)
+ {
+ CTransaction& tx = (*mi).second;
+ if (tx.IsCoinBase() || !tx.IsFinal())
+ continue;
+
+ COrphan* porphan = NULL;
+ double dPriority = 0;
+ foreach(const CTxIn& txin, tx.vin)
+ {
+ // Read prev transaction
+ CTransaction txPrev;
+ CTxIndex txindex;
+ if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ {
+ // Has to wait for dependencies
+ if (!porphan)
+ {
+ // Use list for automatic deletion
+ vOrphan.push_back(COrphan(&tx));
+ porphan = &vOrphan.back();
+ }
+ mapDependers[txin.prevout.hash].push_back(porphan);
+ porphan->setDependsOn.insert(txin.prevout.hash);
+ continue;
+ }
+ int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+
+ // Read block header
+ int nConf = txindex.GetDepthInMainChain();
+
+ dPriority += (double)nValueIn * nConf;
+
+ if (fDebug && GetBoolArg("-printpriority"))
+ printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
+ }
+
+ // Priority is sum(valuein * age) / txsize
+ dPriority /= ::GetSerializeSize(tx, SER_NETWORK);
+
+ if (porphan)
+ porphan->dPriority = dPriority;
+ else
+ mapPriority.insert(make_pair(-dPriority, &(*mi).second));
+
+ if (fDebug && GetBoolArg("-printpriority"))
+ {
+ printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
+ if (porphan)
+ porphan->print();
+ printf("\n");
+ }
+ }
+
+ // Collect transactions into block
+ map<uint256, CTxIndex> mapTestPool;
+ uint64 nBlockSize = 1000;
+ int nBlockSigOps = 100;
+ while (!mapPriority.empty())
+ {
+ // Take highest priority transaction off priority queue
+ double dPriority = -(*mapPriority.begin()).first;
+ CTransaction& tx = *(*mapPriority.begin()).second;
+ mapPriority.erase(mapPriority.begin());
+
+ // Size limits
+ unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
+ if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
+ continue;
+ int nTxSigOps = tx.GetSigOpCount();
+ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+ continue;
+
+ // Transaction fee required depends on block size
+ bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
+ int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree);
+
+ // Connecting shouldn't fail due to dependency on other memory pool transactions
+ // because we're already processing them in order of dependency
+ map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
+ if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
+ continue;
+ swap(mapTestPool, mapTestPoolTmp);
+
+ // Added
+ pblock->vtx.push_back(tx);
+ nBlockSize += nTxSize;
+ nBlockSigOps += nTxSigOps;
+
+ // Add transactions that depend on this one to the priority queue
+ uint256 hash = tx.GetHash();
+ if (mapDependers.count(hash))
+ {
+ foreach(COrphan* porphan, mapDependers[hash])
+ {
+ if (!porphan->setDependsOn.empty())
+ {
+ porphan->setDependsOn.erase(hash);
+ if (porphan->setDependsOn.empty())
+ mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
+ }
+ }
+ }
+ }
+ }
+ pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
+
+ // Fill in header
+ pblock->hashPrevBlock = pindexPrev->GetBlockHash();
+ pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->nBits = GetNextWorkRequired(pindexPrev);
+ pblock->nNonce = 0;
+
+ return pblock.release();
+}
+
+
+void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime)
+{
+ // Update nExtraNonce
+ int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1)
+ {
+ nExtraNonce = 1;
+ nPrevTime = nNow;
+ }
+ pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce);
+ pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+}
+
+
+void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
+{
+ //
+ // Prebuild hash buffers
+ //
+ struct
+ {
+ struct unnamed2
+ {
+ int nVersion;
+ uint256 hashPrevBlock;
+ uint256 hashMerkleRoot;
+ unsigned int nTime;
+ unsigned int nBits;
+ unsigned int nNonce;
+ }
+ block;
+ unsigned char pchPadding0[64];
+ uint256 hash1;
+ unsigned char pchPadding1[64];
+ }
+ tmp;
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.block.nVersion = pblock->nVersion;
+ tmp.block.hashPrevBlock = pblock->hashPrevBlock;
+ tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
+ tmp.block.nTime = pblock->nTime;
+ tmp.block.nBits = pblock->nBits;
+ tmp.block.nNonce = pblock->nNonce;
+
+ FormatHashBlocks(&tmp.block, sizeof(tmp.block));
+ FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
+
+ // Byte swap all the input buffer
+ for (int i = 0; i < sizeof(tmp)/4; i++)
+ ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
+
+ // Precalc the first half of the first hash, which stays constant
+ SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
+
+ memcpy(pdata, &tmp.block, 128);
+ memcpy(phash1, &tmp.hash1, 64);
+}
+
+
+bool CheckWork(CBlock* pblock, CReserveKey& reservekey)
+{
+ uint256 hash = pblock->GetHash();
+ uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
+
+ if (hash > hashTarget)
+ return false;
+
+ //// debug print
+ printf("BitcoinMiner:\n");
+ printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+ pblock->print();
+ printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
+ printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
+
+ // Found a solution
+ CRITICAL_BLOCK(cs_main)
+ {
+ if (pblock->hashPrevBlock != hashBestChain)
+ return error("BitcoinMiner : generated block is stale");
+
+ // Remove key from key pool
+ reservekey.KeepKey();
+
+ // Track how many getdata requests this block gets
+ CRITICAL_BLOCK(cs_mapRequestCount)
+ mapRequestCount[pblock->GetHash()] = 0;
+
+ // Process this block the same as if we had received it from another node
+ if (!ProcessBlock(NULL, pblock))
+ return error("BitcoinMiner : ProcessBlock, block not accepted");
+ }
+
+ Sleep(2000);
+ return true;
+}
+
void BitcoinMiner()
{
SetThreadPriority(THREAD_PRIORITY_LOWEST);
bool f4WaySSE2 = Detect128BitSSE2();
if (mapArgs.count("-4way"))
- f4WaySSE2 = (mapArgs["-4way"] != "0");
+ f4WaySSE2 = GetBoolArg("-4way");
+ // Each thread has its own key and counter
CReserveKey reservekey;
unsigned int nExtraNonce = 0;
int64 nPrevTime = 0;
+
while (fGenerateBitcoins)
{
if (AffinityBugWorkaround(ThreadBitcoinMiner))
return;
}
- unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
- CBlockIndex* pindexPrev = pindexBest;
- unsigned int nBits = GetNextWorkRequired(pindexPrev);
-
-
- //
- // Create coinbase tx
- //
- CTransaction txNew;
- txNew.vin.resize(1);
- txNew.vin[0].prevout.SetNull();
- int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1)
- {
- nExtraNonce = 1;
- nPrevTime = nNow;
- }
- txNew.vin[0].scriptSig << nBits << CBigNum(nExtraNonce);
- txNew.vout.resize(1);
- txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
-
//
// Create new block
//
- auto_ptr<CBlock> pblock(new CBlock());
+ unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
+ CBlockIndex* pindexPrev = pindexBest;
+
+ auto_ptr<CBlock> pblock(CreateNewBlock(reservekey));
if (!pblock.get())
return;
+ IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime);
- // Add our coinbase tx as first transaction
- pblock->vtx.push_back(txNew);
-
- // Collect memory pool transactions into the block
- int64 nFees = 0;
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(cs_mapTransactions)
- {
- CTxDB txdb("r");
-
- // Priority order to process transactions
- list<COrphan> vOrphan; // list memory doesn't move
- map<uint256, vector<COrphan*> > mapDependers;
- multimap<double, CTransaction*> mapPriority;
- for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi)
- {
- CTransaction& tx = (*mi).second;
- if (tx.IsCoinBase() || !tx.IsFinal())
- continue;
-
- COrphan* porphan = NULL;
- double dPriority = 0;
- foreach(const CTxIn& txin, tx.vin)
- {
- // Read prev transaction
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
- {
- // Has to wait for dependencies
- if (!porphan)
- {
- // Use list for automatic deletion
- vOrphan.push_back(COrphan(&tx));
- porphan = &vOrphan.back();
- }
- mapDependers[txin.prevout.hash].push_back(porphan);
- porphan->setDependsOn.insert(txin.prevout.hash);
- continue;
- }
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
-
- // Read block header
- int nConf = 0;
- CBlock block;
- if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- {
- map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(block.GetHash());
- if (it != mapBlockIndex.end())
- {
- CBlockIndex* pindex = (*it).second;
- if (pindex->IsInMainChain())
- nConf = 1 + nBestHeight - pindex->nHeight;
- }
- }
-
- dPriority += (double)nValueIn * nConf;
-
- if (fDebug && mapArgs.count("-printpriority"))
- printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
- }
-
- // Priority is sum(valuein * age) / txsize
- dPriority /= ::GetSerializeSize(tx, SER_NETWORK);
-
- if (porphan)
- porphan->dPriority = dPriority;
- else
- mapPriority.insert(make_pair(-dPriority, &(*mi).second));
-
- if (fDebug && mapArgs.count("-printpriority"))
- {
- printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
- if (porphan)
- porphan->print();
- printf("\n");
- }
- }
-
- // Collect transactions into block
- map<uint256, CTxIndex> mapTestPool;
- uint64 nBlockSize = 1000;
- int nBlockSigOps = 100;
- while (!mapPriority.empty())
- {
- // Take highest priority transaction off priority queue
- double dPriority = -(*mapPriority.begin()).first;
- CTransaction& tx = *(*mapPriority.begin()).second;
- mapPriority.erase(mapPriority.begin());
-
- // Size limits
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
- if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
- continue;
- int nTxSigOps = tx.GetSigOpCount();
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- // Transaction fee required depends on block size
- bool fAllowFree = (nBlockSize + nTxSize < 4000 || dPriority > COIN * 144 / 250);
- int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree);
-
- // Connecting shouldn't fail due to dependency on other memory pool transactions
- // because we're already processing them in order of dependency
- map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
- if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
- continue;
- swap(mapTestPool, mapTestPoolTmp);
-
- // Added
- pblock->vtx.push_back(tx);
- nBlockSize += nTxSize;
- nBlockSigOps += nTxSigOps;
-
- // Add transactions that depend on this one to the priority queue
- uint256 hash = tx.GetHash();
- if (mapDependers.count(hash))
- {
- foreach(COrphan* porphan, mapDependers[hash])
- {
- if (!porphan->setDependsOn.empty())
- {
- porphan->setDependsOn.erase(hash);
- if (porphan->setDependsOn.empty())
- mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
- }
- }
- }
- }
- }
- pblock->nBits = nBits;
- pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
//
- // Prebuild hash buffer
+ // Prebuild hash buffers
//
- struct tmpworkspace
- {
- struct unnamed2
- {
- int nVersion;
- uint256 hashPrevBlock;
- uint256 hashMerkleRoot;
- unsigned int nTime;
- unsigned int nBits;
- unsigned int nNonce;
- }
- block;
- unsigned char pchPadding0[64];
- uint256 hash1;
- unsigned char pchPadding1[64];
- };
- char tmpbuf[sizeof(tmpworkspace)+16];
- tmpworkspace& tmp = *(tmpworkspace*)alignup<16>(tmpbuf);
+ char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf);
+ char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
+ char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
- tmp.block.nVersion = pblock->nVersion;
- tmp.block.hashPrevBlock = pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- tmp.block.hashMerkleRoot = pblock->hashMerkleRoot = pblock->BuildMerkleTree();
- tmp.block.nTime = pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- tmp.block.nBits = pblock->nBits = nBits;
- tmp.block.nNonce = pblock->nNonce = 0;
+ FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1);
- unsigned int nBlocks0 = FormatHashBlocks(&tmp.block, sizeof(tmp.block));
- unsigned int nBlocks1 = FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
-
- // Byte swap all the input buffer
- for (int i = 0; i < sizeof(tmp)/4; i++)
- ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
-
- // Precalc the first half of the first hash, which stays constant
- uint256 midstatebuf[2];
- uint256& midstate = *alignup<16>(midstatebuf);
- SHA256Transform(&midstate, &tmp.block, pSHA256InitState);
+ unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
+ unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
//
#ifdef FOURWAYSSE2
if (f4WaySSE2)
// tcatm's 4-way 128-bit SSE2 SHA-256
- nNonceFound = ScanHash_4WaySSE2((char*)&midstate, (char*)&tmp.block + 64, (char*)&tmp.hash1, (char*)&hash, nHashesDone);
+ nNonceFound = ScanHash_4WaySSE2(pmidstate, pdata + 64, phash1, (char*)&hash, nHashesDone);
else
#endif
// Crypto++ SHA-256
- nNonceFound = ScanHash_CryptoPP((char*)&midstate, (char*)&tmp.block + 64, (char*)&tmp.hash1, (char*)&hash, nHashesDone);
+ nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, (char*)&hash, nHashesDone);
// Check if something found
if (nNonceFound != -1)
pblock->nNonce = ByteReverse(nNonceFound);
assert(hash == pblock->GetHash());
- //// debug print
- printf("BitcoinMiner:\n");
- printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
- pblock->print();
- printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
- printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
-
SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CRITICAL_BLOCK(cs_main)
- {
- if (pindexPrev == pindexBest)
- {
- // Remove key from key pool
- reservekey.KeepKey();
-
- // Track how many getdata requests this block gets
- CRITICAL_BLOCK(cs_mapRequestCount)
- mapRequestCount[pblock->GetHash()] = 0;
-
- // Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock.release()))
- printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n");
- }
- }
+ CheckWork(pblock.get(), reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
-
- Sleep(500);
break;
}
}
return;
if (vNodes.empty())
break;
- if (tmp.block.nNonce >= 0xff000000)
+ if (nBlockNonce >= 0xffff0000)
break;
if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
// Update nTime every few seconds
pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- tmp.block.nTime = ByteReverse(pblock->nTime);
+ nBlockTime = ByteReverse(pblock->nTime);
}
}
}
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* pcoin = &(*it).second;
- if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed())
+ if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
- nTotal += pcoin->GetCredit(true);
+ nTotal += pcoin->GetAvailableCredit();
}
}
}
-bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<CWalletTx*>& setCoinsRet)
+bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
{
setCoinsRet.clear();
+ nValueRet = 0;
// List of values less than target
- int64 nLowestLarger = INT64_MAX;
- CWalletTx* pcoinLowestLarger = NULL;
- vector<pair<int64, CWalletTx*> > vValue;
+ pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
+ coinLowestLarger.first = INT64_MAX;
+ coinLowestLarger.second.first = NULL;
+ vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
int64 nTotalLower = 0;
CRITICAL_BLOCK(cs_mapWallet)
foreach(CWalletTx* pcoin, vCoins)
{
- if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed())
+ if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
+ continue;
+
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
continue;
- int64 n = pcoin->GetCredit();
- if (n <= 0)
- continue;
- if (n < nTargetValue)
- {
- vValue.push_back(make_pair(n, pcoin));
- nTotalLower += n;
- }
- else if (n == nTargetValue)
- {
- setCoinsRet.insert(pcoin);
- return true;
- }
- else if (n < nLowestLarger)
+ for (int i = 0; i < pcoin->vout.size(); i++)
{
- nLowestLarger = n;
- pcoinLowestLarger = pcoin;
+ if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine())
+ continue;
+
+ int64 n = pcoin->vout[i].nValue;
+
+ if (n <= 0)
+ continue;
+
+ pair<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
+
+ if (n == nTargetValue)
+ {
+ setCoinsRet.insert(coin.second);
+ nValueRet += coin.first;
+ return true;
+ }
+ else if (n < nTargetValue + CENT)
+ {
+ vValue.push_back(coin);
+ nTotalLower += n;
+ }
+ else if (n < coinLowestLarger.first)
+ {
+ coinLowestLarger = coin;
+ }
}
}
}
- if (nTotalLower < nTargetValue)
+ if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
+ {
+ for (int i = 0; i < vValue.size(); ++i)
+ {
+ setCoinsRet.insert(vValue[i].second);
+ nValueRet += vValue[i].first;
+ }
+ return true;
+ }
+
+ if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0))
{
- if (pcoinLowestLarger == NULL)
+ if (coinLowestLarger.second.first == NULL)
return false;
- setCoinsRet.insert(pcoinLowestLarger);
+ setCoinsRet.insert(coinLowestLarger.second);
+ nValueRet += coinLowestLarger.first;
return true;
}
+ if (nTotalLower >= nTargetValue + CENT)
+ nTargetValue += CENT;
+
// Solve subset sum by stochastic approximation
sort(vValue.rbegin(), vValue.rend());
vector<char> vfIncluded;
}
// If the next larger is still closer, return it
- if (pcoinLowestLarger && nLowestLarger - nTargetValue <= nBest - nTargetValue)
- setCoinsRet.insert(pcoinLowestLarger);
- else
+ if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue)
{
+ setCoinsRet.insert(coinLowestLarger.second);
+ nValueRet += coinLowestLarger.first;
+ }
+ else {
for (int i = 0; i < vValue.size(); i++)
if (vfBest[i])
+ {
setCoinsRet.insert(vValue[i].second);
+ nValueRet += vValue[i].first;
+ }
//// debug print
printf("SelectCoins() best subset: ");
return true;
}
-bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
+bool SelectCoins(int64 nTargetValue, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
{
- return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet) ||
- SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet) ||
- SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet));
+ return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
}
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
+bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
{
+ int64 nValue = 0;
+ foreach (const PAIRTYPE(CScript, int64)& s, vecSend)
+ {
+ if (nValue < 0)
+ return false;
+ nValue += s.second;
+ }
+ if (vecSend.empty() || nValue < 0)
+ return false;
+
CRITICAL_BLOCK(cs_main)
{
// txdb must be opened before the mapWallet lock
wtxNew.vin.clear();
wtxNew.vout.clear();
wtxNew.fFromMe = true;
- if (nValue < 0)
- return false;
- int64 nValueOut = nValue;
+
int64 nTotalValue = nValue + nFeeRet;
+ double dPriority = 0;
+ // vouts to the payees
+ foreach (const PAIRTYPE(CScript, int64)& s, vecSend)
+ wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use
- set<CWalletTx*> setCoins;
- if (!SelectCoins(nTotalValue, setCoins))
- return false;
+ set<pair<CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
- foreach(CWalletTx* pcoin, setCoins)
- nValueIn += pcoin->GetCredit();
-
- // Fill a vout to the payee
- bool fChangeFirst = GetRand(2);
- if (!fChangeFirst)
- wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
+ if (!SelectCoins(nTotalValue, setCoins, nValueIn))
+ return false;
+ foreach(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins)
+ {
+ int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
+ dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
+ }
// Fill a vout back to self with any change
int64 nChange = nValueIn - nTotalValue;
// Fill a vout to ourself, using same address type as the payment
CScript scriptChange;
- if (scriptPubKey.GetBitcoinAddressHash160() != 0)
+ if (vecSend[0].first.GetBitcoinAddressHash160() != 0)
scriptChange.SetBitcoinAddress(vchPubKey);
else
scriptChange << vchPubKey << OP_CHECKSIG;
- wtxNew.vout.push_back(CTxOut(nChange, scriptChange));
+
+ // Insert change txn at random position:
+ vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
+ wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
}
else
reservekey.ReturnKey();
- // Fill a vout to the payee
- if (fChangeFirst)
- wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
-
// Fill vin
- foreach(CWalletTx* pcoin, setCoins)
- for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
- if (pcoin->vout[nOut].IsMine())
- wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));
+ foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
+ wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
// Sign
int nIn = 0;
- foreach(CWalletTx* pcoin, setCoins)
- for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
- if (pcoin->vout[nOut].IsMine())
- if (!SignSignature(*pcoin, wtxNew, nIn++))
- return false;
+ foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
+ if (!SignSignature(*coin.first, wtxNew, nIn++))
+ return false;
// Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK);
if (nBytes >= MAX_BLOCK_SIZE_GEN/5)
return false;
+ dPriority /= nBytes;
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
- int64 nMinFee = wtxNew.GetMinFee();
+ bool fAllowFree = CTransaction::AllowFree(dPriority);
+ int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree);
if (nFeeRet < max(nPayFee, nMinFee))
{
nFeeRet = max(nPayFee, nMinFee);
return true;
}
+bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
+{
+ vector< pair<CScript, int64> > vecSend;
+ vecSend.push_back(make_pair(scriptPubKey, nValue));
+ return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
+}
+
// Call after CreateTransaction unless you want to abort
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
// Mark old coins as spent
set<CWalletTx*> setCoins;
foreach(const CTxIn& txin, wtxNew.vin)
- setCoins.insert(&mapWallet[txin.prevout.hash]);
- foreach(CWalletTx* pcoin, setCoins)
{
- pcoin->fSpent = true;
- pcoin->WriteToDisk();
- vWalletUpdated.push_back(pcoin->GetHash());
+ CWalletTx &pcoin = mapWallet[txin.prevout.hash];
+ pcoin.MarkSpent(txin.prevout.n);
+ pcoin.WriteToDisk();
+ vWalletUpdated.push_back(pcoin.GetHash());
}
}
+// requires cs_main lock
string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
- CRITICAL_BLOCK(cs_main)
+ CReserveKey reservekey;
+ int64 nFeeRequired;
+ if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
{
- CReserveKey reservekey;
- int64 nFeeRequired;
- if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
- {
- string strError;
- if (nValue + nFeeRequired > GetBalance())
- strError = strprintf(_("Error: This is an oversized transaction that requires a transaction fee of %s "), FormatMoney(nFeeRequired).c_str());
- else
- strError = _("Error: Transaction creation failed ");
- printf("SendMoney() : %s", strError.c_str());
- return strError;
- }
+ string strError;
+ if (nValue + nFeeRequired > GetBalance())
+ strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str());
+ else
+ strError = _("Error: Transaction creation failed ");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
+ }
- if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
- return "ABORTED";
+ if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
+ return "ABORTED";
+
+ if (!CommitTransaction(wtxNew, reservekey))
+ return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
- if (!CommitTransaction(wtxNew, reservekey))
- return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
- }
MainFrameRepaint();
return "";
}
+// requires cs_main lock
string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
// Check amount