Skip to content

Commit

Permalink
Reversed hashes by convention, added some extra comments to the code
Browse files Browse the repository at this point in the history
  • Loading branch information
gertjaap committed Oct 27, 2017
1 parent 8ab7b1a commit c2d3471
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 56 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,4 +1,5 @@
*.o
vtc_indexer
.vscode/
data/
build_and_restart.sh
68 changes: 62 additions & 6 deletions src/blockchaintypes.h
Expand Up @@ -23,51 +23,107 @@

#include <stdlib.h>
#include <vector>

using namespace std;

namespace VtcBlockIndexer {
// ScannedBlock is used to store information about block headers obtained while initially scanning through the block files
struct ScannedBlock {
// The filename (without path) where the block is located in
string fileName;

// The position inside the block file where the block header starts
int filePosition;

// The total size of the block
uint32_t blockSize;

// The hash of the block. This string is the the "reverse hash" used on block explorers
string blockHash;

// The hash of the previous block used to form the chain. This string is the the "reverse hash" used on block explorers
string previousBlockHash;
};

// Describes a transaction output inside a blockchain transaction
struct TransactionOutput {
// The value of the output in Satoshis (0.00000001 VTC)
uint64_t value;

// The output script in Bitcoinscript
vector<unsigned char> script;

// The index of the output in the list of outputs
uint32_t index;
};

// Describes a transaction input inside a blockchain transaction
struct TransactionInput {
// The index of the input int he list of indexes
uint32_t index;

// The hash of the transaction whose output is being spent
string txHash;
bool coinbase;

// The index of the output inside the transaction being spent
uint32_t txoIndex;
uint32_t sequence;

// Indicating if this is a coinbase (Generated coins) input
bool coinbase;

// The script of the input in Bitcoinscript
vector<unsigned char> script;

// The witness data for the input
vector<vector<unsigned char>> witnessData;
};

// Describes a transaction inside a block
struct Transaction {
// The list of inputs for this transaction
vector<TransactionInput> inputs;

// The list of outputs for this transaction
vector<TransactionOutput> outputs;

// The hash for the transaction. This is the reverse hash as used on block explorers.
string txHash;

// The hash for the witness transaction. Contains a different hash in case the transaction uses SegWit. Will be equal to TXHash otherwise.
string txWitHash;

// Position inside the blockfile where this transaction starts
int filePosition;

// Version bit for the transaction
uint32_t version;

// Locktime. Transaction cannot be spent until this number of blocks have been confirmed after its initial inclusion in the blockchain
uint32_t lockTime;
};

// Describes a block
struct Block {
// The blk????.dat file this block is located in.
string fileName;

// The position where the block starts inside the file
int filePosition;

// The hash of the block. This string is the the "reverse hash" used on block explorers
string blockHash;
string merkleRoot;

string previousBlockHash;
int time;

// The merkle root of the transactions inside this block
string merkleRoot;

// The height of the block in the chain
uint64_t height;

// The list of transactions inside this block
vector<Transaction> transactions;
};


}
#endif // BLOCKCHAINTYPES_H_INCLUDED
#endif // BLOCKCHAINTYPES_H_INCLUDED
31 changes: 13 additions & 18 deletions src/blockfilewatcher.cpp
Expand Up @@ -34,11 +34,15 @@
#include <thread>
#include <time.h>

// Block reader object used for reading the contents of blocks
VtcBlockIndexer::BlockReader blockReader("");

// Block indexer object used to pass blocks and store in the index
VtcBlockIndexer::BlockIndexer blockIndexer(nullptr);

using namespace std;

