Creating a cryptocurrency - Creating transactions, mining rewards, mint and gas fee

FreakCdev - Nov 26 '21 - - Dev Community

Hi all, in the previous article, I have covered how to create a simple Blockchain in just 60 lines of code. So today, I will start working on the second part of the series - transactions.

Please read the previous article first if you haven't known anything about blockchains yet.

Also, if this is already too familiar for you, consider checking out the third article about how to create a p2p network and release your cryptocurrency.

Also, you should definitely check out this tutorial on my new videos on Youtube for detailed information:

What we are trying to achieve

Basically, we need to have a representation of a transaction that includes the sender's wallet address, the receiver's wallet address and the amount we are sending. We will add it to a transaction pool, and when we create a new block, we will move all pending transactions to that block's data.

To prevent faulty transactions, we will use a signing mechanism along with a key pair. That key pair will include 2 keys: a private key, and a public key. The public key can be shown to others as a wallet address, the private key is used for signing transactions. Because only you hold the private key, only you can sign your account's transactions, ensuring safety.

We will talk about some other stuff like minting, initial coin release and gas fee.

No more saying, let's start coding!

The transaction class

So we will have a basic class like this:

class Transaction {
    constructor(from, to, amount) {
        this.from = from;
        this.to = to;
        this.amount = amount;
    }
}
Enter fullscreen mode Exit fullscreen mode

Mining transactions

Moving back to the Blockchain class, first of all, we need a transactions pool which holds all the pending transactions.

    this.transactions = [];
Enter fullscreen mode Exit fullscreen mode

Now, we will have a method to create a transaction:

    addTransaction(transaction) {
        this.transactions.push(transaction);
    }
Enter fullscreen mode Exit fullscreen mode

Mining the transactions:

    mineTransactions() {
        this.addBlock(new Block(Date.now().toString(), this.transactions));

        this.transactions = [];
    }
Enter fullscreen mode Exit fullscreen mode

We just basically pass in the pending transactions and then clear the current pending transactions pool.

Mining reward

No one would like to lose their computational power to mine transactions for you for free, so you need to have some form of reward for the miners/validators.

First, we will have a mining reward property, you can set it to whatever you like, I'll set it to 297 for no real reason.

    this.reward = 297;
Enter fullscreen mode Exit fullscreen mode

Now, we shall create a transaction that transfers the reward to the miner.

    mineTransactions(rewardAddress) {
        this.addBlock(new Block(Date.now().toString(), [new Transaction(CREATE_REWARD_ADDRESS, rewardAddress, this.reward), ...this.transactions]));

        // Right now, we are just going assume the "from" address is something like this,
        // we will get back to this later in the next part of the article.
        this.transactions = [];
    }
Enter fullscreen mode Exit fullscreen mode

Minting

This is a well-known term in blockchain development, it simply refers to the act of adding more coins, or printing more money to be simple. When the chain gives miners reward, it is actually minting coins in the mean time.

Signing

We can't miss the signing mechanism, it's ESSENTIAL!

Before we proceed, it's important to do some research about signing first, it's a popular mechanism in cryptography.

I will use an algorithm used by Bitcoin and Ethereum - secp256k1 for generating key pairs.

Since we are staying simple, we wouldn't want to implement the algorithm on our own since it's terribly long and can even be longer than this article.

We are using a package called elliptic, note that elliptic also supports Curve25519 and other algorithms.

Install it through npm:

npm i elliptic
Enter fullscreen mode Exit fullscreen mode

Generate a keypair

This is an example of generating a key pair:

const EC = require("elliptic").ec, ec = new EC("secp256k1");

const keyPair = ec.genKeyPair();
// public key: keyPair.getPublic("hex")
// private key: keyPair.getPrivate("hex")
Enter fullscreen mode Exit fullscreen mode

Signing transactions

Create a sign method in the Transaction class:

    sign(keyPair) {
        // Check if the public key matches the "from" address of the transaction
        if (keyPair.getPublic("hex") === this.from) {
            // Sign the transaction
            this.signature = keyPair.sign(SHA256(this.from + this.to + this.amount), "base64").toDER("hex");
        }
    }
Enter fullscreen mode Exit fullscreen mode

