6
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ IIRC templates are not binding friendly. Binding might require instantiating it manually. \$\endgroup\$ – Incomputable Apr 17 '18 at 13:30
  • \$\begingroup\$ Is there a standard block chain API you are implementing? If so can you provide a link. \$\endgroup\$ – Martin York Apr 17 '18 at 15:07
  • \$\begingroup\$ @MartinYork I'm implementing something like this github.com/lhartikk/naivechain \$\endgroup\$ – lisovskey Apr 17 '18 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.