blockdetail command, fullverify checks difficulty (needs optimizing), move general functions to utils.h
This commit is contained in:
@@ -31,6 +31,15 @@ bool Autolykos2_Hash(
|
||||
);
|
||||
|
||||
bool Autolykos2_LightHash(const uint8_t* seed, blockchain_t* chain, uint64_t nonce, uint8_t* out);
|
||||
bool Autolykos2_LightHashAtHeight(
|
||||
const uint8_t seed32[32],
|
||||
const uint8_t* message,
|
||||
size_t messageLen,
|
||||
uint64_t nonce,
|
||||
uint64_t height,
|
||||
size_t dagBytes,
|
||||
uint8_t out[32]
|
||||
);
|
||||
|
||||
bool Autolykos2_CheckTarget(
|
||||
Autolykos2Context* ctx,
|
||||
|
||||
@@ -27,6 +27,7 @@ void Chain_Wipe(blockchain_t* chain);
|
||||
// I/O
|
||||
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t currentSupply, uint64_t currentReward);
|
||||
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* outCurrentSupply, uint32_t* outDifficultyTarget, uint64_t* outCurrentReward, uint8_t* outLastSavedHash, bool loadTransactions);
|
||||
bool Chain_LoadBlockFromFile(const char* dirpath, uint64_t blockNumber, bool loadTransactions, block_t** outBlock, size_t* outTxCount);
|
||||
|
||||
// Difficulty
|
||||
uint32_t Chain_ComputeNextTarget(blockchain_t* chain, uint32_t currentTarget);
|
||||
|
||||
154
include/utils.h
154
include/utils.h
@@ -3,8 +3,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <crypto/crypto.h>
|
||||
#include <uint256.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -31,6 +33,158 @@ static inline void AddressToHexString(const uint8_t address[32], char out[65]) {
|
||||
to_hex(address, out);
|
||||
}
|
||||
|
||||
static inline void AddressFromCompressedPubkey(const uint8_t compressedPubkey[33], uint8_t outAddress[32]) {
|
||||
if (!compressedPubkey || !outAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
SHA256(compressedPubkey, 33, outAddress);
|
||||
}
|
||||
|
||||
static inline void PrintHexBytes(const uint8_t* bytes, size_t length) {
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
printf("%02x", bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int CompareHashToTarget(const uint8_t hash[32], const uint8_t target[32]) {
|
||||
if (!hash || !target) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
if (hash[i] < target[i]) {
|
||||
return -1;
|
||||
}
|
||||
if (hash[i] > target[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool DecodeCompactTarget(uint32_t nBits, uint8_t target[32]) {
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(target, 0, 32);
|
||||
|
||||
uint32_t exponent = nBits >> 24;
|
||||
uint32_t mantissa = nBits & 0x007fffff;
|
||||
bool negative = (nBits & 0x00800000) != 0;
|
||||
|
||||
if (negative || mantissa == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exponent <= 3) {
|
||||
mantissa >>= 8 * (3 - exponent);
|
||||
target[29] = (uint8_t)((mantissa >> 16) & 0xffu);
|
||||
target[30] = (uint8_t)((mantissa >> 8) & 0xffu);
|
||||
target[31] = (uint8_t)(mantissa & 0xffu);
|
||||
} else {
|
||||
uint32_t byteIndex = exponent - 3;
|
||||
if (byteIndex > 29) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target[32 - byteIndex - 3] = (uint8_t)((mantissa >> 16) & 0xffu);
|
||||
target[32 - byteIndex - 2] = (uint8_t)((mantissa >> 8) & 0xffu);
|
||||
target[32 - byteIndex - 1] = (uint8_t)(mantissa & 0xffu);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool GenerateTestMinerIdentity(uint8_t privateKey[32], uint8_t compressedPubkey[33], uint8_t address[32]) {
|
||||
if (!privateKey || !compressedPubkey || !address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t seed[64];
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
for (uint64_t counter = 0; counter < 1024; ++counter) {
|
||||
const char* base = "skalacoin-test-miner-key";
|
||||
size_t baseLen = strlen(base);
|
||||
memcpy(seed, base, baseLen);
|
||||
memcpy(seed + baseLen, &counter, sizeof(counter));
|
||||
SHA256(seed, baseLen + sizeof(counter), privateKey);
|
||||
|
||||
if (!secp256k1_ec_seckey_verify(ctx, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pubLen = 33;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, compressedPubkey, &pubLen, &pubkey, SECP256K1_EC_COMPRESSED) || pubLen != 33) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddressFromCompressedPubkey(compressedPubkey, address);
|
||||
secp256k1_context_destroy(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool GenerateRandomTestAddress(uint8_t outAddress[32]) {
|
||||
if (!outAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t privateKey[32];
|
||||
uint8_t compressedPubkey[33];
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t attempt = 0; attempt < 4096; ++attempt) {
|
||||
for (size_t i = 0; i < sizeof(privateKey); ++i) {
|
||||
privateKey[i] = (uint8_t)(rand() & 0xFF);
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_seckey_verify(ctx, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pubLen = sizeof(compressedPubkey);
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, compressedPubkey, &pubLen, &pubkey, SECP256K1_EC_COMPRESSED) || pubLen != 33) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddressFromCompressedPubkey(compressedPubkey, outAddress);
|
||||
secp256k1_context_destroy(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool ParseHexAddress32(const char* in, uint8_t outAddress[32]) {
|
||||
if (!in || !outAddress) {
|
||||
return false;
|
||||
|
||||
@@ -179,7 +179,8 @@ static bool ReadDagLaneFromSeed(const uint8_t seed32[32], uint32_t laneIndex, ui
|
||||
}
|
||||
|
||||
static bool Autolykos2_HashCore(
|
||||
const uint8_t seed32[32],
|
||||
const uint8_t messageSeed32[32],
|
||||
const uint8_t dagSeed32[32],
|
||||
const uint8_t* message,
|
||||
size_t messageLen,
|
||||
uint64_t nonce,
|
||||
@@ -189,7 +190,7 @@ static bool Autolykos2_HashCore(
|
||||
bool useContextDag,
|
||||
uint8_t outHash[32]
|
||||
) {
|
||||
if (!seed32 || !message || !outHash || laneCount == 0) {
|
||||
if (!messageSeed32 || !message || !outHash || laneCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,7 +201,7 @@ static bool Autolykos2_HashCore(
|
||||
uint32_t laneIndex = 0;
|
||||
uint8_t lane[32];
|
||||
|
||||
if (!ComputeLaneIndex(seed32, nonce, height, round, laneCount, &laneIndex)) {
|
||||
if (!ComputeLaneIndex(messageSeed32, nonce, height, round, laneCount, &laneIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -209,7 +210,7 @@ static bool Autolykos2_HashCore(
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!ReadDagLaneFromSeed(seed32, laneIndex, lane)) {
|
||||
if (!ReadDagLaneFromSeed(dagSeed32 ? dagSeed32 : messageSeed32, laneIndex, lane)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +222,7 @@ static bool Autolykos2_HashCore(
|
||||
|
||||
uint8_t baseHash[32];
|
||||
uint8_t accHash[32];
|
||||
if (!Autolykos2_FallbackHash(seed32, message, messageLen, nonce, height, baseHash)) {
|
||||
if (!Autolykos2_FallbackHash(messageSeed32, message, messageLen, nonce, height, baseHash)) {
|
||||
return false;
|
||||
}
|
||||
if (!Blake2b_Hash(acc, sizeof(acc), accHash, sizeof(accHash))) {
|
||||
@@ -377,6 +378,7 @@ bool Autolykos2_Hash(
|
||||
|
||||
return Autolykos2_HashCore(
|
||||
seed32,
|
||||
NULL,
|
||||
message,
|
||||
messageLen,
|
||||
nonce,
|
||||
@@ -406,6 +408,7 @@ bool Autolykos2_LightHash(const uint8_t* seed, blockchain_t* chain, uint64_t non
|
||||
|
||||
// Light path derives the needed DAG lanes from seed on-demand, no large DAG allocation required.
|
||||
return Autolykos2_HashCore(
|
||||
seed,
|
||||
seed,
|
||||
seed,
|
||||
32,
|
||||
@@ -418,6 +421,43 @@ bool Autolykos2_LightHash(const uint8_t* seed, blockchain_t* chain, uint64_t non
|
||||
);
|
||||
}
|
||||
|
||||
bool Autolykos2_LightHashAtHeight(
|
||||
const uint8_t seed32[32],
|
||||
const uint8_t* message,
|
||||
size_t messageLen,
|
||||
uint64_t nonce,
|
||||
uint64_t height,
|
||||
size_t dagBytes,
|
||||
uint8_t out[32]
|
||||
) {
|
||||
if (!seed32 || !message || !out || dagBytes < 32 || (dagBytes % 32) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t laneCount64 = dagBytes / 32u;
|
||||
if (laneCount64 == 0 || laneCount64 > UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t messageSeed32[32];
|
||||
if (!DeriveSeedFromMessage(message, messageLen, messageSeed32)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Autolykos2_HashCore(
|
||||
messageSeed32,
|
||||
seed32,
|
||||
message,
|
||||
messageLen,
|
||||
nonce,
|
||||
height,
|
||||
(uint32_t)laneCount64,
|
||||
NULL,
|
||||
false,
|
||||
out
|
||||
);
|
||||
}
|
||||
|
||||
bool Autolykos2_CheckTarget(
|
||||
Autolykos2Context* ctx,
|
||||
const uint8_t message32[32],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <block/block.h>
|
||||
#include <autolykos2/autolykos2.h>
|
||||
#include <utils.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static Autolykos2Context* g_autolykos2Ctx = NULL;
|
||||
@@ -85,79 +86,51 @@ void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Make this not shit
|
||||
DynArr* hashes1 = DynArr_create(sizeof(uint8_t) * 32, 1);
|
||||
DynArr* hashes2 = DynArr_create(sizeof(uint8_t) * 32, 1);
|
||||
if (!hashes1 || !hashes2) {
|
||||
if (hashes1) DynArr_destroy(hashes1);
|
||||
if (hashes2) DynArr_destroy(hashes2);
|
||||
uint8_t* current = (uint8_t*)malloc(txCount * 32u);
|
||||
uint8_t* next = (uint8_t*)malloc(txCount * 32u);
|
||||
if (!current || !next) {
|
||||
free(current);
|
||||
free(next);
|
||||
memset(outHash, 0, 32);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the transactions
|
||||
for (size_t i = 0; i < txCount - 1; i++) {
|
||||
for (size_t i = 0; i < txCount; ++i) {
|
||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
|
||||
signed_transaction_t* txNext = (signed_transaction_t*)DynArr_at(block->transactions, i + 1);
|
||||
uint8_t buf1[32] = {0}; uint8_t buf2[32] = {0}; // Zeroed out
|
||||
|
||||
// Unless if by some miracle the hash just so happens to be all zeros,
|
||||
// I think we can safely assume that a 1 : 2^256 chance will NEVER be hit
|
||||
Transaction_CalculateHash(tx, buf1);
|
||||
Transaction_CalculateHash(txNext, buf2);
|
||||
|
||||
// Concat the two hashes
|
||||
uint8_t dataInBuffer[64] = {0};
|
||||
uint8_t* nextStart = dataInBuffer;
|
||||
nextStart += 32;
|
||||
memcpy(dataInBuffer, buf1, 32);
|
||||
if (txNext) { memcpy(nextStart, buf2, 32); }
|
||||
|
||||
// Double hash that tx set
|
||||
uint8_t outHash[32];
|
||||
SHA256((const unsigned char*)dataInBuffer, 64, outHash);
|
||||
SHA256(outHash, 32, outHash);
|
||||
|
||||
// Copy to the hashes dynarr
|
||||
DynArr_push_back(hashes1, outHash);
|
||||
if (!tx) {
|
||||
free(current);
|
||||
free(next);
|
||||
memset(outHash, 0, 32);
|
||||
return;
|
||||
}
|
||||
Transaction_CalculateHash(tx, current + (i * 32u));
|
||||
}
|
||||
|
||||
// Move to hashing the existing ones until only one remains
|
||||
do {
|
||||
for (size_t i = 0; i < DynArr_size(hashes1) - 1; i++) {
|
||||
uint8_t* hash1 = (uint8_t*)DynArr_at(hashes1, i); uint8_t* hash2 = (uint8_t*)DynArr_at(hashes1, i + 1);
|
||||
size_t levelCount = txCount;
|
||||
while (levelCount > 1) {
|
||||
size_t nextCount = 0;
|
||||
for (size_t i = 0; i < levelCount; i += 2) {
|
||||
const uint8_t* left = current + (i * 32u);
|
||||
const uint8_t* right = (i + 1 < levelCount) ? current + ((i + 1) * 32u) : left;
|
||||
|
||||
// Concat the two hashes
|
||||
uint8_t dataInBuffer[64] = {0};
|
||||
uint8_t* nextStart = dataInBuffer;
|
||||
nextStart += 32;
|
||||
memcpy(dataInBuffer, hash1, 32);
|
||||
memcpy(nextStart, hash2, 32);
|
||||
uint8_t dataInBuffer[64];
|
||||
memcpy(dataInBuffer, left, 32);
|
||||
memcpy(dataInBuffer + 32, right, 32);
|
||||
|
||||
// Double hash that tx set
|
||||
uint8_t outHash[32];
|
||||
SHA256((const unsigned char*)dataInBuffer, 64, outHash);
|
||||
SHA256(outHash, 32, outHash);
|
||||
|
||||
DynArr_push_back(hashes2, outHash);
|
||||
SHA256((const unsigned char*)dataInBuffer, 64, next + (nextCount * 32u));
|
||||
SHA256(next + (nextCount * 32u), 32, next + (nextCount * 32u));
|
||||
++nextCount;
|
||||
}
|
||||
|
||||
DynArr_erase(hashes1);
|
||||
for (size_t i = 0; i < DynArr_size(hashes2); i++) {
|
||||
DynArr_push_back(hashes1, (uint8_t*)DynArr_at(hashes2, i));
|
||||
}
|
||||
DynArr_erase(hashes2);
|
||||
} while (DynArr_size(hashes1) > 1);
|
||||
|
||||
// Final Merkle
|
||||
uint8_t* merkle = (uint8_t*)DynArr_at(hashes1, 0);
|
||||
if (merkle) {
|
||||
memcpy(outHash, merkle, 32);
|
||||
} else {
|
||||
memset(outHash, 0, 32);
|
||||
uint8_t* swap = current;
|
||||
current = next;
|
||||
next = swap;
|
||||
levelCount = nextCount;
|
||||
}
|
||||
|
||||
DynArr_destroy(hashes1);
|
||||
DynArr_destroy(hashes2);
|
||||
memcpy(outHash, current, 32);
|
||||
free(current);
|
||||
free(next);
|
||||
}
|
||||
|
||||
void Block_CalculateAutolykos2Hash(const block_t* block, uint8_t* outHash) {
|
||||
@@ -216,39 +189,6 @@ static int Uint256_CompareBE(const uint8_t a[32], const uint8_t b[32]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool DecodeCompactTarget(uint32_t nBits, uint8_t target[32]) {
|
||||
memset(target, 0, 32);
|
||||
|
||||
uint32_t exponent = nBits >> 24;
|
||||
uint32_t mantissa = nBits & 0x007fffff; // ignore sign bit for now
|
||||
bool negative = (nBits & 0x00800000) != 0;
|
||||
|
||||
if (negative || mantissa == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute: target = mantissa * 256^(exponent - 3)
|
||||
if (exponent <= 3) {
|
||||
mantissa >>= 8 * (3 - exponent);
|
||||
|
||||
target[29] = (mantissa >> 16) & 0xff;
|
||||
target[30] = (mantissa >> 8) & 0xff;
|
||||
target[31] = mantissa & 0xff;
|
||||
} else {
|
||||
uint32_t byte_index = exponent - 3; // number of zero-bytes appended on right
|
||||
|
||||
if (byte_index > 29) {
|
||||
return false; // overflow 256 bits
|
||||
}
|
||||
|
||||
target[32 - byte_index - 3] = (mantissa >> 16) & 0xff;
|
||||
target[32 - byte_index - 2] = (mantissa >> 8) & 0xff;
|
||||
target[32 - byte_index - 1] = mantissa & 0xff;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Block_HasValidProofOfWork(const block_t* block) {
|
||||
if (!block) {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <block/chain.h>
|
||||
#include <constants.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
uint64_t currentBlockHeight = 0;
|
||||
@@ -611,6 +612,178 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Chain_LoadBlockFromFile(const char* dirpath, uint64_t blockNumber, bool loadTransactions, block_t** outBlock, size_t* outTxCount) {
|
||||
if (!dirpath || !outBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*outBlock = NULL;
|
||||
if (outTxCount) {
|
||||
*outTxCount = 0;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(dirpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char metaPath[512];
|
||||
if (!BuildPath(metaPath, sizeof(metaPath), dirpath, "chain.meta")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char chainPath[512];
|
||||
if (!BuildPath(chainPath, sizeof(chainPath), dirpath, "chain.data")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char tablePath[512];
|
||||
if (!BuildPath(tablePath, sizeof(tablePath), dirpath, "chain.table")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* metaFile = fopen(metaPath, "rb");
|
||||
FILE* chainFile = fopen(chainPath, "rb");
|
||||
FILE* tableFile = fopen(tablePath, "rb");
|
||||
if (!metaFile || !chainFile || !tableFile) {
|
||||
if (metaFile) fclose(metaFile);
|
||||
if (chainFile) fclose(chainFile);
|
||||
if (tableFile) fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t savedSize = 0;
|
||||
if (fread(&savedSize, sizeof(size_t), 1, metaFile) != 1) {
|
||||
fclose(metaFile);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(metaFile);
|
||||
|
||||
if (blockNumber >= (uint64_t)savedSize) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t tableOffset = blockNumber * (uint64_t)sizeof(block_table_entry_t);
|
||||
if (blockNumber != 0 && tableOffset / blockNumber != (uint64_t)sizeof(block_table_entry_t)) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
if (tableOffset > (uint64_t)LONG_MAX || fseek(tableFile, (long)tableOffset, SEEK_SET) != 0) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
block_table_entry_t loc;
|
||||
if (fread(&loc, sizeof(block_table_entry_t), 1, tableFile) != 1) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loc.blockNumber != blockNumber) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loc.byteNumber > (uint64_t)LONG_MAX || fseek(chainFile, (long)loc.byteNumber, SEEK_SET) != 0) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
block_t* blk = (block_t*)calloc(1, sizeof(block_t));
|
||||
if (!blk) {
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fread(&blk->header, sizeof(block_header_t), 1, chainFile) != 1) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t txSize = 0;
|
||||
if (fread(&txSize, sizeof(size_t), 1, chainFile) != 1) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outTxCount) {
|
||||
*outTxCount = txSize;
|
||||
}
|
||||
|
||||
if (loadTransactions) {
|
||||
blk->transactions = DYNARR_CREATE(signed_transaction_t, txSize == 0 ? 1 : txSize);
|
||||
if (!blk->transactions) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < txSize; ++i) {
|
||||
signed_transaction_t tx;
|
||||
if (fread(&tx, sizeof(signed_transaction_t), 1, chainFile) != 1) {
|
||||
DynArr_destroy(blk->transactions);
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DynArr_push_back(blk->transactions, &tx)) {
|
||||
DynArr_destroy(blk->transactions);
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (txSize > 0) {
|
||||
uint64_t skipBytes = (uint64_t)txSize * (uint64_t)sizeof(signed_transaction_t);
|
||||
if (txSize != 0 && skipBytes / txSize != (uint64_t)sizeof(signed_transaction_t)) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
if (skipBytes > (uint64_t)LONG_MAX) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
if (fseek(chainFile, (long)skipBytes, SEEK_CUR) != 0) {
|
||||
free(blk);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
blk->transactions = NULL;
|
||||
}
|
||||
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
|
||||
*outBlock = blk;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Chain_ComputeNextTarget(blockchain_t* chain, uint32_t currentTarget) {
|
||||
if (!chain || !chain->blocks) {
|
||||
return 0x00; // Impossible difficulty, only valid hash is all zeros (practically impossible)
|
||||
|
||||
345
src/main.c
345
src/main.c
@@ -1,6 +1,6 @@
|
||||
#include <block/chain.h>
|
||||
#include <block/transaction.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <utils.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <autolykos2/autolykos2.h>
|
||||
|
||||
#include <nets/net_node.h>
|
||||
#include <crypto/crypto.h>
|
||||
|
||||
#ifndef CHAIN_DATA_DIR
|
||||
#define CHAIN_DATA_DIR "chain_data"
|
||||
@@ -34,56 +33,6 @@ uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
||||
// extern the currentReward from constants.h so we can update it as we mine blocks and save it to disk
|
||||
extern uint64_t currentReward;
|
||||
|
||||
static void AddressFromCompressedPubkey(const uint8_t compressedPubkey[33], uint8_t outAddress[32]) {
|
||||
if (!compressedPubkey || !outAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
SHA256(compressedPubkey, 33, outAddress);
|
||||
}
|
||||
|
||||
static bool GenerateTestMinerIdentity(uint8_t privateKey[32], uint8_t compressedPubkey[33], uint8_t address[32]) {
|
||||
if (!privateKey || !compressedPubkey || !address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t seed[64];
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
for (uint64_t counter = 0; counter < 1024; ++counter) {
|
||||
const char* base = "skalacoin-test-miner-key";
|
||||
size_t baseLen = strlen(base);
|
||||
memcpy(seed, base, baseLen);
|
||||
memcpy(seed + baseLen, &counter, sizeof(counter));
|
||||
SHA256(seed, baseLen + sizeof(counter), privateKey);
|
||||
|
||||
if (!secp256k1_ec_seckey_verify(ctx, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pubLen = 33;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, compressedPubkey, &pubLen, &pubkey, SECP256K1_EC_COMPRESSED) || pubLen != 33) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddressFromCompressedPubkey(compressedPubkey, address);
|
||||
secp256k1_context_destroy(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool MineBlock(block_t* block) {
|
||||
if (!block) {
|
||||
return false;
|
||||
@@ -157,6 +106,158 @@ static void AddCoinbaseTransaction(block_t* block, const uint8_t minerAddress[32
|
||||
Block_AddTransaction(block, &coinbaseTx);
|
||||
}
|
||||
|
||||
static void PrintBlockDetail(const block_t* block, size_t txCount, const uint8_t canonicalHash[32], const uint8_t powHash[32]) {
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Block #%llu\n", (unsigned long long)block->header.blockNumber);
|
||||
printf(" Timestamp: %llu\n", (unsigned long long)block->header.timestamp);
|
||||
printf(" Nonce: %llu\n", (unsigned long long)block->header.nonce);
|
||||
printf(" Difficulty Target: 0x%08x\n", block->header.difficultyTarget);
|
||||
printf(" Version: %u\n", block->header.version);
|
||||
printf(" Reserved: %02x %02x %02x\n",
|
||||
block->header.reserved[0],
|
||||
block->header.reserved[1],
|
||||
block->header.reserved[2]);
|
||||
printf(" Previous Hash: ");
|
||||
PrintHexBytes(block->header.prevHash, sizeof(block->header.prevHash));
|
||||
printf("\n");
|
||||
printf(" Merkle Root: ");
|
||||
PrintHexBytes(block->header.merkleRoot, sizeof(block->header.merkleRoot));
|
||||
printf("\n");
|
||||
printf(" Transactions on disk: %zu\n", txCount);
|
||||
printf(" Canonical Hash: ");
|
||||
PrintHexBytes(canonicalHash, 32);
|
||||
printf("\n");
|
||||
printf(" PoW Hash: ");
|
||||
PrintHexBytes(powHash, 32);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static bool ComputeEpochSeedForHeightFromChain(const blockchain_t* chain, uint64_t blockHeight, uint8_t outSeed[32]) {
|
||||
if (!chain || !outSeed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t epochIndex = blockHeight / EPOCH_LENGTH;
|
||||
if (epochIndex == 0) {
|
||||
memset(outSeed, DAG_GENESIS_SEED, 32);
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t seedBlockNumber = (epochIndex * EPOCH_LENGTH) - 1ULL;
|
||||
if (seedBlockNumber >= Chain_Size((blockchain_t*)chain)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
block_t* seedBlock = Chain_GetBlock((blockchain_t*)chain, (size_t)seedBlockNumber);
|
||||
if (!seedBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Block_CalculateHash(seedBlock, outSeed);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ComputeEpochDagBytesForHeightFromChain(const blockchain_t* chain, uint64_t blockHeight, size_t* outDagBytes) {
|
||||
if (!chain || !outDagBytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blockHeight <= EPOCH_LENGTH) {
|
||||
*outDagBytes = DAG_BASE_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t lastBlockNumber = blockHeight - 1ULL;
|
||||
const uint64_t epochStartBlockNumber = lastBlockNumber - EPOCH_LENGTH;
|
||||
if (lastBlockNumber >= Chain_Size((blockchain_t*)chain) || epochStartBlockNumber >= Chain_Size((blockchain_t*)chain)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
block_t* lastBlock = Chain_GetBlock((blockchain_t*)chain, (size_t)lastBlockNumber);
|
||||
block_t* epochStartBlock = Chain_GetBlock((blockchain_t*)chain, (size_t)epochStartBlockNumber);
|
||||
if (!lastBlock || !epochStartBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t difficultyDelta = (int64_t)epochStartBlock->header.difficultyTarget - (int64_t)lastBlock->header.difficultyTarget;
|
||||
int64_t growth = (int64_t)((int64_t)DAG_BASE_GROWTH * difficultyDelta);
|
||||
|
||||
if (growth > 0) {
|
||||
int64_t maxUp = (int64_t)((DAG_BASE_SIZE * 15ULL) / 100ULL);
|
||||
if (growth > maxUp) {
|
||||
growth = maxUp;
|
||||
}
|
||||
if (growth > (int64_t)DAG_MAX_UP_SWING_GB) {
|
||||
growth = (int64_t)DAG_MAX_UP_SWING_GB;
|
||||
}
|
||||
} else {
|
||||
int64_t maxDown = (int64_t)((DAG_BASE_SIZE * 10ULL) / 100ULL);
|
||||
if (-growth > maxDown) {
|
||||
growth = -maxDown;
|
||||
}
|
||||
if (-growth > (int64_t)DAG_MAX_DOWN_SWING_GB) {
|
||||
growth = -(int64_t)DAG_MAX_DOWN_SWING_GB;
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t targetSize = (int64_t)DAG_BASE_SIZE + growth;
|
||||
if (targetSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*outDagBytes = (size_t)targetSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ComputeHistoricalAutolykosHashFromChain(const blockchain_t* chain, const block_t* block, uint64_t blockHeight, uint8_t outHash[32]) {
|
||||
if (!chain || !block || !outHash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t seed[32];
|
||||
size_t dagBytes = 0;
|
||||
if (!ComputeEpochSeedForHeightFromChain(chain, blockHeight, seed)) {
|
||||
return false;
|
||||
}
|
||||
if (!ComputeEpochDagBytesForHeightFromChain(chain, blockHeight, &dagBytes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Autolykos2_LightHashAtHeight(
|
||||
seed,
|
||||
(const uint8_t*)&block->header,
|
||||
sizeof(block_header_t),
|
||||
block->header.nonce,
|
||||
blockHeight,
|
||||
dagBytes,
|
||||
outHash
|
||||
);
|
||||
}
|
||||
|
||||
static bool ComputeHistoricalAutolykosHashFromDisk(const char* chainDataDir, uint64_t blockHeight, const block_t* block, uint8_t outHash[32]) {
|
||||
if (!chainDataDir || !block || !outHash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blockchain_t* headerChain = Chain_Create();
|
||||
if (!headerChain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint256_t supply = uint256_from_u64(0);
|
||||
uint32_t difficulty = INITIAL_DIFFICULTY;
|
||||
uint64_t reward = 0;
|
||||
uint8_t lastHash[32] = {0};
|
||||
bool loaded = Chain_LoadFromFile(headerChain, chainDataDir, &supply, &difficulty, &reward, lastHash, false);
|
||||
bool ok = loaded && ComputeHistoricalAutolykosHashFromChain(headerChain, block, blockHeight, outHash);
|
||||
|
||||
Chain_Destroy(headerChain);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool MineAndAppendBlock(blockchain_t* chain,
|
||||
block_t* block,
|
||||
uint256_t* currentSupply,
|
||||
@@ -221,47 +322,6 @@ static bool MineAndAppendBlock(blockchain_t* chain,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GenerateRandomTestAddress(uint8_t outAddress[32]) {
|
||||
if (!outAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t privateKey[32];
|
||||
uint8_t compressedPubkey[33];
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t attempt = 0; attempt < 4096; ++attempt) {
|
||||
for (size_t i = 0; i < sizeof(privateKey); ++i) {
|
||||
privateKey[i] = (uint8_t)(rand() & 0xFF);
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_seckey_verify(ctx, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, privateKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pubLen = sizeof(compressedPubkey);
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, compressedPubkey, &pubLen, &pubkey, SECP256K1_EC_COMPRESSED) || pubLen != 33) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddressFromCompressedPubkey(compressedPubkey, outAddress);
|
||||
secp256k1_context_destroy(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void WipeChainFiles(const char* chainDataDir) {
|
||||
if (!chainDataDir) {
|
||||
return;
|
||||
@@ -288,45 +348,81 @@ static bool VerifyChainFully(blockchain_t* chain) {
|
||||
}
|
||||
|
||||
size_t chainSize = Chain_Size(chain);
|
||||
// Build a lightweight previous-block-only chain to compute expected difficulty
|
||||
blockchain_t* prevChain = Chain_Create();
|
||||
if (!prevChain) { return false; }
|
||||
|
||||
uint32_t expectedDifficulty = INITIAL_DIFFICULTY;
|
||||
for (size_t i = 0; i < chainSize; ++i) {
|
||||
block_t* blk = Chain_GetBlock(chain, i);
|
||||
if (!blk || !blk->transactions) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blk->header.blockNumber != (uint64_t)i) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
uint8_t zeroHash[32] = {0};
|
||||
if (memcmp(blk->header.prevHash, zeroHash, sizeof(zeroHash)) != 0) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
block_t* prevBlk = Chain_GetBlock(chain, i - 1);
|
||||
if (!prevBlk) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t expectedPrevHash[32];
|
||||
Block_CalculateHash(prevBlk, expectedPrevHash);
|
||||
if (memcmp(blk->header.prevHash, expectedPrevHash, sizeof(expectedPrevHash)) != 0) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Block_HasValidProofOfWork(blk)) {
|
||||
// Determine expected difficulty for this block. TODO: Optimize to recompute at adjustment intervals only instead of every block.
|
||||
if (i < DIFFICULTY_ADJUSTMENT_INTERVAL) {
|
||||
expectedDifficulty = INITIAL_DIFFICULTY;
|
||||
} else if ((i % DIFFICULTY_ADJUSTMENT_INTERVAL) == 0) {
|
||||
// Compute target using previous blocks only (0..i-1)
|
||||
expectedDifficulty = Chain_ComputeNextTarget(prevChain, expectedDifficulty);
|
||||
}
|
||||
|
||||
// Ensure the block's header difficulty matches the expected difficulty (can't cheat easier)
|
||||
if (blk->header.difficultyTarget != expectedDifficulty) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t powHash[32];
|
||||
if (!ComputeHistoricalAutolykosHashFromChain(chain, blk, (uint64_t)i, powHash)) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t target[32];
|
||||
if (!DecodeCompactTarget(blk->header.difficultyTarget, target)) {
|
||||
return false;
|
||||
}
|
||||
if (CompareHashToTarget(powHash, target) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Block_AllTransactionsValid(blk)) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t expectedMerkle[32];
|
||||
Block_CalculateMerkleRoot(blk, expectedMerkle);
|
||||
if (memcmp(blk->header.merkleRoot, expectedMerkle, sizeof(expectedMerkle)) != 0) {
|
||||
Chain_Destroy(prevChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -334,8 +430,16 @@ static bool VerifyChainFully(blockchain_t* chain) {
|
||||
// release its in-memory transaction list to reduce peak memory usage.
|
||||
DynArr_destroy(blk->transactions);
|
||||
blk->transactions = NULL;
|
||||
|
||||
// Push a header-only copy of this block into prevChain for future difficulty calculations.
|
||||
block_t headerOnly;
|
||||
memset(&headerOnly, 0, sizeof(headerOnly));
|
||||
headerOnly.header = blk->header;
|
||||
headerOnly.transactions = NULL;
|
||||
(void)DynArr_push_back(prevChain->blocks, &headerOnly);
|
||||
}
|
||||
|
||||
Chain_Destroy(prevChain);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -426,7 +530,7 @@ int main(int argc, char* argv[]) {
|
||||
char supplyStr[80];
|
||||
Uint256ToDecimal(¤tSupply, supplyStr, sizeof(supplyStr));
|
||||
printf("Current chain has %zu blocks, total supply %s\n", Chain_Size(chain), supplyStr);
|
||||
printf("Commands: mine <x>, send <address> <amount>, balance [address], connect <ipv4>, flushchain, fullverify, wipechain, genaddr, exit\n");
|
||||
printf("Commands: mine <x>, send <address> <amount>, balance [address], connect <ipv4>, flushchain, fullverify, blockdetail <block number>, wipechain, genaddr, exit\n");
|
||||
|
||||
char line[1024];
|
||||
while (true) {
|
||||
@@ -481,7 +585,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
free(block); // Chain stores block by value and owns copied transaction array.
|
||||
|
||||
if (i % 1000 == 0) {
|
||||
if (i % 50 == 0) {
|
||||
// Mid-mine flush
|
||||
(void)FlushChainAndSheet(chain, chainDataDir, currentSupply, currentReward);
|
||||
}
|
||||
@@ -550,17 +654,59 @@ int main(int argc, char* argv[]) {
|
||||
Transaction_Sign(&spendTx, minerPrivateKey);
|
||||
|
||||
Block_AddTransaction(block, &spendTx);
|
||||
|
||||
printf("Created transaction sending %llu pebble(s) to ", (unsigned long long)amount);
|
||||
char recipientHex[65];
|
||||
AddressToHexString(recipientAddress, recipientHex);
|
||||
printf("%s\n\nMining block...\n", recipientHex);
|
||||
|
||||
if (!MineAndAppendBlock(chain, block, ¤tSupply, ¤tReward, &difficultyTarget)) {
|
||||
Block_Destroy(block);
|
||||
continue;
|
||||
}
|
||||
|
||||
FlushChainAndSheet(chain, chainDataDir, currentSupply, currentReward);
|
||||
|
||||
free(block);
|
||||
printf("send committed in mined block\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "blockdetail") == 0) {
|
||||
char* blockNumberStr = strtok(NULL, " \t");
|
||||
char* extra = strtok(NULL, " \t");
|
||||
if (!blockNumberStr || extra) {
|
||||
printf("usage: blockdetail <block number>\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
char* endptr = NULL;
|
||||
unsigned long long requestedBlock = strtoull(blockNumberStr, &endptr, 10);
|
||||
if (*blockNumberStr == '\0' || blockNumberStr[0] == '-' || (endptr && *endptr != '\0')) {
|
||||
printf("invalid block number\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
block_t* detailBlock = NULL;
|
||||
size_t txCount = 0;
|
||||
if (!Chain_LoadBlockFromFile(chainDataDir, (uint64_t)requestedBlock, false, &detailBlock, &txCount)) {
|
||||
printf("block %llu not found\n", requestedBlock);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t canonicalHash[32];
|
||||
uint8_t powHash[32];
|
||||
Block_CalculateHash(detailBlock, canonicalHash);
|
||||
if (!ComputeHistoricalAutolykosHashFromDisk(chainDataDir, (uint64_t)requestedBlock, detailBlock, powHash)) {
|
||||
Block_Destroy(detailBlock);
|
||||
printf("failed to calculate block %llu proof hash\n", requestedBlock);
|
||||
continue;
|
||||
}
|
||||
|
||||
PrintBlockDetail(detailBlock, txCount, canonicalHash, powHash);
|
||||
Block_Destroy(detailBlock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "balance") == 0) {
|
||||
char* addressStr = strtok(NULL, " \t");
|
||||
char* extra = strtok(NULL, " \t");
|
||||
@@ -654,9 +800,6 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
bool ok = false;
|
||||
if (loaded) {
|
||||
uint8_t dagSeed[32];
|
||||
GetNextDAGSeed(verifyChain, dagSeed);
|
||||
(void)Block_RebuildAutolykos2Dag(CalculateTargetDAGSize(verifyChain), dagSeed);
|
||||
ok = VerifyChainFully(verifyChain);
|
||||
}
|
||||
|
||||
@@ -699,7 +842,7 @@ int main(int argc, char* argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Unknown command. Available: mine, send, balance, connect, flushchain, fullverify, wipechain, genaddr, exit\n");
|
||||
printf("Unknown command. Available: mine, send, balance, connect, flushchain, fullverify, blockdetail, wipechain, genaddr, exit\n");
|
||||
}
|
||||
|
||||
(void)FlushChainAndSheet(chain, chainDataDir, currentSupply, currentReward);
|
||||
|
||||
Reference in New Issue
Block a user