mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
tx with conditions
This commit is contained in:
parent
ce945e3409
commit
b76bd72ea0
@ -3,7 +3,6 @@ import random
|
|||||||
import json
|
import json
|
||||||
import rapidjson
|
import rapidjson
|
||||||
|
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb import util
|
from bigchaindb import util
|
||||||
from bigchaindb import config_utils
|
from bigchaindb import config_utils
|
||||||
@ -11,7 +10,6 @@ from bigchaindb import exceptions
|
|||||||
from bigchaindb import crypto
|
from bigchaindb import crypto
|
||||||
from bigchaindb.monitor import Monitor
|
from bigchaindb.monitor import Monitor
|
||||||
|
|
||||||
|
|
||||||
monitor = Monitor()
|
monitor = Monitor()
|
||||||
|
|
||||||
|
|
||||||
@ -150,7 +148,7 @@ class Bigchain(object):
|
|||||||
If no transaction with that `txid` was found it returns `None`
|
If no transaction with that `txid` was found it returns `None`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions'])\
|
response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions']) \
|
||||||
.filter(lambda transaction: transaction['id'] == txid).run(self.conn)
|
.filter(lambda transaction: transaction['id'] == txid).run(self.conn)
|
||||||
|
|
||||||
# transaction ids should be unique
|
# transaction ids should be unique
|
||||||
@ -181,8 +179,8 @@ class Bigchain(object):
|
|||||||
returns `None`
|
returns `None`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor = r.table('bigchain')\
|
cursor = r.table('bigchain') \
|
||||||
.get_all(payload_hash, index='payload_hash')\
|
.get_all(payload_hash, index='payload_hash') \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
|
|
||||||
transactions = list(cursor)
|
transactions = list(cursor)
|
||||||
@ -202,7 +200,7 @@ class Bigchain(object):
|
|||||||
"""
|
"""
|
||||||
# checks if an input was already spent
|
# checks if an input was already spent
|
||||||
# checks if the bigchain has any transaction with input `transaction_id`
|
# checks if the bigchain has any transaction with input `transaction_id`
|
||||||
response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions'])\
|
response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions']) \
|
||||||
.filter(lambda transaction: transaction['transaction']['inputs'].contains(txid)).run(self.conn)
|
.filter(lambda transaction: transaction['transaction']['inputs'].contains(txid)).run(self.conn)
|
||||||
|
|
||||||
# a transaction_id should have been spent at most one time
|
# a transaction_id should have been spent at most one time
|
||||||
@ -226,11 +224,14 @@ class Bigchain(object):
|
|||||||
list: list of `txids` currently owned by `owner`
|
list: list of `txids` currently owned by `owner`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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(lambda tx: tx['transaction']['conditions']
|
||||||
.pluck('id')['id']\
|
.contains(lambda c: c['new_owners']
|
||||||
.run(self.conn)
|
.contains(owner))) \
|
||||||
|
.pluck('id')['id'] \
|
||||||
|
.run(self.conn)
|
||||||
|
|
||||||
owned = []
|
owned = []
|
||||||
|
|
||||||
# remove all inputs already spent
|
# remove all inputs already spent
|
||||||
@ -439,37 +440,37 @@ class Bigchain(object):
|
|||||||
if 'block_number' not in block:
|
if 'block_number' not in block:
|
||||||
update['block_number'] = block_number
|
update['block_number'] = block_number
|
||||||
|
|
||||||
r.table('bigchain')\
|
r.table('bigchain') \
|
||||||
.get(vote['vote']['voting_for_block'])\
|
.get(vote['vote']['voting_for_block']) \
|
||||||
.update(update)\
|
.update(update) \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
|
|
||||||
def get_last_voted_block(self):
|
def get_last_voted_block(self):
|
||||||
"""Returns the last block that this node voted on."""
|
"""Returns the last block that this node voted on."""
|
||||||
|
|
||||||
# query bigchain for all blocks this node is a voter but didn't voted on
|
# query bigchain for all blocks this node is a voter but didn't voted on
|
||||||
last_voted = r.table('bigchain')\
|
last_voted = r.table('bigchain') \
|
||||||
.filter(r.row['block']['voters'].contains(self.me))\
|
.filter(r.row['block']['voters'].contains(self.me)) \
|
||||||
.filter(lambda doc: doc['votes'].contains(lambda vote: vote['node_pubkey'] == self.me))\
|
.filter(lambda doc: doc['votes'].contains(lambda vote: vote['node_pubkey'] == self.me)) \
|
||||||
.order_by(r.desc('block_number'))\
|
.order_by(r.desc('block_number')) \
|
||||||
.limit(1)\
|
.limit(1) \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
|
|
||||||
# return last vote if last vote exists else return Genesis block
|
# return last vote if last vote exists else return Genesis block
|
||||||
last_voted = list(last_voted)
|
last_voted = list(last_voted)
|
||||||
if not last_voted:
|
if not last_voted:
|
||||||
return list(r.table('bigchain')
|
return list(r.table('bigchain')
|
||||||
.filter(r.row['block_number'] == 0)
|
.filter(r.row['block_number'] == 0)
|
||||||
.run(self.conn))[0]
|
.run(self.conn))[0]
|
||||||
|
|
||||||
return last_voted[0]
|
return last_voted[0]
|
||||||
|
|
||||||
def get_unvoted_blocks(self):
|
def get_unvoted_blocks(self):
|
||||||
"""Return all the blocks that has not been voted by this node."""
|
"""Return all the blocks that has not been voted by this node."""
|
||||||
|
|
||||||
unvoted = r.table('bigchain')\
|
unvoted = r.table('bigchain') \
|
||||||
.filter(lambda doc: doc['votes'].contains(lambda vote: vote['node_pubkey'] == self.me).not_())\
|
.filter(lambda doc: doc['votes'].contains(lambda vote: vote['node_pubkey'] == self.me).not_()) \
|
||||||
.order_by(r.asc((r.row['block']['timestamp'])))\
|
.order_by(r.asc((r.row['block']['timestamp']))) \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
|
|
||||||
if unvoted and unvoted[0].get('block_number') == 0:
|
if unvoted and unvoted[0].get('block_number') == 0:
|
||||||
|
@ -144,6 +144,9 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
current_owners = current_owners if isinstance(current_owners, list) else [current_owners]
|
||||||
|
new_owners = new_owners if isinstance(new_owners, list) else [new_owners]
|
||||||
|
inputs = inputs if isinstance(inputs, list) else [inputs]
|
||||||
|
|
||||||
# handle payload
|
# handle payload
|
||||||
data = None
|
data = None
|
||||||
@ -159,17 +162,14 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
|||||||
|
|
||||||
# handle inputs
|
# handle inputs
|
||||||
fulfillments = []
|
fulfillments = []
|
||||||
current_owners = current_owners if isinstance(current_owners, list) else [current_owners]
|
|
||||||
# transfer
|
# transfer
|
||||||
if inputs:
|
if inputs:
|
||||||
for fid, inp in enumerate(inputs):
|
for fid, inp in enumerate(inputs):
|
||||||
fulfillment = ThresholdSha256Fulfillment(threshold=len(current_owners))
|
|
||||||
for current_owner in current_owners:
|
|
||||||
fulfillment.add_subfulfillment(Ed25519Fulfillment(public_key=current_owner))
|
|
||||||
fulfillments.append({
|
fulfillments.append({
|
||||||
'current_owners': current_owners,
|
'current_owners': current_owners,
|
||||||
'input': inp,
|
'input': inp,
|
||||||
'fulfillment': fulfillment.serialize_json(),
|
'fulfillment': None,
|
||||||
'fid': fid
|
'fid': fid
|
||||||
})
|
})
|
||||||
# create
|
# create
|
||||||
@ -184,9 +184,18 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
|||||||
# handle outputs
|
# handle outputs
|
||||||
conditions = []
|
conditions = []
|
||||||
for fulfillment in fulfillments:
|
for fulfillment in fulfillments:
|
||||||
|
if len(new_owners) > 1:
|
||||||
|
for new_owner in new_owners:
|
||||||
|
condition = ThresholdSha256Fulfillment(threshold=len(new_owners))
|
||||||
|
condition.add_subfulfillment(Ed25519Fulfillment(public_key=new_owner))
|
||||||
|
elif len(new_owners) == 1:
|
||||||
|
condition = Ed25519Fulfillment(public_key=new_owners[0])
|
||||||
conditions.append({
|
conditions.append({
|
||||||
'new_owners': new_owners,
|
'new_owners': new_owners,
|
||||||
'condition': None,
|
'condition': {
|
||||||
|
'details': json.loads(condition.serialize_json()),
|
||||||
|
'uri': condition.condition.serialize_uri()
|
||||||
|
},
|
||||||
'cid': fulfillment['fid']
|
'cid': fulfillment['fid']
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -220,7 +229,7 @@ def sign_tx(transaction, private_key):
|
|||||||
|
|
||||||
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 (base58 str): base58 encoded private key to create a signature of the transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: transaction with the `fulfillment` fields populated.
|
dict: transaction with the `fulfillment` fields populated.
|
||||||
|
2
setup.py
2
setup.py
@ -71,7 +71,7 @@ setup(
|
|||||||
'rethinkdb==2.2.0.post4',
|
'rethinkdb==2.2.0.post4',
|
||||||
'pysha3==0.3',
|
'pysha3==0.3',
|
||||||
'pytz==2015.7',
|
'pytz==2015.7',
|
||||||
'cryptoconditions==0.1.4',
|
'cryptoconditions==0.1.5',
|
||||||
'statsd==3.2.1',
|
'statsd==3.2.1',
|
||||||
'python-rapidjson==0.0.6',
|
'python-rapidjson==0.0.6',
|
||||||
'logstats==0.2.1',
|
'logstats==0.2.1',
|
||||||
|
@ -23,17 +23,24 @@ def test_remove_unclosed_sockets():
|
|||||||
|
|
||||||
class TestBigchainApi(object):
|
class TestBigchainApi(object):
|
||||||
|
|
||||||
def test_create_transaction(self, b, user_sk):
|
def test_create_transaction_create(self, b, user_sk):
|
||||||
tx = b.create_transaction(b.me, user_sk, None, 'CREATE')
|
tx = b.create_transaction(b.me, user_sk, None, 'CREATE')
|
||||||
|
|
||||||
assert sorted(tx) == sorted(['id', 'transaction'])
|
assert sorted(tx) == sorted(['id', 'transaction', 'version'])
|
||||||
assert sorted(tx['transaction']) == sorted(['current_owner', 'new_owner', 'input', 'operation',
|
assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp'])
|
||||||
'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=[])
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_create_transaction_transfer(self, b, user_vk, user_sk):
|
||||||
|
input_tx = b.get_owned_ids(user_vk).pop()
|
||||||
|
tx = b.create_transaction(b.me, user_sk, input_tx, 'TRANSFER')
|
||||||
|
|
||||||
|
assert sorted(tx) == sorted(['id', 'transaction', 'version'])
|
||||||
|
assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp'])
|
||||||
|
|
||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user