Validation

The chain is valid when all blocks have valid transactions, transactions are valid only when:

  • From, to, amount are not empty.
  • Sender's address has more money than the amount sent.
  • The signature matches with the data of the transaction.

First, we will create a method in the Blockchain class to get an address's balance for convenience.

We can implement a method to get an address's balance based on the transaction history of the chain:

    getBalance(address) {
        let balance = 0;

        this.chain.forEach(block => {
            block.data.forEach(transaction => {
                // Because if you are the sender, you are sending money away, so your balance will be decremented.
                if (transaction.from === address) {
                    balance -= transaction.amount;
                }

                // But if you are the receiver, you are receiving money, so your balance will be incremented.
                if (transaction.to === address) {
                    balance += transaction.amount;
                }
            })
        });

        return balance;
    }
Enter fullscreen mode Exit fullscreen mode

So we will have a method like this in our Transaction class:

    isValid(tx, chain) {
        return (
            tx.from &&
            tx.to &&
            tx.amount &&
            chain.getBalance(tx.from) >= tx.amount &&
            ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount + tx.gas), tx.signature)
        );
    }
Enter fullscreen mode Exit fullscreen mode

Inside the Block class, create a method to check if it has valid transactions or not.

    hasValidTransactions(chain) {
        return this.data.every(transaction => transaction.isValid(transaction, chain));
    }
Enter fullscreen mode Exit fullscreen mode

Update the isValid method of the Blockchain class:

    if (
        currentBlock.hash !== currentBlock.getHash() || 
        prevBlock.hash !== currentBlock.prevHash || 
        !currentBlock.hasValidTransactions(blockchain)
    ) {
        return false;
    }
Enter fullscreen mode Exit fullscreen mode

Now, we also need to check if a transaction is valid before pushing it to the pool:

    addTransaction(transaction) {
        if (transaction.isValid(transaction, this)) {
            this.transactions.push(transaction);
        }
    }
Enter fullscreen mode Exit fullscreen mode

Now, let's get back to minting as I have promised. First, I'll create an address just for minting.

const MINT_KEY_PAIR = ec.genKeyPair();
const MINT_PUBLIC_ADDRESS = MINT_KEY_PAIR.getPublic("hex");
Enter fullscreen mode Exit fullscreen mode

New method:

    mineTransactions(rewardAddress) {
        // Create a mint transaction for reward.
        const rewardTransaction = new Transaction(MINT_PUBLIC_ADDRESS, rewardAddress, this.reward);
        rewardTransaction.sign(MINT_KEY_PAIR);

        // We will add the reward transaction into the pool.
        this.addBlock(new Block(Date.now().toString(), [rewardTransaction, ...this.transactions]));

        this.transactions = [];
    }
Enter fullscreen mode Exit fullscreen mode

Chain's address will have an exception: Its balance will not be checked since we are printing money, so we need to update Transaction.isValid as well. Also, its amount must be the exact same as the defined reward.

    isValid(tx, chain) {
        return (
            tx.from &&
            tx.to &&
            tx.amount &&
            (chain.getBalance(tx.from) >= tx.amount || tx.from === MINT_PUBLIC_ADDRESS) &&
            ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount), tx.signature)
        );
    }
Enter fullscreen mode Exit fullscreen mode

Releasing the first few coins ever

Going back to the Blockchain class, we will make some changes to our genesis block. We will mint some coins for one address (creating a max diluted marketcap). If anyone wants to buy our currency, they will lend us some money, and we will send them coins. This is also called a contract.

Create the keyPair first

const holderKeyPair = ec.genKeyPair();
Enter fullscreen mode Exit fullscreen mode

In the genesis block, simply create a transaction for inital coin release.

    // We will release 100000 coin
    const initalCoinRelease = new Transaction(MINT_PUBLIC_ADDRESS, holderKeyPair.getPublic("hex"), 100000);
    this.chain = [new Block(Date.now().toString(), [initalCoinRelease])];
Enter fullscreen mode Exit fullscreen mode

The problem with minting

If you are wondering if anyone can access the minting address, can't we print out a lot and a lot faulty money? You would be right, but we are going to handle all of our problems with a peer-to-peer network, which I'm going to make in the next article.

