Solved hash generation
This commit is contained in:
@@ -2,40 +2,37 @@ package com.rviewer.skeletons.domain.model;
|
||||
|
||||
import com.rviewer.skeletons.domain.exception.HashGenerationException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.With;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Random;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@With
|
||||
@Builder
|
||||
public class Block {
|
||||
private LocalDateTime timestamp;
|
||||
private String lasthash;
|
||||
@Builder.Default
|
||||
private LocalDateTime timestamp = LocalDateTime.now();
|
||||
private String previousHash;
|
||||
private String data;
|
||||
private Integer nonce;
|
||||
@Builder.Default
|
||||
private Integer nonce = RandomNonce.getRandomNonce();
|
||||
private String hash;
|
||||
|
||||
public Block() {
|
||||
this.timestamp = LocalDateTime.now();
|
||||
this.nonce = new Random().nextInt();
|
||||
this.hash = generateHashFromBlock(this);
|
||||
}
|
||||
|
||||
public static Block getGenesisBlock() {
|
||||
return new Block().withLasthash("");
|
||||
Block genesisBlock = Block.builder().previousHash("").build();
|
||||
genesisBlock.setHash(generateHashFromBlock(genesisBlock));
|
||||
return genesisBlock;
|
||||
}
|
||||
|
||||
static String generateHashFromBlock(final Block block) throws HashGenerationException {
|
||||
public static String generateHashFromBlock(final Block block) throws HashGenerationException {
|
||||
try {
|
||||
final var digest = MessageDigest.getInstance("SHA-256");
|
||||
final byte[] hash = digest.digest(block.getHashData().getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] hashData = digest.digest(block.getHashData().getBytes(StandardCharsets.UTF_8));
|
||||
final var hexString = new StringBuilder();
|
||||
for (final byte elem : hash) {
|
||||
for (final byte elem : hashData) {
|
||||
final var hex = Integer.toHexString(0xff & elem);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
@@ -46,7 +43,11 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
public void generateHash() {
|
||||
hash = generateHashFromBlock(this);
|
||||
}
|
||||
|
||||
private String getHashData() {
|
||||
return timestamp + lasthash + data + nonce;
|
||||
return timestamp + previousHash + data + nonce;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,35 @@ public class Blockchain {
|
||||
}
|
||||
|
||||
public Boolean replace(List<Block> blockchainList) {
|
||||
return true;
|
||||
if (blockchainList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (blockchainList.size() < this.blockchainList.size()) {
|
||||
return false;
|
||||
}
|
||||
List<Block> newBlockchainList = blockchainList.subList(0, this.blockchainList.size());
|
||||
if (!newBlockchainList.equals(this.blockchainList)) {
|
||||
return false;
|
||||
}
|
||||
return isValid(blockchainList);
|
||||
}
|
||||
|
||||
public Boolean isValid(List<Block> blockchainList) {
|
||||
return true;
|
||||
boolean valid = true;
|
||||
// Validate genesis block
|
||||
if (!blockchainList.get(0).getPreviousHash().equals("")) {
|
||||
return false;
|
||||
}
|
||||
// Validate every element's last hash value matches previous block's hash
|
||||
for (int i = 0; i < blockchainList.size() - 1; i++) {
|
||||
valid = blockchainList.get(i).getHash().equals(blockchainList.get(i + 1).getPreviousHash());
|
||||
if (!valid) return false;
|
||||
}
|
||||
// Validate data hasn't been tampered
|
||||
for (Block block : blockchainList) {
|
||||
valid = block.getHash().equals(Block.generateHashFromBlock(block));
|
||||
if (!valid) break;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.rviewer.skeletons.domain.model;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomNonce {
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private RandomNonce() {}
|
||||
|
||||
public static Integer getRandomNonce() {
|
||||
return RANDOM.nextInt();
|
||||
}
|
||||
}
|
||||
@@ -30,17 +30,21 @@ public class CartService implements CartServicePort {
|
||||
public void save(Cart cart) {
|
||||
cartDatabasePort.save(cart);
|
||||
if (blockchain.getBlockchainList().isEmpty()) {
|
||||
blockchain.addBlock(Block.getGenesisBlock().withData(cart.toString()));
|
||||
blockchain.addBlock(Block.builder().data(cart.toString()).build());
|
||||
} else {
|
||||
blockchain.addBlock(new Block().withData(cart.toString())
|
||||
.withLasthash(blockchain.getBlockchainList().getLast().getHash()));
|
||||
Block saveBlock = Block.builder().data(cart.toString())
|
||||
.previousHash(blockchain.getBlockchainList().getLast().getHash()).build();
|
||||
saveBlock.generateHash();
|
||||
blockchain.addBlock(saveBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
cartDatabasePort.delete(id);
|
||||
blockchain.addBlock(new Block().withData("deleted cart with id:" + id)
|
||||
.withLasthash(blockchain.getBlockchainList().getLast().getHash()));
|
||||
Block deleteBlock = Block.builder().data("deleted cart with id:" + id)
|
||||
.previousHash(blockchain.getBlockchainList().getLast().getHash()).build();
|
||||
deleteBlock.generateHash();
|
||||
blockchain.addBlock(deleteBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class CartControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@MockBean
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.rviewer.skeletons.domain.model;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BlockchainTest {
|
||||
private Blockchain blockchain;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
blockchain = new Blockchain();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_add_block() {
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
assertEquals(blockchain.getBlockchainList().get(0), genesisBlock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_replace_blockchainList() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash(genesisBlock.getHash()).data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
newBlockchainList.add(secondBlock);
|
||||
|
||||
Block thirdBlock = Block.builder().previousHash(secondBlock.getHash()).data(null).build();
|
||||
thirdBlock.generateHash();
|
||||
newBlockchainList.add(thirdBlock);
|
||||
|
||||
assertTrue(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_is_empty() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_smaller() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash(genesisBlock.getHash()).data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_not_equals() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash(genesisBlock.getHash()).data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
newBlockchainList.add(Block.builder().previousHash(genesisBlock.getHash()).data("data").build());
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_invalid_genesis() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.builder().previousHash("non empty").data(null).build();
|
||||
genesisBlock.generateHash();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash(genesisBlock.getHash()).data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
newBlockchainList.add(secondBlock);
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_hash_mismatch() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash("wrong hash").data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
newBlockchainList.add(secondBlock);
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_replace_blockchainList_because_tampered_data() {
|
||||
List<Block> newBlockchainList = new ArrayList<>();
|
||||
|
||||
Block genesisBlock = Block.getGenesisBlock();
|
||||
blockchain.addBlock(genesisBlock);
|
||||
newBlockchainList.add(genesisBlock);
|
||||
|
||||
Block secondBlock = Block.builder().previousHash(genesisBlock.getHash()).data(null).build();
|
||||
secondBlock.generateHash();
|
||||
blockchain.addBlock(secondBlock);
|
||||
secondBlock.setData("tampered data");
|
||||
newBlockchainList.add(secondBlock);
|
||||
|
||||
assertFalse(blockchain.replace(newBlockchainList));
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringBootTest
|
||||
class CartDatabaseAdapterTest {
|
||||
|
||||
@Autowired
|
||||
private CartDatabaseAdapter cartDatabaseAdapter;
|
||||
@Mock
|
||||
|
||||
Reference in New Issue
Block a user