mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
pow-testgiver support
This commit is contained in:
parent
dbde9c1c40
commit
f064b1047a
257 changed files with 6665 additions and 2608 deletions
108
crypto/util/Miner.cpp
Normal file
108
crypto/util/Miner.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
#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 <openssl/sha.h>
|
||||
|
||||
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<const td::uint8*>(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<const td::uint8*>(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<td::int8>(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<HDataEnv>::value, "HDataEnv must be a trivial type");
|
||||
#pragma pack(pop)
|
||||
|
||||
td::optional<std::string> 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<td::uint8, 32> 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.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
|
23
crypto/util/Miner.h
Normal file
23
crypto/util/Miner.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "block/block.h"
|
||||
#include "td/utils/optional.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
namespace ton {
|
||||
class Miner {
|
||||
public:
|
||||
struct Options {
|
||||
block::StdAddress my_address;
|
||||
std::array<td::uint8, 16> seed;
|
||||
std::array<td::uint8, 32> complexity;
|
||||
td::optional<td::Timestamp> expire_at;
|
||||
td::int64 max_iterations = std::numeric_limits<td::int64>::max();
|
||||
std::atomic<td::uint64>* hashes_computed{nullptr};
|
||||
};
|
||||
|
||||
static td::optional<std::string> run(const Options& options);
|
||||
};
|
||||
} // namespace ton
|
214
crypto/util/pow-miner.cpp
Normal file
214
crypto/util/pow-miner.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
#include "common/bigint.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "block/block.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "vm/boc.h"
|
||||
#include "openssl/digest.hpp"
|
||||
#include <openssl/sha.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "Miner.h"
|
||||
|
||||
const char* progname;
|
||||
|
||||
int usage() {
|
||||
std::cerr
|
||||
<< "usage: " << progname
|
||||
<< " [-v][-B][-w<threads>] [-t<timeout>] <my-address> <pow-seed> <pow-complexity> <iterations> [<miner-addr> "
|
||||
"<output-ext-msg-boc>]\n"
|
||||
"Outputs a valid <rdata> value for proof-of-work testgiver after computing at most <iterations> hashes "
|
||||
"or terminates with non-zero exit code\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
td::RefInt256 parse_bigint(std::string str, int bits) {
|
||||
int len = (int)str.size();
|
||||
auto num = td::make_refint();
|
||||
auto& x = num.write();
|
||||
if (len >= 3 && str[0] == '0' && str[1] == 'x') {
|
||||
if (x.parse_hex(str.data() + 2, len - 2) != len - 2) {
|
||||
return {};
|
||||
}
|
||||
} else if (!len || x.parse_dec(str.data(), len) != len) {
|
||||
return {};
|
||||
}
|
||||
return x.unsigned_fits_bits(bits) ? std::move(num) : td::RefInt256{};
|
||||
}
|
||||
|
||||
td::RefInt256 parse_bigint_chk(std::string str, int bits) {
|
||||
auto x = parse_bigint(std::move(str), bits);
|
||||
if (x.is_null()) {
|
||||
std::cerr << "fatal: `" << str << "` is not an integer" << std::endl;
|
||||
usage();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void parse_addr(std::string str, block::StdAddress& addr) {
|
||||
if (!addr.parse_addr(str) || (addr.workchain != -1 && addr.workchain != 0)) {
|
||||
std::cerr << "fatal: `" << str.c_str() << "` is not a valid blockchain address" << std::endl;
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
bool make_boc = false;
|
||||
std::string boc_filename;
|
||||
block::StdAddress miner_address;
|
||||
|
||||
int verbosity = 0;
|
||||
std::atomic<td::uint64> hashes_computed{0};
|
||||
td::Timestamp start_at;
|
||||
|
||||
void print_stats() {
|
||||
auto passed = td::Timestamp::now().at() - start_at.at();
|
||||
if (passed < 1e-9) {
|
||||
passed = 1;
|
||||
}
|
||||
std::cerr << "[ hashes computed: " << hashes_computed << " ]" << std::endl;
|
||||
std::cerr << "[ speed: " << static_cast<double>(hashes_computed) / passed << " hps ]" << std::endl;
|
||||
}
|
||||
|
||||
int found(td::Slice data) {
|
||||
for (unsigned i = 0; i < data.size(); i++) {
|
||||
printf("%02X", data.ubegin()[i]);
|
||||
}
|
||||
printf("\n");
|
||||
if (make_boc) {
|
||||
vm::CellBuilder cb;
|
||||
td::Ref<vm::Cell> ext_msg, body;
|
||||
CHECK(cb.store_bytes_bool(data) // body
|
||||
&& cb.finalize_to(body) // -> body
|
||||
&& cb.store_long_bool(0x44, 7) // ext_message_in$10 ...
|
||||
&& cb.store_long_bool(miner_address.workchain, 8) // workchain
|
||||
&& cb.store_bytes_bool(miner_address.addr.as_slice()) // miner addr
|
||||
&& cb.store_long_bool(1, 6) // amount:Grams ...
|
||||
&& cb.store_ref_bool(std::move(body)) // body:^Cell
|
||||
&& cb.finalize_to(ext_msg));
|
||||
auto boc = vm::std_boc_serialize(std::move(ext_msg), 2).move_as_ok();
|
||||
std::cerr << "Saving " << boc.size() << " bytes of serialized external message into file `" << boc_filename << "`"
|
||||
<< std::endl;
|
||||
td::write_file(boc_filename, boc).ensure();
|
||||
}
|
||||
if (verbosity > 0) {
|
||||
print_stats();
|
||||
}
|
||||
std::exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void miner(const ton::Miner::Options& options) {
|
||||
auto res = ton::Miner::run(options);
|
||||
if (res) {
|
||||
found(res.value());
|
||||
}
|
||||
}
|
||||
|
||||
class MinerBench : public td::Benchmark {
|
||||
public:
|
||||
std::string get_description() const override {
|
||||
return "Miner";
|
||||
}
|
||||
|
||||
void run(int n) override {
|
||||
ton::Miner::Options options;
|
||||
options.my_address.parse_addr("EQDU86V5wyPrLd4nQ0RHPcCLPZq_y1O5wFWyTsMw63vjXTOv");
|
||||
std::fill(options.seed.begin(), options.seed.end(), 0xa7);
|
||||
std::fill(options.complexity.begin(), options.complexity.end(), 0);
|
||||
options.max_iterations = n;
|
||||
CHECK(!ton::Miner::run(options));
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
ton::Miner::Options options;
|
||||
|
||||
progname = argv[0];
|
||||
int i, threads = 0;
|
||||
bool bounce = false, benchmark = false;
|
||||
while ((i = getopt(argc, argv, "bnvw:t:Bh")) != -1) {
|
||||
switch (i) {
|
||||
case 'v':
|
||||
++verbosity;
|
||||
break;
|
||||
case 'w':
|
||||
threads = atoi(optarg);
|
||||
CHECK(threads > 0 && threads <= 128);
|
||||
break;
|
||||
case 't': {
|
||||
int timeout = atoi(optarg);
|
||||
CHECK(timeout > 0);
|
||||
options.expire_at = td::Timestamp::in(timeout);
|
||||
break;
|
||||
}
|
||||
case 'B':
|
||||
benchmark = true;
|
||||
break;
|
||||
case 'b':
|
||||
bounce = true;
|
||||
break;
|
||||
case 'n':
|
||||
bounce = false;
|
||||
break;
|
||||
case 'h':
|
||||
return usage();
|
||||
default:
|
||||
std::cerr << "unknown option" << std::endl;
|
||||
return usage();
|
||||
}
|
||||
}
|
||||
if (benchmark && argc == optind) {
|
||||
td::bench(MinerBench());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc != optind + 4 && argc != optind + 6) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
parse_addr(argv[optind], options.my_address);
|
||||
options.my_address.bounceable = bounce;
|
||||
CHECK(parse_bigint_chk(argv[optind + 1], 128)->export_bytes(options.seed.data(), 16, false));
|
||||
|
||||
auto cmplx = parse_bigint_chk(argv[optind + 2], 256);
|
||||
CHECK(cmplx->export_bytes(options.complexity.data(), 32, false));
|
||||
CHECK(!cmplx->unsigned_fits_bits(256 - 62));
|
||||
td::BigInt256 bigpower, hrate;
|
||||
bigpower.set_pow2(256).mod_div(*cmplx, hrate);
|
||||
long long hash_rate = hrate.to_long();
|
||||
options.max_iterations = parse_bigint_chk(argv[optind + 3], 50)->to_long();
|
||||
if (argc == optind + 6) {
|
||||
make_boc = true;
|
||||
parse_addr(argv[optind + 4], miner_address);
|
||||
boc_filename = argv[optind + 5];
|
||||
}
|
||||
|
||||
if (verbosity >= 2) {
|
||||
std::cerr << "[ expected required hashes for success: " << hash_rate << " ]" << std::endl;
|
||||
}
|
||||
if (benchmark) {
|
||||
td::bench(MinerBench());
|
||||
}
|
||||
|
||||
start_at = td::Timestamp::now();
|
||||
|
||||
options.hashes_computed = &hashes_computed;
|
||||
// may invoke several miner threads
|
||||
if (threads <= 0) {
|
||||
miner(options);
|
||||
} else {
|
||||
std::vector<std::thread> T;
|
||||
for (int i = 0; i < threads; i++) {
|
||||
T.emplace_back(miner, options);
|
||||
}
|
||||
for (auto& thr : T) {
|
||||
thr.join();
|
||||
}
|
||||
}
|
||||
if (verbosity > 0) {
|
||||
print_stats();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue