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
|
|
@ -259,6 +259,7 @@ set(FIFT_TEST_SOURCE
|
|||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
||||
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
|
|
@ -306,6 +307,12 @@ if (WINGETOPT_FOUND)
|
|||
target_link_libraries_system(tlbc wingetopt)
|
||||
endif()
|
||||
|
||||
add_executable(pow-miner util/pow-miner.cpp util/Miner.cpp util/Miner.h)
|
||||
target_link_libraries(pow-miner PUBLIC ton_crypto ton_block)
|
||||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(fift wingetopt)
|
||||
endif()
|
||||
|
||||
add_library(ton_block ${BLOCK_SOURCE})
|
||||
target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
|
|
@ -371,6 +378,7 @@ if (NOT CMAKE_CROSSCOMPILING)
|
|||
GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet)
|
||||
GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2)
|
||||
GenFif(DEST smartcont/auto/restricted-wallet3-code SOURCE smartcont/restricted-wallet3-code.fc NAME restricted-wallet3)
|
||||
GenFif(DEST smartcont/auto/pow-testgiver-code SOURCE smartcont/pow-testgiver-code.fc NAME pow-testgiver)
|
||||
|
||||
GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual)
|
||||
GenFif(DEST smartcont/auto/dns-auto-code SOURCE smartcont/dns-auto-code.fc NAME dns-auto)
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@ class AnyIntView {
|
|||
return size() > 1 ? (double)digits[size() - 1] + (double)digits[size() - 2] * (1.0 / Tr::Base)
|
||||
: (double)digits[size() - 1];
|
||||
}
|
||||
bool is_odd_any() const {
|
||||
return size() > 0 && (digits[0] & 1);
|
||||
}
|
||||
word_t to_long_any() const;
|
||||
int parse_hex_any(const char* str, int str_len, int* frac = nullptr);
|
||||
int parse_binary_any(const char* str, int str_len, int* frac = nullptr);
|
||||
|
|
@ -658,6 +661,15 @@ class BigIntG {
|
|||
word_t to_long() const {
|
||||
return as_any_int().to_long_any();
|
||||
}
|
||||
bool is_odd() const {
|
||||
return n > 0 && (digits[0] & 1);
|
||||
}
|
||||
bool is_even() const {
|
||||
return n > 0 && !(digits[0] & 1);
|
||||
}
|
||||
word_t mod_pow2_short(int pow) const {
|
||||
return n > 0 ? digits[0] & ((1 << pow) - 1) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
word_t top_word() const {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/buffer.h"
|
||||
|
||||
#include "bitstring.h"
|
||||
|
|
|
|||
|
|
@ -399,6 +399,18 @@ x{B7B5} @Defop(8u+1) QUFITS
|
|||
x{B7B600} @Defop QFITSX
|
||||
x{B7B601} @Defop QUFITSX
|
||||
|
||||
// advanced integer constants
|
||||
{ 0 { over 1 and 0= } { 1+ swap 2/ swap } while } : pow2decomp
|
||||
{ dup 8 fits { PUSHINT } {
|
||||
dup pow2decomp over 1 = { nip nip PUSHPOW2 } {
|
||||
over -1 = { nip nip PUSHNEGPOW2 } {
|
||||
dup 20 >= { rot drop -rot PUSHINT swap LSHIFT# } {
|
||||
{ drop PUSHINT } {
|
||||
not pow2decomp swap -1 = { nip PUSHPOW2DEC } {
|
||||
drop PUSHINT
|
||||
} cond } cond } cond } cond } cond } cond
|
||||
} dup : PUSHINTX : INTX
|
||||
|
||||
// integer comparison
|
||||
x{B8} @Defop SGN
|
||||
x{B9} @Defop LESS
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "words.h"
|
||||
#include "td/utils/PathView.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/path.h"
|
||||
|
||||
namespace fift {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,9 @@ AsmOp AsmOp::IntConst(td::RefInt256 x) {
|
|||
if (k >= 0) {
|
||||
return AsmOp::Const(k, "PUSHNEGPOW2");
|
||||
}
|
||||
if (!x->mod_pow2_short(23)) {
|
||||
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINTX");
|
||||
}
|
||||
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
|
||||
}
|
||||
|
||||
|
|
|
|||
59
crypto/smartcont/new-pow-testgiver.fif
Normal file
59
crypto/smartcont/new-pow-testgiver.fif
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
{ ."usage: " $0 type ." <workchain-id> <giver-id> <amount> <interval> <min-complexity> <init-complexity> <max-complexity> [<filename-base>]" cr
|
||||
."Creates a new proof-of-work testgiver with unique 32-bit identifier <giver-id> designed to deliver <amount> every <interval> seconds, with SHA256 hash complexity between 2**<min-complexity> and 2**<max-complexity>, with private key saved to or loaded from <filename-base>.pk" cr
|
||||
."('pow-giver.pk' by default)" cr 1 halt
|
||||
} : usage
|
||||
$# 7 - -2 and ' usage if
|
||||
|
||||
8 :$1..n
|
||||
$1 parse-workchain-id =: wc // set workchain id from command line argument
|
||||
$2 parse-int dup =: subwallet-id
|
||||
0= abort"giver-id must be non-zero"
|
||||
$3 $>GR =: amount
|
||||
$4 parse-int dup =: interval
|
||||
dup 24 ufits and 0= abort"invalid interval"
|
||||
$5 parse-int dup =: min-cpl
|
||||
1- 8 ufits not abort"invalid minimal log-complexity (must be 1..256)"
|
||||
$6 parse-int dup =: init-cpl
|
||||
1- 8 ufits not abort"invalid initial log-complexity (must be 1..256)"
|
||||
$7 parse-int dup =: max-cpl
|
||||
1- 8 ufits not abort"invalid maximal log-complexity (must be 1..256)"
|
||||
$8 "pow-giver" replace-if-null =: file-base
|
||||
|
||||
min-cpl init-cpl > abort"initial complexity cannot be below minimal complexity"
|
||||
max-cpl init-cpl < abort"initial complexity cannot exceed maximal complexity"
|
||||
subwallet-id (.) 1 ' $+ does : +subwallet
|
||||
|
||||
."Creating new proof-of-work testgiver in workchain " wc .
|
||||
."with unique giver id " subwallet-id . cr
|
||||
."Designed to give " amount .GR ."approximately every " interval . ."seconds" cr
|
||||
."Complexity (in SHA256 hashes): min=" min-cpl 1<< . ."init=" init-cpl 1<< . ."max=" max-cpl 1<< . cr
|
||||
|
||||
"auto/pow-testgiver-code.fif" include // code
|
||||
{ 256 swap - 8 u, } : cpl,
|
||||
<b 0 32 u, subwallet-id 32 u, // seqno wallet-id
|
||||
file-base +".pk" load-generate-keypair constant wallet_pk B, // pubkey
|
||||
newkeypair nip 16 B| drop B, // seed
|
||||
256 init-cpl - 1<< 256 u, // pow_complexity
|
||||
-1 32 i, // last_success
|
||||
<b amount Gram, interval 32 u, max-cpl cpl, min-cpl cpl, b> ref,
|
||||
b> // data
|
||||
null // no libraries
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hashu wc swap 2dup 2constant wallet_addr
|
||||
."new PoW testgiver address = " 2dup .addr cr
|
||||
2dup file-base +subwallet +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
<b subwallet-id 32 u, -1 32 i, 0 32 u, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hashu wallet_pk ed25519_sign_uint rot
|
||||
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
|
||||
dup ."External message for initialization is " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
file-base +subwallet +"-query.boc" tuck B>file
|
||||
."(Saved proof-of-work testgiver creating query to file " type .")" cr
|
||||
158
crypto/smartcont/pow-testgiver-code.fc
Normal file
158
crypto/smartcont/pow-testgiver-code.fc
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
;; Advanced TestGiver smart contract with Proof-of-Work verification
|
||||
|
||||
int ufits(int x, int bits) impure asm "UFITSX";
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
() check_proof_of_work(slice cs) impure inline_ref {
|
||||
var hash = slice_hash(cs);
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_seqno_sw, public_key, seed, pow_complexity) = (ds~load_uint(64), ds~load_uint(256), ds~load_uint(128), ds~load_uint(256));
|
||||
throw_unless(24, hash < pow_complexity); ;; hash problem NOT solved
|
||||
var (op, flags, expire, whom, rdata1, rseed, rdata2) = (cs~load_uint(32), cs~load_int(8), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256), cs~load_uint(128), cs~load_uint(256));
|
||||
cs.end_parse();
|
||||
ufits(expire - now(), 10);
|
||||
throw_unless(25, (rseed == seed) & (rdata1 == rdata2));
|
||||
;; Proof of Work correct
|
||||
accept_message();
|
||||
randomize_lt();
|
||||
randomize(rdata1);
|
||||
var (last_success, xdata) = (ds~load_uint(32), ds~load_ref());
|
||||
ds.end_parse();
|
||||
ds = xdata.begin_parse();
|
||||
var (amount, target_delta, min_cpl, max_cpl) = (ds~load_grams(), ds~load_uint(32), ds~load_uint(8), ds~load_uint(8));
|
||||
ds.end_parse();
|
||||
;; recompute complexity
|
||||
int delta = now() - last_success;
|
||||
if (delta > 0) {
|
||||
int factor = muldivr(delta, 1 << 128, target_delta);
|
||||
factor = min(max(factor, 7 << 125), 9 << 125); ;; factor must be in range 7/8 .. 9/8
|
||||
pow_complexity = muldivr(pow_complexity, factor, 1 << 128); ;; rescale complexity
|
||||
pow_complexity = min(max(pow_complexity, 1 << min_cpl), 1 << max_cpl);
|
||||
}
|
||||
;; update data
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno_sw, 64)
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(random() >> 128, 128) ;; new seed
|
||||
.store_uint(pow_complexity, 256)
|
||||
.store_uint(now(), 32) ;; new last_success
|
||||
.store_ref(xdata)
|
||||
.end_cell());
|
||||
commit();
|
||||
;; create outbound message
|
||||
send_raw_message(begin_cell()
|
||||
.store_uint(((flags & 1) << 6) | 0x84, 9)
|
||||
.store_int(flags >> 2, 8)
|
||||
.store_uint(whom, 256)
|
||||
.store_grams(amount)
|
||||
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.end_cell(), 3);
|
||||
}
|
||||
|
||||
() rescale_complexity(slice cs) impure inline_ref {
|
||||
var (op, expire) = (cs~load_uint(32), cs~load_uint(32));
|
||||
cs.end_parse();
|
||||
int time = now();
|
||||
throw_unless(28, time > expire);
|
||||
var ds = get_data().begin_parse();
|
||||
var (skipped_data, pow_complexity, last_success, xdata) = (ds~load_bits(64 + 256 + 128), ds~load_uint(256), ds~load_uint(32), ds~load_ref());
|
||||
ds.end_parse();
|
||||
throw_unless(29, expire > last_success);
|
||||
ds = xdata.begin_parse();
|
||||
var (amount, target_delta) = (ds~load_grams(), ds~load_uint(32));
|
||||
int delta = time - last_success;
|
||||
throw_unless(30, delta >= target_delta * 16);
|
||||
accept_message();
|
||||
var (min_cpl, max_cpl) = (ds~load_uint(8), ds~load_uint(8));
|
||||
ds.end_parse();
|
||||
int factor = muldivr(delta, 1 << 128, target_delta);
|
||||
int max_complexity = (1 << max_cpl);
|
||||
int max_factor = muldiv(max_complexity, 1 << 128, pow_complexity);
|
||||
pow_complexity = (max_factor < factor ? max_complexity : muldivr(pow_complexity, factor, 1 << 128));
|
||||
last_success = time - target_delta;
|
||||
set_data(begin_cell()
|
||||
.store_slice(skipped_data)
|
||||
.store_uint(pow_complexity, 256)
|
||||
.store_uint(last_success, 32) ;; new last_success
|
||||
.store_ref(xdata)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
(slice, ()) ~update_params(slice ds, cell pref) inline_ref {
|
||||
var cs = pref.begin_parse();
|
||||
var reset_cpl = cs~load_uint(8);
|
||||
var (seed, pow_complexity, last_success) = (ds~load_uint(128), ds~load_uint(256), ds~load_uint(32));
|
||||
if (reset_cpl) {
|
||||
randomize(seed);
|
||||
pow_complexity = (1 << reset_cpl);
|
||||
seed = (random() >> 128);
|
||||
}
|
||||
var c = begin_cell()
|
||||
.store_uint(seed, 128)
|
||||
.store_uint(pow_complexity, 256)
|
||||
.store_uint(now(), 32)
|
||||
.store_ref(begin_cell().store_slice(cs).end_cell())
|
||||
.end_cell();
|
||||
return (begin_parse(c), ());
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var op = in_msg.preload_uint(32);
|
||||
if (op == 0x4d696e65) {
|
||||
;; Mine = Obtain test grams by presenting valid proof of work
|
||||
return check_proof_of_work(in_msg);
|
||||
}
|
||||
if (op == 0x5253636c) {
|
||||
;; RScl = Rescale complexity if no success for long time
|
||||
return rescale_complexity(in_msg);
|
||||
}
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
|
||||
throw_if(35, valid_until <= now());
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, (subwallet_id == stored_subwallet) | (subwallet_id == 0));
|
||||
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
cs~touch();
|
||||
while (cs.slice_refs()) {
|
||||
var ref = cs~load_ref();
|
||||
var mode = cs~load_uint(8);
|
||||
if (mode < 0xff) {
|
||||
send_raw_message(ref, mode);
|
||||
} else {
|
||||
ds~update_params(ref);
|
||||
}
|
||||
}
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_slice(ds)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
int seqno() method_id {
|
||||
return get_data().begin_parse().preload_uint(32);
|
||||
}
|
||||
|
||||
;; gets (seed, pow_complexity, amount, interval)
|
||||
(int, int, int, int) get_pow_params() method_id {
|
||||
var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
|
||||
var (seed, pow_complexity, xdata) = (ds~load_uint(128), ds~load_uint(256), ds.preload_ref());
|
||||
ds = xdata.begin_parse();
|
||||
return (seed, pow_complexity, ds~load_grams(), ds.preload_uint(32));
|
||||
}
|
||||
|
||||
int get_public_key() method_id {
|
||||
var ds = get_data().begin_parse();
|
||||
ds~load_uint(32 + 32);
|
||||
return ds.preload_uint(256);
|
||||
}
|
||||
|
|
@ -46,8 +46,10 @@ $3 parse-int =: subwallet_id
|
|||
$4 parse-int =: seqno
|
||||
$5 $>cc extra-cc+! extra-currencies @ 2=: amount
|
||||
$6 "wallet-query" replace-if-null =: savefile
|
||||
subwallet_id (.) 1 ' $+ does : +subwallet
|
||||
|
||||
file-base +".addr" load-address
|
||||
file-base +subwallet +".addr" dup file-exists? { drop file-base +".addr" } ifnot
|
||||
load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
|
|
|||
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "PaymentChannel.h"
|
||||
#include "GenericAccount.h"
|
||||
#include "vm/cells.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "vm/cells.h"
|
||||
#include "vm/cellslice.h"
|
||||
|
|
|
|||
|
|
@ -288,7 +288,8 @@ std::string wycheproof_ed25519() {
|
|||
"sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d2020",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},)abcd" R"abcd(
|
||||
},)abcd"
|
||||
R"abcd(
|
||||
{
|
||||
"tcId" : 34,
|
||||
"comment" : "include pk in signature",
|
||||
|
|
@ -570,7 +571,8 @@ std::string wycheproof_ed25519() {
|
|||
"flags" : [
|
||||
"SignatureMalleability"
|
||||
]
|
||||
},)abcd" R"abcd(
|
||||
},)abcd"
|
||||
R"abcd(
|
||||
{
|
||||
"tcId" : 68,
|
||||
"comment" : "checking malleability ",
|
||||
|
|
@ -858,7 +860,8 @@ std::string wycheproof_ed25519() {
|
|||
"flags" : []
|
||||
}
|
||||
]
|
||||
},)abcd" R"abcd(
|
||||
},)abcd"
|
||||
R"abcd(
|
||||
{
|
||||
"key" : {
|
||||
"curve" : "edwards25519",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <iostream>
|
||||
#include <map>
|
||||
#include "vm/cellslice.h"
|
||||
#include <functional>
|
||||
|
||||
namespace tlb {
|
||||
|
||||
|
|
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -23,9 +23,10 @@
|
|||
#include "vm/cells.h"
|
||||
#include "vm/cellslice.h"
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/Slice-decl.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/Slice-decl.h"
|
||||
|
||||
namespace vm {
|
||||
using td::Ref;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
#include "td/utils/Span.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
extern template class td::Cnt<std::string>;
|
||||
extern template class td::Ref<td::Cnt<std::string>>;
|
||||
|
|
@ -186,7 +188,7 @@ class StackEntry {
|
|||
private:
|
||||
static bool is_list(const StackEntry* se);
|
||||
template <typename T, Type tag>
|
||||
Ref<T> dynamic_as() const & {
|
||||
Ref<T> dynamic_as() const& {
|
||||
return tp == tag ? static_cast<Ref<T>>(ref) : td::Ref<T>{};
|
||||
}
|
||||
template <typename T, Type tag>
|
||||
|
|
@ -198,7 +200,7 @@ class StackEntry {
|
|||
return tp == tag ? static_cast<Ref<T>>(std::move(ref)) : td::Ref<T>{};
|
||||
}
|
||||
template <typename T, Type tag>
|
||||
Ref<T> as() const & {
|
||||
Ref<T> as() const& {
|
||||
return tp == tag ? Ref<T>{td::static_cast_ref(), ref} : td::Ref<T>{};
|
||||
}
|
||||
template <typename T, Type tag>
|
||||
|
|
@ -230,31 +232,31 @@ class StackEntry {
|
|||
return ref;
|
||||
}
|
||||
}
|
||||
td::RefInt256 as_int() const & {
|
||||
td::RefInt256 as_int() const& {
|
||||
return as<td::CntInt256, t_int>();
|
||||
}
|
||||
td::RefInt256 as_int() && {
|
||||
return move_as<td::CntInt256, t_int>();
|
||||
}
|
||||
Ref<Cell> as_cell() const & {
|
||||
Ref<Cell> as_cell() const& {
|
||||
return as<Cell, t_cell>();
|
||||
}
|
||||
Ref<Cell> as_cell() && {
|
||||
return move_as<Cell, t_cell>();
|
||||
}
|
||||
Ref<CellBuilder> as_builder() const & {
|
||||
Ref<CellBuilder> as_builder() const& {
|
||||
return as<CellBuilder, t_builder>();
|
||||
}
|
||||
Ref<CellBuilder> as_builder() && {
|
||||
return move_as<CellBuilder, t_builder>();
|
||||
}
|
||||
Ref<CellSlice> as_slice() const & {
|
||||
Ref<CellSlice> as_slice() const& {
|
||||
return as<CellSlice, t_slice>();
|
||||
}
|
||||
Ref<CellSlice> as_slice() && {
|
||||
return move_as<CellSlice, t_slice>();
|
||||
}
|
||||
Ref<Continuation> as_cont() const &;
|
||||
Ref<Continuation> as_cont() const&;
|
||||
Ref<Continuation> as_cont() &&;
|
||||
Ref<Cnt<std::string>> as_string_ref() const {
|
||||
return as<Cnt<std::string>, t_string>();
|
||||
|
|
@ -269,16 +271,16 @@ class StackEntry {
|
|||
std::string as_bytes() const {
|
||||
return tp == t_bytes ? *as_bytes_ref() : "";
|
||||
}
|
||||
Ref<Box> as_box() const &;
|
||||
Ref<Box> as_box() const&;
|
||||
Ref<Box> as_box() &&;
|
||||
Ref<Tuple> as_tuple() const &;
|
||||
Ref<Tuple> as_tuple() const&;
|
||||
Ref<Tuple> as_tuple() &&;
|
||||
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &;
|
||||
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const&;
|
||||
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&;
|
||||
Ref<Atom> as_atom() const &;
|
||||
Ref<Atom> as_atom() const&;
|
||||
Ref<Atom> as_atom() &&;
|
||||
template <class T>
|
||||
Ref<T> as_object() const & {
|
||||
Ref<T> as_object() const& {
|
||||
return dynamic_as<T, t_object>();
|
||||
}
|
||||
template <class T>
|
||||
|
|
@ -444,7 +446,7 @@ class Stack : public td::CntObject {
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
std::vector<StackEntry> extract_contents() const & {
|
||||
std::vector<StackEntry> extract_contents() const& {
|
||||
return stack;
|
||||
}
|
||||
std::vector<StackEntry> extract_contents() && {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue