Skip to content

Commit 9d350a3

Browse files
authored
KTLN-856: Implement a blockchain in Kotlin (#1038)
1 parent 6fba929 commit 9d350a3

File tree

6 files changed

+108
-0
lines changed

6 files changed

+108
-0
lines changed

kotlin-blockchain/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## Relevant Articles

kotlin-blockchain/pom.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<artifactId>kotlin-blockchain</artifactId>
6+
<name>kotlin-blockchain</name>
7+
<packaging>jar</packaging>
8+
9+
<parent>
10+
<groupId>com.baeldung</groupId>
11+
<artifactId>kotlin-modules</artifactId>
12+
<version>1.0.0-SNAPSHOT</version>
13+
</parent>
14+
15+
</project>
16+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.baeldung.chain
2+
3+
import java.security.MessageDigest
4+
5+
data class Block(
6+
val previousHash: String,
7+
val data: String,
8+
val timestamp: Long = System.currentTimeMillis(),
9+
val nonce: Int = 0
10+
) {
11+
12+
val hash: String by lazy { calculateHash() }
13+
14+
fun calculateHash(): String {
15+
val input = "$previousHash$data$timestamp$nonce"
16+
return input.sha256()
17+
}
18+
}
19+
20+
fun mineBlock(previousHash: String, data: String, acceptingRegexp: Regex): Block {
21+
var finalBlock = Block(previousHash, data)
22+
while (!finalBlock.hash.matches(acceptingRegexp)) {
23+
finalBlock = finalBlock.copy(nonce = finalBlock.nonce + 1)
24+
}
25+
return finalBlock
26+
}
27+
28+
fun String.sha256(): String {
29+
val bytes = this.toByteArray()
30+
val md = MessageDigest.getInstance("SHA-256")
31+
val digest = md.digest(bytes)
32+
return digest.fold("") { str, it -> str + "%02x".format(it) }
33+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.baeldung.chain
2+
3+
class Blockchain(difficulty: Int = 5) {
4+
val chain = mutableListOf<Block>()
5+
val acceptanceRegex = "^[0]{$difficulty}.+".toRegex();
6+
7+
init {
8+
val genesisBlock = mineBlock("0", "Genesis Block", acceptanceRegex)
9+
chain.add(genesisBlock)
10+
}
11+
12+
fun addBlock(block: Block) {
13+
if (isValidBlock(block)) {
14+
chain.add(block)
15+
} else {
16+
throw IllegalArgumentException("Invalid block")
17+
}
18+
}
19+
20+
private fun isValidBlock(block: Block): Boolean {
21+
val lastBlock = chain.last()
22+
return block.previousHash == lastBlock.hash &&
23+
block.hash.matches(acceptanceRegex)
24+
}
25+
26+
fun isValid(): Boolean {
27+
if (chain.size < 2) return true
28+
29+
chain.zipWithNext().forEach { (prev, current) ->
30+
if (current.previousHash != prev.hash) {
31+
return false
32+
}
33+
}
34+
return true
35+
}
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.chain
2+
3+
fun main() {
4+
val blockchain = Blockchain(3)
5+
6+
val block1 = mineBlock(blockchain.chain.last().hash, "Block 1 Data", blockchain.acceptanceRegex)
7+
blockchain.addBlock(block1)
8+
9+
val block2 = mineBlock(blockchain.chain.last().hash, "Block 2 Data", blockchain.acceptanceRegex)
10+
blockchain.addBlock(block2)
11+
12+
val block3 = mineBlock(blockchain.chain.last().hash, "Block 3 Data", blockchain.acceptanceRegex)
13+
blockchain.addBlock(block3)
14+
15+
println("Blockchain valid? ${blockchain.isValid()}")
16+
17+
blockchain.chain.forEach {
18+
println("Block Data: ${it.data}, Hash: ${it.hash}")
19+
}
20+
}

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@
528528
<module>kotlin-rsocket</module>
529529
<module>kotlin-self-executable-jar</module>
530530
<module>kotlin-spark</module>
531+
<module>kotlin-blockchain</module>
531532
<module>kotlin-testing</module>
532533
<module>kotlin-mockito</module>
533534
<!-- <module>kotlin-tornadofx</module> --> <!-- not compatible with Java 9+ -->
@@ -610,6 +611,7 @@
610611
<module>kotlin-rsocket</module>
611612
<module>kotlin-self-executable-jar</module>
612613
<module>kotlin-spark</module>
614+
<module>kotlin-blockchain</module>
613615
<module>kotlin-testing</module>
614616
<module>kotlin-mockito</module>
615617
<!-- <module>kotlin-tornadofx</module> --> <!-- not compatible with Java 9+ -->

0 commit comments

Comments
 (0)