A Bitcoin-Payable Notary Public

Posted by Tyler Julian

A Bitcoin-Payable Notary Public

Overview

In this tutorial we'll show you how to create a bitcoin-payable notary public service. Any user can pay bitcoin to a server endpoint that writes a custom message to the blockchain to be stored forever.

After reading, you should be able to write data to the blockchain and read it back whenever you'd like. You'll also gain experience using the various bitcoin classes to build your own transactions.

Prerequisites

Install 21

You will need the following items:

If you've done all that, you are ready to go. Let's get started!

Step 1: create and start the server

Open a new terminal window. If you use a 21 Bitcoin Computer, ssh into it:

## Only necessary if you use a 21 Bitcoin Computer
ssh twenty@IP_ADDRESS

Create a project folder to house your app and an empty file as follows:

mkdir bitcoin-notary-server
touch bitcoin-notary-server/server.py

The server is going to need a blockchain balance for this example, so flush 20,000 satoshis to your bitcoin wallet by running 21 flush 20000- then wait for the flush to complete (this usually takes about 20 minutes). At any time, you can check your status; when it says you have at least 5,000 satoshis in your 21.co buffer and 20,000 satoshis in your onchain balance, you can continue following this tutorial:

21 status

Now open server.py in your text editor and type in the following code (also downloadable here):

#!/usr/bin/env python3
from flask import Flask, request

from two1.wallet.two1_wallet import Two1Wallet
from two1.blockchain.twentyone_provider import TwentyOneProvider
from two1.bitcoin import utils
from two1.bitcoin import Script
from two1.bitcoin import Transaction, TransactionInput, TransactionOutput
from two1.bitserv.flask import Payment
from two1.wallet import fees

# Create application objects
app = Flask(__name__)
wallet = Two1Wallet(Two1Wallet.DEFAULT_WALLET_PATH, TwentyOneProvider())
payment = Payment(app, wallet)

# We need to make sure that the UXTO we use has enough money left
# in it after fees to exceed the dust limit, or else it will be
# rejected by the blockchain.
DUST_LIMIT = fees.DUST_LIMIT

# Define the fee we're willing to pay for the tx
tx_fee = 20000


@app.route('/write-message')
@payment.required(tx_fee + DUST_LIMIT)
def write_message():
    """Write a message to the blockchain."""

    msg = request.args.get('message')

    # Create a bitcoin script object with our message
    if (len(msg) > 40):
        raise Exception('Message is too long and may not be accepted.')
    message_script = Script('OP_RETURN 0x{}'.format(utils.bytes_to_str(msg.encode())))

    # Get the first UTXO from our set that can cover the fee
    utxo = None
    for utxo_addr, utxos in wallet.get_utxos().items():
        for u in utxos:
            if u.value > tx_fee + DUST_LIMIT:
                utxo = u
                break
        if utxo:
            break

    if not utxo:
        raise Exception('No UTXOs available to pay for the transaction.')

    # Build the transaction inputs (there is only one, but Transaction expects a list)
    inputs = [TransactionInput(outpoint=utxo.transaction_hash,
                               outpoint_index=utxo.outpoint_index,
                               script=utxo.script,
                               sequence_num=0xffffffff)]

    outputs = []
    # Build one output with our custom message script
    outputs.append(TransactionOutput(value=0, script=message_script))
    # Build another output to pay the UTXO money back to one of our addresses
    _, change_key_hash = utils.address_to_key_hash(wallet._accounts[0].get_next_address(True))
    outputs.append(TransactionOutput(value=utxo.value - tx_fee,
                                     script=Script.build_p2pkh(change_key_hash)))

    # Build an unsigned transaction object
    txn = Transaction(version=Transaction.DEFAULT_TRANSACTION_VERSION,
                      inputs=inputs,
                      outputs=outputs,
                      lock_time=0
                      )

    # Sign the transaction with the correct private key
    private_key = wallet.get_private_key(utxo_addr)
    txn.sign_input(input_index=0,
                   hash_type=Transaction.SIG_HASH_ALL,
                   private_key=private_key,
                   sub_script=utxo.script
                   )

    # Broadcast the transaction
    tx = wallet.broadcast_transaction(txn.to_hex())
    return tx

