Merge remote-tracking branch 'remotes/origin/feat/127/crypto-conditions-ilp-bigchain-integration' into feat/128/multiple-input-output

This commit is contained in:
Rodolphe Marques 2016-04-12 12:21:17 +02:00
commit 43a88142f7
3 changed files with 313 additions and 46 deletions

View File

@ -148,7 +148,6 @@ class BaseConsensusRules(AbstractConsensusRules):
# check if the input was already spent by a transaction other than
# this one.
spent = bigchain.get_spent(fulfillment['input'])
print(spent)
if spent and spent['id'] != transaction['id']:
raise exceptions.DoubleSpend(
'input `{}` was already spent'.format(fulfillment['input']))

View File

@ -244,36 +244,15 @@ def sign_tx(transaction, sk):
dict: transaction with the `fulfillment` fields populated.
"""
b = bigchaindb.Bigchain()
sk = crypto.SigningKey(sk)
tx = copy.deepcopy(transaction)
common_data = {
'operation': tx['transaction']['operation'],
'timestamp': tx['transaction']['timestamp'],
'data': tx['transaction']['data'],
'version': tx['version'],
'id': tx['id']
}
for fulfillment in tx['transaction']['fulfillments']:
fulfillment_message = common_data.copy()
fulfillment_message = get_fulfillment_message(transaction, fulfillment)
if tx['transaction']['operation'] in ['CREATE', 'GENESIS']:
fulfillment_message.update({
'input': None,
'condition': None
})
# sign the fulfillment message
parsed_fulfillment = Ed25519Fulfillment(public_key=sk.get_verifying_key())
else:
# get previous condition
previous_tx = b.get_transaction(fulfillment['input']['txid'])
conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid'])
# update the fulfillment message
fulfillment_message.update({
'input': fulfillment['input'],
'condition': conditions[fulfillment['fid']]
})
parsed_fulfillment = Fulfillment.from_json(fulfillment_message['condition']['condition']['details'])
parsed_fulfillment.sign(serialize(fulfillment_message), sk)
signed_fulfillment = parsed_fulfillment.serialize_uri()
@ -311,30 +290,8 @@ def verify_signature(signed_transaction):
bool: True if the signature is correct, False otherwise.
"""
b = bigchaindb.Bigchain()
common_data = {
'operation': signed_transaction['transaction']['operation'],
'timestamp': signed_transaction['transaction']['timestamp'],
'data': signed_transaction['transaction']['data'],
'version': signed_transaction['version'],
'id': signed_transaction['id']
}
for fulfillment in signed_transaction['transaction']['fulfillments']:
fulfillment_message = common_data.copy()
fulfillment_message.update({
'input': fulfillment['input'],
'condition': None,
})
# if not a `CREATE` transaction
if fulfillment['input']:
# get previous condition
previous_tx = b.get_transaction(fulfillment['input']['txid'])
conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid'])
fulfillment_message['condition'] = conditions[fulfillment['fid']]
fulfillment_message = get_fulfillment_message(signed_transaction, fulfillment)
# verify the fulfillment (for now lets assume there is only one owner)
try:
parsed_fulfillment = Fulfillment.from_uri(fulfillment['fulfillment'])
@ -352,6 +309,32 @@ def verify_signature(signed_transaction):
return True
def get_fulfillment_message(transaction, fulfillment):
b = bigchaindb.Bigchain()
common_data = {
'operation': transaction['transaction']['operation'],
'timestamp': transaction['transaction']['timestamp'],
'data': transaction['transaction']['data'],
'version': transaction['version'],
'id': transaction['id']
}
fulfillment_message = common_data.copy()
fulfillment_message.update({
'input': fulfillment['input'],
'condition': None,
})
# if not a `CREATE` transaction
if fulfillment['input']:
# get previous condition
previous_tx = b.get_transaction(fulfillment['input']['txid'])
conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid'])
fulfillment_message['condition'] = conditions[fulfillment['fid']]
return fulfillment_message
def transform_create(tx):
"""Change the owner and signature for a ``CREATE`` transaction created by a node"""

View File

