Skip to content

Instantly share code, notes, and snippets.

@rithvikvibhu
Last active January 1, 2023 20:15
Show Gist options
  • Save rithvikvibhu/a93fa2958a62e505bdcedbaa757345ca to your computer and use it in GitHub Desktop.
Save rithvikvibhu/a93fa2958a62e505bdcedbaa757345ca to your computer and use it in GitHub Desktop.
// Based on: https://gist.github.com/pinheadmz/b51a6ee3c1b414bd7d053951b83f6162
// USAGE: hsd --log-console=false --plugins=/path/to/this/file/shakedex-scan-plugin.js
'use strict';
const layout = require('hsd/lib/blockchain/layout');
const TXMeta = require('hsd/lib/primitives/txmeta');
const { LOCKTIME_FLAG } = require('hsd/lib/protocol/consensus');
const plugin = exports;
class Plugin {
constructor(node) {
this.chain = node.chain;
// Run after chain is ready
this.chain.once('tip', async (entry) => {
console.log('::: Scan for shakedex v1 transfers with bad locktime :::');
this.run();
});
}
async run() {
const start = Date.now();
let total = 0; // Total number of txs scanned
let matches = []; // Matched txs
// Extended txs iterator
const iter = this.chain.db.db.iterator({
gte: layout.t.min(),
lte: layout.t.max(),
values: true,
});
while (await iter.next()) {
total++;
if (total % 100000 === 0) console.log('Scanned', total, 'txs.')
const { value } = iter;
const meta = TXMeta.decode(value);
const tx = meta.tx;
// Skip if coinbase
if (tx.isCoinbase()) continue;
// Skip if no locktime
if (tx.locktime === 0) continue;
// Skip if seq not final
for (const input of tx.inputs) {
if (input.sequence !== 0xFFFFFFFF) continue;
}
// if (tx.inputs[0].sequence !== 0xFFFFFFFF) continue;
let mtp;
// locktime check
if (tx.locktime & LOCKTIME_FLAG) {
// time based locktime
// -> don't care because shakedex v1 marks it as height, not time
// -> even if the value is time
continue;
} else {
// height based locktime
// -> consider value as time, not height because shakedex v1
const entry = await this.chain.getEntryByHash(meta.block);
mtp = await this.chain.getMedianTime(entry);
if (tx.locktime < mtp) {
// Time has passed locktime, it's fine
continue;
}
}
// Locktime is in the future, log it.
const match = {
height: meta.height,
block: meta.block.toString('hex'),
txhash: tx.hash().toString('hex'),
namehash: tx.outputs[0].covenant.getHash(0).toString('hex'),
validAfter: new Date(tx.locktime * 1000),
boughtAt: new Date(mtp * 1000),
difference: `${((tx.locktime - mtp) / 60).toFixed(2)} minutes`,
};
matches.push(match);
console.log(`Possible exploit ${matches.length}:`, match);
}
console.log('DONE.');
console.log(`elapsed time: ${Date.now() - start} ms`);
console.log(`Scanned txs: ${total}\tMatches found: ${matches.length}`);
console.table(matches);
}
}
plugin.id = 'shakedex-scan';
plugin.init = function init(node) {
return new Plugin(node);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment