create_tx and sign_tx create threshold conditions by default if there

are multiple current owners.
Created tests
This commit is contained in:
Rodolphe Marques 2016-04-13 17:44:25 +02:00
parent 74a1e14183
commit 4c4cae8f81
2 changed files with 201 additions and 12 deletions

View File

@ -194,8 +194,8 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
conditions = []
for fulfillment in fulfillments:
if len(new_owners) > 1:
condition = ThresholdSha256Fulfillment(threshold=len(new_owners))
for new_owner in new_owners:
condition = ThresholdSha256Fulfillment(threshold=len(new_owners))
condition.add_subfulfillment(Ed25519Fulfillment(public_key=new_owner))
elif len(new_owners) == 1:
condition = Ed25519Fulfillment(public_key=new_owners[0])
@ -231,7 +231,7 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
# TODO: Change sign_tx to populate the fulfillments
def sign_tx(transaction, sk):
def sign_tx(transaction, sks):
"""Sign a transaction
A transaction signed with the `current_owner` corresponding private key.
@ -244,7 +244,20 @@ def sign_tx(transaction, sk):
dict: transaction with the `fulfillment` fields populated.
"""
sk = crypto.SigningKey(sk)
# validate sk
if not isinstance(sks, list):
sks = [sks]
if len(sks) == 1:
sk = crypto.SigningKey(sks[0])
else:
# create a mapping between sk and vk so that we can match the private key to the current_owners
key_pairs = {}
for sk in sks:
signing_key = crypto.SigningKey(sk)
vk = signing_key.get_verifying_key().to_ascii().decode()
key_pairs[vk] = signing_key
tx = copy.deepcopy(transaction)
for fulfillment in tx['transaction']['fulfillments']:
@ -254,7 +267,19 @@ def sign_tx(transaction, sk):
parsed_fulfillment = Ed25519Fulfillment(public_key=sk.get_verifying_key())
else:
parsed_fulfillment = Fulfillment.from_json(fulfillment_message['condition']['condition']['details'])
parsed_fulfillment.sign(serialize(fulfillment_message), sk)
# single current owner
if isinstance(parsed_fulfillment, Ed25519Fulfillment):
parsed_fulfillment.sign(serialize(fulfillment_message), sk)
# multiple current owners
elif isinstance(parsed_fulfillment, ThresholdSha256Fulfillment):
# replace the fulfillments with the signed fulfillments
parsed_fulfillment.subconditions = []
for current_owner in fulfillment['current_owners']:
subfulfillment = get_subcondition_from_vk(fulfillment_message['condition'], current_owner)
subfulfillment.sign(serialize(fulfillment_message), key_pairs[current_owner])
parsed_fulfillment.add_subfulfillment(subfulfillment)
signed_fulfillment = parsed_fulfillment.serialize_uri()
fulfillment.update({'fulfillment': signed_fulfillment})
@ -331,10 +356,17 @@ def get_fulfillment_message(transaction, fulfillment):
# 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['condition'] = conditions[fulfillment['input']['cid']]
return fulfillment_message
def get_subcondition_from_vk(condition, vk):
threshold_fulfillment = Fulfillment.from_json(condition['condition']['details'])
for subcondition in threshold_fulfillment.subconditions:
if subcondition['body'].public_key.to_ascii().decode() == vk:
return subcondition['body']
def transform_create(tx):
"""Change the owner and signature for a ``CREATE`` transaction created by a node"""

View File

