• About
  • FAQ
  • Earn Bitcoin while Surfing the net
  • Buy & Sell Crypto on Paxful
Newsletter
Approx Foundation
  • Home
    • Home – Layout 1
  • Bitcoin
  • Ethereum
  • Regulation
  • Market
  • Blockchain
  • Business
  • Guide
  • Contact Us
No Result
View All Result
  • Home
    • Home – Layout 1
  • Bitcoin
  • Ethereum
  • Regulation
  • Market
  • Blockchain
  • Business
  • Guide
  • Contact Us
No Result
View All Result
Approx Foundation
No Result
View All Result
Home Bitcoin

javascript – bitcoinjs-lib: “bad-witness-nonstandard” when spending complex P2WSH with OP_IF/ELSE

Moussa by Moussa
August 20, 2025
in Bitcoin
0
peer discovery – how to obtain the IP addresses of nodes for mining pools?
189
SHARES
1.5k
VIEWS
Share on FacebookShare on Twitter


I’m trying to create and spend a P2WSH transaction on testnet with a “complex” script, but I’m running into a bad-witness-nonstandard error that I can’t figure out.

My Goal

I want to create a P2WSH output with a script that allows spending under two conditions:

A 3-of-4 multisig signature is provided.
OR
A timelock (e.g., 5 minutes) has passed AND a single recovery signature is provided.
The script logic uses OP_IF for the multisig path and OP_ELSE for the timelock path.

The Problem

I can generate a wallet, derive the P2WSH address, and fund it correctly. The funding transaction creates a standard P2WSH output (OP_0 <32-byte-hash>).

However, when I try to spend this UTXO via the 3-of-4 multisig path, my transaction is rejected by my Bitcoin Core node’s testmempoolaccept with the error: mandatory-script-verify-flag-failed (bad-witness-nonstandard)

What I’ve Already Verified

The P2W

  1. SH address derived from my scripts matches the address I funded.
  2. A debug script confirms that my redeemScript is generated deterministically and its SHA256 hash correctly matches the hash in the funded UTXO’s scriptPubKey.
  3. The funding transaction is correct and standard.

To Reproduce the Error

Here is all the code and data needed to reproduce the problem.

  1. package.json dependencies:
    {
      "dependencies": {
        "@types/node": "^20.12.12",
        "bitcoinjs-lib": "^6.1.5",
        "ecpair": "^2.1.0",
        "ts-node": "^10.9.2",
        "tiny-secp256k1": "^2.2.3",
        "typescript": "^5.4.5"
      }
    }
  1. Wallet Generation Script (index.ts): This script generates the keys and wallet.json.
import * as bitcoin from 'bitcoinjs-lib'; import ECPairFactory from 'ecpair'; 
import * as ecc from 'tiny-secp256k1'; import * as fs from 'fs';

// Initialisation const ECPair = ECPairFactory(ecc); bitcoin.initEccLib(ecc);

const network = bitcoin.networks.testnet;

// --- 1. Key Generation --- 
const multisigKeys = [
    ECPair.makeRandom({ network }),
    ECPair.makeRandom({ network }),
    ECPair.makeRandom({ network }),
    ECPair.makeRandom({ network }), ]; 

const multisigPubkeys = multisigKeys.map(key => Buffer.from(key.publicKey)).sort((a, b) => a.compare(b)); 
const recoveryKey = ECPair.makeRandom({ network }); const recoveryPubkey = Buffer.from(recoveryKey.publicKey);

// --- 2. Timelock Definition (5 minutes for testing) --- 
const date = new Date(); 
date.setMinutes(date.getMinutes() + 5); 
const lockTime = Math.floor(date.getTime() / 1000); 
const lockTimeBuffer = bitcoin.script.number.encode(lockTime);

// --- 3. Redeem Script Construction --- 
const redeemScript = bitcoin.script.compile([
    bitcoin.opcodes.OP_IF,
    bitcoin.opcodes.OP_3,
    ...multisigPubkeys,
    bitcoin.opcodes.OP_4,
    bitcoin.opcodes.OP_CHECKMULTISIG,
    bitcoin.opcodes.OP_ELSE,
    lockTimeBuffer,
    bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY,
    bitcoin.opcodes.OP_DROP,
    recoveryPubkey,
    bitcoin.opcodes.OP_CHECKSIG,
    bitcoin.opcodes.OP_ENDIF, ]);

