Hmmm...am I drunk?
Let's say I am a rich guy with millions of nxt. That means I will forge a block quite often. When a new block is forged by someone, my counter in the client gets updated.
If the new value of the counter is very low, let's say 15 seconds, I can be quite confident to forge the next block. So I start a transaction and transfer some nxt to an exchange.
When I forge the block my transaction gets included in that block and in a matter of minutes, my transaction will get validated and I can use my nxt on that exchange.
I convert the nxt on the exchange into BTC and transfer them to some BTC wallet.
Now the dark part of my mind thinks: I got the BTC but I would like to have my nxt back too. If I only could eliminate the transaction to the exchange...
Well, I forged the block, so in a certain sense I own that block. Let's change the data of the block!
1) I throw out the transaction to the exchange, update the relevant fields (number of Transactions, totalamount, totalFee, payloadLength, payloadHash).
2) I leave the timestamp as it is so nobody get suspicious.
3) Finally I sign the block (change the block signature).
Since I have many nxt, my public node is attractive. Many peers are asking for blocks and in some cases for older blocks too (we all know how often we had to start from scratch
).
Will a peer accept my corrupted block? Let's check. The peer on the other side calls pushBlock to test whether the block is acceptable. In pushBlock the client checks:
if (blockTimestamp > curTime + 15 || blockTimestamp <= Block.getLastBlock().timestamp)
{
return false;
}
if ((payloadLength > 32640) || (224 + payloadLength != buffer.capacity()))
{
return false;
}
No Problem, we didn't change the blockTimestamp and the payloadLength got updated.
if (block.previousBlock != lastBlock ||
blocks.get(block.getId()) != null ||
!block.verifyGenerationSignature() ||
!block.verifyBlockSignature())
{
return false;
}
No problem, the generation signature didn't change and we were allowed to give the Block a new block signature.
Next, the peer checks the transactions. Since we simply deleted our transaction he will not complain. He will also not complain when checking the balances later.
The numberOfTransactions, totalAmount and totalFee got updated so
if (i != block.numberOfTransactions ||
calculatedTotalAmount != block.totalAmount ||
calculatedTotalFee != block.totalFee)
{
return false;
}
should not be a problem.
The payloadHash got updated too so
if (!Arrays.equals(digest.digest(), block.payloadHash))
{
return false;
}
should be ok.
That's it, the peer has no way to find out that the block is corrupted.
The more clients get the corrupted block, the more the network is corrupted. If rich nxt accounts collude the situation gets worse because it's easier to distribute the corrupted block.
I have my transaction deleted and the guy running the exchange (let's call the exchange DGOX), who is struggling to manage all the transfers in his exchange manually will at some point ask himself why there are some nxt missing.
If my analysis is correct, this is a serious bug in version 0.4.7e.