forked from jbernabe/rblockchain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblockchain.R
148 lines (145 loc) · 4.72 KB
/
blockchain.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
list.of.packages <- c("digest", "httr","jsonlite")
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
require(digest)
require(jsonlite)
require(httr)
Blockchain <- function ()
{
bc = list (
chain = list(),
currentTransactions = list(),
nodes = list()
)
#' Create a new Block in the Blockchain
#'
#' @param proof <int> The proof given by the Proof of Work algorithm
#' @param previousHash <str> Hash of previous Block
#' @return new block generated given the \code{proof} and the \code{previousHash}
#' @examples
#' blockchain = Blockchain()
#' blockchain$nextBlock(previousHash=1, proof=100) # genesis block
bc$nextBlock = function (proof, previousHash=NULL){
previousHash <- ifelse (is.null(previousHash), bc$hashBlock(bc$chain[length(bc$chain)]), previousHash)
block = list('block' = list('index' = length (bc$chain) + 1, 'timestamp' = as.numeric(Sys.time()) , 'transactions' = bc$currentTransactions, 'proof' = proof, 'previousHash' = previousHash))
bc$currentTransactions = NULL
bc$chain <- append(bc$chain, block)
return (block)
}
#' Returns the last block in the Blockchain
#'
#' @examples
#' blockchain$lastBlock()
bc$lastBlock = function () {
bc$chain[length(bc$chain)]
}
#' Register a new transaction in the Blockchain
#'
#' @param sender <str> address of the sender
#' @param recipient <str> address of the recipient
#' @param amount <int> transaction amount
#' @return <int> Index of the Block that will hold this transaction
bc$addTransaction = function (sender, recipient, amount)
{
txn <- list('transaction'= list('sender'=sender,'recipient'=recipient,'amount'=amount))
bc$currentTransactions <- append(bc$currentTransactions, txn)
last.block <- bc$lastBlock()
return(last.block$block$index + 1)
}
#' Hash a block using SHA256
#'
#' @param block <block>
#' @return <str> SHA256 hashed value for \code(block)
#' @examples
bc$hashBlock = function (block) {
require(digest)
digest(block,algo="sha256")
}
#' Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
#' p is the previous proof and p' is the new proof
#' @param last_proof <block>
#' @return <str> SHA256 hashed value for \code(block)
bc$proofOfWork <- function (last_proof)
{
proof <- 0
while (!bc$validProof(last_proof, proof))
{
proof <- proof + 1
}
return (proof)
}
#' Find a number p' such that hash(pp') ends with two zeroes, where p is the previous p'
#' p is the previous proof and p' is the new proof
#' @param last_proof <int> previous proof
#' @param proof <int> proof
#' @return <bool> TRUE if correct, FALSE if not
bc$validProof <- function (last_proof, proof)
{
guess = paste0(last_proof,proof)
guess_hash = digest(guess, algo = 'sha256')
return (gsub('.*(.{2}$)', '\\1',guess_hash) == "00")
}
#' Checks whether a given blockchain is valid
#'
#' @return <bool> TRUE if the chain is valid, FALSE otherwise
bc$validChain <- function (chain)
{
lastBlock <- chain[0]
currentIndex <- 1
while (currentIndex < length(chain))
{
block = chain[currentIndex]
# checking for valid linking
if (block$block$previousHash != bc$hashBlock(lastBlock)) {
return(FALSE)
}
# checking for proof validity
if(!bc$validProof(lastBlock$block$proof, block$block$proof))
{
return (FALSE)
}
lastBlock <- block
currentIndex <- currentIndex +1
}
return(TRUE)
}
#' Add a new node to the list of existing nodes
#'
#' @param address <str> full URL of the node
#' @examples
#' blockchain = Blockchain()
#' blockchain$registerNode('http://192.168.0.5:5000')
bc$registerNode <- function(address)
{
parsed_url = address
bc$nodes<- append(bc$nodes, parsed_url)
}
#' Resolve conflicts by replacing the current chain by the longest chain in the network
#'
#' @return <bool> TRUE if the chain was replaced, FALSE otherwise
bc$handleConflicts <- function()
{
neighbours <- bc$nodes
new_chain <- NULL
max_length = length(bc$chain)
for (i in 1:length(neighbours))
{
chain.node <- GET(paste0(neighbours[i],'/chain'))
node.chain.length <- jsonlite::fromJSON(chain.node)$length
node.chain.chain <- jsonlite::fromJSON(chain.node)$chain
if (node.chain.length > max_length)
{
new_chain = node.chain.chain
max_length<-node.chain.length
}
}
if (!is.null(new_chain))
{
bc$chain <- new_chain
}
}
# Adding bc to the environment
bc <- list2env(bc)
class(bc) <- "BlockchainClass"
return(bc)
}