@ -745,16 +745,167 @@ class TestBigchainBlock(object):
class TestMultipleInputs(object):
def test_transfer_single_owners_single_input(self, b):
def test_transfer_single_owners_single_input(self, b, user_sk, user_vk, inputs):
# create a new user
user2_sk, user2_vk = crypto.generate_key_pair()
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inp = owned_inputs.pop()
# create a transaction
tx = b.create_transaction([user_vk], [user2_sk], inp, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user_sk)
# validate transaction
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 1
assert len(tx_signed['transaction']['conditions']) == 1
def test_transfer_single_owners_multiple_inputs(self, b, user_sk, user_vk):
# create a new user
user2_sk, user2_vk = crypto.generate_key_pair()
# create inputs to spend
transactions = []
for i in range(10):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
transactions.append(tx_signed)
b.write_transaction(tx_signed)
block = b.create_block(transactions)
b.write_block(block, durability='hard')
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inputs = owned_inputs[:3]
# create a transaction
tx = b.create_transaction(user_vk, user2_vk, inputs, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user_sk)
# validate transaction
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 3
assert len(tx_signed['transaction']['conditions']) == 3
def test_transfer_single_owners_single_input_from_multiple_outputs(self, b, user_sk, user_vk):
# create a new user
user2_sk, user2_vk = crypto.generate_key_pair()
# create inputs to spend
transactions = []
for i in range(10):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
transactions.append(tx_signed)
b.write_transaction(tx_signed)
block = b.create_block(transactions)
b.write_block(block, durability='hard')
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inputs = owned_inputs[:3]
# create a transaction
tx = b.create_transaction(user_vk, user2_vk, inputs, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user_sk)
# create block with the transaction
block = b.create_block([tx_signed])
b.write_block(block, durability='hard')
# get inputs from user2
owned_inputs = b.get_owned_ids(user2_vk)
assert len(owned_inputs) == 3
# create a transaction with a single input from a multiple output transaction
inp = owned_inputs.pop()
tx = b.create_transaction(user2_vk, user_vk, inp, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user2_sk)
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 1
assert len(tx_signed['transaction']['conditions']) == 1
def test_single_current_owner_multiple_new_owners_single_input(self, b, user_sk, user_vk, inputs):
# create a new users
user2_sk, user2_vk = crypto.generate_key_pair()
user3_sk, user3_vk = crypto.generate_key_pair()
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inp = owned_inputs.pop()
# create a transaction
tx = b.create_transaction(user_vk, [user2_sk, user3_vk], inp, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user_sk)
# validate transaction
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 1
assert len(tx_signed['transaction']['conditions']) == 1
def test_single_current_owner_multiple_new_owners_multiple_inputs(self, b, user_sk, user_vk):
# create a new users
user2_sk, user2_vk = crypto.generate_key_pair()
user3_sk, user3_vk = crypto.generate_key_pair()
# create inputs to spend
transactions = []
for i in range(5):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
transactions.append(tx_signed)
b.write_transaction(tx_signed)
block = b.create_block(transactions)
b.write_block(block, durability='hard')
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inputs = owned_inputs[:3]
# create a transaction
tx = b.create_transaction(user_vk, [user2_vk, user3_vk], inputs, 'TRANSFER')
tx_signed = b.sign_transaction(tx, user_sk)
# validate transaction
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 3
assert len(tx_signed['transaction']['conditions']) == 3
@pytest.mark.skipif(reason='Skip until we fix default threshold signatures')
def test_multiple_current_owners_single_new_owner_single_input(self, b, user_sk, user_vk):
# create a new users
user2_sk, user2_vk = crypto.generate_key_pair()
user3_sk, user3_vk = crypto.generate_key_pair()
# create input to spend
tx = b.create_transaction(b.me, [user_vk, user2_vk], None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
block = b.create_block([tx_signed])
b.write_block(block, durability='hard')
# get inputs
owned_inputs = b.get_owned_ids(user_vk)
inputs = owned_inputs[:3]
# create a transaction
tx = b.create_transaction([user_vk, user2_vk], user3_vk, inputs, 'TRANSFER')
tx_signed = b.sign_transaction(tx, [user_sk, user2_sk])
# validate transaction
assert b.is_valid_transaction(tx_signed) == tx_signed
assert len(tx_signed['transaction']['fulfillments']) == 3
assert len(tx_signed['transaction']['conditions']) == 3
def test_multiple_current_owners_single_new_owner_multiple_inputs(self, b, user_sk, user_vk):
pass
def test_transfer_single_owners_multiple_inputs(self, b):
def test_multiple_current_owners_multiple_new_owners_single_input(self, b):
pass
def test_transfer_single_owners_single_input_from_multiple_outputs(self, b):
pass
def test_multiple_owners(self, b):
def test_multiple_current_owners_multiple_new_owners_multiple_inputs(self, b):
pass
def test_get_spent(self, b):
@ -1033,4 +1184,10 @@ class TestCryptoconditions(object):
with pytest.raises(exceptions.InvalidSignature):
b.validate_transaction(next_tx)
assert b.is_valid_transaction(next_tx) == False
assert b.is_valid_transaction(next_tx) == False
def test_default_threshold_signatures_for_multiple_owners(self, b):
pass
def test_get_subcondition_from_vk(self):
pass