I am sure if anyone finds this thread interesting or not (or if I am failing to explain it properly), but I did solve one mystery:
Why were only SOME of the suspected Satoshi SPENT blocks following the lowest transaction hash pattern (Yellow highlighted ones) and others did not?If you look closely at the spreadsheet above notice that the
19 coinbase mined blocks identified were only involved in
eight [8] transactions sending bitcoins:
1) 9 -> sent
10 BTC to Hal
(earliest transaction hash pattern)2) 286 -> sent
25 BTC
(earliest transaction hash pattern)3) 2459 & 2485 --> sent
100 (did NOT follow earliest transaction hash pattern)4) 688 -> sent
50 BTC
(earliest transaction hash pattern)5) 5326 -> sent
50 BTC
(earliest transaction hash pattern)6) 1760, 3479, 9443, 9925, 10645, 14450, 15817, 19093, 23014, & 28593 --> sent
500 BTC
(did NOT follow earliest transaction hash pattern)7) 877 & 15625 --> Sent
100 BTC
(did NOT follow earliest transaction hash pattern)8] 29097 -> sent
50 BTC
(earliest transaction hash pattern)So 5 out of 8 followed earliest transaction hash pattern and all 5 were 50 BTC or less so only requiring
one 'input' transaction to be involved from the Wallet, but when 2 or MORE input transactions were involved it didn't follow the pattern. A bit more research and I think I figured out why:
[RFD] Coin selection algorithm and anonymityThe selection algorithm, which I call the "best fit" algorithm, boils down to:
If there is a coin which exactly matches the payment amount, then use it
Otherwise, find the coin (if any) which exceeds the payment amount by the least amount; i.e. if the payment is 10BTC and you have an 11BTC coin and a 15BTC coin, use the 11BTC coin
Otherwise, find the best combination of coins which sums closest to the payment amount.
The selection algorithm makes it a little easier for someone to track the ownership of a given coin, which could allow the tracker to link two transactions to a given individual.
so it did
not use the first transaction hash available when it did this logic: "
Otherwise, find the best combination of coins which sums closest to the payment amount."
Here is the function from early main.cpp for reference:
bool SelectCoins(int64 nTargetValue, set
& setCoinsRet)
{
setCoinsRet.clear();
// List of values less than target
int64 nLowestLarger = _I64_MAX;
CWalletTx* pcoinLowestLarger = NULL;
vector > vValue;
int64 nTotalLower = 0;
CRITICAL_BLOCK(cs_mapWallet)
{
for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || pcoin->fSpent)
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)
{
nLowestLarger = n;
pcoinLowestLarger = pcoin;
}
}
}
if (nTotalLower < nTargetValue)
{
if (pcoinLowestLarger == NULL)
return false;
setCoinsRet.insert(pcoinLowestLarger);
return true;
}
// Solve subset sum by stochastic approximation
sort(vValue.rbegin(), vValue.rend());
vector vfIncluded;
vector vfBest(vValue.size(), true);
int64 nBest = nTotalLower;
for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++)
{
vfIncluded.assign(vValue.size(), false);
int64 nTotal = 0;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
for (int i = 0; i < vValue.size(); i++)
{
if (nPass == 0 ? rand() % 2 : !vfIncluded[i])
{
nTotal += vValue[i].first;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
fReachedTarget = true;
if (nTotal < nBest)
{
nBest = nTotal;
vfBest = vfIncluded;
}
nTotal -= vValue[i].first;
vfIncluded[i] = false;
}
}
}
}
}
// If the next larger is still closer, return it
if (pcoinLowestLarger && nLowestLarger - nTargetValue <= nBest - nTargetValue)
setCoinsRet.insert(pcoinLowestLarger);
else
{
for (int i = 0; i < vValue.size(); i++)
if (vfBest[i])
setCoinsRet.insert(vValue[i].second);
//// debug print
printf("SelectCoins() best subset: ");
for (int i = 0; i < vValue.size(); i++)
if (vfBest[i])
printf("%s ", FormatMoney(vValue[i].first).c_str());
printf("total %s\n", FormatMoney(nBest).c_str());
}
return true;I am not a C++ coder, but it appears when the desired amount to send (nTargetValue) is less (or equal to?) any of the wallet's final and unspent transactions then it simply grabs the first one (by transaction hash order it seems) and uses its coins for the transaction:
for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
but when
2 or more are required it kicks into a different logic to
"find the best combination of coins which sums closest to the payment amount" including changing the order of the transactions to be considered via this line I believe:
sort(vValue.rbegin(), vValue.rend());
I still find the final transaction d3b94dcede3cbb08c7c0fdd1889478baa5a0b482cd917f276fa07be702326385 sending 50 BTC coins on /22/2010 using coins from coinbase transaction 000FB5BEC80D688D4F4CAD4F969BDAD655CED86248007E92C9500D70E00AD204 most interesting considering it used the first non-spent transactions out of 69,613 previous confirmed blocks and it conforms to the Satoshi mining patterns.
All of this simply explains why there was a stark difference in transaction hash selection if all of these spent coins were
supposedly from Satoshi's own wallet. Otherwise, my theories were either the other transactions were
not Satoshi's or somehow another wallet was in use by him, but this discovery provides a third explanation where it could still be Satoshi using his big old fat wallet.
To be honest I was secretly hoping that Satoshi's special miner was simply burning the coins he mined to keep bitcoin going in the early days, but now I don't believe that is the case. I guess he had to test out his wallet code he was actively involved in developing/maintaining before he move on so why not stress test it with his own massive coin collection?