Skip to content

Instantly share code, notes, and snippets.

@1440000bytes
Last active Jul 28, 2022
Embed
What would you like to do?

Coinjoin implementation using nostr

  1. Input registration:

    5 users publish inputs using nostr

import requests
import pexpect

url = "http://127.0.0.1:18332/wallet/W1"

headers = {
    'Authorization': 'Basic dXNlcjpwYXNz',
    'Content-Type': 'text/plain'
}

def publish():
    noscl = pexpect.spawn ('noscl publish "' + input + '"')
    noscl.expect ('Seen ')
    output = str(noscl.read())
    eventid = output.replace('b"','').replace(" on 'wss://nostr-pub.wellorder.net'.","").replace('\\r\\n"','')
    noscl.interact()

    return eventid

def listunspent():

    payload = "{\"jsonrpc\": \"1.0\", \"id\": \"joinstr\", \"method\": \"listunspent\"}"
    response = requests.request("POST", url, headers=headers, data=payload)

    i =0
    for i in range(0,len(response.json()['result'])):
        txid = response.json()['result'][i]['txid']
        vout = response.json()['result'][i]['vout']
        amount = response.json()['result'][i]['amount']
        print(txid, ",",vout ,",",amount)
        i = i + 1

if __name__=="__main__":

    utxo_list = listunspent()
    input = input("Enter input for registration (txid,vout): ")
    eventid = publish()
    print(eventid)
  1. Output registration:

    5 users share a new address for output.

  2. Create and publish tx:

    User that registered the last output creates a PSBT with all inputs, outputs using createpsbt and publishes it.

  3. Sign and broadcast tx:

    All users verify, sign with walletprocesspsbt and publish PSBT. Last user that signs the transaction, combines them with combinepsbt, finalize with finalizepsbt and broadcast with sendrawtransaction.

Example of a coinjoin transaction:

image

Things that could be improved:

  1. Relay shares a random number with first input and same number with next 4 inputs registered. Clients will mention this number in the request sent for registering outputs to prove they own one registered input in this round. If more than 5 outputs are registered for a round its cancelled.

  2. Use new private key for nostr and a new tor circuit every time soemtihng is published using nostr.

  3. Break UTXOs in pool denominations before coinjoin if amount exceeds pool denomination by more than 200 sats.

  4. Create an Android app that connects with bitcoin core and makes it easier to do coinjoin.

@benthecarman
Copy link

benthecarman commented Jul 20, 2022

From my first impressions it looks like the coordinator is able to link the inputs to the outputs because there is no blind signing done.

@1440000bytes
Copy link
Author

1440000bytes commented Jul 20, 2022

Coordination happens between clients and relays they use over nostr. There isn't any centralized coordinator. I read about blind signing here: https://github.com/Samourai-Wallet/Whirlpool/blob/whirlpool/THEORY.md#chaumian-coinjoin although think this could be solved with a shared secret for the round.

Example:


Input 1 registered. Relay responds with 123456. Txid and vout published.
Input 2 registered. Relay responds with 123456. Txid and vout published.
Input 3 registered. Relay responds with 123456. Txid and vout published.
Input 4 registered. Relay responds with 123456. Txid and vout published.
Input 5 registered. Relay responds with 123456. Txid and vout published.

Output 1 registered. Client adds 123456 in the request although its not published.
Output 2 registered. Client adds 123456 in the request. Only address is published.
Output 3 registered. Client adds 123456 in the request. Only address is published.
Output 4 registered. Client adds 123456 in the request. Only address is published.
Output 5 registered. Client adds 123456 in the request. Only address is published.

Relay would know outputs are registered by clients that registered inputs but this does not reveal input and output links as the number remains same for all inputs in a round with different nostr keys and tor circuit used for all requests. If someone tries to register an output without registering any inputs, request would not have the number initially shared with inputs (123456 in this example) so request can be rejected with error or published as unverified. Clients can use multiple relays at the same time to avoid trusting one relay. This would result in different shared secret number but same process. If a relay tries to cheat, users will not sign the transaction and avoid using it in future.

@MaxHillebrand
Copy link

MaxHillebrand commented Jul 28, 2022

Checkout Coinshuffle for a sophisticated decentralized coinjoin coordination scheme.

Single denomintion rounds are very blockspace expensive and create non-private change or loss, so consider multi-standard-denominations [low hamming weight numbers are efficient in decomposition].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment