How 21 Micropayments Work

Posted by Balaji S. Srinivasan, Shayan Yassami, Nigel Drego

How 21 Micropayments Work

Overview

There are three ways to accomplish micropayments using 21.

  • Direct on-chain transactions
  • Off-chain transactions with BitTransfers
  • Micropayment channels

Let's go through these in turn after going through some preliminaries.

Concepts

Your 21.co buffer and your wallet/Blockchain balance

To understand the micropayment methods supported by 21, we first start with the concept of your 21.co buffer and your wallet/Blockchain balance.

The reason these are separate is that directly sending and receiving bitcoin is slow under the best of circumstances, and not fast enough to permit rapid acquisition of bitcoin for programming purposes. As such, we have implemented something we call buffering to speed up the process.

You can buy and sell digital goods with other 21 users within the 21 Network using your 21.co buffer, and then flush to your wallet when ready. This process of doing 21 flush will increment your wallet/blockchain balance.

To recap:

  • Your 21.co buffer: This is your buffer at 21.co. It is constantly incremented by the APIs you run in the background after your first run of 21 sell --all. Your 21.co buffer can also be increased by selling digital goods and services to others within the 21 Network. You can flush bitcoin from your 21.co buffer to your wallet with 21 flush. Importantly, only you can flush, buy, or earn to your 21.co buffer - every action on this buffer must be signed by your wallet.

  • Your blockchain/wallet balance: This is the balance in your 21 wallet on your local device. It is "local" in the sense that the private keys for the wallet are local to your laptop, 21 Bitcoin Computer, or other device. Your wallet's balance is visible on the blockchain and can be viewed with any block explorer. You can increase your blockchain/wallet balance by doing 21 flush, or by sending bitcoin directly to a wallet address.

HTTP 402: Payment Required

As background, since the beginning of the World Wide Web there have been many attempts to implement micropayments. In fact, the HTTP specification includes an error code which is typically not used: "Error 402, Payment Required".

We have implemented this error code in all three of our micropayments systems; as you go through the code samples, you will see it recur repeatedly as the initial challenge that a server issues when a client attempts to access a bitcoin-payable, HTTP-accessible resource.

How the client responds to this challenge differs depending on which of the three 21 micropayments systems you use.

  • With on-chain transactions, the client creates and broadcasts a new transaction for every challenge issued by the server

  • With off-chain transactions (BitTransfers), the client and server pay each other from their 21.co buffer, and then flush to the blockchain when done

  • With micropayment channels, the customer temporarily locks up some of their satoshis on the blockchain and can then release them one satoshi at a time to a merchant by creating a special transaction the merchant can broadcast to the blockchain before the customer's lock expires. This allows the customer to make repeated payments as low as a single satoshi for the transaction fee cost of just two on-chain transactions.

Each of these methods are described in more detail below.

On-Chain Transactions

Before you can do an on-chain transaction at 21, you need bitcoin in your machine wallet. You can do this by either (a) sending bitcoin to an address in your wallet or by (b) using 21 flush to flush bitcoin to a blockchain address controlled by your wallet.

Once you have some bitcoin, suppose you want to buy a bitcoin-payable API with an on-chain transaction. Here is the sequence of events that occur for an on-chain transaction:

  • The client makes an HTTP request to the server for an endpoint, say /foo
  • The server returns an HTTP response with a 402: Payment Required error, including a Bitcoin address and the price required to grant access to the resource
  • The client creates a transaction that pays the desired price to the specified address
  • The client then sends this transaction back to the server along with the original request for /foo
  • The server broadcasts this transaction to the Bitcoin network. At its discretion, it may wait until this transaction is confirmed to avoid a double spend, or proceed immediately
  • The server then returns the resource to the client

This process is (relatively) simple to understand, but highly inefficient as it will send one transaction to the blockchain for every API request. In the two1 library, we have developed a function which handles the mechanics of this process; here is how to call it within code:

#!/usr/bin/env python3
import json
from two1.wallet import Wallet
from two1.bitrequests import OnChainRequests

# Set up an on-chain transaction. The wallet is
# used to provide bitcoin for the request.
wallet = Wallet()
requests = OnChainRequests(wallet)

# Determine the price of a given endpoint, in satoshis
url = 'https://mkt.21.co/21dotco/expand_url/short_url/expand?short_url=http://bit.ly/1WNmMH7'
info = requests.get_402_info(url=url)
print(info)

