Add sources

This commit is contained in:
2018-06-05 22:08:10 +02:00
parent bcc80a4727
commit c8947f3bac
17 changed files with 802 additions and 0 deletions

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "external/crypto-algorithms"]
path = external/crypto-algorithms
url = https://github.com/B-Con/crypto-algorithms
[submodule "external/containers"]
path = external/containers
url = https://github.com/magestik/containers

21
CMakeLists.txt Normal file
View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.3)
project(blockchain)
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Debug)
ENDIF()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
option(ENABLE_ASAN OFF)
if (ENABLE_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
endif (ENABLE_ASAN)
add_subdirectory(external)
add_subdirectory(src)
add_subdirectory(test)

View File

@@ -1,2 +1,3 @@
# blockchain # blockchain
Simple blockchain

37
external/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,37 @@
add_subdirectory(containers)
#############################################
# MD2
add_library(md2 STATIC crypto-algorithms/md2.c crypto-algorithms/md2.h)
target_include_directories(md2 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/crypto-algorithms/)
add_executable(md2_test crypto-algorithms/md2_test.c)
target_link_libraries(md2_test PRIVATE md2)
# MD5
add_library(md5 STATIC crypto-algorithms/md5.c crypto-algorithms/md5.h)
target_include_directories(md5 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/crypto-algorithms/)
add_executable(md5_test crypto-algorithms/md5_test.c)
target_link_libraries(md5_test PRIVATE md5)
# SHA1
add_library(sha1 STATIC crypto-algorithms/sha1.c crypto-algorithms/sha1.h)
target_include_directories(sha1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/crypto-algorithms/)
add_executable(sha1_test crypto-algorithms/sha1_test.c)
target_link_libraries(sha1_test PRIVATE sha1)
# SHA256
add_library(sha256 STATIC crypto-algorithms/sha256.c crypto-algorithms/sha256.h)
target_include_directories(sha1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/crypto-algorithms/)
add_executable(sha256_test crypto-algorithms/sha256_test.c)
target_link_libraries(sha256_test PRIVATE sha256)

1
external/containers vendored Submodule

Submodule external/containers added at 67fa335974

1
external/crypto-algorithms vendored Submodule

35
src/Allocator.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <stdlib.h> // for malloc/free & realloc
class HeapAllocator
{
public:
HeapAllocator(void)
{
// ...
}
~HeapAllocator(void)
{
// ...
}
void * allocate(unsigned int size)
{
void * ptr = malloc(size);
return(ptr);
}
void release(void * ptr)
{
free(ptr);
}
void * resize(void * ptr, unsigned int size)
{
ptr = realloc(ptr, size);
return(ptr);
}
};

1
src/Block.cpp Normal file
View File

@@ -0,0 +1 @@
#include "Block.h"

109
src/Block.h Normal file
View File

@@ -0,0 +1,109 @@
#pragma once
#include "base_types.h"
template<typename HashHeader, typename HashData>
struct BlockHeader
{
BlockHeader(void)
{
version = 1;
timestamp = 0;
previousHash = 0;
dataHash = 0;
nonce = 0;
}
BlockHeader(uint32_t version_, uint32_t timestamp_, const HashHeader & previousHash_, const HashData & dataHash_, uint32_t nonce_)
{
version = version_;
timestamp = timestamp_;
previousHash = previousHash_;
dataHash = dataHash_;
nonce = nonce_;
}
uint32_t getVersion(void) const
{
return(version);
}
uint32_t getTimestamp(void) const
{
return(timestamp);
}
const HashHeader & getPreviousHash(void) const
{
return(previousHash);
}
const HashData & getDataHash(void) const
{
return(dataHash);
}
uint32_t getNonce(void) const
{
return(nonce);
}
private:
uint32_t version;
uint32_t timestamp;
HashHeader previousHash;
HashData dataHash;
uint32_t nonce;
};
template<unsigned int BLOCK_SIZE, typename HASH_HEADER, typename HASH_DATA>
struct Block
{
typedef BlockHeader<HASH_HEADER, HASH_DATA> HEADER;
typedef HASH_HEADER HASH;
enum { SIZE = BLOCK_SIZE };
Block(void) : header()
{
for (int i = 0; i < SIZE; ++i)
{
data[i] = 0;
}
}
Block(uint32_t version, uint32_t timestamp, const HASH & previousHash, uint32_t nonce, byte * data_) : header()
{
for (int i = 0; i < SIZE; ++i)
{
data[i] = data_[i];
}
HASH_DATA dataHash;
dataHash.initFromData(data_, SIZE);
header = HEADER(version, timestamp, previousHash, dataHash, nonce);
}
bool computeHash(HASH & output_hash) const
{
// it's ok to compute header hash only
// -> the header contains the data hash
return(output_hash.initFromData((byte*)&header, sizeof(HEADER)));
}
const HEADER & getHeader(void) const
{
return(header);
}
const byte * getData(void) const
{
return(data);
}
private:
HEADER header;
byte data [SIZE];
};

2
src/Blockchain.cpp Normal file
View File

@@ -0,0 +1,2 @@
#include "Blockchain.h"

176
src/Blockchain.h Normal file
View File

@@ -0,0 +1,176 @@
#pragma once
#include "base_types.h"
#include "Block.h"
#include <string.h>
#include "Array.h"
#include "Allocator.h"
template<typename BLOCK>
class Blockchain
{
public:
enum
{
INITIAL_VERSION = 1,
CURRENT_VERSION = INITIAL_VERSION,
};
/**
* @brief Default Constructor
*/
Blockchain(void) : difficulty(0)
{
// ...
}
/**
* @brief Constructor with initial difficulty
* @param difficulty_
*/
Blockchain(unsigned int difficulty_) : difficulty(difficulty_)
{
if (difficulty > BLOCK::HASH::SIZE)
{
difficulty = 0;
}
}
/**
* @brief Get Current Hash computation difficulty
* @return
*/
inline unsigned int getCurrentDifficulty(void) const
{
return(difficulty);
}
/**
* @brief Get Genesis Block
* @return
*/
inline const BLOCK & getGenesisBlock(void) const
{
return(*blocks[0]);
}
/**
* @brief Get Genesis Block
* @return
*/
inline const BLOCK & getCurrentBlock(void) const
{
return(*blocks[blocks.count()-1]);
}
/**
* @brief Create Genesis Block without data
* @param version
* @param timestamp
* @param output_block
* @return
*/
bool CreateGenesisBlock(uint32_t version, uint32_t timestamp, uint32_t nonce, BLOCK & output_block)
{
return(CreateGenesisBlock(version, timestamp, nonce, nullptr, 0, output_block));
}
/**
* @brief Create Genesis Block with data
* @param version
* @param timestamp
* @param output_block
* @return
*/
bool CreateGenesisBlock(uint32_t version, uint32_t timestamp, uint32_t nonce, byte * data, unsigned int dataSize, BLOCK & output_block)
{
if (version != CURRENT_VERSION)
{
return(false);
}
typename BLOCK::HASH empty_hash;
return(CreateBlockInternal(version, timestamp, empty_hash, nonce, data, dataSize, output_block));
}
/**
* @brief Create Block
* @param version
* @param timestamp
* @param previousHash
* @param nonce
* @param data
* @param dataSize
* @param output_block
* @return
*/
bool CreateBlock(uint32_t version, uint32_t timestamp, const typename BLOCK::HASH & previousHash, uint32_t nonce, byte * data, unsigned int dataSize, BLOCK & output_block)
{
if (version != CURRENT_VERSION)
{
return(false);
}
if (data == nullptr || dataSize == 0 || dataSize > BLOCK::SIZE)
{
return(false);
}
return(CreateBlockInternal(version, timestamp, previousHash, nonce, data, dataSize, output_block));
}
protected:
/**
* @brief Create Block without checking parameters
* @param version
* @param timestamp
* @param previousHash
* @param nonce
* @param data
* @param dataSize
* @param output_block
* @return
*/
bool CreateBlockInternal(uint32_t version, uint32_t timestamp, const typename BLOCK::HASH & previousHash, uint32_t nonce, byte * data, unsigned int dataSize, BLOCK & output_block)
{
byte blockData [BLOCK::SIZE];
memset(blockData, 0, BLOCK::SIZE);
if (nullptr != data)
{
memcpy(blockData, data, dataSize);
}
BLOCK newBlock(version, timestamp, previousHash, nonce, blockData);
typename BLOCK::HASH H;
newBlock.computeHash(H);
for (int i = 0; i < difficulty; ++i)
{
if (H[i] != 0)
{
return(false);
}
}
output_block = newBlock;
blocks.add(&output_block);
return(true);
}
private:
unsigned int difficulty;
Array<BLOCK*, HeapAllocator> blocks;
};

5
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
add_library(blockchain STATIC Block.cpp Block.h Blockchain.cpp Blockchain.h base_types.h)
target_include_directories(blockchain PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(blockchain PUBLIC containers)

5
src/base_types.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <inttypes.h>
typedef uint8_t byte;

4
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,4 @@
add_executable(compute_hash compute_hash.cpp Hash.cpp Hash.h)
target_link_libraries(compute_hash PRIVATE blockchain md5 sha1 sha256)

1
test/Hash.cpp Normal file
View File

@@ -0,0 +1 @@
#include "Hash.h"

187
test/Hash.h Normal file
View File

@@ -0,0 +1,187 @@
#pragma once
#include "base_types.h"
extern "C"
{
#include "md5.h"
#include "sha1.h"
#include "sha256.h"
}
template<unsigned int HASH_SIZE>
struct HashNull
{
enum { SIZE = HASH_SIZE };
HashNull(void)
{
setBytes(0);
}
bool initFromData(const byte * data, unsigned int size)
{
setBytes(0);
return(true);
}
HashNull<SIZE> & operator = (byte data)
{
setBytes(data);
}
byte operator [] (unsigned int index) const
{
return(checksum[index]);
}
protected:
void setBytes(byte data)
{
for (int i = 0; i < SIZE; ++i)
{
checksum[i] = data;
}
}
private:
byte checksum [SIZE];
};
struct HashMd5
{
enum { SIZE = 16 }; // 128 bits
HashMd5(void)
{
setBytes(0);
}
bool initFromData(byte * data, unsigned int size)
{
MD5_CTX context;
md5_init(&context);
md5_update(&context, data, size);
md5_final(&context, checksum);
return(true);
}
HashMd5 & operator = (byte data)
{
setBytes(data);
}
byte operator [] (unsigned int index) const
{
return(checksum[index]);
}
protected:
void setBytes(byte data)
{
for (int i = 0; i < SIZE; ++i)
{
checksum[i] = data;
}
}
private:
byte checksum [SIZE];
};
struct HashSha1
{
enum { SIZE = 20 }; // 160 bits
HashSha1(void)
{
setBytes(0);
}
bool initFromData(byte * data, unsigned int size)
{
SHA1_CTX context;
sha1_init(&context);
sha1_update(&context, data, size);
sha1_final(&context, checksum);
return(true);
}
HashSha1 & operator = (byte data)
{
setBytes(data);
}
byte operator [] (unsigned int index) const
{
return(checksum[index]);
}
protected:
void setBytes(byte data)
{
for (int i = 0; i < SIZE; ++i)
{
checksum[i] = data;
}
}
private:
byte checksum [SIZE];
};
struct HashSha256
{
enum { SIZE = 32 }; // 256 bits
HashSha256(void)
{
setBytes(0);
}
bool initFromData(byte * data, unsigned int size)
{
SHA256_CTX context;
sha256_init(&context);
sha256_update(&context, data, size);
sha256_final(&context, checksum);
return(true);
}
HashSha256 & operator = (byte data)
{
setBytes(data);
}
byte operator [] (unsigned int index) const
{
return(checksum[index]);
}
protected:
void setBytes(byte data)
{
for (int i = 0; i < SIZE; ++i)
{
checksum[i] = data;
}
}
private:
byte checksum [SIZE];
};

210
test/compute_hash.cpp Normal file
View File

@@ -0,0 +1,210 @@
#include "Blockchain.h"
#include "Hash.h"
#include <stdio.h>
#include <time.h>
typedef HashSha256 HASH_HEADER;
typedef HashMd5 HASH_DATA;
typedef Block<1024, HASH_HEADER, HASH_DATA> BLOCK;
typedef Blockchain<BLOCK> BLOCKCHAIN;
const unsigned int DIFFICULTY = 2;
static inline uint32_t getCurrentTimestamp()
{
time_t unix_timestamp = time(nullptr);
if (-1 == unix_timestamp)
{
return(false);
}
uint32_t timestamp = unix_timestamp & 0xFFFFFFFF; // should be ok for now
return(timestamp);
}
struct MiningBlockHeader
{
uint32_t version;
uint32_t timestamp;
HASH_HEADER previousHash;
HASH_DATA dataHash;
uint32_t nonce;
MiningBlockHeader(const BLOCK::HASH & previousBlockHash, byte * data, unsigned int size)
{
version = 1;
timestamp = getCurrentTimestamp();
previousHash = previousBlockHash;
if (data != nullptr)
{
dataHash.initFromData(data, size);
}
nonce = 0;
}
};
static void print_hash(unsigned char * data, unsigned int size)
{
for (int i = 0; i < size; ++i)
{
printf("%02x", (unsigned)(*(data+i)));
}
}
static bool print_block_info(const BLOCK & block)
{
BLOCK::HASH H;
bool success = block.computeHash(H);
if (!success)
{
return(false);
}
printf("Block ");
print_hash((unsigned char*)&H, BLOCK::HASH::SIZE);
printf(" : \n");
printf("\t Version : %d\n", block.getHeader().getVersion());
printf("\t Timestamp : %d\n", block.getHeader().getTimestamp());
printf("\t Previous Block : ");
print_hash((unsigned char*)&block.getHeader().getPreviousHash(), HASH_HEADER::SIZE);
printf(" \n");
printf("\t Data Hash : ");
print_hash((unsigned char*)&block.getHeader().getDataHash(), HASH_DATA::SIZE);
printf(" \n");
printf("\t Nonce : %d\n", block.getHeader().getNonce());
printf("\n");
return(true);
}
static inline bool isHashValidForBlockchain(BLOCKCHAIN & blockchain, const BLOCK::HASH & H)
{
for (int i = 0; i < blockchain.getCurrentDifficulty(); ++i)
{
if (H[i] != 0)
{
return(false);
}
}
return(true);
}
static bool ComputeNonce(BLOCKCHAIN & blockchain, MiningBlockHeader & parameters)
{
BLOCK::HASH H;
H.initFromData((byte*)&parameters, sizeof(parameters));
while (!isHashValidForBlockchain(blockchain, H))
{
++parameters.nonce;
if (!H.initFromData((byte*)&parameters, sizeof(parameters)))
{
return(false);
}
}
return(true);
}
bool CreateGenesisBlock(BLOCKCHAIN & blockchain, byte * data, unsigned int size)
{
BLOCK::HASH empty_hash;
MiningBlockHeader parameters(empty_hash, data, size);
ComputeNonce(blockchain, parameters);
BLOCK block;
if (!blockchain.CreateGenesisBlock(parameters.version, parameters.timestamp, parameters.nonce, data, size, block))
{
printf("Block creation failed !\n");
return(false);
}
BLOCK::HASH H;
bool success = block.computeHash(H);
if (!success)
{
return(false);
}
print_block_info(block);
return(true);
}
bool CreateEmptyGenesisBlock(BLOCKCHAIN & blockchain)
{
byte no_data [BLOCK::SIZE];
memset(no_data, 0, BLOCK::SIZE);
return(CreateGenesisBlock(blockchain, no_data, BLOCK::SIZE));
}
bool AddDataToBlockChain(BLOCKCHAIN & blockchain, const BLOCK::HASH & previousBlockHash, byte * data, unsigned int size)
{
MiningBlockHeader parameters(previousBlockHash, data, size);
ComputeNonce(blockchain, parameters);
BLOCK block;
if (!blockchain.CreateBlock(parameters.version, parameters.timestamp, parameters.previousHash, parameters.nonce, data, size, block))
{
printf("Block creation failed !\n");
return(false);
}
BLOCK::HASH H;
bool success = block.computeHash(H);
if (!success)
{
return(false);
}
print_block_info(block);
return(true);
}
int main(int argc, char ** argv)
{
BLOCKCHAIN blockchain(DIFFICULTY);
//
// Create Genesis Block
if (!CreateEmptyGenesisBlock(blockchain))
{
return(-1);
}
BLOCK::HASH H;
const BLOCK & genesis = blockchain.getGenesisBlock();
genesis.computeHash(H);
//
// Create some Block
byte random_data [BLOCK::SIZE];
memset(random_data, 0, BLOCK::SIZE);
for (int i = 0; i < 10; ++i)
{
AddDataToBlockChain(blockchain, H, random_data, BLOCK::SIZE);
const BLOCK & current = blockchain.getCurrentBlock();
current.computeHash(H);
}
return(0);
}