Skip to content

Commit 1d83b85

Browse files
author
Juraj Veverka
committed
created blockchain demo
1 parent 409403c commit 1d83b85

File tree

18 files changed

+770
-0
lines changed

18 files changed

+770
-0
lines changed

block-chain/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.gradle
2+
.settings
3+
bin
4+
build
5+
build_gradle
6+
out
7+
.classpath
8+
.project
9+
java-logging.log
10+
*.iml
11+
.idea

block-chain/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Simple BlockChain demo
2+
3+
This is simple block-chain demo for java, using sha256 hashing algorithm.
4+
5+
There are two block-chain implementations.
6+
1. simple one in ```itx.examples.blockchain.simple``` which does not require proof-of-work (mining) for next block to be
7+
added into ledger. This implementation is much faster.
8+
2. advanced one in ```itx.examples.blockchain.advanced``` which requires proof-of-work (mining) for next block to be added into ledger.
9+
When new block is added into ledger, it's hash and nonce is setup to meed certain criteria. This procedure takes computing time.
10+
This implementation is inspired by [this](https://www.youtube.com/watch?v=_160oMzblY8&t=204s) video tutorial.
11+
12+
13+
### Example of use
14+
```
15+
LedgerBuilder ledgerBuilder = new LedgerBuilder();
16+
ledgerBuilder.setId("ledger 1");
17+
ledgerBuilder.setHashPrefix("00"); //<- this is only for advanced ledger
18+
ledgerBuilder.addData("data 1");
19+
ledgerBuilder.addData("data 2");
20+
ledgerBuilder.addData("data 3");
21+
ledgerBuilder.addData("data 4");
22+
23+
Ledger ledger = ledgerBuilder.build();
24+
25+
boolean ledgerOk = BlockChainUtils.verifyLedger(ledger);
26+
```
27+
28+
#### Build
29+
```gradle clean build```

block-chain/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
apply plugin: 'java'
3+
apply plugin: 'maven'
4+
5+
sourceCompatibility = 10
6+
targetCompatibility = 10
7+
8+
repositories {
9+
mavenCentral()
10+
}
11+
12+
dependencies {
13+
compile 'org.slf4j:slf4j-api:1.8.0-beta2'
14+
compile 'org.slf4j:slf4j-simple:1.8.0-beta2'
15+
testCompile 'org.testng:testng:6.14.3'
16+
}
17+
18+
test {
19+
// enable TestNG support (default is JUnit)
20+
useTestNG()
21+
testLogging {
22+
events "passed", "skipped", "failed"
23+
}
24+
}
25+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package itx.examples.blockchain;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.security.MessageDigest;
5+
import java.security.NoSuchAlgorithmException;
6+
7+
public final class CommonUtils {
8+
9+
private CommonUtils() {
10+
}
11+
12+
private static MessageDigest digest;
13+
14+
static {
15+
try {
16+
digest = MessageDigest.getInstance("SHA-256");
17+
} catch (NoSuchAlgorithmException e) {
18+
digest = null;
19+
}
20+
}
21+
22+
public static final String GENESIS_BLOCK_ID = "0";
23+
public static final String GENESIS_BLOCK_PREVIOUS_HASH = "0000000000000000000000000000000000000000000000000000000000000000";
24+
25+
public static String getNextBlockId(String lastBlockId) {
26+
Long id = Long.parseLong(lastBlockId) + 1;
27+
return id.toString();
28+
}
29+
30+
public static String createSHA256Hash(String data) {
31+
byte[] encodedHash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
32+
return bytesToHex(encodedHash);
33+
}
34+
35+
public static String bytesToHex(byte[] bytes) {
36+
StringBuffer result = new StringBuffer();
37+
for (byte byt : bytes) result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
38+
return result.toString();
39+
}
40+
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package itx.examples.blockchain.advanced;
2+
3+
public class Block {
4+
5+
private String id;
6+
private String nonce;
7+
private String data;
8+
private String previousHash;
9+
private String hash;
10+
11+
public Block(String id, String nonce, String data, String previousHash, String hash) {
12+
this.id = id;
13+
this.nonce = nonce;
14+
this.data = data;
15+
this.previousHash = previousHash;
16+
this.hash = hash;
17+
}
18+
19+
public String getId() {
20+
return id;
21+
}
22+
23+
public String getNonce() {
24+
return nonce;
25+
}
26+
27+
public String getData() {
28+
return data;
29+
}
30+
31+
public String getHash() {
32+
return hash;
33+
}
34+
35+
public String getPreviousHash() {
36+
return previousHash;
37+
}
38+
39+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package itx.examples.blockchain.advanced;
2+
3+
public class BlockBuilder {
4+
5+
private String id;
6+
private String nonce;
7+
private String data;
8+
private String previousHash;
9+
10+
public BlockBuilder setId(String id) {
11+
this.id = id;
12+
return this;
13+
}
14+
15+
public BlockBuilder setNonce(String nonce) {
16+
this.nonce = nonce;
17+
return this;
18+
}
19+
20+
public BlockBuilder setData(String data) {
21+
this.data = data;
22+
return this;
23+
}
24+
25+
public BlockBuilder setPreviousHash(String previousHash) {
26+
this.previousHash = previousHash;
27+
return this;
28+
}
29+
30+
public BlockBuilder from (Block block) {
31+
this.id = block.getId();
32+
this.nonce = block.getNonce();
33+
this.data = block.getData();
34+
this.previousHash = block.getPreviousHash();
35+
return this;
36+
}
37+
38+
public Block build() {
39+
return new Block(id, nonce, data, previousHash, BlockChainUtils.createSHA256Hash(id, nonce, data, previousHash));
40+
}
41+
42+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package itx.examples.blockchain.advanced;
2+
3+
import itx.examples.blockchain.CommonUtils;
4+
5+
public final class BlockChainUtils {
6+
7+
private BlockChainUtils() {
8+
}
9+
10+
private static final String ALPHABET = "0123456789abcdefghijklmnopqrstuvxywz";
11+
12+
public static String getNextNonce(String lastNonce) {
13+
if (lastNonce == null) return String.valueOf(ALPHABET.charAt(0));
14+
int maxIndex = ALPHABET.length() - 1;
15+
StringBuilder nextNonce = new StringBuilder(lastNonce);
16+
int remainder = 0;
17+
for (int i=(lastNonce.length()-1); i>=0; i--) {
18+
int nextIndex = ALPHABET.indexOf(lastNonce.charAt(i)) + 1;
19+
if (nextIndex <= maxIndex) {
20+
nextNonce.setCharAt(i, ALPHABET.charAt(nextIndex));
21+
remainder = 0;
22+
break;
23+
} else {
24+
nextNonce.setCharAt(i, ALPHABET.charAt(0));
25+
remainder = 1;
26+
}
27+
}
28+
if (remainder != 0) {
29+
return ALPHABET.charAt(0) + nextNonce.toString();
30+
} else {
31+
return nextNonce.toString();
32+
}
33+
}
34+
35+
public static String createSHA256Hash(Block block) {
36+
String blockData = block.getId() + block.getNonce() + block.getData() + block.getPreviousHash();
37+
return CommonUtils.createSHA256Hash(blockData);
38+
}
39+
40+
public static String createSHA256Hash(String id, String nonce, String data, String previousHash) {
41+
String blockData = id + nonce + data + previousHash;
42+
return CommonUtils.createSHA256Hash(blockData);
43+
}
44+
45+
public static boolean isValid(Block block, String hashPrefix) {
46+
return block.getHash().startsWith(hashPrefix);
47+
}
48+
49+
public static Block mineGenesisBlock(String data, String hashPrefix) {
50+
String nextId = CommonUtils.GENESIS_BLOCK_ID;
51+
return mineForValidBlock(nextId, data, CommonUtils.GENESIS_BLOCK_PREVIOUS_HASH, hashPrefix);
52+
}
53+
54+
public static Block mineNextBlock(Block lastBlock, String data, String hashPrefix) {
55+
String nextId = CommonUtils.getNextBlockId(lastBlock.getId());
56+
return mineForValidBlock(nextId, data, lastBlock.getHash(), hashPrefix);
57+
}
58+
59+
private static Block mineForValidBlock(String id, String data, String previousHash, String hashPrefix) {
60+
String nonce = getNextNonce(null);
61+
Block nextBlock = new BlockBuilder()
62+
.setId(id)
63+
.setNonce(nonce)
64+
.setData(data)
65+
.setPreviousHash(previousHash)
66+
.build();
67+
while (true) {
68+
if (isValid(nextBlock, hashPrefix)) break;
69+
nonce = getNextNonce(nonce);
70+
nextBlock = new BlockBuilder()
71+
.from(nextBlock)
72+
.setNonce(nonce)
73+
.build();
74+
}
75+
return nextBlock;
76+
}
77+
78+
public static boolean verifyBlock(Block block, Block previousBlock, String hashPrefix) {
79+
return (verifyBlock(block, hashPrefix) && verifyBlock(previousBlock, hashPrefix) && block.getPreviousHash().equals(previousBlock.getHash()));
80+
}
81+
82+
public static boolean verifyBlock(Block block, String hashPrefix) {
83+
String hash = createSHA256Hash(block);
84+
return (hash.equals(block.getHash()) && isValid(block, hashPrefix));
85+
}
86+
87+
public static boolean verifyLedger(Ledger ledger) {
88+
for (int i=(ledger.size()-1); i>0; i--) {
89+
Block previousBlock = ledger.getBlockAt(i - 1);
90+
Block block = ledger.getBlockAt(i);
91+
if (!verifyBlock(block, previousBlock, ledger.getHashPrefix())) {
92+
return false;
93+
}
94+
}
95+
return true;
96+
}
97+
98+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package itx.examples.blockchain.advanced;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
6+
public class Ledger {
7+
8+
private String id;
9+
private String hashPrefix;
10+
private List<Block> blocks;
11+
12+
public Ledger(String id, String hashPrefix, List<Block> blocks) {
13+
this.id = id;
14+
this.hashPrefix = hashPrefix;
15+
this.blocks = blocks;
16+
}
17+
18+
public String getId() {
19+
return id;
20+
}
21+
22+
public String getHashPrefix() {
23+
return hashPrefix;
24+
}
25+
26+
public List<Block> getBlocks() {
27+
return Collections.unmodifiableList(blocks);
28+
}
29+
30+
public int size() {
31+
return blocks.size();
32+
}
33+
34+
public Block getBlockAt(int index) {
35+
return blocks.get(index);
36+
}
37+
38+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package itx.examples.blockchain.advanced;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class LedgerBuilder {
7+
8+
private String id;
9+
private String hashPrefix;
10+
private List<Block> blocks;
11+
12+
public LedgerBuilder() {
13+
blocks = new ArrayList<>();
14+
}
15+
16+
public LedgerBuilder setId(String id) {
17+
this.id = id;
18+
return this;
19+
}
20+
21+
public LedgerBuilder setHashPrefix(String hashPrefix) {
22+
this.hashPrefix = hashPrefix;
23+
return this;
24+
}
25+
26+
public LedgerBuilder from(Ledger ledger) {
27+
this.id = ledger.getId();
28+
this.hashPrefix = ledger.getHashPrefix();
29+
this.blocks = new ArrayList<>(ledger.getBlocks());
30+
return this;
31+
}
32+
33+
public LedgerBuilder addData(String data) {
34+
if (blocks.size() == 0) {
35+
Block nextBlock = BlockChainUtils.mineGenesisBlock(data, hashPrefix);
36+
this.blocks.add(nextBlock);
37+
} else {
38+
Block lastBlock = blocks.get(blocks.size() - 1);
39+
Block nextBlock = BlockChainUtils.mineNextBlock(lastBlock, data, hashPrefix);
40+
this.blocks.add(nextBlock);
41+
}
42+
return this;
43+
}
44+
45+
public Ledger build() {
46+
return new Ledger(id, hashPrefix, blocks);
47+
}
48+
49+
}

0 commit comments

Comments
 (0)