/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2020 Telegram Systems LLP */ #include "Miner.h" #include "td/utils/Random.h" #include "td/utils/misc.h" #include "td/utils/crypto.h" #include "td/utils/port/Clocks.h" #include namespace ton { #pragma pack(push, 1) struct HData { unsigned char op[4]; signed char flags = -4; unsigned char expire[4] = {}, myaddr[32] = {}, rdata1[32] = {}, pseed[16] = {}, rdata2[32] = {}; void inc() { for (long i = 31; !(rdata1[i] = ++(rdata2[i])); --i) { } } void set_expire(unsigned x) { for (int i = 3; i >= 0; --i) { expire[i] = (x & 0xff); x >>= 8; } } td::Slice as_slice() const { return td::Slice(reinterpret_cast(this), sizeof(*this)); } }; struct HDataEnv { unsigned char d1 = 0, d2 = sizeof(HData) * 2; HData body; td::Slice as_slice() const { return td::Slice(reinterpret_cast(this), sizeof(*this)); } void init(const block::StdAddress& my_address, td::Slice seed) { std::memcpy(body.myaddr, my_address.addr.data(), sizeof(body.myaddr)); body.flags = static_cast(my_address.workchain * 4 + my_address.bounceable); CHECK(seed.size() == 16); std::memcpy(body.pseed, seed.data(), 16); std::memcpy(body.op, "Mine", 4); td::Random::secure_bytes(body.rdata1, 32); std::memcpy(body.rdata2, body.rdata1, 32); } }; static_assert(std::is_trivially_copyable::value, "HDataEnv must be a trivial type"); #pragma pack(pop) td::optional Miner::run(const Options& options) { HDataEnv H; H.init(options.my_address, td::Slice(options.seed.data(), options.seed.size())); td::Slice data = H.as_slice(); CHECK(data.size() == 123); constexpr size_t prefix_size = 72; constexpr size_t guard_pos = prefix_size - (72 - 28); CHECK(0 <= guard_pos && guard_pos < 32); size_t got_prefix_size = (const unsigned char*)H.body.rdata1 + guard_pos + 1 - (const unsigned char*)&H; CHECK(prefix_size == got_prefix_size); auto head = data.substr(0, prefix_size); auto tail = data.substr(prefix_size); SHA256_CTX shactx1, shactx2; std::array hash; SHA256_Init(&shactx1); auto guard = head.back(); td::int64 i = 0, i0 = 0; for (; i < options.max_iterations; i++) { if (!(i & 0xfffff) || head.back() != guard) { if (options.token_) { break; } if (options.hashes_computed) { *options.hashes_computed += i - i0; } i0 = i; if (options.expire_at && options.expire_at.value().is_in_past(td::Timestamp::now())) { break; } H.body.set_expire((unsigned)td::Clocks::system() + 900); guard = head.back(); SHA256_Init(&shactx1); SHA256_Update(&shactx1, head.ubegin(), head.size()); } shactx2 = shactx1; SHA256_Update(&shactx2, tail.ubegin(), tail.size()); SHA256_Final(hash.data(), &shactx2); if (memcmp(hash.data(), options.complexity.data(), 32) < 0) { // FOUND if (options.hashes_computed) { *options.hashes_computed += i - i0; } return H.body.as_slice().str(); } H.body.inc(); } if (options.hashes_computed) { *options.hashes_computed += i - i0; } return {}; } } // namespace ton