github.com/igggame/nebulas-go@v2.1.0+incompatible/nbre/util/bc_generator.cpp (about) 1 // Copyright (C) 2018 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or 6 // modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 // 11 // the go-nebulas library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with the go-nebulas library. If not, see 18 // <http://www.gnu.org/licenses/>. 19 // 20 #include "util/bc_generator.h" 21 #include "crypto/hash.h" 22 #include "fs/bc_storage_session.h" 23 #include "fs/proto/trie.pb.h" 24 #include "util/chrono.h" 25 #include "util/json_parser.h" 26 #include <boost/property_tree/json_parser.hpp> 27 #include <boost/property_tree/ptree.hpp> 28 29 namespace neb { 30 namespace util { 31 void all_accounts::add_account( 32 const std::shared_ptr<corepb::Account> &account) { 33 address_t addr = get_address_from_account(account.get()); 34 m_all_accounts.insert(std::make_pair(addr, std::move(account))); 35 m_all_addresses.push_back(addr); 36 } 37 38 corepb::Account *all_accounts::random_account() const { 39 if (m_all_accounts.empty()) 40 throw std::invalid_argument("no account yet"); 41 std::uniform_int_distribution<uint64_t> dist(0, m_all_accounts.size() - 1); 42 const address_t &addr = m_all_addresses[dist(m_random_generator)]; 43 auto it = m_all_accounts.find(addr); 44 return it->second.get(); 45 } 46 47 uint64_t all_accounts::get_nonce(const address_t &addr) { 48 auto it = m_all_accounts.find(addr); 49 if (it == m_all_accounts.end()) 50 return 0; 51 return it->second->nonce(); 52 } 53 54 void all_accounts::increase_nonce(const address_t &addr) { 55 auto it = m_all_accounts.find(addr); 56 if (it == m_all_accounts.end()) 57 return; 58 59 auto account = it->second; 60 account->set_nonce(account->nonce()); 61 } 62 63 void all_accounts::increase_balance(const address_t &addr, const wei &val) { 64 auto it = m_all_accounts.find(addr); 65 if (it == m_all_accounts.end()) 66 return; 67 68 auto account = it->second; 69 wei_t b = storage_to_wei(string_to_byte(account->balance())); 70 b = b + val.wei_value(); 71 account->set_balance(byte_to_string(wei_to_storage(b))); 72 } 73 74 bool all_accounts::decrease_balance(const address_t &addr, const wei &val) { 75 auto it = m_all_accounts.find(addr); 76 if (it == m_all_accounts.end()) { 77 return false; 78 } 79 80 auto account = it->second; 81 wei_t b = storage_to_wei(string_to_byte(account->balance())); 82 b = b - val.wei_value(); 83 if (b < 0) { 84 return false; 85 } 86 account->set_balance(byte_to_string(wei_to_storage(b))); 87 return true; 88 } 89 90 corepb::Account *all_accounts::random_user_account() const { 91 corepb::Account *account = random_account(); 92 address_t addr = get_address_from_account(account); 93 while (!is_normal_address(addr)) { 94 account = random_account(); 95 addr = get_address_from_account(account); 96 } 97 return account; 98 } 99 100 corepb::Account *all_accounts::random_contract_account() const { 101 corepb::Account *account = random_account(); 102 address_t addr = get_address_from_account(account); 103 uint32_t retry_max_num = m_all_accounts.size(); 104 while (!is_contract_address(addr)) { 105 account = random_account(); 106 addr = get_address_from_account(account); 107 if (--retry_max_num < 0) { 108 return nullptr; 109 } 110 } 111 return account; 112 } 113 114 address_t get_address_from_account(corepb::Account *account) { 115 return to_address(account->address()); 116 } 117 118 generate_block::generate_block(all_accounts *accounts, uint64_t height) 119 : m_all_accounts(accounts), m_height(height) {} 120 121 std::shared_ptr<corepb::Account> 122 generate_block::gen_user_account(const nas &v) { 123 std::shared_ptr<corepb::Account> ret = std::make_shared<corepb::Account>(); 124 address_t addr(NAS_ADDRESS_LEN); 125 addr[0] = NAS_ADDRESS_MAGIC_NUM; 126 addr[1] = NAS_ADDRESS_ACCOUNT_MAGIC_NUM; 127 for (int i = 2; i < NAS_ADDRESS_LEN;) { 128 int32_t num = std::rand(); 129 int32_t *p = (int32_t *)(addr.value() + i); 130 *p = num; 131 i += 4; 132 } 133 ret->set_address(address_to_string(addr)); 134 std::string balance = byte_to_string(wei_to_storage(v.wei_value())); 135 ret->set_balance(balance); 136 m_all_accounts->add_account(ret); 137 return ret; 138 } 139 140 std::shared_ptr<corepb::Account> 141 generate_block::add_deploy_transaction(const address_t &owner, 142 const bytes &payload) { 143 transaction_ptr tx(new corepb::Transaction()); 144 tx->set_to(address_to_string(owner)); 145 tx->set_from(address_to_string(owner)); 146 corepb::Data *data = new corepb::Data(); 147 data->set_type("deploy"); 148 boost::property_tree::ptree pt; 149 pt.put("Data", payload.to_base64()); 150 std::string payload_str; 151 neb::util::json_parser::write_json(payload_str, pt); 152 data->set_payload(payload_str); 153 tx->set_allocated_data(data); 154 tx->set_timestamp(util::now()); 155 tx->set_nonce(m_all_accounts->get_nonce(owner)); 156 wei value(1000_wei); 157 bool b_ret = m_all_accounts->decrease_balance(owner, value); 158 if (b_ret == false) 159 return nullptr; 160 tx->set_value(byte_to_string(wei_to_storage(value.wei_value()))); 161 m_all_accounts->increase_nonce(owner); 162 neb::bytes b(tx->ByteSizeLong()); 163 tx->SerializeToArray(b.value(), b.size()); 164 tx->set_hash(byte_to_string(from_fix_bytes(crypto::sha3_256_hash(b)))); 165 m_transactions.push_back(tx); 166 167 std::shared_ptr<corepb::Account> ret = std::make_shared<corepb::Account>(); 168 address_t addr(NAS_ADDRESS_LEN); 169 addr[0] = NAS_ADDRESS_MAGIC_NUM; 170 addr[1] = NAS_ADDRESS_CONTRACT_MAGIC_NUM; 171 for (int i = 2; i < NAS_ADDRESS_LEN;) { 172 int32_t num = std::rand(); 173 int32_t *p = (int32_t *)(addr.value() + i); 174 *p = num; 175 i += 4; 176 } 177 ret->set_address(address_to_string(addr)); 178 ret->set_birth_place(tx->hash()); 179 m_all_accounts->add_account(ret); 180 181 return ret; 182 } 183 std::shared_ptr<corepb::Transaction> 184 generate_block::add_protocol_transaction(const address_t &owner, 185 const bytes &payload) { 186 transaction_ptr tx(new corepb::Transaction()); 187 tx->set_to(address_to_string(owner)); 188 tx->set_from(address_to_string(owner)); 189 corepb::Data *data = new corepb::Data(); 190 data->set_type("protocol"); 191 boost::property_tree::ptree pt; 192 pt.put("Data", payload.to_base64()); 193 194 std::string payload_str; 195 neb::util::json_parser::write_json(payload_str, pt); 196 data->set_payload(payload_str); 197 wei value(1000_wei); 198 bool ret = m_all_accounts->decrease_balance(owner, value); 199 if (ret == false) 200 return nullptr; 201 tx->set_value(byte_to_string(wei_to_storage(value.wei_value()))); 202 tx->set_allocated_data(data); 203 tx->set_timestamp(util::now()); 204 tx->set_nonce(m_all_accounts->get_nonce(owner)); 205 m_all_accounts->increase_nonce(owner); 206 neb::bytes b(tx->ByteSizeLong()); 207 tx->SerializeToArray(b.value(), b.size()); 208 tx->set_hash(byte_to_string(from_fix_bytes(crypto::sha3_256_hash(b)))); 209 m_transactions.push_back(tx); 210 211 return tx; 212 } 213 std::shared_ptr<corepb::Transaction> 214 generate_block::add_binary_transaction(const address_t &from, 215 const address_t &to, const nas &value) { 216 transaction_ptr tx(new corepb::Transaction()); 217 tx->set_to(address_to_string(to)); 218 tx->set_from(address_to_string(from)); 219 corepb::Data *data = new corepb::Data(); 220 data->set_type("binary"); 221 bool ret = m_all_accounts->decrease_balance(from, wei(value)); 222 if (ret == false) 223 return nullptr; 224 m_all_accounts->increase_balance(to, wei(value)); 225 std::string v = byte_to_string(wei_to_storage(value.wei_value())); 226 tx->set_value(v); 227 tx->set_allocated_data(data); 228 tx->set_timestamp(util::now()); 229 tx->set_nonce(m_all_accounts->get_nonce(from)); 230 m_all_accounts->increase_nonce(from); 231 neb::bytes b(tx->ByteSizeLong()); 232 tx->SerializeToArray(b.value(), b.size()); 233 tx->set_hash(byte_to_string(from_fix_bytes(crypto::sha3_256_hash(b)))); 234 m_transactions.push_back(tx); 235 236 return tx; 237 } 238 239 std::shared_ptr<corepb::Transaction> 240 generate_block::add_call_transaction(const address_t &from, 241 const address_t &to) { 242 transaction_ptr tx(new corepb::Transaction()); 243 tx->set_to(address_to_string(to)); 244 tx->set_from(address_to_string(from)); 245 corepb::Data *data = new corepb::Data(); 246 data->set_type("call"); 247 tx->set_allocated_data(data); 248 tx->set_timestamp(util::now()); 249 wei value(1000_wei); 250 std::string v = byte_to_string(wei_to_storage(value.wei_value())); 251 tx->set_value(v); 252 tx->set_nonce(m_all_accounts->get_nonce(from)); 253 m_all_accounts->increase_nonce(from); 254 neb::bytes b(tx->ByteSizeLong()); 255 tx->SerializeToArray(b.value(), b.size()); 256 tx->set_hash(byte_to_string(from_fix_bytes(crypto::sha3_256_hash(b)))); 257 m_transactions.push_back(tx); 258 259 return tx; 260 } 261 262 void generate_block::write_to_blockchain_db() { 263 // 1. generate corepb::Block 264 std::unique_ptr<corepb::BlockHeader> header = 265 std::make_unique<corepb::BlockHeader>(); 266 header->set_timestamp(util::now()); 267 268 std::unique_ptr<corepb::Block> block = std::make_unique<corepb::Block>(); 269 block->set_allocated_header(header.get()); 270 block->set_height(m_height); 271 for (auto &tx : m_transactions) { 272 corepb::Transaction *etx = block->add_transactions(); 273 *etx = *tx; 274 } 275 std::string block_str = block->SerializeAsString(); 276 header->set_hash( 277 byte_to_string(from_fix_bytes(crypto::sha3_256_hash(block_str)))); 278 279 // 2. update to LIB 280 fs::blockchain::write_LIB_block(block.get()); 281 block->release_header(); 282 283 // 3. write all accounts to DB 284 //! We use triepb::Node to write all accounts to db. 285 //! This is a trick! 286 //! Ideally, we should use trie to shrink db size. 287 //! Yet, we only fill several fields in Account, so it's ok to write dup data. 288 triepb::Node t_accounts; 289 m_all_accounts->for_each_account( 290 [&t_accounts](const std::shared_ptr<corepb::Account> &account) { 291 std::string *s = t_accounts.add_val(); 292 *s = account->SerializeAsString(); 293 }); 294 295 std::string key = std::string("account") + std::to_string(m_height); 296 std::string account_str = t_accounts.SerializeAsString(); 297 fs::bc_storage_session::instance().put_bytes(string_to_byte(key), 298 string_to_byte(account_str)); 299 } 300 301 std::vector<std::shared_ptr<corepb::Account>> 302 generate_block::read_accounts_in_height(block_height_t height) { 303 std::string key = std::string("account") + std::to_string(height); 304 auto account_str = 305 fs::bc_storage_session::instance().get_bytes(string_to_byte(key)); 306 triepb::Node t_accounts; 307 t_accounts.ParseFromArray(account_str.value(), account_str.size()); 308 std::vector<std::shared_ptr<corepb::Account>> ret; 309 for (size_t i = 0; i < t_accounts.val_size(); ++i) { 310 std::string s = t_accounts.val(i); 311 std::shared_ptr<corepb::Account> account = 312 std::make_shared<corepb::Account>(); 313 account->ParseFromString(s); 314 ret.push_back(account); 315 } 316 return ret; 317 } 318 319 std::shared_ptr<corepb::Block> 320 generate_block::read_block_with_height(block_height_t height) { 321 std::unique_ptr<corepb::Block> block = 322 fs::blockchain::load_block_with_height(height); 323 return std::shared_ptr<corepb::Block>(std::move(block)); 324 } 325 326 } // namespace util 327 } // namespace neb