analyze_psbt_security
Detect mempool sniping vulnerabilities in Bitcoin PSBTs by analyzing signature hash flags and multisig configurations to identify front-running risks in ordinals listings.
Instructions
Analyze a PSBT for ordinals inscription listing mempool sniping vulnerability.
Detects whether an ordinals listing PSBT is vulnerable to front-running in the mempool. A listing is VULNERABLE when it uses SIGHASH_SINGLE|ANYONECANPAY without a 2-of-2 multisig locking step — an attacker can redirect the inscription before confirmation. A listing is PROTECTED when the inscription is locked in a 2-of-2 P2WSH multisig and the marketplace co-signs with SIGHASH_ALL, preventing any transaction modification.
No Bitcoin node required — analysis is pure PSBT parsing (BIP 174).
Args: psbt_hex: Hex-encoded PSBT string (BIP 174 v0)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| psbt_hex | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/bitcoin_mcp/server.py:1738-1754 (handler)The tool handler for "analyze_psbt_security" which calls _psbt_analyze to assess the PSBT vulnerability.
@mcp.tool() def analyze_psbt_security(psbt_hex: str) -> str: """Analyze a PSBT for ordinals inscription listing mempool sniping vulnerability. Detects whether an ordinals listing PSBT is vulnerable to front-running in the mempool. A listing is VULNERABLE when it uses SIGHASH_SINGLE|ANYONECANPAY without a 2-of-2 multisig locking step — an attacker can redirect the inscription before confirmation. A listing is PROTECTED when the inscription is locked in a 2-of-2 P2WSH multisig and the marketplace co-signs with SIGHASH_ALL, preventing any transaction modification. No Bitcoin node required — analysis is pure PSBT parsing (BIP 174). Args: psbt_hex: Hex-encoded PSBT string (BIP 174 v0) """ result = _psbt_analyze(psbt_hex) return json.dumps(result, indent=2) - src/bitcoin_mcp/server.py:1673-1736 (helper)Helper function that parses and analyzes the PSBT for security vulnerabilities.
def _psbt_analyze(psbt_hex: str) -> dict: """Parse a PSBT hex string and return a vulnerability assessment dict.""" import binascii try: data = binascii.unhexlify(psbt_hex.strip()) except Exception: return {"error": "Invalid hex encoding"} if not data.startswith(_PSBT_MAGIC): return {"error": "Invalid PSBT: missing magic bytes"} offset = 5 global_map, offset = _psbt_parse_map(data, offset) raw_tx = global_map.get(b"\x00") if raw_tx is None: return {"error": "Invalid PSBT: missing unsigned transaction (BIP 174 v0 required)"} input_count, _ = _psbt_read_varint(raw_tx, 4) inputs = [] for i in range(input_count): input_map, offset = _psbt_parse_map(data, offset) sighash_type = None if b"\x03" in input_map: raw = input_map[b"\x03"] sighash_type = int.from_bytes(raw[:4].ljust(4, b"\x00"), "little") for key, val in input_map.items(): if key and key[:1] == b"\x02" and len(val) >= 1 and sighash_type is None: sighash_type = val[-1] sighash_name = _PSBT_SIGHASH_NAMES.get(sighash_type, f"0x{sighash_type:02x}") if sighash_type is not None else None witness_script = input_map.get(b"\x05") is_multisig = _psbt_is_2of2_multisig(witness_script) if witness_script else False if sighash_type == 0x83: vuln = "protected" if is_multisig else "vulnerable" elif sighash_type is not None: vuln = "not_applicable" else: vuln = "unknown" inputs.append({ "index": i, "sighash_type": sighash_type, "sighash_name": sighash_name, "has_witness_script": witness_script is not None, "is_2of2_multisig": is_multisig, "vulnerability": vuln, }) vulns = [inp["vulnerability"] for inp in inputs] if "vulnerable" in vulns: overall_risk = "vulnerable" elif "protected" in vulns: overall_risk = "protected" elif all(v == "not_applicable" for v in vulns): overall_risk = "not_inscription_listing" else: overall_risk = "unknown" return {"input_count": input_count, "overall_risk": overall_risk, "inputs": inputs}