Electroneum
Loading...
Searching...
No Matches
validators.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29
30#include "validators.h"
31
32#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
33#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "Validators"
34
35namespace electroneum {
36 namespace basic {
37 Validator::Validator(const std::string &publicKey, uint64_t startHeight, uint64_t endHeight, string name, string domain, string page_link)
38 : publicKey(publicKey), startHeight(startHeight), endHeight(endHeight), name(name), domain(domain), page_link(page_link) {}
39
40 Validator::Validator() = default;
41
42 list_update_outcome Validators::validate_and_update(electroneum::basic::v_list_struct res, bool saveToDB, bool isEmergencyUpdate) {
43
44 MGINFO("Loading Validator List...");
45 LOG_PRINT_L2("Validator List Struct received: " << store_t_to_json(res));
46
47 std::vector<std::string> testnet_vl_publicKeys = {"443ED0A8172A8A79E606771679F06BD5A9AAFC65F01C8BB94A1A01257393A96E",
48 "9C2F74BE7292BD9DE1DBF15667E48778F3971EEBB85FFCB265B8D03356A5F9C2",
49 "171BE7497D81281C36E62D865BFDA8C86CC76171580CB50FDA4C64C14C184773"};
50
51 std::vector<std::string> mainnet_vl_publicKeys = {"814A92F191735D989FFD3A2A7B33A2EE3ED6AD746B1530AED8E91E3B259DCD4B",
52 "38BBE01388170750FAF8FE9B9C31DF6432987283F49171DA86039566C3288BF9",
53 "8BC9D71CE4CD0DE0D50F45C8619257399B108D35C8CBB72FC29B38DFE3847769"};
54
55 std::vector<std::string> vl_publicKeys = this->testnet ? testnet_vl_publicKeys : mainnet_vl_publicKeys;
56
57 //Check against our hardcoded public-keys to make sure it's a valid message
58 if (res.pubkeys.size() != vl_publicKeys.size()) {
59 LOG_PRINT_L1("Validator list has too few public keys.");
61 }
62
63 if (res.signatures.size() != vl_publicKeys.size()) {
64 LOG_PRINT_L1("Validator list has too few signatures.");
66 }
67
68 //Check against our hardcoded public-keys to make sure it's a valid message
69 if (res.pubkeys != vl_publicKeys) {
70 LOG_PRINT_L1("Validator list has one or more invalid public keys.");
72 }
73
74 //We sign our validator lists with multiple keys for security purposes.
75 for (unsigned int i = 0; i < vl_publicKeys.size(); ++i){
76 if(!crypto::verify_signature(res.blob, unhex(string(vl_publicKeys[i])), unhex(string(res.signatures[i])))){
77 LOG_PRINT_L1("Validator list has an invalid signature and will be ignored.");
79 }
80 }
81
82 LOG_PRINT_L2("Validator List received: " << crypto::base64_decode(res.blob));
83
84 LOG_PRINT_L2("BEFORE");
85 int v_counter = 0;
86 all_of(this->list.begin(), this->list.end(), [&v_counter](std::unique_ptr<Validator> &v) {
87 LOG_PRINT_L2("Validator " << v_counter << " (" << v->getName() << ") :: PublicKey=" << v->getPublicKey() << ",\n FromHeight=" << v->getStartHeight() << ", ToHeight=" << v->getEndHeight());
88 v_counter++;
89 return true;
90 });
91
92 json_obj obj;
94
95 if(obj.list_timestamp < this->current_list_timestamp) {
96
97 LOG_PRINT_L1("Validator list received is older than our local list.");
98
99 if(isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
101 }else {
103 }
104 } else if(obj.list_timestamp == this->current_list_timestamp) {
105
106 this->last_updated = time(nullptr);
107 this->status = ValidatorsState::Valid;
108
109 LOG_PRINT_L1("Validator list received has the same timestamp than our local list.");
110 if(isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
112 }
114 }
115
116 uint8_t anon_v_count = 0;
117 MGINFO_MAGENTA("Public Validators:");
118 for (const auto &v : obj.validators) {
119 this->addOrUpdate(v.validation_public_key, v.valid_from_height, v.valid_to_height, v.name, v.domain, v.page_link);
120
121 if(!v.name.empty()) {
122 MGINFO(v.name << " | Public Key: " << v.validation_public_key);
123 } else {
124 anon_v_count++;
125 }
126 }
127 MGINFO_MAGENTA("Anon Validators: " << to_string(anon_v_count));
128
129 LOG_PRINT_L2("AFTER");
130 v_counter = 0;
131 all_of(this->list.begin(), this->list.end(), [&v_counter](std::unique_ptr<Validator> &v) {
132 LOG_PRINT_L2("Validator " << v_counter << " (" << v->getName() << ") :: PublicKey=" << v->getPublicKey() << ",\n FromHeight=" << v->getStartHeight() << ", ToHeight=" << v->getEndHeight());
133 v_counter++;
134 return true;
135 });
136
137 //Serialize & save valid http response to propagate to p2p upon request
138 this->serialized_v_list = store_t_to_json(res);
139 this->last_updated = time(nullptr);
140 this->current_list_timestamp = obj.list_timestamp;
141 this->status = ValidatorsState::Valid;
142
143 if(saveToDB) {
144 m_db.set_validator_list(this->serialized_v_list, this->last_updated + this->timeout);
145 }
146
147 // Only relay emergency lists within a 5 hour window to prevent p2p spam.
148 // Regular list updates at the endpoint will be time-stamped so that they cannot fall into this timezone
149 // and be propagated by spammers as if they were emergency lists.
150 if (isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
152 }
154 }
155
156 void Validators::add(const string &key, uint64_t startHeight, uint64_t endHeight, string name, string domain, string page_link) {
157 if (!this->exists(key)) this->list.emplace_back(std::unique_ptr<Validator>(new Validator(key, startHeight, endHeight, name, domain, page_link)));
158 }
159
160 void Validators::addOrUpdate(const string &key, uint64_t startHeight, uint64_t endHeight, string name, string domain, string page_link) {
161 this->exists(key) ? this->update(key, endHeight, name, domain, page_link) : this->list.emplace_back(
162 std::unique_ptr<Validator>(new Validator(key, startHeight, endHeight, name, domain, page_link)));
163 }
164
165 std::unique_ptr<Validator> Validators::find(const string &key) {
166 auto it = find_if(this->list.begin(), this->list.end(), [&key](std::unique_ptr<Validator> &v) {
167 return v->getPublicKey() == key;
168 });
169 return std::move(*it);
170 }
171
172 bool Validators::exists(const string &key) {
173 bool found = false;
174 all_of(this->list.begin(), this->list.end(), [&key, &found](std::unique_ptr<Validator> &v) {
175 if (v->getPublicKey() == key) {
176 found = true;
177 return false;
178 }
179 return true;
180 });
181 return found;
182 }
183
184 void Validators::update(const string &key, uint64_t endHeight, string name, string domain, string page_link) {
185 find_if(this->list.begin(), this->list.end(), [&key, &endHeight, &name, &domain, &page_link](std::unique_ptr<Validator> &v) {
186 if (v->getPublicKey() == key) {
187 v->setEndHeight(endHeight);
188 v->setName(name);
189 v->setDomain(domain);
190 v->setPageLink(page_link);
191 return true;
192 }
193 return false;
194 });
195 }
196
197 ValidatorsState Validators::validate_expiration() {
198 if((time(nullptr) - this->last_updated) >= this->timeout && this->status == ValidatorsState::Valid) {
199 this->status = ValidatorsState::NeedsUpdate;
200 }
201
202 if((time(nullptr) - this->last_updated) >= this->timeout + this->timeout_grace_period && this->status == ValidatorsState::NeedsUpdate) {
203 this->status = ValidatorsState::Expired;
204 }
205
206 return this->status;
207 }
208 }
209}
time_t time
const char * res
const char * key
#define LOG_PRINT_L1(x)
#define MGINFO(x)
Definition misc_log_ex.h:80
#define MGINFO_MAGENTA(x)
Definition misc_log_ex.h:85
#define LOG_PRINT_L2(x)
bool verify_signature(const std::string &message, const std::string &publicKey, const std::string &signature)
Definition crypto.h:380
std::string base64_decode(const std::string &val)
Definition crypto.h:392
const char * name
std::string to_string(t_connection_type type)
bool store_t_to_json(t_struct &str_in, std::string &json_buff, size_t indent=0, bool insert_newlines=true)
bool load_t_from_json(t_struct &out, const std::string &json_buff)
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136