Sell or License Any File for Bitcoin

Posted by Ronak Mehta

Sell or License Any File for Bitcoin

Overview

In this tutorial we'll show you how to set up a simple static file server that hosts files you can sell for bitcoin. By extending this example you can run your very own iTunes-like digital media store.

Prerequisites

Install 21

You will need the following items:

  • Either a 21 Bitcoin Computer or 21
  • The latest version of the 21 software, obtained by running 21 update

If you've got all the prerequisites, you are ready to go. Let's get started!

Step 1: Create a server to sell digital goods for bitcoin

Open a terminal. 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 folder for your project:

mkdir fileserver && cd fileserver

Now start editing a file named server.py in this directory with the following code:

#!/usr/bin/env python3
import os
import sys
import json
import random
import os.path

from flask import Flask
from flask import request
from flask import send_from_directory

from two1.wallet import Wallet
from two1.bitserv.flask import Payment

app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)

# Print error and die if a files directory isn't provided
if len(sys.argv) != 2:
    print("Usage: {} <files_directory>".format(sys.argv[0]))
    sys.exit(1)

dir_path = os.path.abspath(sys.argv[1])

# get a list of the files in the directory
file_list = sorted(os.listdir(dir_path))

# simple content model: dictionary of files w/ prices
files = {}
for file_id in range(len(file_list)):
    files[file_id+1] = file_list[file_id], random.randrange(3000, 5000)


# endpoint to look up files to buy
@app.route('/files')
def file_lookup():
    return json.dumps(files)


# return the price of the selected file
def get_price_from_request(request):
    id = int(request.args.get('selection'))
    return files[id][1]


# machine-payable endpoint that returns selected file if payment made
@app.route('/buy')
@payment.required(get_price_from_request)
def buy_file():

    # extract selection from client request
    sel = int(request.args.get('selection'))

    # check if selection is valid
    if(sel < 1 or sel > len(file_list)):
        return 'Invalid selection.'
    else:
        return send_from_directory(dir_path, file_list[int(sel)-1])

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

Before you start the server, we will need to create a directory and load it up with some files to sell. Execute the following at the command line:

cd ..
mkdir sellfiles
cd sellfiles
wget https://bitcoin.org/bitcoin.pdf
wget https://en.bitcoin.it/w/images/en/2/29/BC_Logo_.png
cd ..

You can also copy more content from your computer into the sellfiles/ directory. If you are connected to a 21 Bitcoin Computer from a Linux or Mac machine, you can also transfer more content from your laptop to your Bitcoin Computer with the scp command. For example:

## Only if you use a 21 Bitcoin Computer
scp file.txt twenty@IP_ADDRESS:~/sellfiles/

The above command copies file.txt from your PC or Mac to the location $HOME/sellfiles/ on the Bitcoin Computer. Once you have some content in there, go ahead and start the server with:

python3 fileserver/server.py sellfiles

Step 3: Create a client to buy digital goods for bitcoin

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

This will be the user identity for the client who will buy the file that the server is hosting.

Create a folder to house the client project:

mkdir fileclient && cd fileclient

Now start editing a file named client.py in this directory and type in the following code:

#!/usr/bin/env python3
import json

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

# set up bitrequest client for BitTransfer requests
wallet = Wallet()
requests = BitTransferRequests(wallet)

# server address
server_url = 'http://[::1]:5000/'


def buy_file():
    # get the file listing from the server
    response = requests.get(url=server_url+'files')
    file_list = json.loads(response.text)

    # print the file list to the console
    for file in range(len(file_list)):
        print("{}. {}\t{}".format(file+1, file_list[str(file+1)][0], file_list[str(file+1)][1]))

    try:
        # prompt the user to input the index number of the file to be purchased
        sel = input("Please enter the index of the file that you would like to purchase:")

        # check if the input index is valid key in file_list dict
        if sel in file_list:
            print('You selected {} in our database'.format(file_list[sel][0]))

            # create a 402 request with the server payout address
            sel_url = server_url+'buy?selection={0}&payout_address={1}'
            answer = requests.get(url=sel_url.format(int(sel), wallet.get_payout_address()), stream=True)

            if answer.status_code != 200:
                print("Could not make an offchain payment. Please check that you have sufficient buffer.")
            else:
                # open a file with the same name as the file being purchased and stream the data into it.
                filename = file_list[str(sel)][0]

                with open(filename, 'wb') as fd:
                    for chunk in answer.iter_content(4096):
                        fd.write(chunk)
                fd.close()
                print('Congratulations, you just purchased a file for bitcoin!')

        else:
            print("That is an invalid selection.")

    except ValueError:
        print("That is an invalid input. Only numerical inputs are accepted.")

if __name__ == '__main__':
    buy_file()

Save this file and close it. Now run your client script with the following command:

python3 client.py

If all goes well, you should see a prompt for the files you can buy on the server, along with the price of those files. Execute a purchase and you should see the file placed into the client directory. You can confirm a purchase happened by running 21 log

Next Steps

Now that you have the basic loop working, if you want to add more files you can get inspiration from some of the following resources:

These are for inspiration only, to get a sense of what kinds of digital goods people value on the internet. It is your responsibility to make sure that you have the license to sell or resell these items! Not all public domain or Creative Commons material is licensed for resale, and some of it is only licensed outside the US.

Subject to that proviso, there are an essentially infinite list of things you can sell in this fashion - books, tutorials, music, movies, icons, photos, artwork, podcasts, code, Excel templates, PSD files, recordings, and the like. One of the most interesting applications would be re-selling bundles of your Instagram photos, Youtube videos, or Soundcloud recordings - anything that you’ve curated or assembled on social media where you created it and possess the license. We already know these items have some value, but we have only begun in terms of the peer-to-peer social monetization of digital goods with bitcoin!

One other note: please also know that if you want to set this up as a production-grade server to sell files for bitcoin, you'll want to do something more robust than the simple server.py script. For now, take a look at this and this article; we'll be integrating these instructions into the application code in the near future.


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.