CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { // "Dust" is defined in terms of dustRelayFee, // which has units satoshis-per-kilobyte. // If you'd pay more than 1/3 in fees // to spend something, then we consider it dust. // A typical spendable non-segwit txout is 34 bytes big, and will // need a CTxIn of at least 148 bytes to spend: // so dust is a spendable txout less than // 546*dustRelayFee/1000 (in satoshis). // A typical spendable segwit txout is 31 bytes big, and will // need a CTxIn of at least 67 bytes to spend: // so dust is a spendable txout less than // 294*dustRelayFee/1000 (in satoshis). if (txout.scriptPubKey.IsUnspendable()) return 0; size_t nSize = GetSerializeSize(txout, SER_DISK, 0); int witnessversion = 0; std::vector<unsigned char> witnessprogram; if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { // sum the sizes of the parts of a transaction input // with 75% segwit discount applied to the script size. nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); } else { nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above } return 3 * dustRelayFeeIn.GetFee(nSize); }
ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<uint256, CTransactionRef>>& extra_txn) { if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty())) return READ_STATUS_INVALID; if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT) return READ_STATUS_INVALID; assert(header.IsNull() && txn_available.empty()); header = cmpctblock.header; txn_available.resize(cmpctblock.BlockTxCount()); int32_t lastprefilledindex = -1; for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) { if (cmpctblock.prefilledtxn[i].tx->IsNull()) return READ_STATUS_INVALID; lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here if (lastprefilledindex > std::numeric_limits<uint16_t>::max()) return READ_STATUS_INVALID; if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) { // If we are inserting a tx at an index greater than our full list of shorttxids // plus the number of prefilled txn we've inserted, then we have txn for which we // have neither a prefilled txn or a shorttxid! return READ_STATUS_INVALID; } txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx; } prefilled_count = cmpctblock.prefilledtxn.size(); // Calculate map of txids -> positions and check mempool to see what we have (or don't) // Because well-formed cmpctblock messages will have a (relatively) uniform distribution // of short IDs, any highly-uneven distribution of elements can be safely treated as a // READ_STATUS_FAILED. std::unordered_map<uint64_t, uint16_t> shorttxids(cmpctblock.shorttxids.size()); uint16_t index_offset = 0; for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++) { while (txn_available[i + index_offset]) index_offset++; shorttxids[cmpctblock.shorttxids[i]] = i + index_offset; // To determine the chance that the number of entries in a bucket exceeds N, // we use the fact that the number of elements in a single bucket is // binomially distributed (with n = the number of shorttxids S, and p = // 1 / the number of buckets), that in the worst case the number of buckets is // equal to S (due to std::unordered_map having a default load factor of 1.0), // and that the chance for any bucket to exceed N elements is at most // buckets * (the chance that any given bucket is above N elements). // Thus: P(max_elements_per_bucket > N) <= S * (1 - cdf(binomial(n=S,p=1/S), N)). // If we assume blocks of up to 16000, allowing 12 elements per bucket should // only fail once per ~1 million block transfers (per peer and connection). if (shorttxids.bucket_size(shorttxids.bucket(cmpctblock.shorttxids[i])) > 12) return READ_STATUS_FAILED; } // TODO: in the shortid-collision case, we should instead request both transactions // which collided. Falling back to full-block-request here is overkill. if (shorttxids.size() != cmpctblock.shorttxids.size()) return READ_STATUS_FAILED; // Short ID collision std::vector<bool> have_txn(txn_available.size()); { LOCK(pool->cs); const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes; for (size_t i = 0; i < vTxHashes.size(); i++) { uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first); std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); if (idit != shorttxids.end()) { if (!have_txn[idit->second]) { txn_available[idit->second] = vTxHashes[i].second->GetSharedTx(); have_txn[idit->second] = true; mempool_count++; } else { // If we find two mempool txn that match the short id, just request it. // This should be rare enough that the extra bandwidth doesn't matter, // but eating a round-trip due to FillBlock failure would be annoying if (txn_available[idit->second]) { txn_available[idit->second].reset(); mempool_count--; } } } // Though ideally we'd continue scanning for the two-txn-match-shortid case, // the performance win of an early exit here is too good to pass up and worth // the extra risk. if (mempool_count == shorttxids.size()) break; } } for (size_t i = 0; i < extra_txn.size(); i++) { uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first); std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); if (idit != shorttxids.end()) { if (!have_txn[idit->second]) { txn_available[idit->second] = extra_txn[i].second; have_txn[idit->second] = true; mempool_count++; extra_count++; } else { // If we find two mempool/extra txn that match the short id, just // request it. // This should be rare enough that the extra bandwidth doesn't matter, // but eating a round-trip due to FillBlock failure would be annoying // Note that we don't want duplication between extra_txn and mempool to // trigger this case, so we compare witness hashes first if (txn_available[idit->second] && txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) { txn_available[idit->second].reset(); mempool_count--; extra_count--; } } } // Though ideally we'd continue scanning for the two-txn-match-shortid case, // the performance win of an early exit here is too good to pass up and worth // the extra risk. if (mempool_count == shorttxids.size()) break; } LogPrint(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION)); return READ_STATUS_OK; }
std::string TxToString(uint256 BlockHash, const CTransaction& tx) { CAmount Input = 0; CAmount Output = tx.GetValueOut(); std::string InputsContentCells[] = {_("#"), _("Taken from"), _("Address"), _("Amount")}; std::string InputsContent = makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); std::string OutputsContentCells[] = {_("#"), _("Redeemed in"), _("Address"), _("Amount")}; std::string OutputsContent = makeHTMLTableRow(OutputsContentCells, sizeof(OutputsContentCells) / sizeof(std::string)); if (tx.IsCoinBase()) { std::string InputsContentCells[] = { "0", "coinbase", "-", ValueToString(Output)}; InputsContent += makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); } else for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint Out = tx.vin[i].prevout; CTxOut PrevOut = getPrevOut(tx.vin[i].prevout); if (PrevOut.nValue < 0) Input = -Params().MaxMoneyOut(); else Input += PrevOut.nValue; std::string InputsContentCells[] = { itostr(i), "<span>" + makeHRef(Out.hash.GetHex()) + ":" + itostr(Out.n) + "</span>", ScriptToString(PrevOut.scriptPubKey, true), ValueToString(PrevOut.nValue)}; InputsContent += makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); } uint256 TxHash = tx.GetHash(); for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& Out = tx.vout[i]; uint256 HashNext = uint256S("0"); unsigned int nNext = 0; bool fAddrIndex = false; getNextIn(COutPoint(TxHash, i), HashNext, nNext); std::string OutputsContentCells[] = { itostr(i), (HashNext == uint256S("0")) ? (fAddrIndex ? _("no") : _("unknown")) : "<span>" + makeHRef(HashNext.GetHex()) + ":" + itostr(nNext) + "</span>", ScriptToString(Out.scriptPubKey, true), ValueToString(Out.nValue)}; OutputsContent += makeHTMLTableRow(OutputsContentCells, sizeof(OutputsContentCells) / sizeof(std::string)); } InputsContent = table + InputsContent + "</table>"; OutputsContent = table + OutputsContent + "</table>"; std::string Hash = TxHash.GetHex(); std::string Labels[] = { _("In Block"), "", _("Size"), itostr(GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)), _("Input"), tx.IsCoinBase() ? "-" : ValueToString(Input), _("Output"), ValueToString(Output), _("Fees"), tx.IsCoinBase() ? "-" : ValueToString(Input - Output), _("Timestamp"), "", _("Hash"), "<pre>" + Hash + "</pre>", }; // std::map<uint256, CBlockIndex*>::iterator iter = mapBlockIndex.find(BlockHash); BlockMap::iterator iter = mapBlockIndex.find(BlockHash); if (iter != mapBlockIndex.end()) { CBlockIndex* pIndex = iter->second; Labels[0 * 2 + 1] = makeHRef(itostr(pIndex->nHeight)); Labels[5 * 2 + 1] = TimeToString(pIndex->nTime); } std::string Content; Content += "<h2>" + _("Transaction") + " <span>" + Hash + "</span></h2>"; Content += makeHTMLTable(Labels, sizeof(Labels) / (2 * sizeof(std::string)), 2); Content += "</br>"; Content += "<h3>" + _("Inputs") + "</h3>"; Content += InputsContent; Content += "</br>"; Content += "<h3>" + _("Outputs") + "</h3>"; Content += OutputsContent; return Content; }
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx) { int64_t nTimeStart = GetTimeMicros(); resetBlock(); pblocktemplate.reset(new CBlockTemplate()); if(!pblocktemplate.get()) return nullptr; pblock = &pblocktemplate->block; // pointer for convenience // Add dummy coinbase tx as first transaction pblock->vtx.emplace_back(); pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); nHeight = pindexPrev->nHeight + 1; pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) pblock->nVersion = GetArg("-blockversion", pblock->nVersion); pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : pblock->GetBlockTime(); // Decide whether to include witness transactions // This is only needed in case the witness softfork activation is reverted // (which would require a very deep reorganization) or when // -promiscuousmempoolflags is used. // TODO: replace this with a call to main to assess validity of a mempool // transaction (which in most cases can be a no-op). fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx; int nPackagesSelected = 0; int nDescendantsUpdated = 0; addPackageTxs(nPackagesSelected, nDescendantsUpdated); int64_t nTime1 = GetTimeMicros(); nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; nLastBlockWeight = nBlockWeight; // Create coinbase transaction. CMutableTransaction coinbaseTx; coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout.SetNull(); coinbaseTx.vout.resize(1); coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION); LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nNonce = 0; pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); CValidationState state; if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); } int64_t nTime2 = GetTimeMicros(); LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", 0.001 * (nTime1 - nTimeStart), nPackagesSelected, nDescendantsUpdated, 0.001 * (nTime2 - nTime1), 0.001 * (nTime2 - nTimeStart)); return std::move(pblocktemplate); }
std::string BlockToString(CBlockIndex* pBlock) { if (!pBlock) return ""; CBlock block; ReadBlockFromDisk(block, pBlock); CAmount Fees = 0; CAmount OutVolume = 0; CAmount Reward = 0; std::string TxLabels[] = {_("Hash"), _("From"), _("Amount"), _("To"), _("Amount")}; std::string TxContent = table + makeHTMLTableRow(TxLabels, sizeof(TxLabels) / sizeof(std::string)); for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction& tx = block.vtx[i]; TxContent += TxToRow(tx); CAmount In = getTxIn(tx); CAmount Out = tx.GetValueOut(); if (tx.IsCoinBase()) Reward += Out; else if (In < 0) Fees = -Params().MaxMoneyOut(); else { Fees += In - Out; OutVolume += Out; } } TxContent += "</table>"; CAmount Generated; if (pBlock->nHeight == 0) Generated = OutVolume; else Generated = GetBlockValue(pBlock->nHeight - 1); std::string BlockContentCells[] = { _("Height"), itostr(pBlock->nHeight), _("Size"), itostr(GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)), _("Number of Transactions"), itostr(block.vtx.size()), _("Value Out"), ValueToString(OutVolume), _("Fees"), ValueToString(Fees), _("Generated"), ValueToString(Generated), _("Timestamp"), TimeToString(block.nTime), _("Difficulty"), strprintf("%.4f", GetDifficulty(pBlock)), _("Bits"), utostr(block.nBits), _("Nonce"), utostr(block.nNonce), _("Version"), itostr(block.nVersion), _("Hash"), "<pre>" + block.GetHash().GetHex() + "</pre>", _("Merkle Root"), "<pre>" + block.hashMerkleRoot.GetHex() + "</pre>", // _("Hash Whole Block"), "<pre>" + block.hashWholeBlock.GetHex() + "</pre>" // _("Miner Signature"), "<pre>" + block.MinerSignature.ToString() + "</pre>" }; std::string BlockContent = makeHTMLTable(BlockContentCells, sizeof(BlockContentCells) / (2 * sizeof(std::string)), 2); std::string Content; Content += "<h2><a class=\"nav\" href="; Content += itostr(pBlock->nHeight - 1); Content += ">◄ </a>"; Content += _("Block"); Content += " "; Content += itostr(pBlock->nHeight); Content += "<a class=\"nav\" href="; Content += itostr(pBlock->nHeight + 1); Content += "> ►</a></h2>"; Content += BlockContent; Content += "</br>"; /* if (block.nHeight > getThirdHardforkBlock()) { std::vector<std::string> votes[2]; for (int i = 0; i < 2; i++) { for (unsigned int j = 0; j < block.vvotes[i].size(); j++) { votes[i].push_back(block.vvotes[i][j].hash.ToString() + ':' + itostr(block.vvotes[i][j].n)); } } Content += "<h3>" + _("Votes +") + "</h3>"; Content += makeHTMLTable(&votes[1][0], votes[1].size(), 1); Content += "</br>"; Content += "<h3>" + _("Votes -") + "</h3>"; Content += makeHTMLTable(&votes[0][0], votes[0].size(), 1); Content += "</br>"; } */ Content += "<h2>" + _("Transactions") + "</h2>"; Content += TxContent; return Content; }