210 lines
5.1 KiB
C
210 lines
5.1 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
|
|
/* You can use another sha256 implementation if you want */
|
|
#include "sha256.h"
|
|
|
|
#define DEFAULT_BLOCKS 1024
|
|
#define MAX_BLOCK_DATA 1024
|
|
#define DIFFICULTY 2
|
|
#define FILENAME "log.bit"
|
|
|
|
typedef struct {
|
|
int index;
|
|
char previous_hash[65];
|
|
char diff[MAX_BLOCK_DATA];
|
|
char timestamp[20];
|
|
int nonce;
|
|
char hash[65];
|
|
} block_t;
|
|
|
|
void print_block(block_t *b)
|
|
{
|
|
printf("Block %d\n", b->index);
|
|
printf("Previous Hash: %s\n", b->previous_hash);
|
|
printf("Diff : %s\n", b->diff);
|
|
printf("Timestamp : %s\n", b->timestamp);
|
|
printf("Nonce : %d\n", b->nonce);
|
|
printf("Hash : %s\n\n", b->hash);
|
|
}
|
|
|
|
void calculate_hash(block_t *b, char *output)
|
|
{
|
|
unsigned char hash_bytes[32];
|
|
char input[MAX_BLOCK_DATA + 88];
|
|
|
|
snprintf(input, sizeof(input), "%d%s%s%s%d", b->index, b->previous_hash,
|
|
b->diff, b->timestamp, b->nonce);
|
|
|
|
sha256(input, strlen(input), hash_bytes);
|
|
|
|
/* Convert to hex */
|
|
for (int i = 0; i < 32; i++) {
|
|
sprintf(output + (i * 2), "%02x", hash_bytes[i]);
|
|
}
|
|
output[64] = '\0';
|
|
}
|
|
|
|
/*
|
|
* Stimulate mining block to find valid hash
|
|
*/
|
|
void mine_block(block_t *b)
|
|
{
|
|
char target[DIFFICULTY + 1];
|
|
/* Adjust DIFFICULTY for the hash to start with 0s */
|
|
memset(target, '0', DIFFICULTY);
|
|
target[DIFFICULTY] = '\0';
|
|
|
|
while (1) {
|
|
calculate_hash(b, b->hash);
|
|
if (strncmp(b->hash, target, DIFFICULTY) == 0)
|
|
break;
|
|
b->nonce++;
|
|
}
|
|
}
|
|
|
|
block_t *create_genesis_block(void)
|
|
{
|
|
block_t *genesis = malloc(sizeof(block_t));
|
|
genesis->index = 1;
|
|
/* No previous hash */
|
|
strcpy(genesis->previous_hash, "0");
|
|
/* Example initial commit diff */
|
|
strcpy(genesis->diff, "+ print(\"Hello World\")");
|
|
time_t now = time(NULL);
|
|
strftime(genesis->timestamp, sizeof(genesis->timestamp),
|
|
"%Y-%m-%d %H:%M:%S", localtime(&now));
|
|
genesis->nonce = 0;
|
|
mine_block(genesis);
|
|
return genesis;
|
|
}
|
|
|
|
block_t *create_block(block_t *previous, const char *diff)
|
|
{
|
|
block_t *new_block = malloc(sizeof(block_t));
|
|
new_block->index = previous->index + 1;
|
|
strcpy(new_block->previous_hash, previous->hash);
|
|
strncpy(new_block->diff, diff, MAX_BLOCK_DATA);
|
|
time_t now = time(NULL);
|
|
strftime(new_block->timestamp, sizeof(new_block->timestamp),
|
|
"%Y-%m-%d %H:%M:%S", localtime(&now));
|
|
new_block->nonce = 0;
|
|
mine_block(new_block);
|
|
return new_block;
|
|
}
|
|
|
|
void save_blockchain(block_t *block)
|
|
{
|
|
FILE *file = fopen(FILENAME, "a");
|
|
fprintf(file, "%d,%s,%s,%s,%d,%s\n", block->index, block->previous_hash,
|
|
block->diff, block->timestamp, block->nonce, block->hash);
|
|
fclose(file);
|
|
}
|
|
|
|
block_t *get_blocks(int *num)
|
|
{
|
|
FILE *file = fopen(FILENAME, "r");
|
|
if (!file) {
|
|
perror("Blockchain file not found");
|
|
return NULL;
|
|
}
|
|
|
|
block_t *blocks = malloc(sizeof(block_t) * DEFAULT_BLOCKS);
|
|
int block_num = 0;
|
|
char line[MAX_BLOCK_DATA + 153];
|
|
while (fgets(line, sizeof(line), file)) {
|
|
block_t current_block;
|
|
char *token;
|
|
token = strtok(line, ",");
|
|
current_block.index = atoi(token);
|
|
|
|
token = strtok(NULL, ",");
|
|
strcpy(current_block.previous_hash, token);
|
|
|
|
token = strtok(NULL, ",");
|
|
strcpy(current_block.diff, token);
|
|
|
|
token = strtok(NULL, ",");
|
|
strcpy(current_block.timestamp, token);
|
|
|
|
token = strtok(NULL, ",");
|
|
current_block.nonce = atoi(token);
|
|
|
|
token = strtok(NULL, ",");
|
|
strcpy(current_block.hash, token);
|
|
|
|
current_block.hash[strcspn(current_block.hash, "\n")] = 0;
|
|
blocks[block_num++] = current_block;
|
|
}
|
|
*num = block_num;
|
|
fclose(file);
|
|
return blocks;
|
|
}
|
|
|
|
block_t *create_blockchain(void)
|
|
{
|
|
block_t *genesis = create_genesis_block();
|
|
print_block(genesis);
|
|
save_blockchain(genesis);
|
|
return genesis;
|
|
}
|
|
|
|
void verify_blockchain(block_t *blocks, int block_num)
|
|
{
|
|
for (int i = 0; i < block_num; i++) {
|
|
char calculated_hash[65];
|
|
calculate_hash(&blocks[i], calculated_hash);
|
|
if (strncmp(blocks[i].hash, calculated_hash, 65) != 0) {
|
|
printf("Hash mismatch in block %d\n", blocks[i].index);
|
|
printf("Blockchain is invalid!\n");
|
|
return;
|
|
}
|
|
}
|
|
printf("Blockchain is valid!\n");
|
|
}
|
|
|
|
block_t *add_block(block_t *last_block)
|
|
{
|
|
char diff[MAX_BLOCK_DATA];
|
|
|
|
if (!fgets(diff, MAX_BLOCK_DATA, stdin)) {
|
|
fprintf(stderr, "Error reading diff from stdin\n");
|
|
return NULL;
|
|
}
|
|
|
|
diff[strcspn(diff, "\n")] = '\0';
|
|
|
|
block_t *new_block = create_block(last_block, diff);
|
|
print_block(new_block);
|
|
save_blockchain(new_block);
|
|
return new_block;
|
|
}
|
|
|
|
void help(int code)
|
|
{
|
|
fprintf(stderr, "bit create|verify|add\n");
|
|
exit(code);
|
|
}
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc != 2) {
|
|
help(1);
|
|
}
|
|
if (!strncmp(argv[1], "create", 6)) {
|
|
create_blockchain();
|
|
} else if (!strncmp(argv[1], "verify", 6)) {
|
|
int block_num;
|
|
block_t *blocks = get_blocks(&block_num);
|
|
verify_blockchain(blocks, block_num);
|
|
} else if (!strncmp(argv[1], "add", 3)) {
|
|
int block_num;
|
|
block_t *blocks = get_blocks(&block_num);
|
|
add_block(&blocks[block_num - 1]);
|
|
} else {
|
|
help(0);
|
|
}
|
|
return 0;
|
|
}
|