C++17. A blockchain implementation in 207 lines of code

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
6
down vote

favorite
2












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?







share|improve this question



















  • 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
















up vote
6
down vote

favorite
2












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?







share|improve this question



















  • 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












up vote
6
down vote

favorite
2









up vote
6
down vote

favorite
2






2





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?







share|improve this question











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?









share|improve this question










share|improve this question




share|improve this question









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
















  • 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















active

oldest

votes











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















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



































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes










 

draft saved


draft discarded


























 


draft saved


draft discarded














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













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation