|
|
@@ -1,5 +1,6 @@
|
|
|
from collections import namedtuple
|
|
|
-from crypto import get_hasher, sign, verify_sign
|
|
|
+
|
|
|
+from .crypto import get_hasher, Signing
|
|
|
|
|
|
""" The recipient of a transaction ('coin'). """
|
|
|
TransactionTarget = namedtuple("TransactionTarget", ["recipient_pk", "amount"])
|
|
|
@@ -10,28 +11,40 @@ TransactionInput = namedtuple("TransactionInput", ["transaction_hash", "output_i
|
|
|
|
|
|
class Transaction:
|
|
|
|
|
|
- def __init__(self, inputs, targets, signatures=None):
|
|
|
+ def __init__(self, inputs, targets, signatures=None, iv=None):
|
|
|
self.inputs = inputs
|
|
|
self.targets = targets
|
|
|
self.signatures = signatures or []
|
|
|
+ # The IV is used to differentiate block reward transactions.
|
|
|
+ # These have no inputs and therefore would otherwise hash to the
|
|
|
+ # same value, when the target is identical.
|
|
|
+ # Reuse of IVs leads to inaccessible coins.
|
|
|
+ self.iv = iv
|
|
|
|
|
|
def get_hash(self):
|
|
|
""" Hash this transaction. Returns raw bytes. """
|
|
|
h = get_hasher()
|
|
|
+ if self.iv is not None:
|
|
|
+ h.update(self.iv)
|
|
|
for target in self.targets:
|
|
|
- h.update(target.amount)
|
|
|
- h.update(target.recipient_pk)
|
|
|
+ h.update(str(target.amount).encode())
|
|
|
+ h.update(target.recipient_pk.as_bytes())
|
|
|
for inp in self.inputs:
|
|
|
- h.update(inp)
|
|
|
+ h.update(inp.transaction_hash)
|
|
|
+ h.update(str(inp.output_idx).encode())
|
|
|
return h.digest()
|
|
|
|
|
|
- def sign(self, private_key):
|
|
|
- """ Sign this transaction with a private key. You need to call this in the same order as the inputs. """
|
|
|
- self.signatures.append(sign(self.get_hash(), private_key))
|
|
|
+ def sign(self, private_keys):
|
|
|
+ """
|
|
|
+ Sign this transaction with the given private keys. The private keys need
|
|
|
+ to be in the same order as the inputs.
|
|
|
+ """
|
|
|
+ for private_key in private_keys:
|
|
|
+ self.signatures.append(private_key.sign(self.get_hash()))
|
|
|
|
|
|
def _verify_signatures(self, chain):
|
|
|
""" Verify that all inputs are signed and the signatures are valid. """
|
|
|
- if len(self.signatures) != len(self.inputs)
|
|
|
+ if len(self.signatures) != len(self.inputs):
|
|
|
return False
|
|
|
|
|
|
for (s, i) in zip(self.signatures, self.inputs):
|
|
|
@@ -42,8 +55,8 @@ class Transaction:
|
|
|
def _verify_single_sig(self, sig, inp, chain):
|
|
|
""" Verifies the signature on a single input. """
|
|
|
trans = chain.get_transaction_by_hash(inp.transaction_hash)
|
|
|
- sender_pk = trans.targets[inp.output_idx]
|
|
|
- return verify_sign(self.get_hash(), sig)
|
|
|
+ sender_pk = trans.targets[inp.output_idx].recipient_pk
|
|
|
+ return sender_pk.verify_sign(self.get_hash(), sig)
|
|
|
|
|
|
|
|
|
def _verify_single_spend(self, chain, prev_block):
|