Creating transactions, mining rewards, mint and gas fee

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 any…


This content originally appeared on DEV Community and was authored by FreakCdev

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, 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;
    }
}

Mining transactions

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

    this.transactions = [];

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

    addTransaction(transaction) {
        this.transactions.push(transaction);
    }

Mining the transactions:

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

        this.transactions = [];
    }

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;

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 = [];
    }

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

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")

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");
        }
    }

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;
    }

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)
        );
    }

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));
    }

Update the isValid method of the Blockchain class:

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

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);
        }
    }

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");

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 = [];
    }

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 && tx.amount === this.reward) &&
            ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount), tx.signature)
        );
    }

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();

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])];

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 server, which I'm going to make in the next article.

The peer-to-peer server 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 && tx.amount === this.reward) &&
                ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount + tx.gas), tx.signature)
            );
        }
    }

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;
    }

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 = [];
    }

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")));

It should look like this:
Image description

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

Resources

Find me on:

Check out the Youtube version of this article:


This content originally appeared on DEV Community and was authored by FreakCdev


Print Share Comment Cite Upload Translate Updates
APA

FreakCdev | Sciencx (2021-11-26T10:16:38+00:00) Creating transactions, mining rewards, mint and gas fee. Retrieved from https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/

MLA
" » Creating transactions, mining rewards, mint and gas fee." FreakCdev | Sciencx - Friday November 26, 2021, https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/
HARVARD
FreakCdev | Sciencx Friday November 26, 2021 » Creating transactions, mining rewards, mint and gas fee., viewed ,<https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/>
VANCOUVER
FreakCdev | Sciencx - » Creating transactions, mining rewards, mint and gas fee. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/
CHICAGO
" » Creating transactions, mining rewards, mint and gas fee." FreakCdev | Sciencx - Accessed . https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/
IEEE
" » Creating transactions, mining rewards, mint and gas fee." FreakCdev | Sciencx [Online]. Available: https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/. [Accessed: ]
rf:citation
» Creating transactions, mining rewards, mint and gas fee | FreakCdev | Sciencx | https://www.scien.cx/2021/11/26/creating-transactions-mining-rewards-mint-and-gas-fee/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.