Contrat verrouillé dans le temps de hachage - Comment signer P2WSH PSBT avec sats-connect ?

J'ai créé un script P2WSH financé, qui est un HTLC. Je souhaite maintenant créer un PSBT qui sera signé par un autre portefeuille pour retirer les fonds.

Le problème est que la signature PSBT génère l'erreur bitcoinlib-js suivante : le script d'entrée n'a pas de pubKey. Je ne sais pas pourquoi ni comment ajouter correctement cette clé publique au PSBT.

Ce qui suit est un aperçu détaillé des étapes que je prends pour créer, financer et (à défaut) racheter le HTLC.

Adresse : tb1pnz86gezf5dzmja9q86z5agnmgjj29f00nxjj9jx0fsss6kkyh03sjkqhpd Clé publique : 59fff87c1bb3f75d34ea9d1588b72d0df43540695671c7a5ad3ec6a71d44bd79

Script pour créer le HTLC :

importer du bitcoin depuis 'bitcoinjs-lib' ; importer la crypto depuis « crypto » ; function createHTLC (secret, lockduration, destinatairePubKey, senderPubKey, networkType) { const network = networkType === 'testnet' ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const secretHash = crypto.createHash('sha256').update(Buffer.from(secret, 'utf-8')).digest(); const destinataireHash = 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,
secretHash,
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,
]); // Calculez l'adresse P2WSH et scriptPubKey const redirectScriptHash = bitcoin.crypto.sha256(redeemScript); const scriptPubKey = bitcoin.script.compile([
bitcoin.opcodes.OP_0, // Witness version 0
redeemScriptHash
]); const p2wshAddress = bitcoin.payments.p2wsh({ racheter : { sortie : racheterScript, réseau }, réseau }).adresse ; console.log('\nCréation d'un script HTLC ! '); console.log('---------------------------------------------- ----'); console.log('Adresse de dépôt Bitcoin P2WSH pour HTLC :', p2wshAddress); console.log('Witness Script Hex:', rachatScript.toString('hex')); console.log('Redeem Block Number:', lockduration); console.log('Secret (pour les dépenses) :', secret); console.log('SHA256(Secret) (pour la création HTLC) :', secretHash.toString('hex')); console.log('ScriptPubKey Hex:', scriptPubKey.toString('hex')); console.log('---------------------------------------------- ----'); // Pour financer le HTLC, envoyez BTC à l'adresse p2wsh.address // L'échange du HTLC impliquerait de créer une transaction qui dépense à partir de cette adresse // en utilisant le script témoin fourni, qui serait inclus dans le champ témoin de la transaction } // Exemple d'utilisation createHTLC( 'mysecret', 1, // temps de verrouillage en blocs "59fff87c1bb3f75d34ea9d1588b72d0df43540695671c7a5ad3ec6a71d44bd79", "59fff87c1bb3f75d34ea9d1588b72d0df43540695671c 7a5ad3ec6a71d44bd79", 'testnet' );

Cela crée avec succès la transaction P2WSH et donne le résultat suivant à l'écran

Création d'un script HTLC ! ------------------------------------------------- P2WSH Adresse de dépôt Bitcoin pour HTLC : tb1q5cyw034kcvsp6fsfx9skeq93vhvuqghmjc9y8xdhjll8m9thrn9q5mv0nr Script de témoin Hex : 63a820652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f 1dd5913c5cd08876a914e399056c4ca63571aca44fc2d11b3fdac69a37e06751b17576a914e399056c4ca63571aca44fc2d11b3fdac69a37e06888ac Utiliser le numéro de bloc : 1 Secret (pour dépenser) : mysec ret SHA256 (Secret) (pour la création HTLC) : 652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0 ScriptPubKey Hex : 0020a608e7c6b6c3201d260931616c80b165d 9c022fb960a4399b797fe7d95771cca ----------- --------------------------------------

Ensuite, je finance le script via xverse en envoyant du bitcoin directement à

tb1q5cyw034kcvsp6fsfx9skeq93vhvuqghmjc9y8xdhjll8m9thrn9q5mv0nr

https://mempool.space/testnet/tx/be9cc0e300d1c01b7fdbeeff1c99acc0fb8a7d9e8d025547b7bfc9635dedcbb3

Le ScriptPubKey sur mempool.space semble correspondre au mien

0020a608e7c6b6c3201d260931616c80b165d9c022fb960a4399b797fe7d95771cca

Il est alors temps de créer le PSBT. Je ne vois pas immédiatement de problème avec la façon dont je crée ce PSBT. Dois-je ajouter la clé publique quelque part ? :

importer * en tant que bitcoin depuis 'bitcoinjs-lib' ; importer la crypto depuis « crypto » ; importer * en tant que tinysecp256k1 depuis 'tiny-secp256k1' ; // Initialise l'importation de la bibliothèque ECC * en tant que bitcoinjs depuis "bitcoinjs-lib" ; importer * comme ecc depuis "tiny-secp256k1" ; bitcoin.initEccLib(ecc); function createSpendPSBT (secret, lockduration, scriptPubKeyHex, htlcTxId, htlcOutputIndex, rembourséAmount, destinatairePubKey, senderPubKey, destinataireAddress, networkType) { const network = networkType === 'testnet' ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const secretHash = crypto.createHash('sha256').update(Buffer.from(secret, 'utf-8')).digest(); // 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,
secretHash,
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 - 1000, // Soustraire des frais nominaux }) .setVersion(2) .setLocktime(lockduration); console.log("PSBT à signer :", psbt.toBase64()); } // Exemple d'utilisation (remplissez les valeurs réelles) createSpendPSBT( "mysecret", 0, "0020a608e7c6b6c3201d260931616c80b165d9c022fb960a4399b797fe7d95771cca", "be9cc0e300d1c01b7fdbeeff1c99acc0fb 8a7d9e8d025547b7bfc9635dedcbb3", 0, 1000, "59fff87c1bb3f75d34ea9d1588b72d0df43540695671c7a5ad3ec6a71d44bd79", "59fff87c1bb3f75d34ea9d1588b72d 0df43540695671c7a5ad3ec6a71d44bd79", "tb1pnz86gezf5dzmja9q86z5agnmgjj29f00nxjj9jx0fsss6kkyh03sjkqhpd", "testnet", ); //createSpendPSBT (secret, lockduration, scriptPubKey, htlcTxId, htlcOutputIndex, rembourséAmount, destinatairePubKey, senderPubKey, destinataireAddress, networkType)

Cela me donne le PSBT suivant :

cHNidP8BAF4CAAAAAbPL7V1jyb+3R1UCjZ59ivvArJkc/+7bfxvA0QDjwJy+AAAAAAD+////AQAAAAAAAAAAIlEgmI+kZEmjRbl0oD6FTqJ7RKSipe+ZpSLIz0whDVrEu+MAAAAAAAAEBK+gDAAAAAAAAIgAgpgjnxr bDIB0mCTFhbICxZdnAIvuWCkOZt5f+fZV3HMoBBVljqCBlLH3Gh9mmMmIkwTtLkCMdLYR6GpAyqUcS0Px3VkTxc0Ih2qRTjmQVsTKY1caykT8LRGz/axpo34GcAsXV2qRTjmQVsTKY1caykT8LRG z/axpo34GiIrAAA

Lorsque je le décode, afin de pouvoir inspecter le contenu, j'obtiens ce qui suit

{ "tx": { "txid": "a1eaefe490f5d3be11fbd6a5afeffcff20a9e92cfde3363484168c9f5769c57a", "hash": "a1eaefe490f5d3be11fbd6a5afeffcff20a9e92cfde3363484168c9f5769 c57a", "version": 2, "taille": 94, "vsize": 94, "poids": 376, "locktime": 0, "vin": [
{
"txid": "be9cc0e300d1c01b7fdbeeff1c99acc0fb8a7d9e8d025547b7bfc9635dedcbb3",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"sequence": 4294967294
}
]"vout": [
{
"value": 0.00000000,
"n": 0,
"scriptPubKey": {
"asm": "1 988fa46449a345b974a03e854ea27b44a4a2a5ef99a522c8cf4c210d5ac4bbe3",
"desc": "rawtr(988fa46449a345b974a03e854ea27b44a4a2a5ef99a522c8cf4c210d5ac4bbe3)#4xpnet5r",
"hex": "5120988fa46449a345b974a03e854ea27b44a4a2a5ef99a522c8cf4c210d5ac4bbe3",
"address": "tb1pnz86gezf5dzmja9q86z5agnmgjj29f00nxjj9jx0fsss6kkyh03sjkqhpd",
"type": "witness_v1_taproot"
}
}
]
}, "global_xpubs" : [
]"psbt_version": 0, "propriétaire": [
]"inconnu": { }, "entrées": [
{
"witness_utxo": {
"amount": 0.00001000,
"scriptPubKey": {
"asm": "0 a608e7c6b6c3201d260931616c80b165d9c022fb960a4399b797fe7d95771cca",
"desc": "addr(tb1q5cyw034kcvsp6fsfx9skeq93vhvuqghmjc9y8xdhjll8m9thrn9q5mv0nr)#wjcfmgw8",
"hex": "0020a608e7c6b6c3201d260931616c80b165d9c022fb960a4399b797fe7d95771cca",
"address": "tb1q5cyw034kcvsp6fsfx9skeq93vhvuqghmjc9y8xdhjll8m9thrn9q5mv0nr",
"type": "witness_v0_scripthash"
}
},
"witness_script": {
"asm": "OP_IF OP_SHA256 652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0 OP_EQUALVERIFY OP_DUP OP_HASH160 e399056c4ca63571aca44fc2d11b3fdac69a37e0 OP_ELSE 0 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 e399056c4ca63571aca44fc2d11b3fdac69a37e0 OP_ENDIF OP_EQUALVERIFY OP_CHECKSIG",
"hex": "63a820652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd08876a914e399056c4ca63571aca44fc2d11b3fdac69a37e06700b17576a914e399056c4ca63571aca44fc2d11b3fdac69a37e06888ac",
"type": "nonstandard"
}
}
]"les sorties": [
{
}
]"frais" : 0,00001000 }

Quand je regarde la partie entrées du PSBT, je peux voir qu'il y a des entrées avec mes fonds HTLC et un scriptPubKey. N'est-ce pas la clé publique dont l'erreur se plaint n'existe pas ?

À partir de là, j'ai quand même essayé de le signer en utilisant sats-connect :

const signPsbtOptions = { payload : { network : { type : 'Testnet' // Changez pour 'Regtest' ou 'Mainnet' si nécessaire }, psbtBase64: `cHNidP8BAF4CAAAAAbPL7V1jyb+3R1UCjZ59ivvArJkc/+7bfxvA0QDjwJy+AAAAAD+////AQAAAAAAAAAAIlE gmI+kZEmjRbl0oD6FTqJ7RKSipe+ZpSLIz0whDVrEu +MAAAAAAAEBK+gDAAAAAAAAIgAgpgjnxrbDIB0mCTFhbICxZdnAIvuWCkOZt5f+fZV3HMoBBVljqCBlLH3Gh9mMmIkwTtLkCMdLYR6GpAyqUcS0Px3VkTxc0Ih2qRTjmQVsTKY1caykT8LRGz/axpo34G cAsXV2qRTjmQVsTKY1caykT8LRGz/axpo34GiIrAAA`, Broadcast : false, // Définissez sur true si vous souhaitez diffuser après avoir signé inputsToSign : [
{
address: "tb1pnz86gezf5dzmja9q86z5agnmgjj29f00nxjj9jx0fsss6kkyh03sjkqhpd", //should this be the address of signer or the address of the input?
signingIndexes: // En supposant que vous souhaitiez signer la première entrée } ], }, onFinish: (response) => { console.log('Signed PSBT:', Response.psbtBase64); // Ici, vous pouvez ajouter du code supplémentaire pour gérer le PSBT signé }, onCancel: () => alert('Signing Cancel'), }; essayez { wait signTransaction (signPsbtOptions); } catch (erreur) { console.error('Erreur de signature PSBT :', erreur); alert('Échec de la signature du PSBT.'); }

et j'ai rencontré l'erreur suivante

Le script d'entrée n'a pas de pubKey

Ma question

Pourquoi le script d'entrée n'a-t-il pas de clé publique, et n'est-ce pas le but de scriptPubKey ? Comment fournir correctement la clé publique pour signer ce psbt ?