There is a blockchain
class with minimal functionality inspired by naivechain. It has template data and hash function parameters:
#include "blockchain_block.hpp"
#ifndef BLOCKCHAIN_HPP
#define BLOCKCHAIN_HPP
#include <string>
#include <list>
#include <type_traits>
namespace rzd {
template <
typename T,
typename Hash = std::hash<std::string>
> class blockchain {
public:
struct block;
using hash_type = std::invoke_result_t<Hash, std::string>;
using value_type = block;
using data_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = block*;
using iterator = typename std::list<block>::iterator;
using const_iterator = typename std::list<block>::const_iterator;
private:
std::list<block> chain;
template <typename It>
size_type valid_size(It first, It last) {
if (*first != *begin() || first == last) {
return {};
}
iterator pos{ std::next(begin()) };
difference_type index{ 1 };
for (auto it{ std::next(first) }; it != last; ++it) {
if (it->valid(*std::prev(it))) {
if (*pos == *it) {
++pos;
++index;
}
} else {
return {};
}
}
return index;
}
public:
template <typename It>
iterator replace(It first, It last) {
if (auto index{ valid_size(first, last) },
range{ std::distance(first, last) };
index != 0 && range > size()) {
chain.resize(range);
return std::copy(std::next(first, index), last,
std::next(begin(), index));
}
return begin();
}
inline void push(const value_type& node) {
if (node.valid(*std::prev(end())))
chain.push_back({ node });
}
inline iterator begin() {
return std::begin(chain);
}
inline const_iterator cbegin() const {
return std::cbegin(chain);
}
inline iterator end() {
return std::end(chain);
}
inline const_iterator cend() const {
return std::cend(chain);
}
inline size_type size() const {
return std::size(chain);
}
inline bool operator<(const blockchain& another) {
return std::size(chain) < std::size(another);
}
inline bool operator>(const blockchain& another) {
return std::size(chain) > std::size(another);
}
explicit blockchain(const data_type& data)
: chain{ { { {}, data, {} } } } {}
};
}
#endif
And a block
actually:
#ifndef BLOCKCHAIN_BLOCK_HPP
#define BLOCKCHAIN_BLOCK_HPP
#include "blockchain.hpp"
#include <iostream>
#include <sstream>
#include <chrono>
namespace rzd {
template <
typename T,
typename Hash
> struct blockchain<T, Hash>::block {
using hash_type = typename blockchain::hash_type;
using data_type = typename blockchain::data_type;
using index_type = typename blockchain::size_type;
using hasher = Hash;
using time_type = std::chrono::time_point<std::chrono::system_clock>;
using clock = std::chrono::system_clock;
hasher hash_string;
index_type index;
data_type data;
hash_type previous_hash;
hash_type hash;
time_type timestamp;
hash_type get_hash() const {
std::stringstream ss;
ss << index
<< data
<< previous_hash
<< clock::to_time_t(timestamp);
return hash_string(ss.str());
}
bool valid(const block& previous_block) const {
if (index != previous_block.index + 1) {
std::cerr << "Index "
<< index
<< " does not follow by "
<< previous_block.index
<< std::endl;
return false;
}
if (previous_hash != previous_block.hash) {
std::cerr << "Hash "
<< previous_hash
<< " is not equal to "
<< previous_block.hash
<< std::endl;
return false;
}
if (hash != get_hash()) {
std::cerr << "Hash "
<< hash
<< " is not equal to "
<< get_hash()
<< std::endl;
return false;
}
return true;
}
bool operator==(const block& node) {
return hash == node.hash;
}
bool operator!=(const block& node) {
return hash != node.hash;
}
block(const index_type index,
const data_type& data,
const hash_type& previous_hash)
: index{ index }
, data{ data }
, previous_hash{ previous_hash } {
timestamp = clock::now();
hash = get_hash();
}
block(block& previous_block, const data_type& data)
: block(previous_block.index + 1,
data,
previous_block.hash) {}
block(const block& block) = default;
block() = default;
};
}
#endif
I also wrote some tests for that stuff:
#include "blockchain.hpp"
#include <string>
#include <list>
#include <cassert>
int main() {
rzd::blockchain<std::string> chain{ "foo" };
assert(chain.begin()->data == "foo");
chain.push({ *std::prev(chain.end()), "bar" });
assert(std::next(chain.begin())->data == "bar");
assert(std::size(chain) == 2);
assert(std::next(chain.begin())->valid(*chain.begin()));
assert(std::prev(chain.end())->previous_hash == chain.begin()->hash);
chain.push({ *chain.begin(), "spam" });
assert(std::size(chain) == 2);
std::list<rzd::blockchain<std::string>::block> copy;
copy.push_back(*chain.begin());
copy.push_back(*std::next(chain.begin()));
copy.push_back({ *std::prev(copy.end()), "eggs" });
copy.push_back({ *std::prev(copy.end()), "sause" });
assert(std::prev(copy.end())->valid(*std::prev(copy.end(), 2)));
chain.replace(copy.begin(), copy.end());
assert(std::size(chain) == 4);
assert(std::prev(chain.end())->data == "sause");
rzd::blockchain<std::string> chain2{ "toe" };
assert(chain > chain2);
}
Does it correspond to blockchain principles?
Is there enough methods to call them from Python, for example?
Are there any refactoring possibilities?