# Buy the endpoint
results = requests.get(url=url)
print(json.loads(results.text))

Off-Chain Transactions (BitTransfers)

The general concept behind off-chain transactions is to avoid the inefficiency of writing to the blockchain until requested by the user.

Before you can execute an off-chain transaction in the 21 system, you need a bitcoin buffer at 21.co. You can get some by selling a digital good to someone for bitcoin.

Once you have some bitcoin in your 21.co buffer, here is the sequence of events that happen when you execute an off-chain transaction to buy an API call from another 21 user:

  • The client makes an HTTP request to the server for an endpoint, say /foo
  • The server returns an HTTP response with a 402: Payment Required error, including a 21.co username and the price required to grant access to the resource
  • The client uses their wallet to sign a request that pays that amount of bitcoin from the client's 21.co buffer to the server's username's 21.co buffer
  • The client also sends their signed request to the server for /foo
  • The server sends the signed request to 21.co for confirmation, to ensure that the buyer has sufficient bitcoin and was the bonafide signer (via verification with the buyer's public key)
  • Upon verification, the buyer's 21.co buffer is debited and the seller's 21.co buffer is credited
  • The server checks that their 21.co buffer has updated and returns the resource

These mechanistic details are taken care of for you on both the client and server sides with the two1 library. Here is how to execute an off-chain transaction (also known as a BitTransfer):

#!/usr/bin/env python3
import json
from two1.wallet import Wallet
from two1.bitrequests import BitTransferRequests

# Set up a BitTransfer. The wallet's private key
# is used to sign transactions, confirming that the
# buffer is spendable by the given 21 username.
wallet = Wallet()
requests = BitTransferRequests(wallet)

# Determine the price of a given endpoint, in satoshis
url = 'https://mkt.21.co/21dotco/zip_code_data/zipdata/collect?zip_code=94109'
info = requests.get_402_info(url=url)
print(info)

# Buy the endpoint
results = requests.get(url=url)
print(json.loads(results.text))

Micropayment Channels

While off-chain transactions are fast, they only interface with the Bitcoin network at the very end (when you are doing a 21 flush) and require some trust in a centralized actor (namely us!). While we do think we are reasonably trustworthy people, we also support micropayment channels, which have many of the advantages of both off-chain transactions (in terms of performance) and on-chain transactions (in terms of having the decentralized and low-trust security of blockchain transactions).

One important note: our implementation of micropayment channels benefits from the fact that every client has bitcoin. As such, setting up the initial micropayment channel deposit is easy. Here's how you would execute a micropayment channels payment:

#!/usr/bin/env python3
from time import sleep
from two1.wallet import Wallet
from two1.bitrequests import ChannelRequests
from two1.channels.paymentchannel import NotReadyError

# Set up a micropayment channel. The wallet is used
# to provide bitcoin and create the multisig deposit tx.
wallet = Wallet()
requests = ChannelRequests(wallet)

# Determine the price of a given endpoint, in satoshis
# Also give channel status
url = "https://mkt.21.co/21dotco/zip_code_data/zipdata/collect"
info = requests.get_402_info(url=url)
server_url = info.get('bitcoin-payment-channel-server')

for key, val in info.items():
    print('{}: {}'.format(key, val))

# Make many requests in the channel and print balance
queries = ("94109", "08015", "07728")
for ii in range(3):
    while True:
        try:
            results = requests.get(url=url, params=dict(zip_code=queries[ii]))
            channel_url = requests._channelclient.list(server_url)[0]
            print('Balance remaining in channel: {}'.format(requests._channelclient.status(channel_url).balance))
            break
        except NotReadyError:
            print("Channel not open yet.  Checking again in 60 seconds")
            sleep(60)

Summary

Summing up, here's a table that compares the three micropayment methods in terms of their advantages and disadvantages:

Advantages

Disadvantages

On-Chain Payments

Very simple to understand

Works without any client/server setup or minimum deposit

Up to 1 tx per API call

Limited by Blockchain (absolute max of ~500k per day)

Micropayment Channels

Only 2 tx on blockchain even for N API calls

Deposit and final tx are on blockchain

Requires client to have sufficient BTC for deposit that is nontrivial multiple of API call cost

Deposit requires confirmation time

BitTransfers

Fast: operates at speed of internet rather than speed of blockchain

No rate limits

Buffer not recorded on blockchain until 21 flush occurs