Script - Pourquoi mon argument P2WSH OP_IF/NOTIF n'est-il pas minimal ?
J'ai créé un HTLC avec le script Bitcoin suivant en utilisant bitcoinlib-js :
const racheterScript = bitcoin.script.compile([
bitcoin.opcodes.OP_IF,
bitcoin.opcodes.OP_SHA256,
secretHashBuffer,
bitcoin.opcodes.OP_EQUALVERIFY,
bitcoin.opcodes.OP_DUP,
bitcoin.opcodes.OP_HASH160,
recipientHash, // Hashed recipient public key
bitcoin.opcodes.OP_ELSE,
bitcoin.script.number.encode(lockduration),
bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY,
bitcoin.opcodes.OP_DROP,
bitcoin.opcodes.OP_DUP,
bitcoin.opcodes.OP_HASH160,
senderHash, // Hashed sender public key
bitcoin.opcodes.OP_ENDIF,
bitcoin.opcodes.OP_EQUALVERIFY,
bitcoin.opcodes.OP_CHECKSIG,
]);
Je peux débloquer les fonds dans la branche OP_IF en utilisant le bon secret et en dépensant à partir de la clé publique du destinataire. Cependant, je rencontre un problème lorsque je tente de récupérer des fonds via la branche OP_ELSE du script en utilisant la durée de verrouillage du timelock et en diffusant à l'aide de la clé publique de l'expéditeur.
L'erreur spécifique que j'obtiens est la suivante :
code d'erreur : -26 message d'erreur : non-mandatory-script-verify-flag (l'argument OP_IF/NOTIF doit être minimal)
Je pense que l'erreur vient de la façon dont j'encode mes valeurs. Je construis un PSBT que je signe ensuite avec xverse et que je convertis en une transaction brute en utilisant bitcoin-cli finalizepsbt cHNidP8BAFMCAAAAAa/...
Tout d'abord je crée le PSBT
importer * en tant que bitcoin depuis 'bitcoinjs-lib' ; importer la crypto depuis « crypto » ; fonction d'exportation createRefundPSBT (secretHash : chaîne, lockduration : numéro, scriptPubKeyHex : chaîne, htlcTxId : chaîne, htlcOutputIndex : numéro, returnAmount : numéro, destinatairePubKey : chaîne, senderPubKey : chaîne, destinataireAddress : chaîne, networkType : chaîne) { const network = networkType = == "testnet" ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const secretHashBuffer = Buffer.from(secretHash, "hex"); // Recréez le script HTLC en utilisant le secret const fourni containHash = bitcoin.crypto.hash160( Buffer.from(recipientPubKey, "hex") ); const senderHash = bitcoin.crypto.hash160(Buffer.from(senderPubKey, "hex")); const racheterScript = bitcoin.script.compile([
bitcoin.opcodes.OP_IF,
bitcoin.opcodes.OP_SHA256,
secretHashBuffer,
bitcoin.opcodes.OP_EQUALVERIFY,
bitcoin.opcodes.OP_DUP,
bitcoin.opcodes.OP_HASH160,
recipientHash, // Hashed recipient public key
bitcoin.opcodes.OP_ELSE,
bitcoin.script.number.encode(lockduration),
bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY,
bitcoin.opcodes.OP_DROP,
bitcoin.opcodes.OP_DUP,
bitcoin.opcodes.OP_HASH160,
senderHash, // Hashed sender public key
bitcoin.opcodes.OP_ENDIF,
bitcoin.opcodes.OP_EQUALVERIFY,
bitcoin.opcodes.OP_CHECKSIG,
]); const scriptPubKey = Buffer.from(scriptPubKeyHex, "hex"); console.log("Création du PSBT"); // Créer un const PSBT psbt = new bitcoin.Psbt({ network: network }) .addInput({ hash: htlcTxId, index: htlcOutputIndex, séquence: 0xfffffffe, // Nécessaire pour OP_CHECKLOCKTIMEVERIFY WitnessUtxo: { script: scriptPubKey, valeur: rembourséAmount, }, WitnessScript : racheScript, }) .addOutput({ adresse : destinataireAddress, valeur : remboursementAmount - 3000, // Soustraire des frais nominaux }) .setVersion(2) .setLocktime(lockduration); console.log("PSBT à signer :", psbt.toBase64()); retourner psbt.toBase64(); }
Ensuite, je le signe avec le portefeuille xverse en utilisant un frontal et j'essaie de finaliser le PSBT.
// Ce script est destiné à l'importation de remboursement * en tant que bitcoin depuis 'bitcoinjs-lib' ; importer un varuint depuis 'varuint-bitcoin' ; function WitnessStackToScriptWitness(témoin : any) { let buffer = Buffer.allocUnsafe(0); function writeSlice(slice:string) { buffer = Buffer.concat([buffer, Buffer.from(slice)]); } function writeVarInt(i: nombre) { const varintLen = varuint.encodingLength(i); const currentLen = buffer.length; tampon = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); varuint.encode(i, buffer, currentLen); } function writeVarSlice(slice: string) { writeVarInt(slice.length); writeSlice(tranche); } function writeVector(vecteur : any) { writeVarInt(vecteur.length); vecteur.forEach(writeVarSlice); } writeVector(témoin); tampon de retour ; } function getFinalScriptsSpecial(inputIndex : nombre, entrée : tout, script : tout, isSegwit : booléen, isP2SH : booléen, isP2WSH : booléen) { if ( ! input.partialSig || input.partialSig.length === 0) { lancer un nouveau Erreur(`Impossible de finaliser l'entrée #${inputIndex} : signatures partielles manquantes`); } if ( ! input.witnessUtxo) { throw new Error(`Impossible de finaliser l'entrée #${inputIndex} : témoin manquant UTXO`); } if ( ! script) { throw new Error(`Impossible de finaliser l'entrée #${inputIndex} : Script manquant`); } signature const = input.partialSig.signature; const pubkey = input.partialSig.pubkey; const finalScriptWitness = témoinStackToScriptWitness([
signature,
pubkey,
//Buffer.from(), // Tableau d'octets pour OP_FALSE ou 0 Buffer.alloc(0), // Tableau d'octets vide pour OP_FALSE ou 0 script ]); return { finalScriptWitness : finalScriptWitness } ; } fonction d'exportation prepareFinalRefundPSBT( WitnessHexWithdraw: string, psbtBase64: string, ): string | null { laisser psbt : n'importe lequel ; essayez { psbt = bitcoin.Psbt.fromBase64(psbtBase64); psbt.isSegwit = true; psbt.isP2SH = faux ; psbt.isP2WSH = vrai ; psbt.finalizeInput(0, getFinalScriptsSpecial); // ajout du script témoin hex à partir de bitcoin-cli decodepsbt car psbt.finalizeInput le supprime pour une raison quelconque psbt.data.inputs.witnessScript = Buffer.from(witnessHexWithdraw, 'hex'); } catch (e) { console.error(`Erreur lors de la finalisation du PSBT : ${e}`); processus.exit(1); } return psbt.toBase64(); }
Cela me donne un PSBT finalisé que je convertis en transaction brute. Voici la transaction brute décodée :
{ "txid": "d773911f5e0c2bd4590794676e7c95bd2e33d8156c22e64c80e8c5bf4cfcbece", "hash": "5f1025270b4f2b298d2a90647c2640cc222815d0df3e2d38b72 b9e37b04c14ff", "version": 2, "taille": 285, "vsize": 134, "poids": 534, "locktime": 1, "vin" : [
{
"txid": "fa69f9cee0b3fffb419084ccdaa19dbc319ca23965d3e6a9444210449b92a194",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"3045022100cf0ca0b7cbdda5e0c6733d508031c160cb93c7f4e9fcc6bafed4e50e7c23dd6e02200bc9a300ed5d6ec1202de946a59b4fdbb07136d5ffc43f8f5fe1e8fae6d291cd01",
"0269bf7a1301185625758c324221cf6614e4a4104f90f83f633a8790f62919aa0a",
"00",
"63a820ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb8876a914a5102a75c05993aa082ca365b5ba7f49bed517586751b17576a914a5102a75c05993aa082ca365b5ba7f49bed517586888ac"
]"séquence": 4294967294 } ], "vout": [
{
"value": 0.00007000,
"n": 0,
"scriptPubKey": {
"asm": "OP_HASH160 836676150f0f5892416d8bd9bc5e923b494677f3 OP_EQUAL",
"desc": "addr(2N5E1FYfiQgM5U8aTvU1btZYpiTVPE9ReaK)#00u0x08a",
"hex": "a914836676150f0f5892416d8bd9bc5e923b494677f387",
"address": "2N5E1FYfiQgM5U8aTvU1btZYpiTVPE9ReaK",
"type": "scripthash"
}
}
]
}
Ensuite j'essaye de diffuser la transaction
bitcoin-cli senddrawtransaction
Et je rencontre l'erreur suivante :
code d'erreur : -26 message d'erreur : non-mandatory-script-verify-flag (l'argument OP_IF/NOTIF doit être minimal)
Serait-ce cette part du txintémoin qui n'est pas minime ?
"00",
Devrait-il plutôt être
"0",
Ou peut-être même
"",
Ou y a-t-il une autre raison pour laquelle mon argument OP_IF/NOTIF n'est pas minimal ?
