bitcoinjs/bitcoinjs-lib

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