C++17. A blockchain implementation in 207 lines of code
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
6
down vote
favorite
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) 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?
c++ c++17 blockchain
add a comment |Â
up vote
6
down vote
favorite
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) 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?
c++ c++17 blockchain
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46
add a comment |Â
up vote
6
down vote
favorite
up vote
6
down vote
favorite
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) 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?
c++ c++17 blockchain
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) 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?
c++ c++17 blockchain
asked Apr 17 at 12:54
lisovskey
1039
1039
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46
add a comment |Â
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f192287%2fc17-a-blockchain-implementation-in-207-lines-of-code%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
IIRC templates are not binding friendly. Binding might require instantiating it manually.
â Incomputable
Apr 17 at 13:30
Is there a standard block chain API you are implementing? If so can you provide a link.
â Martin York
Apr 17 at 15:07
@MartinYork I'm implementing something like this github.com/lhartikk/naivechain
â lisovskey
Apr 17 at 15:46