1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-15 04:32:21 +00:00
ton/crypto/util/pow-miner.cpp
2020-07-06 17:07:20 +03:00

214 lines
6.1 KiB
C++

#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();
}
}