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:
parent
054c17d38c
commit
70692a851c
|
@ -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
113
bigchaindb/client.py
Normal 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')
|
||||
|
|
@ -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']
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
1
setup.py
1
setup.py
|
@ -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
94
tests/test_client.py
Normal 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)
|
||||
|
Loading…
Reference in New Issue
Block a user