Building a Blockchain in Python, Part 2: Transactions and Keys

in #programming7 years ago

Transactions are the information contained in blocks that are digitally signed by the sender of the coins, so that anyone can verify that own those coins, and have the right to spend them.

However, how is this signing done? Bitcoin uses ECDSA, which is a digital signature scheme that allows for owners of a private key to sign a message, and allow anyone to verify that the true owner was the signer by verifying it with their public key.

First off, we want to structure a transaction. Our cryptocurrency won't be the most secure in terms of protecting against double-spends, but it will suffice for learning purpose.

from hashlib import sha256
import json

class Transaction:
    def __init__(self, _to, _from, amount, fee, timestamp):
        self._to = _to
        self._from = _from
        self.amount = amount
        self.fee = fee
        self.timestamp = timestamp
        self.hash = self.calculate_hash()
        self.signature = None
    def calculate_hash(self):
        return sha256((self._to + self._from + str(self.amount) + str(self.fee) + str(self.timestamp)).encode()).hexdigest()
    def __str__(self):
        return json.dumps({'_to': self._to,
                           '_from': self._from,
                           'amount': self.amount,
                           'fee': self.fee,
                           'timestamp': self.timestamp,
                           'hash': self.hash,
                           'signature': self.signature}, indent=4)

This simple transaction class can take all the needed inputs, calculate its own hash, and print out a pretty output when printed. However, notice that the signature is set to None. For the next step, if you do not already have the ECDSA module installed, you may have to enter pip install ecdsa into the command line to install it.

Adding on the the transaction class, we can now add the sign() and verify() functions to provide the functionality we want.

from ecdsa import SigningKey, VerifyingKey  <--At the top

def sign(self, signingKey): <--add these inside the transaction class
        self.signature = signingKey.sign(self.calculate_hash().encode()).hex()

def verify(self):
        try:
            VerifyingKey.from_string(bytes.fromhex(self._from)).verify(
                 bytes.fromhex(self.signature), self.hash.encode())
            return True
        except:
            return False

Now, we can test out our new transaction verification and signing functionalities in the Python shell:

>>> sk = SigningKey.generate()
>>> vk = sk.get_verifying_key()
>>> transaction = Transaction('kachangred', vk.to_string().hex(), 1000, 1, 0)
>>> print(transaction)
{
    "_to": "kachangred",
    "_from": "374b7b69fce3fb0fb318f0e8f63e4054df10095f0bdcf36f9b4971a37c9de8ac338cb79a0f0f4450aef2a0ea11740b0d",
    "amount": 1000,
    "fee": 1,
    "timestamp": 0,
    "hash": "320fe11aa0a44d7c67f162bd1b5429397da4da436a419e8f559760dd60670994",
    "signature": null
}

So far, we created our transaction, but we have not yet signed it. Notice the use of vk.to_string().hex(), which converts the public key to a human readable format, and is the reason the verify() function in the transactions has to use from_string() and bytes.fromhex() to convert it back to a Python Verifying Key object. Now, onto signing and verifying:

>>> transaction.sign(sk)
>>> print(transaction)
{
    "_to": "kachangred",
    "_from": "374b7b69fce3fb0fb318f0e8f63e4054df10095f0bdcf36f9b4971a37c9de8ac338cb79a0f0f4450aef2a0ea11740b0d",
    "amount": 1000,
    "fee": 1,
    "timestamp": 0,
    "hash": "320fe11aa0a44d7c67f162bd1b5429397da4da436a419e8f559760dd60670994",
    "signature": "a28892357c8408b70914f09aa698c53d50f5b31f6984f3218389dc3e6809b369de46bb0bc2e52d68e1d2b57518111f55"
}

Now that the transaction is signed and the signature is included, we can move on to verifying:

>>> transaction.verify()
True

Since the function returned True, that means the transaction was signed by the private key of the address that is attempted to send the funds, and is therefore a legitimate transaction.

If you have any questions or problems, feel free to ask them below. If you enjoyed this article, please upvote!

Sort:  

thanks! this is really useful!
I'll play around with your script to have a better understanding of blockchain for myself!

@therealwolf 's created platform smartsteem scammed my post this morning (mothersday) that was supposed to be for an Abused Childrens Charity. Dude literally stole from abused children that don't have mothers ... on mothersday.

https://steemit.com/steemit/@prometheusrisen/beware-of-smartsteem-scam