From 45451841ce1779676e48777bdde17e0b74e91cb1 Mon Sep 17 00:00:00 2001 From: droideparanoico Date: Sat, 8 Oct 2022 12:27:21 +0200 Subject: [PATCH] Solved hash generation --- .../rviewer/skeletons/domain/model/Block.java | 35 ++--- .../skeletons/domain/model/Blockchain.java | 29 +++- .../skeletons/domain/model/RandomNonce.java | 13 ++ .../skeletons/domain/service/CartService.java | 14 +- .../adapter/CartControllerTest.java | 1 - .../domain/model/BlockchainTest.java | 138 ++++++++++++++++++ .../adapter/CartDatabaseAdapterTest.java | 1 - 7 files changed, 205 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/rviewer/skeletons/domain/model/RandomNonce.java create mode 100644 src/test/java/com/rviewer/skeletons/domain/model/BlockchainTest.java diff --git a/src/main/java/com/rviewer/skeletons/domain/model/Block.java b/src/main/java/com/rviewer/skeletons/domain/model/Block.java index 95bda24..c766d01 100644 --- a/src/main/java/com/rviewer/skeletons/domain/model/Block.java +++ b/src/main/java/com/rviewer/skeletons/domain/model/Block.java @@ -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; } } diff --git a/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java b/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java index 54c254e..19ea7a4 100644 --- a/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java +++ b/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java @@ -19,10 +19,35 @@ public class Blockchain { } public Boolean replace(List blockchainList) { - return true; + if (blockchainList.isEmpty()) { + return false; + } + if (blockchainList.size() < this.blockchainList.size()) { + return false; + } + List newBlockchainList = blockchainList.subList(0, this.blockchainList.size()); + if (!newBlockchainList.equals(this.blockchainList)) { + return false; + } + return isValid(blockchainList); } public Boolean isValid(List 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; } } diff --git a/src/main/java/com/rviewer/skeletons/domain/model/RandomNonce.java b/src/main/java/com/rviewer/skeletons/domain/model/RandomNonce.java new file mode 100644 index 0000000..4b5e624 --- /dev/null +++ b/src/main/java/com/rviewer/skeletons/domain/model/RandomNonce.java @@ -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(); + } +} diff --git a/src/main/java/com/rviewer/skeletons/domain/service/CartService.java b/src/main/java/com/rviewer/skeletons/domain/service/CartService.java index be2fcbe..34b39f6 100644 --- a/src/main/java/com/rviewer/skeletons/domain/service/CartService.java +++ b/src/main/java/com/rviewer/skeletons/domain/service/CartService.java @@ -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); } } diff --git a/src/test/java/com/rviewer/skeletons/application/adapter/CartControllerTest.java b/src/test/java/com/rviewer/skeletons/application/adapter/CartControllerTest.java index 2327b83..3a2b790 100644 --- a/src/test/java/com/rviewer/skeletons/application/adapter/CartControllerTest.java +++ b/src/test/java/com/rviewer/skeletons/application/adapter/CartControllerTest.java @@ -31,7 +31,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc class CartControllerTest { - @Autowired private MockMvc mockMvc; @MockBean diff --git a/src/test/java/com/rviewer/skeletons/domain/model/BlockchainTest.java b/src/test/java/com/rviewer/skeletons/domain/model/BlockchainTest.java new file mode 100644 index 0000000..fa7115c --- /dev/null +++ b/src/test/java/com/rviewer/skeletons/domain/model/BlockchainTest.java @@ -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 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 newBlockchainList = new ArrayList<>(); + + Block genesisBlock = Block.getGenesisBlock(); + blockchain.addBlock(genesisBlock); + + assertFalse(blockchain.replace(newBlockchainList)); + } + + @Test + void should_not_replace_blockchainList_because_smaller() { + List 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 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 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 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 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)); + } +} diff --git a/src/test/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapterTest.java b/src/test/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapterTest.java index 1730cf0..dcf7515 100644 --- a/src/test/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapterTest.java +++ b/src/test/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapterTest.java @@ -23,7 +23,6 @@ import static org.mockito.Mockito.when; @SpringBootTest class CartDatabaseAdapterTest { - @Autowired private CartDatabaseAdapter cartDatabaseAdapter; @Mock