The peer-to-peer network handles this problem by simply dismissing the chain of which block:

  • Has more or less than 1 transaction for minting.
  • Has lower than 1 transaction other than the mint transaction, it basically means that he's constantly minting blocks without actually producing some real transactions.

Gas fees

There is also a kind of miner's reward called gas fee, but it's a little different. It's basically user's reward for miners. This makes mining more appealing to miners, and it also pays for the energy used for mining, and people would have to pay a higher gas fee to be picked by miners quicker.

We are adding the gas property into our Transaction class.

    class Transaction {
        // Gas will be set to 0 because we are making it optional
        constructor(from, to, amount, gas = 0) {
            this.from = from;
            this.to = to;
            this.amount = amount;
            this.gas = gas;
        }

        sign(keyPair) {
            if (keyPair.getPublic("hex") === this.from) {
                // Add gas
                this.signature = keyPair.sign(SHA256(this.from + this.to + this.amount + this.gas), "base64").toDER("hex");
            }
        }

        isValid(tx, chain) {
            return (
                tx.from &&
                tx.to &&
                tx.amount &&
                // Add gas
                (chain.getBalance(tx.from) >= tx.amount + tx.gas || tx.from === MINT_PUBLIC_ADDRESS) &&
                ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount + tx.gas), tx.signature)
            );
        }
    }
Enter fullscreen mode Exit fullscreen mode

We will update the getBalance method too:

    getBalance(address) {
        let balance = 0;

        this.chain.forEach(block => {
            block.data.forEach(transaction => {
                if (transaction.from === address) {
                    balance -= transaction.amount;
                    balance -= transaction.gas
                }

                if (transaction.to === address) {
                    balance += transaction.amount;
                }
            })
        });

        return balance;
    }
Enter fullscreen mode Exit fullscreen mode

Now, we should give the gas fee to the miner:

    mineTransactions(rewardAddress) {
        let gas = 0;

        this.transactions.forEach(transaction => {
            gas += transaction.gas;
        });

        const rewardTransaction = new Transaction(MINT_PUBLIC_ADDRESS, rewardAddress, this.reward + gas);
        rewardTransaction.sign(MINT_KEY_PAIR);

        // Prevent people from minting coins and mine the minting transaction.
        if (this.transactions.length !== 0) this.addBlock(new Block(Date.now().toString(), [rewardTransaction, ...this.transactions]));

        this.transactions = [];
    }
Enter fullscreen mode Exit fullscreen mode

Our validation method for the block must also be changed:

    hasValidTransactions(chain) {
        let gas = 0, reward = 0;

        this.data.forEach(transaction => {
            if (transaction.from !== MINT_PUBLIC_ADDRESS) {
                gas += transaction.gas;
            } else {
                reward = transaction.amount;
            }
        });

        return (
            reward - gas === chain.reward &&
            this.data.every(transaction => transaction.isValid(transaction, chain)) && 
            this.data.filter(transaction => transaction.from === MINT_PUBLIC_ADDRESS).length === 1
        );
    }
Enter fullscreen mode Exit fullscreen mode

Testing

// Your original balance is 100000

const girlfriendWallet = ec.genKeyPair();

// Create a transaction
const transaction = new Transaction(holderKeyPair.getPublic("hex"), girlfriendWallet.getPublic("hex"), 100, 10);
// Sign the transaction
transaction.sign(holderKeyPair);
// Add transaction to pool
JeChain.addTransaction(transaction);
// Mine transaction
JeChain.mineTransactions(holderKeyPair.getPublic("hex"));

// Prints out balance of both address
console.log("Your balance:", JeChain.getBalance(holderKeyPair.getPublic("hex")));
console.log("Your girlfriend's balance:", JeChain.getBalance(girlfriendWallet.getPublic("hex")));
Enter fullscreen mode Exit fullscreen mode

It should look like this:
Image description

So, that's it! Here's the full source code if you want:

Honourable mention

The code from this video was made by me and my friend, whose name is Apple.
You can check his Github account in here: https://github.com/apple096/

Resources

Find me on:

Check out the Youtube version of this article:

. . . . . . . . .