if __name__ == '__main__':
    app.run(host='::')

Save the file and exit your text editor. Then start the server by running the following command:

python3 bitcoin-notary-server/server.py

Your bitcoin-enabled notary web-service is up and running! Let's create a client to purchase your notary service for a bitcoin micropayment.

Step 2: purchase the endpoint

Open a new terminal window. If you use a 21 Bitcoin Computer, ssh into it:

## Only necessary if you use a 21 Bitcoin Cobmputer
ssh twenty@IP_ADDRESS

Create a new directory and a file for the client:

mkdir bitcoin-notary-client
touch bitcoin-notary-client/client.py

Open the client.py file in your text editor and type in the following code (also downloadable here):

#!/usr/bin/env python3
import sys
import urllib.parse

from two1.wallet import Wallet
from two1.bitrequests import BitTransferRequests

wallet = Wallet()


def write_message(raw_msg):
    msg = urllib.parse.quote_plus(raw_msg)
    requests = BitTransferRequests(wallet)

    # purchase the bitcoin payable endpoint
    response = requests.get('http://[::1]:5000/write-message?message={}'.format(msg))

    # print out the transaction
    print("Transaction: {}".format(response.text))
    print("View it live at https://live.blockcypher.com/btc/tx/{}".format(response.text))

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Usage: {} <message to put in blockchain>'.format(sys.argv[0]))
        sys.exit(1)

    write_message(sys.argv[1])

Save and close the file. Then run the client; the text in quotes is the text that will be added to the blockchain.

python3 bitcoin-notary-client/client.py "Carving my name into the blockchain"

Congratulations, you just wrote a message to the blockchain that will be stored there permanently! Here's an an example transaction visible on blockcypher.

Next Steps

It's worth tracing through exactly how this worked:

  • First, we wrote a message. Our message was encoded in the script of an output after an OP_RETURN instruction, which tells bitcoin nodes that this output cannot be used as an input for another transaction. Note that many nodes will reject a transaction if there is more than 40 bytes of data after an OP_RETURN. This will be extended to 80 bytes as Bitcoin Core 0.12.0 becomes standard.

    • Then, we created inputs. To build an input, we needed an unspent transaction output (UTXO), which is essentially just spendable bitcoin that we have in our wallet. We had to include a fee for miners, so we made sure our UTXO was large enough to cover it.

    • Next, we created outputs. One of our transaction outputs will be our message, and another output will send our own money (the change) back to our wallet.

    • Next, we built and signed the transaction. We combined the inputs and outputs into a transaction and signed it.

    • Broadcasting the transaction. Last but not least, we got the word out by broadcasting the transaction to the bitcoin network.

This example mixes concepts from the Bitcoin Payable API tutorial and the Bitcoin Library tutorials, showing how to build bitcoin-payable APIs that manipulate bitcoin data structures and write to the blockchain - therefore actually "costing bitcoin" in an intrinsic sense. As a next step, you might investigate the Library and Wallet tutorials.


How to send your Bitcoin to the Blockchain

Just as a reminder, you can send bitcoin mined or earned in your 21.co balance to the blockchain at any time by running 21 flush . A transaction will be created within 10 minutes, and you can view the transaction id with 21 log. Once the transaction has been confirmed, you can check the balance in your bitcoin wallet from the command line with wallet balance, and you can send bitcoin from your wallet to another address with wallet sendto $BITCOIN_ADDRESS --satoshis $SATOSHI_AMOUNT --use-unconfirmed. The --satoshis flag allows you to specify the amount in satoshis; without it the sendto amount is in BTC, but this behavior is deprecated and will be removed soon. The --use-unconfirmed flag ensures that you can send even if you have unconfirmed transactions in your wallet.


Ready to sell your endpoint? Go to slack.21.co

Ready to try out your bitcoin-payable server in the wild? Or simply want to browse and purchase from other bitcoin-enabled servers? Head over to the 21 Developer Community at slack.21.co to join the bitcoin machine-payable marketplace hosted on the 21 peer-to-peer network.