How to redeem UTXO with P2SH
peter-jim opened this issue · 4 comments
I had to send utxo from p2pkh to p2sh address, here is my code .
const bitcoin = require('bitcoinjs-lib');
const ecc = require('tiny-secp256k1');
const ecpair = require('ecpair');
const { buffer } = require('stream/consumers');
const TESTNET = bitcoin.networks.testnet;
const ECPair = ecpair.ECPairFactory(ecc);
const alice = ECPair.fromWIF(
'xxx',
);
const address = bitcoin.payments.p2pkh({
pubkey: alice.publicKey,
network: TESTNET,
});
console.log("alice address", address.address);
const tx = new bitcoin.Transaction(TESTNET);
const txId = "c1e48b3ae44001fb1dddf2958b177ace2c640ce636c32860c85b82431fa3eb13";
const vout = 0;
tx.addInput(Buffer.from(txId, 'hex').reverse(), vout);
//
// const sha256HashString = bitcoin.crypto.sha256(Buffer.from('Btrust Builders')).toString('hex');
const lockingScript = bitcoin.script.compile([
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_13,
bitcoin.opcodes.OP_EQUAL
]);
const decompiled = bitcoin.script.decompile(Buffer.from(lockingScript, 'hex')).toString('hex');
console.log('Decompiled Redeem Script:', decompiled);
const p2shAddress = bitcoin.payments.p2sh({
redeem: { output: lockingScript },
network: TESTNET
});
console.log('p2shAddress :', p2shAddress.address);
//
// const outputValue = utxo.value - FEE; //
tx.addOutput(
bitcoin.address.toOutputScript(p2shAddress.address, TESTNET),
800
);
console.log("output ScriptPubKey (HEX)", bitcoin.address.toOutputScript(p2shAddress.address, TESTNET));
const outputScript = bitcoin.address.toOutputScript(p2shAddress.address, TESTNET);
const asmCode = bitcoin.script.toASM(outputScript);
console.log('Output Script ASM:', asmCode);
//
const hashType = bitcoin.Transaction.SIGHASH_ALL;
const utxoAddress = bitcoin.payments.p2pkh({ pubkey: alice.publicKey, network: TESTNET });
const p2pkhScriptPubKey = bitcoin.address.toOutputScript(utxoAddress.address, TESTNET); //
console.log('toOutputScript',p2pkhScriptPubKey);
const signatureHash = tx.hashForSignature(0, p2pkhScriptPubKey, hashType); //
console.log("signatureHash",signatureHash.toString('hex'));
const signature = bitcoin.script.signature.encode(alice.sign(signatureHash), hashType);
console.log("signature",signature.toString('hex'));
const scriptSig = bitcoin.script.compile([
signature,
alice.publicKey
]);
console.log("scriptSig",scriptSig.toString('hex')) //
tx.setInputScript(0, scriptSig);
console.log('Tx Hex:', tx.toHex());
and then I want unlocking this utxo from p2sh tp p2pkh, here is my code
const bitcoin = require('bitcoinjs-lib');
const ecc = require('tiny-secp256k1');
const ecpair = require('ecpair');
const TESTNET = bitcoin.networks.testnet;
const ECPair = ecpair.ECPairFactory(ecc);
const alice = ECPair.fromWIF(
'xxx',
);
const address = bitcoin.payments.p2pkh({
pubkey: alice.publicKey,
network: TESTNET,
});
console.log("alice address", address.address);
//
const p2shUtxo = {
txId: "94b5a43de760c4e88c25a7b3642174d5e52a9b5b9334997ce4a50d2b2f8cefec",
outputIndex: 0,
address: "2ND8PB9RrfCaAcjfjP1Y6nAgFd9zWHYX4DN",
value: 300
};
const pubKeyHash = bitcoin.crypto.hash256(alice.publicKey);
console.log("pubKeyHash",pubKeyHash);
const unlockingScript = bitcoin.script.compile([
bitcoin.opcodes.OP_6,
bitcoin.opcodes.OP_7,
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_13,
bitcoin.opcodes.OP_EQUAL
]);
// const data = Buffer.from('Btrust Builders');
const redeemTx = new bitcoin.Transaction(TESTNET);
redeemTx.addInput(Buffer.from(p2shUtxo.txId, 'hex').reverse(), p2shUtxo.outputIndex);
redeemTx.addOutput(bitcoin.address.toOutputScript(address.address, TESTNET), p2shUtxo.value);
// console.log(bitcoin.address.toOutputScript(address.address, TESTNET))
redeemTx.setInputScript(0, unlockingScript);
console.log('Redeem Tx Hex:', redeemTx.toHex());
When I want broad the hex
,"0100000001ecef8c2f2b0da5e47c9934935b9b2ae5d5742164b3a7258ce8c460e73da4b59400000000055657935d87ffffffff012c010000000000001976a914b163d850125f1b65eadbcf15f88b7ef16836c1ee88ac00000000"
I got error mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)
I can make sure my txid and vout is right. How can I solve this problem , thank you
The final item in the scriptSig needs to be the compiled redeemScript as a single PUSHDATA:
bitcoin.script.compile([
bitcoin.opcodes.OP_6,
bitcoin.opcodes.OP_7,
bitcoin.script.compile([
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_13,
bitcoin.opcodes.OP_EQUAL,
]),
])
Your code outputs: <Buffer 56 57 93 5d 87>
You need: <Buffer 56 57 03 93 5d 87>
(notice the 0x03 there which will push the next 3 bytes on the stack as a single item)
Thanks you ,it work ! but I had another question ,What the diffrent between with using this method
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_13,
bitcoin.opcodes.OP_EQUAL,
]);
const unlockingScript = bitcoin.script.compile([
bitcoin.opcodes.OP_6,
bitcoin.opcodes.OP_7,
redeemScript
]);
const redeemScriptSig = bitcoin.payments.p2sh({
redeem: {
input: unlockingScript,
output: redeemScript
},
}).input;
const decompiled = bitcoin.script.decompile(Buffer.from(redeemScriptSig, 'hex')).toString('hex');
console.log('Decompiled Redeem Script:', decompiled);
redeemTx.setInputScript(0, redeemScriptSig);
console.log('Redeem Tx Hex:', redeemTx.toHex());
redeemTx.setInputScript(0, unlockingScript); //
console.log('Redeem Tx Hex:', redeemTx.toHex());
When I using this method after broadcast I got error 'mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)'
redeemTx.setInputScript(0, redeemScriptSig);
console.log('Redeem Tx Hex:', redeemTx.toHex());
Remove the redeemScript from the unlockingScript