shitcoin.c (5422B)
1 #include <err.h> 2 #include <stdarg.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <time.h> 7 8 #include <sha256.h> 9 10 #define HASH_LEN 64 11 #define PENDING_MAX 128 12 13 struct data { 14 char *saddr; 15 char *raddr; 16 long amount; 17 }; 18 19 struct block { 20 struct data *data; 21 char hash[HASH_LEN + 1]; 22 char prevhash[HASH_LEN + 1]; 23 char tstmp[20]; 24 int nonce; 25 }; 26 27 struct blockchain { 28 struct block **blocks; 29 struct block *pending[PENDING_MAX]; 30 size_t nblocks; 31 size_t npending; 32 int difficulty; 33 int reward; 34 }; 35 36 static void transaction(const char *, const char *, long); 37 static struct block *newblock(const char *, const char *, long, const char *); 38 static char *calchash(struct block *); 39 static void initchain(void); 40 static void minepending(const char *); 41 static void mineblock(struct block *); 42 static int validchain(void); 43 static struct block *lastblock(void); 44 static long balance(const char *); 45 static void printchain(void); 46 static void cleanchain(void); 47 static void *emalloc(size_t); 48 49 static struct blockchain *chain; 50 51 static void 52 transaction(const char *from, const char *to, long amount) 53 { 54 if (chain->npending < PENDING_MAX) 55 chain->pending[chain->npending++] = 56 newblock(from, to, amount, NULL); 57 else 58 warnx("transaction array is full"); 59 } 60 61 static struct block * 62 newblock(const char *saddr, const char *raddr, long amount, const char *prevhash) 63 { 64 struct block *b; 65 struct tm *tm; 66 time_t rtime; 67 68 b = emalloc(sizeof(struct block)); 69 b->data = emalloc(sizeof(struct data)); 70 b->data->saddr = strdup(saddr); 71 b->data->raddr = strdup(raddr); 72 b->data->amount = amount; 73 time(&rtime); 74 tm = localtime(&rtime); 75 strftime(b->tstmp, sizeof(b->tstmp), "%F %T", tm); 76 strcpy(b->prevhash, prevhash == NULL ? "" : prevhash); 77 strcpy(b->hash, calchash(b)); 78 b->nonce = 0; 79 80 return (b); 81 } 82 83 static char * 84 calchash(struct block *b) 85 { 86 SHA256_CTX sha256; 87 unsigned char hash[SHA256_DIGEST_LENGTH]; 88 char buf[HASH_LEN + 19 + strlen(b->data->saddr) + strlen(b->data->raddr) + 10 + 10 + 1]; 89 char *res; 90 int i = 0; 91 92 res = emalloc(HASH_LEN + 1); 93 sprintf(buf, "%s%s%s%s%ld%d", 94 b->prevhash, b->tstmp, 95 b->data->saddr, b->data->raddr, b->data->amount, b->nonce); 96 97 SHA256_Init(&sha256); 98 SHA256_Update(&sha256, buf, strlen(buf)); 99 SHA256_Final(hash, &sha256); 100 for (; i < SHA256_DIGEST_LENGTH; i++) 101 sprintf(&res[i << 1], "%02x", hash[i]); 102 res[HASH_LEN] = '\0'; 103 104 return (res); 105 } 106 107 static void 108 initchain(void) 109 { 110 chain = emalloc(sizeof(struct blockchain)); 111 chain->blocks = emalloc(sizeof(struct block *)); 112 memset(chain->pending, 0, sizeof(chain->pending)); 113 chain->nblocks = 1; 114 chain->npending = 0; 115 chain->difficulty = 4; 116 chain->reward = 100; 117 chain->blocks[0] = newblock("Genesis", "Genesis", 0, "0"); 118 } 119 120 static void 121 minepending(const char *rewaddr) 122 { 123 struct block *b, *last; 124 int i = 0; 125 126 if (chain->npending < 1) 127 return; 128 129 if ((chain->blocks = realloc(chain->blocks, 130 sizeof(struct block *) * (chain->nblocks + chain->npending + 1))) == NULL) 131 err(1, "realloc"); 132 133 for (; i < chain->npending; i++) { 134 b = chain->pending[i]; 135 last = lastblock(); 136 if (!strcmp(b->prevhash, "")) 137 strcpy(b->prevhash, last->hash); 138 mineblock(b); 139 chain->blocks[chain->nblocks++] = b; 140 } 141 chain->npending = 0; 142 memset(chain->pending, 0, sizeof(chain->pending)); 143 transaction("Mining Award", rewaddr, chain->reward); 144 } 145 146 static void 147 mineblock(struct block *b) 148 { 149 int d = chain->difficulty, i = 0; 150 char z[d]; 151 152 for (; i < d; i++) 153 z[i] = '0'; 154 while (strncmp(b->hash, z, d)) { 155 b->nonce++; 156 strcpy(b->hash, calchash(b)); 157 } 158 printf("struct block mined: %s\n", b->hash); 159 } 160 static struct block * 161 lastblock(void) 162 { 163 return (chain->blocks[chain->nblocks - 1]); 164 } 165 166 static int 167 validchain(void) 168 { 169 int i = 0; 170 171 for (; i < chain->nblocks; i++) { 172 if (i != 0 && strcmp(chain->blocks[i]->prevhash, 173 chain->blocks[i-1]->hash)) 174 return (0); 175 if (i != 0 && strcmp(chain->blocks[i]->hash, 176 calchash(chain->blocks[i]))) 177 return (0); 178 } 179 180 return (1); 181 } 182 183 static long 184 balance(const char *addr) 185 { 186 long bal = 0; 187 int i; 188 189 for (i = 0; i < chain->nblocks; i++) { 190 if (!strcmp(chain->blocks[i]->data->saddr, addr)) 191 bal -= chain->blocks[i]->data->amount; 192 if (!strcmp(chain->blocks[i]->data->raddr, addr)) 193 bal += chain->blocks[i]->data->amount; 194 } 195 196 return (bal); 197 } 198 199 static void 200 printchain(void) 201 { 202 int i = 0; 203 204 for (; i < chain->nblocks; i++) { 205 printf("HASH: %s\n", chain->blocks[i]->hash); 206 printf("PREVHASH: %s\n", chain->blocks[i]->prevhash); 207 printf("TIMESTAMP: %s\n", chain->blocks[i]->tstmp); 208 printf("DATA:\n"); 209 printf(" FROM: %s\n", chain->blocks[i]->data->saddr); 210 printf(" TO: %s\n", chain->blocks[i]->data->raddr); 211 printf(" AMOUNT: %ld\n", chain->blocks[i]->data->amount); 212 printf("\n"); 213 } 214 } 215 216 static void 217 cleanchain(void) 218 { 219 int i = 0; 220 221 for (; i < chain->nblocks; i++) { 222 free(chain->blocks[i]->data->saddr); 223 free(chain->blocks[i]->data->raddr); 224 free(chain->blocks[i]->data); 225 free(chain->blocks[i]); 226 } 227 free(chain->blocks); 228 free(chain); 229 } 230 231 static void * 232 emalloc(size_t nb) 233 { 234 void *p; 235 236 if ((p = malloc(nb)) == NULL) 237 err(1, "malloc"); 238 239 return (p); 240 } 241 242 int 243 main(int argc, char *argv[]) 244 { 245 initchain(); 246 247 transaction("Christos", "Lol", 1000); 248 transaction("N", "Lol", 1000); 249 minepending("miner"); 250 251 printchain(); 252 printf("Valid chain: %s\n", validchain() ? "Yes" : "No"); 253 printf("Lol's balance: %ld\n", balance("Lol")); 254 255 cleanchain(); 256 257 return (0); 258 }