├── .gitignore
├── test.sh
├── Makefile
├── js_client_test.html
├── hash.hpp
├── Block.hpp
├── common.hpp
├── requests.hpp
├── BlockChain.hpp
├── README.md
├── main.cpp
├── status_code.hpp
├── license.txt
├── crypto.hpp
├── utility.hpp
├── server_http.hpp
└── client_http.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 | main
3 | *.out
4 | *.o
5 | .DS_Store
6 | **/.DS_Store
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | g++ -std=c++14 -stdlib=libc++ -lssl -lcrypto -Wall -lboost_system main.cpp
3 | ./a.out
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | EXENAME = main
2 | OBJS = main.o
3 |
4 | CXX = g++
5 | CXXFLAGS = -std=c++14 -stdlib=libc++ -lssl -lcrypto -Wall -lboost_system
6 |
7 | main.o: main.cpp
8 | $(CXX) $(CXXFLAGS) main.cpp -o $(OBJS)
9 |
10 | clean:
11 | -rm -f *.o $(EXENAME)
12 |
13 |
--------------------------------------------------------------------------------
/js_client_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BlockChain WebSocket test
6 |
7 |
8 |
21 |
22 |
--------------------------------------------------------------------------------
/hash.hpp:
--------------------------------------------------------------------------------
1 | //author: tko
2 | #ifndef _HASH_H_
3 | #define _HASH_H_
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | using namespace std;
12 |
13 |
14 | string sha256(const string str) {
15 | unsigned char hash[SHA256_DIGEST_LENGTH];
16 | SHA256_CTX sha256;
17 | SHA256_Init(&sha256);
18 | SHA256_Update(&sha256, str.c_str(), str.size());
19 | SHA256_Final(hash, &sha256);
20 |
21 | stringstream ss;
22 | for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
23 | {
24 | ss << hex << setw(2) << setfill('0') << (int)hash[i];
25 | }
26 |
27 | return ss.str();
28 | }
29 |
30 | #endif
--------------------------------------------------------------------------------
/Block.hpp:
--------------------------------------------------------------------------------
1 | //author: tko
2 | #ifndef BLOCK_H
3 | #define BLOCK_H
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "json.hh"
12 | using json = nlohmann::json;
13 |
14 | class Block {
15 | public:
16 | Block(int index, string prevHas, string hash, string nonce, vector data);
17 | string getPreviousHash(void);
18 | string getHash(void);
19 | int getIndex(void);
20 | vector getData(void);
21 |
22 | void toString(void);
23 | json toJSON(void);
24 | private:
25 | int index;
26 | string previousHash;
27 | string blockHash;
28 | string nonce;
29 | vector data;
30 | // string getMerkleRoot(const vector &merkle);
31 | };
32 | // Constructor
33 | Block::Block(int index, string prevHash, string hash, string nonce, vector data ) {
34 | printf("\nInitializing Block: %d ---- Hash: %s \n", index,hash.c_str());
35 | this -> previousHash = prevHash;
36 | this -> data = data;
37 | this -> index = index;
38 | this -> nonce = nonce;
39 | this -> blockHash = hash;
40 |
41 | }
42 |
43 | int Block::getIndex(void) {
44 | return this -> index;
45 | }
46 |
47 | string Block::getPreviousHash(void) {
48 | return this -> previousHash;
49 | }
50 |
51 | string Block::getHash(void) {
52 | return this -> blockHash;
53 | }
54 |
55 | vector Block::getData(void){
56 | return this -> data;
57 | }
58 |
59 | // Prints Block data
60 | void Block::toString(void) {
61 | string dataString;
62 | for (int i=0; i < data.size(); i++)
63 | dataString += data[i] + ", ";
64 | printf("\n-------------------------------\n");
65 | printf("Block %d\nHash: %s\nPrevious Hash: %s\nContents: %s",
66 | index,this->blockHash.c_str(),this->previousHash.c_str(),dataString.c_str());
67 | printf("\n-------------------------------\n");
68 | }
69 |
70 | json Block::toJSON(void) {
71 | json j;
72 | j["index"] = this->index;
73 | j["hash"] = this->blockHash;
74 | j["previousHash"] = this->previousHash;
75 | j["nonce"] = this->nonce;
76 | j["data"] = this->data;
77 | return j;
78 | }
79 |
80 | #endif
--------------------------------------------------------------------------------
/common.hpp:
--------------------------------------------------------------------------------
1 | // author: tko
2 | #ifndef COMMON_H
3 | #define COMMON_H
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | void print_hex(const char *label, const uint8_t *v, size_t len) {
12 | size_t i;
13 |
14 | printf("%s: ", label);
15 | for (i = 0; i < len; ++i) {
16 | printf("%02x", v[i]);
17 | }
18 | printf("\n");
19 | }
20 |
21 | string getMerkleRoot(const vector &merkle) {
22 | printf("\nFinding Merkle Root.... \n");
23 | if (merkle.empty())
24 | return "";
25 | else if (merkle.size() == 1){
26 | return sha256(merkle[0]);
27 | }
28 |
29 | vector new_merkle = merkle;
30 |
31 | while (new_merkle.size() > 1) {
32 | if ( new_merkle.size() % 2 == 1 )
33 | new_merkle.push_back(merkle.back());
34 |
35 | vector result;
36 |
37 | for (int i=0; i < new_merkle.size(); i += 2){
38 | string var1 = sha256(new_merkle[i]);
39 | string var2 = sha256(new_merkle[i+1]);
40 | string hash = sha256(var1+var2);
41 | // printf("---hash(hash(%s), hash(%s)) => %s\n",new_merkle[0].c_str(),new_merkle[1].c_str(),hash.c_str());
42 | result.push_back(hash);
43 | }
44 | new_merkle = result;
45 | }
46 | return new_merkle[0];
47 |
48 | }
49 | pair findHash(int index, string prevHash, vector &merkle) {
50 | string header = to_string(index) + prevHash + getMerkleRoot(merkle);
51 | unsigned int nonce;
52 | for (nonce = 0; nonce < 100000; nonce++ ) {
53 | string blockHash = sha256(header + to_string(nonce));
54 | if (blockHash.substr(0,2) == "00"){
55 | // cout << "nonce: " << nonce;
56 | // cout << "header: " << header;
57 | return make_pair(blockHash,to_string(nonce));
58 | break;
59 | }
60 | }
61 | return make_pair("fail","fail");
62 | }
63 | // int addBlock(int index, string prevHash, vector &merkle, vector > &blockchain) {
64 | // string header = to_string(index) + prevHash + getMerkleRoot(merkle);
65 | // auto pair = findHash(header);
66 |
67 | // blockchain.push_back(std::make_unique(index,prevHash,pair.first,pair.second,merkle));
68 | // return 1;
69 | // }
70 | #endif
--------------------------------------------------------------------------------
/requests.hpp:
--------------------------------------------------------------------------------
1 | //author: tko
2 | #ifndef REQUESTS_H
3 | #define REQUESTS_H
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "json.hh"
13 | using json = nlohmann::json;
14 |
15 | using namespace std;
16 |
17 | #include "client_http.hpp"
18 |
19 | using HttpClient = SimpleWeb::Client;
20 |
21 |
22 | /*
23 | * Client request functions
24 | */
25 | /*
26 | * gets the latest chain from the network and finds the longest one
27 | * used when node initializes
28 | * Returns: JSON object of the blockchain
29 | */
30 | json getChainFromNodes(const vector *listOfNodes){
31 | printf("Pinging nodes for chains....\n");
32 | vector vect;
33 | for ( int a = 0; a < (*listOfNodes).size(); a++ ){
34 | int port = (*listOfNodes)[a];
35 | printf("--- pinging node %d\n",port);
36 | HttpClient client("localhost:"+to_string(port));
37 | try {
38 | auto req = client.request("GET", "/latestchain");
39 | vect.push_back(req->content.string());
40 | }
41 | catch(const SimpleWeb::system_error &e) {
42 | cerr << "Client request error: " << e.what() << endl;
43 | }
44 | }
45 |
46 | //find biggest blockchain
47 | json biggest_bc = json::parse(vect[0]);
48 | int max = 0;
49 | for (int i =0; i < vect.size(); i++) {
50 | auto json_data = json::parse(vect[i]);
51 | if ( max < json_data["length"].get() ){
52 | max = json_data["length"].get();
53 | biggest_bc = json_data;
54 | }
55 | }
56 | return biggest_bc;
57 |
58 | }
59 |
60 | /*
61 | * sends out the new blockchain to network
62 | */
63 | void sendNewChain(const vector *listOfNodes, string json){
64 | printf("Sending new chain to network....\n");
65 | for ( int a = 0; a < (*listOfNodes).size(); a++ ){
66 | int port = (*listOfNodes)[a];
67 | printf("--- sending to node %d\n",port);
68 | HttpClient client("localhost:" + to_string(port));
69 | try {
70 | auto req = client.request("POST", "/newchain",json);
71 | cout << "Node " << port << " Response: " << req->content.string() << endl;
72 | }
73 | catch(const SimpleWeb::system_error &e) {
74 | cerr << "Client request error: " << e.what() << endl;
75 | }
76 | }
77 | }
78 | /*
79 | * adds self to network
80 | * called when node initializes - used so nodes could keep track of nodes in the network
81 | */
82 | void addSelfToNetwork(const vector *listOfNodes,int port) {
83 | printf("Sending port to all nodes\n");
84 | json j;
85 | j["port"] = port;
86 | for ( int a = 0; a < (*listOfNodes).size(); a++ ){
87 | int port = (*listOfNodes)[a];
88 | printf("--- sending port to node %d\n",port);
89 | HttpClient client("localhost:"+to_string(port));
90 | try {
91 | auto req = client.request("POST","/addnode",j.dump(3));
92 | cout << "Node " << port << " Reponse: " << req->content.string() << endl;
93 | }
94 | catch(const SimpleWeb::system_error &e) {
95 | cerr << "Client request error: " << e.what() << endl;
96 | }
97 | }
98 | }
99 |
100 |
101 |
102 |
103 | #endif
--------------------------------------------------------------------------------
/BlockChain.hpp:
--------------------------------------------------------------------------------
1 | //author: tko
2 | #ifndef BLOCKCHAIN_H
3 | #define BLOCKCHAIN_H
4 |
5 | #include
6 | #include
7 | #include "hash.hpp"
8 | #include
9 | #include
10 | #include
11 |
12 | #include "json.hh"
13 | using json = nlohmann::json;
14 |
15 | class BlockChain {
16 | public:
17 | BlockChain(int genesis = 1 );
18 | Block getBlock(int index);
19 | // getBlock(string hash); //not implemented
20 | int getNumOfBlocks(void);
21 | int addBlock(int index, string prevHash, string hash, string nonce, vector &merkle);
22 | string getLatestBlockHash(void);
23 | // void toString(void);
24 | string toJSON(void);
25 | int replaceChain(json chain);
26 | private:
27 | vector > blockchain; //vector that is the blockchain
28 | };
29 |
30 | // If integer passed into constructor is 0, it the first node and creates the genesis block
31 | BlockChain::BlockChain(int genesis ){
32 | if (genesis == 0) {
33 | vector v;
34 | v.push_back("Genesis Block!");
35 | // string header = to_string(0) + string("00000000000000") + getMerkleRoot(v);
36 | auto hash_nonce_pair = findHash(0,string("00000000000000"),v);
37 |
38 | this -> blockchain.push_back(std::make_unique(0,string("00000000000000"),hash_nonce_pair.first,hash_nonce_pair.second,v));
39 | printf("Created blockchain!\n");
40 | }
41 | }
42 | // Gets block based on the index
43 | Block BlockChain::getBlock(int index) {
44 | for ( int i = 0; i getIndex() == index) {
46 | return *(blockchain[i]);
47 | }
48 | }
49 | throw invalid_argument("Index does not exist.");
50 | }
51 |
52 | // returns number of blocks
53 | int BlockChain::getNumOfBlocks(void) {
54 | return this -> blockchain.size();
55 | }
56 |
57 | // checks whether data fits with the right hash -> add block
58 | int BlockChain::addBlock(int index, string prevHash, string hash, string nonce, vector &merkle) {
59 | string header = to_string(index) + prevHash + getMerkleRoot(merkle) + nonce;
60 | if ( (!sha256(header).compare(hash)) && (hash.substr(0,2) == "00" ) && (index == blockchain.size())) {
61 | printf("Block hashes match --- Adding Block %s \n",hash.c_str());
62 | this->blockchain.push_back(std::make_unique(index,prevHash,hash,nonce,merkle));
63 | return 1;
64 | }
65 | cout << "Hash doesn't match criteria\n";
66 | return 0;
67 | }
68 |
69 | // returns hash of the latest block, used for finding the previousHash when mining new block
70 | string BlockChain::getLatestBlockHash(void) {
71 | return this->blockchain[blockchain.size()-1]->getHash();
72 | }
73 |
74 | // returns JSON string of JSON - used to send to network
75 | string BlockChain::toJSON() {
76 | json j;
77 | j["length"] = this->blockchain.size();
78 | for (int i = 0; i < this->blockchain.size(); i++){
79 | j["data"][this->blockchain[i]->getIndex()] = this->blockchain[i]->toJSON();
80 | }
81 | return j.dump(3);
82 | }
83 |
84 | // replaces Chain with new chain represented by a JSON, used when node sends new blockchain
85 | int BlockChain::replaceChain(json chain) {
86 | //remove all blocks except for the first block
87 | while (this->blockchain.size() > 1){
88 | this->blockchain.pop_back();
89 | }
90 | for (int a = 1; a (); a++ ){
91 | auto block = chain["data"][a];
92 | vector data = block["data"].get >();
93 | this->addBlock(block["index"],block["previousHash"],block["hash"],block["nonce"],data);
94 | }
95 | return 1;
96 | }
97 |
98 | #endif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # simple-blockchain
2 |
3 | My implementation of a blockchain in C++ I created for fun :)
4 |
5 | _Note that this was written before knowing more about distributed system concepts. Use at your own discretion._
6 |
7 | Follows some Bitcoin design principles including a peer-to-peer network, SHA-256 to hash headers and blocks, merkle trees, and "mining"(more on that below).
8 |
9 | #### Requirements
10 |
11 | - Uses C++14, OpenSSL library, Simple-Web-Server, and a [JSON library](https://github.com/nlohmann/json)
12 |
13 | Includes a Command line interface that allows you to view blockchains at different indices and add new blocks. You
14 | can do that 20 times until it automatically quits but you can change that. Control-c to quit.
15 |
16 | And unfortunately, everything is stored in memory and is deleted when program quits.
17 |
18 | ## Peer-to-Peer Network
19 |
20 | At first, I used WebSockets but a peer-to-peer system would require setting up a WS server and WS clients for each and every node.
21 |
22 | So instead, I make HTTP requests to connect to nodes to the network
23 |
24 | #### For this to work we need to:
25 |
26 | - Keep track of nodes in the network
27 | - Get the latest chains from every node -- to validate your chain & get up to date when a new node is added to the network
28 | - Send out your chain to the network when a new block is added
29 |
30 | #### Conflicts in different chains
31 |
32 | There can only be one explicit set of blocks in the chain at a given time. If there are conflicts(e.g. when the chains at different nodes have the same size but have different blocks), the longest chain is chosen.
33 |
34 | So if some other node sends in a new chain that's longer than yours, your chain is replaced.
35 |
36 | **Note:** this was a simple implementation and thus, it replaces the entire chain except for the genesis block. For future improvements, each node should check the new chain with other nodes before it is added and entire nodes shouldn't be sent out. I didn't have access to other computers in the same network, so the nodes are connected through different ports inside your computer.
37 |
38 | ## BlockChain Object
39 |
40 | #### Private Variables:
41 |
42 | - blockchain(vector >): vector of smart pointers to Block objects
43 |
44 | Genesis Block is created during intialization.
45 |
46 | #### Validating the integrity of blocks
47 |
48 | Every time you want to add a block to the blockchain, you will need to provide it:
49 |
50 | `int index, string prevHash, string hash, string nonce, vector &merkle`
51 |
52 | - index: index of the block
53 | - prevHash: hash of the previous block
54 | - nonce: self-explantory
55 | - merkle: vector holding in the data of the block
56 |
57 | It will then check whether you have the correct hash(it rehashes the information given), if you have "00" in front, and whether your index is correct.
58 |
59 | Note: this is the only way to add to the blockchain.
60 |
61 | ## Mining for I guess... Proof of Stake
62 |
63 | (I made it very simple because I didn't want to spend much processing power so it's honestly not proof of stake but I just added it in for fun) - use findHash() to get hash and nonce
64 |
65 | first two characters of the hash must be 0
66 |
67 | - e.g. `003d9dc40cad6b414d45555e4b83045cfde74bcee6b09fb42536ca2500087fd9` works
68 |
69 | ## Block Object
70 |
71 | Hash header: index + prevHash + merkleRoot(data) + nonce
72 |
73 | #### Private Variables:
74 |
75 | - index
76 | - Data: vector of strings
77 | - previousHash
78 | - blockHash
79 | - nonce
80 |
81 | For a block to be immutable, its properties are private and there are only methods that return them but not update them.
82 |
83 | ## Common Functions
84 |
85 | #### getMerkleRoot(const vector &merkle)
86 |
87 | - gets merkle root based on elements of a vector
88 |
89 | #### findHash(int index, string prevHash, vector &merkle)
90 |
91 | - "Mining" part
92 | - finds hash and returns a std::pair of the hash found and nonce used to find it
93 |
94 | ### Author
95 |
96 | tk2@illinois.edu
97 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | // author: tko
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "hash.hpp"
10 | #include "Block.hpp"
11 | #include "common.hpp"
12 | #include "BlockChain.hpp"
13 | #include "requests.hpp"
14 |
15 | #include "json.hh"
16 | using json = nlohmann::json;
17 |
18 | using namespace std;
19 |
20 | #include "client_http.hpp"
21 | #include "server_http.hpp"
22 |
23 | using HttpServer = SimpleWeb::Server;
24 | using HttpClient = SimpleWeb::Client;
25 |
26 | /*
27 | Hash header: index + prevHash + merkleRoot(data) + nonce
28 | */
29 |
30 |
31 | /*
32 | * Main function - sets up server, command line interface
33 | */
34 | int main() {
35 | printf("Welcome! To quit-> Control c \n");
36 | HttpServer server;
37 |
38 | // Set up ports
39 |
40 | int port;
41 | printf("Enter port: ");
42 | scanf("%d",&port);
43 | server.config.port = port; //server port
44 |
45 | vector listOfNodes; //vector of the ports of nodes in the network
46 |
47 | // BLOCK CHAIN INITIALIZATION AND ADDING SELF TO NETWORK
48 |
49 | char ch;
50 | printf("Are you the initial Node? (y or n) ");
51 | scanf(" %c",&ch);
52 | BlockChain bc;
53 | if (ch == 'y'){
54 | // Initial Node: setup Blockchain with genesis block
55 | bc = BlockChain(0);
56 | }
57 | else if(ch =='n'){
58 | // New Node - need to add self to network by providing ports
59 | bc = BlockChain(0);
60 | char otherPorts[50];
61 | // Example input: 8000,3000,3030
62 | printf("Enter ports of nodes in network(with commas in between): ");
63 | scanf("%s",otherPorts);
64 | stringstream ss(otherPorts);
65 | int i;
66 | // parse string of nodes and add them to listOfNoes
67 | while (ss >> i)
68 | {
69 | listOfNodes.push_back(i);
70 | if (ss.peek() == ',' || ss.peek() == ' ')
71 | ss.ignore();
72 | }
73 | addSelfToNetwork(&listOfNodes,server.config.port);
74 | json chain = getChainFromNodes(&listOfNodes);
75 | //skips first block - same genesis block across all nodes
76 | for (int a = 1; a (); a++ ){
77 | auto block = chain["data"][a];
78 | vector data = block["data"].get >();
79 | bc.addBlock(block["index"],block["previousHash"],block["hash"],block["nonce"],data);
80 | }
81 | }
82 | else {
83 | return 0;
84 | }
85 |
86 | // SERVER INITIALIZATION
87 |
88 |
89 | /* POST /addnode - used to add node to network, called by new node to all the nodes in the network
90 | * adds node(port) to listOfNodes
91 | */
92 | server.resource["^/addnode$"]["POST"] = [&listOfNodes](shared_ptr response, shared_ptr request) {
93 | printf("POST /addnode --- New Node adding to network....\n");
94 | try {
95 | json content = json::parse(request->content);
96 | int port = content["port"].get();
97 | listOfNodes.push_back(port); // Adds port to listOfNodes
98 | printf("----Adding node %d to listOfNodes\n",port);
99 | response->write("Added You to our List");
100 | }
101 | catch(const exception &e) {
102 | *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what();
103 | }
104 | };
105 |
106 | /* GET /latestchain gets latest blockchain and sends it*/
107 | server.resource["^/latestchain$"]["GET"] = [&bc](shared_ptr response, shared_ptr request) {
108 | printf("GET /latestchain --- Sending BlockChain....\n");
109 | response->write(bc.toJSON());
110 | printf("---Sent current BlockChain\n");
111 | };
112 |
113 | /* POST /newchain called by a node when a new block is added to it -
114 | * checks whether the length of the blockchain is bigger than our own blockchain
115 | * if it is bigger -> replace chain, else don't do anything
116 | */
117 | server.resource["^/newchain$"]["POST"] = [&bc](shared_ptr response, shared_ptr request) {
118 | cout << "POST /newchain --- Node in Network sent new chain\n";
119 | try {
120 | json content = json::parse(request->content);
121 | if (content["length"].get() > bc.getNumOfBlocks()){
122 | bc.replaceChain(content);
123 | cout << "----Replaced current chain with new one" << endl;
124 | response->write("Replaced Chain\n");
125 | }
126 | else {
127 | cout << "----Chain was not replaced: sent chain had same size" <write("Same Chain Size -- invalid");
129 | }
130 | }
131 | catch(const exception &e) {
132 | *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what();
133 | }
134 | };
135 |
136 | // On error lambda function
137 | server.on_error = [](shared_ptr /*request*/, const SimpleWeb::error_code & ec) {
138 | if (ec.message() != "End of file") {
139 | cout << "SERVER ERROR: " << ec.message() << endl;
140 | }
141 | };
142 | printf("Starting server at %d",server.config.port);
143 |
144 | // start server
145 | thread server_thread([&server]() {
146 | server.start();
147 | });
148 |
149 | //COMMAND LINE INTERFACE
150 |
151 | // loop for 20 inputs - can change
152 | for ( int i = 0; i < 20; i++ ) {
153 | vector v;
154 | int temp;
155 | // ask for what to do
156 | printf("\n(1) Look at Blocks \n(2) Add block\n");
157 | int valid = scanf("%d",&temp);
158 |
159 | if ( (valid == 1) && (temp == 1)){ // queue up block if 1
160 | printf("What Block do you want to look at? ");
161 | scanf("%d",&temp);
162 | try {
163 | bc.getBlock(temp).toString();
164 | }
165 | catch (const exception& e){
166 | cout << e.what() << endl;
167 | }
168 | }
169 | else if (temp == 2){ // add a new block if 2
170 | char tmp[201];
171 | printf("\nADDING BLOCKS!\nEnter your message: ");
172 | scanf("%200s",tmp);
173 | string str = tmp;
174 | printf("Entered '%s' into block\n",str.c_str());
175 | v.push_back(str);
176 |
177 | int in;
178 | printf("Press any number to add block to blockchain: ");
179 | scanf("%d",&in);
180 |
181 | try {
182 | if (bc.getNumOfBlocks() == 0) {
183 | printf("----------------------------------\nPlease join the network... Your blockchain doesn't have any blocks ");
184 | continue;
185 | }
186 | // mine for the has
187 | auto pair = findHash(bc.getNumOfBlocks(),bc.getLatestBlockHash(),v);
188 | // add the block to the blockchain
189 | bc.addBlock(bc.getNumOfBlocks(),bc.getLatestBlockHash(),pair.first,pair.second,v );
190 | // send the blockchain to the network
191 | sendNewChain(&listOfNodes,bc.toJSON());
192 | }
193 | catch (const exception& e) {
194 | cout << e.what() << "\n" << endl;
195 | }
196 | }
197 | }
198 |
199 | // bc.addBlock(0,string("00000000000000"),string("003d9dc40cad6b414d45555e4b83045cfde74bcee6b09fb42536ca2500087fd9"),string("46"),v);
200 | printf("\n");
201 | return 0;
202 | }
203 |
204 |
--------------------------------------------------------------------------------
/status_code.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SIMPLE_WEB_STATUS_CODE_HPP
2 | #define SIMPLE_WEB_STATUS_CODE_HPP
3 |
4 | #include
5 | #include
6 |
7 | namespace SimpleWeb {
8 | enum class StatusCode {
9 | unknown = 0,
10 | information_continue = 100,
11 | information_switching_protocols,
12 | information_processing,
13 | success_ok = 200,
14 | success_created,
15 | success_accepted,
16 | success_non_authoritative_information,
17 | success_no_content,
18 | success_reset_content,
19 | success_partial_content,
20 | success_multi_status,
21 | success_already_reported,
22 | success_im_used = 226,
23 | redirection_multiple_choices = 300,
24 | redirection_moved_permanently,
25 | redirection_found,
26 | redirection_see_other,
27 | redirection_not_modified,
28 | redirection_use_proxy,
29 | redirection_switch_proxy,
30 | redirection_temporary_redirect,
31 | redirection_permanent_redirect,
32 | client_error_bad_request = 400,
33 | client_error_unauthorized,
34 | client_error_payment_required,
35 | client_error_forbidden,
36 | client_error_not_found,
37 | client_error_method_not_allowed,
38 | client_error_not_acceptable,
39 | client_error_proxy_authentication_required,
40 | client_error_request_timeout,
41 | client_error_conflict,
42 | client_error_gone,
43 | client_error_length_required,
44 | client_error_precondition_failed,
45 | client_error_payload_too_large,
46 | client_error_uri_too_long,
47 | client_error_unsupported_media_type,
48 | client_error_range_not_satisfiable,
49 | client_error_expectation_failed,
50 | client_error_im_a_teapot,
51 | client_error_misdirection_required = 421,
52 | client_error_unprocessable_entity,
53 | client_error_locked,
54 | client_error_failed_dependency,
55 | client_error_upgrade_required = 426,
56 | client_error_precondition_required = 428,
57 | client_error_too_many_requests,
58 | client_error_request_header_fields_too_large = 431,
59 | client_error_unavailable_for_legal_reasons = 451,
60 | server_error_internal_server_error = 500,
61 | server_error_not_implemented,
62 | server_error_bad_gateway,
63 | server_error_service_unavailable,
64 | server_error_gateway_timeout,
65 | server_error_http_version_not_supported,
66 | server_error_variant_also_negotiates,
67 | server_error_insufficient_storage,
68 | server_error_loop_detected,
69 | server_error_not_extended = 510,
70 | server_error_network_authentication_required
71 | };
72 |
73 | const static std::vector> &status_codes() noexcept {
74 | const static std::vector> status_codes = {
75 | {StatusCode::unknown, ""},
76 | {StatusCode::information_continue, "100 Continue"},
77 | {StatusCode::information_switching_protocols, "101 Switching Protocols"},
78 | {StatusCode::information_processing, "102 Processing"},
79 | {StatusCode::success_ok, "200 OK"},
80 | {StatusCode::success_created, "201 Created"},
81 | {StatusCode::success_accepted, "202 Accepted"},
82 | {StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"},
83 | {StatusCode::success_no_content, "204 No Content"},
84 | {StatusCode::success_reset_content, "205 Reset Content"},
85 | {StatusCode::success_partial_content, "206 Partial Content"},
86 | {StatusCode::success_multi_status, "207 Multi-Status"},
87 | {StatusCode::success_already_reported, "208 Already Reported"},
88 | {StatusCode::success_im_used, "226 IM Used"},
89 | {StatusCode::redirection_multiple_choices, "300 Multiple Choices"},
90 | {StatusCode::redirection_moved_permanently, "301 Moved Permanently"},
91 | {StatusCode::redirection_found, "302 Found"},
92 | {StatusCode::redirection_see_other, "303 See Other"},
93 | {StatusCode::redirection_not_modified, "304 Not Modified"},
94 | {StatusCode::redirection_use_proxy, "305 Use Proxy"},
95 | {StatusCode::redirection_switch_proxy, "306 Switch Proxy"},
96 | {StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"},
97 | {StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"},
98 | {StatusCode::client_error_bad_request, "400 Bad Request"},
99 | {StatusCode::client_error_unauthorized, "401 Unauthorized"},
100 | {StatusCode::client_error_payment_required, "402 Payment Required"},
101 | {StatusCode::client_error_forbidden, "403 Forbidden"},
102 | {StatusCode::client_error_not_found, "404 Not Found"},
103 | {StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"},
104 | {StatusCode::client_error_not_acceptable, "406 Not Acceptable"},
105 | {StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"},
106 | {StatusCode::client_error_request_timeout, "408 Request Timeout"},
107 | {StatusCode::client_error_conflict, "409 Conflict"},
108 | {StatusCode::client_error_gone, "410 Gone"},
109 | {StatusCode::client_error_length_required, "411 Length Required"},
110 | {StatusCode::client_error_precondition_failed, "412 Precondition Failed"},
111 | {StatusCode::client_error_payload_too_large, "413 Payload Too Large"},
112 | {StatusCode::client_error_uri_too_long, "414 URI Too Long"},
113 | {StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"},
114 | {StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"},
115 | {StatusCode::client_error_expectation_failed, "417 Expectation Failed"},
116 | {StatusCode::client_error_im_a_teapot, "418 I'm a teapot"},
117 | {StatusCode::client_error_misdirection_required, "421 Misdirected Request"},
118 | {StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"},
119 | {StatusCode::client_error_locked, "423 Locked"},
120 | {StatusCode::client_error_failed_dependency, "424 Failed Dependency"},
121 | {StatusCode::client_error_upgrade_required, "426 Upgrade Required"},
122 | {StatusCode::client_error_precondition_required, "428 Precondition Required"},
123 | {StatusCode::client_error_too_many_requests, "429 Too Many Requests"},
124 | {StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"},
125 | {StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"},
126 | {StatusCode::server_error_internal_server_error, "500 Internal Server Error"},
127 | {StatusCode::server_error_not_implemented, "501 Not Implemented"},
128 | {StatusCode::server_error_bad_gateway, "502 Bad Gateway"},
129 | {StatusCode::server_error_service_unavailable, "503 Service Unavailable"},
130 | {StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"},
131 | {StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"},
132 | {StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"},
133 | {StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"},
134 | {StatusCode::server_error_loop_detected, "508 Loop Detected"},
135 | {StatusCode::server_error_not_extended, "510 Not Extended"},
136 | {StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}};
137 | return status_codes;
138 | }
139 |
140 | inline StatusCode status_code(const std::string &status_code_str) noexcept {
141 | for(auto &status_code : status_codes()) {
142 | if(status_code.second == status_code_str)
143 | return status_code.first;
144 | }
145 | return StatusCode::unknown;
146 | }
147 |
148 | inline const std::string &status_code(StatusCode status_code_enum) noexcept {
149 | for(auto &status_code : status_codes()) {
150 | if(status_code.first == status_code_enum)
151 | return status_code.second;
152 | }
153 | return status_codes()[0].second;
154 | }
155 | } // namespace SimpleWeb
156 |
157 | #endif // SIMPLE_WEB_STATUS_CODE_HPP
158 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | LICENSE ISSUES
2 | ==============
3 |
4 | The OpenSSL toolkit stays under a double license, i.e. both the conditions of
5 | the OpenSSL License and the original SSLeay license apply to the toolkit.
6 | See below for the actual license texts.
7 |
8 | OpenSSL License
9 | ---------------
10 |
11 | /* ====================================================================
12 | * Copyright (c) 1998-2017 The OpenSSL Project. All rights reserved.
13 | *
14 | * Redistribution and use in source and binary forms, with or without
15 | * modification, are permitted provided that the following conditions
16 | * are met:
17 | *
18 | * 1. Redistributions of source code must retain the above copyright
19 | * notice, this list of conditions and the following disclaimer.
20 | *
21 | * 2. Redistributions in binary form must reproduce the above copyright
22 | * notice, this list of conditions and the following disclaimer in
23 | * the documentation and/or other materials provided with the
24 | * distribution.
25 | *
26 | * 3. All advertising materials mentioning features or use of this
27 | * software must display the following acknowledgment:
28 | * "This product includes software developed by the OpenSSL Project
29 | * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
30 | *
31 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
32 | * endorse or promote products derived from this software without
33 | * prior written permission. For written permission, please contact
34 | * openssl-core@openssl.org.
35 | *
36 | * 5. Products derived from this software may not be called "OpenSSL"
37 | * nor may "OpenSSL" appear in their names without prior written
38 | * permission of the OpenSSL Project.
39 | *
40 | * 6. Redistributions of any form whatsoever must retain the following
41 | * acknowledgment:
42 | * "This product includes software developed by the OpenSSL Project
43 | * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
44 | *
45 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
46 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
49 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
51 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
52 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
54 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
56 | * OF THE POSSIBILITY OF SUCH DAMAGE.
57 | * ====================================================================
58 | *
59 | * This product includes cryptographic software written by Eric Young
60 | * (eay@cryptsoft.com). This product includes software written by Tim
61 | * Hudson (tjh@cryptsoft.com).
62 | *
63 | */
64 |
65 | Original SSLeay License
66 | -----------------------
67 |
68 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
69 | * All rights reserved.
70 | *
71 | * This package is an SSL implementation written
72 | * by Eric Young (eay@cryptsoft.com).
73 | * The implementation was written so as to conform with Netscapes SSL.
74 | *
75 | * This library is free for commercial and non-commercial use as long as
76 | * the following conditions are aheared to. The following conditions
77 | * apply to all code found in this distribution, be it the RC4, RSA,
78 | * lhash, DES, etc., code; not just the SSL code. The SSL documentation
79 | * included with this distribution is covered by the same copyright terms
80 | * except that the holder is Tim Hudson (tjh@cryptsoft.com).
81 | *
82 | * Copyright remains Eric Young's, and as such any Copyright notices in
83 | * the code are not to be removed.
84 | * If this package is used in a product, Eric Young should be given attribution
85 | * as the author of the parts of the library used.
86 | * This can be in the form of a textual message at program startup or
87 | * in documentation (online or textual) provided with the package.
88 | *
89 | * Redistribution and use in source and binary forms, with or without
90 | * modification, are permitted provided that the following conditions
91 | * are met:
92 | * 1. Redistributions of source code must retain the copyright
93 | * notice, this list of conditions and the following disclaimer.
94 | * 2. Redistributions in binary form must reproduce the above copyright
95 | * notice, this list of conditions and the following disclaimer in the
96 | * documentation and/or other materials provided with the distribution.
97 | * 3. All advertising materials mentioning features or use of this software
98 | * must display the following acknowledgement:
99 | * "This product includes cryptographic software written by
100 | * Eric Young (eay@cryptsoft.com)"
101 | * The word 'cryptographic' can be left out if the rouines from the library
102 | * being used are not cryptographic related :-).
103 | * 4. If you include any Windows specific code (or a derivative thereof) from
104 | * the apps directory (application code) you must include an acknowledgement:
105 | * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
106 | *
107 | * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
108 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
109 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
110 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
111 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
112 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
113 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
114 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
115 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
116 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
117 | * SUCH DAMAGE.
118 | *
119 | * The licence and distribution terms for any publically available version or
120 | * derivative of this code cannot be changed. i.e. this code cannot simply be
121 | * copied and put under another distribution licence
122 | * [including the GNU Public Licence.]
123 | */
124 |
125 | The MIT License (MIT)
126 |
127 | Copyright (c) 2014-2016 Ole Christian Eidheim
128 |
129 | Permission is hereby granted, free of charge, to any person obtaining a copy
130 | of this software and associated documentation files (the "Software"), to deal
131 | in the Software without restriction, including without limitation the rights
132 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
133 | copies of the Software, and to permit persons to whom the Software is
134 | furnished to do so, subject to the following conditions:
135 |
136 | The above copyright notice and this permission notice shall be included in all
137 | copies or substantial portions of the Software.
138 |
139 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
142 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
143 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
144 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
145 | SOFTWARE.
146 |
147 | MIT License
148 |
149 | Copyright (c) 2013-2017 Niels Lohmann
150 |
151 | Permission is hereby granted, free of charge, to any person obtaining a copy
152 | of this software and associated documentation files (the "Software"), to deal
153 | in the Software without restriction, including without limitation the rights
154 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
155 | copies of the Software, and to permit persons to whom the Software is
156 | furnished to do so, subject to the following conditions:
157 |
158 | The above copyright notice and this permission notice shall be included in all
159 | copies or substantial portions of the Software.
160 |
161 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
163 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
164 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
165 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
166 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
167 | SOFTWARE.
--------------------------------------------------------------------------------
/crypto.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SIMPLE_WEB_CRYPTO_HPP
2 | #define SIMPLE_WEB_CRYPTO_HPP
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | namespace SimpleWeb {
17 | // TODO 2017: remove workaround for MSVS 2012
18 | #if _MSC_VER == 1700 // MSVS 2012 has no definition for round()
19 | inline double round(double x) noexcept { // Custom definition of round() for positive numbers
20 | return floor(x + 0.5);
21 | }
22 | #endif
23 |
24 | class Crypto {
25 | const static size_t buffer_size = 131072;
26 |
27 | public:
28 | class Base64 {
29 | public:
30 | static std::string encode(const std::string &ascii) noexcept {
31 | std::string base64;
32 |
33 | BIO *bio, *b64;
34 | BUF_MEM *bptr = BUF_MEM_new();
35 |
36 | b64 = BIO_new(BIO_f_base64());
37 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
38 | bio = BIO_new(BIO_s_mem());
39 | BIO_push(b64, bio);
40 | BIO_set_mem_buf(b64, bptr, BIO_CLOSE);
41 |
42 | // Write directly to base64-buffer to avoid copy
43 | auto base64_length = static_cast(round(4 * ceil(static_cast(ascii.size()) / 3.0)));
44 | base64.resize(base64_length);
45 | bptr->length = 0;
46 | bptr->max = base64_length + 1;
47 | bptr->data = &base64[0];
48 |
49 | if(BIO_write(b64, &ascii[0], static_cast(ascii.size())) <= 0 || BIO_flush(b64) <= 0)
50 | base64.clear();
51 |
52 | // To keep &base64[0] through BIO_free_all(b64)
53 | bptr->length = 0;
54 | bptr->max = 0;
55 | bptr->data = nullptr;
56 |
57 | BIO_free_all(b64);
58 |
59 | return base64;
60 | }
61 |
62 | static std::string decode(const std::string &base64) noexcept {
63 | std::string ascii;
64 |
65 | // Resize ascii, however, the size is a up to two bytes too large.
66 | ascii.resize((6 * base64.size()) / 8);
67 | BIO *b64, *bio;
68 |
69 | b64 = BIO_new(BIO_f_base64());
70 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
71 | bio = BIO_new_mem_buf(&base64[0], static_cast(base64.size()));
72 | bio = BIO_push(b64, bio);
73 |
74 | auto decoded_length = BIO_read(bio, &ascii[0], static_cast(ascii.size()));
75 | if(decoded_length > 0)
76 | ascii.resize(static_cast(decoded_length));
77 | else
78 | ascii.clear();
79 |
80 | BIO_free_all(b64);
81 |
82 | return ascii;
83 | }
84 | };
85 |
86 | /// Return hex string from bytes in input string.
87 | static std::string to_hex_string(const std::string &input) noexcept {
88 | std::stringstream hex_stream;
89 | hex_stream << std::hex << std::internal << std::setfill('0');
90 | for(auto &byte : input)
91 | hex_stream << std::setw(2) << static_cast(static_cast(byte));
92 | return hex_stream.str();
93 | }
94 |
95 | static std::string md5(const std::string &input, size_t iterations = 1) noexcept {
96 | std::string hash;
97 |
98 | hash.resize(128 / 8);
99 | MD5(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0]));
100 |
101 | for(size_t c = 1; c < iterations; ++c)
102 | MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
103 |
104 | return hash;
105 | }
106 |
107 | static std::string md5(std::istream &stream, size_t iterations = 1) noexcept {
108 | MD5_CTX context;
109 | MD5_Init(&context);
110 | std::streamsize read_length;
111 | std::vector buffer(buffer_size);
112 | while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
113 | MD5_Update(&context, buffer.data(), static_cast(read_length));
114 | std::string hash;
115 | hash.resize(128 / 8);
116 | MD5_Final(reinterpret_cast(&hash[0]), &context);
117 |
118 | for(size_t c = 1; c < iterations; ++c)
119 | MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
120 |
121 | return hash;
122 | }
123 |
124 | static std::string sha1(const std::string &input, size_t iterations = 1) noexcept {
125 | std::string hash;
126 |
127 | hash.resize(160 / 8);
128 | SHA1(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0]));
129 |
130 | for(size_t c = 1; c < iterations; ++c)
131 | SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
132 |
133 | return hash;
134 | }
135 |
136 | static std::string sha1(std::istream &stream, size_t iterations = 1) noexcept {
137 | SHA_CTX context;
138 | SHA1_Init(&context);
139 | std::streamsize read_length;
140 | std::vector buffer(buffer_size);
141 | while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
142 | SHA1_Update(&context, buffer.data(), static_cast(read_length));
143 | std::string hash;
144 | hash.resize(160 / 8);
145 | SHA1_Final(reinterpret_cast(&hash[0]), &context);
146 |
147 | for(size_t c = 1; c < iterations; ++c)
148 | SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
149 |
150 | return hash;
151 | }
152 |
153 | static std::string sha256(const std::string &input, size_t iterations = 1) noexcept {
154 | std::string hash;
155 |
156 | hash.resize(256 / 8);
157 | SHA256(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0]));
158 |
159 | for(size_t c = 1; c < iterations; ++c)
160 | SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
161 |
162 | return hash;
163 | }
164 |
165 | static std::string sha256(std::istream &stream, size_t iterations = 1) noexcept {
166 | SHA256_CTX context;
167 | SHA256_Init(&context);
168 | std::streamsize read_length;
169 | std::vector buffer(buffer_size);
170 | while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
171 | SHA256_Update(&context, buffer.data(), static_cast(read_length));
172 | std::string hash;
173 | hash.resize(256 / 8);
174 | SHA256_Final(reinterpret_cast(&hash[0]), &context);
175 |
176 | for(size_t c = 1; c < iterations; ++c)
177 | SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
178 |
179 | return hash;
180 | }
181 |
182 | static std::string sha512(const std::string &input, size_t iterations = 1) noexcept {
183 | std::string hash;
184 |
185 | hash.resize(512 / 8);
186 | SHA512(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0]));
187 |
188 | for(size_t c = 1; c < iterations; ++c)
189 | SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
190 |
191 | return hash;
192 | }
193 |
194 | static std::string sha512(std::istream &stream, size_t iterations = 1) noexcept {
195 | SHA512_CTX context;
196 | SHA512_Init(&context);
197 | std::streamsize read_length;
198 | std::vector buffer(buffer_size);
199 | while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0)
200 | SHA512_Update(&context, buffer.data(), static_cast(read_length));
201 | std::string hash;
202 | hash.resize(512 / 8);
203 | SHA512_Final(reinterpret_cast(&hash[0]), &context);
204 |
205 | for(size_t c = 1; c < iterations; ++c)
206 | SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0]));
207 |
208 | return hash;
209 | }
210 |
211 | /// key_size is number of bytes of the returned key.
212 | static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept {
213 | std::string key;
214 | key.resize(static_cast(key_size));
215 | PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(),
216 | reinterpret_cast(salt.c_str()), salt.size(), iterations,
217 | key_size, reinterpret_cast(&key[0]));
218 | return key;
219 | }
220 | };
221 | }
222 | #endif /* SIMPLE_WEB_CRYPTO_HPP */
223 |
--------------------------------------------------------------------------------
/utility.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SIMPLE_WEB_UTILITY_HPP
2 | #define SIMPLE_WEB_UTILITY_HPP
3 |
4 | #include "status_code.hpp"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace SimpleWeb {
12 | inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept {
13 | return str1.size() == str2.size() &&
14 | std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
15 | return tolower(a) == tolower(b);
16 | });
17 | }
18 | class CaseInsensitiveEqual {
19 | public:
20 | bool operator()(const std::string &str1, const std::string &str2) const noexcept {
21 | return case_insensitive_equal(str1, str2);
22 | }
23 | };
24 | // Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
25 | class CaseInsensitiveHash {
26 | public:
27 | std::size_t operator()(const std::string &str) const noexcept {
28 | std::size_t h = 0;
29 | std::hash hash;
30 | for(auto c : str)
31 | h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
32 | return h;
33 | }
34 | };
35 |
36 | using CaseInsensitiveMultimap = std::unordered_multimap;
37 |
38 | /// Percent encoding and decoding
39 | class Percent {
40 | public:
41 | /// Returns percent-encoded string
42 | static std::string encode(const std::string &value) noexcept {
43 | static auto hex_chars = "0123456789ABCDEF";
44 |
45 | std::string result;
46 | result.reserve(value.size()); // Minimum size of result
47 |
48 | for(auto &chr : value) {
49 | if(chr == ' ')
50 | result += '+';
51 | else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']')
52 | result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
53 | else
54 | result += chr;
55 | }
56 |
57 | return result;
58 | }
59 |
60 | /// Returns percent-decoded string
61 | static std::string decode(const std::string &value) noexcept {
62 | std::string result;
63 | result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result
64 |
65 | for(std::size_t i = 0; i < value.size(); ++i) {
66 | auto &chr = value[i];
67 | if(chr == '%' && i + 2 < value.size()) {
68 | auto hex = value.substr(i + 1, 2);
69 | auto decoded_chr = static_cast(std::strtol(hex.c_str(), nullptr, 16));
70 | result += decoded_chr;
71 | i += 2;
72 | }
73 | else if(chr == '+')
74 | result += ' ';
75 | else
76 | result += chr;
77 | }
78 |
79 | return result;
80 | }
81 | };
82 |
83 | /// Query string creation and parsing
84 | class QueryString {
85 | public:
86 | /// Returns query string created from given field names and values
87 | static std::string create(const CaseInsensitiveMultimap &fields) noexcept {
88 | std::string result;
89 |
90 | bool first = true;
91 | for(auto &field : fields) {
92 | result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second);
93 | first = false;
94 | }
95 |
96 | return result;
97 | }
98 |
99 | /// Returns query keys with percent-decoded values.
100 | static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept {
101 | CaseInsensitiveMultimap result;
102 |
103 | if(query_string.empty())
104 | return result;
105 |
106 | std::size_t name_pos = 0;
107 | auto name_end_pos = std::string::npos;
108 | auto value_pos = std::string::npos;
109 | for(std::size_t c = 0; c < query_string.size(); ++c) {
110 | if(query_string[c] == '&') {
111 | auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
112 | if(!name.empty()) {
113 | auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
114 | result.emplace(std::move(name), Percent::decode(value));
115 | }
116 | name_pos = c + 1;
117 | name_end_pos = std::string::npos;
118 | value_pos = std::string::npos;
119 | }
120 | else if(query_string[c] == '=') {
121 | name_end_pos = c;
122 | value_pos = c + 1;
123 | }
124 | }
125 | if(name_pos < query_string.size()) {
126 | auto name = query_string.substr(name_pos, name_end_pos - name_pos);
127 | if(!name.empty()) {
128 | auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
129 | result.emplace(std::move(name), Percent::decode(value));
130 | }
131 | }
132 |
133 | return result;
134 | }
135 | };
136 |
137 | class HttpHeader {
138 | public:
139 | /// Parse header fields
140 | static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
141 | CaseInsensitiveMultimap result;
142 | std::string line;
143 | getline(stream, line);
144 | std::size_t param_end;
145 | while((param_end = line.find(':')) != std::string::npos) {
146 | std::size_t value_start = param_end + 1;
147 | if(value_start < line.size()) {
148 | if(line[value_start] == ' ')
149 | value_start++;
150 | if(value_start < line.size())
151 | result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
152 | }
153 |
154 | getline(stream, line);
155 | }
156 | return result;
157 | }
158 | };
159 |
160 | class RequestMessage {
161 | public:
162 | /// Parse request line and header fields
163 | static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
164 | header.clear();
165 | std::string line;
166 | getline(stream, line);
167 | std::size_t method_end;
168 | if((method_end = line.find(' ')) != std::string::npos) {
169 | method = line.substr(0, method_end);
170 |
171 | std::size_t query_start = std::string::npos;
172 | std::size_t path_and_query_string_end = std::string::npos;
173 | for(std::size_t i = method_end + 1; i < line.size(); ++i) {
174 | if(line[i] == '?' && (i + 1) < line.size())
175 | query_start = i + 1;
176 | else if(line[i] == ' ') {
177 | path_and_query_string_end = i;
178 | break;
179 | }
180 | }
181 | if(path_and_query_string_end != std::string::npos) {
182 | if(query_start != std::string::npos) {
183 | path = line.substr(method_end + 1, query_start - method_end - 2);
184 | query_string = line.substr(query_start, path_and_query_string_end - query_start);
185 | }
186 | else
187 | path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
188 |
189 | std::size_t protocol_end;
190 | if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) {
191 | if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0)
192 | return false;
193 | version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
194 | }
195 | else
196 | return false;
197 |
198 | header = HttpHeader::parse(stream);
199 | }
200 | else
201 | return false;
202 | }
203 | else
204 | return false;
205 | return true;
206 | }
207 | };
208 |
209 | class ResponseMessage {
210 | public:
211 | /// Parse status line and header fields
212 | static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
213 | header.clear();
214 | std::string line;
215 | getline(stream, line);
216 | std::size_t version_end = line.find(' ');
217 | if(version_end != std::string::npos) {
218 | if(5 < line.size())
219 | version = line.substr(5, version_end - 5);
220 | else
221 | return false;
222 | if((version_end + 1) < line.size())
223 | status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
224 | else
225 | return false;
226 |
227 | header = HttpHeader::parse(stream);
228 | }
229 | else
230 | return false;
231 | return true;
232 | }
233 | };
234 |
235 | class ContentDisposition {
236 | public:
237 | /// Can be used to parse the Content-Disposition header field value when
238 | /// clients are posting requests with enctype="multipart/form-data"
239 | static CaseInsensitiveMultimap parse(const std::string &line) {
240 | CaseInsensitiveMultimap result;
241 |
242 | std::size_t para_start_pos = 0;
243 | std::size_t para_end_pos = std::string::npos;
244 | std::size_t value_start_pos = std::string::npos;
245 | for(std::size_t c = 0; c < line.size(); ++c) {
246 | if(para_start_pos != std::string::npos) {
247 | if(para_end_pos == std::string::npos) {
248 | if(line[c] == ';') {
249 | result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
250 | para_start_pos = std::string::npos;
251 | }
252 | else if(line[c] == '=')
253 | para_end_pos = c;
254 | }
255 | else {
256 | if(value_start_pos == std::string::npos) {
257 | if(line[c] == '"' && c + 1 < line.size())
258 | value_start_pos = c + 1;
259 | }
260 | else if(line[c] == '"') {
261 | result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
262 | para_start_pos = std::string::npos;
263 | para_end_pos = std::string::npos;
264 | value_start_pos = std::string::npos;
265 | }
266 | }
267 | }
268 | else if(line[c] != ' ' && line[c] != ';')
269 | para_start_pos = c;
270 | }
271 | if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
272 | result.emplace(line.substr(para_start_pos), std::string());
273 |
274 | return result;
275 | }
276 | };
277 | } // namespace SimpleWeb
278 |
279 | #ifdef __SSE2__
280 | #include
281 | namespace SimpleWeb {
282 | inline void spin_loop_pause() noexcept { _mm_pause(); }
283 | } // namespace SimpleWeb
284 | // TODO: need verification that the following checks are correct:
285 | #elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
286 | #include
287 | namespace SimpleWeb {
288 | inline void spin_loop_pause() noexcept { _mm_pause(); }
289 | } // namespace SimpleWeb
290 | #else
291 | namespace SimpleWeb {
292 | inline void spin_loop_pause() noexcept {}
293 | } // namespace SimpleWeb
294 | #endif
295 |
296 | namespace SimpleWeb {
297 | /// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service
298 | class ScopeRunner {
299 | /// Scope count that is set to -1 if scopes are to be canceled
300 | std::atomic count;
301 |
302 | public:
303 | class SharedLock {
304 | friend class ScopeRunner;
305 | std::atomic &count;
306 | SharedLock(std::atomic &count) noexcept : count(count) {}
307 | SharedLock &operator=(const SharedLock &) = delete;
308 | SharedLock(const SharedLock &) = delete;
309 |
310 | public:
311 | ~SharedLock() noexcept {
312 | count.fetch_sub(1);
313 | }
314 | };
315 |
316 | ScopeRunner() noexcept : count(0) {}
317 |
318 | /// Returns nullptr if scope should be exited, or a shared lock otherwise
319 | std::unique_ptr continue_lock() noexcept {
320 | long expected = count;
321 | while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
322 | spin_loop_pause();
323 |
324 | if(expected < 0)
325 | return nullptr;
326 | else
327 | return std::unique_ptr(new SharedLock(count));
328 | }
329 |
330 | /// Blocks until all shared locks are released, then prevents future shared locks
331 | void stop() noexcept {
332 | long expected = 0;
333 | while(!count.compare_exchange_weak(expected, -1)) {
334 | if(expected < 0)
335 | return;
336 | expected = 0;
337 | spin_loop_pause();
338 | }
339 | }
340 | };
341 | } // namespace SimpleWeb
342 |
343 | #endif // SIMPLE_WEB_UTILITY_HPP
344 |
--------------------------------------------------------------------------------
/server_http.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SERVER_HTTP_HPP
2 | #define SERVER_HTTP_HPP
3 |
4 | #include "utility.hpp"
5 | #include
6 | #include
7 | #include
8 | #include