diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca33d7b --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +.PHONY: clean-pyc clean-build docs + +help: + @echo "clean-build - remove build artifacts" + @echo "clean-pyc - remove Python file artifacts" + @echo "release - package and upload a release" + @echo "sdist - package" + +clean: clean-build clean-pyc + +clean-build: + rm -fr build/ + rm -fr dist/ + rm -fr *.egg-info + +clean-pyc: + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + +release: clean + python setup.py sdist bdist bdist_wheel upload + +sdist: clean + python setup.py sdist bdist bdist_wheel + ls -l dist diff --git a/docker/docker_dapp b/docker/docker_dapp new file mode 160000 index 0000000..95cb0c5 --- /dev/null +++ b/docker/docker_dapp @@ -0,0 +1 @@ +Subproject commit 95cb0c516474bbc774991fc073f55b7656dcb78c diff --git a/gpg/jmunsch b/gpg/jmunsch new file mode 100644 index 0000000..5678f8c --- /dev/null +++ b/gpg/jmunsch @@ -0,0 +1,49 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.1.5 +Comment: Hostname: pgp.mit.edu + +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 +=2NUP +-----END PGP PUBLIC KEY BLOCK----- diff --git a/saffron/accounts.py b/saffron/accounts.py index 6ba70ec..6b12a69 100644 --- a/saffron/accounts.py +++ b/saffron/accounts.py @@ -46,10 +46,23 @@ class Account: _address (str): chain address. _name (str): name of token/chain. + Ethereum methods : + self.p.importRawKey + self.p.newAccount + self.p.listAccounts + self.p.getListAccounts + self.p.sendTransaction + self.p.signAndSendTransaction + self.p.lockAccount + self.p.unlockAccount + self.p.sign + self.p.ecRecover ''' def __init__(self, name=None, address=None, password=None, chain=None): '''initialize the class TODO : document chain () + TODO : salt passwords with bcrypt or better + Args: _address (str): chain address. _name (str): name of token/chain. @@ -66,13 +79,15 @@ def __init__(self, name=None, address=None, password=None, chain=None): self.name = _name self.address = _address self._new_account = False - + node_info = json.loads(open(os.environ['NODE_INFO_JSON']).read()) + self.web3 = Web3(Web3.HTTPProvider("http://127.0.0.1:{port}".format(port=node_info.get('rpcport')))) + self.p = Personal(self.web3) @classmethod def _from_db(self, name=None, address=None): return - #TODO - #fancy shit making interacting with the blockchain easy (get balance, transact, etc) def balance(self): - return Eth.get_balance(self.address) \ No newline at end of file + return Eth.get_balance(self.address) + + diff --git a/setup.py b/setup.py index a40d383..7c207a4 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup(name='saffron-cli', packages=find_packages(), - version = '0.1.10', + version = '0.1.11', description = 'blockchain artifact development tools', author = 'Lamden', author_email = 'james@lamden.io', diff --git a/tests/test_accounts.py b/tests/test_accounts.py index fd3b7b4..b8be2dc 100644 --- a/tests/test_accounts.py +++ b/tests/test_accounts.py @@ -1,4 +1,10 @@ import pytest, os +from http.server import BaseHTTPRequestHandler, HTTPServer +import json +import re +import socket +from threading import Thread + from saffron.accounts import Account from saffron.genesis import Chain from saffron.database import insert_account @@ -26,6 +32,45 @@ def read(self, *args, **kwargs): # monkeypatch.setattr( 'some.Example', Example) return Chain() + + +class MockServerRequestHandler(BaseHTTPRequestHandler): + def do_POST(self, *args, **kwargs): + content_len = int(self.headers.get('content-length')) + post_body = self.rfile.read(content_len) + p_body = json.loads(post_body) + # tests = [b'{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 0}', + # b'{"jsonrpc": "2.0", "method": "personal_newAccount", "params": ["doesnt_matter"], "id": 0}'] + try: + stub = json.dumps({"result": ''}) + tests = {"personal_listAccounts": stub, + "personal_newAccount": stub, + "personal_importRawKey": stub, + "personal_newAccount": stub, + "personal_listAccounts": stub, + "personal_sendTransaction": stub, + "personal_lockAccount": stub, + "personal_unlockAccount": stub, + "personal_sign": stub, + "personal_ecRecover": stub} + self.send_response(200) + self.send_header('Content-Type', 'application/json; charset=utf-8') + self.end_headers() + self.wfile.write(tests.get(p_body['method']).encode('utf-8')) + return + except Exception as e: + import traceback + print(traceback.format_exc(), p_body['method']) + +def start_mock_server(port): + mock_server = HTTPServer(('127.0.0.1', port), MockServerRequestHandler) + mock_server_thread = Thread(target=mock_server.serve_forever) + mock_server_thread.setDaemon(True) + mock_server_thread.start() + +start_mock_server(8545) +p = 'doesnt_matter' + def test_accounts(chain): new_account = str(uuid.uuid1()) a = Account(name=new_account, password='testing123', chain=chain) @@ -38,8 +83,63 @@ def test_accounts(chain): def test_account_stored_in_db_when_created(): new_account = str(uuid.uuid1()) - a = Account(name=new_account, password='doesnt_matter', chain=Chain()) - b = Account(name=new_account, password='doesnt_matter', chain=Chain()) + a = Account(name=new_account, password=p, chain=Chain()) + b = Account(name=new_account, password=p, chain=Chain()) assert a.name == b.name assert a._new_account # assert b._new_account is False + +# def test_importRawKey(chain): +# new_account = str(uuid.uuid1()) +# p = 'doesnt_matter' +# a = Account(name=new_account, password=p, chain=Chain()) +# private_key = 'asdf' +# a.p.importRawKey(private_key, p) + +def test_newAccount(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.newAccount(password=p) + +def test_listAccounts(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.listAccounts + +def test_getListAccounts(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + try: + a.p.getListAccounts() + except NotImplementedError: + pass + +def test_sendTransaction(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.sendTransaction({"from": "0xhere", "data": 'hello'}, p) + +def test_signAndSendTransaction(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.signAndSendTransaction({"from": "0xhere", "data": 'hello'}, p) + +def test_lockAccount(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.unlockAccount(new_account, p, duration=None) + +def test_unlockAccount(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.unlockAccount(new_account, p) + +def test_sign(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.sign('message', new_account, p) + +def test_ecRecover(chain): + new_account = str(uuid.uuid1()) + a = Account(name=new_account, password=p, chain=Chain()) + a.p.ecRecover('message', 'signature') \ No newline at end of file