mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Adjust fulfillment (de)serialization
This commit is contained in:
parent
331150b9c2
commit
2ae5d49783
@ -46,7 +46,15 @@ class Fulfillment(object):
|
|||||||
try:
|
try:
|
||||||
fulfillment = self.fulfillment.serialize_uri()
|
fulfillment = self.fulfillment.serialize_uri()
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
fulfillment = None
|
# NOTE: When a non-signed transaction is casted to a dict,
|
||||||
|
# `self.fulfillments` value is lost, as in the node's
|
||||||
|
# transaction model that is saved to the database, does not
|
||||||
|
# account for its dictionary form but just for its signed uri
|
||||||
|
# form.
|
||||||
|
# Hence, when a non-signed fulfillment is to be cast to a
|
||||||
|
# dict, we just call its internal `to_dict` method here and
|
||||||
|
# its `from_dict` method in `Fulfillment.from_dict`.
|
||||||
|
fulfillment = self.fulfillment.to_dict()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# NOTE: `self.tx_input` can be `None` and that's fine
|
# NOTE: `self.tx_input` can be `None` and that's fine
|
||||||
@ -70,14 +78,17 @@ class Fulfillment(object):
|
|||||||
try:
|
try:
|
||||||
fulfillment = CCFulfillment.from_uri(ffill['fulfillment'])
|
fulfillment = CCFulfillment.from_uri(ffill['fulfillment'])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
fulfillment = None
|
# NOTE: See comment about this special case in
|
||||||
|
# `Fulfillment.to_dict`
|
||||||
|
fulfillment = CCFulfillment.from_dict(ffill['fulfillment'])
|
||||||
return cls(fulfillment, ffill['owners_before'], TransactionLink.from_dict(ffill['input']))
|
return cls(fulfillment, ffill['owners_before'], TransactionLink.from_dict(ffill['input']))
|
||||||
|
|
||||||
|
|
||||||
class TransactionLink(object):
|
class TransactionLink(object):
|
||||||
# NOTE: In an IPLD implementation, this class is not necessary anymore, as an IPLD link can simply point to an
|
# NOTE: In an IPLD implementation, this class is not necessary anymore,
|
||||||
# object, as well as an objects properties. So instead of having a (de)serializable class, we can have a
|
# as an IPLD link can simply point to an object, as well as an objects
|
||||||
# simple IPLD link of the form: `/<tx_id>/transaction/conditions/<cid>/`
|
# 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, txid=None, cid=None):
|
def __init__(self, txid=None, cid=None):
|
||||||
self.txid = txid
|
self.txid = txid
|
||||||
self.cid = cid
|
self.cid = cid
|
||||||
@ -111,8 +122,8 @@ class Condition(object):
|
|||||||
"""Create a new condition for a fulfillment
|
"""Create a new condition for a fulfillment
|
||||||
|
|
||||||
Args
|
Args
|
||||||
owners_after (Optional(list)): base58 encoded public key of the owner of the digital asset after
|
owners_after (Optional(list)): base58 encoded public key of the
|
||||||
this transaction.
|
owner of the digital asset after this transaction.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.fulfillment = fulfillment
|
self.fulfillment = fulfillment
|
||||||
@ -234,48 +245,9 @@ class Transaction(object):
|
|||||||
ALLOWED_OPERATIONS = (CREATE, TRANSFER, GENESIS)
|
ALLOWED_OPERATIONS = (CREATE, TRANSFER, GENESIS)
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
|
||||||
def __init__(self, operation, fulfillments=None, conditions=None, data=None, timestamp=None, version=None):
|
def __init__(self, operation, fulfillments=None, conditions=None,
|
||||||
|
data=None, timestamp=None, version=None):
|
||||||
# TODO: Update this comment
|
# TODO: Update this comment
|
||||||
"""Create a new transaction in memory
|
|
||||||
|
|
||||||
A transaction in BigchainDB is a transfer of a digital asset between two entities represented
|
|
||||||
by public keys.
|
|
||||||
|
|
||||||
Currently BigchainDB supports two types of operations:
|
|
||||||
|
|
||||||
`CREATE` - Only federation nodes are allowed to use this operation. In a create operation
|
|
||||||
a federation node creates a digital asset in BigchainDB and assigns that asset to a public
|
|
||||||
key. The owner of the private key can then decided to transfer this digital asset by using the
|
|
||||||
`transaction id` of the transaction as an input in a `TRANSFER` transaction.
|
|
||||||
|
|
||||||
`TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.
|
|
||||||
|
|
||||||
If a transaction is initialized with the inputs being `None` a `operation` `CREATE` is
|
|
||||||
chosen. Otherwise the transaction is of `operation` `TRANSFER`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
# TODO: Write a description here
|
|
||||||
fulfillments
|
|
||||||
conditions
|
|
||||||
operation
|
|
||||||
data (Optional[dict]): dictionary with information about asset.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
TypeError: if the optional ``data`` argument is not a ``dict``.
|
|
||||||
|
|
||||||
# TODO: Incorporate this text somewhere better in the docs of this class
|
|
||||||
Some use cases for this class:
|
|
||||||
|
|
||||||
1. Create a new `CREATE` transaction:
|
|
||||||
- This means `inputs` is empty
|
|
||||||
|
|
||||||
2. Create a new `TRANSFER` transaction:
|
|
||||||
- This means `inputs` is a filled list (one to multiple transactions)
|
|
||||||
|
|
||||||
3. Written transactions must be managed somehow in the user's program: use `from_dict`
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.timestamp = timestamp if timestamp is not None else gen_timestamp()
|
self.timestamp = timestamp if timestamp is not None else gen_timestamp()
|
||||||
self.version = version if version is not None else Transaction.VERSION
|
self.version = version if version is not None else Transaction.VERSION
|
||||||
|
|
||||||
@ -397,7 +369,11 @@ class Transaction(object):
|
|||||||
return cls(cls.TRANSFER, inputs, conditions, data)
|
return cls(cls.TRANSFER, inputs, conditions, data)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.to_dict() == other.to_dict()
|
try:
|
||||||
|
other = other.to_dict()
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
return self.to_dict() == other
|
||||||
|
|
||||||
def to_inputs(self, condition_indices=None):
|
def to_inputs(self, condition_indices=None):
|
||||||
inputs = []
|
inputs = []
|
||||||
@ -428,6 +404,8 @@ class Transaction(object):
|
|||||||
self.conditions.append(condition)
|
self.conditions.append(condition)
|
||||||
|
|
||||||
def sign(self, private_keys):
|
def sign(self, private_keys):
|
||||||
|
# TODO: Singing should be possible with at least one of all private
|
||||||
|
# keys supplied to this method.
|
||||||
""" Signs a transaction
|
""" Signs a transaction
|
||||||
Acts as a proxy for `_sign_fulfillments`, for exposing a nicer API to the outside.
|
Acts as a proxy for `_sign_fulfillments`, for exposing a nicer API to the outside.
|
||||||
"""
|
"""
|
||||||
@ -460,6 +438,9 @@ class Transaction(object):
|
|||||||
self._sign_simple_signature_fulfillment(fulfillment, index, tx_serialized, key_pairs)
|
self._sign_simple_signature_fulfillment(fulfillment, index, tx_serialized, key_pairs)
|
||||||
elif isinstance(fulfillment.fulfillment, ThresholdSha256Fulfillment):
|
elif isinstance(fulfillment.fulfillment, ThresholdSha256Fulfillment):
|
||||||
self._sign_threshold_signature_fulfillment(fulfillment, index, tx_serialized, key_pairs)
|
self._sign_threshold_signature_fulfillment(fulfillment, index, tx_serialized, key_pairs)
|
||||||
|
else:
|
||||||
|
raise ValueError("Fulfillment couldn't be matched to "
|
||||||
|
'Cryptocondition fulfillment type.')
|
||||||
|
|
||||||
def _sign_simple_signature_fulfillment(self, fulfillment, index, tx_serialized, key_pairs):
|
def _sign_simple_signature_fulfillment(self, fulfillment, index, tx_serialized, key_pairs):
|
||||||
# NOTE: To eliminate the dangers of accidentially signing a condition by reference,
|
# NOTE: To eliminate the dangers of accidentially signing a condition by reference,
|
||||||
@ -574,12 +555,14 @@ class Transaction(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_signatures(tx_dict):
|
def _remove_signatures(tx_dict):
|
||||||
# NOTE: We remove the reference since we need `tx_dict` only for the transaction's hash
|
# NOTE: We remove the reference since we need `tx_dict` only for the
|
||||||
|
# transaction's hash
|
||||||
tx_dict = deepcopy(tx_dict)
|
tx_dict = deepcopy(tx_dict)
|
||||||
for fulfillment in tx_dict['transaction']['fulfillments']:
|
for fulfillment in tx_dict['transaction']['fulfillments']:
|
||||||
# NOTE: Not all Cryptoconditions return a `signature` key (e.g. ThresholdSha256Fulfillment), so setting it
|
# NOTE: Not all Cryptoconditions return a `signature` key (e.g.
|
||||||
# to `None` in any case could yield incorrect signatures. This is why we only set it to `None` if
|
# ThresholdSha256Fulfillment), so setting it to `None` in any case
|
||||||
# it's set in the dict.
|
# could yield incorrect signatures. This is why we only set it to
|
||||||
|
# `None` if it's set in the dict.
|
||||||
fulfillment['fulfillment'] = None
|
fulfillment['fulfillment'] = None
|
||||||
return tx_dict
|
return tx_dict
|
||||||
|
|
||||||
@ -599,7 +582,8 @@ class Transaction(object):
|
|||||||
return serialize(value)
|
return serialize(value)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return Transaction._to_str(self.to_dict())
|
tx = Transaction._remove_signatures(self.to_dict())
|
||||||
|
return Transaction._to_str(tx)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
# TODO: Make this method more pretty
|
# TODO: Make this method more pretty
|
||||||
|
20
util.py
20
util.py
@ -8,7 +8,7 @@ def gen_timestamp():
|
|||||||
See https://en.wikipedia.org/wiki/Unix_time
|
See https://en.wikipedia.org/wiki/Unix_time
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: the Unix time
|
str: the Unix time
|
||||||
"""
|
"""
|
||||||
return str(round(time.time()))
|
return str(round(time.time()))
|
||||||
|
|
||||||
@ -24,10 +24,22 @@ def serialize(data):
|
|||||||
differences.
|
differences.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (dict): dict to serialize
|
data (dict): dict to serialize
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: JSON formatted string
|
str: JSON formatted string
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, sort_keys=True)
|
return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, sort_keys=True)
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize(data):
|
||||||
|
"""Deserialize a JSON formatted string into a dict.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (str): JSON formatted string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: dict resulting from the serialization of a JSON formatted string.
|
||||||
|
"""
|
||||||
|
return rapidjson.loads(data)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user