Esempio n. 1
0
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")
Esempio n. 2
0
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")