mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge dca74116aba80376c8cb9d16f603b6f0afc15cb3 into 16d78bb2fb4bc64fca4a7e9460d7f6c5d746139f
This commit is contained in:
commit
ffb09a2283
36
README.md
36
README.md
@ -1,14 +1,38 @@
|
|||||||
# BigchainDB
|
# BigchainDB - Integration Examples
|
||||||
|
|
||||||
A scalable blockchain database. [The whitepaper](https://www.bigchaindb.com/whitepaper/) explains what that means.
|
A scalable blockchain database. [The whitepaper](https://www.bigchaindb.com/whitepaper/) explains what that means.
|
||||||
|
|
||||||
[](https://gitter.im/bigchaindb/bigchaindb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/bigchaindb/bigchaindb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](https://pypi.python.org/pypi/BigchainDB)
|
[](https://travis-ci.org/diminator/bigchaindb)
|
||||||
[](https://travis-ci.org/bigchaindb/bigchaindb)
|
[](https://codecov.io/github/diminator/bigchaindb?branch=develop)
|
||||||
[](https://codecov.io/github/bigchaindb/bigchaindb?branch=develop)
|
|
||||||
[](http://bigchaindb.readthedocs.org/en/develop/?badge=develop)
|
|
||||||
|
|
||||||
## Quick Start
|
## Interledger
|
||||||
|
|
||||||
|
This fork provides basic functionality for supporting the interledger protocol, see http://interledger.org/
|
||||||
|
|
||||||
|
The edits are found under interledger/core.py and interledger/tests/test_connector.py
|
||||||
|
To run the interledger test, [install bigchaindb](#gettingstarted)
|
||||||
|
```
|
||||||
|
$0> rethinkdb &
|
||||||
|
|
||||||
|
$1> bigchaindb -c examples/interledger/bigchain.json start
|
||||||
|
$2> bigchaindb -c examples/interledger/megachain.json start
|
||||||
|
|
||||||
|
$3> cd examples/interledger
|
||||||
|
$3> python3 run_cross_ledger_payment_simple.py
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] multisig
|
||||||
|
- [x] escrow
|
||||||
|
- [x] connectors
|
||||||
|
- [ ] signed receipts
|
||||||
|
- [ ] receipt propagation and listeners
|
||||||
|
- [ ] proper asset conversion
|
||||||
|
- [ ] RESTful API wrapper
|
||||||
|
- [ ] multi-(big)chain(db) instantiation + network path optimization
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="gettingstarted"></a>Quick Start
|
||||||
|
|
||||||
### [Install & Run BigchainDB](http://bigchaindb.readthedocs.org/en/develop/installing.html)
|
### [Install & Run BigchainDB](http://bigchaindb.readthedocs.org/en/develop/installing.html)
|
||||||
### [Run BigchainDB with Docker](http://bigchaindb.readthedocs.org/en/develop/installing.html#run-bigchaindb-with-docker)
|
### [Run BigchainDB with Docker](http://bigchaindb.readthedocs.org/en/develop/installing.html#run-bigchaindb-with-docker)
|
||||||
|
|||||||
@ -72,7 +72,7 @@ class Bigchain(object):
|
|||||||
return r.connect(host=self.host, port=self.port, db=self.dbname)
|
return r.connect(host=self.host, port=self.port, db=self.dbname)
|
||||||
|
|
||||||
@monitor.timer('create_transaction', rate=bigchaindb.config['statsd']['rate'])
|
@monitor.timer('create_transaction', rate=bigchaindb.config['statsd']['rate'])
|
||||||
def create_transaction(self, current_owner, new_owner, tx_input, operation, payload=None):
|
def create_transaction(self, current_owners, new_owners, tx_input, operation, payload=None):
|
||||||
"""Create a new transaction
|
"""Create a new transaction
|
||||||
|
|
||||||
A transaction in the bigchain is a transfer of a digital asset between two entities represented
|
A transaction in the bigchain is a transfer of a digital asset between two entities represented
|
||||||
@ -88,8 +88,8 @@ class Bigchain(object):
|
|||||||
`TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.
|
`TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
current_owner (str): base58 encoded public key of the current owner of the asset.
|
current_owners (list): base58 encoded public keys of all current owners of the asset.
|
||||||
new_owner (str): base58 encoded public key of the new owner of the digital asset.
|
new_owners (list): base58 encoded public keys of all new owners of the digital asset.
|
||||||
tx_input (str): id of the transaction to use as input.
|
tx_input (str): id of the transaction to use as input.
|
||||||
operation (str): Either `CREATE` or `TRANSFER` operation.
|
operation (str): Either `CREATE` or `TRANSFER` operation.
|
||||||
payload (Optional[dict]): dictionary with information about asset.
|
payload (Optional[dict]): dictionary with information about asset.
|
||||||
@ -119,8 +119,8 @@ class Bigchain(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
tx = {
|
tx = {
|
||||||
'current_owner': current_owner,
|
'current_owners': current_owners if isinstance(current_owners, list) else [current_owners],
|
||||||
'new_owner': new_owner,
|
'new_owners': new_owners if isinstance(new_owners, list) else [new_owners],
|
||||||
'input': tx_input,
|
'input': tx_input,
|
||||||
'operation': operation,
|
'operation': operation,
|
||||||
'timestamp': self.timestamp(),
|
'timestamp': self.timestamp(),
|
||||||
@ -139,7 +139,7 @@ class Bigchain(object):
|
|||||||
|
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def sign_transaction(self, transaction, private_key):
|
def sign_transaction(self, transaction, private_key, public_key=None):
|
||||||
"""Sign a transaction
|
"""Sign a transaction
|
||||||
|
|
||||||
A transaction signed with the `current_owner` corresponding private key.
|
A transaction signed with the `current_owner` corresponding private key.
|
||||||
@ -147,15 +147,29 @@ class Bigchain(object):
|
|||||||
Args:
|
Args:
|
||||||
transaction (dict): transaction to sign.
|
transaction (dict): transaction to sign.
|
||||||
private_key (str): base58 encoded private key to create a signature of the transaction.
|
private_key (str): base58 encoded private key to create a signature of the transaction.
|
||||||
|
public_key (str): (optional) base58 encoded public key to identify each signature of a multisig transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: transaction with the `signature` field included.
|
dict: transaction with the `signature` field included.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
private_key = PrivateKey(private_key)
|
private_key = PrivateKey(private_key)
|
||||||
signature = private_key.sign(self.serialize(transaction))
|
if len(transaction['transaction']['current_owners']) == 1:
|
||||||
|
signatures_updated = private_key.sign(self.serialize(transaction))
|
||||||
|
else:
|
||||||
|
# multisig, sign for each input and store {pub_key: signature_for_priv_key}
|
||||||
|
if public_key is None:
|
||||||
|
raise ValueError('public_key must be provided for signing multisig transactions')
|
||||||
|
transaction_without_signatures = transaction.copy()
|
||||||
|
signatures = transaction_without_signatures.pop('signatures') \
|
||||||
|
if 'signatures' in transaction_without_signatures else []
|
||||||
|
signatures_updated = signatures.copy()
|
||||||
|
signatures_updated = [s for s in signatures_updated if not s['public_key'] == public_key]
|
||||||
|
signatures_updated.append({'public_key': public_key,
|
||||||
|
'signature': private_key.sign(self.serialize(transaction_without_signatures))})
|
||||||
|
|
||||||
signed_transaction = transaction.copy()
|
signed_transaction = transaction.copy()
|
||||||
signed_transaction.update({'signature': signature})
|
signed_transaction.update({'signatures': signatures_updated})
|
||||||
return signed_transaction
|
return signed_transaction
|
||||||
|
|
||||||
def verify_signature(self, signed_transaction):
|
def verify_signature(self, signed_transaction):
|
||||||
@ -175,11 +189,26 @@ class Bigchain(object):
|
|||||||
# if assignee field in the transaction, remove it
|
# if assignee field in the transaction, remove it
|
||||||
if 'assignee' in data:
|
if 'assignee' in data:
|
||||||
data.pop('assignee')
|
data.pop('assignee')
|
||||||
|
if 'signatures' not in data:
|
||||||
|
return False
|
||||||
|
|
||||||
signature = data.pop('signature')
|
signatures = data.pop('signatures')
|
||||||
public_key_base58 = signed_transaction['transaction']['current_owner']
|
for public_key_base58 in signed_transaction['transaction']['current_owners']:
|
||||||
public_key = PublicKey(public_key_base58)
|
public_key = PublicKey(public_key_base58)
|
||||||
return public_key.verify(self.serialize(data), signature)
|
|
||||||
|
if isinstance(signatures, list):
|
||||||
|
try:
|
||||||
|
signature = [s['signature'] for s in signatures if s['public_key'] == public_key_base58]
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
if not len(signature) == 1:
|
||||||
|
return False
|
||||||
|
signature = signature[0]
|
||||||
|
else:
|
||||||
|
signature = signatures
|
||||||
|
if not public_key.verify(self.serialize(data), signature):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
@monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate'])
|
@monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate'])
|
||||||
def write_transaction(self, signed_transaction):
|
def write_transaction(self, signed_transaction):
|
||||||
@ -189,7 +218,7 @@ class Bigchain(object):
|
|||||||
it has been validated by the nodes of the federation.
|
it has been validated by the nodes of the federation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
singed_transaction (dict): transaction with the `signature` included.
|
signed_transaction (dict): transaction with the `signature` included.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: database response
|
dict: database response
|
||||||
@ -303,9 +332,10 @@ class Bigchain(object):
|
|||||||
list: list of `txids` currently owned by `owner`
|
list: list of `txids` currently owned by `owner`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# TODO: fix for multisig. new_owners is a list!
|
||||||
response = r.table('bigchain')\
|
response = r.table('bigchain')\
|
||||||
.concat_map(lambda doc: doc['block']['transactions'])\
|
.concat_map(lambda doc: doc['block']['transactions'])\
|
||||||
.filter({'transaction': {'new_owner': owner}})\
|
.filter({'transaction': {'new_owners': owner if isinstance(owner, list) else [owner]}})\
|
||||||
.pluck('id')['id']\
|
.pluck('id')['id']\
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
owned = []
|
owned = []
|
||||||
@ -341,7 +371,7 @@ class Bigchain(object):
|
|||||||
if transaction['transaction']['operation'] == 'CREATE':
|
if transaction['transaction']['operation'] == 'CREATE':
|
||||||
if transaction['transaction']['input']:
|
if transaction['transaction']['input']:
|
||||||
raise ValueError('A CREATE operation has no inputs')
|
raise ValueError('A CREATE operation has no inputs')
|
||||||
if transaction['transaction']['current_owner'] not in self.federation_nodes + [self.me]:
|
if not(set(transaction['transaction']['current_owners']) <= set(self.federation_nodes + [self.me])):
|
||||||
raise exceptions.OperationError('Only federation nodes can use the operation `CREATE`')
|
raise exceptions.OperationError('Only federation nodes can use the operation `CREATE`')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -354,9 +384,9 @@ class Bigchain(object):
|
|||||||
raise exceptions.TransactionDoesNotExist('input `{}` does not exist in the bigchain'.format(
|
raise exceptions.TransactionDoesNotExist('input `{}` does not exist in the bigchain'.format(
|
||||||
transaction['transaction']['input']))
|
transaction['transaction']['input']))
|
||||||
|
|
||||||
if tx_input['transaction']['new_owner'] != transaction['transaction']['current_owner']:
|
if tx_input['transaction']['new_owners'] != transaction['transaction']['current_owners']:
|
||||||
raise exceptions.TransactionOwnerError('current_owner `{}` does not own the input `{}`'.format(
|
raise exceptions.TransactionOwnerError('current_owner `{}` does not own the input `{}`'.format(
|
||||||
transaction['transaction']['current_owner'], transaction['transaction']['input']))
|
transaction['transaction']['current_owners'], transaction['transaction']['input']))
|
||||||
|
|
||||||
# check if the input was already spent by a transaction other then this one.
|
# check if the input was already spent by a transaction other then this one.
|
||||||
spent = self.get_spent(tx_input['id'])
|
spent = self.get_spent(tx_input['id'])
|
||||||
|
|||||||
0
examples/__init__.py
Normal file
0
examples/__init__.py
Normal file
45
examples/accounts.py
Normal file
45
examples/accounts.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, ledger):
|
||||||
|
self.ledger = ledger
|
||||||
|
self.private, self.public = ledger.generate_keys()
|
||||||
|
self.assets = []
|
||||||
|
|
||||||
|
def create_asset(self):
|
||||||
|
tx = self.ledger.create_transaction(self.ledger.me, self.public, None, 'CREATE')
|
||||||
|
tx_signed = self.ledger.sign_transaction(tx, self.ledger.me_private)
|
||||||
|
self.ledger.validate_transaction(tx_signed)
|
||||||
|
self.ledger.write_transaction(tx_signed)
|
||||||
|
self.assets.append(tx_signed)
|
||||||
|
|
||||||
|
def create_assets(self, amount=1):
|
||||||
|
for i in range(amount):
|
||||||
|
self.create_asset()
|
||||||
|
|
||||||
|
|
||||||
|
class Escrow(User):
|
||||||
|
def __init__(self, ledger=None, current_owner=None, new_owner=None,
|
||||||
|
asset_id=None, condition_func=None, payload=None):
|
||||||
|
User.__init__(self, ledger)
|
||||||
|
self.condition_func = condition_func if condition_func else lambda proof: True
|
||||||
|
self.new_owner = new_owner
|
||||||
|
tx = self.ledger.create_transaction(current_owner,
|
||||||
|
[current_owner, self.public],
|
||||||
|
asset_id,
|
||||||
|
'TRANSFER',
|
||||||
|
payload)
|
||||||
|
self.assets = tx
|
||||||
|
|
||||||
|
def release(self, receipt=None):
|
||||||
|
if not self.validate(receipt):
|
||||||
|
raise Exception
|
||||||
|
tx = self.ledger.create_transaction(self.assets['transaction']['new_owners'],
|
||||||
|
self.new_owner,
|
||||||
|
self.assets['id'],
|
||||||
|
'TRANSFER',
|
||||||
|
self.assets['transaction']['data']['payload'])
|
||||||
|
return self.ledger.sign_transaction(tx, self.private, self.public)
|
||||||
|
|
||||||
|
def validate(self, receipt):
|
||||||
|
return self.condition_func(receipt)
|
||||||
0
examples/interledger/__init__.py
Normal file
0
examples/interledger/__init__.py
Normal file
1
examples/interledger/bigchain.json
Normal file
1
examples/interledger/bigchain.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"database": {"host": "localhost", "name": "bigchain", "port": 28015}, "keyring": [], "keypair": {"public": "jhpCiHPiMmHGFQvjqn1LdLddYhYFd63ywUbi3tFCW31f", "private": "5vbka7oLMf2UTNV1sq3nyKJXTERCPWvD8Pf4KnGYNBtA"}}
|
||||||
76
examples/interledger/connector.py
Normal file
76
examples/interledger/connector.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from examples.accounts import User, Escrow
|
||||||
|
|
||||||
|
|
||||||
|
class LedgerConnection(User):
|
||||||
|
def __init__(self, ledger):
|
||||||
|
self._escrow = None
|
||||||
|
User.__init__(self, ledger)
|
||||||
|
|
||||||
|
def escrow(self, current_owner=None, new_owner=None, condition_func=None, asset_id=None, payload=None):
|
||||||
|
self._escrow = Escrow(ledger=self.ledger,
|
||||||
|
current_owner=current_owner if current_owner else self.public,
|
||||||
|
new_owner=new_owner if new_owner else self.public,
|
||||||
|
asset_id=asset_id if asset_id else self.assets[0]['id'],
|
||||||
|
condition_func=condition_func,
|
||||||
|
payload=payload)
|
||||||
|
if not current_owner:
|
||||||
|
tx_connector_signed = self.ledger.sign_transaction(self._escrow.assets, self.private)
|
||||||
|
self.ledger.validate_transaction(tx_connector_signed)
|
||||||
|
self._escrow.assets = tx_connector_signed
|
||||||
|
self.ledger.write_transaction(tx_connector_signed)
|
||||||
|
|
||||||
|
def release(self, condition):
|
||||||
|
return self._escrow.release(condition)
|
||||||
|
|
||||||
|
|
||||||
|
class Connector:
|
||||||
|
def __init__(self, ledger=None):
|
||||||
|
self.ledger_connections = []
|
||||||
|
if ledger:
|
||||||
|
self.add_ledger(ledger)
|
||||||
|
|
||||||
|
def public(self, ledger=None):
|
||||||
|
return self.get_ledger_connection(ledger).public if self.get_ledger_connection(ledger) else None
|
||||||
|
|
||||||
|
def private(self, ledger=None):
|
||||||
|
return self.get_ledger_connection(ledger).private if self.get_ledger_connection(ledger) else None
|
||||||
|
|
||||||
|
def get_assets(self, ledger=None):
|
||||||
|
ledger_connection = self.get_ledger_connection(ledger)
|
||||||
|
return ledger_connection.assets if ledger_connection else None
|
||||||
|
|
||||||
|
def create_assets(self, amount=1, ledger=None):
|
||||||
|
ledger_connection = self.get_ledger_connection(ledger)
|
||||||
|
if ledger_connection:
|
||||||
|
ledger_connection.create_assets(amount)
|
||||||
|
|
||||||
|
def get_ledger_connection(self, ledger=None):
|
||||||
|
if not ledger:
|
||||||
|
return self.ledger_connections[0]
|
||||||
|
# TODO: yield
|
||||||
|
ledger_connection = [l for l in self.ledger_connections if l.ledger == ledger]
|
||||||
|
return ledger_connection[0] if ledger_connection else None
|
||||||
|
|
||||||
|
def add_ledger(self, ledger):
|
||||||
|
if self.can_add_ledger_connection(ledger):
|
||||||
|
self.ledger_connections.append(LedgerConnection(ledger))
|
||||||
|
|
||||||
|
def can_add_ledger_connection(self, ledger):
|
||||||
|
return False if self.get_ledger_connection(ledger) else True
|
||||||
|
|
||||||
|
def connect(self, user_from=None, ledger_from=None, user_to=None, ledger_to=None,
|
||||||
|
condition_func=None, asset_id=None, payload=None):
|
||||||
|
connection_from = self.get_ledger_connection(ledger_from)
|
||||||
|
connection_to = self.get_ledger_connection(ledger_to)
|
||||||
|
connection_from.escrow(current_owner=user_from,
|
||||||
|
condition_func=condition_func,
|
||||||
|
asset_id=asset_id,
|
||||||
|
payload=payload)
|
||||||
|
connection_to.escrow(new_owner=user_to,
|
||||||
|
condition_func=condition_func,
|
||||||
|
payload=payload)
|
||||||
|
return connection_from._escrow.assets
|
||||||
|
|
||||||
|
def release(self, ledger=None, receipt=None):
|
||||||
|
connection = self.get_ledger_connection(ledger)
|
||||||
|
return connection.release(receipt)
|
||||||
1
examples/interledger/megachain.json
Normal file
1
examples/interledger/megachain.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"keyring": [], "database": {"host": "localhost", "port": 28015, "name": "megachain"}, "keypair": {"private": "DGKiLSdLsZofmzZMv3B9KZVj79wrq4PZ3PsMmk8Hu8KK", "public": "gMGJ9j15qyBVC1t8ceiDgJLLaRBgvrFchyzB1GgWk3Fa"}}
|
||||||
54
examples/interledger/run_cross_ledger_payment_simple.py
Normal file
54
examples/interledger/run_cross_ledger_payment_simple.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import json
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from bigchaindb import Bigchain
|
||||||
|
from examples.accounts import User
|
||||||
|
from examples.interledger.connector import Connector
|
||||||
|
|
||||||
|
config_bigchain = json.load(open('bigchain.json', 'r'))
|
||||||
|
config_megachain = json.load(open('megachain.json', 'r'))
|
||||||
|
|
||||||
|
bigchain = Bigchain(dbname=config_bigchain['database']['name'],
|
||||||
|
public_key=config_bigchain['keypair']['public'],
|
||||||
|
private_key=config_bigchain['keypair']['private'])
|
||||||
|
|
||||||
|
megachain = Bigchain(dbname=config_megachain['database']['name'],
|
||||||
|
public_key=config_megachain['keypair']['public'],
|
||||||
|
private_key=config_megachain['keypair']['private'])
|
||||||
|
|
||||||
|
alice = User(bigchain)
|
||||||
|
bob = User(megachain)
|
||||||
|
|
||||||
|
connector = Connector(bigchain)
|
||||||
|
connector.add_ledger(megachain)
|
||||||
|
|
||||||
|
# create assets
|
||||||
|
alice.create_assets(amount=2)
|
||||||
|
connector.create_assets(amount=2, ledger=bigchain)
|
||||||
|
connector.create_assets(amount=2, ledger=megachain)
|
||||||
|
|
||||||
|
sleep(6)
|
||||||
|
# transfer asset to escrow
|
||||||
|
tx_alice = connector.connect(user_from=alice.public,
|
||||||
|
ledger_from=alice.ledger,
|
||||||
|
user_to=bob.public,
|
||||||
|
ledger_to=bob.ledger,
|
||||||
|
condition_func=lambda proof: True,
|
||||||
|
asset_id=alice.assets[0]['id'],
|
||||||
|
payload={'what': 'ever'})
|
||||||
|
|
||||||
|
tx_alice_signed = alice.ledger.sign_transaction(tx_alice, alice.private)
|
||||||
|
alice.ledger.validate_transaction(tx_alice_signed)
|
||||||
|
alice.ledger.write_transaction(tx_alice_signed)
|
||||||
|
|
||||||
|
sleep(6)
|
||||||
|
# release asset from escrow
|
||||||
|
tx_bob = connector.release(ledger=bob.ledger, receipt=None)
|
||||||
|
tx_bob_signed = bob.ledger.sign_transaction(tx_bob, connector.private(bob.ledger), connector.public(bob.ledger))
|
||||||
|
bob.ledger.validate_transaction(tx_bob_signed)
|
||||||
|
bob.ledger.write_transaction(tx_bob_signed)
|
||||||
|
|
||||||
|
tx_connector = connector.release(ledger=alice.ledger, receipt=None)
|
||||||
|
tx_connector_signed = alice.ledger.sign_transaction(tx_connector, alice.private, alice.public)
|
||||||
|
alice.ledger.validate_transaction(tx_connector_signed)
|
||||||
|
alice.ledger.write_transaction(tx_connector_signed)
|
||||||
0
examples/interledger/tests/__init__.py
Normal file
0
examples/interledger/tests/__init__.py
Normal file
@ -52,13 +52,25 @@ class TestBigchainApi(object):
|
|||||||
tx = b.create_transaction('a', 'b', 'c', 'd')
|
tx = b.create_transaction('a', 'b', 'c', 'd')
|
||||||
|
|
||||||
assert sorted(tx) == sorted(['id', 'transaction'])
|
assert sorted(tx) == sorted(['id', 'transaction'])
|
||||||
assert sorted(tx['transaction']) == sorted(['current_owner', 'new_owner', 'input', 'operation',
|
assert sorted(tx['transaction']) == sorted(['current_owners', 'new_owners', 'input', 'operation',
|
||||||
'timestamp', 'data'])
|
'timestamp', 'data'])
|
||||||
|
|
||||||
def test_create_transaction_with_unsupported_payload_raises(self, b):
|
def test_create_transaction_with_unsupported_payload_raises(self, b):
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
b.create_transaction('a', 'b', 'c', 'd', payload=[])
|
b.create_transaction('a', 'b', 'c', 'd', payload=[])
|
||||||
|
|
||||||
|
def test_create_transaction_with_multiple_owners(self, b):
|
||||||
|
num_current_owners = 42
|
||||||
|
num_new_owners = 73
|
||||||
|
tx = b.create_transaction(['a']*num_current_owners, ['b']*num_new_owners, 'd', 'e')
|
||||||
|
|
||||||
|
assert sorted(tx) == sorted(['id', 'transaction'])
|
||||||
|
assert sorted(tx['transaction']) == sorted(['current_owners', 'new_owners', 'input', 'operation',
|
||||||
|
'timestamp', 'data'])
|
||||||
|
|
||||||
|
assert len(tx['transaction']['current_owners']) == num_current_owners
|
||||||
|
assert len(tx['transaction']['new_owners']) == num_new_owners
|
||||||
|
|
||||||
def test_transaction_hash(self, b):
|
def test_transaction_hash(self, b):
|
||||||
payload = {'cats': 'are awesome'}
|
payload = {'cats': 'are awesome'}
|
||||||
tx = b.create_transaction('a', 'b', 'c', 'd', payload)
|
tx = b.create_transaction('a', 'b', 'c', 'd', payload)
|
||||||
@ -79,9 +91,30 @@ class TestBigchainApi(object):
|
|||||||
def test_transaction_signature(self, b):
|
def test_transaction_signature(self, b):
|
||||||
sk, vk = b.generate_keys()
|
sk, vk = b.generate_keys()
|
||||||
tx = b.create_transaction(vk, 'b', 'c', 'd')
|
tx = b.create_transaction(vk, 'b', 'c', 'd')
|
||||||
|
|
||||||
|
assert b.verify_signature(tx) is False
|
||||||
tx_signed = b.sign_transaction(tx, sk)
|
tx_signed = b.sign_transaction(tx, sk)
|
||||||
|
|
||||||
assert 'signature' in tx_signed
|
assert 'signatures' in tx_signed
|
||||||
|
assert b.verify_signature(tx_signed)
|
||||||
|
|
||||||
|
def test_transaction_signature_multiple_owners(self, b):
|
||||||
|
num_current_owners = 42
|
||||||
|
sk, vk = [], []
|
||||||
|
for _ in range(num_current_owners):
|
||||||
|
sk_, vk_ = b.generate_keys()
|
||||||
|
sk.append(sk_)
|
||||||
|
vk.append(vk_)
|
||||||
|
tx = b.create_transaction(vk, 'b', 'c', 'd')
|
||||||
|
tx_signed = tx
|
||||||
|
for i in range(num_current_owners):
|
||||||
|
assert b.verify_signature(tx_signed) is False
|
||||||
|
tx_signed = b.sign_transaction(tx_signed, sk[i], vk[i])
|
||||||
|
|
||||||
|
assert 'signatures' in tx_signed
|
||||||
|
assert 'public_key' in tx_signed['signatures'][0]
|
||||||
|
assert 'signature' in tx_signed['signatures'][0]
|
||||||
|
assert len(tx_signed['signatures']) == num_current_owners
|
||||||
assert b.verify_signature(tx_signed)
|
assert b.verify_signature(tx_signed)
|
||||||
|
|
||||||
def test_serializer(self, b):
|
def test_serializer(self, b):
|
||||||
@ -289,7 +322,7 @@ class TestTransactionValidation(object):
|
|||||||
with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
|
with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
|
||||||
b.validate_transaction(tx)
|
b.validate_transaction(tx)
|
||||||
|
|
||||||
assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
|
assert excinfo.value.args[0] == 'current_owner `[\'a\']` does not own the input `{}`'.format(valid_input)
|
||||||
assert b.is_valid_transaction(tx) is False
|
assert b.is_valid_transaction(tx) is False
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user