├── db ├── tests │ └── __init__.py ├── __init__.py └── migrations │ ├── __init__.py │ ├── migration2.py │ └── migration1.py ├── market ├── tests │ ├── __init__.py │ ├── test_protocol.py │ ├── test_btcprice.py │ ├── test_listeners.py │ └── test_profile.py ├── __init__.py ├── profile.py ├── btcprice.py ├── listeners.py ├── transactions.py └── moderation.py ├── keys ├── __init__.py ├── setup.py ├── bip32utils.py ├── blockchainid.py ├── credentials.py ├── guidc.c ├── guid.py └── keychain.py ├── net ├── __init__.py ├── utils.py ├── sslcontext.py ├── heartbeat.py ├── dos.py ├── upnp.py ├── rpcudp.py └── wireprotocol.py ├── seed ├── __init__.py ├── peers.proto └── peers.py ├── dht ├── tests │ ├── __init__.py │ ├── test_utils.py │ ├── test_storage.py │ ├── test_routing.py │ ├── test_node.py │ └── utils.py ├── __init__.py ├── utils.py ├── node.py ├── storage.py ├── routing.py ├── crawling.py └── protocol.py ├── protos ├── __init__.py ├── message.proto ├── contract.proto ├── objects.proto ├── message.py └── countries.proto ├── test_requirements.txt ├── .travis.yml ├── requirements.txt ├── Makefile ├── .github └── ISSUE_TEMPLATE ├── scripts └── pycheck.sh ├── Dockerfile ├── api ├── __init__.py └── utils.py ├── .gitignore ├── bootstrap.py ├── LICENSE ├── ob.cfg ├── README.md ├── log.py ├── daemon.py ├── interfaces.py └── config.py /db/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /market/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /db/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | -------------------------------------------------------------------------------- /keys/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | -------------------------------------------------------------------------------- /net/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | -------------------------------------------------------------------------------- /seed/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | -------------------------------------------------------------------------------- /db/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | -------------------------------------------------------------------------------- /dht/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests live here. 3 | """ 4 | -------------------------------------------------------------------------------- /protos/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | """ 3 | Package for holding all of our protobuf classes 4 | """ 5 | -------------------------------------------------------------------------------- /seed/peers.proto: -------------------------------------------------------------------------------- 1 | message PeerSeeds { 2 | repeated bytes serializedNode = 1; 3 | required bytes signature = 2; 4 | } -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | bandit==0.10.1 2 | coverage==3.7.1 3 | mock==1.0.1 4 | nose==1.3.4 5 | pylint==1.4.4 6 | astroid==1.3.8 7 | python-coveralls==2.5.0 8 | -------------------------------------------------------------------------------- /market/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | """ 3 | Package for two way communication between nodes. 4 | Primarily used for buyer-vendor communication. 5 | """ 6 | -------------------------------------------------------------------------------- /net/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def looping_retry(func, *args): 5 | while True: 6 | try: 7 | return func(*args) 8 | except Exception: 9 | time.sleep(0.5) 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | 6 | install: 7 | - pip install --upgrade pip 8 | - pip install -r requirements.txt 9 | - pip install -r test_requirements.txt 10 | 11 | script: 12 | - make -s 13 | 14 | after_success: 15 | - coveralls 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | protobuf==3.0.0b2 2 | Twisted==16.1.0 3 | txrudp==0.5.1 4 | pystun==0.1.0 5 | bitcointools==1.1.44 6 | gnupg==2.0.2 7 | pynacl==1.0.1 8 | txrestapi==0.2 9 | txws==0.9.1 10 | python-libbitcoinclient==0.4.3 11 | python-bitcoinlib==0.5.0 12 | pyopenssl==0.15.1 13 | miniupnpc==1.9 14 | bleach==1.4.2 15 | service_identity 16 | -------------------------------------------------------------------------------- /keys/setup.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | 3 | # pylint: disable=E0611,F0401 4 | # No name 'core' in module 'distutils' 5 | # Unable to import 'distutils.core' 6 | from distutils.core import setup, Extension 7 | 8 | setup(name='guidc', version='0.0', 9 | ext_modules=[Extension('guidc', sources=['guidc.c'], libraries=['sodium'])]) 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SCRIPTS=./scripts 2 | TESTPATH=./dht/tests ./db/tests ./market/tests 3 | 4 | .PHONY: all unittest check 5 | 6 | all: check unittest 7 | 8 | unittest: 9 | nosetests -vs --with-doctest --with-coverage --cover-package=dht --cover-package=db --cover-package=market --cover-inclusive $(TESTPATH) 10 | 11 | check: pycheck 12 | 13 | pycheck: $(SCRIPTS)/pycheck.sh 14 | $(SCRIPTS)/pycheck.sh 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | Brief Description: 2 | 3 | Operating System (OS and version): 4 | OpenBazaar version: 5 | Hardware: 6 | 7 | Reproducible (Always / Almost Always / Sometimes / Rarely / Couldn't Reproduce): 8 | 9 | 10 | Steps to reproduce: 11 | 1. 12 | 2. 13 | 3. 14 | 15 | Observed Behavior: 16 | 17 | 18 | Expected Behavior: 19 | 20 | 21 | Additional info (links, images, etc go here): 22 | -------------------------------------------------------------------------------- /dht/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | An implementation of the kademlia protocol based on https://github.com/bmuller/kademlia 3 | and modified for use in OpenBazaar. 4 | 5 | Modifications include: 6 | Protobuf for serializing the wire protocol. 7 | The 'STORE' rpc call which appends data to stored dictionaries. Useful for keyword searches. 8 | A 'DELETE' rpc which allows data to be removed from the DHT when a valid signature is presented. 9 | RUDP in place of straight UDP. 10 | The node ID is generated using a proof of work and validated when a new message is received. 11 | """ 12 | -------------------------------------------------------------------------------- /keys/bip32utils.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | import bitcointools 3 | from binascii import unhexlify 4 | 5 | 6 | def derive_childkey(key, chaincode, prefix=bitcointools.MAINNET_PUBLIC): 7 | """ 8 | Given a 33 byte public key and 32 byte chaincode (both in hex) derive the first child key. 9 | """ 10 | 11 | master_key = bitcointools.bip32_serialize((prefix, 0, b'\x00'*4, 0, 12 | unhexlify(chaincode), unhexlify(key))) 13 | child_key = bitcointools.bip32_ckd(master_key, 0) 14 | return bitcointools.bip32_extract_key(child_key) 15 | 16 | 17 | -------------------------------------------------------------------------------- /scripts/pycheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if command -v pylint; then 4 | PYLINT=pylint 5 | elif command -v pylint2; then 6 | PYLINT=pylint2 7 | else 8 | echo 'ERROR: pylint not installed.' 9 | exit 1 10 | fi 11 | 12 | echo '.: Checking python source files...' 13 | errored=0; 14 | count=0; 15 | for file in $(find . -name "*.py" \ 16 | -not -path "./env/*" \ 17 | -not -path "./protos/*" \ 18 | -not -path "./interfaces.py" \ 19 | -not -path "./html/bower_components/*"); do 20 | if ! $PYLINT --rcfile .pylintrc $file; then 21 | errored=$((errored + 1)); 22 | fi 23 | count=$((count + 1)); 24 | done 25 | 26 | if (( errored > 0 )); then 27 | echo "FAIL: Detected $errored files with errors." 28 | exit 1 29 | fi 30 | echo "PASS: Successfully checked $count files." 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | 3 | MAINTAINER Joshua Sindy 4 | # Examples 5 | # docker build -t observer . 6 | # docker run --rm -it -e flags="--help" observer 7 | # docker run -d --name observer -e flags="--testnet" observer 8 | # docker logs observer 9 | 10 | RUN apt-get update 11 | RUN apt-get install -y python-dev python-pip build-essential git libffi-dev libssl-dev 12 | RUN pip install pyopenssl ndg-httpsclient pyasn1 13 | RUN pip install --upgrade pip virtualenv 14 | RUN pip install mock coverage nose pylint 15 | RUN git clone https://github.com/OpenBazaar/OpenBazaar-Server.git 16 | WORKDIR /OpenBazaar-Server/ 17 | RUN pip install -r requirements.txt && pip install -r test_requirements.txt 18 | RUN make 19 | RUN adduser --disabled-password --gecos \"\" openbazaar 20 | RUN chown -R openbazaar:openbazaar /OpenBazaar-Server 21 | 22 | USER openbazaar 23 | CMD python openbazaard.py start $flags 24 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | 3 | ALLOWED_TAGS = [ 4 | 'a', 5 | 'b', 6 | 'blockquote', 7 | 'em', 8 | 'hr', 9 | 'h2', 10 | 'h3', 11 | 'h4', 12 | 'h5', 13 | 'i', 14 | 'img', 15 | 'li', 16 | 'p', 17 | 'ol', 18 | 'nl', 19 | 'span', 20 | 'strike', 21 | 'strong', 22 | 'ul', 23 | ] 24 | ALLOWED_ATTRIBUTES = { 25 | 'a': ['href', 'title', 'alt', 'style'], 26 | 'img': ['src', 'width', 'height'], 27 | 'b': ['style'], 28 | 'blockquote': ['style'], 29 | 'em': ['style'], 30 | 'h2': ['style'], 31 | 'h3': ['style'], 32 | 'h4': ['style'], 33 | 'h5': ['style'], 34 | 'i': ['style'], 35 | 'li': ['style'], 36 | 'p': ['style'], 37 | 'ol': ['style'], 38 | 'nl': ['style'], 39 | 'span': ['style'], 40 | 'strike': ['style'], 41 | 'strong': ['style'] 42 | } 43 | 44 | ALLOWED_STYLES = ['color', 'background'] 45 | -------------------------------------------------------------------------------- /keys/blockchainid.py: -------------------------------------------------------------------------------- 1 | __author__ = 'chris' 2 | 3 | import json 4 | import urllib2 5 | from config import RESOLVER 6 | 7 | 8 | def resolve(blockchain_id): 9 | """ 10 | Given a blockchain id return the corresponding GUID. The resolver 11 | url can be set in the config file. The resolver is open source so 12 | you can run your own if you want the full decentralized experience. 13 | """ 14 | if blockchain_id[:1] == "@": 15 | blockchain_id = blockchain_id[1:] 16 | 17 | try: 18 | data = json.load(urllib2.urlopen(RESOLVER + 'v2/users/' + blockchain_id)) 19 | for account in data[blockchain_id]["profile"]["account"]: 20 | if str(account["service"]).lower() == "openbazaar": 21 | return account["identifier"] 22 | except Exception: 23 | return None 24 | 25 | 26 | def validate(blockchain_id, guid): 27 | """Does the blockchain_id resolve to the given guid""" 28 | return resolve(blockchain_id) == guid 29 | -------------------------------------------------------------------------------- /db/migrations/migration2.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from config import DATA_FOLDER 3 | 4 | 5 | def migrate(database_path): 6 | print "migrating to db version 2" 7 | conn = sqlite3.connect(database_path) 8 | conn.text_factory = str 9 | cursor = conn.cursor() 10 | 11 | # read hashmap from db 12 | cursor.execute('''SELECT * FROM hashmap''') 13 | mappings = cursor.fetchall() 14 | 15 | for mapping in mappings: 16 | if DATA_FOLDER not in mapping[1]: 17 | raise Exception("To complete migration 2 please run openbazaard at least once using the original " 18 | "data folder location before moving it to a different location.") 19 | path = mapping[1][len(DATA_FOLDER):] 20 | cursor.execute('''INSERT OR REPLACE INTO hashmap(hash, filepath) 21 | VALUES (?,?)''', (mapping[0], path)) 22 | 23 | # update version 24 | cursor.execute('''PRAGMA user_version = 2''') 25 | conn.commit() 26 | conn.close() 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | .idea/ 61 | _trial_temp/ 62 | -------------------------------------------------------------------------------- /bootstrap.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | 5 | 6 | def bootstrap(testnet): 7 | script_dir = os.path.dirname(os.path.abspath(__file__)) 8 | 9 | if sys.platform == "darwin": 10 | 11 | # OSX won't pass the PATH 12 | subprocess.call(['/usr/local/bin/pip', 'install', '-r', '%s/requirements.txt' % script_dir]) 13 | os.chdir(script_dir) 14 | if testnet: 15 | os.system('/usr/local/bin/python openbazaard.py start --testnet') 16 | else: 17 | os.system('/usr/local/bin/python openbazaard.py start') 18 | 19 | else: 20 | subprocess.call(['pip', 'install', '-r', '%s%srequirements.txt' % (script_dir, os.pathsep)]) 21 | os.chdir(script_dir) 22 | if testnet: 23 | os.system('python sopenbazaard.py start --testnet') 24 | else: 25 | os.system('python openbazaard.py start') 26 | 27 | 28 | if __name__ == '__main__': 29 | use_testnet = len(sys.argv) > 1 and sys.argv[1] == 'testnet' 30 | bootstrap(use_testnet) 31 | -------------------------------------------------------------------------------- /api/utils.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=W1402 2 | def smart_unicode(s, encoding='utf8'): 3 | """ Convert str to unicode. If s is unicode, return itself. 4 | >>> smart_unicode('') 5 | u'' 6 | >>> smart_unicode('abc') 7 | u'abc' 8 | >>> smart_unicode('\xe4\xbd\xa0\xe5\xa5\xbd') 9 | u'\u4f60\u597d' 10 | >>> smart_unicode(u'abc') 11 | u'abc' 12 | >>> smart_unicode(u'\u4f60\u597d') 13 | u'\u4f60\u597d' 14 | """ 15 | if isinstance(s, unicode): 16 | return s 17 | return s.decode(encoding) 18 | 19 | 20 | def smart_str(s, encoding='utf8'): 21 | """ Convert unicode to str. If s is str, return itself. 22 | >>> smart_str(u'') 23 | '' 24 | >>> smart_str(u'abc') 25 | 'abc' 26 | >>> smart_str(u'\u4f60\u597d') == '\xe4\xbd\xa0\xe5\xa5\xbd' 27 | True 28 | >>> smart_str('abc') 29 | 'abc' 30 | >>> smart_str('\xe4\xbd\xa0\xe5\xa5\xbd') == '\xe4\xbd\xa0\xe5\xa5\xbd' 31 | True 32 | """ 33 | if isinstance(s, str): 34 | return s 35 | return s.encode(encoding) 36 | -------------------------------------------------------------------------------- /keys/credentials.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import random 3 | from config import USERNAME, PASSWORD 4 | from hashlib import sha256 5 | 6 | 7 | def get_credentials(database): 8 | settings = database.settings 9 | creds = settings.get_credentials() 10 | if creds == (USERNAME, PASSWORD): 11 | return creds 12 | elif creds is not None and (USERNAME is None or PASSWORD is None): 13 | return creds 14 | elif creds is not None and USERNAME is not None and PASSWORD is not None: 15 | settings.set_credentials(USERNAME, PASSWORD) 16 | return (USERNAME, PASSWORD) 17 | elif creds is None and (USERNAME is None or PASSWORD is None): 18 | username = base64.b64encode(sha256(str(random.getrandbits(255))).digest())[:20] 19 | password = base64.b64encode(sha256(str(random.getrandbits(255))).digest())[:20] 20 | settings.set_credentials(username, password) 21 | return (username, password) 22 | elif creds is None and (USERNAME is not None and PASSWORD is not None): 23 | settings.set_credentials(USERNAME, PASSWORD) 24 | return (USERNAME, PASSWORD) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 OpenBazaar Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /ob.cfg: -------------------------------------------------------------------------------- 1 | [CONSTANTS] 2 | #DATA_FOLDER = /path/to/OpenBazaar/ 3 | 4 | KSIZE = 20 5 | ALPHA = 3 6 | 7 | TRANSACTION_FEE = 15000 8 | 9 | RESOLVER = https://resolver.onename.com/ 10 | 11 | [LIBBITCOIN_SERVERS] 12 | mainnet_server1 = tcp://libbitcoin1.openbazaar.org:9091 13 | mainnet_server3 = tcp://libbitcoin3.openbazaar.org:9091 14 | mainnet_server5 = tcp://obelisk.airbitz.co:9091 15 | 16 | [LIBBITCOIN_SERVERS_TESTNET] 17 | testnet_server2 = tcp://libbitcoin2.openbazaar.org:9091,baihZB[vT(dcVCwkhYLAzah{3@k?+>T&^3 18 | testnet_server4 = tcp://libbitcoin4.openbazaar.org:9091,