mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
cc-based escrow
This commit is contained in:
parent
3daa153878
commit
9875cab3b3
@ -238,6 +238,23 @@ class Bigchain(object):
|
||||
.run(self.conn)
|
||||
owned = []
|
||||
|
||||
def owner_in_subfulfillments(condition_details, _owner):
|
||||
if 'subfulfillments' in condition_details:
|
||||
result = owner_in_subfulfillments(condition_details['subfulfillments'], _owner)
|
||||
if result:
|
||||
return True
|
||||
|
||||
elif isinstance(condition_details, list):
|
||||
for _subfulfillment in condition_details:
|
||||
result = owner_in_subfulfillments(_subfulfillment, _owner)
|
||||
if result:
|
||||
return True
|
||||
else:
|
||||
if 'public_key' in condition_details \
|
||||
and _owner == condition_details['public_key']:
|
||||
return True
|
||||
return False
|
||||
|
||||
for tx in response:
|
||||
# a transaction can contain multiple outputs (conditions) so we need to iterate over all of them
|
||||
# to get a list of outputs available to spend
|
||||
@ -251,9 +268,8 @@ class Bigchain(object):
|
||||
# for transactions with multiple `new_owners` there will be several subfulfillments nested
|
||||
# in the condition. We need to iterate the subfulfillments to make sure there is a
|
||||
# subfulfillment for `owner`
|
||||
for subfulfillment in condition['condition']['details']['subfulfillments']:
|
||||
if subfulfillment['public_key'] == owner:
|
||||
tx_input = {'txid': tx['id'], 'cid': condition['cid']}
|
||||
if owner_in_subfulfillments(condition['condition']['details'], owner):
|
||||
tx_input = {'txid': tx['id'], 'cid': condition['cid']}
|
||||
# check if input was already spent
|
||||
if not self.get_spent(tx_input):
|
||||
owned.append(tx_input)
|
||||
|
@ -447,7 +447,8 @@ def validate_fulfillments(signed_transaction):
|
||||
return False
|
||||
|
||||
# TODO: might already break on a False here
|
||||
is_valid = parsed_fulfillment.validate(serialize(fulfillment_message))
|
||||
is_valid = parsed_fulfillment.validate(message=serialize(fulfillment_message),
|
||||
now=timestamp())
|
||||
|
||||
# if transaction has an input (i.e. not a `CREATE` transaction)
|
||||
# TODO: avoid instantiation, pass as argument!
|
||||
|
2
setup.py
2
setup.py
@ -96,7 +96,7 @@ setup(
|
||||
'rethinkdb==2.3.0',
|
||||
'pysha3==0.3',
|
||||
'pytz==2015.7',
|
||||
'cryptoconditions==0.2.2',
|
||||
'cryptoconditions==0.3.0',
|
||||
'statsd==3.2.1',
|
||||
'python-rapidjson==0.0.6',
|
||||
'logstats==0.2.1',
|
||||
|
@ -1878,3 +1878,269 @@ class TestCryptoconditions(object):
|
||||
for new_owner in new_owners:
|
||||
subcondition = condition.get_subcondition_from_vk(new_owner)[0]
|
||||
assert subcondition.public_key.to_ascii().decode() == new_owner
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_transfer_asset_with_escrow_condition(self, b, user_vk, user_sk):
|
||||
first_input_tx = b.get_owned_ids(user_vk).pop()
|
||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||
|
||||
escrow_tx = b.create_transaction(user_vk, [user_vk, user2_vk], first_input_tx, 'TRANSFER')
|
||||
|
||||
time_sleep = 3
|
||||
|
||||
condition_escrow = cc.ThresholdSha256Fulfillment(threshold=1)
|
||||
fulfillment_timeout = cc.TimeoutFulfillment(expire_time=str(float(util.timestamp()) + time_sleep))
|
||||
condition_user = cc.Ed25519Fulfillment(public_key=user_vk)
|
||||
condition_user2 = cc.Ed25519Fulfillment(public_key=user2_vk)
|
||||
|
||||
# execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_execute.add_subfulfillment(condition_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_abort.add_subfulfillment(condition_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
|
||||
condition_escrow.add_subfulfillment(fulfillment_and_execute)
|
||||
condition_escrow.add_subfulfillment(fulfillment_and_abort)
|
||||
|
||||
# Update the condition in the newly created transaction
|
||||
escrow_tx['transaction']['conditions'][0]['condition'] = {
|
||||
'details': json.loads(condition_escrow.serialize_json()),
|
||||
'uri': condition_escrow.condition.serialize_uri()
|
||||
}
|
||||
|
||||
# conditions have been updated, so hash needs updating
|
||||
escrow_tx['id'] = util.get_hash_data(escrow_tx)
|
||||
|
||||
escrow_tx_signed = b.sign_transaction(escrow_tx, user_sk)
|
||||
|
||||
assert b.validate_transaction(escrow_tx_signed) == escrow_tx_signed
|
||||
assert b.is_valid_transaction(escrow_tx_signed) == escrow_tx_signed
|
||||
|
||||
b.write_transaction(escrow_tx_signed)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([escrow_tx_signed])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
# create hashlock fulfillment tx
|
||||
# Retrieve the last transaction of thresholduser1_pub
|
||||
tx_retrieved_id = b.get_owned_ids(user2_vk).pop()
|
||||
|
||||
# EXECUTE
|
||||
|
||||
# Create a base template for output transaction
|
||||
escrow_tx_transfer = b.create_transaction([user_vk, user2_vk], user2_vk, tx_retrieved_id, 'TRANSFER')
|
||||
|
||||
# Parse the threshold cryptocondition
|
||||
escrow_fulfillment = cc.Fulfillment.from_json(
|
||||
escrow_tx['transaction']['conditions'][0]['condition']['details'])
|
||||
|
||||
subfulfillment_user = escrow_fulfillment.get_subcondition_from_vk(user_vk)[0]
|
||||
subfulfillment_user2 = escrow_fulfillment.get_subcondition_from_vk(user2_vk)[0]
|
||||
|
||||
# Get the fulfillment message to sign
|
||||
escrow_tx_fulfillment_message = util.get_fulfillment_message(escrow_tx_transfer,
|
||||
escrow_tx_transfer['transaction']['fulfillments'][0],
|
||||
serialized=True)
|
||||
escrow_fulfillment.subconditions = []
|
||||
# fulfill execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
subfulfillment_user2.sign(escrow_tx_fulfillment_message, crypto.SigningKey(user2_sk))
|
||||
fulfillment_and_execute.add_subfulfillment(subfulfillment_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
escrow_fulfillment.add_subfulfillment(fulfillment_and_execute)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_abort.add_subfulfillment(subfulfillment_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
escrow_fulfillment.add_subcondition(fulfillment_and_abort.condition)
|
||||
|
||||
escrow_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = escrow_fulfillment.serialize_uri()
|
||||
|
||||
# in-time validation (execute)
|
||||
assert b.is_valid_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
assert b.validate_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
|
||||
time.sleep(time_sleep)
|
||||
|
||||
assert b.is_valid_transaction(escrow_tx_transfer) is False
|
||||
with pytest.raises(exceptions.InvalidSignature):
|
||||
assert b.validate_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
|
||||
# ABORT
|
||||
|
||||
# Create a base template for output transaction
|
||||
escrow_tx_abort = b.create_transaction([user_vk, user2_vk], user_vk, tx_retrieved_id, 'TRANSFER')
|
||||
|
||||
# Parse the threshold cryptocondition
|
||||
escrow_fulfillment = cc.Fulfillment.from_json(
|
||||
escrow_tx['transaction']['conditions'][0]['condition']['details'])
|
||||
|
||||
subfulfillment_user = escrow_fulfillment.get_subcondition_from_vk(user_vk)[0]
|
||||
subfulfillment_user2 = escrow_fulfillment.get_subcondition_from_vk(user2_vk)[0]
|
||||
|
||||
# Get the fulfillment message to sign
|
||||
escrow_tx_fulfillment_message = util.get_fulfillment_message(escrow_tx_abort,
|
||||
escrow_tx_abort['transaction']['fulfillments'][0],
|
||||
serialized=True)
|
||||
escrow_fulfillment.subconditions = []
|
||||
# fulfill execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_execute.add_subfulfillment(subfulfillment_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
escrow_fulfillment.add_subcondition(fulfillment_and_execute.condition)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
subfulfillment_user.sign(escrow_tx_fulfillment_message, crypto.SigningKey(user_sk))
|
||||
fulfillment_and_abort.add_subfulfillment(subfulfillment_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
escrow_fulfillment.add_subfulfillment(fulfillment_and_abort)
|
||||
|
||||
escrow_tx_abort['transaction']['fulfillments'][0]['fulfillment'] = escrow_fulfillment.serialize_uri()
|
||||
|
||||
# out-of-time validation (abort)
|
||||
assert b.validate_transaction(escrow_tx_abort) == escrow_tx_abort
|
||||
assert b.is_valid_transaction(escrow_tx_abort) == escrow_tx_abort
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_transfer_asset_with_escrow_condition_doublespend(self, b, user_vk, user_sk):
|
||||
first_input_tx = b.get_owned_ids(user_vk).pop()
|
||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||
|
||||
escrow_tx = b.create_transaction(user_vk, [user_vk, user2_vk], first_input_tx, 'TRANSFER')
|
||||
|
||||
time_sleep = 3
|
||||
|
||||
condition_escrow = cc.ThresholdSha256Fulfillment(threshold=1)
|
||||
fulfillment_timeout = cc.TimeoutFulfillment(expire_time=str(float(util.timestamp()) + time_sleep))
|
||||
condition_user = cc.Ed25519Fulfillment(public_key=user_vk)
|
||||
condition_user2 = cc.Ed25519Fulfillment(public_key=user2_vk)
|
||||
|
||||
# execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_execute.add_subfulfillment(condition_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_abort.add_subfulfillment(condition_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
|
||||
condition_escrow.add_subfulfillment(fulfillment_and_execute)
|
||||
condition_escrow.add_subfulfillment(fulfillment_and_abort)
|
||||
|
||||
# Update the condition in the newly created transaction
|
||||
escrow_tx['transaction']['conditions'][0]['condition'] = {
|
||||
'details': json.loads(condition_escrow.serialize_json()),
|
||||
'uri': condition_escrow.condition.serialize_uri()
|
||||
}
|
||||
|
||||
# conditions have been updated, so hash needs updating
|
||||
escrow_tx['id'] = util.get_hash_data(escrow_tx)
|
||||
|
||||
escrow_tx_signed = b.sign_transaction(escrow_tx, user_sk)
|
||||
|
||||
assert b.validate_transaction(escrow_tx_signed) == escrow_tx_signed
|
||||
assert b.is_valid_transaction(escrow_tx_signed) == escrow_tx_signed
|
||||
|
||||
b.write_transaction(escrow_tx_signed)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([escrow_tx_signed])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
# create hashlock fulfillment tx
|
||||
# Retrieve the last transaction of thresholduser1_pub
|
||||
tx_retrieved_id = b.get_owned_ids(user2_vk).pop()
|
||||
|
||||
# EXECUTE
|
||||
|
||||
# Create a base template for output transaction
|
||||
escrow_tx_transfer = b.create_transaction([user_vk, user2_vk], user2_vk, tx_retrieved_id, 'TRANSFER')
|
||||
|
||||
# Parse the threshold cryptocondition
|
||||
escrow_fulfillment = cc.Fulfillment.from_json(
|
||||
escrow_tx['transaction']['conditions'][0]['condition']['details'])
|
||||
|
||||
subfulfillment_user = escrow_fulfillment.get_subcondition_from_vk(user_vk)[0]
|
||||
subfulfillment_user2 = escrow_fulfillment.get_subcondition_from_vk(user2_vk)[0]
|
||||
|
||||
# Get the fulfillment message to sign
|
||||
escrow_tx_fulfillment_message = util.get_fulfillment_message(escrow_tx_transfer,
|
||||
escrow_tx_transfer['transaction']['fulfillments'][0],
|
||||
serialized=True)
|
||||
escrow_fulfillment.subconditions = []
|
||||
# fulfill execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
subfulfillment_user2.sign(escrow_tx_fulfillment_message, crypto.SigningKey(user2_sk))
|
||||
fulfillment_and_execute.add_subfulfillment(subfulfillment_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
escrow_fulfillment.add_subfulfillment(fulfillment_and_execute)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_abort.add_subfulfillment(subfulfillment_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
escrow_fulfillment.add_subcondition(fulfillment_and_abort.condition)
|
||||
|
||||
escrow_tx_transfer['transaction']['fulfillments'][0]['fulfillment'] = escrow_fulfillment.serialize_uri()
|
||||
|
||||
# in-time validation (execute)
|
||||
assert b.is_valid_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
assert b.validate_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
|
||||
b.write_transaction(escrow_tx_transfer)
|
||||
|
||||
# create and write block to bigchain
|
||||
block = b.create_block([escrow_tx_transfer])
|
||||
b.write_block(block, durability='hard')
|
||||
|
||||
time.sleep(time_sleep)
|
||||
|
||||
assert b.is_valid_transaction(escrow_tx_transfer) is False
|
||||
with pytest.raises(exceptions.InvalidSignature):
|
||||
assert b.validate_transaction(escrow_tx_transfer) == escrow_tx_transfer
|
||||
|
||||
# ABORT
|
||||
|
||||
# Create a base template for output transaction
|
||||
escrow_tx_abort = b.create_transaction([user_vk, user2_vk], user_vk, tx_retrieved_id, 'TRANSFER')
|
||||
|
||||
# Parse the threshold cryptocondition
|
||||
escrow_fulfillment = cc.Fulfillment.from_json(
|
||||
escrow_tx['transaction']['conditions'][0]['condition']['details'])
|
||||
|
||||
subfulfillment_user = escrow_fulfillment.get_subcondition_from_vk(user_vk)[0]
|
||||
subfulfillment_user2 = escrow_fulfillment.get_subcondition_from_vk(user2_vk)[0]
|
||||
|
||||
# Get the fulfillment message to sign
|
||||
escrow_tx_fulfillment_message = util.get_fulfillment_message(escrow_tx_abort,
|
||||
escrow_tx_abort['transaction']['fulfillments'][0],
|
||||
serialized=True)
|
||||
escrow_fulfillment.subconditions = []
|
||||
# fulfill execute branch
|
||||
fulfillment_and_execute = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
fulfillment_and_execute.add_subfulfillment(subfulfillment_user2)
|
||||
fulfillment_and_execute.add_subfulfillment(fulfillment_timeout)
|
||||
escrow_fulfillment.add_subcondition(fulfillment_and_execute.condition)
|
||||
|
||||
# do not fulfill abort branch
|
||||
fulfillment_and_abort = cc.ThresholdSha256Fulfillment(threshold=2)
|
||||
subfulfillment_user.sign(escrow_tx_fulfillment_message, crypto.SigningKey(user_sk))
|
||||
fulfillment_and_abort.add_subfulfillment(subfulfillment_user)
|
||||
fulfillment_and_abort.add_subfulfillment(fulfillment_timeout, weight=-1)
|
||||
escrow_fulfillment.add_subfulfillment(fulfillment_and_abort)
|
||||
|
||||
escrow_tx_abort['transaction']['fulfillments'][0]['fulfillment'] = escrow_fulfillment.serialize_uri()
|
||||
|
||||
# out-of-time validation (abort)
|
||||
with pytest.raises(exceptions.DoubleSpend):
|
||||
b.validate_transaction(escrow_tx_abort)
|
||||
assert b.is_valid_transaction(escrow_tx_abort) is False
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user