mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Transfer-tx fulfillments validation
This commit is contained in:
parent
dd51a0bcd8
commit
f0fc3c4243
102
transaction.py
102
transaction.py
@ -101,6 +101,31 @@ class Fulfillment(object):
|
|||||||
return cls(fulfillment, ffill['owners_before'], ffill['fid'], TransactionLink.from_dict(ffill['input']))
|
return cls(fulfillment, ffill['owners_before'], ffill['fid'], TransactionLink.from_dict(ffill['input']))
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionLink(object):
|
||||||
|
# NOTE: In an IPLD implementation, this class is not necessary anymore, as an IPLD link can simply point to an
|
||||||
|
# object, as well as an objects properties. So instead of having a (de)serializable class, we can have a
|
||||||
|
# simple IPLD link of the form: `/<tx_id>/transaction/conditions/<cid>/`
|
||||||
|
def __init__(self, tx_id=None, cid=None):
|
||||||
|
self.tx_id = tx_id
|
||||||
|
self.cid = cid
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, link):
|
||||||
|
try:
|
||||||
|
return cls(link['tx_id'], link['cid'])
|
||||||
|
except TypeError:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
if self.tx_id is None and self.cid is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'tx_id': self.tx_id,
|
||||||
|
'cid': self.cid,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Condition(object):
|
class Condition(object):
|
||||||
def __init__(self, condition_uri, owners_after=None, cid=0):
|
def __init__(self, condition_uri, owners_after=None, cid=0):
|
||||||
# TODO: Add more description
|
# TODO: Add more description
|
||||||
@ -162,31 +187,6 @@ class Data(object):
|
|||||||
return uuid4()
|
return uuid4()
|
||||||
|
|
||||||
|
|
||||||
class TransactionLink(object):
|
|
||||||
# NOTE: In an IPLD implementation, this class is not necessary anymore, as an IPLD link can simply point to an
|
|
||||||
# object, as well as an objects properties. So instead of having a (de)serializable class, we can have a
|
|
||||||
# simple IPLD link of the form: `/<tx_id>/transaction/conditions/<cid>/`
|
|
||||||
def __init__(self, tx_id=None, cid=None):
|
|
||||||
self.tx_id = tx_id
|
|
||||||
self.cid = cid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, link):
|
|
||||||
try:
|
|
||||||
return cls(link['tx_id'], link['cid'])
|
|
||||||
except TypeError:
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
if self.tx_id is None and self.cid is None:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return {
|
|
||||||
'tx_id': self.tx_id,
|
|
||||||
'cid': self.cid,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Transaction(object):
|
class Transaction(object):
|
||||||
CREATE = 'CREATE'
|
CREATE = 'CREATE'
|
||||||
TRANSFER = 'TRANSFER'
|
TRANSFER = 'TRANSFER'
|
||||||
@ -371,7 +371,7 @@ class Transaction(object):
|
|||||||
|
|
||||||
subfulfillment.sign(tx_serialized, private_key)
|
subfulfillment.sign(tx_serialized, private_key)
|
||||||
|
|
||||||
def fulfillments_valid(self):
|
def fulfillments_valid(self, input_condition_uris=None):
|
||||||
# TODO: Update Comment
|
# TODO: Update Comment
|
||||||
"""Verify the signature of a transaction
|
"""Verify the signature of a transaction
|
||||||
|
|
||||||
@ -383,29 +383,57 @@ class Transaction(object):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if the signature is correct, False otherwise.
|
bool: True if the signature is correct, False otherwise.
|
||||||
"""
|
"""
|
||||||
if len(self.fulfillments) > 1 and len(self.conditions) > 1:
|
if not isinstance(input_condition_uris, list) and input_condition_uris is not None:
|
||||||
def gen_tx(fulfillment, condition):
|
raise TypeError('`input_condition_uris` must be list instance')
|
||||||
|
elif input_condition_uris is None:
|
||||||
|
input_condition_uris = []
|
||||||
|
|
||||||
|
input_condition_uris_count = len(input_condition_uris)
|
||||||
|
fulfillments_count = len(self.fulfillments)
|
||||||
|
conditions_count = len(self.conditions)
|
||||||
|
|
||||||
|
def gen_tx(fulfillment, condition, input_condition_uris=None):
|
||||||
return Transaction(self.operation, [fulfillment], [condition], self.data, self.timestamp,
|
return Transaction(self.operation, [fulfillment], [condition], self.data, self.timestamp,
|
||||||
self.version).fulfillments_valid()
|
self.version).fulfillments_valid(input_condition_uris)
|
||||||
|
|
||||||
|
if self.operation is Transaction.CREATE:
|
||||||
|
if not fulfillments_count == conditions_count:
|
||||||
|
raise ValueError('Fulfillments, conditions must have the same count')
|
||||||
|
elif fulfillments_count > 1 and conditions_count > 1:
|
||||||
return reduce(and_, map(gen_tx, self.fulfillments, self.conditions))
|
return reduce(and_, map(gen_tx, self.fulfillments, self.conditions))
|
||||||
else:
|
else:
|
||||||
return self._fulfillment_valid()
|
return self._fulfillment_valid()
|
||||||
|
elif self.operation is Transaction.TRANSFER:
|
||||||
|
if not fulfillments_count == conditions_count == input_condition_uris_count:
|
||||||
|
raise ValueError('Fulfillments, conditions and input_condition_uris must have the same count')
|
||||||
|
elif fulfillments_count > 1 and conditions_count > 1 and input_condition_uris > 1:
|
||||||
|
return reduce(and_, map(gen_tx, self.fulfillments, self.conditions, input_condition_uris))
|
||||||
|
else:
|
||||||
|
return self._fulfillment_valid(input_condition_uris.pop())
|
||||||
|
else:
|
||||||
|
raise TypeError('`operation` must be either `Transaction.TRANSFER` or `Transaction.CREATE`')
|
||||||
|
|
||||||
def _fulfillment_valid(self):
|
def _fulfillment_valid(self, input_condition_uri=None):
|
||||||
# NOTE: We're always taking the first fulfillment, as this method is called recursively.
|
# NOTE: We're always taking the first fulfillment, as this method is called recursively.
|
||||||
# See: `fulfillments_valid`
|
# See: `fulfillments_valid`
|
||||||
fulfillment = self.fulfillments[0].fulfillment
|
fulfillment = self.fulfillments[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parsed_fulfillment = CCFulfillment.from_uri(fulfillment.serialize_uri())
|
parsed_fulfillment = CCFulfillment.from_uri(fulfillment.fulfillment.serialize_uri())
|
||||||
# TODO: Figure out if we need all three of those errors here
|
|
||||||
except (TypeError, ValueError, ParsingError):
|
except (TypeError, ValueError, ParsingError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# TODO: For transfer-transaction, we'll also have to validate against the given condition
|
if self.operation == Transaction.CREATE:
|
||||||
# NOTE: We pass a timestamp here, as in case of a timeout condition we'll have to validate against it.
|
input_condition_valid = True
|
||||||
return parsed_fulfillment.validate(message=Transaction._to_str(Transaction._remove_signatures(self.to_dict())),
|
else:
|
||||||
now=gen_timestamp())
|
# NOTE: When passing a `TRANSFER` transaction for validation, we check if it's valid by validating its
|
||||||
|
# input condition (taken from previous transaction) against the current fulfillment.
|
||||||
|
input_condition_valid = input_condition_uri == fulfillment.fulfillment.condition_uri
|
||||||
|
|
||||||
|
tx_serialized = Transaction._to_str(Transaction._remove_signatures(self.to_dict()))
|
||||||
|
# NOTE: We pass a timestamp to `.validate`, as in case of a timeout condition we'll have to validate against
|
||||||
|
# it.
|
||||||
|
return parsed_fulfillment.validate(message=tx_serialized, now=gen_timestamp()) and input_condition_valid
|
||||||
|
|
||||||
def transfer(self, conditions):
|
def transfer(self, conditions):
|
||||||
return Transaction(Transaction.TRANSFER, self._fulfillments_as_inputs(), conditions)
|
return Transaction(Transaction.TRANSFER, self._fulfillments_as_inputs(), conditions)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user