mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
sign whole transaction body instead of partial transaction
This commit is contained in:
parent
4a5a5566e7
commit
bae05e80a0
@ -686,22 +686,17 @@ class Transaction(object):
|
|||||||
key_pairs = {gen_public_key(PrivateKey(private_key)):
|
key_pairs = {gen_public_key(PrivateKey(private_key)):
|
||||||
PrivateKey(private_key) for private_key in private_keys}
|
PrivateKey(private_key) for private_key in private_keys}
|
||||||
|
|
||||||
for index, input_ in enumerate(self.inputs):
|
tx_dict = self.to_dict()
|
||||||
# NOTE: We clone the current transaction but only add the output
|
tx_dict = Transaction._remove_signatures(tx_dict)
|
||||||
# and input we're currently working on plus all
|
tx_serialized = Transaction._to_str(tx_dict)
|
||||||
# previously signed ones.
|
for i, input_ in enumerate(self.inputs):
|
||||||
tx_partial = Transaction(self.operation, self.asset, [input_],
|
message = '%s:%s' % (i, tx_serialized)
|
||||||
self.outputs, self.metadata,
|
self.inputs[i] = self._sign_input(input_, message, key_pairs)
|
||||||
self.version)
|
|
||||||
|
|
||||||
tx_partial_dict = tx_partial.to_dict()
|
|
||||||
tx_partial_dict = Transaction._remove_signatures(tx_partial_dict)
|
|
||||||
tx_serialized = Transaction._to_str(tx_partial_dict)
|
|
||||||
self._sign_input(input_, index, tx_serialized, key_pairs)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _sign_input(self, input_, index, tx_serialized, key_pairs):
|
@classmethod
|
||||||
"""Signs a single Input with a partial Transaction as message.
|
def _sign_input(cls, input_, message, key_pairs):
|
||||||
|
"""Signs a single Input.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
This method works only for the following Cryptoconditions
|
This method works only for the following Cryptoconditions
|
||||||
@ -712,31 +707,27 @@ class Transaction(object):
|
|||||||
Args:
|
Args:
|
||||||
input_ (:class:`~bigchaindb.common.transaction.
|
input_ (:class:`~bigchaindb.common.transaction.
|
||||||
Input`) The Input to be signed.
|
Input`) The Input to be signed.
|
||||||
index (int): The index of the input to be signed.
|
message (str): The message to be signed
|
||||||
tx_serialized (str): The Transaction to be used as message.
|
|
||||||
key_pairs (dict): The keys to sign the Transaction with.
|
key_pairs (dict): The keys to sign the Transaction with.
|
||||||
"""
|
"""
|
||||||
if isinstance(input_.fulfillment, Ed25519Fulfillment):
|
if isinstance(input_.fulfillment, Ed25519Fulfillment):
|
||||||
self._sign_simple_signature_fulfillment(input_, index,
|
return cls._sign_simple_signature_fulfillment(input_, message,
|
||||||
tx_serialized, key_pairs)
|
key_pairs)
|
||||||
elif isinstance(input_.fulfillment, ThresholdSha256Fulfillment):
|
elif isinstance(input_.fulfillment, ThresholdSha256Fulfillment):
|
||||||
self._sign_threshold_signature_fulfillment(input_, index,
|
return cls._sign_threshold_signature_fulfillment(input_, message,
|
||||||
tx_serialized,
|
key_pairs)
|
||||||
key_pairs)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Fulfillment couldn't be matched to "
|
raise ValueError("Fulfillment couldn't be matched to "
|
||||||
'Cryptocondition fulfillment type.')
|
'Cryptocondition fulfillment type.')
|
||||||
|
|
||||||
def _sign_simple_signature_fulfillment(self, input_, index,
|
@classmethod
|
||||||
tx_serialized, key_pairs):
|
def _sign_simple_signature_fulfillment(cls, input_, message, key_pairs):
|
||||||
"""Signs a Ed25519Fulfillment.
|
"""Signs a Ed25519Fulfillment.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_ (:class:`~bigchaindb.common.transaction.
|
input_ (:class:`~bigchaindb.common.transaction.
|
||||||
Input`) The input to be signed.
|
Input`) The input to be signed.
|
||||||
index (int): The index of the input to be
|
message (str): The message to be signed
|
||||||
signed.
|
|
||||||
tx_serialized (str): The Transaction to be used as message.
|
|
||||||
key_pairs (dict): The keys to sign the Transaction with.
|
key_pairs (dict): The keys to sign the Transaction with.
|
||||||
"""
|
"""
|
||||||
# NOTE: To eliminate the dangers of accidentally signing a condition by
|
# NOTE: To eliminate the dangers of accidentally signing a condition by
|
||||||
@ -748,23 +739,21 @@ class Transaction(object):
|
|||||||
try:
|
try:
|
||||||
# cryptoconditions makes no assumptions of the encoding of the
|
# cryptoconditions makes no assumptions of the encoding of the
|
||||||
# message to sign or verify. It only accepts bytestrings
|
# message to sign or verify. It only accepts bytestrings
|
||||||
input_.fulfillment.sign(tx_serialized.encode(), key_pairs[public_key])
|
input_.fulfillment.sign(message.encode(), key_pairs[public_key])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeypairMismatchException('Public key {} is not a pair to '
|
raise KeypairMismatchException('Public key {} is not a pair to '
|
||||||
'any of the private keys'
|
'any of the private keys'
|
||||||
.format(public_key))
|
.format(public_key))
|
||||||
self.inputs[index] = input_
|
return input_
|
||||||
|
|
||||||
def _sign_threshold_signature_fulfillment(self, input_, index,
|
@classmethod
|
||||||
tx_serialized, key_pairs):
|
def _sign_threshold_signature_fulfillment(cls, input_, message, key_pairs):
|
||||||
"""Signs a ThresholdSha256Fulfillment.
|
"""Signs a ThresholdSha256Fulfillment.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_ (:class:`~bigchaindb.common.transaction.
|
input_ (:class:`~bigchaindb.common.transaction.
|
||||||
Input`) The Input to be signed.
|
Input`) The Input to be signed.
|
||||||
index (int): The index of the Input to be
|
message (str): The message to be signed
|
||||||
signed.
|
|
||||||
tx_serialized (str): The Transaction to be used as message.
|
|
||||||
key_pairs (dict): The keys to sign the Transaction with.
|
key_pairs (dict): The keys to sign the Transaction with.
|
||||||
"""
|
"""
|
||||||
input_ = deepcopy(input_)
|
input_ = deepcopy(input_)
|
||||||
@ -794,8 +783,8 @@ class Transaction(object):
|
|||||||
|
|
||||||
# cryptoconditions makes no assumptions of the encoding of the
|
# cryptoconditions makes no assumptions of the encoding of the
|
||||||
# message to sign or verify. It only accepts bytestrings
|
# message to sign or verify. It only accepts bytestrings
|
||||||
subffill.sign(tx_serialized.encode(), private_key)
|
subffill.sign(message.encode(), private_key)
|
||||||
self.inputs[index] = input_
|
return input_
|
||||||
|
|
||||||
def inputs_valid(self, outputs=None):
|
def inputs_valid(self, outputs=None):
|
||||||
"""Validates the Inputs in the Transaction against given
|
"""Validates the Inputs in the Transaction against given
|
||||||
@ -848,24 +837,19 @@ class Transaction(object):
|
|||||||
raise ValueError('Inputs and '
|
raise ValueError('Inputs and '
|
||||||
'output_condition_uris must have the same count')
|
'output_condition_uris must have the same count')
|
||||||
|
|
||||||
def gen_tx(input_, output, output_condition_uri=None):
|
tx_dict = self.to_dict()
|
||||||
"""Splits multiple IO Transactions into partial single IO
|
tx_dict = Transaction._remove_signatures(tx_dict)
|
||||||
Transactions.
|
tx_serialized = Transaction._to_str(tx_dict)
|
||||||
"""
|
|
||||||
tx = Transaction(self.operation, self.asset, [input_],
|
|
||||||
self.outputs, self.metadata, self.version)
|
|
||||||
tx_dict = tx.to_dict()
|
|
||||||
tx_dict = Transaction._remove_signatures(tx_dict)
|
|
||||||
tx_serialized = Transaction._to_str(tx_dict)
|
|
||||||
|
|
||||||
return self.__class__._input_valid(input_,
|
def validate(i, output_condition_uri=None):
|
||||||
self.operation,
|
""" Validate input against output condition URI """
|
||||||
tx_serialized,
|
message = '%s:%s' % (i, tx_serialized)
|
||||||
output_condition_uri)
|
|
||||||
|
|
||||||
partial_transactions = map(gen_tx, self.inputs,
|
return self._input_valid(self.inputs[i], self.operation, message,
|
||||||
self.outputs, output_condition_uris)
|
output_condition_uri)
|
||||||
return all(partial_transactions)
|
|
||||||
|
return all(validate(i, cond)
|
||||||
|
for i, cond in enumerate(output_condition_uris))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _input_valid(input_, operation, tx_serialized, output_condition_uri=None):
|
def _input_valid(input_, operation, tx_serialized, output_condition_uri=None):
|
||||||
|
@ -49,4 +49,4 @@ Here's some explanation of the contents of a :ref:`transaction <transaction>`:
|
|||||||
|
|
||||||
Later, when we get to the models for the block and the vote, we'll see that both include a signature (from the node which created it). You may wonder why transactions don't have signatures... The answer is that they do! They're just hidden inside the ``fulfillment`` string of each input. A creation transaction is signed by whoever created it. A transfer transaction is signed by whoever currently controls or owns it.
|
Later, when we get to the models for the block and the vote, we'll see that both include a signature (from the node which created it). You may wonder why transactions don't have signatures... The answer is that they do! They're just hidden inside the ``fulfillment`` string of each input. A creation transaction is signed by whoever created it. A transfer transaction is signed by whoever currently controls or owns it.
|
||||||
|
|
||||||
What gets signed? For each input in the transaction, the "fullfillment message" that gets signed includes the ``operation``, ``data``, ``version``, ``id``, corresponding ``condition``, and the fulfillment itself, except with its fulfillment string set to ``null``. The computed signature goes into creating the ``fulfillment`` string of the input.
|
What gets signed? For each input in the transaction, the "fullfillment message" that gets signed includes the JSON serialized body of the transaction, minus any fulfillment strings, and with "n:" prepended where n is the index of the input being signed. The computed signature goes into creating the ``fulfillment`` string of the input.
|
||||||
|
@ -496,7 +496,8 @@ def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
|
|||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
|
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
|
||||||
expected = deepcopy(user_output)
|
expected = deepcopy(user_output)
|
||||||
expected.fulfillment.sign(str(tx).encode(), PrivateKey(user_priv))
|
message = ('0:' + str(tx)).encode()
|
||||||
|
expected.fulfillment.sign(message, PrivateKey(user_priv))
|
||||||
tx.sign([user_priv])
|
tx.sign([user_priv])
|
||||||
|
|
||||||
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
||||||
@ -513,7 +514,6 @@ def test_invoke_simple_signature_fulfillment_with_invalid_params(utx,
|
|||||||
with raises(KeypairMismatchException):
|
with raises(KeypairMismatchException):
|
||||||
invalid_key_pair = {'wrong_pub_key': 'wrong_priv_key'}
|
invalid_key_pair = {'wrong_pub_key': 'wrong_priv_key'}
|
||||||
utx._sign_simple_signature_fulfillment(user_input,
|
utx._sign_simple_signature_fulfillment(user_input,
|
||||||
0,
|
|
||||||
'somemessage',
|
'somemessage',
|
||||||
invalid_key_pair)
|
invalid_key_pair)
|
||||||
|
|
||||||
@ -524,13 +524,11 @@ def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_input,
|
|||||||
|
|
||||||
with raises(KeypairMismatchException):
|
with raises(KeypairMismatchException):
|
||||||
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
||||||
0,
|
|
||||||
'somemessage',
|
'somemessage',
|
||||||
{user3_pub: user3_priv})
|
{user3_pub: user3_priv})
|
||||||
with raises(KeypairMismatchException):
|
with raises(KeypairMismatchException):
|
||||||
user_user2_threshold_input.owners_before = ['somewrongvalue']
|
user_user2_threshold_input.owners_before = ['somewrongvalue']
|
||||||
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
||||||
0,
|
|
||||||
'somemessage',
|
'somemessage',
|
||||||
None)
|
None)
|
||||||
|
|
||||||
@ -560,13 +558,11 @@ def test_validate_multiple_inputs(user_input, user_output, user_priv,
|
|||||||
|
|
||||||
expected_first = deepcopy(tx)
|
expected_first = deepcopy(tx)
|
||||||
expected_second = deepcopy(tx)
|
expected_second = deepcopy(tx)
|
||||||
expected_first.inputs = [expected_first.inputs[0]]
|
|
||||||
expected_second.inputs = [expected_second.inputs[1]]
|
|
||||||
|
|
||||||
expected_first_bytes = str(expected_first).encode()
|
expected_first_bytes = ('0:' + str(tx)).encode()
|
||||||
expected_first.inputs[0].fulfillment.sign(expected_first_bytes,
|
expected_first.inputs[0].fulfillment.sign(expected_first_bytes,
|
||||||
PrivateKey(user_priv))
|
PrivateKey(user_priv))
|
||||||
expected_second_bytes = str(expected_second).encode()
|
expected_second_bytes = ('1:' + str(tx)).encode()
|
||||||
expected_second.inputs[0].fulfillment.sign(expected_second_bytes,
|
expected_second.inputs[0].fulfillment.sign(expected_second_bytes,
|
||||||
PrivateKey(user_priv))
|
PrivateKey(user_priv))
|
||||||
tx.sign([user_priv])
|
tx.sign([user_priv])
|
||||||
@ -596,10 +592,11 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
|
|||||||
tx = Transaction(Transaction.CREATE, asset_definition,
|
tx = Transaction(Transaction.CREATE, asset_definition,
|
||||||
[user_user2_threshold_input],
|
[user_user2_threshold_input],
|
||||||
[user_user2_threshold_output])
|
[user_user2_threshold_output])
|
||||||
|
message = ('0:' + str(tx)).encode()
|
||||||
expected = deepcopy(user_user2_threshold_output)
|
expected = deepcopy(user_user2_threshold_output)
|
||||||
expected.fulfillment.subconditions[0]['body'].sign(str(tx).encode(),
|
expected.fulfillment.subconditions[0]['body'].sign(message,
|
||||||
PrivateKey(user_priv))
|
PrivateKey(user_priv))
|
||||||
expected.fulfillment.subconditions[1]['body'].sign(str(tx).encode(),
|
expected.fulfillment.subconditions[1]['body'].sign(message,
|
||||||
PrivateKey(user2_priv))
|
PrivateKey(user2_priv))
|
||||||
tx.sign([user_priv, user2_priv])
|
tx.sign([user_priv, user2_priv])
|
||||||
|
|
||||||
@ -861,7 +858,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
|||||||
|
|
||||||
expected_input = deepcopy(inputs[0])
|
expected_input = deepcopy(inputs[0])
|
||||||
expected['id'] = transfer_tx['id']
|
expected['id'] = transfer_tx['id']
|
||||||
expected_input.fulfillment.sign(serialize(expected).encode(),
|
expected_input.fulfillment.sign(('0:' + serialize(expected)).encode(),
|
||||||
PrivateKey(user_priv))
|
PrivateKey(user_priv))
|
||||||
expected_ffill = expected_input.fulfillment.serialize_uri()
|
expected_ffill = expected_input.fulfillment.serialize_uri()
|
||||||
transfer_ffill = transfer_tx['inputs'][0]['fulfillment']
|
transfer_ffill = transfer_tx['inputs'][0]['fulfillment']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user