1
0
mirror of https://github.com/bigchaindb/bigchaindb.git synced 2024-06-26 03:06:43 +02:00

Add client code to create/transfer txs

This commit is contained in:
vrde 2016-02-26 16:23:50 +01:00
parent 054c17d38c
commit 70692a851c
6 changed files with 218 additions and 2 deletions

View File

@ -39,7 +39,8 @@ config = {
'host': e('BIGCHAIN_STATSD_HOST', default='localhost'),
'port': e('BIGCHAIN_STATSD_PORT', default=8125),
'rate': e('BIGCHAIN_STATSD_SAMPLERATE', default=0.01)
}
},
'api_endpoint': 'http://localhost:8008/api/v1'
}
# We need to maintain a backup copy of the original config dict in case

113
bigchaindb/client.py Normal file
View File

@ -0,0 +1,113 @@
import requests
import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils
from bigchaindb import exceptions
from bigchaindb import crypto
class Client:
"""Client for BigchainDB.
A Client is initialized with a keypair and is able to create, sign, and submit transactions to a Node
in the Federation. At the moment, a Client instance is bounded to a specific ``host`` in the Federation.
In the future, a Client might connect to >1 hosts.
"""
def __init__(self, public_key=None, private_key=None, api_endpoint=None):
"""Initialize the Client instance
There are three ways in which the Client instance can get its parameters.
The order by which the parameters are chosen are:
1. Setting them by passing them to the `__init__` method itself.
2. Setting them as environment variables
3. Reading them from the `config.json` file.
Args:
public_key (str): the base58 encoded public key for the ECDSA secp256k1 curve.
private_key (str): the base58 encoded private key for the ECDSA secp256k1 curve.
host (str): hostname where the rethinkdb is running.
port (int): port in which rethinkb is running (usually 28015).
"""
config_utils.autoconfigure()
self.public_key = public_key or bigchaindb.config['keypair']['public']
self.private_key = private_key or bigchaindb.config['keypair']['private']
self.api_endpoint = api_endpoint or bigchaindb.config['api_endpoint']
if not self.public_key or not self.private_key:
raise exceptions.KeypairNotFoundException()
def make_tx(self, new_owner, tx_input, operation='TRANSFER', payload=None):
"""Make a new transaction
Refer to the documentation of ``bigchaindb.util.create_tx``
"""
return util.create_tx(self.public_key, new_owner, tx_input, operation, payload)
def sign_tx(self, tx):
"""Sign a transaction
Refer to the documentation of ``bigchaindb.util.sign_tx``
"""
return util.sign_tx(tx, self.private_key)
def push_tx(self, tx):
"""Submit a transaction to the Federation.
Args:
tx (dict): the transaction to be pushed to the Federation.
Return:
The transaction pushed to the Federation.
"""
res = requests.post(self.api_endpoint + '/tx/', json=tx)
return res.json()
def create(self, payload=None):
"""Create a transaction.
Args:
payload (dict): the payload for the transaction.
Return:
The transaction pushed to the Federation.
"""
tx = self.make_tx(self.public_key, None, operation='CREATE', payload=payload)
signed_tx = self.sign_tx(tx)
return self.push_tx(signed_tx)
def transfer(self, new_owner, tx_input, payload=None):
"""Transfer a transaction.
Args:
new_owner (str): the public key of the new owner
tx_input (str): the id of the transaction to use as input
payload (dict, optional): the payload for the transaction.
Return:
The transaction pushed to the Federation.
"""
tx = self.make_tx(new_owner, tx_input, payload=payload)
signed_tx = self.sign_tx(tx)
return self.push_tx(signed_tx)
def temp_client():
"""Create a new temporary client.
Return:
A client initialized with a keypair generated on the fly.
"""
private_key, public_key = crypto.generate_key_pair()
return Client(private_key=private_key, public_key=public_key, api_endpoint='http://localhost:5000')

View File

@ -43,8 +43,8 @@ class Bigchain(object):
public_key (str): the base58 encoded public key for the ECDSA secp256k1 curve.
private_key (str): the base58 encoded private key for the ECDSA secp256k1 curve.
keyring (list[str]): list of base58 encoded public keys of the federation nodes.
"""
config_utils.autoconfigure()
self.host = host or bigchaindb.config['database']['host']
self.port = port or bigchaindb.config['database']['port']

View File

@ -6,6 +6,7 @@ import rethinkdb as r
from bigchaindb import Bigchain
from bigchaindb.voter import Voter
from bigchaindb.block import Block
from bigchaindb.web import server
logger = logging.getLogger(__name__)
@ -80,3 +81,9 @@ class Processes(object):
logger.info('starting voter')
p_voter.start()
# start the web api
webapi = server.create_app()
p_webapi = mp.Process(name='webapi', target=webapi.run, kwargs={'host': '0.0.0.0'})
p_webapi.start()

View File

@ -74,6 +74,7 @@ setup(
'base58==0.2.2',
'bitcoin==1.1.42',
'flask==0.10.1',
'requests==2.9',
],
setup_requires=['pytest-runner'],
tests_require=tests_require,

94
tests/test_client.py Normal file
View File

@ -0,0 +1,94 @@
import pytest
@pytest.fixture
def client():
from bigchaindb.client import temp_client
return temp_client()
@pytest.fixture
def mock_requests_post(monkeypatch):
class MockResponse:
def __init__(self, json):
self._json = json
def json(self):
return self._json
def mockreturn(*args, **kwargs):
return MockResponse(kwargs.get('json'))
monkeypatch.setattr('requests.post', mockreturn)
def test_temp_client_returns_a_temp_client():
from bigchaindb.client import temp_client
client = temp_client()
assert client.public_key
assert client.private_key
def test_client_can_make_transactions(client):
tx = client.make_tx('a', 123)
assert tx['transaction']['current_owner'] == client.public_key
assert tx['transaction']['new_owner'] == 'a'
assert tx['transaction']['input'] == 123
def test_client_can_sign_transactions(client):
from bigchaindb import util
tx = client.make_tx('a', 123)
signed_tx = client.sign_tx(tx)
assert signed_tx['transaction']['current_owner'] == client.public_key
assert signed_tx['transaction']['new_owner'] == 'a'
assert signed_tx['transaction']['input'] == 123
assert util.verify_signature(signed_tx)
def test_client_can_push_transactions(mock_requests_post, client):
from bigchaindb import util
tx = client.make_tx('a', 123)
signed_tx = client.sign_tx(tx)
ret_tx = client.push_tx(signed_tx)
assert ret_tx['transaction']['current_owner'] == client.public_key
assert ret_tx['transaction']['new_owner'] == 'a'
assert ret_tx['transaction']['input'] == 123
assert util.verify_signature(ret_tx)
def test_client_can_create_transactions_using_shortcut_method(mock_requests_post, client):
from bigchaindb import util
tx = client.create()
# XXX: `CREATE` operations require the node that receives the transaction to modify the data in
# the transaction itself.
# `current_owner` will be overwritten with the public key of the node in the federation
# that will create the real transaction. `signature` will be overwritten with the new signature.
# Note that this scenario is ignored by this test.
assert tx['transaction']['current_owner'] == client.public_key
assert tx['transaction']['new_owner'] == client.public_key
assert tx['transaction']['input'] == None
assert util.verify_signature(tx)
def test_client_can_transfer_transactions_using_shortcut_method(mock_requests_post, client):
from bigchaindb import util
tx = client.transfer('a', 123)
assert tx['transaction']['current_owner'] == client.public_key
assert tx['transaction']['new_owner'] == 'a'
assert tx['transaction']['input'] == 123
assert util.verify_signature(tx)