├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bitroute-server.py ├── bitroute.py ├── manifest.yaml └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Pull requests 2 | 3 | ## Code style 4 | 5 | > Programs must be written for people to read, and only incidentally for 6 | > machines to execute. 7 | > 8 | > -- Harold Abelson, Structure and Interpretation of Computer Programs 9 | 10 | At a minimum, changes to the code should be compliant with [PEP 11 | 8](https://www.python.org/dev/peps/pep-0008/). For general design principles, 12 | consult [The Zen of Python](https://www.python.org/dev/peps/pep-0020/). 13 | 14 | ## Commit message format 15 | 16 | Commit your changes using the following commit message format (with the same 17 | capitalization, spacing, and punctuation): 18 | 19 | ``` 20 | logging: Move function to module level 21 | ``` 22 | 23 | The first part of the message is the scope and the second part is a description 24 | of the change, written in the imperative tense. 25 | 26 | ## Commit history 27 | 28 | Commits should be organized into logical units. Keep your git history clean by 29 | [rewriting](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) it as 30 | necessary using `git rebase -i` (interactive rebase) and `git push -f` (force 31 | push). Commits addressing review comments and test failures should be squashed 32 | if necessary. 33 | 34 | # Opening issues 35 | 36 | ## Bug reports 37 | 38 | Bug reports should include clear instructions to reproduce the bug. Include a 39 | stack trace if applicable. 40 | 41 | ## Security issues 42 | 43 | Critical security bugs should be sent via email to security@21.co and should not 44 | be publicly disclosed. Eligible security disclosures are eligible, at our sole 45 | discretion, for a monetary bounty of up to $1000 based on severity. 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017, 21 Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of 21 Inc. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to run and publish _**Bitroute**_ 2 | 3 | ### Step 1 4 | Get the latest version of the 21 software. You should be at version `2.3.1`. 5 | 6 | ``` 7 | $ 21 update 8 | $ 21 --version 9 | 21 v2.3.1 10 | ``` 11 | 12 | ### Step 2 13 | Clone the Bitroute repository and make sure you have got traceroute installed. 14 | 15 | ``` 16 | $ git clone https://github.com/21dotco/bitroute.git 17 | $ cd bitroute 18 | $ sudo pip3 install -r requirements.txt 19 | $ sudo apt-get install traceroute 20 | ``` 21 | 22 | ### Step 3 23 | Join the `21market` Marketplace, and start a local 24 | server to accept Bitroute requests. The server will run in the 25 | background and process requests. You can optionally edit the file 26 | to change the default price, which is 1000 Satoshis per request. 27 | 28 | ``` 29 | $ 21 join 21market 30 | $ python3 bitroute-server.py & 31 | ``` 32 | 33 | ### Step 4 34 | Now use `21 publish` to submit the manifest file describing the 35 | Bitroute service you just started. You can pass in arguments to override the 36 | default values in the file. The name and email should be your own. The price 37 | field should match the price in the `@payment.required` decorator in `bitroute-server.py`. 38 | The host of `'AUTO'` is a special input that tells the publish command to use 39 | the IP of your bitcoin computer within the `21market` Marketplace (see 40 | [here](https://21.co/learn/21-marketplace/#the-21-network for details)). The 41 | port of 6003 is the default port specified within the 42 | `bitroute-server.py` code that you are running. 43 | 44 | ``` 45 | $ 21 publish submit manifest.yaml -p 'name="Joe Smith" email="joe@example.com" price="1000" host="AUTO" port="6003"' 46 | ``` 47 | 48 | ### Step 5 49 | After a brief wait of a second or so, you should be able to use `21 publish list` 50 | to see the endpoint you just put up: 51 | 52 | ``` 53 | $ 21 publish list 54 | ``` 55 | 56 | You can also search the 21 Marketplace for your app: 57 | 58 | ``` 59 | $ 21 search "Bitroute" 60 | ``` 61 | 62 | ### Step 6 63 | You can now use bitcoin to buy that endpoint from yourself to test it out: 64 | 65 | ``` 66 | $ 21 buy $HOST:$PORT/?uri=google.com 67 | ``` 68 | 69 | where `$HOST` is your Bitcoin Computer's IP address on the 21 Marketplace and 70 | `$PORT` is the port the web service is running on. 71 | 72 | ### Step 7 73 | And you can see a receipt for this transaction: 74 | 75 | ``` 76 | $ 21 log 77 | ``` 78 | -------------------------------------------------------------------------------- /bitroute-server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | import logging 6 | import yaml 7 | 8 | from flask import Flask 9 | from flask import request 10 | 11 | from two1.wallet.two1_wallet import Wallet 12 | from two1.bitserv.flask import Payment 13 | 14 | from bitroute import bitroute 15 | 16 | app = Flask(__name__) 17 | 18 | # setup wallet 19 | wallet = Wallet() 20 | payment = Payment(app, wallet) 21 | 22 | # hide logging 23 | log = logging.getLogger('werkzeug') 24 | log.setLevel(logging.ERROR) 25 | 26 | 27 | @app.route('/manifest') 28 | def manifest(): 29 | """Provide the app manifest to the 21 crawler. 30 | """ 31 | with open('./manifest.yaml', 'r') as f: 32 | manifest = yaml.load(f) 33 | return json.dumps(manifest) 34 | 35 | 36 | @app.route('/') 37 | @payment.required(1000) 38 | def traceroute(): 39 | """ Runs traceroute on the provided url 40 | 41 | Returns: HTTPResponse 200 with a json containing the traceroute info. 42 | HTTP Response 400 if no uri is specified or the uri is malformed/cannot be tracerouted. 43 | """ 44 | try: 45 | uri = request.args['uri'] 46 | except KeyError: 47 | return 'HTTP Status 400: URI query parameter is missing from your request.', 400 48 | 49 | try: 50 | data = bitroute(uri) 51 | response = json.dumps(data, indent=4, sort_keys=True) 52 | return response 53 | except ValueError as e: 54 | return 'HTTP Status 400: {}'.format(e.args[0]), 400 55 | 56 | 57 | if __name__ == '__main__': 58 | print("Server running...") 59 | app.run(host='::', port=6003) 60 | -------------------------------------------------------------------------------- /bitroute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | import requests 6 | import shutil 7 | import subprocess 8 | import sys 9 | 10 | __all__ = ["bitroute"] 11 | 12 | 13 | def is_compatible(): 14 | """ Checks whether the current machine is capable of running bitroute 15 | 16 | Returns: 17 | bool: True if the machine is compatible false if not. 18 | 19 | """ 20 | # check for Windows 21 | if hasattr(sys, 'getwindowsversion'): 22 | print('error: Windows is currently not supported.') 23 | return False 24 | 25 | # check for command presence 26 | if not shutil.which('traceroute'): 27 | print('error: Missing `traceroute` binary.') 28 | return False 29 | 30 | return True 31 | 32 | 33 | def get_server_info(): 34 | """Gets network metadata for the machine calling the function. 35 | 36 | see http://ipinfo.io for more info. 37 | Returns: 38 | dict: A dictionary with keys ip, hostname, city, region, country, loc, org, postal 39 | 40 | """ 41 | uri = 'http://ipinfo.io' 42 | raw = requests.get(uri) 43 | data = raw.json() 44 | return data 45 | 46 | 47 | def bitroute(url): 48 | """ runs traceroute against the url. 49 | 50 | Args: 51 | url (str): A url to run traceroute against. 52 | 53 | Raises: 54 | ValueError: if the url is malformed or traceroute cannot be performed on it. 55 | Returns: 56 | dict: A dictionary containing traceroute information. 57 | 58 | """ 59 | if not is_compatible(): 60 | return 61 | 62 | uri = url.replace('https://', '').replace('http://', '') 63 | try: 64 | out = subprocess.check_output(['traceroute', str(uri)]).decode('unicode_escape') 65 | except subprocess.CalledProcessError: 66 | raise ValueError("traceroute cannot be performed on url={}".format(uri)) 67 | res = [line for line in out.split('\n') if line != ''] 68 | info = { 69 | 'traceroute': res, 70 | 'server': get_server_info() 71 | } 72 | return info 73 | 74 | 75 | if __name__ == '__main__': 76 | url = sys.argv[1] 77 | data = bitroute(url) 78 | formatted_data = json.dumps(data, indent=4, sort_keys=True) 79 | print(formatted_data) 80 | -------------------------------------------------------------------------------- /manifest.yaml: -------------------------------------------------------------------------------- 1 | basePath: / 2 | definitions: 3 | Server: 4 | properties: 5 | city: {type: string} 6 | provider: {type: string} 7 | public_ip: {type: string} 8 | state: {type: string} 9 | zip_code: {type: string} 10 | type: object 11 | host: 10.244.108.173:6003 12 | info: 13 | contact: {email: nakamoto@nakamoto.com, name: Satoshi Nakamoto} 14 | description: Run a traceroute on demand for bitcoin. 15 | title: Bitroute 16 | x-21-category: utilities 17 | x-21-github-project-url: https://github.com/21dotco/bitroute 18 | x-21-keywords: [traceroute, network] 19 | x-21-quick-buy: "$ 21 buy url http://10.244.108.172:6003/?uri=google.com\n\n# Output:\n\ 20 | # { \n# \"server\":{ \n# \"city\":\"San Francisco\",\n# \ 21 | \ \"country\":\"US\",\n# \"hostname\":\"10-21-28-12.static.wiline.com\"\ 22 | ,\n# \"ip\":\"10.21.28.12\",\n# \"loc\":\"37.7749,-122.4194\"\ 23 | ,\n# \"org\":\"AS33544 WiLine Networks Inc.\",\n# \"postal\":\"\ 24 | 94158\",\n# \"region\":\"California\"\n# },\n# \"traceroute\"\ 25 | :[ \n# \"traceroute to google.com (172.217.1.46), 30 hops max, 60 byte\ 26 | \ packets\",\n# \" 1 192.168.128.1 (192.168.128.1) 0.496 ms 0.343 ms\ 27 | \ 0.235 ms\",\n# \" 2 10-21-28-12.static.wiline.com (10.21.28.12) 11.196\ 28 | \ ms 11.562 ms 11.692 ms\",\n# \" 3 10-21-28-12.static.wiline.com (10.21.28.12)\ 29 | \ 11.272 ms 11.386 ms 11.678 ms\",\n# \" 4 10.20.91.17 (10.20.91.17)\ 30 | \ 16.580 ms 17.269 ms 18.038 ms\",\n# \" 5 10.20.91.30 (10.20.91.30)\ 31 | \ 11.177 ms 11.732 ms 11.821 ms\",\n# \" 6 sfosf0077ra90101-v20.wiline.com\ 32 | \ (216.75.229.1) 11.842 ms 11.820 ms 12.240 ms\",\n# \" 7 216-75-229-9.static.wiline.com\ 33 | \ (216.75.229.9) 11.430 ms 11.637 ms 11.331 ms\",\n# \" 8 10.20.50.114\ 34 | \ (10.20.50.114) 11.740 ms 11.844 ms 11.997 ms\",\n# \" 9 10.20.50.109\ 35 | \ (10.20.50.109) 11.525 ms 11.666 ms 11.645 ms\",\n# \"10 10.21.42.22\ 36 | \ (10.21.42.22) 12.361 ms 12.723 ms 13.128 ms\",\n# \"11 10.20.186.237\ 37 | \ (10.20.186.237) 12.070 ms 12.054 ms 12.531 ms\",\n# \"12 xe-3-2-1.mpr3.sfo7.us.above.net\ 38 | \ (208.184.37.89) 11.629 ms 11.537 ms 12.243 ms\",\n# \"13 ae1.mpr4.sfo7.us.zip.zayo.com\ 39 | \ (64.125.31.30) 12.290 ms 31.622 ms 30.234 ms\",\n# \"14 ae5.cr2.sjc2.us.zip.zayo.com\ 40 | \ (64.125.26.21) 13.252 ms 13.161 ms 13.071 ms\",\n# \"15 ae16.mpr4.sjc7.us.zip.zayo.com\ 41 | \ (64.125.31.15) 12.980 ms 13.435 ms 13.334 ms\",\n# \"16 64.125.13.111\ 42 | \ (64.125.13.111) 13.937 ms 14.163 ms 13.993 ms\",\n# \"17 216.239.49.168\ 43 | \ (216.239.49.168) 14.769 ms 14.714 ms 14.767 ms\",\n# \"18 209.85.246.38\ 44 | \ (209.85.246.38) 15.247 ms 209.85.249.63 (209.85.249.63) 15.271 ms 209.85.246.20\ 45 | \ (209.85.246.20) 15.000 ms\",\n# \"19 64.233.174.206 (64.233.174.206)\ 46 | \ 22.349 ms 22.253 ms 64.233.174.204 (64.233.174.204) 22.155 ms\",\n# \ 47 | \ \"20 209.85.248.124 (209.85.248.124) 21.701 ms 22.157 ms 22.066 ms\"\ 48 | ,\n# \"21 209.85.252.149 (209.85.252.149) 22.482 ms 22.397 ms 22.727\ 49 | \ ms\",\n# \"22 lax17s01-in-f14.1e100.net (172.217.1.46) 21.690 ms \ 50 | \ 22.110 ms 22.015 ms\"\n# ]\n# }" 51 | x-21-total-price: {max: 1000, min: 1000} 52 | paths: 53 | /: 54 | get: 55 | consumes: [application/x-www-form-urlencoded] 56 | produces: [application/json] 57 | responses: 58 | 200: 59 | description: Traceroute statistics and information on server location. 60 | schema: 61 | properties: 62 | ping: 63 | items: {type: string} 64 | type: array 65 | server: {$ref: '#/definitions/Server'} 66 | type: object 67 | summary: Return traceroute statistics between this device and a given domain 68 | or IP. 69 | schemes: [http] 70 | swagger: '2.0' 71 | x-21-manifest-path: /manifest -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | PyYAML==3.11 3 | requests==2.7.0 4 | --------------------------------------------------------------------------------