A Bitcoin Mashup

Posted by John Granata

A Bitcoin Mashup as a Digital Supply Chain

Overview

In this tutorial, you will compose two bitcoin-payable APIs to get a sense for what a machine-payable digital supply chain might look like. The overall result for your customer will be a service that accepts bitcoin to create unique, location-based digital artwork and then sends this artwork to the customer as an MMS message. We will build this in pieces.

  • First, you will set up a micropayments server that turns a date and location-specific search query into unique WordCloud art composed of words from the most popular tweets that fit the search parameters. The server will perform this service in exchange for a small amount of bitcoin.

  • Then, you'll set up a second server that charges bitcoin to send an MMS message to a provided phone number.

  • You will then compose these services to create a single bitcoin-payable service for the end user. The end user doesn't care about the implementation, but by using bitcoin in this fashion you have suddenly built a digital supply chain.

  • Specifically, the customer will send a request to the first server to generate the WordCloud artwork in exchange for bitcoin, and then this server will subcontract the MMS task out to another server by paying it some bitcoin to perform it's task, which is to send an MMS with the artwork back to the customer.

This example illustrates how it is possible to build digital supply chains by composing bitcoin-payable APIs hosted by different people.

Prerequisites

Install 21

You will need the following first:

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

Step 1: Set up the first server in the digital supply chain

Open up 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

Install the system packages we'll need for the demo.

## If you use Linux
sudo apt-get -y install libjpeg-dev libfreetype6-dev
## If you use Mac OS X
sudo brew install jpeg freetype

Then run the following commands:

sudo pip3 install geopy
sudo pip3 install wordcloud
sudo pip3 install numpy
sudo pip3 install pillow
sudo pip3 install imgurpython
sudo pip3 install flask

In order to successfully run this demo, you will need API keys from the following services: imgur, twitter & twilio. Before you proceed with the demo, please take a moment to go through this section to ensure you have all the necessary API keys. While the basic API signup procedure is similar across the 3 web services, the information below may help to reduce confusion when you register for your keys.

Imgur

Go to Imgur and sign into your imgur account. Select Register an Application from the left-hand side menu. Ignore the ERROR: Invalid Captcha message when you first come to this page. It will disappear upon form submission provided the correct captcha is entered. Enter a name in the Application Name field, and for Authorization Type, select option 2 - OAuth 2 authorization without a callback URL. Upon completion of the signup, put your client ID and client secret into environmental variables:

export IMGUR_CLIENT_ID=<your_id>
export IMGUR_CLIENT_SECRET=<your_secret>

Twitter

Go to Twitter and sign in to your twitter account. Click on the Create a new app button to register your app. Enter a name and description for your app, and put https://www.google.com into the Website field for now. Upon completion of the signup, put your consumer key and consumer secret into environmental variables:

export TWITTER_CONSUMER_KEY=<your_key>
export TWITTER_CONSUMER_SECRET=<your_secret>

Twilio

Go to Twilio to register your app and create a new API key. Click on the Create an API Key button. Enter the name of your app. Take note of the Sid and Secret on the next page. Select the check-box below Secret and click Done.

Click on PHONE NUMBERS at the top of the page and then select Buy a number. In the Capabilities field, make sure to select MMS. Click on Search to get a list of available numbers. Put the number you purchase, along with the Sid and Secret, into environmental variables:

export TWILIO_PHONE_NUMBER=<your_phone_number>
export TWILIO_ACCOUNT_SID=<your_sid>
export TWILIO_AUTH_TOKEN=<your_secret>

Now you are ready to build the server side code for this demo. Create a project directory and a file called supply-chain-server-1.py:

cd ~/
mkdir twitter-wordcloud-server && cd twitter-wordcloud-server
touch supply-chain-server-1.py

Edit the file in a text editor and add the following code:

#!/usr/bin/env python3
import base64
import os
import requests
import urllib.parse

from wordcloud import WordCloud
from imgurpython import ImgurClient
from geopy.geocoders import Nominatim
from flask import Flask, request

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

# ---------------------------------- Setup ---------------------------------- #

