Psbt.signInput Passed but Broadcast Failure with msg `non-mandatory-script-verify-flag (Witness program hash mismatch)`
VictorZhang2014 opened this issue · 5 comments
Hi
My workbench parameters are :
node v22.1.0
"bitcoinjs-lib": "^6.1.6",
"tiny-secp256k1": "^2.2.3",
"ecpair": "^2.1.0"
The full code snippet as shown below
const bitcoin = require('bitcoinjs-lib');
const ecc = require('tiny-secp256k1');
const ecpair = require('ecpair');
bitcoin.initEccLib(ecc);
const ECPair = ecpair.ECPairFactory(ecc);
const toXOnly = (pubKey) => pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
const LEAF_VERSION_TAPSCRIPT = 0xc0;
const SEQUENCE = 0xfffffffd;
async function etching(from, privateKey, to, serviceFee) {
const network = bitcoin.networks.bitcoin;
const utxo = {
txid: '5de2fd2a35de70ec1699efb0399a6076c1ad60822e1ac2c5af7193b1c72dfbe8',
vout: 0,
value: 8000
}
const keyPair = ECPair.fromPrivateKey(Buffer.from(privateKey, 'hex'), { network });
const etching_script = Buffer.from("209393706508000ab7b942c8138383d7bfb8641b692068b9eb60fb6a9634e4be3aac00630b0a6bc5b1f67df96acb22b768", "hex");
const runestoneScript = Buffer.from("6a5d20020304eb8ac7b5dfafbeb5cbc5dc0503900405bf4106000a904e08a09c011601", "hex");
const scriptTree = {
output: etching_script,
}
const script_p2tr = bitcoin.payments.p2tr({
internalPubkey: toXOnly(keyPair.publicKey),
scriptTree,
network,
});
const etching_redeem = {
output: etching_script,
redeemVersion: LEAF_VERSION_TAPSCRIPT
}
const etching_p2tr = bitcoin.payments.p2tr({
internalPubkey: toXOnly(keyPair.publicKey),
scriptTree,
redeem: etching_redeem,
network
});
const psbt = new bitcoin.Psbt({ network });
psbt.addInput({
hash: utxo.txid,
index: utxo.vout,
witnessUtxo: { value: utxo.value, script: script_p2tr.output },
tapLeafScript: [
{
leafVersion: etching_redeem.redeemVersion,
script: etching_redeem.output,
controlBlock: etching_p2tr.witness[etching_p2tr.witness.length - 1]
}
],
sequence: SEQUENCE,
});
psbt.addOutput({
script: runestoneScript,
value: 0
})
const fee = 5500;
const change = utxo.value - 546 - fee;
psbt.addOutput({
address: to,
value: 546
});
psbt.addOutput({
address: serviceFee,
value: change
});
psbt.signInput(0, keyPair);
psbt.finalizeAllInputs();
let feeRate = psbt.getFeeRate();
const rawTxHex = psbt.extractTransaction().toHex()
console.log({rawTxHex, feeRate})
}
const t0 = async () => {
const fromAddr = "bc1pv4fhy64300wdr7regcanc3q7z5qry0nss8xvvvm4hyey7zxuhwusyq8qx2"
const fromPubKey = "029393706508000ab7b942c8138383d7bfb8641b692068b9eb60fb6a9634e4be3a"
const fromPrivateKey = "PRIVATE KEY"
const to = "bc1pe5x2f3yahhqr6tcyzy3m0vf96350u33240yzzqt7wmqhguf8m4zs8pdvt5"
const serviceFee = "bc1pe5x2f3yahhqr6tcyzy3m0vf96350u33240yzzqt7wmqhguf8m4zs8pdvt5"
await etching(fromAddr, fromPrivateKey, to, serviceFee)
}
t0()
The output is
{
rawTxHex: '02000000000101e8fb2dc7b19371afc5c21a2e8260adc176609a39b0ef9916ec70de352afde25d0000000000fdffffff030000000000000000236a5d20020304eb8ac7b5dfafbeb5cbc5dc0503900405bf4106000a904e08a09c0116012202000000000000225120cd0ca4c49dbdc03d2f041123b7b125d468fe462aabc821017e76c1747127dd45a207000000000000225120cd0ca4c49dbdc03d2f041123b7b125d468fe462aabc821017e76c1747127dd4503408d5b61b3aa6d23810f3bba992852613773c18a80bda453831b81e45008d18eba3512d3a3193bc35c5cf880c1c6144c06e9e8dfea6c57f226933c072c902e43a531209393706508000ab7b942c8138383d7bfb8641b692068b9eb60fb6a9634e4be3aac00630b0a6bc5b1f67df96acb22b76821c09393706508000ab7b942c8138383d7bfb8641b692068b9eb60fb6a9634e4be3a00000000',
feeRate: 25
}
When I tried to broadcast the transaction with the RawHex, I always got error with message: sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Witness program hash mismatch)"}
The broadcast API is https://mempool.space/api/tx
.
I have stuck into the issue for weeks, please save me, I really appreciate your assistance and code guidance.
I guessed that the issue is the Tapscript signature, if yes, but why it passed the PSBT sign?
After looking at the code, I think the privateKey may need to be tweaked.
I'm not sure if this is the problem.
Can you provide a runnable demo repo to debug?
Are you trying to key spend or script spend?
Script Spend
bitcoinjs-lib/test/integration/taproot.spec.ts
Lines 220 to 337 in 8c88016
Key Spend (with unused script tree)
bitcoinjs-lib/test/integration/taproot.spec.ts
Lines 161 to 218 in 8c88016
For the part of Script Spend
in line of 220, which ran as expected, but what the two scripts '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG'
and '2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7 OP_CHECKSIG'
indicate in the scriptTree
data? @junderw
In fact, I'm working around with the Runes etching, it requires two transactions, first is commit
and second is reveal
separately. As you mentioned above Script Spend
or Key Spend
should be used in the commit
transaction or reveal
transaction?
... indicate in the scriptTree data?
These are leaves in the script tree. Any one of these scripts may be selected to spend a UTXO given to this p2tr address. It seems like in your code there is only one leaf. So there's not much of a tree. A large tree is not required, though. One script is fine. Your protocol seems to require the script to be used... so I am guessing your code should use script spend.
I'm working around with the Runes etching
This is a Bitcoin library, if you would like support with other protocols, please ask questions on their forum. You are more likely to get an answer.
I would assume that reveal
is "revealing" the witness, so that's probably it, but again I have no clue. Read the protocol documentation (if there is any).
Thank you for the hints. @junderw