The Transaction That Changed Its Own Name — JavaScript Bug Hunt
Modelled on transaction malleability as exploited against Mt.
- Language: JavaScript
- Layer: Backend
- Difficulty: Hard
- Concepts: Hashing, Idempotency
- Modelled on: Mt. Gox · 2014
- Visible tests: different transfers have different ids; re-encoding the signature does not change the id
- Reward: 50 XP for a complete fix
Briefing
Modelled on transaction malleability as exploited against Mt. Gox (2014): a transaction's id was derived from data that a third party could tamper with without invalidating it. Withdrawals were re-broadcast under a new id, the exchange's bookkeeping never saw the original settle, and it re-sent the funds.
txid.js computes a transaction id over fields that include a mutable signature blob.
Fix computeId so the id depends only on the immutable parts of the transaction.
Bug report
BUG-MALLEABLE · Priority: Critical (double spend) · Reported by: treasury
computeId(tx) must produce the same id for two transactions that move the same funds, even if their signature fields differ — the signature is not part of a transaction's identity.
Observed: re-encoding the signature yields a different id, so the ledger treats a re-broadcast withdrawal as brand new and pays out twice.
Logs
[ledger] withdrawal wd-7781 settled under two distinct ids
[ledger] duplicate payout detected after reconciliation: 1 of 219The code as shipped
src/tx/txid.js (editable)
function hash(text) {
var h = 2166136261;
for (var i = 0; i < text.length; i++) {
h = h ^ text.charCodeAt(i);
h = (h * 16777619) >>> 0;
}
return h.toString(16);
}
// Identity of a transaction.
exports.computeId = function (tx) {
return hash(tx.from + "|" + tx.to + "|" + tx.amount + "|" + tx.nonce + "|" + tx.signature);
};
Read-only context: src/tx/PROTOCOL.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.