// --- 4. Address Creation --- 
const p2wsh = bitcoin.payments.p2wsh({
    redeem: { output: redeemScript, network },
    network, });

// --- 5. Save Data --- 
const wallet = {
    network: 'testnet',
    lockTime: lockTime,
    lockTimeDate: date.toISOString(),
    p2wshAddress: p2wsh.address,
    redeemScriptHex: redeemScript.toString('hex'),
    multisigKeysWIF: multisigKeys.map(k => k.toWIF()),
    recoveryKeyWIF: recoveryKey.toWIF(), };

fs.writeFileSync('wallet.json', JSON.stringify(wallet, null, 2));

console.log('Wallet generated and saved to wallet.json'); 
console.log('P2WSH Deposit Address:', wallet.p2wshAddress);
  1. Multisig Spending Script (1_spend_multisig.ts): This is the script that fails with bad-witness-nonstandard.
import * as bitcoin from 'bitcoinjs-lib'; 
import ECPairFactory from 'ecpair'; 
import * as ecc from 'tiny-secp256k1'; 
import * as fs from 'fs';

// --- UTXO Configuration --- 
const UTXO_TXID = 'PASTE_YOUR_FUNDING_TXID_HERE'; 
const UTXO_INDEX = 0; // Or 1, depending on the output 
const UTXO_VALUE_SATS = 10000; // Amount in satoshis 
const DESTINATION_ADDRESS = 'PASTE_A_TESTNET_ADDRESS_HERE'; 
const FEE_SATS = 2000;

// --- Initialization --- 
const ECPair = ECPairFactory(ecc); bitcoin.initEccLib(ecc); 
const network = bitcoin.networks.testnet;

// --- 1. Load Wallet --- 
const wallet = JSON.parse(fs.readFileSync('wallet.json', 'utf-8')); 
const redeemScript = Buffer.from(wallet.redeemScriptHex, 'hex'); 
const p2wsh = bitcoin.payments.p2wsh({ redeem: { output: redeemScript, network }, network }); 
const multisigKeys = wallet.multisigKeysWIF.map((wif: string) => ECPair.fromWIF(wif, network));

// --- 2. Build PSBT --- 
const psbt = new bitcoin.Psbt({ network }); psbt.addInput({
    hash: UTXO_TXID,
    index: UTXO_INDEX,
    witnessUtxo: { script: p2wsh.output!, value: UTXO_VALUE_SATS },
    witnessScript: redeemScript, }); 
psbt.addOutput({ address: DESTINATION_ADDRESS, value: UTXO_VALUE_SATS - FEE_SATS });

// --- 3. Sign Transaction --- 
const createSigner = (key: any) => ({   publicKey: Buffer.from(key.publicKey),   
    sign: (hash: Buffer): Buffer => Buffer.from(key.sign(hash)), }); // Sign with 3 of the 4 keys 
psbt.signInput(0, createSigner(multisigKeys[0])); 
psbt.signInput(0, createSigner(multisigKeys[1])); 
psbt.signInput(0, createSigner(multisigKeys[2]));

// --- 4. Finalize Transaction --- 
const finalizer = (inputIndex: number, input: any) => {
    const emptySignature = Buffer.from([]); // Placeholder for OP_CHECKMULTISIG bug
    const partialSignatures = input.partialSig.map((ps: any) => ps.signature);
    const witnessStack = [
        emptySignature,
        ...partialSignatures,
        bitcoin.script.number.encode(1), // Standard way to push OP_1
        redeemScript,
    ];
    const witness = witnessStack.reduce((acc, item) => {
        const push = bitcoin.script.compile([item]);
        return Buffer.concat([acc, push]);
    }, Buffer.from([witnessStack.length]));
    return { finalScriptWitness: witness }; }; psbt.finalizeInput(0, finalizer);

// --- 5. Extract and create validation command --- 
const tx = psbt.extractTransaction(); 
const txHex = tx.toHex(); 
console.log('\n--- testmempoolaccept command ---'); 
console.log(`bitcoin-cli -testnet testmempoolaccept '["${txHex}"]'`);
  1. Data to reproduce:

wallet.json (TESTNET KEYS, NO VALUE):

