mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
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:
parent
cbb9a55de8
commit
a4bbffa544
@ -28,4 +28,5 @@ class DatabaseDoesNotExist(Exception):
|
|||||||
class KeypairNotFoundException(Exception):
|
class KeypairNotFoundException(Exception):
|
||||||
"""Raised if operation cannot proceed because the keypair was not given"""
|
"""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)"""
|
||||||
|
@ -248,36 +248,38 @@ def sign_tx(transaction, sks):
|
|||||||
if not isinstance(sks, list):
|
if not isinstance(sks, list):
|
||||||
sks = [sks]
|
sks = [sks]
|
||||||
|
|
||||||
if len(sks) == 1:
|
# create a mapping between sk and vk so that we can match the private key to the current_owners
|
||||||
sk = crypto.SigningKey(sks[0])
|
key_pairs = {}
|
||||||
else:
|
for sk in sks:
|
||||||
# create a mapping between sk and vk so that we can match the private key to the current_owners
|
signing_key = crypto.SigningKey(sk)
|
||||||
key_pairs = {}
|
vk = signing_key.get_verifying_key().to_ascii().decode()
|
||||||
for sk in sks:
|
key_pairs[vk] = signing_key
|
||||||
signing_key = crypto.SigningKey(sk)
|
|
||||||
vk = signing_key.get_verifying_key().to_ascii().decode()
|
|
||||||
key_pairs[vk] = signing_key
|
|
||||||
|
|
||||||
tx = copy.deepcopy(transaction)
|
tx = copy.deepcopy(transaction)
|
||||||
|
|
||||||
for fulfillment in tx['transaction']['fulfillments']:
|
for fulfillment in tx['transaction']['fulfillments']:
|
||||||
fulfillment_message = get_fulfillment_message(transaction, fulfillment)
|
fulfillment_message = get_fulfillment_message(transaction, fulfillment)
|
||||||
if tx['transaction']['operation'] in ['CREATE', 'GENESIS']:
|
parsed_fulfillment = Fulfillment.from_json(fulfillment_message['condition']['condition']['details'])
|
||||||
# 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
|
# single current owner
|
||||||
if isinstance(parsed_fulfillment, Ed25519Fulfillment):
|
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
|
# multiple current owners
|
||||||
elif isinstance(parsed_fulfillment, ThresholdSha256Fulfillment):
|
elif isinstance(parsed_fulfillment, ThresholdSha256Fulfillment):
|
||||||
# replace the fulfillments with the signed fulfillments
|
# replace the fulfillments with the signed fulfillments
|
||||||
parsed_fulfillment.subconditions = []
|
parsed_fulfillment.subconditions = []
|
||||||
for current_owner in fulfillment['current_owners']:
|
for current_owner in fulfillment['current_owners']:
|
||||||
subfulfillment = get_subcondition_from_vk(fulfillment_message['condition'], current_owner)
|
subfulfillment = get_subcondition_from_vk(fulfillment_message['condition'], current_owner)
|
||||||
subfulfillment.sign(serialize(fulfillment_message), key_pairs[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)
|
parsed_fulfillment.add_subfulfillment(subfulfillment)
|
||||||
|
|
||||||
signed_fulfillment = parsed_fulfillment.serialize_uri()
|
signed_fulfillment = parsed_fulfillment.serialize_uri()
|
||||||
@ -351,12 +353,18 @@ def get_fulfillment_message(transaction, fulfillment):
|
|||||||
'condition': None,
|
'condition': None,
|
||||||
})
|
})
|
||||||
|
|
||||||
# if not a `CREATE` transaction
|
# if `TRANSFER` transaction
|
||||||
if fulfillment['input']:
|
if fulfillment['input']:
|
||||||
# get previous condition
|
# get previous condition
|
||||||
previous_tx = b.get_transaction(fulfillment['input']['txid'])
|
previous_tx = b.get_transaction(fulfillment['input']['txid'])
|
||||||
conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid'])
|
conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid'])
|
||||||
fulfillment_message['condition'] = conditions[fulfillment['input']['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
|
return fulfillment_message
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_vk).pop()
|
input_tx = b.get_owned_ids(user_vk).pop()
|
||||||
assert b.verify_signature(b.get_transaction(input_tx['txid'])) == True
|
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) == sorted(['id', 'transaction', 'version'])
|
||||||
assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp'])
|
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 excinfo.value.args[0] == 'Only federation nodes can use the operation `CREATE`'
|
||||||
assert b.is_valid_transaction(tx) is False
|
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):
|
def test_non_create_operation_no_inputs(self, b, user_vk):
|
||||||
tx = b.create_transaction(user_vk, user_vk, None, 'TRANSFER')
|
tx = b.create_transaction(user_vk, user_vk, None, 'TRANSFER')
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
@ -326,16 +318,25 @@ class TestTransactionValidation(object):
|
|||||||
assert b.is_valid_transaction(tx_valid) is False
|
assert b.is_valid_transaction(tx_valid) is False
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@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()
|
input_valid = b.get_owned_ids(user_vk).pop()
|
||||||
tx_valid = b.create_transaction(user_vk, user_vk, input_valid, 'TRANSFER')
|
tx_valid = b.create_transaction(user_vk, user_vk, input_valid, 'TRANSFER')
|
||||||
|
|
||||||
wrong_private_key = '4fyvJe1aw2qHZ4UNRYftXK7JU7zy9bCqoU5ps6Ne3xrY'
|
wrong_private_key = '4fyvJe1aw2qHZ4UNRYftXK7JU7zy9bCqoU5ps6Ne3xrY'
|
||||||
|
|
||||||
tx_invalid_signed = b.sign_transaction(tx_valid, wrong_private_key)
|
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):
|
with pytest.raises(exceptions.InvalidSignature):
|
||||||
b.validate_transaction(tx_invalid_signed)
|
b.validate_transaction(tx_signed)
|
||||||
assert b.is_valid_transaction(tx_invalid_signed) is False
|
assert b.is_valid_transaction(tx_signed) is False
|
||||||
|
|
||||||
def test_valid_create_transaction(self, b, user_vk):
|
def test_valid_create_transaction(self, b, user_vk):
|
||||||
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
|
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user