bool CBBlockChainStorageLoadOutputs(void * validator, uint8_t * txHash, uint8_t ** data, uint32_t * dataAllocSize, uint32_t * position){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 14, 0)) { CBLogError("Could not read a transaction reference from the transaction index."); return false; } // Set position of outputs *position = CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_POSITION_OUPTUTS); // Get transaction to find position for output in the block // Reallocate transaction data memory if needed. if (CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_LENGTH_OUTPUTS) > *dataAllocSize) { *dataAllocSize = CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_LENGTH_OUTPUTS); *data = realloc(*data, *dataAllocSize); if (NOT *data) { CBLogError("Could not allocate memory for reading a transaction."); return false; } } // Read transaction from the block CB_BLOCK_KEY[2] = CB_DATA_ARRAY[CB_TRANSACTION_REF_BRANCH]; memcpy(CB_BLOCK_KEY + 3, CB_DATA_ARRAY + CB_TRANSACTION_REF_BLOCK_INDEX, 4); if (NOT CBDatabaseReadValue(database, CB_BLOCK_KEY, *data, CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_LENGTH_OUTPUTS), CB_BLOCK_START + *position)) { CBLogError("Could not read a transaction from the block-chain database."); return false; } return true; }
void * CBBlockChainStorageLoadUnspentOutput(void * validator, uint8_t * txHash, uint32_t outputIndex, bool * coinbase, uint32_t * outputHeight){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // First read data for the unspent output key. memcpy(CB_UNSPENT_OUTPUT_KEY + 2, txHash, 32); CBInt32ToArray(CB_UNSPENT_OUTPUT_KEY, 34, outputIndex); if (NOT CBDatabaseReadValue(database, CB_UNSPENT_OUTPUT_KEY, CB_DATA_ARRAY, 8, 0)) { CBLogError("Cannot read unspent output information from the block chain database"); return NULL; } uint32_t outputPosition = CBArrayToInt32(CB_DATA_ARRAY, CB_UNSPENT_OUTPUT_REF_POSITION); uint32_t outputLength = CBArrayToInt32(CB_DATA_ARRAY, CB_UNSPENT_OUTPUT_REF_LENGTH); // Now read data for the transaction memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 14, 0)) { CBLogError("Cannot read a transaction reference from the transaction index."); return NULL; } uint8_t outputBranch = CB_DATA_ARRAY[CB_TRANSACTION_REF_BRANCH]; uint32_t outputBlockIndex = CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_BLOCK_INDEX); // Set coinbase *coinbase = CB_DATA_ARRAY[CB_TRANSACTION_REF_IS_COINBASE]; // Set output height *outputHeight = validatorObj->branches[outputBranch].startHeight + outputBlockIndex; // Get the output from storage CB_BLOCK_KEY[2] = outputBranch; CBInt32ToArray(CB_BLOCK_KEY, 3, outputBlockIndex); // Get output data CBByteArray * outputBytes = CBNewByteArrayOfSize(outputLength); if (NOT outputBytes) { CBLogError("Could not create CBByteArray for an unspent output."); return NULL; } if (NOT CBDatabaseReadValue(database, CB_BLOCK_KEY, CBByteArrayGetData(outputBytes), outputLength, CB_BLOCK_START + outputPosition)) { CBLogError("Could not read an unspent output"); CBReleaseObject(outputBytes); return NULL; } // Create output object CBTransactionOutput * output = CBNewTransactionOutputFromData(outputBytes); CBReleaseObject(outputBytes); if (NOT output) { CBLogError("Could not create an object for an unspent output"); return NULL; } if (NOT CBTransactionOutputDeserialise(output)) { CBLogError("Could not deserialise an unspent output"); return NULL; } return output; }
bool CBBlockChainStorageSaveTransactionRef(void * validator, uint8_t * txHash, uint8_t branch, uint32_t blockIndex, uint32_t outputPos, uint32_t outputsLen, bool coinbase, uint32_t numOutputs){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); if (CBDatabaseGetLength(database, CB_TRANSACTION_INDEX_KEY)) { // We have the transaction already. Thus obtain the data already in the index. if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 22, 0)) { CBLogError("Could not read a transaction reference from the transaction index."); return false; } // Increase the instance count. We change nothing else as we will use the first instance for all other instances. CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_INSTANCE_COUNT, CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_INSTANCE_COUNT) + 1); }else{ // This transaction has not yet been seen in the block chain. CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_BLOCK_INDEX, blockIndex); CB_DATA_ARRAY[CB_TRANSACTION_REF_BRANCH] = branch; CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_POSITION_OUPTUTS, outputPos); CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_LENGTH_OUTPUTS, outputsLen); CB_DATA_ARRAY[CB_TRANSACTION_REF_IS_COINBASE] = coinbase; // We start with an instance count of one CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_INSTANCE_COUNT, 1); } // Always set the number of unspent outputs back to the number of outputs in the transaction CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_NUM_UNSPENT_OUTPUTS, numOutputs); // Write to the transaction index. if (NOT CBDatabaseWriteValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 22)) { CBLogError("Could not write transaction reference to transaction index."); return false; } return true; }
void * CBBlockChainStorageLoadBlock(void * validator, uint32_t blockID, uint32_t branch){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; CB_BLOCK_KEY[2] = branch; CBInt32ToArray(CB_BLOCK_KEY, 3, blockID); uint32_t blockDataLen = CBDatabaseGetLength(database, CB_BLOCK_KEY); if (NOT blockDataLen) return NULL; blockDataLen -= CB_BLOCK_START; // Get block data CBByteArray * data = CBNewByteArrayOfSize(blockDataLen); if (NOT data) { CBLogError("Could not initialise a byte array for loading a block."); return NULL; } if (NOT CBDatabaseReadValue(database, CB_BLOCK_KEY, CBByteArrayGetData(data), blockDataLen, CB_BLOCK_START)){ CBLogError("Could not read a block from the database."); CBReleaseObject(data); return NULL; } // Make and return the block CBBlock * block = CBNewBlockFromData(data); CBReleaseObject(data); if (NOT block) { CBLogError("Could not create a block object when loading a block."); return NULL; } return block; }
bool CBBlockChainStorageLoadOrphan(void * validator, uint8_t orphanNum){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; CB_ORPHAN_KEY[2] = orphanNum; uint32_t len = CBDatabaseGetLength(database, CB_ORPHAN_KEY); CBByteArray * orphanData = CBNewByteArrayOfSize(len); if (NOT orphanData) { CBLogError("There was an error when initialising a byte array for an orphan."); return false; } if (NOT CBDatabaseReadValue(database, CB_ORPHAN_KEY, CBByteArrayGetData(orphanData), len, 0)) { CBLogError("There was an error when reading the data for an orphan."); CBReleaseObject(orphanData); return false; } validatorObj->orphans[orphanNum] = CBNewBlockFromData(orphanData); CBBlockDeserialise(validatorObj->orphans[orphanNum], true); if (NOT validatorObj->orphans[orphanNum]) { CBLogError("There was an error when creating a block object for an orphan."); CBReleaseObject(orphanData); return false; } CBReleaseObject(orphanData); return true; }
bool CBBlockChainStorageDeleteTransactionRef(void * validator, uint8_t * txHash){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // Place transaction hash into the key memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); // Read the instance count if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 22, 0)) { CBLogError("Could not read a transaction reference from storage."); return false; } uint32_t txInstanceNum = CB_DATA_ARRAY[CB_TRANSACTION_REF_INSTANCE_COUNT] - 1; if (txInstanceNum) { // There are still more instances of this transaction. Do not remove the transaction, only make the unspent output number equal to zero and decrement the instance count. CB_DATA_ARRAY[CB_TRANSACTION_REF_NUM_UNSPENT_OUTPUTS] = 0; CB_DATA_ARRAY[CB_TRANSACTION_REF_INSTANCE_COUNT] = txInstanceNum; // Write to storage. if (NOT CBDatabaseWriteValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 22)) { CBLogError("Could not update a transaction reference for deleting an instance."); return false; } }else{ // This was the last instance. // Remove from storage if (NOT CBDatabaseRemoveValue(database, CB_TRANSACTION_INDEX_KEY)) { CBLogError("Could not remove a transaction reference from storage."); return false; } } return true; }
bool CBBlockChainStorageChangeUnspentOutputsNum(CBDatabase * database, uint8_t * txHash, int8_t change){ // Place transaction hash into the key memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); // Read the number of unspent outputs to be decremented if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 18, 0)) return false; CBInt32ToArray(CB_DATA_ARRAY, CB_TRANSACTION_REF_NUM_UNSPENT_OUTPUTS, CBArrayToInt32(CB_DATA_ARRAY, CB_TRANSACTION_REF_NUM_UNSPENT_OUTPUTS) + change); // Now write the new value return CBDatabaseWriteValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 18); }
uint32_t CBBlockChainStorageGetBlockTarget(void * validator, uint8_t branch, uint32_t blockIndex){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; CB_BLOCK_KEY[2] = branch; CBInt32ToArray(CB_BLOCK_KEY, 3, blockIndex); if (NOT CBDatabaseReadValue(database, CB_BLOCK_KEY, CB_DATA_ARRAY, 4, CB_BLOCK_TARGET)) { CBLogError("Could not read the target for a block."); return 0; } return CBArrayToInt32(CB_DATA_ARRAY, 0); }
bool CBBlockChainStorageGetBlockLocation(void * validator, uint8_t * blockHash, uint8_t * branch, uint32_t * index){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; memcpy(CB_BLOCK_HASH_INDEX_KEY + 2, blockHash, 20); if (NOT CBDatabaseReadValue(database, CB_BLOCK_HASH_INDEX_KEY, CB_DATA_ARRAY, 5, 0)) { CBLogError("Could not read a block hash reference from the block chain database."); return false; } *branch = CB_DATA_ARRAY[CB_BLOCK_HASH_REF_BRANCH]; *index = CBArrayToInt32(CB_DATA_ARRAY, CB_BLOCK_HASH_REF_INDEX); return true; }
bool CBBlockChainStorageLoadBasicValidator(void * validator){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; if (NOT CBDatabaseReadValue(database, CB_VALIDATOR_INFO_KEY, CB_DATA_ARRAY, 4, 0)){ CBLogError("There was an error when reading the validator information from storage."); return false; } validatorObj->firstOrphan = CB_DATA_ARRAY[CB_VALIDATION_FIRST_ORPHAN]; validatorObj->numOrphans = CB_DATA_ARRAY[CB_VALIDATION_NUM_ORPHANS]; validatorObj->mainBranch = CB_DATA_ARRAY[CB_VALIDATION_MAIN_BRANCH]; validatorObj->numBranches = CB_DATA_ARRAY[CB_VALIDATION_NUM_BRANCHES]; return true; }
bool CBBlockChainStorageLoadBranchWork(void * validator, uint8_t branchNum){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // Get work CB_WORK_KEY[2] = branchNum; uint8_t workLen = CBDatabaseGetLength(database, CB_WORK_KEY); if (NOT CBBigIntAlloc(&validatorObj->branches[branchNum].work, workLen)){ CBLogError("There was an error when allocating memory for a branch's work."); return false; } validatorObj->branches[branchNum].work.length = workLen; if (NOT CBDatabaseReadValue(database, CB_WORK_KEY, validatorObj->branches[branchNum].work.data, workLen, 0)) { CBLogError("There was an error when reading the work for a branch."); free(validatorObj->branches[branchNum].work.data); return false; } return true; }
bool CBBlockChainStorageLoadBranch(void * validator, uint8_t branchNum){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // Get simple information CB_BRANCH_KEY[2] = branchNum; if (NOT CBDatabaseReadValue(database, CB_BRANCH_KEY, CB_DATA_ARRAY, 21, 0)) { CBLogError("There was an error when reading the data for a branch's information."); return false; } validatorObj->branches[branchNum].lastRetargetTime = CBArrayToInt32(CB_DATA_ARRAY, CB_BRANCH_LAST_RETARGET); validatorObj->branches[branchNum].lastValidation = CBArrayToInt32(CB_DATA_ARRAY, CB_BRANCH_LAST_VALIDATION); validatorObj->branches[branchNum].numBlocks = CBArrayToInt32(CB_DATA_ARRAY, CB_BRANCH_NUM_BLOCKS); validatorObj->branches[branchNum].parentBlockIndex = CBArrayToInt32(CB_DATA_ARRAY, CB_BRANCH_PARENT_BLOCK_INDEX); validatorObj->branches[branchNum].parentBranch = CB_DATA_ARRAY[CB_BRANCH_PARENT_BRANCH]; validatorObj->branches[branchNum].startHeight = CBArrayToInt32(CB_DATA_ARRAY, CB_BRANCH_START_HEIGHT); validatorObj->branches[branchNum].working = false; // Start off not working on any branch. return true; }
bool CBBlockChainStorageIsTransactionWithUnspentOutputs(void * validator, uint8_t * txHash, bool * exists){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // Place transaction hash into the key memcpy(CB_TRANSACTION_INDEX_KEY + 2, txHash, 32); // See if the transaction exists if (NOT CBDatabaseGetLength(database, CB_TRANSACTION_INDEX_KEY)){ *exists = false; return true; } // Now see if the transaction has unspent outputs if (NOT CBDatabaseReadValue(database, CB_TRANSACTION_INDEX_KEY, CB_DATA_ARRAY, 4, CB_TRANSACTION_REF_NUM_UNSPENT_OUTPUTS)) { CBLogError("Could not read the number of unspent outputs for a transaction."); return false; } *exists = CBArrayToInt32(CB_DATA_ARRAY, 0); return true; }
uint64_t CBNewAddressStorage(char * dataDir){ CBAddressStore * self = malloc(sizeof(*self)); if (NOT self) { CBLogError("Could not create the address storage object."); return 0; } if (NOT CBInitDatabase(CBGetDatabase(self), dataDir, "addr")) { CBLogError("Could not create a database object for address storage."); free(self); return 0; } if (CBDatabaseGetLength(CBGetDatabase(self), CB_ADDR_NUM_KEY)) { // Get the number of addresses if (NOT CBDatabaseReadValue(CBGetDatabase(self), CB_ADDR_NUM_KEY, CB_DATA_ARRAY, 4, 0)) { CBLogError("Could not read the number of addresses from storage."); free(self); return 0; } self->numAddresses = CBArrayToInt32(CB_DATA_ARRAY, 0); }else{ self->numAddresses = 0; // Insert zero number of addresses. CBInt32ToArray(CB_DATA_ARRAY, 0, 0); if (NOT CBDatabaseWriteValue(CBGetDatabase(self), CB_ADDR_NUM_KEY, CB_DATA_ARRAY, 4)) { CBLogError("Could not write the initial number of addresses to storage."); free(self); return 0; } // Commit changes if (NOT CBDatabaseCommit(CBGetDatabase(self))) { CBLogError("Could not commit the initial number of addresses to storage."); free(self); return 0; } } return (uint64_t) self; }
bool CBBlockChainStorageDeleteBlock(void * validator, uint8_t branch, uint32_t blockIndex){ CBFullValidator * validatorObj = validator; CBDatabase * database = (CBDatabase *)validatorObj->storage; // Delete from storage CB_BLOCK_KEY[2] = branch; CBInt32ToArray(CB_BLOCK_KEY, 3, blockIndex); // Get hash if (NOT CBDatabaseReadValue(database, CB_BLOCK_KEY, CB_DATA_ARRAY, 20, CB_BLOCK_HASH)) { CBLogError("Could not obtain a block hash from the block chain database."); return false; } // Remove data if (NOT CBDatabaseRemoveValue(database, CB_BLOCK_KEY)){ CBLogError("Could not remove block value from database."); return false; } // Remove hash index reference memcpy(CB_BLOCK_HASH_INDEX_KEY + 2, CB_DATA_ARRAY, 20); if (NOT CBDatabaseRemoveValue(database, CB_BLOCK_HASH_INDEX_KEY)){ CBLogError("Could not remove block hash index reference from database."); return false; } return true; }