Ronak Mehta
In this tutorial we'll discuss the script that you went through in the [Introduction to the 21 Bitcoin Computer][18] tutorial in more detail, to show 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.
You will need the following items:
- 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][19] process. - Do the [Introduction to the 21 Bitcoin Computer][18] tutorial
- Do the [Working with Multiple 21 Users][20] tutorial
If you've got all the prerequisites, you are ready to go. Let's get started!
Follow the instructions in the
[Working With Multiple 21 Users][20] tutorial to set
up the twenty-client
, twenty-server
, and twenty-server-2
users
and install Flask.
Log in as the twenty-server
user and 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:
[ 1][21]
[ 2][22]
[ 3][23]
[ 4][24]
[ 5][25]
[ 6][26]
[ 7][27]
[ 8][28]
[ 9][29]
[10][30]
[11][31]
[12][32]
[13][33]
[14][34]
[15][35]
[16][36]
[17][37]
[18][38]
[19][39]
[20][40]
[21][41]
[22][42]
[23][43]
[24][44]
[25][45]
[26][46]
[27][47]
[28][48]
[29][49]
[30][50]
[31][51]
[32][52]
[33][53]
[34][54]
[35][55]
[36][56]
[37][57]
[38][58]
[39][59]
[40][60]
[41][61]
[42][62]
[43][63]
[44][64]
[45][65]
[46][66]
[47][67]
[48][68]
[49][69]
[50][70]
[51][71]
[52][72]
[53][73]
[54][74]
#!/usr/bin/env python3
import os
import json
import random
from flask import Flask
from flask import request
from flask import send_from_directory
# import from the 21 Developer Library
from two1.lib.wallet import Wallet
from two1.lib.bitserv.flask import Payment
app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)
# directory of the digital content we'd like to sell
dir_path = '/home/twenty-server/sellfiles'
# get a list of the files in the directory
file_list = 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(1000,3000)
# 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='0.0.0.0')
Before you start the server, we will need to create the dir_path
and
load it up with some files to sell. Execute the following at the
command line:
[1][75]
[2][76]
[3][77]
[4][78]
[5][79]
[6][80]
cd $HOME
mkdir sellfiles
cd sellfiles
wget https://bitcoin.org/bitcoin.pdf
wget https://en.bitcoin.it/w/images/en/2/29/BC_Logo_.png
cd $HOME
If you are connected to the 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:
scp file.txt [[email protected]][81]:~/sellfiles/
The above command copies file.txt
from your PC or Mac to the
location /home/twenty-server/sellfiles/
on the Bitcoin
Computer. Once you have some content in there, go ahead and start the
server with:
python3 fileserver/server.py
Open a new terminal and SSH into your Bitcoin Computer as twenty-client
.
This will be the user identity for the client who will buy the file
that the twenty-server
user 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:
[ 1][82]
[ 2][83]
[ 3][84]
[ 4][85]
[ 5][86]
[ 6][87]
[ 7][88]
[ 8][89]
[ 9][90]
[10][91]
[11][92]
[12][93]
[13][94]
[14][95]
[15][96]
[16][97]
[17][98]
[18][99]
[19][100]
[20][101]
[21][102]
[22][103]
[23][104]
[24][105]
[25][106]
[26][107]
[27][108]
[28][109]
[29][110]
[30][111]
[31][112]
[32][113]
[33][114]
[34][115]
[35][116]
[36][117]
[37][118]
[38][119]
[39][120]
[40][121]
[41][122]
[42][123]
[43][124]
[44][125]
[45][126]
[46][127]
[47][128]
[48][129]
[49][130]
[50][131]
[51][132]
[52][133]
[53][134]
[54][135]
[55][136]
[56][137]
[57][138]
[58][139]
[59][140]
#!/usr/bin/env python3
import json
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
# set up bitrequest client for BitTransfer requests
wallet = Wallet()
username = Config().username
requests = BitTransferRequests(wallet, username)
# server address
server_url = 'http://localhost: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-payable 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 balance.")
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 twenty-client
balance decrease and the
twenty-server
balance increase. You can confirm this by running 21 log
as the twenty-client
and the twenty-server
user
respectively. And as the twenty-server
user, you can flush your
winnings to the blockchain by doing 21 flush
.
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:
- [Project Gutenberg: free ebooks][141] (pdfs)
- [Creative Commons music][142] (mp3s)
- [Creative Commons videos][143] (mp4s)
- [Free icons][144] (pngs)
- [BSD-licensed code][145] (software)
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][146], 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][147]
and [this][148]
article; we'll be integrating these instructions into the application
code in the near future.
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][149] 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.
[][15][][16][__][150]
Authors
Ronak Mehta
If you have any questions or issues, please drop us a line at [[email protected] ][151] or join our Slack community.
This content is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][152].
- [____][153]
- [____][154]
(c) 2016, 21.co
[15]: 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/sell-or-license-any-file-for-bitcoin/ [16]: https://www.facebook.com/sharer/sharer.php?u=https%3A//21.co/learn/sell-or-license-any-file-for-bitcoin/ [17]: /cdn-cgi/l/email-protection#102f366365727a7573642d44787963307c7f7f7b63307c797b75307130777f7f74306275637f6562737530767f62307c7571627e797e7730787f6730647f30726579Xld Bitcoin apps&body=Here's the URL: https%3A//21.co/learn/sell-or-license-any-file-for-bitcoin/ [18]: ../introduction-to-the-21-bitcoin-computer/ [19]: /setup [20]: ../21-multiple-users [21]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-1 [22]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-2 [23]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-3 [24]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-4 [25]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-5 [26]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-6 [27]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-7 [28]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-8 [29]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-9 [30]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-10 [31]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-11 [32]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-12 [33]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-13 [34]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-14 [35]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-15 [36]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-16 [37]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-17 [38]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-18 [39]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-19 [40]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-20 [41]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-21 [42]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-22 [43]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-23 [44]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-24 [45]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-25 [46]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-26 [47]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-27 [48]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-28 [49]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-29 [50]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-30 [51]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-31 [52]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-32 [53]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-33 [54]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-34 [55]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-35 [56]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-36 [57]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-37 [58]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-38 [59]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-39 [60]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-40 [61]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-41 [62]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-42 [63]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-43 [64]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-44 [65]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-45 [66]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-46 [67]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-47 [68]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-48 [69]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-49 [70]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-50 [71]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-51 [72]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-52 [73]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-53 [74]: #code-ba9c5d616c5521249a50c536fddca96cdf6aff76-54 [75]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-1 [76]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-2 [77]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-3 [78]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-4 [79]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-5 [80]: #code-1c4fe80353cd81a398bbb8039e67c57895f18021-6 [81]: /cdn-cgi/l/email-protection [82]: #code-d3b8a40c20692de73193312072e8d597700b58be-1 [83]: #code-d3b8a40c20692de73193312072e8d597700b58be-2 [84]: #code-d3b8a40c20692de73193312072e8d597700b58be-3 [85]: #code-d3b8a40c20692de73193312072e8d597700b58be-4 [86]: #code-d3b8a40c20692de73193312072e8d597700b58be-5 [87]: #code-d3b8a40c20692de73193312072e8d597700b58be-6 [88]: #code-d3b8a40c20692de73193312072e8d597700b58be-7 [89]: #code-d3b8a40c20692de73193312072e8d597700b58be-8 [90]: #code-d3b8a40c20692de73193312072e8d597700b58be-9 [91]: #code-d3b8a40c20692de73193312072e8d597700b58be-10 [92]: #code-d3b8a40c20692de73193312072e8d597700b58be-11 [93]: #code-d3b8a40c20692de73193312072e8d597700b58be-12 [94]: #code-d3b8a40c20692de73193312072e8d597700b58be-13 [95]: #code-d3b8a40c20692de73193312072e8d597700b58be-14 [96]: #code-d3b8a40c20692de73193312072e8d597700b58be-15 [97]: #code-d3b8a40c20692de73193312072e8d597700b58be-16 [98]: #code-d3b8a40c20692de73193312072e8d597700b58be-17 [99]: #code-d3b8a40c20692de73193312072e8d597700b58be-18 [100]: #code-d3b8a40c20692de73193312072e8d597700b58be-19 [101]: #code-d3b8a40c20692de73193312072e8d597700b58be-20 [102]: #code-d3b8a40c20692de73193312072e8d597700b58be-21 [103]: #code-d3b8a40c20692de73193312072e8d597700b58be-22 [104]: #code-d3b8a40c20692de73193312072e8d597700b58be-23 [105]: #code-d3b8a40c20692de73193312072e8d597700b58be-24 [106]: #code-d3b8a40c20692de73193312072e8d597700b58be-25 [107]: #code-d3b8a40c20692de73193312072e8d597700b58be-26 [108]: #code-d3b8a40c20692de73193312072e8d597700b58be-27 [109]: #code-d3b8a40c20692de73193312072e8d597700b58be-28 [110]: #code-d3b8a40c20692de73193312072e8d597700b58be-29 [111]: #code-d3b8a40c20692de73193312072e8d597700b58be-30 [112]: #code-d3b8a40c20692de73193312072e8d597700b58be-31 [113]: #code-d3b8a40c20692de73193312072e8d597700b58be-32 [114]: #code-d3b8a40c20692de73193312072e8d597700b58be-33 [115]: #code-d3b8a40c20692de73193312072e8d597700b58be-34 [116]: #code-d3b8a40c20692de73193312072e8d597700b58be-35 [117]: #code-d3b8a40c20692de73193312072e8d597700b58be-36 [118]: #code-d3b8a40c20692de73193312072e8d597700b58be-37 [119]: #code-d3b8a40c20692de73193312072e8d597700b58be-38 [120]: #code-d3b8a40c20692de73193312072e8d597700b58be-39 [121]: #code-d3b8a40c20692de73193312072e8d597700b58be-40 [122]: #code-d3b8a40c20692de73193312072e8d597700b58be-41 [123]: #code-d3b8a40c20692de73193312072e8d597700b58be-42 [124]: #code-d3b8a40c20692de73193312072e8d597700b58be-43 [125]: #code-d3b8a40c20692de73193312072e8d597700b58be-44 [126]: #code-d3b8a40c20692de73193312072e8d597700b58be-45 [127]: #code-d3b8a40c20692de73193312072e8d597700b58be-46 [128]: #code-d3b8a40c20692de73193312072e8d597700b58be-47 [129]: #code-d3b8a40c20692de73193312072e8d597700b58be-48 [130]: #code-d3b8a40c20692de73193312072e8d597700b58be-49 [131]: #code-d3b8a40c20692de73193312072e8d597700b58be-50 [132]: #code-d3b8a40c20692de73193312072e8d597700b58be-51 [133]: #code-d3b8a40c20692de73193312072e8d597700b58be-52 [134]: #code-d3b8a40c20692de73193312072e8d597700b58be-53 [135]: #code-d3b8a40c20692de73193312072e8d597700b58be-54 [136]: #code-d3b8a40c20692de73193312072e8d597700b58be-55 [137]: #code-d3b8a40c20692de73193312072e8d597700b58be-56 [138]: #code-d3b8a40c20692de73193312072e8d597700b58be-57 [139]: #code-d3b8a40c20692de73193312072e8d597700b58be-58 [140]: #code-d3b8a40c20692de73193312072e8d597700b58be-59 [141]: https://www.gutenberg.org/ [142]: http://freemusicarchive.org/curator/Creative_Commons/ [143]: https://soosck.wordpress.com/2010/11/04/20-open-source-movies-edit-redistribute-free/ [144]: https://www.iconfinder.com/free_icons [145]: https://www.google.com/?gws_rd=ssl#q=site:github.com+%22BSD+license%22 [146]: http://www.fastcompany.com/3032732/most-creative-people/how-brands-are-using-your-best-instagram-shots-for-more-authentic-marketing [147]: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xvii-deployment-on-linux-even-on-the-raspberry-pi [148]: http://tutos.readthedocs.org/en/latest/source/ndg.html [149]: https://slack.21.co/ [150]: /cdn-cgi/l/email-protection#6e51481d1b0c040b0d1a533a06071d4e020101051d4e0207050b4e0f4e0901010a4e1c0b1d011b1c0d0b4e08011c4e020b0f1c000700094e0601194e1a014e0c1b07Xld Bitcoin apps&body=Here's the URL: https%3A//21.co/learn/sell-or-license-any-file-for-bitcoin/ [151]: /cdn-cgi/l/email-protection#3b484e4b4b54494f7b090a155854 [152]: https://creativecommons.org/licenses/by-sa/4.0/ [153]: //twitter.com/21 [154]: //medium.com/@21