# Create application objects
app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)
bit_requests = BitTransferRequests(wallet)
geolocator = Nominatim()

# Create the Imgur API Client
imgur_client = ImgurClient(
    os.environ.get('IMGUR_CLIENT_ID'),
    os.environ.get('IMGUR_CLIENT_SECRET')
)

# Create credentials for Twitter Search API
concat = os.environ.get('TWITTER_CONSUMER_KEY') + ':' + os.environ.get('TWITTER_CONSUMER_SECRET')
encoded = base64.b64encode(concat.encode('ascii'))

# Obtain a Authorization Bearer token from Twitter
headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
           'Authorization': 'Basic ' + encoded.decode()}
response = requests.post('https://api.twitter.com/oauth2/token',
                         headers=headers, data='grant_type=client_credentials')
bearer = response.json().get('access_token')

# Define the API url and authentication headers for making calls later
twitter_url = 'https://api.twitter.com/1.1/search/tweets.json?'
twitter_headers = {'Authorization': 'Bearer ' + str(bearer)}

# Define the 402 API url for sending multimedia text messages
txt_msg_url = 'http://[::1]:9000/send-mms?'

# ------------------------------- API Routing ------------------------------- #


@app.route('/wordcloud')
@payment.required(3000)
def get_word_cloud():

    # Extract and process query parameters from GET request
    zipcode = request.args.get('zipcode')
    query = request.args.get('query')
    date = request.args.get('date')
    phone = request.args.get('phone')
    radius = request.args.get('radius') or '5mi'
    location = geolocator.geocode(zipcode)

    # Twitter search query parameters
    params = dict(
        query=query,
        count=100,
        until=date,  # Format: YYYY-MM-DD (e.g. 2015-11-14)
        result_type='popular',  # Choices: (mixed | recent | popular)
        geocode=','.join([str(location.latitude), str(location.longitude), radius])
    )

    # Make a GET request to the Twitter API with the search parameters
    response = requests.get(twitter_url+'q={}&count={}&geocode={}'.format(
        params['query'], params['count'], params['geocode']), headers=twitter_headers)
    search_results = response.json()

    # Combine the all the status text into one variable
    all_text = ''
    for status in search_results['statuses']:
        all_text = all_text + status['text'][0:status['text'].find('http')] + ' '

    # Generate and save the word cloud
    wordcloud = WordCloud(max_words=150).generate(all_text)
    image = wordcloud.to_image()
    image.save('wordcloud_img.png')

    # Upload using the Imgur API
    print('Uploading image...')
    config = dict(title='Bitcoin-powered artwork!')
    img_url = imgur_client.upload_from_path('wordcloud_img.png', config=config)
    print('Uploaded to: {}'.format(img_url['link']))

    # Text the image to the user using a 402 API
    print('Call next link in digital supply chain: send text message')
    bit_requests.get(txt_msg_url + 'media={0}&phone={1}'.format(urllib.parse.quote_plus(img_url['link']), phone))

    return 'Text message sent.\n'

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

Start the micropayments server:

python3 supply-chain-server-1.py

The first server in our digital supply chain is up and running. Let's set up another bitcoin-payable server to serve as the second link in the chain.

Step 2: Set up the second server in the digital supply chain

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

Then install the packages we'll need for the second server:

sudo pip3 install twilio
sudo pip3 install flask

Next, create a project folder in the home directory to house our app and create a file called supply-chain-server-2.py:

cd ~/
mkdir send-mms-server && cd send-mms-server
touch supply-chain-server-2.py

Now open this file in a text editor and type in the following code:

#!/usr/bin/env python3
import os
from twilio.rest import TwilioRestClient

from flask import Flask
from flask import request

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

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

# create the twilio rest client
client = TwilioRestClient(
    os.environ.get('TWILIO_ACCOUNT_SID'),
    os.environ.get('TWILIO_AUTH_TOKEN')
)


@app.route('/send-mms')
@payment.required(3000)
def send_mms():
    phone = request.args.get('phone')
    media_link = request.args.get('media')

    print('Sending text message...')
    client.messages.create(
        to='+'+str(phone),
        from_=os.environ.get('TWILIO_PHONE_NUMBER'),
        body='Bitcoin-powered artwork from 21.co!',
        media_url=media_link
    )

    return "OK"

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

Then start the micropayments server:

python3 supply-chain-server-2.py

Now that both links in our digital supply chain are up, let's create a client script to request the full service.

Step 3: Set up the client

Open up a third 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

We'll run a client script which prompts the user for some input and sends this to our first server, effectively piping it into the machine-payable digital supply chain.

Create a folder in your home directory to house the client project:

cd ~/
mkdir bitcoin-mashup-client && cd bitcoin-mashup-client

In this directory, create a file called client.py and open it in your editor. Add the following code to this file:

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

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

wallet = Wallet()
requests = BitTransferRequests(wallet)

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


def get_art():
    print("--Generate bitcoin-powered digital artwork--")

    sel_url = server_url+'wordcloud?query={0}&zipcode={1}&date={2}&phone={3}'

    query = input("Please enter search term(s): ")
    zipcode = input("Please enter a zipcode: ")
    date = input("Please enter a date (YYYY-MM-DD): ")
    phone = input("Please enter your cell number (1XXXXXXXXXX): ")

    # call the machine-payable endpoint
    requests.get(url=sel_url.format(urllib.parse.quote_plus(query), zipcode, date, phone))

    print('Congratulations, you just generated a unique piece of digital art for bitcoin!')
    print('It has been sent to you as an MMS, enjoy!')

if __name__ == '__main__':
    get_art()

Save and close this file. Then run the client side script to test our digital supply chain:

python3 client.py

Congratulations, you just purchased unique digital artwork with bitcoin and received it on your phone!

Next Steps

You've learned how to compose multiple bitcoin-payable APIs to form a digital supply chain. This is actually a concept of major importance for the Bitcoin price, whose significance we'll try to tease out here.

  • First, if you are a company like Dell, it doesn't actually help the Bitcoin USD/price in the long run if you accept bitcoin for a laptop. The reason is that Dell liquidates any bitcoin received for USD upon receipt. This means a sell order is still placed on an exchange - it is just delayed in time from the original purchase.

  • Why does Dell liquidate the bitcoin immediately? Because its suppliers only accept USD (or, more generally, fiat currencies). A massive global physical supply chain that already runs on dollars is not going to get entirely rewired to use bitcoin. The costs in infrastructure and volatility are large, and the benefits for physical vendors as of 2015 are not large enough to trigger a system-wide change.

  • However, when it comes to the digital realm, we have a completely different story. Now we have a data structure - the call graph which is conceptually similar to the supply chain.

  • Typically, however, most of the nodes in the call graph for a given program are executed locally, with each function using the same computational resources as the owner of the main function.

  • The few exceptions are calls to very high value remote paid APIs like Twilio and Stripe; these API calls need to be extremely high value because the fixed costs of getting API keys and setting up a new API are quite high.

  • However, with the advent of bitcoin-payable APIs, suddenly we can think about doing many more remote API calls, as the fixed costs of doing these calls will rapidly decline now that we have bitcoin as a common international digital currency.

  • These bitcoin-payable API calls in turn allow us to build up complex digital supply chains like the one seen in this example.

  • Over time, a large number of interconnected bitcoin-payable APIs help us build up closed loops: sectors of the bitcoin economy where bitcoin is constantly recycled into new uses and never sold for fiat.

  • Systematically taking sell orders off exchanges in this way - by building the closed loops - is the long-term way to increase the bitcoin price and build the cloud economy.

This gives you a sense of the conceptual importance of this example. As a next step, you might think about other bitcoin-payable APIs you might connect together. Additionally, while we don't support Haskell right now, some of the concepts in Hoogle (a type signature-based search engine) could potentially be used in Python 3 using function annotations to help systematize the search for composable functions.

If you build anything related to this and want to earn some bitcoin for your efforts, write it up and submit it as a bitcoin tutorial. If we decide to publish it on our site, you'll win $200 in BTC!

You can also come to our Slack channel at slack.21.co to find other 21 users to post your endpoints and give feedback. We look forward to seeing you there!


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.