{   
    "network": "testnet",   
    "lockTime": 1723986942,   
    "lockTimeDate": "2025-08-18T13:15:42.339Z",   
    "p2wshAddress": "tb1qztq5rg30lv8y7kup7tftuelppcy2f9u9ygm8daq7gv4lgf0dw3ss3hj9qw",   
    "redeemScriptHex": "6353210200847c4a13f98cb1e3138bda175ba6f4c7ffd9e03a4c8617878ab03cf4a4a97921024b3e2544b4e311985477d88ac77ea00aa68f85490d0c663fba38fcdf582d043f2102c822f5026d382a93476d20de66c87c5e4e4997654817bfacc69b29f2dc8b6a10210328c2213b0813b4dac9c063f674b2c61dc50344c6e093df045c8ee2fe09f67bd854ae6704c413a368b1752103c5512e31f8a2555a116146262382be4be774fca326a2ee01d71e0fe33ffe4925ac68", 
    "multisigKeysWIF": [
        "cT5h8LgJ2a4V3c4yF5g6H7j8K9L0M1n2p3q4R5s6T7u8V9w0XyZ",
        "cT5h8LgJ2a4V3c4yF5g6H7j8K9L0M1n2p3q4R5s6T7u8V9w0XyZ",
        "cT5h8LgJ2a4V3c4yF5g6H7j8K9L0M1n2p3q4R5s6T7u8V9w0XyZ",
        "cT5h8LgJ2a4V3c4yF5g6H7j8K9L0M1n2p3q4R5s6T7u8V9w0XyZ"   ],   
"recoveryKeyWIF": "cT5h8LgJ2a4V3c4yF5g6H7j8K9L0M1n2p3q4R5s6T7u8V9w0XyZ" }

(Note: this will be generated using index.ts).

Funding Transaction:
 
Funded Address: tb1qztq5rg30lv8y7kup7tftuelppcy2f9u9ygm8daq7gv4lgf0dw3ss3hj9qw   
Funding TXID: e9e764b3c63740d0eef68506970e80f819d360bdfc173d0b983f1e3d5411096d  
Funding VOUT: 1   
Funding ScriptPubKey: OP_0 12c141a22ffb0e4f5b81f2d2be67e10e08a49785223676f41e432bf425ed7461  

Any idea why my manually constructed witness would be considered non-standard?
Thank you.



Source link

Related articles

XRP Healthcare Shuts Down: How a Wallet-App Flaw Drained 4,011 XRP Wallets

September 12, 2026
A16z Wants Your Crypto Private and Your Car Tracked by Flock

A16z Wants Your Crypto Private and Your Car Tracked by Flock

September 12, 2026
Share76Tweet47

Related Posts

XRP Healthcare Shuts Down: How a Wallet-App Flaw Drained 4,011 XRP Wallets

by Moussa
September 12, 2026
0

Last week, an attacker drained 4,011 wallets tied to XRP Healthcare’s XRPH Wallet app, stealing an estimated $450,000 to $452,000...

A16z Wants Your Crypto Private and Your Car Tracked by Flock

A16z Wants Your Crypto Private and Your Car Tracked by Flock

by Moussa
September 12, 2026
0

Key TakeawaysA16z backs Flock’s surveillance network while its crypto arm champions financial privacy.Flock has faced growing backlash, with hundreds of...

XRP ETF Demand Holds Firm Despite a 2.8% Token Drop

XRP ETF Demand Holds Firm Despite a 2.8% Token Drop

by Moussa
September 12, 2026
0

U.S. spot Bitcoin ETFs reportedly shed $282.6 million on September 10, their third straight session of net outflows. Over the...

Canada Regulator Clears Tokenized Deposits for Banking System

Canada Regulator Clears Tokenized Deposits for Banking System

by Moussa
September 12, 2026
0

Key TakeawaysCanada’s OSFI ruled tokenized deposits legally match traditional ones, removing a major regulatory hurdle.The regulator adopted a tech-neutral stance,...

Hello, everyone.Can a Bitcoin node build a partial UTXO set from only the most recent blocks and use it to validate new transactions?

by Moussa
September 12, 2026
0

I am investigating whether a storage-constrained Bitcoin node could retain only the most recent (N) blocks, construct a partial UTXO...

Load More

youssufi.com

sephina.com

