diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/rpc.cpp | 61 |
1 files changed, 48 insertions, 13 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index 41c0328..d73b09c 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -167,29 +167,64 @@ static bool RebuildJob() | |||
| 167 | CBlock block; | 167 | CBlock block; |
| 168 | block.vtx.push_back(txNew); | 168 | block.vtx.push_back(txNew); |
| 169 | 169 | ||
| 170 | // Fill the block the same way BitcoinMiner() does, because this is the | ||
| 171 | // same job: a block that gets mined and submitted for real. | ||
| 172 | // | ||
| 173 | // It used to differ in two ways, and both were bugs. | ||
| 174 | // | ||
| 175 | // One pass over mapTransactions is not enough. That map is keyed by | ||
| 176 | // hash, so it has no dependency order, and a transaction spending the | ||
| 177 | // still-unconfirmed output of another (change from a recent send, most | ||
| 178 | // commonly) fails ConnectInputs whenever its parent happens to sit | ||
| 179 | // later in map order. A single pass then drops it -- and because map | ||
| 180 | // order does not change between rebuilds, every later rebuild drops it | ||
| 181 | // again at the same point. Deterministic exclusion, not bad luck: if | ||
| 182 | // pool operators mine most of the blocks, a transaction of that shape | ||
| 183 | // can stay unconfirmed for as long as its parent does. | ||
| 184 | // | ||
| 185 | // Diagnosis and fix both come from Vic-Nas, who found it in his fork | ||
| 186 | // of this code and described it exactly. The fork has since been | ||
| 187 | // deleted, so this carries it back rather than losing it. | ||
| 188 | int64 nFees = 0; | ||
| 170 | { | 189 | { |
| 171 | CTxDB txdb("r"); | 190 | CTxDB txdb("r"); |
| 172 | CRITICAL_BLOCK(cs_mapTransactions) | 191 | CRITICAL_BLOCK(cs_mapTransactions) |
| 173 | { | 192 | { |
| 174 | map<uint256, CTxIndex> pool; | 193 | map<uint256, CTxIndex> pool; |
| 194 | vector<char> vfAlreadyAdded(mapTransactions.size()); | ||
| 195 | bool fFoundSomething = true; | ||
| 175 | unsigned int sz = 0; | 196 | unsigned int sz = 0; |
| 176 | for (auto& kv : mapTransactions) { | 197 | while (fFoundSomething && sz < MAX_SIZE / 2) |
| 177 | CTransaction& tx = kv.second; | 198 | { |
| 178 | if (tx.IsCoinBase() || !tx.IsFinal()) continue; | 199 | fFoundSomething = false; |
| 179 | int64 nFees = 0; | 200 | unsigned int n = 0; |
| 180 | map<uint256, CTxIndex> tmp(pool); | 201 | for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); |
| 181 | if (!tx.ConnectInputs(txdb, tmp, CDiskTxPos(1,1,1), 0, nFees, | 202 | mi != mapTransactions.end(); ++mi, ++n) |
| 182 | false, true, tx.GetMinFee(block.vtx.size() < 100))) | 203 | { |
| 183 | continue; | 204 | if (vfAlreadyAdded[n]) |
| 184 | pool = tmp; | 205 | continue; |
| 185 | block.vtx.push_back(tx); | 206 | CTransaction& tx = (*mi).second; |
| 186 | sz += ::GetSerializeSize(tx, SER_NETWORK); | 207 | if (tx.IsCoinBase() || !tx.IsFinal()) continue; |
| 187 | if (sz > MAX_SIZE / 2) break; | 208 | map<uint256, CTxIndex> tmp(pool); |
| 209 | if (!tx.ConnectInputs(txdb, tmp, CDiskTxPos(1,1,1), 0, nFees, | ||
| 210 | false, true, tx.GetMinFee(block.vtx.size() < 100))) | ||
| 211 | continue; | ||
| 212 | swap(pool, tmp); | ||
| 213 | block.vtx.push_back(tx); | ||
| 214 | sz += ::GetSerializeSize(tx, SER_NETWORK); | ||
| 215 | vfAlreadyAdded[n] = true; | ||
| 216 | fFoundSomething = true; | ||
| 217 | } | ||
| 188 | } | 218 | } |
| 189 | } | 219 | } |
| 190 | } | 220 | } |
| 191 | 221 | ||
| 192 | block.vtx[0].vout[0].nValue = block.GetBlockValue(pindexPrev->nHeight + 1, 0); | 222 | // And the fees actually go to the coinbase now. nFees was declared |
| 223 | // inside the scan loop, so every transaction reset it and the total was | ||
| 224 | // thrown away, while this line passed a hardcoded 0. A pool operator | ||
| 225 | // collected fees into a block and paid none of them to itself. Solo | ||
| 226 | // mining has always passed nFees here; only this builder did not. | ||
| 227 | block.vtx[0].vout[0].nValue = block.GetBlockValue(pindexPrev->nHeight + 1, nFees); | ||
| 193 | block.hashPrevBlock = pindexPrev->GetBlockHash(); | 228 | block.hashPrevBlock = pindexPrev->GetBlockHash(); |
| 194 | block.hashMerkleRoot = block.BuildMerkleTree(); | 229 | block.hashMerkleRoot = block.BuildMerkleTree(); |
| 195 | block.nTime = max((unsigned int)(pindexPrev->GetMedianTimePast() + 1), | 230 | block.nTime = max((unsigned int)(pindexPrev->GetMedianTimePast() + 1), |