Return-Path: Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 8E8DAC0032 for ; Fri, 3 Mar 2023 01:44:22 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 5B3CC82116 for ; Fri, 3 Mar 2023 01:44:22 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 5B3CC82116 Authentication-Results: smtp1.osuosl.org; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.a=rsa-sha256 header.s=protonmail3 header.b=VXxqkg2H X-Virus-Scanned: amavisd-new at osuosl.org X-Spam-Flag: NO X-Spam-Score: 1.94 X-Spam-Level: * X-Spam-Status: No, score=1.94 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, BITCOIN_IMGUR=2.043, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, PDS_OTHER_BAD_TLD=1.999, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001] autolearn=no autolearn_force=no Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id vtwHbuWn7cjT for ; Fri, 3 Mar 2023 01:44:20 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org BD21582110 Received: from mail-4319.protonmail.ch (mail-4319.protonmail.ch [185.70.43.19]) by smtp1.osuosl.org (Postfix) with ESMTPS id BD21582110 for ; Fri, 3 Mar 2023 01:44:19 +0000 (UTC) Date: Fri, 03 Mar 2023 01:44:03 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1677807856; x=1678067056; bh=LU7y0AVD3lCVP4VacqsDgryJUMy7OoVvq2ptMkUF4hU=; h=Date:To:From:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=VXxqkg2H8/LHQS3nfI/2zZBiC/U7IA5H97J065p+CaSsPvSkZde7fuz9b3MbnHqCg agD14K00Uqk7pvHoqtETxrCMJeLt4tRLG4UOdvw7OotWAKndjgyriJmWT4wFWJzsRA VJQfbKUKxtd2atXFtfNeZDGDfWsq/7xGK7jjy4zmrI1KTf1R/9wArGuue09oVDLGTh 0OW6Q3ckpLGO3mw4zXLPTeFszDeoWiq6MUjs3IDCMvOA8Y2CJJPCYwHI+UcLsRO3f/ 6GOaLoGNBnS0Y/wI3K+bn4g7TY6M0+mK3Onn4L7BPiajeUa1A+rz4Tzg0beU93G4ep ZGb8VsijfVZgQ== To: Bitcoin Protocol Discussion From: alicexbt Message-ID: Feedback-ID: 40602938:user:proton MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Mailman-Approved-At: Fri, 03 Mar 2023 14:36:32 +0000 Subject: [bitcoin-dev] BIP: Trust minimized swaps using PSBT, SINGLE|ANYONECANPAY and nostr X-BeenThere: bitcoin-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Bitcoin Protocol Discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 03 Mar 2023 01:44:22 -0000 Hi Bitcoin Developers, I have written a BIP that describes the process to swap inscriptions howeve= r there can be other use cases for it as well: https://gist.github.com/1440= 000bytes/a7deeb3f1740bc533a61fbcc1fe58d77 Feel free to share your opinion or feedback to improve the usage of PSBTs i= n swaps. BIP: 2023-ordswap Layer: Applications Title: Trust minimized swaps using PSBTs Author: /dev/fd0 Status: Draft Created: 2023-03-02 License: Public Domain =20 =20 ### Introduction This BIP describes a process for creating offers using PSBTs to swap inscri= ptions. It was originally shared by [Casey](https://github.com/casey/ord/is= sues/802). There are two other approaches (`joinpsbts` and coinswap) to swa= p inscriptions however they degrade the UX and use of SINGLE|ANYONECANPAY w= orks better. ### Specification [SINGLE|ANYONECANPAY](https://en.bitcoin.it/wiki/Contract#SIGHASH_flags) is= used for creating a PSBT by the seller. It is signed and published as offe= r. Buyer updates the PSBT with appropriate inputs and outputs. Order of inp= uts and outputs in the PSBT is very important as wrong ordering can burn in= scriptions. [Ordinal theory](https://docs.ordinals.com/faq.html?#how-does-o= rdinal-theory-work) uses an algorithm to determine how satoshis hop from th= e inputs of a transaction to its outputs. ### Protocol Sequence diagram: ```mermaid sequenceDiagram Note right of Seller: Create and Sign PSBT Seller->>+Nostr relays: Publish offer Buyer->>+Nostr relays: Accept offer Note left of Buyer: Add inputs, outputs, sign and broadcast PSBT ``` Seller: - Create PSBT with inscription UTXO input and a new address with sell amoun= t as output - Sign PSBT - Publish PSBT as defined in [NIP](https://github.com/orenyomtov/openordex/= blob/main/NIP.md) Buyer: - Add new address as output in PSBT to receive inscription - Create [dummy UTXO](https://i.imgur.com/8Rw3TFX.png) if not available in = wallet (Less than 1000 sats) - Add UTXO to pay seller and dummy UTXO as inputs in PSBT - Sign and broadcast transaction.=20 Example tx: https://mempool.space/signet/tx/ee7032f08ed18113c16ab8759d294c0= 9f57492d8d255b5dbd16326df53bbdcac This transaction has 3 inputs (dummy, inscription, UTXO used for paying sel= ler) and 4 outputs (inscription, payment, new dummy for future, change) Note: Openordex creates a dummy UTXO and reuses address if there is no dumm= y UTXO found for the address entered by buyer. Example: https://mempool.spa= ce/signet/tx/388942887f79358a1deba3aae86e97b982a923566b2ef2249eab42288efc5a= bf Pseudocode or Implementation (2 functions used by openordex for creating PS= BTs) ```js =09 async function generatePSBTListingInscriptionForSale(ordinalOutput, price, = paymentAddress) { let psbt =3D new bitcoin.Psbt({ network }); const [ordinalUtxoTxId, ordinalUtxoVout] =3D ordinalOutput.split(':') const tx =3D bitcoin.Transaction.fromHex(await getTxHexById(ordinalUtxo= TxId)) for (const output in tx.outs) { try { tx.setWitness(output, []) } catch { } } psbt.addInput({ hash: ordinalUtxoTxId, index: parseInt(ordinalUtxoVout), nonWitnessUtxo: tx.toBuffer(), // witnessUtxo: tx.outs[ordinalUtxoVout], sighashType: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transacti= on.SIGHASH_ANYONECANPAY, }); psbt.addOutput({ address: paymentAddress, value: price, }); return psbt.toBase64(); } ``` ```js generatePSBTBuyingInscription =3D async (payerAddress, receiverAddress,= price, paymentUtxos, dummyUtxo) =3D> { const psbt =3D new bitcoin.Psbt({ network }); let totalValue =3D 0 let totalPaymentValue =3D 0 // Add dummy utxo input const tx =3D bitcoin.Transaction.fromHex(await getTxHexById(dummyUt= xo.txid)) for (const output in tx.outs) { try { tx.setWitness(output, []) } catch { } } psbt.addInput({ hash: dummyUtxo.txid, index: dummyUtxo.vout, nonWitnessUtxo: tx.toBuffer(), // witnessUtxo: tx.outs[dummyUtxo.vout], }); // Add inscription output psbt.addOutput({ address: receiverAddress, value: dummyUtxo.value + Number(inscription['output value']), }); // Add payer signed input psbt.addInput({ ...sellerSignedPsbt.data.globalMap.unsignedTx.tx.ins[0], ...sellerSignedPsbt.data.inputs[0] }) // Add payer output psbt.addOutput({ ...sellerSignedPsbt.data.globalMap.unsignedTx.tx.outs[0], }) // Add payment utxo inputs for (const utxo of paymentUtxos) { const tx =3D bitcoin.Transaction.fromHex(await getTxHexById(utx= o.txid)) for (const output in tx.outs) { try { tx.setWitness(output, []) } catch { } } psbt.addInput({ hash: utxo.txid, index: utxo.vout, nonWitnessUtxo: tx.toBuffer(), // witnessUtxo: tx.outs[utxo.vout], }); totalValue +=3D utxo.value totalPaymentValue +=3D utxo.value } // Create a new dummy utxo output for the next purchase psbt.addOutput({ address: payerAddress, value: dummyUtxoValue, }) const fee =3D calculateFee(psbt.txInputs.length, psbt.txOutputs.len= gth, await recommendedFeeRate) const changeValue =3D totalValue - dummyUtxo.value - price - fee if (changeValue < 0) { throw `Your wallet address doesn't have enough funds to buy thi= s inscription. Price: ${satToBtc(price)} BTC Fees: ${satToBtc(fee + dummyUtxoValue)} BTC You have: ${satToBtc(totalPaymentValue)} BTC Required: ${satToBtc(totalValue - changeValue)} BTC Missing: ${satToBtc(-changeValue)} BTC` } // Change utxo psbt.addOutput({ address: payerAddress, value: changeValue, }); return psbt.toBase64(); } =09 ``` Note: Openordex reuses address for change, however this can be avoided. ### Acknowledgements - Casey Rodarmor - Oren Yomtov - Rijndael /dev/fd0 floppy disk guy Sent with Proton Mail secure email.