Skip to content

Instantly share code, notes, and snippets.

@relayking
Created March 2, 2020 09:01
Show Gist options
  • Save relayking/7a0c73c8a687f5af18d7e0e814be7bdc to your computer and use it in GitHub Desktop.
Save relayking/7a0c73c8a687f5af18d7e0e814be7bdc to your computer and use it in GitHub Desktop.
fastor 21e8
const axios = require('axios');
const bsv = require('bsv');
const chalk = require('chalk');
const secp256k1 = require('secp256k1')
const PrivateKey = bsv.PrivateKey;
const Opcode = bsv.Opcode;
const Transaction = bsv.Transaction;
const crypto = require('crypto');
function sha256(data) {
return crypto.createHash('sha256').update(data).digest('hex')
}
const sigtype = bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID;
const flags = bsv.Script.Interpreter.SCRIPT_VERIFY_MINIMALDATA | bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID | bsv.Script.Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES | bsv.Script.Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES;
function is21e8Out(script) {
return !!(
script.chunks.length === 12 &&
script.chunks[0].buf &&
script.chunks[0].buf.length === 32 &&
script.chunks[1].buf &&
script.chunks[1].buf.length >= 1 &&
script.chunks[2].opcodenum === Opcode.OP_SIZE &&
script.chunks[3].opcodenum === Opcode.OP_4 &&
script.chunks[4].opcodenum === Opcode.OP_PICK &&
script.chunks[5].opcodenum === Opcode.OP_SHA256 &&
script.chunks[6].opcodenum === Opcode.OP_SWAP &&
script.chunks[7].opcodenum === Opcode.OP_SPLIT &&
script.chunks[8].opcodenum === Opcode.OP_DROP &&
script.chunks[9].opcodenum === Opcode.OP_EQUALVERIFY &&
script.chunks[10].opcodenum === Opcode.OP_DROP &&
script.chunks[11].opcodenum === Opcode.OP_CHECKSIG
);
}
let count = 0;
const st = new Date()
function sign(hashbuf, target=''){
const privKey = PrivateKey.fromRandom();
const sig = secp256k1.signatureExport(secp256k1.signatureNormalize(secp256k1.ecdsaSign(hashbuf, privKey.toBuffer()).signature));
count ++;
if(target!=''){
const sig256 = sha256(Buffer.concat([sig, Buffer.from(sigtype.toString(16), 'hex')]));
if(!sig256.startsWith(target)){
if (count % 1000 === 0) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(chalk.gray(count / (+ new Date() - st) * 1000));
}
return [false, false];
} else {
return [sig, privKey];
}
}
return [sig, privKey];
}
const start = async() => {
try {
const txid = '7e4479e828eae6f7d465a26a25c769e886e933df3432b55c60b9a31e9e985b45';
if(txid === 'exit') return; //let them exit
let tx;
try {
const {data} = await axios.get(`https://api.whatsonchain.com/v1/bsv/main/tx/hash/${txid}`);
tx = data;
} catch(e) {
throw("TX not found.");
}
let index = -1;
for(let i=0; i<tx.vout.length; i++) {
if(is21e8Out(bsv.Script.fromHex(tx.vout[i].scriptPubKey.hex))){
index = i;
break;
}
}
if(index<0){
throw("No 21e8 outputs found");
}
let {to} = {to: '1KzCAPfsGzmNukukPcZSwyhAAdNFd5MCnG'};
if(txid === 'exit') return; //let them exit
if(!to.length){
throw("No address found.");
}
try {
to = bsv.Script.buildPublicKeyHashOut(to);
} catch(e){
throw("Invalid address");
}
console.log("Automatically publish when mined? Y/N");
let {publish} = {publish: 'y'};
publish = (publish.toLowerCase()[0] == 'y') ? true : false;
console.log(chalk.green(`Mining TX ${txid} output ${index}`));
console.log(chalk.green(`Pay to: ${to}`));
mineId(tx, index, to, publish);
} catch(e){
console.log(chalk.red(e));
start();
}
}
const mineId = async(from, index, to, publish) => {
const vout = from.vout[index];
const value = Math.floor(vout.value*1e8);
const targetScript = bsv.Script.fromHex(vout.scriptPubKey.hex);
const target = targetScript.toASM().split(" ")[1].toString('hex');
//Make initial TX
let tx = new Transaction();
tx.addInput(
new Transaction.Input({
output: new Transaction.Output({
script: targetScript,
satoshis: value
}),
prevTxId: from.txid,
outputIndex: index,
script: bsv.Script.empty()
})
);
tx.addOutput(
new Transaction.Output({
satoshis: value-218,
script: to
})
);
console.log(chalk.green(`Targeting: ${target}`));
let sig, privKey;
if(!is21e8Out(tx.inputs[0].output.script)){
throw("Not a valid 21e8 script");
}
const sighash = Transaction.sighash.sighash(tx, sigtype, 0, tx.inputs[0].output.script, new bsv.crypto.BN(tx.inputs[0].output.satoshis), flags).reverse();
while(!sig) {
[sig, privKey] = sign(sighash, target)
}
const unlockingScript = new bsv.Script({});
unlockingScript
.add(
Buffer.concat([
sig,
Buffer.from([sigtype & 0xff])
])
)
.add(privKey.toPublicKey().toBuffer());
tx.inputs[0].setScript(unlockingScript);
console.log(unlockingScript.toString())
console.log(chalk.green(`Signed ${target} with ${privKey.toString()}`));
if(!!publish){
try {
const {data} = await axios.post('https://api.whatsonchain.com/v1/bsv/main/tx/raw', { txhex: tx.uncheckedSerialize() });
console.log(chalk.green('Published ' + Buffer.from(tx._getHash()).reverse().toString('hex')));
} catch(e) {
console.log(chalk.red(JSON.stringify({error: e.response.data})));
}
} else {
return;
}
}
start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment