John Granata
In this tutorial, you will compose two bitcoin-payable APIs set up by different users 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 under a different user 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 users.
You will need the following first:
- A 21 Bitcoin Computer, with built-in mining and software
- The latest version of the 21 software, obtained by running
21 update
as described in the [setup][22] process. - Do the [Introduction to the 21 Bitcoin Computer][23] tutorial
- Do the [Working with Multiple 21 Users][24] tutorial
- Do the [Bitcoin-Payable API][25] tutorial
- Do the [Bitcoin SMS][26] tutorial
If you've done all that, you are ready to go. Let's get started!
Follow the instructions in the
[Working With Multiple 21 Users][24] tutorial to set
up the twenty-client
, twenty-server
, and twenty-server-2
users
and install Flask.
Log into the 21 Bitcoin Computer as the twenty-server
user.
Install the packages we'll need for the demo:
[1][27]
[2][28]
[3][29]
[4][30]
[5][31]
[6][32]
[7][33]
[8][34]
sudo apt-get -y install libjpeg-dev
sudo apt-get -y install libfreetype6-dev
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.
Go to [Imgur][35] 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, take note of your client ID and client secret.
Go to [Twitter][36] 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, take note of your consumer key and consumer secret.
Go to [Twilio][37] 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. Note the number you purchase along with the Sid
and 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
:
[1][38]
[2][39]
[3][40]
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:
[ 1][41]
[ 2][42]
[ 3][43]
[ 4][44]
[ 5][45]
[ 6][46]
[ 7][47]
[ 8][48]
[ 9][49]
[10][50]
[11][51]
[12][52]
[13][53]
[14][54]
[15][55]
[16][56]
[17][57]
[18][58]
[19][59]
[20][60]
[21][61]
[22][62]
[23][63]
[24][64]
[25][65]
[26][66]
[27][67]
[28][68]
[29][69]
[30][70]
[31][71]
[32][72]
[33][73]
[34][74]
[35][75]
[36][76]
[37][77]
[38][78]
[39][79]
[40][80]
[41][81]
[42][82]
[43][83]
[44][84]
[45][85]
[46][86]
[47][87]
[48][88]
[49][89]
[50][90]
[51][91]
[52][92]
[53][93]
[54][94]
[55][95]
[56][96]
[57][97]
[58][98]
[59][99]
[60][100]
[61][101]
[62][102]
[63][103]
[64][104]
[65][105]
[66][106]
[67][107]
[68][108]
[69][109]
[70][110]
[71][111]
[72][112]
[73][113]
[74][114]
[75][115]
[76][116]
[77][117]
[78][118]
[79][119]
[80][120]
[81][121]
[82][122]
[83][123]
[84][124]
[85][125]
[86][126]
[87][127]
[88][128]
[89][129]
[90][130]
[91][131]
[92][132]
[93][133]
[94][134]
[95][135]
[96][136]
[97][137]
import base64
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.lib.wallet import Wallet
from two1.commands.config import Config
from two1.lib.bitserv.flask import Payment
from two1.lib.bitrequests import BitTransferRequests
# ---------------------------------- Setup ---------------------------------- #
# Create application objects
app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)
bit_requests = BitTransferRequests(wallet, Config().username)
geolocator = Nominatim()
# Create the Imgur API Client
imgur_client = ImgurClient('<Your Imgur Client ID>', '<Your Imgur Client Secret>')
# Create credentials for Twitter Search API
concat = '<Twitter Consumer Key>' + ':' + '<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-payable API url for sending multimedia text messages
txt_msg_url = 'http://localhost:9000/send-mms?'
# ------------------------------- API Routing ------------------------------- #
@app.route('/wordcloud')
@payment.required(1000)
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-payable 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='0.0.0.0')
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.
Open a new terminal and log into the Bitcoin Computer as the
twenty-server-2
user. Then install the packages we'll need for the
second server:
[1][138]
[2][139]
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
:
[1][140]
[2][141]
[3][142]
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:
[ 1][143]
[ 2][144]
[ 3][145]
[ 4][146]
[ 5][147]
[ 6][148]
[ 7][149]
[ 8][150]
[ 9][151]
[10][152]
[11][153]
[12][154]
[13][155]
[14][156]
[15][157]
[16][158]
[17][159]
[18][160]
[19][161]
[20][162]
[21][163]
[22][164]
[23][165]
[24][166]
[25][167]
[26][168]
[27][169]
[28][170]
[29][171]
[30][172]
[31][173]
[32][174]
[33][175]
[34][176]
[35][177]
from twilio.rest import TwilioRestClient
from flask import Flask
from flask import request
from two1.lib.wallet import Wallet
from two1.lib.bitserv.flask import Payment
app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)
# create the twilio rest client
client = TwilioRestClient(
'<Your Twilio Account SID>', '<Your Twilio Auth Token>'
)
@app.route('/send-mms')
@payment.required(1000)
def send_mms():
phone = request.args.get('phone')
media_link = request.args.get('media')
print('Sending text message...')
resp = client.messages.create(to='+'+str(phone),
from_='<Your Twilio Account Phone Number>',
body='Bitcoin-powered artwork from 21.co!',
media_url=media_link
)
return "OK"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=9000)
Replace <Your Twilio Account Phone Number>
with your Twilio phone number, replace
<Your Twilio Account SID>
with your Twilio account SID, and replace
<Your Twilio Auth Token>
with your Twilio auth token.
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.
Finally, log into the 21 Bitcoin Computer as the twenty-client
user.
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:
[1][178]
[2][179]
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:
[ 1][180]
[ 2][181]
[ 3][182]
[ 4][183]
[ 5][184]
[ 6][185]
[ 7][186]
[ 8][187]
[ 9][188]
[10][189]
[11][190]
[12][191]
[13][192]
[14][193]
[15][194]
[16][195]
[17][196]
[18][197]
[19][198]
[20][199]
[21][200]
[22][201]
[23][202]
[24][203]
[25][204]
[26][205]
[27][206]
[28][207]
[29][208]
[30][209]
[31][210]
[32][211]
[33][212]
[34][213]
[35][214]
import json
import urllib.parse
import os
# import from the 21 Developer Library
from two1.commands.config import Config
from two1.lib.wallet import Wallet
from two1.lib.bitrequests import BitTransferRequests
wallet = Wallet()
username = Config().username
requests = BitTransferRequests(wallet, username)
# merchant server address
server_url = 'http://localhost: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
response = 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!
You've learned how to compose multiple bitcoin-payable APIs from different users 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][215] 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][216] (a type signature-based search engine) could potentially be used in Python 3 using [function annotations][217] 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][218]. 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 folks with 21 Bitcoin Computers to post your endpoints and give feedback. We look forward to seeing you there!
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 $BTC_AMOUNT --use-unconfirmed
. Make sure
to use the amount in BTC, not in Satoshi; the
--use-unconfirmed
flag ensures that you can send even if you
have unconfirmed transactions in your wallet.
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][219] at slack.21.co to join the bitcoin machine-payable marketplace hosted on the 21 peer-to-peer network.
Check out all the Bitcoin Apps you can build with the 21 Bitcoin Computer.
[][19][][20][__][220]
Authors
John Granata
If you have any questions or issues, please drop us a line at [[email protected] ][221] or join our Slack community.
This content is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][222].
- [____][223]
- [____][224]
(c) 2016, 21.co
[19]: https://twitter.com/intent/tweet?text=Hmm. This looks like a good resource for learning how to build Bitcoin apps:&url=https%3A//21.co/learn/bitcoin-mashup/ [20]: https://www.facebook.com/sharer/sharer.php?u=https%3A//21.co/learn/bitcoin-mashup/ [21]: /cdn-cgi/l/email-protection#28170e5b5d4a424d4b5c157c40415b08444747435b084441434d0849084f47474c085a4d5b475d5a4b4d084e475a08444d495a4641464f0840475f085c47084a5d41Xld Bitcoin apps&body=Here's the URL: https%3A//21.co/learn/bitcoin-mashup/ [22]: /setup [23]: ../introduction-to-the-21-bitcoin-computer/ [24]: ../21-multiple-users [25]: ../bitcoin-payable-api [26]: ../bitcoin-sms-contact [27]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-1 [28]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-2 [29]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-3 [30]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-4 [31]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-5 [32]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-6 [33]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-7 [34]: #code-70646e9dc87560a4675a387ed09645dcd12e6fac-8 [35]: https://api.imgur.com [36]: https://dev.twitter.com/apps [37]: https://www.twilio.com/user/account/phone-numbers/dev-tools/api-keys [38]: #code-66a4db1944e78a47de749378267ab80e41342cac-1 [39]: #code-66a4db1944e78a47de749378267ab80e41342cac-2 [40]: #code-66a4db1944e78a47de749378267ab80e41342cac-3 [41]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-1 [42]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-2 [43]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-3 [44]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-4 [45]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-5 [46]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-6 [47]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-7 [48]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-8 [49]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-9 [50]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-10 [51]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-11 [52]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-12 [53]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-13 [54]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-14 [55]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-15 [56]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-16 [57]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-17 [58]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-18 [59]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-19 [60]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-20 [61]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-21 [62]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-22 [63]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-23 [64]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-24 [65]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-25 [66]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-26 [67]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-27 [68]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-28 [69]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-29 [70]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-30 [71]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-31 [72]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-32 [73]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-33 [74]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-34 [75]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-35 [76]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-36 [77]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-37 [78]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-38 [79]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-39 [80]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-40 [81]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-41 [82]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-42 [83]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-43 [84]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-44 [85]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-45 [86]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-46 [87]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-47 [88]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-48 [89]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-49 [90]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-50 [91]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-51 [92]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-52 [93]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-53 [94]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-54 [95]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-55 [96]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-56 [97]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-57 [98]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-58 [99]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-59 [100]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-60 [101]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-61 [102]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-62 [103]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-63 [104]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-64 [105]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-65 [106]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-66 [107]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-67 [108]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-68 [109]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-69 [110]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-70 [111]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-71 [112]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-72 [113]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-73 [114]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-74 [115]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-75 [116]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-76 [117]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-77 [118]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-78 [119]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-79 [120]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-80 [121]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-81 [122]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-82 [123]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-83 [124]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-84 [125]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-85 [126]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-86 [127]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-87 [128]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-88 [129]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-89 [130]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-90 [131]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-91 [132]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-92 [133]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-93 [134]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-94 [135]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-95 [136]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-96 [137]: #code-9dad1b72601261f529dd3adfa52a796f47c3c9c5-97 [138]: #code-14abf3d8f96acceb772da091de3f75aeb43b96aa-1 [139]: #code-14abf3d8f96acceb772da091de3f75aeb43b96aa-2 [140]: #code-608bbb32776ca86abdef29c9a4a93e7acd6f6552-1 [141]: #code-608bbb32776ca86abdef29c9a4a93e7acd6f6552-2 [142]: #code-608bbb32776ca86abdef29c9a4a93e7acd6f6552-3 [143]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-1 [144]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-2 [145]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-3 [146]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-4 [147]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-5 [148]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-6 [149]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-7 [150]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-8 [151]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-9 [152]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-10 [153]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-11 [154]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-12 [155]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-13 [156]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-14 [157]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-15 [158]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-16 [159]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-17 [160]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-18 [161]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-19 [162]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-20 [163]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-21 [164]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-22 [165]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-23 [166]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-24 [167]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-25 [168]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-26 [169]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-27 [170]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-28 [171]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-29 [172]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-30 [173]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-31 [174]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-32 [175]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-33 [176]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-34 [177]: #code-551b71cf6df6d9e0bc392f02bec0e008fc2a05e2-35 [178]: #code-a8bdd8839c06e4fd6e6cccf3f2aee46ec95a8363-1 [179]: #code-a8bdd8839c06e4fd6e6cccf3f2aee46ec95a8363-2 [180]: #code-539096d5e673816b921257d4ac49fa3d16366c26-1 [181]: #code-539096d5e673816b921257d4ac49fa3d16366c26-2 [182]: #code-539096d5e673816b921257d4ac49fa3d16366c26-3 [183]: #code-539096d5e673816b921257d4ac49fa3d16366c26-4 [184]: #code-539096d5e673816b921257d4ac49fa3d16366c26-5 [185]: #code-539096d5e673816b921257d4ac49fa3d16366c26-6 [186]: #code-539096d5e673816b921257d4ac49fa3d16366c26-7 [187]: #code-539096d5e673816b921257d4ac49fa3d16366c26-8 [188]: #code-539096d5e673816b921257d4ac49fa3d16366c26-9 [189]: #code-539096d5e673816b921257d4ac49fa3d16366c26-10 [190]: #code-539096d5e673816b921257d4ac49fa3d16366c26-11 [191]: #code-539096d5e673816b921257d4ac49fa3d16366c26-12 [192]: #code-539096d5e673816b921257d4ac49fa3d16366c26-13 [193]: #code-539096d5e673816b921257d4ac49fa3d16366c26-14 [194]: #code-539096d5e673816b921257d4ac49fa3d16366c26-15 [195]: #code-539096d5e673816b921257d4ac49fa3d16366c26-16 [196]: #code-539096d5e673816b921257d4ac49fa3d16366c26-17 [197]: #code-539096d5e673816b921257d4ac49fa3d16366c26-18 [198]: #code-539096d5e673816b921257d4ac49fa3d16366c26-19 [199]: #code-539096d5e673816b921257d4ac49fa3d16366c26-20 [200]: #code-539096d5e673816b921257d4ac49fa3d16366c26-21 [201]: #code-539096d5e673816b921257d4ac49fa3d16366c26-22 [202]: #code-539096d5e673816b921257d4ac49fa3d16366c26-23 [203]: #code-539096d5e673816b921257d4ac49fa3d16366c26-24 [204]: #code-539096d5e673816b921257d4ac49fa3d16366c26-25 [205]: #code-539096d5e673816b921257d4ac49fa3d16366c26-26 [206]: #code-539096d5e673816b921257d4ac49fa3d16366c26-27 [207]: #code-539096d5e673816b921257d4ac49fa3d16366c26-28 [208]: #code-539096d5e673816b921257d4ac49fa3d16366c26-29 [209]: #code-539096d5e673816b921257d4ac49fa3d16366c26-30 [210]: #code-539096d5e673816b921257d4ac49fa3d16366c26-31 [211]: #code-539096d5e673816b921257d4ac49fa3d16366c26-32 [212]: #code-539096d5e673816b921257d4ac49fa3d16366c26-33 [213]: #code-539096d5e673816b921257d4ac49fa3d16366c26-34 [214]: #code-539096d5e673816b921257d4ac49fa3d16366c26-35 [215]: https://en.wikipedia.org/wiki/Call_graph [216]: https://www.haskell.org/hoogle/ [217]: http://ceronman.com/2013/03/12/a-powerful-unused-feature-of-python-function-annotations/ [218]: /submit-bitcoin-tutorial [219]: https://slack.21.co/ [220]: /cdn-cgi/l/email-protection#d2edf4a1a7b0b8b7b1a6ef86babba1f2bebdbdb9a1f2bebbb9b7f2b3f2b5bdbdb6f2a0b7a1bda7a0b1b7f2b4bda0f2beb7b3a0bcbbbcb5f2babda5f2a6bdf2b0a7bbXld Bitcoin apps&body=Here's the URL: https%3A//21.co/learn/bitcoin-mashup/ [221]: /cdn-cgi/l/email-protection#12616762627d60665220233c717d [222]: https://creativecommons.org/licenses/by-sa/4.0/ [223]: //twitter.com/21 [224]: //medium.com/@21