Refactor sign_tx. Fix tests

The implicit condition of create transactions is now handled by
`get_fulfillment_message` instead of `sign_tx`
This commit is contained in:
Rodolphe Marques 2016-04-14 16:13:57 +02:00
parent cbb9a55de8
commit a4bbffa544
3 changed files with 41 additions and 31 deletions

View File

@ -28,4 +28,5 @@ class DatabaseDoesNotExist(Exception):
class KeypairNotFoundException(Exception):
"""Raised if operation cannot proceed because the keypair was not given"""
class KeypairMismatchException(Exception):
"""Raised if the private key(s) provided for signing don't match any of the curret owner(s)"""

View File

@ -248,9 +248,6 @@ def sign_tx(transaction, sks):
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:
@ -262,22 +259,27 @@ def sign_tx(transaction, sks):
for fulfillment in tx['transaction']['fulfillments']:
fulfillment_message = get_fulfillment_message(transaction, fulfillment)
if tx['transaction']['operation'] in ['CREATE', 'GENESIS']:
# sign the fulfillment message
parsed_fulfillment = Ed25519Fulfillment(public_key=sk.get_verifying_key())
else:
parsed_fulfillment = Fulfillment.from_json(fulfillment_message['condition']['condition']['details'])
# single current owner
if isinstance(parsed_fulfillment, Ed25519Fulfillment):
parsed_fulfillment.sign(serialize(fulfillment_message), sk)
current_owner = fulfillment['current_owners'][0]
try:
parsed_fulfillment.sign(serialize(fulfillment_message), key_pairs[current_owner])
except KeyError:
raise exceptions.KeypairMismatchException('Public key {} is not a pair to any of the private keys'
.format(current_owner))
# 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)
try:
subfulfillment.sign(serialize(fulfillment_message), key_pairs[current_owner])
except KeyError:
raise exceptions.KeypairMismatchException('Public key {} is not a pair to any of the private keys'
.format(current_owner))
parsed_fulfillment.add_subfulfillment(subfulfillment)
signed_fulfillment = parsed_fulfillment.serialize_uri()
@ -351,12 +353,18 @@ def get_fulfillment_message(transaction, fulfillment):
'condition': None,
})
# if not a `CREATE` transaction
# if `TRANSFER` 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['input']['cid']]
# if `CREATE` transaction
# there is no previous transaction so we need to create one on the fly
else:
current_owner = transaction['transaction']['fulfillments'][0]['current_owners'][0]
condition = json.loads(Ed25519Fulfillment(public_key=current_owner).serialize_json())
fulfillment_message['condition'] = {'condition': {'details': condition}}
return fulfillment_message

View File

@ -41,7 +41,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
assert b.verify_signature(b.get_transaction(input_tx['txid'])) == True
tx = b.create_transaction(b.me, user_sk, input_tx, 'TRANSFER')
tx = b.create_transaction(user_vk, b.me, input_tx, 'TRANSFER')
assert sorted(tx) == sorted(['id', 'transaction', 'version'])
assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp'])
@ -260,14 +260,6 @@ 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:
@ -326,16 +318,25 @@ class TestTransactionValidation(object):
assert b.is_valid_transaction(tx_valid) is False
@pytest.mark.usefixtures('inputs')
def test_wrong_signature(self, b, user_vk):
def test_wrong_signature(self, b, user_sk, user_vk):
input_valid = b.get_owned_ids(user_vk).pop()
tx_valid = b.create_transaction(user_vk, user_vk, input_valid, 'TRANSFER')
wrong_private_key = '4fyvJe1aw2qHZ4UNRYftXK7JU7zy9bCqoU5ps6Ne3xrY'
with pytest.raises(exceptions.KeypairMismatchException):
tx_invalid_signed = b.sign_transaction(tx_valid, wrong_private_key)
# create a correctly signed transaction and change the signature
tx_signed = b.sign_transaction(tx_valid, user_sk)
fulfillment = tx_signed['transaction']['fulfillments'][0]['fulfillment']
changed_fulfillment = Ed25519Fulfillment().from_uri(fulfillment)
changed_fulfillment.signature = b'0' * 64
tx_signed['transaction']['fulfillments'][0]['fulfillment'] = changed_fulfillment.serialize_uri()
with pytest.raises(exceptions.InvalidSignature):
b.validate_transaction(tx_invalid_signed)
assert b.is_valid_transaction(tx_invalid_signed) is False
b.validate_transaction(tx_signed)
assert b.is_valid_transaction(tx_signed) is False
def test_valid_create_transaction(self, b, user_vk):
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')