// Constructor
VtcBlockIndexer::BlockFileWatcher::BlockFileWatcher(string blocksDir, leveldb::DB* dbInstance) {
this->db = dbInstance;
blockIndexer = VtcBlockIndexer::BlockIndexer(this->db);
Expand All @@ -53,17 +57,18 @@ VtcBlockIndexer::BlockFileWatcher::BlockFileWatcher(string blocksDir, leveldb::D
void VtcBlockIndexer::BlockFileWatcher::startWatcher() {
DIR *dir;
dirent *ent;

string blockFilePrefix = "blk";

while(true) {
cout << "Checking blockdir [" << this->blocksDir << "] for modifications..." << endl;
bool shouldUpdate = false;
dir = opendir(&*this->blocksDir.begin());
while ((ent = readdir(dir)) != NULL) {
const string file_name = ent->d_name;
struct stat result;

// Check if the filename starts with "blk"
string prefix = "blk";
if(strncmp(file_name.c_str(), prefix.c_str(), prefix.size()) == 0)
if(strncmp(file_name.c_str(), blockFilePrefix.c_str(), blockFilePrefix.size()) == 0)
{
stringstream fullPath;
fullPath << this->blocksDir << "/" << file_name;
Expand All @@ -82,8 +87,6 @@ void VtcBlockIndexer::BlockFileWatcher::startWatcher() {

if(shouldUpdate) {
updateIndex();
} else {
cout << "No modifications found." << endl;
}

std::this_thread::sleep_for(std::chrono::seconds(10));
Expand Down Expand Up @@ -123,11 +126,7 @@ void VtcBlockIndexer::BlockFileWatcher::scanBlocks(string fileName) {
}
}

/** Scans a folder for block files present and passes them to the scanBlocks
* method
*
* @param dirPath The directory to scan for blockfiles.
*/

void VtcBlockIndexer::BlockFileWatcher::scanBlockFiles(string dirPath) {
DIR *dir;
dirent *ent;
Expand All @@ -146,6 +145,7 @@ void VtcBlockIndexer::BlockFileWatcher::scanBlockFiles(string dirPath) {
closedir(dir);
}


VtcBlockIndexer::ScannedBlock VtcBlockIndexer::BlockFileWatcher::findLongestChain(vector<VtcBlockIndexer::ScannedBlock> matchingBlocks) {
vector<string> nextBlockHashes;
for(uint i = 0; i < matchingBlocks.size(); i++) {
Expand Down Expand Up @@ -184,13 +184,7 @@ VtcBlockIndexer::ScannedBlock VtcBlockIndexer::BlockFileWatcher::findLongestChai
}
}

/** Finds the next block in line (by matching the prevBlockHash which is the
* key in the unordered_map). Then uses the block processor to do the indexing.
* Returns the hash of the block that was processed.
*
* @param prevBlockHash the hex hash of the block that was last processed that we should
* extend the chain onto.
*/

string VtcBlockIndexer::BlockFileWatcher::processNextBlock(string prevBlockHash) {


Expand Down Expand Up @@ -224,7 +218,6 @@ string VtcBlockIndexer::BlockFileWatcher::processNextBlock(string prevBlockHash)
}
}


void VtcBlockIndexer::BlockFileWatcher::updateIndex() {

time_t start;
Expand All @@ -243,6 +236,8 @@ void VtcBlockIndexer::BlockFileWatcher::updateIndex() {
string processedBlock = processNextBlock(nextBlock);
double nextUpdate = 10;
while(processedBlock != "") {

// Show progress every 10 seconds
double seconds = difftime(time(NULL), start);
if(seconds >= nextUpdate) {
nextUpdate += 10;
Expand Down
21 changes: 20 additions & 1 deletion src/blockfilewatcher.h
Expand Up @@ -56,8 +56,27 @@ class BlockFileWatcher {
* @param fileName The file name of the BLK????.DAT to scan for blocks.
*/
void scanBlocks(std::string fileName);

/** Scans a folder for block files present and passes them to the scanBlocks
* method
*
* @param dirPath The directory to scan for blockfiles.
*/
void scanBlockFiles(std::string dirName);
VtcBlockIndexer::ScannedBlock findLongestChain(std::vector<VtcBlockIndexer::ScannedBlock> matchingBlocks);

/** Orphaned blocks stay in the blockfiles. So this method is created to find out which of the canditate follow-up blocks
* chain of work behind it.
* @param matchingBlocks The blocks that should be investigated.
*/
VtcBlockIndexer::ScannedBlock findLongestChain(std::vector<VtcBlockIndexer::ScannedBlock> matchingBlocks);

/** Finds the next block in line (by matching the prevBlockHash which is the
* key in the unordered_map). Then uses the block processor to do the indexing.
* Returns the hash of the block that was processed.
*
* @param prevBlockHash the hex hash of the block that was last processed that we should
* extend the chain onto.
*/
std::string processNextBlock(std::string prevBlockHash);
std::string blocksDir;
leveldb::DB* db;
Expand Down
3 changes: 3 additions & 0 deletions src/blockindexer.cpp
Expand Up @@ -31,7 +31,10 @@

using namespace std;

// This map keeps the nextTxoIndex in memory for speed - no database fetching on every TX
unordered_map<string, int> nextTxoIndex;

// Reference to the scriptsolver class
VtcBlockIndexer::ScriptSolver* scriptSolver;

VtcBlockIndexer::BlockIndexer::BlockIndexer(leveldb::DB* dbInstance) {
Expand Down
20 changes: 7 additions & 13 deletions src/blockreader.cpp
Expand Up @@ -40,6 +40,8 @@ VtcBlockIndexer::BlockReader::BlockReader(const string blocksDir) {
VtcBlockIndexer::Block VtcBlockIndexer::BlockReader::readBlock(ScannedBlock block, uint64_t blockHeight) {
VtcBlockIndexer::Block fullBlock;

fullBlock.fileName = block.fileName;
fullBlock.filePosition = block.filePosition;
fullBlock.height = blockHeight;
fullBlock.previousBlockHash = block.previousBlockHash;
fullBlock.blockHash = block.blockHash;
Expand All @@ -49,21 +51,13 @@ VtcBlockIndexer::Block VtcBlockIndexer::BlockReader::readBlock(ScannedBlock bloc
ifstream blockFile(ss.str(), ios_base::in | ios_base::binary);

if(!blockFile.is_open()) {
cerr << "Block file could not be opened";
cerr << "Block file could not be opened" << endl;
exit(0);
}

// Seek to the start of the merkle root (we didn't read that while scanning)
blockFile.seekg(block.filePosition+36, ios_base::beg);
unique_ptr<unsigned char> merkleRoot(new unsigned char[32]);
blockFile.read(reinterpret_cast<char *>(&merkleRoot.get()[0]) , 32);

stringstream ssMR;
for(int i = 0; i < 32; i++)
{
ssMR << hex << setw(2) << setfill('0') << (int)merkleRoot.get()[i];
}
fullBlock.merkleRoot = ssMR.str();
fullBlock.merkleRoot = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::FileReader::readHash(blockFile));

// Find number of transactions
blockFile.seekg(block.filePosition+80, ios_base::beg);
Expand Down Expand Up @@ -97,7 +91,7 @@ VtcBlockIndexer::Block VtcBlockIndexer::BlockReader::readBlock(ScannedBlock bloc

for(uint64_t input = 0; input < inputCount; input++) {
VtcBlockIndexer::TransactionInput txInput;
txInput.txHash = VtcBlockIndexer::FileReader::readHash(blockFile);
txInput.txHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::FileReader::readHash(blockFile));
blockFile.read(reinterpret_cast<char *>(&txInput.txoIndex), sizeof(txInput.txoIndex));
txInput.script = VtcBlockIndexer::FileReader::readString(blockFile);
blockFile.read(reinterpret_cast<char *>(&txInput.sequence), sizeof(txInput.sequence));
Expand Down Expand Up @@ -154,14 +148,14 @@ VtcBlockIndexer::Block VtcBlockIndexer::BlockReader::readBlock(ScannedBlock bloc

blockFile.seekg(endPosTx-4, ios_base::beg);
blockFile.read(reinterpret_cast<char *>(&txHashBytes[0] + 4 + txitxoLength), sizeof(transaction.lockTime));
transaction.txHash = VtcBlockIndexer::Utility::hashToHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(txHashBytes)));
transaction.txHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(txHashBytes)));

if(segwit) {
blockFile.seekg(startPosTx, ios_base::beg);
uint64_t length = endPosTx-startPosTx;
std::vector<unsigned char> transactionBytes(length);
blockFile.read(reinterpret_cast<char *>(&transactionBytes[0]) , length);
transaction.txWitHash = VtcBlockIndexer::Utility::hashToHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(transactionBytes)));
transaction.txWitHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(transactionBytes)));
} else {
transaction.txWitHash = string(transaction.txHash);
}
Expand Down
5 changes: 2 additions & 3 deletions src/blockscanner.cpp
Expand Up @@ -67,11 +67,10 @@ VtcBlockIndexer::ScannedBlock VtcBlockIndexer::BlockScanner::scanNextBlock() {
vector<unsigned char> blockHeader(80);
this->blockFileStream.read(reinterpret_cast<char *>(&blockHeader[0]) , 80);

block.blockHash = VtcBlockIndexer::Utility::hashToHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(blockHeader)));
block.blockHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(blockHeader)));
vector<unsigned char> previousBlockHash(32);
memcpy(&previousBlockHash[0], &blockHeader[4], 32);

block.previousBlockHash = VtcBlockIndexer::Utility::hashToHex(previousBlockHash);
block.previousBlockHash = VtcBlockIndexer::Utility::hashToReverseHex(previousBlockHash);

this->blockFileStream.seekg(blockSize - 80, std::ios_base::cur);

Expand Down
17 changes: 4 additions & 13 deletions src/filereader.cpp
Expand Up @@ -60,19 +60,10 @@ uint64_t VtcBlockIndexer::FileReader::readVarInt(ifstream& stream)

}




string VtcBlockIndexer::FileReader::readHash(ifstream& stream) {
unique_ptr<unsigned char> hash(new unsigned char[32]);
stream.read(reinterpret_cast<char *>(&hash.get()[0]) , 32);

stringstream ss;
for(int i = 0; i < 32; i++)
{
ss << hex << setw(2) << setfill('0') << (int)hash.get()[i];
}
return ss.str();
vector<unsigned char> VtcBlockIndexer::FileReader::readHash(ifstream& stream) {
vector<unsigned char> data(32);
stream.read(reinterpret_cast<char *>(&data[0]) , 32);
return data;
}

vector<unsigned char> VtcBlockIndexer::FileReader::readString(ifstream& stream) {
Expand Down
5 changes: 3 additions & 2 deletions src/filereader.h
Expand Up @@ -36,11 +36,12 @@ namespace VtcBlockIndexer {
*/
static uint64_t readVarInt(std::ifstream& stream);

/** Reads a hash (32 bytes) from the ifstream and returns it as hex encoded
/** Reads a hash (32 bytes) from the ifstream and returns it as vector<unsigned char>
* use Utility::hashToHex or ::hashToReverseHex to convert it to hex
*
* @param stream the stream to read from
*/
static std::string readHash(std::ifstream& stream);
static std::vector<unsigned char> readHash(std::ifstream& stream);

/** Reads a string (first a VarInt with the length, then the contents) from
* the ifstream and returns it as vector<char>
Expand Down
9 changes: 9 additions & 0 deletions src/httpserver.h
Expand Up @@ -44,10 +44,19 @@ namespace VtcBlockIndexer {
public:
HttpServer(leveldb::DB* dbInstance);
void run();
/* REST Api for returning the balance of a given address */
void addressBalance( const shared_ptr< Session > session );

/* REST Api for returning the TXOs on a given address */
void addressTxos( const shared_ptr< Session > session );

/* REST Api for returning the transaction details with a given hash */
void getTransaction(const shared_ptr<Session> session);

/* REST Api for returning if a given outpoint is spent (and if so, which TX spends it) */
void outpointSpend( const shared_ptr< Session > session );

/* REST Api for returning if a collection of given outpoints is spent (and if so, which TX spends it) */
void outpointSpends( const shared_ptr< Session > session );

private:
Expand Down

0 comments on commit c2d3471

Please sign in to comment.