Electroneum
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 
35 namespace 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  this->last_updated = time(nullptr);
98  this->status = ValidatorsState::Valid;
99 
100  LOG_PRINT_L1("Validator list received is older than our local list.");
101 
102  if(isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
104  }else {
106  }
107  } else if(obj.list_timestamp == this->current_list_timestamp) {
108 
109  this->last_updated = time(nullptr);
110  this->status = ValidatorsState::Valid;
111 
112  LOG_PRINT_L1("Validator list received has the same timestamp than our local list.");
113  if(isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
115  }
116  else return list_update_outcome::Same_List;
117  }
118 
119  uint8_t anon_v_count = 0;
120  MGINFO_MAGENTA("Public Validators:");
121  for (const auto &v : obj.validators) {
122  this->addOrUpdate(v.validation_public_key, v.valid_from_height, v.valid_to_height, v.name, v.domain, v.page_link);
123 
124  if(!v.name.empty()) {
125  MGINFO(v.name << " | Public Key: " << v.validation_public_key);
126  } else {
127  anon_v_count++;
128  }
129  }
130  MGINFO_MAGENTA("Anon Validators: " << to_string(anon_v_count));
131 
132  LOG_PRINT_L2("AFTER");
133  v_counter = 0;
134  all_of(this->list.begin(), this->list.end(), [&v_counter](std::unique_ptr<Validator> &v) {
135  LOG_PRINT_L2("Validator " << v_counter << " (" << v->getName() << ") :: PublicKey=" << v->getPublicKey() << ",\n FromHeight=" << v->getStartHeight() << ", ToHeight=" << v->getEndHeight());
136  v_counter++;
137  return true;
138  });
139 
140  //Serialize & save valid http response to propagate to p2p upon request
141  this->serialized_v_list = store_t_to_json(res);
142  this->last_updated = time(nullptr);
143  this->current_list_timestamp = obj.list_timestamp;
144  this->status = ValidatorsState::Valid;
145 
146  if(saveToDB) {
147  m_db.set_validator_list(this->serialized_v_list, this->last_updated + this->timeout);
148  }
149 
150  // Only relay emergency lists within a 5 hour window to prevent p2p spam.
151  // Regular list updates at the endpoint will be time-stamped so that they cannot fall into this timezone
152  // and be propagated by spammers as if they were emergency lists.
153  if (isEmergencyUpdate && (std::time(nullptr) - obj.list_timestamp < 18000)){
155  }
157  }
158 
159  void Validators::add(const string &key, uint64_t startHeight, uint64_t endHeight, string name, string domain, string page_link) {
160  if (!this->exists(key)) this->list.emplace_back(std::unique_ptr<Validator>(new Validator(key, startHeight, endHeight, name, domain, page_link)));
161  }
162 
163  void Validators::addOrUpdate(const string &key, uint64_t startHeight, uint64_t endHeight, string name, string domain, string page_link) {
164  this->exists(key) ? this->update(key, endHeight, name, domain, page_link) : this->list.emplace_back(
165  std::unique_ptr<Validator>(new Validator(key, startHeight, endHeight, name, domain, page_link)));
166  }
167 
168  std::unique_ptr<Validator> Validators::find(const string &key) {
169  auto it = find_if(this->list.begin(), this->list.end(), [&key](std::unique_ptr<Validator> &v) {
170  return v->getPublicKey() == key;
171  });
172  return std::move(*it);
173  }
174 
175  bool Validators::exists(const string &key) {
176  bool found = false;
177  all_of(this->list.begin(), this->list.end(), [&key, &found](std::unique_ptr<Validator> &v) {
178  if (v->getPublicKey() == key) {
179  found = true;
180  return false;
181  }
182  return true;
183  });
184  return found;
185  }
186 
187  void Validators::update(const string &key, uint64_t endHeight, string name, string domain, string page_link) {
188  find_if(this->list.begin(), this->list.end(), [&key, &endHeight, &name, &domain, &page_link](std::unique_ptr<Validator> &v) {
189  if (v->getPublicKey() == key) {
190  v->setEndHeight(endHeight);
191  v->setName(name);
192  v->setDomain(domain);
193  v->setPageLink(page_link);
194  return true;
195  }
196  return false;
197  });
198  }
199 
200  ValidatorsState Validators::validate_expiration() {
201  if((time(nullptr) - this->last_updated) >= this->timeout && this->status == ValidatorsState::Valid) {
202  this->status = ValidatorsState::NeedsUpdate;
203  }
204 
205  if((time(nullptr) - this->last_updated) >= this->timeout + this->timeout_grace_period && this->status == ValidatorsState::NeedsUpdate) {
206  this->status = ValidatorsState::Expired;
207  }
208 
209  return this->status;
210  }
211  }
212 }
const char * res
Definition: hmac_keccak.cpp:41
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
std::string publicKey
#define LOG_PRINT_L1(x)
Definition: misc_log_ex.h:100
::std::string string
Definition: gtest-port.h:1097
const char * key
Definition: hmac_keccak.cpp:39
STL namespace.
unsigned char uint8_t
Definition: stdint.h:124
const char * name
bool store_t_to_json(t_struct &str_in, std::string &json_buff, size_t indent=0, bool insert_newlines=true)
#define MGINFO(x)
Definition: misc_log_ex.h:80
time_t time
Definition: blockchain.cpp:93
bool load_t_from_json(t_struct &out, const std::string &json_buff)
bool verify_signature(const std::string &message, const std::string &publicKey, const std::string &signature)
Definition: crypto.h:380
unsigned __int64 uint64_t
Definition: stdint.h:136
std::string base64_decode(const std::string &val)
Definition: crypto.h:392
const T & move(const T &t)
Definition: gtest-port.h:1317
virtual void set_validator_list(std::string, uint32_t expiration_date)=0
#define MGINFO_MAGENTA(x)
Definition: misc_log_ex.h:85
std::string to_string(t_connection_type type)