[vc_row full_width="stretch_row" parallax="content-moving" vc_row_background="" background_repeat="no-repeat" background_position="center center" footer_scheme="dark" css=".vc_custom_1517813231908{padding-top: 60px !important;padding-bottom: 30px !important;background-color: #191818 !important;background-position: center;background-repeat: no-repeat !important;background-size: cover !important;}" footer_widget_title_color="#fcbf46" footer_button_bg="#fcb11e"][vc_column width="1/4"]

We bring you the latest in Crypto News

[/vc_column][vc_column width="1/4"][vc_wp_categories]
[/vc_column][vc_column width="1/4"][vc_wp_tagcloud taxonomy="post_tag"][/vc_column][vc_column width="1/4"]

Newsletter

[vc_raw_html]JTNDcCUzRSUzQ2RpdiUyMGNsYXNzJTNEJTIydG5wJTIwdG5wLXN1YnNjcmlwdGlvbiUyMiUzRSUwQSUzQ2Zvcm0lMjBtZXRob2QlM0QlMjJwb3N0JTIyJTIwYWN0aW9uJTNEJTIyaHR0cHMlM0ElMkYlMkZhcHByb3gub3JnJTJGJTNGbmElM0RzJTIyJTNFJTBBJTBBJTNDaW5wdXQlMjB0eXBlJTNEJTIyaGlkZGVuJTIyJTIwbmFtZSUzRCUyMm5sYW5nJTIyJTIwdmFsdWUlM0QlMjIlMjIlM0UlM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1maXJzdG5hbWUlMjIlM0UlM0NsYWJlbCUyMGZvciUzRCUyMnRucC0xJTIyJTNFRmlyc3QlMjBuYW1lJTIwb3IlMjBmdWxsJTIwbmFtZSUzQyUyRmxhYmVsJTNFJTBBJTNDaW5wdXQlMjBjbGFzcyUzRCUyMnRucC1uYW1lJTIyJTIwdHlwZSUzRCUyMnRleHQlMjIlMjBuYW1lJTNEJTIybm4lMjIlMjBpZCUzRCUyMnRucC0xJTIyJTIwdmFsdWUlM0QlMjIlMjIlM0UlM0MlMkZkaXYlM0UlMEElM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1lbWFpbCUyMiUzRSUzQ2xhYmVsJTIwZm9yJTNEJTIydG5wLTIlMjIlM0VFbWFpbCUzQyUyRmxhYmVsJTNFJTBBJTNDaW5wdXQlMjBjbGFzcyUzRCUyMnRucC1lbWFpbCUyMiUyMHR5cGUlM0QlMjJlbWFpbCUyMiUyMG5hbWUlM0QlMjJuZSUyMiUyMGlkJTNEJTIydG5wLTIlMjIlMjB2YWx1ZSUzRCUyMiUyMiUyMHJlcXVpcmVkJTNFJTNDJTJGZGl2JTNFJTBBJTNDZGl2JTIwY2xhc3MlM0QlMjJ0bnAtZmllbGQlMjB0bnAtcHJpdmFjeS1maWVsZCUyMiUzRSUzQ2xhYmVsJTNFJTNDaW5wdXQlMjB0eXBlJTNEJTIyY2hlY2tib3glMjIlMjBuYW1lJTNEJTIybnklMjIlMjByZXF1aXJlZCUyMGNsYXNzJTNEJTIydG5wLXByaXZhY3klMjIlM0UlQzIlQTBCeSUyMGNvbnRpbnVpbmclMkMlMjB5b3UlMjBhY2NlcHQlMjB0aGUlMjBwcml2YWN5JTIwcG9saWN5JTNDJTJGbGFiZWwlM0UlM0MlMkZkaXYlM0UlM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1idXR0b24lMjIlM0UlM0NpbnB1dCUyMGNsYXNzJTNEJTIydG5wLXN1Ym1pdCUyMiUyMHR5cGUlM0QlMjJzdWJtaXQlMjIlMjB2YWx1ZSUzRCUyMlN1YnNjcmliZSUyMiUyMCUzRSUwQSUzQyUyRmRpdiUzRSUwQSUzQyUyRmZvcm0lM0UlMEElM0MlMkZkaXYlM0UlM0NiciUyRiUzRSUzQyUyRnAlM0U=[/vc_raw_html][/vc_column][/vc_row]
No Result
View All Result
  • Contact Us
  • Homepages
  • Business
  • Guide

© 2024 APPROX FOUNDATION - The Crypto Currency News