Skip to content

Instantly share code, notes, and snippets.

@lukechilds
Created May 17, 2020 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukechilds/3c374d6c5d85229a6c0d30f1b26630e6 to your computer and use it in GitHub Desktop.
Save lukechilds/3c374d6c5d85229a6c0d30f1b26630e6 to your computer and use it in GitHub Desktop.
const bitcoin = require('bitcoinjs-lib')
const regcli = require('regtest-client')
const bs58 = require('bs58check')
const APIURL = process.env['APIURL'] || 'http://127.0.0.1:8080/1';
const APIPASS = process.env['APIPASS'] || 'satoshi';
const regtestUtils = new regcli.RegtestUtils({ APIURL, APIPASS })
const network = bitcoin.networks.regtest
const rootNode = bitcoin.bip32.fromBase58('tprv8ZgxMBicQKsPfBD2PErVQNAqcjwLBg8fWZSX8qwx1cRyFsDrgvRDLqaT5Rf2N4VEXZDAkpWeJ9vXXREbAUY67RtoZorrfxqgDMxsb6FiBFH', network)
async function main() {
const amount = 69420
const alice = createPayment('p2wpkh', [rootNode]);
const inputData = await getInputData(
amount,
alice.payment,
true,
'noredeem',
);
const psbt = new bitcoin.Psbt({ network })
.addInput(inputData)
.addOutput({
address: alice.payment.address,
value: amount - 1000,
})
.signInput(0, rootNode)
// delete psbt.data.inputs[0].witnessUtxo
console.log(psbt.toBase64())
psbt.finalizeAllInputs()
console.log(psbt.toBase64())
}
main().then(() => {
process.exit(0)
})
.catch(err => {
console.error(err);
process.exit(1)
})
// psbt.updateInput(0, {
// bip32Derivation: [
// {
// path: "m/0",
// masterFingerprint: rootNode.fingerprint,
// pubkey: rootNode.publicKey,
// }
// ]
// })
// psbt.updateGlobal({
// globalXpub: [
// {
// extendedPubkey: bs58.decode(rootNode.derivePath("m/44'/0'/0'").neutered().toBase58()),
// masterFingerprint: rootNode.fingerprint,
// path: "m/44'/0'/0'",
// }
// ]
// })
function createPayment(_type, myKeys) {
const splitType = _type.split('-').reverse();
const isMultisig = splitType[0].slice(0, 4) === 'p2ms';
const keys = myKeys || [];
let m;
if (isMultisig) {
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/);
m = parseInt(match[1], 10);
let n = parseInt(match[2], 10);
if (keys.length > 0 && keys.length !== n) {
throw new Error('Need n keys for multisig');
}
while (!myKeys && n > 1) {
keys.push(bitcoin.ECPair.makeRandom({ network }));
n--;
}
}
if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }));
let payment;
splitType.forEach(type => {
if (type.slice(0, 4) === 'p2ms') {
payment = bitcoin.payments.p2ms({
m,
pubkeys: keys.map(key => key.publicKey).sort(),
network,
});
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
payment = bitcoin.payments[type]({
redeem: payment,
network,
});
} else {
payment = bitcoin.payments[type]({
pubkey: keys[0].publicKey,
network,
});
}
});
return {
payment,
keys,
};
}
function getWitnessUtxo(out) {
delete out.address;
out.script = Buffer.from(out.script, 'hex');
return out;
}
async function getInputData(
amount,
payment,
isSegwit,
redeemType,
) {
const unspent = await regtestUtils.faucetComplex(payment.output, amount);
const utx = await regtestUtils.fetch(unspent.txId);
// for non segwit inputs, you must pass the full transaction buffer
const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
// for segwit inputs, you only need the output script and value as an object.
const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]);
const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo };
const mixin2 = {};
switch (redeemType) {
case 'p2sh':
mixin2.redeemScript = payment.redeem.output;
break;
case 'p2wsh':
mixin2.witnessScript = payment.redeem.output;
break;
case 'p2sh-p2wsh':
mixin2.witnessScript = payment.redeem.redeem.output;
mixin2.redeemScript = payment.redeem.output;
break;
}
return {
hash: unspent.txId,
index: unspent.vout,
...mixin,
...mixin2,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment