mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge remote-tracking branch 'remotes/origin/feat/127/crypto-conditions-ilp-bigchain-integration' into feat/128/multiple-input-output
This commit is contained in:
commit
43a88142f7
@ -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']))
|
||||
|
@ -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"""
|
||||
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user