RObservations #22: Creating a Simple Blockchain with R6 Objects

[This article was first published on r – bensstats, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Introduction

With my recent breakthrough in figuring out how OOP works in R by using R6 classes, a whole new world of possibilities is open to explore with it. While R is defined by Wikipedia) as “[…] a programming language for statistical computing and graphics” R6 classes allow for applications of the R programming language extend which extend beyond that limiting definition. Recently, I have been spending some time reading Cryptocurrency Investing for Dummies (affiliate link), so I thought why not jump in the deep-end with trying to create a Blockchain using R!

In this blog I explore creating a simple Blockchain with proof-of-work mining in R with R6 classes and how its possible to make your own “cryptocurrency” using R. This could be the start of something big with getting R to compete with C++, Javascript and Solidity for Blockchain development – or it could just be a cool blog (which it likely is).

Anyways, lets get into it.

How I did it

While a brief Google search shows that I wasn’t the first person to explore this idea (see here, here,
and here). However, none of the sources attempted to use R6 objects. The way I figured out how to do this was from watching Savjee’s videos on the topic in Javascript and following along in R. You can check out the videos I used below. Note that the language of instruction is in Javascript and not R.

Creating a Block

The packages used for this are R6 for creating the block and Blockchain objects and openssl for the SHA-256 hash function which is used for encryption. For creating a block we create a object by using the R6Class() function and add the following characteristics:

  • The block index – user defined
  • The time stamp – user defined; when the transaction took place.
  • The data – user defined; this could be anything but for our case we’ll go with the currency transactions.
  • The hash of the previous block; Its possible to change it, but doing so invalidates the Blockchain.
  • The hash of the block; automatically created using the block’s information and encrypts using the SHA-256 hash function (for our case).
  • The nonce- a random value; relevant for block mining.

The relevant methods for the block are:

  • A “calculate hash” method- this essentially combines all the information added to the block into one single string and generates a hash based on it with SHA-256. Any non-string data is coerced to a string with the toString() function.
  • A “mine block” function. As part of a measure of security, the blocks are “mined” by trying to guess the hash generated. The difficulty of mining is controlled in the BlockChain object (discussed later). The method of mining is proof of work, so the speed of the calculation really depends on your CPU (or GPU if you are running R off of it).

Without getting too much into how the code works. The R code for creating a block is the following. It is pretty much verbatim to the Javascript code in Savjee’s videos but in R obeying the R6 syntax.

library(R6)
library(openssl)


Block <- R6Class(classname="Block",
                 list(
                   index = 0,
                   timestamp = NULL,
                   data= NULL,
                   previousHash = '',
                   hash = '',
                   nonce=0,

                   calculateHash=function(){
                     return(
                       sha256(
                       paste0(c(toString(self$index),
                              self$previousHash,
                              self$timestamp,
                              toString(self$data),
                              toString(self$nonce)),
                              collapse=";")
                       )
                     )
                   },

                    mineBlock = function(difficulty){
                     cat("Mining Block...\n")

                     while(substr(self$hash,
                                  1,
                                   difficulty)!= paste0(rep('',
                                                            difficulty+1),
                                                        collapse='0')
                            ){
                        self$nonce <-self$nonce+1
                        #print(c("Nonce updated to:", toString(self$nonce)))
                        self$hash <- self$calculateHash()
                      }

                     cat(c("Block Added:", self$hash))
                   }
                   ,
                   initialize= function(index=NA,
                                        timestamp=NA,
                                        data=NA,
                                        previousHash=NA,
                                        hash=NA,
                                        nonce=0){
                     self$index  <- index
                     self$timestamp<-timestamp 
                     self$data <- data
                     self$previousHash <- previousHash
                     self$hash<-self$calculateHash()
                     self$nonce<-nonce
                   }
                 )
                 )

Creating a Blockchain

For creating a Blockchain, another object is required. This requires two specific characteristics.

  1. The “chain” where the blocks can be added and accessed.
  2. The mining difficulty- something that can be user defined. Defines how difficult the mining should be. If blocks are more difficult to mine, it will take longer for each one to be mined.

The methods contained in this Blockchain are:

  • A Genesis block creation method- essentially initializes the Blockchain with its first block. This is defined explicitly within the code below, but its possible to make it user defined.
  • An “add block” method- this method allows blocks to be appended to the Blockchain.
  • A “get latest block” method- as its titled; gets the most recent block added to the Blockchain for our viewing pleasure.
  • A chain validion method – checks if the Blockchain was altered. Returns FALSE if it has, TRUE if it has not.
BlockChain<- R6Class(classname = "BlockChain",
                     list(
                       chain = list(),
                       miningDifficulty = 4,
                       createGeneisisBlock= function(){
                         return(Block$new(0,
                                          Sys.time(),
                                          '',
                                          "Genesis Block",
                                          "0"))
                       },

                       addBlock = function(newBlock){
                         newBlock$previousHash <- self$getLatestBlock()$hash
                         newBlock$mineBlock(self$miningDifficulty)
                         self$chain<-append(self$chain, newBlock)
                       },

                         getLatestBlock=function(){
                         return(self$chain[length(self$chain)][[1]])
                       },
                       isChainValid = function(){

                         for(i in 2:(length(self$chain))){
                           currentBlock <- self$chain[i][[1]]
                           previousBlock <- self$chain[i-1][[1]]

                           if(currentBlock$hash != currentBlock$calculateHash()){
                             return(FALSE)
                           }

                           if(currentBlock$previousHash != previousBlock$hash){
                             return(FALSE)
                           }

                           return(TRUE)
                         }


                       },
                       initialize=function(chain=list()){
                          self$chain<- append(chain,self$createGeneisisBlock())
                       }

                     )
)

