class TransactionInput(base.Base): transaction = base.UInt128() index = base.UInt16() signature = base.UInt512() def verify(self): try: output = BlockChain().get_output_from_input(self) pubkey = ECC.import_key(bytes.fromhex(output.wallet)) except Exception as error: raise InvalidOutputReferenceError from error verifier = DSS.new(pubkey, "fips-186-3") # privkey = ECC.import_key(key) # signer = DSS.new(privkey, "fips-186-3") hash_ = SHA256.new(output.serialize()) try: verifier.verify(hash_, self.signature.to_bytes(64, "little")) except ValueError as check_fail: raise SecretError from check_fail def sign(self, wallet): private_key = ECC.import_key(bytes.fromhex(wallet.private_key)) signer = DSS.new(private_key, 'fips-186-3') output = BlockChain().get_output_from_input(self) hash_ = SHA256.new(output.serialize()) self.signature = int.from_bytes(signer.sign(hash_), "little")
class Transaction(base.Base): ID = base.UInt128() inputs = base.SequenceField(TransactionInput) outputs = base.SequenceField(TransactionOutput) signature = base.UInt512() transaction_type = base.UInt8() def __init__(self, **kwargs): kwargs.setdefault("ID", int(uuid.uuid4())) kwargs.setdefault( "transaction_type", 0) # 0 indicates normal transaction. 1 is for reward transaction self.blockchain = BlockChain() super().__init__(**kwargs) def get_fee(self): self.verify() if self.transaction_type == 1: # Reward transaction - balance comes out of thin air return 0 bl = BlockChain() input_amount = sum( bl.get_output_from_input(inp).amount for inp in self.inputs) output_amount = sum(out.amount for out in self.outputs) if output_amount > input_amount: raise AmountError( f"Total fee is negative '{output_amount - input_amount}'") return input_amount - output_amount def verify(self): for input in self.inputs: input.verify() self.verify_transaction_signature() def verify_transaction_signature(self): if self.transaction_type == 1: return wallet = BlockChain().get_output_from_input(self.inputs[0]).wallet pubkey = ECC.import_key(bytes.fromhex(wallet)) verifier = DSS.new(pubkey, "fips-186-3") signature = self.signature self.signature = 0 hash_ = SHA256.new(self.serialize()) self.signature = signature try: verifier.verify(hash_, signature.to_bytes(64, "little")) except ValueError as check_fail: raise SecretError("Invalid transaction signature") def sign_transaction(self, wallets): """ Sign transaction with private key # based on https://github.com/adilmoujahid/blockchain-python-tutorial/blob/master/blockchain_client/blockchain_client.py """ from .wallet import Wallet if isinstance(wallets, (Sequence, Iterable)): wallets = {wallet.public_key: wallet for wallet in wallets} elif isinstance(wallets, Wallet): wallets = {wallets.public_key: wallets} for input in self.inputs: output = BlockChain().get_output_from_input(input) try: input.sign(wallets[output.wallet]) except KeyError as error: raise WalletError from error # Use the wallet used for the first input to sign the whole transaction if self.inputs: signer_wallet = BlockChain().get_output_from_input( self.inputs[0]).wallet wallet = wallets[signer_wallet] elif self.transaction_type == 1: # we have input if we are a reward transaction wallet = next(iter(wallets.values())) else: raise TransactionError("Trying to sign transaction with no input") private_key = ECC.import_key(bytes.fromhex(wallet.private_key)) signer = DSS.new(private_key, 'fips-186-3') self.signature = 0 hash_ = SHA256.new(self.serialize()) self.signature = int.from_bytes(signer.sign(hash_), "little")