bitcoinjs/bitcoinjs-lib

How to redeem atomic Script with P2SH(HTLC)

peter-jim opened this issue · 3 comments

recently I got some problem with atomic(HTLC) . When I broad hex,I got error with mandatory-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation) . I think my signature and alice.publickey is corretly. Can you tell why ,thank you

here is my code

const bitcoin = require('bitcoinjs-lib');
const ecc = require('tiny-secp256k1');
const ecpair = require('ecpair');
const crypto = require("crypto");



const TESTNET = bitcoin.networks.testnet;
const ECPair = ecpair.ECPairFactory(ecc);

const alice = ECPair.fromWIF(
    'xxxx',
);

const address = bitcoin.payments.p2pkh({
    pubkey: alice.publicKey,
    network: TESTNET,
});

const txId = "c4c76dd3d64a82d591494b01364cc61d160ea41b4283f67f3949992233546440";
const vout = 0;
const value = 600;


const secret = Buffer.from('123', 'utf-8'); 

const secretHash = bitcoin.crypto.sha256(secret);// 

console.log("secretHash",secretHash.toString('hex'))
console.log("alice address",address.address)


const expiry = 1000; 

const lockingScript = bitcoin.script.compile([
    bitcoin.opcodes.OP_IF,
    bitcoin.opcodes.OP_SHA256,
    secretHash,
    bitcoin.opcodes.OP_EQUALVERIFY,
    bitcoin.opcodes.OP_DUP,
    bitcoin.opcodes.OP_HASH160,
    bitcoin.crypto.hash160(alice.publicKey),
    bitcoin.opcodes.OP_ELSE,
    bitcoin.script.number.encode(expiry).toString('hex'),
    bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY,
    bitcoin.opcodes.OP_DROP,
    bitcoin.opcodes.OP_DUP,
    bitcoin.opcodes.OP_HASH160,
    bitcoin.crypto.hash160(alice.publicKey),
    bitcoin.opcodes.OP_ENDIF,
    bitcoin.opcodes.OP_EQUALVERIFY,
    bitcoin.opcodes.OP_CHECKSIG
]);



const p2shAddress = bitcoin.payments.p2sh({
    redeem: { output: lockingScript },
    network: TESTNET
});
console.log('p2shAddress :', p2shAddress.address);


const redeemTx = new bitcoin.Transaction(TESTNET);


redeemTx.addInput(Buffer.from(txId, 'hex').reverse(), vout);
redeemTx.addOutput(bitcoin.address.toOutputScript(address.address, TESTNET), value);


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.toString('hex'));
console.log('toOutputScript alice',  bitcoin.address.toOutputScript(address.address, TESTNET).toString('hex')     );


const signatureHash = redeemTx.hashForSignature(0, p2pkhScriptPubKey, hashType); 
const signature =  bitcoin.script.signature.encode(alice.sign(signatureHash), hashType);
console.log("signature",    bitcoin.script.signature.encode(alice.sign(signatureHash), hashType).toString('hex'))
console.log("alice publickey",   alice.publicKey)


const pubKeyHash = crypto.createHash('sha256').update(alice.publicKey.toString('hex')).digest();
console.log('SHA256(alice.publicKey) with crypto lib:', pubKeyHash.toString('hex'));
console.log('SHA256(alice.publicKey) with bitcoinjs lib:', bitcoin.crypto.sha256(Buffer.from('032cf9dd2b7cf826a8d0e176ae0127e1e32b7e33dc55d78754c5f60bfd8f1173b8', 'utf-8')).toString('hex'));

console.log(bitcoin.address.fromBase58Check(address.address).hash.toString("hex"));
console.log(bitcoin.crypto.hash160(Buffer.from('032cf9dd2b7cf826a8d0e176ae0127e1e32b7e33dc55d78754c5f60bfd8f1173b8', 'hex')).toString('hex'));

const redeemScriptSig = bitcoin.payments.p2sh({
    redeem: {
        input: bitcoin.script.compile([
            signature,
            alice.publicKey,
            secret,
            bitcoin.opcodes.OP_TRUE,

            // Buffer.from([]),
        ]),
        output: lockingScript
    },
    network: TESTNET,
}).input;

redeemTx.setInputScript(0, redeemScriptSig);
console.log('Redeem Tx Hex:', redeemTx.toHex());
  1. Don't convert to a hex string here: bitcoin.script.number.encode(expiry).toString('hex'),
  2. hashForSignature should not take p2pkhScriptPubKey it should take lockingScript.

Amazing ,Thank you a lot ,it work! have a nice day!

Also if you will always use the same key twice, you can move the DUP and hash160 stuff outside the ENDIF