Putting it all together.

To put this together, first define the Blockchain and create the Genesis block. Next add the blocks accordingly. If you run this code on your machine you will notice a delay between the block being mined and the block added. This is because your CPU/GPU is trying to guess the block’s hash.

Below is the code for a made up cryptocurrency called “R-Coin” and the data consists of the amount and alpha which is an arbitrary value.

# Create the blockchain with a genesis block

R_coin<- BlockChain$new()

# Mining Block 1

R_coin$addBlock(Block$new(index=1,
                          timestamp="2022-01-10",
                          data=list("amount"=20,
                                    "alpha"=0.2)))


## Mining Block...
## Block Added: 000096e53e6d0f5742af8a47dd24a1486b1db3dcab734ab9342fb5f98e9544fc


# Mining Block 2

R_coin$addBlock(Block$new(index=2,
                          timestamp="2022-01-11",
                          data=list("amount"=5,
                                    "alpha"= 1)))


## Mining Block...
## Block Added: 000045b945f9d9add02bb75c9dd250f1c4a285cb5bf6aef2329576662b892421


# Mining Block 3

R_coin$addBlock(Block$new(index=2,
                          timestamp="2022-01-12",
                          data=list("amount"=200,
                                    "alpha"=0.5)))


## Mining Block...
## Block Added: 00009c9845678909037d594db94a9be503f8a2f204e5e4b8d04a763010de60c8

After the blocks have been added, we can see what the Blockchain looks like:

R_coin$chain


## [[1]]
## <Block>
##   Public:
##     calculateHash: function () 
##     clone: function (deep = FALSE) 
##     data: 
##     hash: 7bc9e4bc6769e0e07e9d1b0f021557553e5e8d78a6f3f8dc826c034f ...
##     index: 0
##     initialize: function (index = NA, timestamp = NA, data = NA, previousHash = NA, 
##     mineBlock: function (difficulty) 
##     nonce: 0
##     previousHash: Genesis Block
##     timestamp: 2022-01-09 12:58:45
## 
## [[2]]
## <Block>
##   Public:
##     calculateHash: function () 
##     clone: function (deep = FALSE) 
##     data: list
##     hash: 000096e53e6d0f5742af8a47dd24a1486b1db3dcab734ab9342fb5f9 ...
##     index: 1
##     initialize: function (index = NA, timestamp = NA, data = NA, previousHash = NA, 
##     mineBlock: function (difficulty) 
##     nonce: 41257
##     previousHash: 7bc9e4bc6769e0e07e9d1b0f021557553e5e8d78a6f3f8dc826c034f ...
##     timestamp: 2022-01-10
## 
## [[3]]
## <Block>
##   Public:
##     calculateHash: function () 
##     clone: function (deep = FALSE) 
##     data: list
##     hash: 000045b945f9d9add02bb75c9dd250f1c4a285cb5bf6aef232957666 ...
##     index: 2
##     initialize: function (index = NA, timestamp = NA, data = NA, previousHash = NA, 
##     mineBlock: function (difficulty) 
##     nonce: 30814
##     previousHash: 000096e53e6d0f5742af8a47dd24a1486b1db3dcab734ab9342fb5f9 ...
##     timestamp: 2022-01-11
## 
## [[4]]
## <Block>
##   Public:
##     calculateHash: function () 
##     clone: function (deep = FALSE) 
##     data: list
##     hash: 00009c9845678909037d594db94a9be503f8a2f204e5e4b8d04a7630 ...
##     index: 2
##     initialize: function (index = NA, timestamp = NA, data = NA, previousHash = NA, 
##     mineBlock: function (difficulty) 
##     nonce: 10873
##     previousHash: 000045b945f9d9add02bb75c9dd250f1c4a285cb5bf6aef232957666 ...
##     timestamp: 2022-01-12

We can view the most recent block and also check if the chain is valid.

R_coin$getLatestBlock()


## <Block>
##   Public:
##     calculateHash: function () 
##     clone: function (deep = FALSE) 
##     data: list
##     hash: 00009c9845678909037d594db94a9be503f8a2f204e5e4b8d04a7630 ...
##     index: 2
##     initialize: function (index = NA, timestamp = NA, data = NA, previousHash = NA, 
##     mineBlock: function (difficulty) 
##     nonce: 10873
##     previousHash: 000045b945f9d9add02bb75c9dd250f1c4a285cb5bf6aef232957666 ...
##     timestamp: 2022-01-12


R_coin$isChainValid()


## [1] TRUE

If we were to change a block our chain would be invalid. This can also be verified.

#Alter blockchain

R_coin$chain[[2]]$data <-list("amount"=2,
                              "alpha"= 0)

R_coin$isChainValid()


## [1] FALSE

Conclusion

There you have it! What I covered in this blog was only two parts of the five-part series Simply Explained has posted on YouTube. Getting to program a Blockchain myself has given me a better idea of what this technology is beyond just having read about I would be interested in exploring is how to go about hosting a network across multiple computers and getting over the hurdle of doing it in R as well. I presently haven’t found any specific resources on this topic yet so if you know about it, let me know!

Thank you for reading my blog! Be sure to subscribe to see more content!

Want to see more of my content?

Be sure to subscribe and never miss an update!

To leave a comment for the author, please follow the link and comment on their blog: r – bensstats.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)