mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Create transfer-tx interface
This commit is contained in:
parent
71b7eaed3e
commit
e41ccae6bd
114
transaction.py
114
transaction.py
@ -147,6 +147,44 @@ class Condition(object):
|
|||||||
cond['cid'] = cid
|
cond['cid'] = cid
|
||||||
return cond
|
return cond
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate(cls, owners_after):
|
||||||
|
if not isinstance(owners_after, list):
|
||||||
|
raise TypeError('`owners_after` must be an instance of list')
|
||||||
|
|
||||||
|
if len(owners_after) == 0:
|
||||||
|
raise ValueError('`owners_after` needs to contain at least one'
|
||||||
|
'owner')
|
||||||
|
elif len(owners_after) == 1 and not isinstance(owners_after[0], list):
|
||||||
|
try:
|
||||||
|
ffill = Ed25519Fulfillment(public_key=owners_after[0])
|
||||||
|
except TypeError:
|
||||||
|
ffill = owners_after[0]
|
||||||
|
return cls(ffill, owners_after)
|
||||||
|
else:
|
||||||
|
threshold = ThresholdSha256Fulfillment(threshold=len(owners_after))
|
||||||
|
return cls(reduce(cls._gen_condition, owners_after, threshold),
|
||||||
|
owners_after)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _gen_condition(cls, initial, current):
|
||||||
|
if isinstance(current, list) and len(current) > 1:
|
||||||
|
ffill = ThresholdSha256Fulfillment(threshold=len(current))
|
||||||
|
reduce(cls._gen_condition, current, ffill)
|
||||||
|
elif isinstance(current, list) and len(current) <= 1:
|
||||||
|
raise ValueError('Sublist cannot contain single owner')
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
current = current.pop()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
ffill = Ed25519Fulfillment(public_key=current)
|
||||||
|
except TypeError:
|
||||||
|
ffill = current
|
||||||
|
initial.add_subfulfillment(ffill)
|
||||||
|
return initial
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, cond):
|
def from_dict(cls, cond):
|
||||||
# TODO: This case should have it's own serialization test
|
# TODO: This case should have it's own serialization test
|
||||||
@ -279,29 +317,23 @@ class Transaction(object):
|
|||||||
# NOTE: For this case its sufficient to use the same
|
# NOTE: For this case its sufficient to use the same
|
||||||
# fulfillment for the fulfillment and condition.
|
# fulfillment for the fulfillment and condition.
|
||||||
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
||||||
ffill_for_cond = Ed25519Fulfillment(public_key=owners_after[0])
|
|
||||||
ffill_tx = Fulfillment(ffill, owners_before)
|
ffill_tx = Fulfillment(ffill, owners_before)
|
||||||
cond_tx = Condition(ffill_for_cond, owners_after)
|
cond_tx = Condition.generate(owners_after)
|
||||||
return cls(cls.CREATE, [ffill_tx], [cond_tx], data)
|
return cls(cls.CREATE, [ffill_tx], [cond_tx], data)
|
||||||
|
|
||||||
elif len(owners_before) == len(owners_after) and len(owners_after) > 1:
|
elif len(owners_before) == len(owners_after) and len(owners_after) > 1:
|
||||||
# NOTE: Multiple inputs and outputs case.
|
raise NotImplementedError('Multiple inputs and outputs not'
|
||||||
|
'available for CREATE')
|
||||||
|
# NOTE: Multiple inputs and outputs case. Currently not supported.
|
||||||
ffills = [Fulfillment(Ed25519Fulfillment(public_key=owner_before),
|
ffills = [Fulfillment(Ed25519Fulfillment(public_key=owner_before),
|
||||||
[owner_before])
|
[owner_before])
|
||||||
for owner_before in owners_before]
|
for owner_before in owners_before]
|
||||||
conds = [Condition(Ed25519Fulfillment(public_key=owner_after),
|
conds = [Condition.generate(owners) for owners in owners_after]
|
||||||
[owner_after])
|
|
||||||
for owner_after in owners_after]
|
|
||||||
return cls(cls.CREATE, ffills, conds, data)
|
return cls(cls.CREATE, ffills, conds, data)
|
||||||
|
|
||||||
elif len(owners_before) == 1 and len(owners_after) > 1:
|
elif len(owners_before) == 1 and len(owners_after) > 1:
|
||||||
# NOTE: Multiple owners case
|
# NOTE: Multiple owners case
|
||||||
threshold = ThresholdSha256Fulfillment(threshold=len(owners_after))
|
cond_tx = Condition.generate(owners_after)
|
||||||
for owner_after in owners_after:
|
|
||||||
threshold.add_subfulfillment(Ed25519Fulfillment(public_key=owner_after))
|
|
||||||
cond_tx = Condition(threshold, owners_after)
|
|
||||||
# TODO: Is this correct? Can I fulfill a threshold condition in
|
|
||||||
# a create? I guess so?!
|
|
||||||
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
||||||
ffill_tx = Fulfillment(ffill, owners_before)
|
ffill_tx = Fulfillment(ffill, owners_before)
|
||||||
return cls(cls.CREATE, [ffill_tx], [cond_tx], data)
|
return cls(cls.CREATE, [ffill_tx], [cond_tx], data)
|
||||||
@ -321,35 +353,53 @@ class Transaction(object):
|
|||||||
raise ValueError('Define a secret to create a hashlock condition')
|
raise ValueError('Define a secret to create a hashlock condition')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("This is not the case you're looking for ;)")
|
raise ValueError("This is not the cases you're looking for ;)")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def transfer(cls, inputs, owners_after, payload=None, secret=None, time_expiry=None):
|
def transfer(cls, inputs, owners_after, payload=None):
|
||||||
if not isinstance(inputs, list):
|
if not isinstance(inputs, list):
|
||||||
raise TypeError('`inputs` must be a list instance')
|
raise TypeError('`inputs` must be a list instance')
|
||||||
|
if len(inputs) == 0:
|
||||||
|
raise ValueError('`inputs` must contain at least one item')
|
||||||
if not isinstance(owners_after, list):
|
if not isinstance(owners_after, list):
|
||||||
raise TypeError('`owners_after` must be a list instance')
|
raise TypeError('`owners_after` must be a list instance')
|
||||||
|
|
||||||
data = Data(payload)
|
#
|
||||||
|
#
|
||||||
|
# NOTE: Different cases for threshold conditions:
|
||||||
|
#
|
||||||
|
# Combining multiple `inputs` with an arbitrary number of
|
||||||
|
# `owners_after` can yield interesting cases for the creation of
|
||||||
|
# threshold conditions we'd like to support. The following
|
||||||
|
# notation is proposed:
|
||||||
|
#
|
||||||
|
# 1. The index of an `owner_after` corresponds to the index of
|
||||||
|
# an input:
|
||||||
|
# e.g. `transfer([input1], [a])`, means `input1` would now be
|
||||||
|
# owned by user `a`.
|
||||||
|
#
|
||||||
|
# 2. `owners_after` can (almost) get arbitrary deeply nested,
|
||||||
|
# creating various complex threshold conditions:
|
||||||
|
# e.g. `transfer([inp1, inp2], [[a, [b, c]], d])`, means
|
||||||
|
# `a`'s signature would have a 50% weight on `inp1`
|
||||||
|
# compared to `b` and `c` that share 25% of the leftover
|
||||||
|
# weight respectively. `inp2` is owned completely by `d`.
|
||||||
|
#
|
||||||
|
#
|
||||||
if len(inputs) == len(owners_after) and len(owners_after) == 1:
|
if len(inputs) == len(owners_after) and len(owners_after) == 1:
|
||||||
# NOTE: Standard case, one input and one output
|
conditions = [Condition.generate(owners_after)]
|
||||||
ffill_for_cond = Ed25519Fulfillment(public_key=owners_after[0])
|
|
||||||
cond_tx = Condition(ffill_for_cond, owners_after)
|
|
||||||
return cls(cls.TRANSFER, inputs, [cond_tx], data)
|
|
||||||
|
|
||||||
elif len(inputs) == 1 and len(owners_after) > 1:
|
|
||||||
# NOTE: Threshold condition case
|
|
||||||
threshold = ThresholdSha256Fulfillment(threshold=len(owners_after))
|
|
||||||
for owner_after in owners_after:
|
|
||||||
threshold.add_subfulfillment(Ed25519Fulfillment(public_key=owner_after))
|
|
||||||
cond_tx = Condition(threshold, owners_after)
|
|
||||||
return cls(cls.TRANSFER, inputs, [cond_tx], data)
|
|
||||||
|
|
||||||
elif len(inputs) == len(owners_after) and len(owners_after) > 1:
|
elif len(inputs) == len(owners_after) and len(owners_after) > 1:
|
||||||
# NOTE: Multiple inputs and outputs and threshold
|
conditions = [Condition.generate(owners) for owners
|
||||||
# even though asset could also live on as multiple inputs
|
in owners_after]
|
||||||
# and outputs
|
elif len(inputs) != len(owners_after):
|
||||||
pass
|
raise ValueError("`inputs` and `owners_after`'s count must be the "
|
||||||
|
"same")
|
||||||
|
else:
|
||||||
|
raise ValueError("This is not the cases you're looking for ;)")
|
||||||
|
|
||||||
|
data = Data(payload)
|
||||||
|
inputs = deepcopy(inputs)
|
||||||
|
return cls(cls.TRANSFER, inputs, conditions, data)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.to_dict() == other.to_dict()
|
return self.to_dict() == other.to_dict()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user