@ -1,9 +1,14 @@
import copy
import multiprocessing as mp
import random
import time
import json
import pytest
import rethinkdb as r
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
from cryptoconditions.condition import Condition
from cryptoconditions.fulfillment import Fulfillment
import bigchaindb
from bigchaindb import util
@ -255,6 +260,14 @@ class TestTransactionValidation(object):
assert excinfo.value.args[0] == 'Only federation nodes can use the operation `CREATE`'
assert b.is_valid_transaction(tx) is False
tx_signed = b.sign_transaction(tx, b.me_private)
with pytest.raises(exceptions.OperationError) as excinfo:
b.validate_transaction(tx_signed)
assert excinfo.value.args[0] == 'Only federation nodes can use the operation `CREATE`'
assert b.is_valid_transaction(tx_signed) is False
def test_non_create_operation_no_inputs(self, b, user_vk):
tx = b.create_transaction(user_vk, user_vk, None, 'TRANSFER')
with pytest.raises(ValueError) as excinfo:
@ -749,3 +762,275 @@ class TestMultipleInputs(object):
def test_get_owned_ids(self, b):
pass
class TestCryptoconditions(object):
def test_fulfillment_transaction_create(self, b, user_vk):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
condition = tx['transaction']['conditions'][0]['condition']
condition_from_uri = Condition.from_uri(condition['uri'])
condition_from_json = Fulfillment.from_json(condition['details']).condition
assert condition_from_uri.serialize_uri() == condition_from_json.serialize_uri()
assert condition['details']['public_key'] == user_vk
tx_signed = b.sign_transaction(tx, b.me_private)
fulfillment = tx_signed['transaction']['fulfillments'][0]
fulfillment_from_uri = Fulfillment.from_uri(fulfillment['fulfillment'])
assert fulfillment['current_owners'][0] == b.me
assert fulfillment_from_uri.public_key.to_ascii().decode() == b.me
assert b.verify_signature(tx_signed) == True
assert b.is_valid_transaction(tx_signed) == tx_signed
@pytest.mark.usefixtures('inputs')
def test_fulfillment_transaction_transfer(self, b, user_vk, user_sk):
# create valid transaction
other_sk, other_vk = crypto.generate_key_pair()
prev_tx_id = b.get_owned_ids(user_vk).pop()
tx = b.create_transaction(user_vk, other_vk, prev_tx_id, 'TRANSFER')
prev_tx = b.get_transaction(prev_tx_id['txid'])
prev_condition = prev_tx['transaction']['conditions'][0]['condition']
prev_condition_from_uri = Condition.from_uri(prev_condition['uri'])
prev_condition_from_json = Fulfillment.from_json(prev_condition['details']).condition
assert prev_condition_from_uri.serialize_uri() == prev_condition_from_json.serialize_uri()
assert prev_condition['details']['public_key'] == user_vk
condition = tx['transaction']['conditions'][0]['condition']
condition_from_uri = Condition.from_uri(condition['uri'])
condition_from_json = Fulfillment.from_json(condition['details']).condition
assert condition_from_uri.serialize_uri() == condition_from_json.serialize_uri()
assert condition['details']['public_key'] == other_vk
tx_signed = b.sign_transaction(tx, user_sk)
fulfillment = tx_signed['transaction']['fulfillments'][0]
fulfillment_from_uri = Fulfillment.from_uri(fulfillment['fulfillment'])
assert fulfillment['current_owners'][0] == user_vk
assert fulfillment_from_uri.public_key.to_ascii().decode() == user_vk
assert fulfillment_from_uri.condition.serialize_uri() == prev_condition['uri']
assert b.verify_signature(tx_signed) == True
assert b.is_valid_transaction(tx_signed) == tx_signed
def test_override_condition_create(self, b, user_vk):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
fulfillment = Ed25519Fulfillment(public_key=user_vk)
tx['transaction']['conditions'][0]['condition'] = {
'details': json.loads(fulfillment.serialize_json()),
'uri': fulfillment.condition.serialize_uri()
}
tx_signed = b.sign_transaction(tx, b.me_private)
fulfillment = tx_signed['transaction']['fulfillments'][0]
fulfillment_from_uri = Fulfillment.from_uri(fulfillment['fulfillment'])
assert fulfillment['current_owners'][0] == b.me
assert fulfillment_from_uri.public_key.to_ascii().decode() == b.me
assert b.verify_signature(tx_signed) == True
assert b.is_valid_transaction(tx_signed) == tx_signed
@pytest.mark.usefixtures('inputs')
def test_override_condition_transfer(self, b, user_vk, user_sk):
# create valid transaction
other_sk, other_vk = crypto.generate_key_pair()
prev_tx_id = b.get_owned_ids(user_vk).pop()
tx = b.create_transaction(user_vk, other_vk, prev_tx_id, 'TRANSFER')
fulfillment = Ed25519Fulfillment(public_key=other_vk)
tx['transaction']['conditions'][0]['condition'] = {
'details': json.loads(fulfillment.serialize_json()),
'uri': fulfillment.condition.serialize_uri()
}
tx_signed = b.sign_transaction(tx, user_sk)
fulfillment = tx_signed['transaction']['fulfillments'][0]
fulfillment_from_uri = Fulfillment.from_uri(fulfillment['fulfillment'])
assert fulfillment['current_owners'][0] == user_vk
assert fulfillment_from_uri.public_key.to_ascii().decode() == user_vk
assert b.verify_signature(tx_signed) == True
assert b.is_valid_transaction(tx_signed) == tx_signed
def test_override_fulfillment_create(self, b, user_vk):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
original_fulfillment = tx['transaction']['fulfillments'][0]
fulfillment_message = util.get_fulfillment_message(tx, original_fulfillment)
fulfillment = Ed25519Fulfillment(public_key=b.me)
fulfillment.sign(util.serialize(fulfillment_message), crypto.SigningKey(b.me_private))
tx['transaction']['fulfillments'][0]['fulfillment'] = fulfillment.serialize_uri()
assert b.verify_signature(tx) == True
assert b.is_valid_transaction(tx) == tx
@pytest.mark.usefixtures('inputs')
def test_override_fulfillment_transfer(self, b, user_vk, user_sk):
# create valid transaction
other_sk, other_vk = crypto.generate_key_pair()
prev_tx_id = b.get_owned_ids(user_vk).pop()
tx = b.create_transaction(user_vk, other_vk, prev_tx_id, 'TRANSFER')
original_fulfillment = tx['transaction']['fulfillments'][0]
fulfillment_message = util.get_fulfillment_message(tx, original_fulfillment)
fulfillment = Ed25519Fulfillment(public_key=user_vk)
fulfillment.sign(util.serialize(fulfillment_message), crypto.SigningKey(user_sk))
tx['transaction']['fulfillments'][0]['fulfillment'] = fulfillment.serialize_uri()
assert b.verify_signature(tx) == True
assert b.is_valid_transaction(tx) == tx
@pytest.mark.usefixtures('inputs')
def test_override_condition_and_fulfillment_transfer(self, b, user_vk, user_sk):
other_sk, other_vk = crypto.generate_key_pair()
first_input_tx = b.get_owned_ids(user_vk).pop()
first_tx = b.create_transaction(user_vk, other_vk, first_input_tx, 'TRANSFER')
first_tx_condition = Ed25519Fulfillment(public_key=other_vk)
first_tx['transaction']['conditions'][0]['condition'] = {
'details': json.loads(first_tx_condition.serialize_json()),
'uri': first_tx_condition.condition.serialize_uri()
}
first_tx_fulfillment = first_tx['transaction']['fulfillments'][0]
first_tx_fulfillment_message = util.get_fulfillment_message(first_tx, first_tx_fulfillment)
first_tx_fulfillment = Ed25519Fulfillment(public_key=user_vk)
first_tx_fulfillment.sign(util.serialize(first_tx_fulfillment_message), crypto.SigningKey(user_sk))
first_tx['transaction']['fulfillments'][0]['fulfillment'] = first_tx_fulfillment.serialize_uri()
assert b.validate_transaction(first_tx)
assert b.is_valid_transaction(first_tx) == first_tx
b.write_transaction(first_tx)
# create and write block to bigchain
block = b.create_block([first_tx])
b.write_block(block, durability='hard')
next_input_tx = b.get_owned_ids(other_vk).pop()
# create another transaction with the same input
next_tx = b.create_transaction(other_vk, user_vk, next_input_tx, 'TRANSFER')
next_tx_fulfillment = next_tx['transaction']['fulfillments'][0]
next_tx_fulfillment_message = util.get_fulfillment_message(next_tx, next_tx_fulfillment)
next_tx_fulfillment = Ed25519Fulfillment(public_key=other_vk)
next_tx_fulfillment.sign(util.serialize(next_tx_fulfillment_message), crypto.SigningKey(other_sk))
next_tx['transaction']['fulfillments'][0]['fulfillment'] = next_tx_fulfillment.serialize_uri()
assert b.validate_transaction(next_tx)
assert b.is_valid_transaction(next_tx) == next_tx
@pytest.mark.usefixtures('inputs')
def test_override_condition_and_fulfillment_transfer_threshold(self, b, user_vk, user_sk):
other1_sk, other1_vk = crypto.generate_key_pair()
other2_sk, other2_vk = crypto.generate_key_pair()
first_input_tx = b.get_owned_ids(user_vk).pop()
first_tx = b.create_transaction(user_vk, [other1_vk, other2_vk], first_input_tx, 'TRANSFER')
first_tx_condition = ThresholdSha256Fulfillment(threshold=2)
first_tx_condition.add_subfulfillment(Ed25519Fulfillment(public_key=other1_vk))
first_tx_condition.add_subfulfillment(Ed25519Fulfillment(public_key=other2_vk))
first_tx['transaction']['conditions'][0]['condition'] = {
'details': json.loads(first_tx_condition.serialize_json()),
'uri': first_tx_condition.condition.serialize_uri()
}
# conditions have been updated, so hash needs updating
transaction_data = copy.deepcopy(first_tx)
for fulfillment in transaction_data['transaction']['fulfillments']:
fulfillment['fulfillment'] = None
calculated_hash = crypto.hash_data(util.serialize(transaction_data['transaction']))
first_tx['id'] = calculated_hash
first_tx_signed = b.sign_transaction(first_tx, user_sk)
assert b.validate_transaction(first_tx_signed)
assert b.is_valid_transaction(first_tx_signed) == first_tx_signed
b.write_transaction(first_tx_signed)
# create and write block to bigchain
block = b.create_block([first_tx])
b.write_block(block, durability='hard')
next_input_tx = b.get_owned_ids(other1_vk).pop()
# create another transaction with the same input
next_tx = b.create_transaction([other1_vk, other2_vk], user_vk, next_input_tx, 'TRANSFER')
next_tx_fulfillment = next_tx['transaction']['fulfillments'][0]
next_tx_fulfillment_message = util.get_fulfillment_message(next_tx, next_tx_fulfillment)
next_tx_fulfillment = ThresholdSha256Fulfillment(threshold=2)
next_tx_subfulfillment1 = Ed25519Fulfillment(public_key=other1_vk)
next_tx_subfulfillment1.sign(util.serialize(next_tx_fulfillment_message), crypto.SigningKey(other1_sk))
next_tx_fulfillment.add_subfulfillment(next_tx_subfulfillment1)
next_tx_subfulfillment2 = Ed25519Fulfillment(public_key=other2_vk)
next_tx_subfulfillment2.sign(util.serialize(next_tx_fulfillment_message), crypto.SigningKey(other2_sk))
next_tx_fulfillment.add_subfulfillment(next_tx_subfulfillment2)
next_tx['transaction']['fulfillments'][0]['fulfillment'] = next_tx_fulfillment.serialize_uri()
assert b.validate_transaction(next_tx)
assert b.is_valid_transaction(next_tx) == next_tx
@pytest.mark.usefixtures('inputs')
def test_override_condition_and_fulfillment_transfer_threshold_wrongly_signed(self, b, user_vk, user_sk):
other1_sk, other1_vk = crypto.generate_key_pair()
other2_sk, other2_vk = crypto.generate_key_pair()
first_input_tx = b.get_owned_ids(user_vk).pop()
first_tx = b.create_transaction(user_vk, [other1_vk, other2_vk], first_input_tx, 'TRANSFER')
first_tx_condition = ThresholdSha256Fulfillment(threshold=2)
first_tx_condition.add_subfulfillment(Ed25519Fulfillment(public_key=other1_vk))
first_tx_condition.add_subfulfillment(Ed25519Fulfillment(public_key=other2_vk))
first_tx['transaction']['conditions'][0]['condition'] = {
'details': json.loads(first_tx_condition.serialize_json()),
'uri': first_tx_condition.condition.serialize_uri()
}
# conditions have been updated, so hash needs updating
transaction_data = copy.deepcopy(first_tx)
for fulfillment in transaction_data['transaction']['fulfillments']:
fulfillment['fulfillment'] = None
calculated_hash = crypto.hash_data(util.serialize(transaction_data['transaction']))
first_tx['id'] = calculated_hash
first_tx_signed = b.sign_transaction(first_tx, user_sk)
assert b.validate_transaction(first_tx_signed)
assert b.is_valid_transaction(first_tx_signed) == first_tx_signed
b.write_transaction(first_tx_signed)
# create and write block to bigchain
block = b.create_block([first_tx])
b.write_block(block, durability='hard')
next_input_tx = b.get_owned_ids(other1_vk).pop()
# create another transaction with the same input
next_tx = b.create_transaction([other1_vk, other2_vk], user_vk, next_input_tx, 'TRANSFER')
next_tx_fulfillment = next_tx['transaction']['fulfillments'][0]
next_tx_fulfillment_message = util.get_fulfillment_message(next_tx, next_tx_fulfillment)
next_tx_fulfillment = ThresholdSha256Fulfillment(threshold=2)
next_tx_subfulfillment1 = Ed25519Fulfillment(public_key=other1_vk)
next_tx_subfulfillment1.sign(util.serialize(next_tx_fulfillment_message), crypto.SigningKey(other1_sk))
next_tx_fulfillment.add_subfulfillment(next_tx_subfulfillment1)
# Wrong signing happens here
next_tx_subfulfillment2 = Ed25519Fulfillment(public_key=other1_vk)
next_tx_subfulfillment2.sign(util.serialize(next_tx_fulfillment_message), crypto.SigningKey(other1_sk))
next_tx_fulfillment.add_subfulfillment(next_tx_subfulfillment2)
next_tx['transaction']['fulfillments'][0]['fulfillment'] = next_tx_fulfillment.serialize_uri()
with pytest.raises(exceptions.InvalidSignature):
b.validate_transaction(next_tx)
assert b.is_valid_transaction(next_tx) == False