summaryrefslogtreecommitdiff
path: root/58/4b3c10d0d59d714ed3bc81c13e7007967678ae
blob: 9ae302bae4073a4dd5f2b7a9f4b99491481e9a19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
Return-Path: <alicexbt@protonmail.com>
Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])
 by lists.linuxfoundation.org (Postfix) with ESMTP id 8E8DAC0032
 for <bitcoin-dev@lists.linuxfoundation.org>;
 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 <bitcoin-dev@lists.linuxfoundation.org>;
 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 <bitcoin-dev@lists.linuxfoundation.org>;
 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 <bitcoin-dev@lists.linuxfoundation.org>;
 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 <bitcoin-dev@lists.linuxfoundation.org>
From: alicexbt <alicexbt@protonmail.com>
Message-ID: <QubGl0eXGrvufb_878QU6FKQdIQ7T4t0kgs7KNraNBFKSAqF2z2gEET67pJFb3fTwOH8vx8iqBFuhNGZIzfAMS_SQIiRziqxJ6M_B0_mKuM=@protonmail.com>
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 <bitcoin-dev.lists.linuxfoundation.org>
List-Unsubscribe: <https://lists.linuxfoundation.org/mailman/options/bitcoin-dev>, 
 <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=unsubscribe>
List-Archive: <http://lists.linuxfoundation.org/pipermail/bitcoin-dev/>
List-Post: <mailto:bitcoin-dev@lists.linuxfoundation.org>
List-Help: <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=help>
List-Subscribe: <https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev>, 
 <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=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.