mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
updated smartcontract code
updated lite-client and configuration smartcontract updated tonlib code
This commit is contained in:
parent
8e5bd938aa
commit
bce33f588a
46 changed files with 677 additions and 299 deletions
|
@ -24,10 +24,61 @@
|
|||
#include "common/util.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/tl_storers.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace block {
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
td::Result<PublicKey> PublicKey::from_bytes(td::Slice key) {
|
||||
if (key.size() != 32) {
|
||||
return td::Status::Error("Ed25519 public key must be exactly 32 bytes long");
|
||||
}
|
||||
PublicKey res;
|
||||
res.key = key.str();
|
||||
return res;
|
||||
}
|
||||
|
||||
td::Result<PublicKey> PublicKey::parse(td::Slice key) {
|
||||
if (key.size() != 48) {
|
||||
return td::Status::Error("Serialized Ed25519 public key must be exactly 48 characters long");
|
||||
}
|
||||
td::uint8 buf[36];
|
||||
if (!buff_base64_decode(td::MutableSlice(buf, 36), key, true)) {
|
||||
return td::Status::Error("Public key is not serialized in base64 encoding");
|
||||
}
|
||||
|
||||
td::uint16 hash = static_cast<td::uint16>((static_cast<unsigned>(buf[34]) << 8) + buf[35]);
|
||||
if (hash != td::crc16(td::Slice(buf, 34))) {
|
||||
return td::Status::Error("Public key has incorrect crc16 hash");
|
||||
}
|
||||
|
||||
if (buf[0] != 0x3e) {
|
||||
return td::Status::Error("Not a public key");
|
||||
}
|
||||
if (buf[1] != 0xe6) {
|
||||
return td::Status::Error("Not an ed25519 public key");
|
||||
}
|
||||
|
||||
return from_bytes(td::Slice(buf + 2, 32));
|
||||
}
|
||||
|
||||
std::string PublicKey::serialize(bool base64_url) {
|
||||
CHECK(key.size() == 32);
|
||||
std::string buf(36, 0);
|
||||
td::MutableSlice bytes(buf);
|
||||
|
||||
bytes[0] = static_cast<char>(0x3e);
|
||||
bytes[1] = static_cast<char>(0xe6);
|
||||
bytes.substr(2).copy_from(key);
|
||||
auto hash = td::crc16(bytes.substr(0, 34));
|
||||
bytes[34] = static_cast<char>(hash >> 8);
|
||||
bytes[35] = static_cast<char>(hash & 255);
|
||||
|
||||
std::string res(48, 0);
|
||||
buff_base64_encode(res, bytes, base64_url);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool pack_std_smc_addr_to(char result[48], bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr,
|
||||
bool bounceable, bool testnet) {
|
||||
if (wc < -128 || wc >= 128) {
|
||||
|
@ -316,14 +367,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
|
|||
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & {
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
|
||||
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
|
||||
(last_inmsg_lt > other.last_inmsg_lt ||
|
||||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const & {
|
||||
ton::BlockSeqno other_mc_seqno) const& {
|
||||
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
|
||||
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
|
||||
}
|
||||
|
|
|
@ -33,6 +33,16 @@ namespace block {
|
|||
|
||||
using td::Ref;
|
||||
|
||||
struct PublicKey {
|
||||
std::string key;
|
||||
|
||||
static td::Result<PublicKey> from_bytes(td::Slice key);
|
||||
|
||||
static td::Result<PublicKey> parse(td::Slice key);
|
||||
|
||||
std::string serialize(bool base64_url = false);
|
||||
};
|
||||
|
||||
struct StdAddress {
|
||||
ton::WorkchainId workchain{ton::workchainInvalid};
|
||||
bool bounceable{true}; // addresses must be bounceable by default
|
||||
|
@ -149,12 +159,12 @@ struct MsgProcessedUpto {
|
|||
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
|
||||
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
|
||||
}
|
||||
bool operator<(const MsgProcessedUpto& other) const & {
|
||||
bool operator<(const MsgProcessedUpto& other) const& {
|
||||
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
|
||||
}
|
||||
bool contains(const MsgProcessedUpto& other) const &;
|
||||
bool contains(const MsgProcessedUpto& other) const&;
|
||||
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const &;
|
||||
ton::BlockSeqno other_mc_seqno) const&;
|
||||
// NB: this is for checking whether we have already imported an internal message
|
||||
bool already_processed(const EnqueuedMsgDescr& msg) const;
|
||||
};
|
||||
|
@ -470,13 +480,14 @@ struct BlockProofLink {
|
|||
bool incomplete() const {
|
||||
return dest_proof.is_null();
|
||||
}
|
||||
td::Status validate() const;
|
||||
td::Status validate(td::uint32* save_utime = nullptr) const;
|
||||
};
|
||||
|
||||
struct BlockProofChain {
|
||||
ton::BlockIdExt from, to;
|
||||
int mode;
|
||||
bool complete{false}, has_key_block{false}, valid{false};
|
||||
td::uint32 last_utime{0};
|
||||
bool complete{false}, has_key_block{false}, has_utime{false}, valid{false};
|
||||
ton::BlockIdExt key_blkid;
|
||||
std::vector<BlockProofLink> links;
|
||||
std::size_t link_count() const {
|
||||
|
|
|
@ -599,6 +599,10 @@ gas_prices#dd gas_price:uint64 gas_limit:uint64 gas_credit:uint64
|
|||
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
|
||||
= GasLimitsPrices;
|
||||
|
||||
gas_prices_ext#de gas_price:uint64 gas_limit:uint64 special_gas_limit:uint64 gas_credit:uint64
|
||||
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
|
||||
= GasLimitsPrices;
|
||||
|
||||
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
|
||||
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;
|
||||
|
||||
|
|
|
@ -296,7 +296,10 @@ td::Result<TransactionList::Info> TransactionList::validate() const {
|
|||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Status BlockProofLink::validate() const {
|
||||
td::Status BlockProofLink::validate(td::uint32* save_utime) const {
|
||||
if (save_utime) {
|
||||
*save_utime = 0;
|
||||
}
|
||||
if (!(from.is_masterchain_ext() && to.is_masterchain_ext())) {
|
||||
return td::Status::Error("BlockProofLink must have both source and destination blocks in the masterchain");
|
||||
}
|
||||
|
@ -346,6 +349,9 @@ td::Status BlockProofLink::validate() const {
|
|||
return td::Status::Error(PSTRING() << "incorrect is_key_block value " << is_key << " for destination block "
|
||||
<< to.to_str());
|
||||
}
|
||||
if (save_utime) {
|
||||
*save_utime = info.gen_utime;
|
||||
}
|
||||
} else if (!is_key) {
|
||||
// return td::Status::Error("Zerostate destination block "s + to.to_str() + " does not have is_key_block set");
|
||||
}
|
||||
|
@ -414,6 +420,8 @@ td::Status BlockProofLink::validate() const {
|
|||
td::Status BlockProofChain::validate() {
|
||||
valid = false;
|
||||
has_key_block = false;
|
||||
has_utime = false;
|
||||
last_utime = 0;
|
||||
key_blkid.invalidate();
|
||||
if (!(from.is_masterchain_ext() && to.is_masterchain_ext())) {
|
||||
return td::Status::Error("BlockProofChain must have both source and destination blocks in the masterchain");
|
||||
|
@ -435,7 +443,7 @@ td::Status BlockProofChain::validate() {
|
|||
<< link.from.to_str() << " but the previous link ends at different block "
|
||||
<< cur.to_str());
|
||||
}
|
||||
auto err = link.validate();
|
||||
auto err = link.validate(&last_utime);
|
||||
if (err.is_error()) {
|
||||
return td::Status::Error(PSTRING() << "link #" << i << " in BlockProofChain is invalid: " << err.to_string());
|
||||
}
|
||||
|
@ -449,6 +457,7 @@ td::Status BlockProofChain::validate() {
|
|||
return td::Status::Error("last link of BlockProofChain ends at block "s + cur.to_str() +
|
||||
" different from declared chain destination block " + to.to_str());
|
||||
}
|
||||
has_utime = (last_utime > 0);
|
||||
valid = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
|
|
@ -695,7 +695,7 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
|
|||
bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg) {
|
||||
// Compute gas limits
|
||||
if (account.is_special) {
|
||||
cp.gas_max = cfg.gas_limit; // TODO: introduce special gas limits?
|
||||
cp.gas_max = cfg.special_gas_limit;
|
||||
} else {
|
||||
cp.gas_max = cfg.gas_bought_for(balance.grams);
|
||||
}
|
||||
|
|
|
@ -96,6 +96,7 @@ struct StoragePhase {
|
|||
struct ComputePhaseConfig {
|
||||
td::uint64 gas_price;
|
||||
td::uint64 gas_limit;
|
||||
td::uint64 special_gas_limit;
|
||||
td::uint64 gas_credit;
|
||||
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
|
||||
td::RefInt256 gas_price256;
|
||||
|
@ -104,7 +105,11 @@ struct ComputePhaseConfig {
|
|||
Ref<vm::Cell> global_config;
|
||||
td::BitArray<256> block_rand_seed;
|
||||
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
|
||||
: gas_price(_gas_price), gas_limit(_gas_limit), gas_credit(_gas_credit) {
|
||||
: gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_gas_limit), gas_credit(_gas_credit) {
|
||||
compute_threshold();
|
||||
}
|
||||
ComputePhaseConfig(td::uint64 _gas_price, td::uint64 _gas_limit, td::uint64 _spec_gas_limit, td::uint64 _gas_credit)
|
||||
: gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_spec_gas_limit), gas_credit(_gas_credit) {
|
||||
compute_threshold();
|
||||
}
|
||||
void compute_threshold();
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/Timer.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
#include "td/utils/crypto.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
|
@ -704,6 +705,10 @@ void interpret_int_to_bytes(vm::Stack& stack, bool sgnd, bool lsb) {
|
|||
stack.push_bytes(std::string{(char*)buffer, sz});
|
||||
}
|
||||
|
||||
void interpret_string_to_bytes(vm::Stack& stack) {
|
||||
stack.push_bytes(stack.pop_string());
|
||||
}
|
||||
|
||||
void interpret_bytes_hash(vm::Stack& stack) {
|
||||
std::string str = stack.pop_bytes();
|
||||
unsigned char buffer[32];
|
||||
|
@ -1306,6 +1311,21 @@ void interpret_ed25519_chksign(vm::Stack& stack) {
|
|||
stack.push_bool(res.is_ok());
|
||||
}
|
||||
|
||||
void interpret_crc16(vm::Stack& stack) {
|
||||
std::string str = stack.pop_bytes();
|
||||
stack.push_smallint(td::crc16(td::Slice{str}));
|
||||
}
|
||||
|
||||
void interpret_crc32(vm::Stack& stack) {
|
||||
std::string str = stack.pop_bytes();
|
||||
stack.push_smallint(td::crc32(td::Slice{str}));
|
||||
}
|
||||
|
||||
void interpret_crc32c(vm::Stack& stack) {
|
||||
std::string str = stack.pop_bytes();
|
||||
stack.push_smallint(td::crc32c(td::Slice{str}));
|
||||
}
|
||||
|
||||
// vm dictionaries
|
||||
void interpret_dict_new(vm::Stack& stack) {
|
||||
stack.push({});
|
||||
|
@ -2427,6 +2447,7 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("B>Li@ ", std::bind(interpret_bytes_fetch_int, _1, 0x11));
|
||||
d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12));
|
||||
d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13));
|
||||
d.def_stack_word("$>B ", interpret_string_to_bytes);
|
||||
d.def_stack_word("Bhash ", interpret_bytes_hash);
|
||||
// cell manipulation (create, write and modify cells)
|
||||
d.def_stack_word("<b ", interpret_empty);
|
||||
|
@ -2493,6 +2514,9 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("ed25519_sign ", interpret_ed25519_sign);
|
||||
d.def_stack_word("ed25519_chksign ", interpret_ed25519_chksign);
|
||||
d.def_stack_word("ed25519_sign_uint ", interpret_ed25519_sign_uint);
|
||||
d.def_stack_word("crc16 ", interpret_crc16);
|
||||
d.def_stack_word("crc32 ", interpret_crc32);
|
||||
d.def_stack_word("crc32c ", interpret_crc32c);
|
||||
// vm dictionaries
|
||||
d.def_stack_word("dictnew ", interpret_dict_new);
|
||||
d.def_stack_word("dict>s ", interpret_dict_to_slice);
|
||||
|
|
|
@ -96,8 +96,8 @@ dictnew constant special-dict
|
|||
1 'nop
|
||||
} ::_ sg~
|
||||
|
||||
// gas_price gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit -- c
|
||||
{ 6 0 reverse <b x{dd} s, swap 64 u, swap 64 u, swap 64 u, swap 64 u, swap 64 u, swap 64 u, b>
|
||||
// gas_price gas_limit spec_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit -- c
|
||||
{ 7 0 reverse <b x{de} s, { swap 64 u, } 7 times b>
|
||||
} : make-gas-prices
|
||||
{ make-gas-prices 20 config! } : config.mc_gas_prices!
|
||||
{ make-gas-prices 21 config! } : config.gas_prices!
|
||||
|
@ -136,7 +136,6 @@ dictnew constant special-dict
|
|||
|
||||
{ <b swap dict, b> 7 config! } : config.to_mint!
|
||||
|
||||
|
||||
1000000000 constant Gram
|
||||
1000000 constant mGram
|
||||
1000 constant uGram
|
||||
|
@ -149,6 +148,7 @@ dictnew constant special-dict
|
|||
{ mGram swap */r } : mGram*/
|
||||
{ uGram swap */r } : uGram*/
|
||||
{ /r } : nGram*/
|
||||
|
||||
// GR$.17 is equivalent to 170000000
|
||||
{ bl word (number) ?dup 0= abort"not a valid Gram amount"
|
||||
1- { Gram swap */r } { Gram * } cond
|
||||
|
@ -157,8 +157,16 @@ dictnew constant special-dict
|
|||
|
||||
{ 10 << } : *Ki
|
||||
{ 20 << } : *Mi
|
||||
{ 30 << } : *Gi
|
||||
{ 10 <</r } : */Ki
|
||||
{ 20 <</r } : */Mi
|
||||
{ 30 <</r } : */Gi
|
||||
{ 1000 * } : *K
|
||||
{ 1000000 * } : *M
|
||||
{ 1000000000 * } : *G
|
||||
{ 1000 swap */r } : */K
|
||||
{ 1000000 swap */r } : */M
|
||||
{ 1000000000 swap */r } : */G
|
||||
|
||||
0 constant recv_internal
|
||||
-1 constant recv_external
|
||||
|
|
|
@ -168,9 +168,9 @@ elector_addr config.elector_smc!
|
|||
1 500 1000 500000 config.storage_prices!
|
||||
config.special!
|
||||
|
||||
// gas_price gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit --
|
||||
1000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.gas_prices!
|
||||
10000 sg* 1000000 10000 10000000 GR$0.1 GR$1.0 config.mc_gas_prices!
|
||||
// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit --
|
||||
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 config.gas_prices!
|
||||
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 config.mc_gas_prices!
|
||||
// lump_price bit_price cell_price ihr_factor first_frac next_frac
|
||||
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
|
||||
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
|
@ -6,10 +6,9 @@
|
|||
."Creates a new wallet in specified workchain, with private key saved to or loaded from <filename-base>.pk" cr
|
||||
."('new-wallet.pk' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 1- -2 and ' usage if } if
|
||||
$# 1- -2 and ' usage if
|
||||
|
||||
Basechain constant wc // create a wallet in workchain 0 (basechain)
|
||||
def? $1 { @' $1 parse-workchain-id =: wc } if // set workchain id from command line argument
|
||||
$1 parse-workchain-id =: wc // set workchain id from command line argument
|
||||
def? $2 { @' $2 } { "new-wallet" } cond constant file-base
|
||||
|
||||
."Creating new wallet in workchain " wc . cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." [<savefile>]" cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base>" cr
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <dest-addr> <seqno> <amount> [<savefile>]" cr
|
||||
."Creates a request to TestGiver and saves it into <savefile>.boc" cr
|
||||
."('testgiver-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 3 - -2 and ' usage if } if
|
||||
|
||||
$# 3 - -2 and ' usage if
|
||||
|
||||
// "testgiver.addr" load-address
|
||||
Masterchain 0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
2constant giver_addr
|
||||
."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr
|
||||
|
||||
Basechain 0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
2constant dest_addr
|
||||
false constant bounce
|
||||
|
||||
0 constant seqno
|
||||
GR$6.666 constant amount
|
||||
|
||||
def? $3 {
|
||||
@' $1 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
@' $2 parse-int =: seqno
|
||||
@' $3 $>GR =: amount
|
||||
} if
|
||||
$1 true parse-load-address =: bounce 2=: dest_addr
|
||||
$2 parse-int =: seqno
|
||||
$3 $>GR =: amount
|
||||
def? $4 { @' $4 } { "testgiver-query" } cond constant savefile
|
||||
|
||||
."Requesting " amount .GR ."to account "
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> [<savefile>]" cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> <index> <new-value-boc> [<savefile>]" cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <seqno> [<savefile>]" cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <wallet-addr> <elect-utime> <max-factor> <adnl-addr> [<savefile>]" cr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <wallet-addr> <elect-utime> <max-factor> <adnl-addr> <validator-pubkey> <validator-signature> [<savefile>]" cr
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
|
||||
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
|
||||
@' $# 2- =: $# } if } if
|
||||
def? $# { @' $# dup 4 < swap 5 > or ' usage if } if
|
||||
|
||||
"new-wallet" constant file-base
|
||||
Basechain 0x13CB612A00A7C092C7DFD2EA45D603A9B54591BA4C88F71E707E009B879F0FB2 2constant dest_addr
|
||||
0 constant seqno
|
||||
GR$.666 constant amount
|
||||
true constant bounce
|
||||
|
||||
def? $4 {
|
||||
@' $1 =: file-base
|
||||
@' $2 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
@' $3 parse-int =: seqno
|
||||
@' $4 $>GR =: amount
|
||||
} if
|
||||
$1 =: file-base
|
||||
$2 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
$3 parse-int =: seqno
|
||||
$4 $>GR =: amount
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
|
||||
file-base +".addr" load-address
|
||||
|
|
|
@ -31,9 +31,18 @@ and
|
|||
Ef+BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb (base64) or
|
||||
Ef-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODSkb (base64url)
|
||||
|
||||
|
||||
in the "user-friendly" form (to be displayed by user-friendly clients). Notice that both forms (base64 and base64url) are valid and must be accepted.
|
||||
|
||||
Incidentally, other binary data related to the TON Blockchain have similar "armored" base64 representations, differing by their first bytes. For example, the ubiquitious 256-bit Ed25519 public keys are represented by first creating a 36-byte sequence as follows:
|
||||
- one tag byte 0x3E, meaning that this is a public key
|
||||
- one tag byte 0xE6, meaning that this is a Ed25519 public key
|
||||
- 32 bytes containing the standard binary representation of the Ed25519 public key
|
||||
- 2 bytes containing the big-endian representation of CRC16-CCITT of the previous 34 bytes.
|
||||
|
||||
The resulting 36-byte sequence is converted into a 48-character base64 or base64url string in the standard fashion. For example, the Ed25519 public key E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D (usually represented by a sequence of 32 bytes 0xE3, 0x9E, ..., 0x7D) has the following "armored" representation:
|
||||
|
||||
Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2
|
||||
|
||||
2. Inspecting the state of a smart contract
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -76,7 +85,7 @@ account state is (account
|
|||
data:(just
|
||||
value:(raw@^Cell
|
||||
x{}
|
||||
x{00000000}
|
||||
x{00009A15}
|
||||
))
|
||||
library:hme_empty))))
|
||||
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C0000000000000000000000001C0E35FA931A000134_}
|
||||
|
@ -92,7 +101,7 @@ Finally, the last several lines beginning with x{CFF538... (the "raw dump") cont
|
|||
|
||||
We can see that x{FF0020DDA4F260...} is the code of this smart contract. If we consult the Appendix A of the TON Virtual Machine documentation, we can even disassemble this code: FF00 is SETCP 0, 20 is DUP, DD is IFNOTRET, A4 is INC, F260 is THROWIF 32, and so on. (Incidentally, you can find the source code of this smartcontract in the source file crypto/block/mc0.fif .)
|
||||
|
||||
We can also see that x{00000000} (the actual value you see may be different) is the persistent data of this smart contract. It is actually an unsigned 32-bit integer, used by the smart contract as the counter of operations performed so far. Notice that this value is big-endian (i.e., 3 is encoded as x{00000003}, not as x{03000000}), as are all integers inside the TON Blockchain.
|
||||
We can also see that x{00009A15} (the actual value you see may be different) is the persistent data of this smart contract. It is actually an unsigned 32-bit integer, used by the smart contract as the counter of operations performed so far. Notice that this value is big-endian (i.e., 3 is encoded as x{00000003}, not as x{03000000}), as are all integers inside the TON Blockchain. In this case the counter is equal to 0x9A15 = 39445.
|
||||
|
||||
The current balance of the smart contract is easily seen in the pretty-printed portion of the output. In this case, we see ... balance:(currencies:(grams:(nanograms:(... value:1000000000000000...)))), which is the balance of the account in (test) nanograms (a million test Grams in this example; the actual number you see may be smaller). If you study the TL-B scheme provided in crypto/block/scheme.tlb, you will be able to find this number (10^15) in binary big-endian form in the raw dump portion as well (it is located near the end of the data bits of the root cell).
|
||||
|
||||
|
@ -103,96 +112,117 @@ Before uploading a new smart contract into the TON Blockchain, you need to deter
|
|||
|
||||
Obviously, you'll need some tools for developing smart contracts - namely, a TON smart contract compiler. Basically, a TON smart contract compiler is a program that reads the source of a smart contract in a specialized high-level programming language and creates a .boc file from this source.
|
||||
|
||||
One such tool is the Fift interpreter, which is included in this distribution and can help create simple smart contracts. You may wish to develop more sophisticated tools. However, Fift is sufficient for demonstration purposes.
|
||||
One such tool is the Fift interpreter, which is included in this distribution and can help create simple smart contracts. Larger smart contracts should be developed using more sophisticated tools (such as the FunC compiler included in this distribution, that creates Fift assembler files from FunC source files; you can find some FunC smart-contract sources in the directory `crypto/smartcont`). However, Fift is sufficient for demonstration purposes.
|
||||
|
||||
Create the file `new-wallet.fif` containing the source of our new smart contract:
|
||||
Consider the file `new-wallet.fif` (usually located as `crypto/smartcont/new-wallet.fif` with respect to the source directory) containing the source of a simple wallet smart contract:
|
||||
|
||||
------------------------------------
|
||||
#!/usr/bin/fift -s
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
0 constant wc // create a wallet in workchain 0 (basechain)
|
||||
{ ."usage: " @' $0 type ." <workchain-id> [<filename-base>]" cr
|
||||
."Creates a new wallet in specified workchain, with private key saved to or loaded from <filename-base>.pk" cr
|
||||
."('new-wallet.pk' by default)" cr 1 halt
|
||||
} : usage
|
||||
$# 1- -2 and ' usage if
|
||||
|
||||
$1 parse-workchain-id =: wc // set workchain id from command line argument
|
||||
def? $2 { @' $2 } { "new-wallet" } cond constant file-base
|
||||
|
||||
."Creating new wallet in workchain " wc . cr
|
||||
|
||||
// Create new simple wallet
|
||||
<{ SETCP0 DUP IFNOTRET INC 32 THROWIF // return if recv_internal, fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP
|
||||
DUP SREFS IF:<{
|
||||
8 LDU LDREF // pubk cnt mode msg cs
|
||||
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
|
||||
}>
|
||||
ENDS
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
}>c
|
||||
<{ SETCP0 DUP IFNOTRET // return if recv_internal
|
||||
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
|
||||
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
|
||||
}>
|
||||
INC 32 THROWIF // fail unless recv_external
|
||||
512 INT LDSLICEX DUP 32 PLDU // sign cs cnt
|
||||
c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk
|
||||
s1 s2 XCPU // sign cs cnt pubk cnt' cnt
|
||||
EQUAL 33 THROWIFNOT // ( seqno mismatch? )
|
||||
s2 PUSH HASHSU // sign cs cnt pubk hash
|
||||
s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk
|
||||
CHKSIGNU // pubk cs cnt ?
|
||||
34 THROWIFNOT // signature mismatch
|
||||
ACCEPT
|
||||
SWAP 32 LDU NIP
|
||||
DUP SREFS IF:<{
|
||||
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
|
||||
8 LDU LDREF // pubk cnt mode msg cs
|
||||
s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent )
|
||||
}>
|
||||
ENDS
|
||||
INC NEWC 32 STU 256 STU ENDC c4 POPCTR
|
||||
}>c // >libref
|
||||
// code
|
||||
<b 0 32 u,
|
||||
newkeypair swap dup constant wallet_pk
|
||||
"new-wallet.pk" B>file
|
||||
file-base +".pk" load-generate-keypair
|
||||
constant wallet_pk
|
||||
B,
|
||||
b> // data
|
||||
// no libraries
|
||||
<b b{00110} s, rot ref, swap ref, b> // create StateInit
|
||||
null // no libraries
|
||||
// Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
dup hash dup constant wallet_addr
|
||||
."new wallet address = " wc . .": " dup x. cr
|
||||
wc over 7 smca>$ type cr
|
||||
256 u>B "new-wallet.addr" B>file
|
||||
dup hash wc swap 2dup 2constant wallet_addr
|
||||
."new wallet address = " 2dup .addr cr
|
||||
2dup file-base +".addr" save-address-verbose
|
||||
."Non-bounceable address (for init): " 2dup 7 .Addr cr
|
||||
."Bounceable address (for later access): " 6 .Addr cr
|
||||
<b 0 32 u, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint rot
|
||||
<b b{1000100} s, wc 8 i, wallet_addr 256 u, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
|
||||
<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
|
||||
"new-wallet-query.boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
file-base +"-query.boc" tuck B>file
|
||||
."(Saved wallet creating query to file " type .")" cr
|
||||
--------------------------------------------
|
||||
|
||||
Incidentally, you can find a more sophisticated version of this sample file in crypto/smartcont/new-wallet.fif. It accepts command-line arguments, so you don't need to edit the source file each time you want to create a new wallet.
|
||||
(The actual source file in your distribution may be slighly different.) Essentially, it is a complete Fift script for creating a new instance of this smart contract controlled by a newly-generated keypair. The script accepts command-line arguments, so you don't need to edit the source file each time you want to create a new wallet.
|
||||
|
||||
Now, provided that you have compiled Fift binary (usually located as "crypto/fift" with respect to the build directory), you can run
|
||||
|
||||
crypto/fift -I<source-directory>/crypto/fift/lib new-wallet.fif
|
||||
$ crypto/fift -I<source-directory>/crypto/fift/lib -s <source-directory>/crypto/smartcont/new-wallet.fif 0 my_wallet_name
|
||||
|
||||
assuming that you have copied new-wallet.fif into the current directory. Alternatively, you might skip the source editing phase and simply run
|
||||
where 0 is the workchain to contain the new wallet (0 = basechain, -1 = masterchain), `my_wallet_name` is any identifier you wish to be associated with this wallet. The address of the new wallet will be saved into file `my_wallet_name.addr`, its newly-generated private key will be saved to `my_wallet_name.pk` (unless this file already exists; then the key will be loaded from this file instead), and the external message will be saved into my_wallet_name-query.boc. If you do not indicate the name of your wallet (`my_wallet_name` in the example above), the default name `new-wallet` is used.
|
||||
|
||||
crypto/fift -I<source-directory>/crypto/fift/lib -s <source-directory>/smartcont/new-wallet.fif 0 my_wallet_name
|
||||
You may opt to set the FIFTPATH environment variable to <source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont, the directories containing Fift.fif and Asm.fif library files, and the sample smart-contract sources, respectively; then you can omit the -I argument to the Fift interpreter. If you install the Fift binary `crypto/fift` to a directory included in your PATH (e.g., /usr/bin/fift), you can simply invoke
|
||||
|
||||
where 0 is the workchain to contain the new wallet (0 = basechain, -1 = masterchain), `my_wallet_name` is any identifier you wish to be associated with this wallet. The address of the new wallet will be saved into file `my_wallet_name.addr`, its newly-generated private key will be saved to `my_wallet_name.pk` (unless this file already exists; then the key will be loaded from this file instead), and the external message will be saved into my_new_wallet-query.boc. If you do not indicate the name of your wallet (`my_wallet_name` in the example above), the default name `new-wallet` is used.
|
||||
$ fift -s new-wallet.fif 0 my_wallet_name
|
||||
|
||||
You may wish to set the FIFTPATH environment variable to <source-directory>/crypto/fift/lib, the directory containing Fift.fif and Asm.fif library files; then you can omit the -I argument to the Fift interpreter.
|
||||
instead of indicating the complete search paths in the command line.
|
||||
|
||||
If everything worked, you'll see something like the following
|
||||
|
||||
--------------------------------------------
|
||||
Creating new wallet in workchain 0
|
||||
Saved new private key to file my_wallet_name.pk
|
||||
StateInit: x{34_}
|
||||
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
|
||||
x{00000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
|
||||
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
|
||||
x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446}
|
||||
|
||||
new wallet address = -1 : 60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD
|
||||
new wallet address = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2
|
||||
(Saving address to file my_wallet_name.addr)
|
||||
Non-bounceable address (for init): 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
|
||||
Bounceable address (for later access): kQAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4rie
|
||||
signing message: x{00000000}
|
||||
|
||||
External message for initialization is x{89FEC18082838D4F72DAD0C2BCF523A4CB5A1E7535245D35D39203A9F507EBA781A0119401748E6F89C1BA026A363C9F58765508DFF6854475357210D0D69F07C3A5453CEEDF1A0383FC405B57FF10CE060C2377BDD954A336DE5161F0AC1C61084180E00000001_}
|
||||
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
|
||||
x{00000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
|
||||
External message for initialization is x{88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C00000001_}
|
||||
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
|
||||
x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446}
|
||||
|
||||
B5EE9C724104030100000000DA0002CF89FEC18082838D4F72DAD0C2BCF523A4CB5A1E7535245D35D39203A9F507EBA781A0119401748E6F89C1BA026A363C9F58765508DFF6854475357210D0D69F07C3A5453CEEDF1A0383FC405B57FF10CE060C2377BDD954A336DE5161F0AC1C61084180E0000000100102008CFF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54004800000000F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA32DB9BE63
|
||||
(Saved to file new-wallet-query.boc)
|
||||
B5EE9C724104030100000000E50002CF88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C000000010010200A2FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54004800000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446BCF59C17
|
||||
(Saved wallet creating query to file my_wallet_name-query.boc)
|
||||
--------------------------------------------
|
||||
|
||||
In a nutshell, the Fift assembler (loaded by the "Asm.fif" include line) is used to compile the source code of the smart contract (contained in <{ SETCP0 ... c4 POPCTR }> lines) into its internal representation. The initial data of the smart contract is also created (by <b 0 32 u, ... b> lines), containing a 32-bit sequence number (equal to zero) and a 256-bit public key from a newly-generated Ed25519 keypair. The corresponding private key is saved into the file `new-wallet.pk` (be careful not to run this code twice in the same directory, otherwise this private key file will be overwritten).
|
||||
In a nutshell, the Fift assembler (loaded by the "Asm.fif" include line) is used to compile the source code of the smart contract (contained in <{ SETCP0 ... c4 POPCTR }> lines) into its internal representation. The initial data of the smart contract is also created (by <b 0 32 u, ... b> lines), containing a 32-bit sequence number (equal to zero) and a 256-bit public key from a newly-generated Ed25519 keypair. The corresponding private key is saved into the file `my_wallet_name.pk` unless it already exists (if you run this code twice in the same directory, the private key will be loaded from this file instead).
|
||||
|
||||
The code and data for the new smart contract are combined into a StateInit structure (in the next lines), the address of the new smart contract (equal to the hash of this StateInit structure) is computed and output, and then an external message with a destination address equal to that of the new smart contract is created. This external message contains both the correct StateInit for the new smart contract and a non-trivial payload (signed by the correct private key).
|
||||
|
||||
Finally, the external message is serialized into a bag of cells (represented by B5EE...BE63) and saved into the file `new-wallet-query.boc`. Essentially, this file is your compiled smart contract with all additional information necessary to upload it into the TON Blockchain.
|
||||
Finally, the external message is serialized into a bag of cells (represented by B5EE...BE63) and saved into the file `my_wallet_name-query.boc`. Essentially, this file is your compiled smart contract with all additional information necessary to upload it into the TON Blockchain.
|
||||
|
||||
4. Transferring some funds to the new smart contract
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -203,9 +233,9 @@ You might try to upload the new smart contract immediately by running the Lite C
|
|||
|
||||
or
|
||||
|
||||
> sendfile my_new_wallet-query.boc
|
||||
> sendfile my_wallet_name-query.boc
|
||||
|
||||
if you chose to name your wallet `my_new_wallet`.
|
||||
if you chose to name your wallet `my_wallet_name`.
|
||||
|
||||
Unfortunately, this won't work, because smart contracts must have a positive balance to be able to pay for storing and processing their data in the blockchain. So you have to transfer some funds to your new smart contract address first, displayed during its generation as -1:60c0...c0d0 (in raw form) and 0f9..EKD (in user-friendly form).
|
||||
|
||||
|
@ -223,64 +253,78 @@ You need to know the address of the test giver smart contract. We'll assume that
|
|||
|
||||
as explained above in Section 2. The only number you need from the output is the 32-bit sequence number stored in the smart contract data (it is zero in the example above, but generally it will be non-zero).
|
||||
|
||||
Next, you create an external message to the test giver asking it to send another message to your (uninitialized) smart contract carrying a specified amount of test Grams. There is a special Fift source file for generating this external message, a more sophisticated version of which is located at crypto/smartcont/testgiver.fif:
|
||||
Next, you create an external message to the test giver asking it to send another message to your (uninitialized) smart contract carrying a specified amount of test Grams. There is a special Fift script for generating this external message located at crypto/smartcont/testgiver.fif:
|
||||
|
||||
--------------------------------------------
|
||||
// "testgiver.addr" file>B 256 B>u@
|
||||
0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
dup constant wallet_addr ."Test giver address = " x. cr
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
constant dest_addr
|
||||
{ ."usage: " @' $0 type ." <dest-addr> <seqno> <amount> [<savefile>]" cr
|
||||
."Creates a request to TestGiver and saves it into <savefile>.boc" cr
|
||||
."('testgiver-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
|
||||
-1 constant wc
|
||||
0 constant seqno
|
||||
$# 3 - -2 and ' usage if
|
||||
|
||||
1000000000 constant Gram
|
||||
{ Gram swap */ } : Gram*/
|
||||
// "testgiver.addr" load-address
|
||||
Masterchain 0x8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
2constant giver_addr
|
||||
."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr
|
||||
|
||||
6.666 Gram*/ constant amount
|
||||
$1 true parse-load-address =: bounce 2=: dest_addr
|
||||
$2 parse-int =: seqno
|
||||
$3 $>GR =: amount
|
||||
def? $4 { @' $4 } { "testgiver-query" } cond constant savefile
|
||||
|
||||
."Requesting " amount .GR ."to account "
|
||||
dest_addr 2dup bounce 7 + .Addr ." = " .addr
|
||||
."seqno=0x" seqno x. ."bounce=" bounce . cr
|
||||
|
||||
// b x --> b' ( serializes a Gram amount )
|
||||
{ -1 { 1+ 2dup 8 * ufits } until
|
||||
rot over 4 u, -rot 8 * u, } : Gram,
|
||||
|
||||
// create a message (NB: 01b00.., b = bounce)
|
||||
<b b{010000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
|
||||
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr,
|
||||
amount Gram, 0 9 64 32 + + 1+ 1+ u, "GIFT" $, b>
|
||||
<b seqno 32 u, 1 8 u, swap ref, b>
|
||||
dup ."enveloping message: " <s csr. cr
|
||||
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
|
||||
<b b{1000100} s, giver_addr addr, 0 Gram, b{00} s,
|
||||
swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
"wallet-query.boc" B>file
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
---------------------------------------------
|
||||
|
||||
In general, you will need to edit the line containing the destination address (0x60c0...c0d0 in our example) and the sequence number of the test giver (the "0" in the "0 constant seqno" line). Alternatively, you can pass the required parameters as command-line arguments to
|
||||
You can pass the required parameters as command-line arguments to this script
|
||||
|
||||
fift -I<include-path> -s <path-to-testgiver-fif> <dest-addr> <seqno> <amount> [<savefile>]
|
||||
$ crypto/fift -I<include-path> -s <path-to-testgiver-fif> <dest-addr> <testgiver-seqno> <gram-amount> [<savefile>]
|
||||
|
||||
For instance,
|
||||
|
||||
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s testgiver.fif 0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD 6.666 wallet-query
|
||||
$ crypto/fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query
|
||||
|
||||
(Again, setting FIFTPATH to <source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont and installing fift binary as /usr/bin/fift is a good idea.)
|
||||
or simply
|
||||
|
||||
The newly-created message to the new smart contract must have its bounce bit clear, otherwise the transfer will be "bounced" to its sender.
|
||||
$ fift -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query
|
||||
|
||||
provided you have set up the environment variable FIFTPATH to <source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont and installed the fift binary as /usr/bin/fift (or anywhere else in your PATH).
|
||||
|
||||
The newly-created message to the new smart contract must have its bounce bit clear, otherwise the transfer will be "bounced" to its sender. This is the reason we have passed the "non-bounceable" address 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb of our new wallet smart contract.
|
||||
|
||||
This Fift code creates an internal message from the test giver smart contract to the address of our new smart contract carrying 6.666 test Grams (you can enter any other amount here up to approximately 20 Grams). Then this message is enveloped into an external message addressed to the test giver; this external message must also contain the correct sequence number of the test giver. When the test giver receives such an external message, it checks whether the sequence number matches the one stored in its persistent data, and if it does, sends the embedded internal message with the required amount of test Grams to its destination (our smart contract in this case).
|
||||
|
||||
The external message is serialized and saved into the file `wallet-query.boc`. Some output is generated in the process:
|
||||
|
||||
---------------------------------------------
|
||||
Test giver address = 8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
enveloping message: x{0000000001}
|
||||
x{427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B4000000000000000000000000000047494654}
|
||||
Test giver address = -1:8156775b79325e5d62e742d9b96c30b6515a5cd2f1f64c5da4b193c03f070e0d
|
||||
kf-BVndbeTJeXWLnQtm5bDC2UVpc0vH2TF2ksZPAPwcODZKR
|
||||
Requesting GR$6.666 to account 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 seqno=0x9a15 bounce=0
|
||||
enveloping message: x{00009A1501}
|
||||
x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654}
|
||||
|
||||
resulting external message: x{89FEA71F4F9849FF1D54203B094BE356FD065FC3B0966139BFDE9DD286E755901EFA00000000000C_}
|
||||
x{427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B4000000000000000000000000000047494654}
|
||||
resulting external message: x{89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C_}
|
||||
x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654}
|
||||
|
||||
B5EE9C7241040201000000006600014F89FEA71F4F9849FF1D54203B094BE356FD065FC3B0966139BFDE9DD286E755901EFA00000000000C010072427FB06020A0E353DCB6B430AF3D48E932D6879D4D49174D74E480EA7D41FAE9E068280C6A98B40000000000000000000000000000474946545D6254A9
|
||||
B5EE9C7241040201000000006600014F89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C01007242001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654AFC17FA4
|
||||
(Saved to file wallet-query.boc)
|
||||
---------------------------------------------
|
||||
|
||||
6. Uploading the external message to the test giver smart contract
|
||||
|
@ -329,7 +373,7 @@ account state is (account
|
|||
data:(just
|
||||
value:(raw@^Cell
|
||||
x{}
|
||||
x{00000001}
|
||||
x{00009A16}
|
||||
))
|
||||
library:hme_empty))))
|
||||
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C00000000000000009F65D110DC0E35F450FA914134_}
|
||||
|
@ -337,21 +381,20 @@ x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C000
|
|||
x{00000001}
|
||||
---------------------------------------------
|
||||
|
||||
You may notice that the sequence number stored in the persistent data has changed (in our example, to one), and the last_trans_lt field (the logical time of the last transaction of this account) has been increased.
|
||||
You may notice that the sequence number stored in the persistent data has changed (in our example, to 0x9A16 = 39446), and the last_trans_lt field (the logical time of the last transaction of this account) has been increased.
|
||||
|
||||
Now we can inspect the state of our new smart contract:
|
||||
|
||||
> getaccount 0f9gwEFBxqe5bWhhXnqR0mWtDzqaki6a6ckB1PqD9dPA0EKD
|
||||
> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
|
||||
or
|
||||
> getaccount -1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
|
||||
> getaccount 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2
|
||||
Now we see
|
||||
|
||||
---------------------------------------------
|
||||
got account state for -1:60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB
|
||||
got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB
|
||||
account state is (account
|
||||
addr:(addr_std
|
||||
anycast:nothing workchain_id:-1 address:x60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0)
|
||||
anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2)
|
||||
storage_stat:(storage_info
|
||||
used:(storage_used
|
||||
cells:(var_uint len:1 value:1)
|
||||
|
@ -376,16 +419,16 @@ Our new smart contract has some positive balance (of 6.666 test Grams), but has
|
|||
Now you can finally upload the external message with the StateInit of the new smart contract, containing its code and data:
|
||||
|
||||
---------------------------------------------
|
||||
> sendfile new-wallet-query.boc
|
||||
> sendfile my_wallet_name-query.boc
|
||||
... external message status is 1
|
||||
> last
|
||||
...
|
||||
> getaccount -1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0
|
||||
> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
|
||||
...
|
||||
got account state for -1:60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0 with respect to blocks (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E and (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E
|
||||
got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E and (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E
|
||||
account state is (account
|
||||
addr:(addr_std
|
||||
anycast:nothing workchain_id:-1 address:x60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D0)
|
||||
anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2)
|
||||
storage_stat:(storage_info
|
||||
used:(storage_used
|
||||
cells:(var_uint len:1 value:3)
|
||||
|
@ -423,63 +466,76 @@ You will see that the smart contract has been initialized using code and data fr
|
|||
8. Using the simple wallet smart contract
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Actually, the simple wallet smart contract used in this example can be used to transfer test Grams to any other accounts. It is in this respect similar to the test giver smart contract discussed above, with the difference that it processes only external messages signed by the correct private key (of its owner). In our case, it is the private key saved into the file "new-wallet.pk" during the compilation of the smart contract (see Section 3).
|
||||
Actually, the simple wallet smart contract used in this example can be used to transfer test Grams to any other accounts. It is in this respect similar to the test giver smart contract discussed above, with the difference that it processes only external messages signed by the correct private key (of its owner). In our case, it is the private key saved into the file "my_wallet_name.pk" during the compilation of the smart contract (see Section 3).
|
||||
|
||||
An example of how you might use this smart contract is provided in sample file crypto/smartcont/wallet.fif :
|
||||
|
||||
--------------------------------------------------------
|
||||
#!/usr/bin/fift -s
|
||||
def? $1 { "new-wallet" =: $1 } ifnot
|
||||
$1 $len { "new-wallet" =: $1 } ifnot
|
||||
$1 +".addr" file>B 256 B>u@ dup constant wallet_addr
|
||||
."Wallet address = " x. cr
|
||||
$1 +".pk" file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
|
||||
constant wallet_pk
|
||||
#!/usr/bin/env fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
// 0x1111111122222222333333334444444455555555666666667777777788888888 constant dest_addr
|
||||
0x13CB612A00A7C092C7DFD2EA45D603A9B54591BA4C88F71E707E009B879F0FB2 constant dest_addr
|
||||
-1 constant wc
|
||||
0 constant seqno
|
||||
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
|
||||
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
|
||||
@' $# 2- =: $# } if } if
|
||||
|
||||
1000000000 constant Gram
|
||||
{ Gram swap */ } : Gram*/
|
||||
true constant bounce
|
||||
|
||||
.666 Gram*/ constant amount
|
||||
$1 =: file-base
|
||||
$2 bounce parse-load-address =: bounce 2=: dest_addr
|
||||
$3 parse-int =: seqno
|
||||
$4 $>GR =: amount
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
|
||||
// b x --> b' ( serializes a Gram amount )
|
||||
{ -1 { 1+ 2dup 8 * ufits } until
|
||||
rot over 4 u, -rot 8 * u, } : Gram,
|
||||
file-base +".addr" load-address
|
||||
2dup 2constant wallet_addr
|
||||
."Source wallet address = " 2dup .addr cr 6 .Addr cr
|
||||
file-base +".pk" load-keypair nip constant wallet_pk
|
||||
|
||||
def? body-boc-file { @' body-boc-file file>B B>boc } { <b "TEST" $, b> } cond
|
||||
constant body-cell
|
||||
|
||||
."Transferring " amount .GR ."to account "
|
||||
dest_addr 2dup bounce 7 + .Addr ." = " .addr
|
||||
."seqno=0x" seqno x. ."bounce=" bounce . cr
|
||||
."Body of transfer message is " body-cell <s csr. cr
|
||||
|
||||
// create a message
|
||||
<b b{011000100} s, wc 8 i, dest_addr 256 u, amount Gram, 0 9 64 32 + + 1+ 1+ u, "TEST" $, b>
|
||||
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr, amount Gram, 0 9 64 32 + + 1+ u,
|
||||
body-cell <s 2dup s-fits? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
|
||||
b>
|
||||
<b seqno 32 u, 1 8 u, swap ref, b>
|
||||
dup ."signing message: " <s csr. cr
|
||||
dup hash wallet_pk ed25519_sign_uint
|
||||
<b b{1000100} s, wc 8 i, wallet_addr 256 u, 0 Gram, b{00} s,
|
||||
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
|
||||
swap B, swap <s s, b>
|
||||
dup ."resulting external message: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
$1 +"-query.boc" B>file
|
||||
savefile +".boc" tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
-------------------------------------
|
||||
|
||||
You can hard-code the address of your smart contract here by changing the fourth line to, say,
|
||||
You can invoke this script as follows:
|
||||
|
||||
0x60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0 dup constant wallet_addr
|
||||
$ fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <gram-amount>
|
||||
|
||||
You will also need to change the destination address, the name of the file with the private key, the sequence number (it will be 1 immediately after the smart contract is initialized; the actual value can always be retrieved by inspecting the current account state) and the Gram amount to be transferred. The payload of the internal message contains 32 bits with the string "TEST"; it can be changed to something more useful if necessary.
|
||||
or simply
|
||||
|
||||
Alternatively, you can use the more sophisticated version of this script provided in crypto/smartcont/wallet.fif in the source tree, and pass all the required parameters as command-line arguments:
|
||||
$ fift -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <gram-amount>
|
||||
|
||||
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <gram-amount>
|
||||
if you have correctly set up PATH and FIFTPATH.
|
||||
|
||||
For example,
|
||||
|
||||
fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif my_new_wallet kf8Ty2EqAKfAksff0upF1gOptUWRukyI9x5wfgCbh58Pss9j 1 .666
|
||||
$ fift -s wallet.fif my_wallet_name kf8Ty2EqAKfAksff0upF1gOptUWRukyI9x5wfgCbh58Pss9j 1 .666
|
||||
|
||||
Here `my_new_wallet` is the identifier of your wallet used before with new-wallet.fif; the address and the private key of your test wallet will be loaded from files `my_new_wallet.addr` and `my_new_wallet.pk` in the current directory.
|
||||
Here `my_wallet_name` is the identifier of your wallet used before with new-wallet.fif; the address and the private key of your test wallet will be loaded from files `my_wallet_name.addr` and `my_wallet_name.pk` in the current directory.
|
||||
|
||||
When you run this code (by invoking the Fift interpreter), you create an external message with a destination equal to the address of your wallet smart contract, containing a correct Ed25519 signature, a sequence number, and an enveloped internal message from your wallet smart contract to the smart contract indicated in dest_addr, with an arbitrary value attached and an arbitrary payload. When your smart contract receives and processes this external message, it first checks the signature and the sequence number. If they are correct, it accepts the external message, sends the embedded internal message from itself to the intended destination, and increases the sequence number in its persistent data (this is a simple measure to prevent replay attacks, in case this sample wallet smart contract code ends up used in a real wallet application).
|
||||
|
||||
Of course, a true TON Blockchain wallet application would hide all the intermediate steps explained above. It would first communicate the address of the new smart contract to the user, asking them to transfer some funds to the indicated address (displayed in its non-bounceable user-friendly form) from another wallet or a cryptocurrency exchange, and then would provide a simple interface to display the current balance and to transfer funds to whatever other addresses the user wants. (The aim of this document is to explain how to create new non-trivial smart contracts and experiment with the TON Blockchain Test Network, rather than to explain how one could use the Lite Client instead of a more user-friendly wallet application.)
|
||||
|
||||
One final remark: The above examples used smart contracts in the basic workchain (workchain 0). They would work in exactly the same way in the masterchain (workchain -1), if one changes the "0 constant wc" to "-1 constant wc" in relevant places. The only difference is that the processing and storage fees in the basic workchain are 10-100 times lower than in the masterchain. Some smart contracts (such as the validator election smart contract) accept transfers only from masterchain smart contracts, so you'll need a wallet in the masterchain if you wish to make stakes on behalf of your own validator and participate in the elections.
|
||||
One final remark: The above examples used smart contracts in the basic workchain (workchain 0). They would work in exactly the same way in the masterchain (workchain -1), if one passes workchain identifier -1 instead of 0 as the first argument to `new-wallet.fif`. The only difference is that the processing and storage fees in the basic workchain are 100-1000 times lower than in the masterchain. Some smart contracts (such as the validator election smart contract) accept transfers only from masterchain smart contracts, so you'll need a wallet in the masterchain if you wish to make stakes on behalf of your own validator and participate in the elections.
|
||||
|
|
|
@ -14,7 +14,7 @@ https://test.ton.org/download
|
|||
|
||||
The TON Blockchain Test Network is updated quite often, so we cannot guarantee that older versions of the Lite Client will always work. Backward compatibility is not enforced at this development stage.
|
||||
|
||||
2) Install the newest versions of make, cmake (version 3.0.2 or later), OpenSSL (including C header files), and g++ or clang (or another C++14-compatible compiler as appropriate for your operating system).
|
||||
2) Install the newest versions of make, cmake (version 3.0.2 or later), OpenSSL (including C header files), and g++ or clang (or another C++14-compatible compiler as appropriate for your operating system). We strongly recommend installing OpenSSL version 1.1.1 or later for better performance, especially if you intend to run a Full Node or a Validator as well.
|
||||
|
||||
3) Suppose that you have unpacked this archive to directory ~/lite-client, where ~ is your home directory, and that you have created an empty directory ~/liteclient-build. Then run the following in a terminal on a Linux system:
|
||||
|
||||
|
@ -22,9 +22,10 @@ cd ~/liteclient-build
|
|||
cmake ~/lite-client
|
||||
cmake --build . --target lite-client
|
||||
|
||||
You might also want to build some extra utilities useful for smart-contract development:
|
||||
You might also build some extra utilities useful for smart-contract development:
|
||||
|
||||
cmake --build . --target fift
|
||||
cmake --build . --target func
|
||||
|
||||
4) Download the newest configuration file from https://test.ton.org/ton-lite-client-test1.config.json :
|
||||
|
||||
|
|
|
@ -235,57 +235,98 @@ bool TestNode::get_server_time() {
|
|||
});
|
||||
}
|
||||
|
||||
bool TestNode::get_server_version() {
|
||||
bool TestNode::get_server_version(int mode) {
|
||||
auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getVersion>(), true);
|
||||
return envelope_send_query(std::move(b), [&, Self = actor_id(this) ](td::Result<td::BufferSlice> res)->void {
|
||||
server_ok_ = false;
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot get server version and time (server too old?)";
|
||||
} else {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_version>(res.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getVersion";
|
||||
} else {
|
||||
auto a = F.move_as_ok();
|
||||
server_version_ = a->version_;
|
||||
server_capabilities_ = a->capabilities_;
|
||||
server_time_ = a->now_;
|
||||
server_time_got_at_ = static_cast<td::uint32>(td::Clocks::system());
|
||||
LOG(INFO) << "server time is " << server_time_ << " (delta " << server_time_ - server_time_got_at_ << ")";
|
||||
LOG(WARNING) << "server version is " << (server_version_ >> 8) << "." << (server_version_ & 0xff)
|
||||
<< ", capabilities " << server_capabilities_;
|
||||
server_ok_ = (server_version_ >= min_ls_version) && !(~server_capabilities_ & min_ls_capabilities);
|
||||
}
|
||||
}
|
||||
if (!server_ok_) {
|
||||
LOG(ERROR) << "server version is too old (at least " << (min_ls_version >> 8) << "." << (min_ls_version & 0xff)
|
||||
<< " with capabilities " << min_ls_capabilities << " required), some queries are unavailable";
|
||||
}
|
||||
return envelope_send_query(std::move(b), [ Self = actor_id(this), mode ](td::Result<td::BufferSlice> res) {
|
||||
td::actor::send_closure_later(Self, &TestNode::got_server_version, std::move(res), mode);
|
||||
});
|
||||
};
|
||||
|
||||
void TestNode::got_server_version(td::Result<td::BufferSlice> res, int mode) {
|
||||
server_ok_ = false;
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot get server version and time (server too old?)";
|
||||
} else {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_version>(res.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getVersion";
|
||||
} else {
|
||||
auto a = F.move_as_ok();
|
||||
set_server_version(a->version_, a->capabilities_);
|
||||
set_server_time(a->now_);
|
||||
}
|
||||
}
|
||||
if (!server_ok_) {
|
||||
LOG(ERROR) << "server version is too old (at least " << (min_ls_version >> 8) << "." << (min_ls_version & 0xff)
|
||||
<< " with capabilities " << min_ls_capabilities << " required), some queries are unavailable";
|
||||
}
|
||||
if (mode & 0x100) {
|
||||
get_server_mc_block_id();
|
||||
}
|
||||
}
|
||||
|
||||
void TestNode::set_server_version(td::int32 version, td::int64 capabilities) {
|
||||
if (server_version_ != version || server_capabilities_ != capabilities) {
|
||||
server_version_ = version;
|
||||
server_capabilities_ = capabilities;
|
||||
LOG(WARNING) << "server version is " << (server_version_ >> 8) << "." << (server_version_ & 0xff)
|
||||
<< ", capabilities " << server_capabilities_;
|
||||
}
|
||||
server_ok_ = (server_version_ >= min_ls_version) && !(~server_capabilities_ & min_ls_capabilities);
|
||||
}
|
||||
|
||||
void TestNode::set_server_time(int server_utime) {
|
||||
server_time_ = server_utime;
|
||||
server_time_got_at_ = static_cast<td::uint32>(td::Clocks::system());
|
||||
LOG(INFO) << "server time is " << server_time_ << " (delta " << server_time_ - server_time_got_at_ << ")";
|
||||
}
|
||||
|
||||
bool TestNode::get_server_mc_block_id() {
|
||||
auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
|
||||
return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result<td::BufferSlice> res)->void {
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot get masterchain info from server";
|
||||
return;
|
||||
} else {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(res.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getMasterchainInfo";
|
||||
int mode = (server_capabilities_ & 2) ? 0 : -1;
|
||||
if (mode < 0) {
|
||||
auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
|
||||
return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result<td::BufferSlice> res)->void {
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot get masterchain info from server";
|
||||
return;
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
auto blk_id = create_block_id(f->last_);
|
||||
auto zstate_id = create_zero_state_id(f->init_);
|
||||
LOG(INFO) << "last masterchain block is " << blk_id.to_str();
|
||||
td::actor::send_closure_later(Self, &TestNode::got_server_mc_block_id, blk_id, zstate_id);
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(res.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getMasterchainInfo";
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
auto blk_id = create_block_id(f->last_);
|
||||
auto zstate_id = create_zero_state_id(f->init_);
|
||||
LOG(INFO) << "last masterchain block is " << blk_id.to_str();
|
||||
td::actor::send_closure_later(Self, &TestNode::got_server_mc_block_id, blk_id, zstate_id, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
auto b =
|
||||
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfoExt>(mode), true);
|
||||
return envelope_send_query(std::move(b), [ Self = actor_id(this), mode ](td::Result<td::BufferSlice> res)->void {
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot get extended masterchain info from server";
|
||||
return;
|
||||
} else {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfoExt>(res.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getMasterchainInfoExt";
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
auto blk_id = create_block_id(f->last_);
|
||||
auto zstate_id = create_zero_state_id(f->init_);
|
||||
LOG(INFO) << "last masterchain block is " << blk_id.to_str();
|
||||
td::actor::send_closure_later(Self, &TestNode::got_server_mc_block_id_ext, blk_id, zstate_id, mode,
|
||||
f->version_, f->capabilities_, f->last_utime_, f->now_);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TestNode::got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid) {
|
||||
void TestNode::got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int created) {
|
||||
if (!zstate_id_.is_valid()) {
|
||||
zstate_id_ = zstateid;
|
||||
LOG(INFO) << "zerostate id set to " << zstate_id_.to_str();
|
||||
|
@ -304,9 +345,36 @@ void TestNode::got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt
|
|||
} else if (mc_last_id_.id.seqno < blkid.id.seqno) {
|
||||
mc_last_id_ = blkid;
|
||||
}
|
||||
td::TerminalIO::out() << "latest masterchain block known to server is " << blkid.to_str();
|
||||
if (created > 0) {
|
||||
td::TerminalIO::out() << " created at " << created << " (" << static_cast<td::int32>(td::Clocks::system()) - created
|
||||
<< " seconds ago)\n";
|
||||
} else {
|
||||
td::TerminalIO::out() << "\n";
|
||||
}
|
||||
show_new_blkids();
|
||||
}
|
||||
|
||||
void TestNode::got_server_mc_block_id_ext(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int mode, int version,
|
||||
long long capabilities, int last_utime, int server_now) {
|
||||
set_server_version(version, capabilities);
|
||||
set_server_time(server_now);
|
||||
if (last_utime > server_now) {
|
||||
LOG(WARNING) << "server claims to have a masterchain block " << blkid.to_str() << " created at " << last_utime
|
||||
<< " (" << last_utime - server_now << " seconds in the future)";
|
||||
} else if (last_utime < server_now - 60) {
|
||||
LOG(WARNING) << "server appears to be out of sync: its newest masterchain block is " << blkid.to_str()
|
||||
<< " created at " << last_utime << " (" << server_now - last_utime
|
||||
<< " seconds ago according to the server's clock)";
|
||||
} else if (last_utime < server_time_got_at_ - 60) {
|
||||
LOG(WARNING) << "either the server is out of sync, or the local clock is set incorrectly: the newest masterchain "
|
||||
"block known to server is "
|
||||
<< blkid.to_str() << " created at " << last_utime << " (" << server_now - server_time_got_at_
|
||||
<< " seconds ago according to the local clock)";
|
||||
}
|
||||
got_server_mc_block_id(blkid, zstateid, last_utime);
|
||||
}
|
||||
|
||||
bool TestNode::request_block(ton::BlockIdExt blkid) {
|
||||
auto b = ton::serialize_tl_object(
|
||||
ton::create_tl_object<ton::lite_api::liteServer_getBlock>(ton::create_tl_lite_block_id(blkid)), true);
|
||||
|
@ -423,8 +491,7 @@ td::Status TestNode::save_db_file(ton::FileHash file_hash, td::BufferSlice data)
|
|||
}
|
||||
|
||||
void TestNode::run_init_queries() {
|
||||
get_server_version();
|
||||
get_server_mc_block_id();
|
||||
get_server_version(0x100);
|
||||
}
|
||||
|
||||
std::string TestNode::get_word(char delim) {
|
||||
|
@ -2008,10 +2075,10 @@ bool TestNode::get_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mod
|
|||
}
|
||||
if (!(mode & 0x2000)) {
|
||||
LOG(INFO) << "got block proof request from " << from.to_str() << " to "
|
||||
<< ((mode & 1) ? to.to_str() : "last masterchain block") << " with mode=" << mode;
|
||||
<< ((mode & 1) ? to.to_str() : "last masterchain block") << " with mode=" << mode;
|
||||
} else {
|
||||
LOG(DEBUG) << "got block proof request from " << from.to_str() << " to "
|
||||
<< ((mode & 1) ? to.to_str() : "last masterchain block") << " with mode=" << mode;
|
||||
<< ((mode & 1) ? to.to_str() : "last masterchain block") << " with mode=" << mode;
|
||||
}
|
||||
if (!from.is_masterchain_ext()) {
|
||||
LOG(ERROR) << "source block " << from.to_str() << " is not a valid masterchain block id";
|
||||
|
@ -2112,22 +2179,36 @@ void TestNode::got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mod
|
|||
return;
|
||||
}
|
||||
auto chain = res.move_as_ok();
|
||||
if (chain->from != from) {
|
||||
LOG(ERROR) << "block proof chain starts from block " << chain->from.to_str() << ", not from requested block "
|
||||
<< from.to_str();
|
||||
return;
|
||||
}
|
||||
auto err = chain->validate();
|
||||
if (err.is_error()) {
|
||||
LOG(ERROR) << "block proof chain is invalid: " << err;
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "valid " << (chain->complete ? "" : "in") << "complete proof chain: last block is " << chain->to.to_str()
|
||||
<< ", last key block is " << (chain->has_key_block ? chain->key_blkid.to_str() : "(undefined)");
|
||||
// TODO: if `from` was a trusted key block, then mark `to` as a trusted key block, and update the known value of latest trusted key block if `to` is newer
|
||||
if (!chain->complete && (mode & 0x1000)) {
|
||||
LOG(INFO) << "valid " << (chain->complete ? "" : "in") << "complete proof chain: last block is "
|
||||
<< chain->to.to_str() << ", last key block is "
|
||||
<< (chain->has_key_block ? chain->key_blkid.to_str() : "(undefined)");
|
||||
get_block_proof(chain->to, to, mode | 0x2000);
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << "valid " << (chain->complete ? "" : "in") << "complete proof chain: last block is "
|
||||
<< chain->to.to_str() << ", last key block is "
|
||||
<< (chain->has_key_block ? chain->key_blkid.to_str() : "(undefined)") << std::endl;
|
||||
if (chain->has_key_block) {
|
||||
register_blkid(chain->key_blkid);
|
||||
}
|
||||
register_blkid(chain->to);
|
||||
auto now = static_cast<td::uint32>(td::Clocks::system());
|
||||
if (!(mode & 1) || (chain->last_utime > now - 3600)) {
|
||||
td::TerminalIO::out() << "last block in chain was generated at " << chain->last_utime << " ("
|
||||
<< now - chain->last_utime << " seconds ago)\n";
|
||||
}
|
||||
show_new_blkids();
|
||||
}
|
||||
|
||||
|
|
|
@ -94,9 +94,14 @@ class TestNode : public td::actor::Actor {
|
|||
|
||||
void run_init_queries();
|
||||
bool get_server_time();
|
||||
bool get_server_version();
|
||||
bool get_server_version(int mode = 0);
|
||||
void got_server_version(td::Result<td::BufferSlice> res, int mode);
|
||||
bool get_server_mc_block_id();
|
||||
void got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid);
|
||||
void got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int created_at);
|
||||
void got_server_mc_block_id_ext(ton::BlockIdExt blkid, ton::ZeroStateIdExt zstateid, int mode, int version,
|
||||
long long capabilities, int last_utime, int server_now);
|
||||
void set_server_version(td::int32 version, td::int64 capabilities);
|
||||
void set_server_time(int server_utime);
|
||||
bool request_block(ton::BlockIdExt blkid);
|
||||
bool request_state(ton::BlockIdExt blkid);
|
||||
void got_mc_block(ton::BlockIdExt blkid, td::BufferSlice data);
|
||||
|
|
|
@ -35,6 +35,11 @@ void OptionsParser::set_description(std::string description) {
|
|||
|
||||
void OptionsParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
for (auto &option : options_) {
|
||||
if (option.short_key == short_key || (!long_key.empty() && long_key == option.long_key)) {
|
||||
LOG(ERROR) << "Ignore duplicated option '" << short_key << "' '" << long_key << "'";
|
||||
}
|
||||
}
|
||||
options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ void Random::secure_bytes(unsigned char *ptr, size_t size) {
|
|||
buf_pos = buf_size;
|
||||
generation = 0;
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
td::MutableSlice(buf, buf_size).fill_zero_secure();
|
||||
buf_pos = buf_size;
|
||||
return;
|
||||
}
|
||||
if (generation != random_seed_generation.load(std::memory_order_relaxed)) {
|
||||
generation = random_seed_generation.load(std::memory_order_acquire);
|
||||
buf_pos = buf_size;
|
||||
|
@ -109,6 +114,10 @@ void Random::add_seed(Slice bytes, double entropy) {
|
|||
RAND_add(bytes.data(), static_cast<int>(bytes.size()), entropy);
|
||||
random_seed_generation++;
|
||||
}
|
||||
|
||||
void Random::secure_cleanup() {
|
||||
Random::secure_bytes(nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int rand_device_helper() {
|
||||
|
|
|
@ -35,6 +35,7 @@ class Random {
|
|||
|
||||
// works only for current thread
|
||||
static void add_seed(Slice bytes, double entropy = 0);
|
||||
static void secure_cleanup();
|
||||
#endif
|
||||
|
||||
static uint32 fast_uint32();
|
||||
|
|
|
@ -153,10 +153,11 @@ inline char to_lower(char c) {
|
|||
return c;
|
||||
}
|
||||
|
||||
inline void to_lower_inplace(MutableSlice slice) {
|
||||
inline MutableSlice to_lower_inplace(MutableSlice slice) {
|
||||
for (auto &c : slice) {
|
||||
c = to_lower(c);
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
inline string to_lower(Slice slice) {
|
||||
|
@ -321,8 +322,8 @@ string url_encode(Slice str);
|
|||
|
||||
namespace detail {
|
||||
template <class T, class U>
|
||||
struct is_same_signedness
|
||||
: public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {};
|
||||
struct is_same_signedness : public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {
|
||||
};
|
||||
|
||||
template <class T, class Enable = void>
|
||||
struct safe_undeflying_type {
|
||||
|
|
|
@ -27,6 +27,7 @@ liteServer.error code:int message:string = liteServer.Error;
|
|||
liteServer.accountId workchain:int id:int256 = liteServer.AccountId;
|
||||
|
||||
liteServer.masterchainInfo last:tonNode.blockIdExt state_root_hash:int256 init:tonNode.zeroStateIdExt = liteServer.MasterchainInfo;
|
||||
liteServer.masterchainInfoExt mode:# version:int capabilities:long last:tonNode.blockIdExt last_utime:int now:int state_root_hash:int256 init:tonNode.zeroStateIdExt = liteServer.MasterchainInfoExt;
|
||||
liteServer.currentTime now:int = liteServer.CurrentTime;
|
||||
liteServer.version mode:# version:int capabilities:long now:int = liteServer.Version;
|
||||
liteServer.blockData id:tonNode.blockIdExt data:bytes = liteServer.BlockData;
|
||||
|
@ -53,6 +54,7 @@ liteServer.debug.verbosity value:int = liteServer.debug.Verbosity;
|
|||
---functions---
|
||||
|
||||
liteServer.getMasterchainInfo = liteServer.MasterchainInfo;
|
||||
liteServer.getMasterchainInfoExt mode:# = liteServer.MasterchainInfoExt;
|
||||
liteServer.getTime = liteServer.CurrentTime;
|
||||
liteServer.getVersion = liteServer.Version;
|
||||
liteServer.getBlock id:tonNode.blockIdExt = liteServer.BlockData;
|
||||
|
|
Binary file not shown.
|
@ -24,6 +24,8 @@ exportedKey word_list:vector<secureString> = ExportedKey;
|
|||
exportedPemKey pem:secureString = ExportedPemKey;
|
||||
exportedEncryptedKey data:secureBytes = ExportedEncryptedKey;
|
||||
|
||||
bip39Hints words:vector<string> = Bip39Hints;
|
||||
|
||||
accountAddress account_address:string = AccountAddress;
|
||||
|
||||
internal.transactionId lt:int64 hash:bytes = internal.TransactionId;
|
||||
|
@ -56,7 +58,7 @@ close = Ok;
|
|||
|
||||
options.setConfig config:string = Ok;
|
||||
|
||||
createNewKey local_password:secureBytes mnemonic_password:secureBytes = Key;
|
||||
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
|
||||
deleteKey public_key:bytes = Ok;
|
||||
exportKey input_key:inputKey = ExportedKey;
|
||||
exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey;
|
||||
|
@ -66,6 +68,8 @@ importPemKey local_password:secureBytes key_password:secureBytes exported_key:ex
|
|||
importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key;
|
||||
changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key;
|
||||
|
||||
getBip39Hints prefix:string = Bip39Hints;
|
||||
|
||||
//raw.init initial_account_state:raw.initialAccountState = Ok;
|
||||
raw.getAccountAddress initital_account_state:raw.initialAccountState = AccountAddress;
|
||||
raw.getAccountState account_address:accountAddress = raw.AccountState;
|
||||
|
|
Binary file not shown.
|
@ -132,6 +132,12 @@ TEST(Tonlib, TestGiver) {
|
|||
vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr);
|
||||
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
|
||||
}
|
||||
TEST(Tonlib, PublicKey) {
|
||||
block::PublicKey::parse("pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").ensure_error();
|
||||
auto key = block::PublicKey::parse("Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").move_as_ok();
|
||||
CHECK(td::buffer_to_hex(key.key) == "3EE9DC0A7A0B6CA01770CE34698792BD8ECB53A6949BFD6C81B6E3CA475B74D7");
|
||||
CHECK(key.serialize() == "Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2");
|
||||
}
|
||||
|
||||
TEST(Tonlib, Address) {
|
||||
auto a = block::StdAddress::parse("-1:538fa7cc24ff8eaa101d84a5f1ab7e832fe1d84b309cdfef4ee94373aac80f7d").move_as_ok();
|
||||
|
@ -287,12 +293,14 @@ TEST(Tonlib, KeysApi) {
|
|||
auto local_password = td::SecureString("local password");
|
||||
auto mnemonic_password = td::SecureString("mnemonic password");
|
||||
{
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), td::SecureString{}))
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), td::SecureString{},
|
||||
td::SecureString{}))
|
||||
.move_as_ok();
|
||||
}
|
||||
|
||||
//createNewKey local_password:bytes mnemonic_password:bytes = Key;
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), mnemonic_password.copy()))
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(local_password.copy(), mnemonic_password.copy(),
|
||||
td::SecureString{}))
|
||||
.move_as_ok();
|
||||
|
||||
sync_send(client, make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
|
||||
|
|
|
@ -126,9 +126,9 @@ void transfer_grams(Client& client, std::string from, std::string to, td::int64
|
|||
}
|
||||
Wallet create_empty_wallet(Client& client) {
|
||||
using tonlib_api::make_object;
|
||||
auto key =
|
||||
sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString("mnemonic")))
|
||||
.move_as_ok();
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(td::SecureString("local"),
|
||||
td::SecureString("mnemonic"), td::SecureString()))
|
||||
.move_as_ok();
|
||||
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
|
||||
|
||||
auto account_address =
|
||||
|
@ -210,8 +210,8 @@ int main(int argc, char* argv[]) {
|
|||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, "."))).ensure();
|
||||
|
||||
auto key = sync_send(client,
|
||||
make_object<tonlib_api::createNewKey>(td::SecureString("local"), td::SecureString("mnemonic")))
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(
|
||||
td::SecureString("local"), td::SecureString("mnemonic"), td::SecureString()))
|
||||
.move_as_ok();
|
||||
|
||||
auto create_input_key = [&] {
|
||||
|
|
|
@ -63,9 +63,11 @@ td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_k
|
|||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Result<KeyStorage::Key> KeyStorage::create_new_key(td::Slice local_password, td::Slice mnemonic_password) {
|
||||
td::Result<KeyStorage::Key> KeyStorage::create_new_key(td::Slice local_password, td::Slice mnemonic_password,
|
||||
td::Slice entropy) {
|
||||
Mnemonic::Options create_options;
|
||||
create_options.password = td::SecureString(mnemonic_password);
|
||||
create_options.entropy = td::SecureString(entropy);
|
||||
TRY_RESULT(mnemonic, Mnemonic::create_new(std::move(create_options)));
|
||||
|
||||
return save_key(DecryptedKey(std::move(mnemonic)), local_password);
|
||||
|
|
|
@ -50,7 +50,7 @@ class KeyStorage {
|
|||
|
||||
td::Status set_directory(std::string directory);
|
||||
|
||||
td::Result<Key> create_new_key(td::Slice local_password, td::Slice key_password);
|
||||
td::Result<Key> create_new_key(td::Slice local_password, td::Slice key_password, td::Slice entropy);
|
||||
|
||||
td::Result<ExportedKey> export_key(InputKey input_key);
|
||||
td::Result<ExportedPemKey> export_pem_key(InputKey input_key, td::Slice key_password);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "tonlib/TestWallet.h"
|
||||
#include "tonlib/TestGiver.h"
|
||||
#include "tonlib/utils.h"
|
||||
#include "tonlib/keys/Mnemonic.h"
|
||||
|
||||
#include "auto/tl/tonlib_api.hpp"
|
||||
#include "block/block-auto.h"
|
||||
|
@ -350,6 +351,7 @@ bool TonlibClient::is_static_request(td::int32 id) {
|
|||
case tonlib_api::raw_getAccountAddress::ID:
|
||||
case tonlib_api::testWallet_getAccountAddress::ID:
|
||||
case tonlib_api::testGiver_getAccountAddress::ID:
|
||||
case tonlib_api::getBip39Hints::ID:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -405,6 +407,11 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
return tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize());
|
||||
}
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(tonlib_api::getBip39Hints& request) {
|
||||
return tonlib_api::make_object<tonlib_api::bip39Hints>(
|
||||
td::transform(Mnemonic::word_hints(td::trim(td::to_lower_inplace(request.prefix_))), [](auto& x) { return x; }));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::init& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
if (state_ != State::Uninited) {
|
||||
|
@ -900,8 +907,8 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
|
|||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request,
|
||||
td::Promise<object_ptr<tonlib_api::key>>&& promise) {
|
||||
TRY_RESULT(key,
|
||||
key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_)));
|
||||
TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
|
||||
std::move(request.random_extra_seed_)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key.public_key.as_slice().str(), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::raw_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testWallet_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testGiver_getAccountAddress& request);
|
||||
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::getBip39Hints& request);
|
||||
template <class T, class P>
|
||||
td::Status do_request(const T& request, P&& promise) {
|
||||
return td::Status::Error(400, "Function is unsupported");
|
||||
|
|
|
@ -28,9 +28,12 @@
|
|||
#include "td/utils/Span.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/optional.h"
|
||||
#include "td/utils/Timer.h"
|
||||
|
||||
#include "crypto/Ed25519.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace tonlib {
|
||||
td::Result<Mnemonic> Mnemonic::create(td::SecureString words, td::SecureString password) {
|
||||
return create_from_normalized(normalize_and_split(std::move(words)), std::move(password));
|
||||
|
@ -133,7 +136,28 @@ td::SecureString Mnemonic::join(td::Span<td::SecureString> words) {
|
|||
return res;
|
||||
}
|
||||
|
||||
td::Span<std::string> Mnemonic::word_hints(td::Slice prefix) {
|
||||
static std::vector<std::string> words = [] {
|
||||
auto bip_words = Mnemonic::normalize_and_split(td::SecureString(bip39_english()));
|
||||
std::vector<std::string> res;
|
||||
for (auto &word : bip_words) {
|
||||
res.push_back(word.as_slice().str());
|
||||
}
|
||||
return res;
|
||||
}();
|
||||
if (prefix.empty()) {
|
||||
return words;
|
||||
}
|
||||
|
||||
auto p = std::equal_range(words.begin(), words.end(), prefix, [&](td::Slice a, td::Slice b) {
|
||||
return a.truncate(prefix.size()) < b.truncate(prefix.size());
|
||||
});
|
||||
|
||||
return td::Span<std::string>(&*p.first, p.second - p.first);
|
||||
}
|
||||
|
||||
td::Result<Mnemonic> Mnemonic::create_new(Options options) {
|
||||
td::Timer timer;
|
||||
if (options.words_count == 0) {
|
||||
options.words_count = 24;
|
||||
}
|
||||
|
@ -146,14 +170,28 @@ td::Result<Mnemonic> Mnemonic::create_new(Options options) {
|
|||
max_iterations *= 256;
|
||||
}
|
||||
|
||||
td::Random::add_seed(options.entropy.as_slice());
|
||||
SCOPE_EXIT {
|
||||
td::Random::secure_cleanup();
|
||||
};
|
||||
|
||||
auto bip_words = Mnemonic::normalize_and_split(td::SecureString(bip39_english()));
|
||||
CHECK(bip_words.size() == 2048);
|
||||
|
||||
int A = 0, B = 0, C = 0;
|
||||
for (int iteration = 0; iteration < max_iterations; iteration++) {
|
||||
std::vector<td::SecureString> words;
|
||||
td::SecureString rnd((options.words_count * 11 + 7) / 8);
|
||||
td::Random::secure_bytes(rnd.as_mutable_slice());
|
||||
for (int i = 0; i < options.words_count; i++) {
|
||||
words.push_back(bip_words[td::Random::secure_int32() & 2047].copy());
|
||||
size_t word_i = 0;
|
||||
for (size_t j = 0; j < 11; j++) {
|
||||
size_t offset = i * 11 + j;
|
||||
if ((rnd[offset / 8] & (1 << (offset & 7))) != 0) {
|
||||
word_i |= 1 << j;
|
||||
}
|
||||
}
|
||||
words.push_back(bip_words[word_i].copy());
|
||||
}
|
||||
|
||||
bool has_password = !options.password.empty();
|
||||
|
@ -180,7 +218,7 @@ td::Result<Mnemonic> Mnemonic::create_new(Options options) {
|
|||
continue;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Mnemonic generation debug stats: " << A << " " << B << " " << C;
|
||||
LOG(INFO) << "Mnemonic generation debug stats: " << A << " " << B << " " << C << " " << timer;
|
||||
return std::move(mnemonic);
|
||||
}
|
||||
return td::Status::Error("Failed to create a mnemonic (should not happen)");
|
||||
|
|
|
@ -36,6 +36,7 @@ class Mnemonic {
|
|||
}
|
||||
int words_count = 24;
|
||||
td::SecureString password;
|
||||
td::SecureString entropy;
|
||||
};
|
||||
static td::Result<Mnemonic> create_new(Options options = {});
|
||||
|
||||
|
@ -51,6 +52,7 @@ class Mnemonic {
|
|||
std::vector<td::SecureString> get_words() const;
|
||||
|
||||
static std::vector<td::SecureString> normalize_and_split(td::SecureString words);
|
||||
static td::Span<std::string> word_hints(td::Slice prefix);
|
||||
|
||||
private:
|
||||
std::vector<td::SecureString> words_;
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Span.h"
|
||||
|
||||
namespace tonlib {
|
||||
td::CSlice bip39_english();
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -152,6 +152,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
transfer(from, to, grams);
|
||||
} else if (cmd == "hint") {
|
||||
get_hints(parser.read_word());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,21 +193,27 @@ class TonlibCli : public td::actor::Actor {
|
|||
};
|
||||
}
|
||||
|
||||
void generate_key(std::string entropy = "") {
|
||||
void generate_key(td::SecureString entropy = {}) {
|
||||
if (entropy.size() < 20) {
|
||||
td::TerminalIO::out() << "Enter some entropy";
|
||||
cont_ = [this, entropy](td::Slice new_entropy) { generate_key(entropy + new_entropy.str()); };
|
||||
cont_ = [this, entropy = std::move(entropy)](td::Slice new_entropy) {
|
||||
td::SecureString res(entropy.size() + new_entropy.size());
|
||||
res.as_mutable_slice().copy_from(entropy.as_slice());
|
||||
res.as_mutable_slice().substr(entropy.size()).copy_from(new_entropy);
|
||||
generate_key(std::move(res));
|
||||
};
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << "Enter password (could be empty)";
|
||||
cont_ = [this, entropy](td::Slice password) { generate_key(std::move(entropy), td::SecureString(password)); };
|
||||
cont_ = [this, entropy = std::move(entropy)](td::Slice password) mutable {
|
||||
generate_key(std::move(entropy), td::SecureString(password));
|
||||
};
|
||||
}
|
||||
|
||||
void generate_key(std::string entropy, td::SecureString password) {
|
||||
//TODO: use entropy
|
||||
void generate_key(td::SecureString entropy, td::SecureString password) {
|
||||
auto password_copy = password.copy();
|
||||
send_query(tonlib_api::make_object<tonlib_api::createNewKey>(std::move(password_copy),
|
||||
td::SecureString() /*mnemonic password*/),
|
||||
send_query(tonlib_api::make_object<tonlib_api::createNewKey>(
|
||||
std::move(password_copy), td::SecureString() /*mnemonic password*/, std::move(entropy)),
|
||||
[this, password = std::move(password)](auto r_key) mutable {
|
||||
if (r_key.is_error()) {
|
||||
LOG(ERROR) << "Failed to create new key: " << r_key.error();
|
||||
|
@ -542,6 +550,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
}
|
||||
|
||||
void get_hints(td::Slice prefix) {
|
||||
using tonlib_api::make_object;
|
||||
auto obj = tonlib::TonlibClient::static_request(make_object<tonlib_api::getBip39Hints>(prefix.str()));
|
||||
if (obj->get_id() == tonlib_api::error::ID) {
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(obj);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
|
|
@ -1443,19 +1443,32 @@ bool Collator::fetch_config_params() {
|
|||
{
|
||||
// compute compute_phase_cfg / storage_phase_cfg
|
||||
auto cell = config_->get_config_param(is_masterchain() ? 20 : 21);
|
||||
block::gen::GasLimitsPrices::Record rec;
|
||||
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
|
||||
if (cell.is_null()) {
|
||||
return fatal_error("cannot fetch current gas prices and limits from masterchain configuration");
|
||||
}
|
||||
compute_phase_cfg_.gas_limit = rec.gas_limit;
|
||||
compute_phase_cfg_.gas_credit = rec.gas_credit;
|
||||
compute_phase_cfg_.gas_price = rec.gas_price;
|
||||
auto f = [self = this](const auto& r, td::uint64 spec_limit) {
|
||||
self->compute_phase_cfg_.gas_limit = r.gas_limit;
|
||||
self->compute_phase_cfg_.special_gas_limit = spec_limit;
|
||||
self->compute_phase_cfg_.gas_credit = r.gas_credit;
|
||||
self->compute_phase_cfg_.gas_price = r.gas_price;
|
||||
self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
|
||||
self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit};
|
||||
};
|
||||
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
|
||||
if (tlb::unpack_cell(cell, rec)) {
|
||||
f(rec, rec.special_gas_limit);
|
||||
} else {
|
||||
block::gen::GasLimitsPrices::Record_gas_prices rec0;
|
||||
if (tlb::unpack_cell(std::move(cell), rec0)) {
|
||||
f(rec0, rec0.gas_limit);
|
||||
} else {
|
||||
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
|
||||
}
|
||||
}
|
||||
compute_phase_cfg_.compute_threshold();
|
||||
compute_phase_cfg_.block_rand_seed = rand_seed_;
|
||||
compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
|
||||
compute_phase_cfg_.global_config = config_->get_root_cell();
|
||||
storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, rec.freeze_due_limit};
|
||||
storage_phase_cfg_.delete_due_limit = td::RefInt256{true, rec.delete_due_limit};
|
||||
}
|
||||
{
|
||||
// compute action_phase_cfg
|
||||
|
|
|
@ -118,7 +118,10 @@ void LiteQuery::start_up() {
|
|||
td::overloaded(
|
||||
[&](lite_api::liteServer_getTime& q) { this->perform_getTime(); },
|
||||
[&](lite_api::liteServer_getVersion& q) { this->perform_getVersion(); },
|
||||
[&](lite_api::liteServer_getMasterchainInfo& q) { this->perform_getMasterchainInfo(); },
|
||||
[&](lite_api::liteServer_getMasterchainInfo& q) { this->perform_getMasterchainInfo(-1); },
|
||||
[&](lite_api::liteServer_getMasterchainInfoExt& q) {
|
||||
this->perform_getMasterchainInfo(q.mode_ & 0x7fffffff);
|
||||
},
|
||||
[&](lite_api::liteServer_getBlock& q) { this->perform_getBlock(ton::create_block_id(q.id_)); },
|
||||
[&](lite_api::liteServer_getBlockHeader& q) {
|
||||
this->perform_getBlockHeader(ton::create_block_id(q.id_), q.mode_);
|
||||
|
@ -181,22 +184,27 @@ void LiteQuery::perform_getVersion() {
|
|||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::perform_getMasterchainInfo() {
|
||||
LOG(INFO) << "started a getMasterchainInfo() liteserver query";
|
||||
void LiteQuery::perform_getMasterchainInfo(int mode) {
|
||||
LOG(INFO) << "started a getMasterchainInfo(" << mode << ") liteserver query";
|
||||
if (mode > 0) {
|
||||
fatal_error("unsupported getMasterchainInfo mode");
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
|
||||
[Self = actor_id(this)](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res)->void {
|
||||
[ Self = actor_id(this), mode ](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
auto pair = res.move_as_ok();
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getMasterchainInfo, std::move(pair.first),
|
||||
pair.second);
|
||||
pair.second, mode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getMasterchainInfo(Ref<ton::validator::MasterchainState> mc_state, BlockIdExt blkid) {
|
||||
void LiteQuery::continue_getMasterchainInfo(Ref<ton::validator::MasterchainState> mc_state, BlockIdExt blkid,
|
||||
int mode) {
|
||||
LOG(INFO) << "obtained data for getMasterchainInfo() : last block = " << blkid.to_str();
|
||||
auto mc_state_q = Ref<ton::validator::MasterchainStateQ>(std::move(mc_state));
|
||||
if (mc_state_q.is_null()) {
|
||||
|
@ -206,8 +214,12 @@ void LiteQuery::continue_getMasterchainInfo(Ref<ton::validator::MasterchainState
|
|||
auto zerostate_id = mc_state_q->get_zerostate_id();
|
||||
auto zs_tl = create_tl_object<lite_api::tonNode_zeroStateIdExt>(zerostate_id.workchain, zerostate_id.root_hash,
|
||||
zerostate_id.file_hash);
|
||||
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_masterchainInfo>(
|
||||
ton::create_tl_lite_block_id(blkid), mc_state_q->root_hash(), std::move(zs_tl));
|
||||
td::int32 now = static_cast<td::int32>(std::time(nullptr));
|
||||
auto b = (mode == -1) ? ton::create_serialize_tl_object<ton::lite_api::liteServer_masterchainInfo>(
|
||||
ton::create_tl_lite_block_id(blkid), mc_state_q->root_hash(), std::move(zs_tl))
|
||||
: ton::create_serialize_tl_object<ton::lite_api::liteServer_masterchainInfoExt>(
|
||||
mode, ls_version, ls_capabilities, ton::create_tl_lite_block_id(blkid),
|
||||
mc_state_q->get_unix_time(), now, mc_state_q->root_hash(), std::move(zs_tl));
|
||||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,11 @@ class LiteQuery : public td::actor::Actor {
|
|||
std::unique_ptr<block::BlockProofChain> chain_;
|
||||
|
||||
public:
|
||||
enum { default_timeout_msec = 4500 }; // 4.5 seconds
|
||||
enum { ls_version = 0x101, ls_capabilities = 1 }; // version 1.1; +1 = build block proof chains
|
||||
enum { default_timeout_msec = 4500 }; // 4.5 seconds
|
||||
enum {
|
||||
ls_version = 0x101,
|
||||
ls_capabilities = 3
|
||||
}; // version 1.1; +1 = build block proof chains, +2 = masterchainInfoExt
|
||||
LiteQuery(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
static void run_query(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
|
||||
|
@ -75,8 +78,8 @@ class LiteQuery : public td::actor::Actor {
|
|||
void start_up() override;
|
||||
void perform_getTime();
|
||||
void perform_getVersion();
|
||||
void perform_getMasterchainInfo();
|
||||
void continue_getMasterchainInfo(Ref<MasterchainState> mc_state, BlockIdExt blkid);
|
||||
void perform_getMasterchainInfo(int mode);
|
||||
void continue_getMasterchainInfo(Ref<MasterchainState> mc_state, BlockIdExt blkid, int mode);
|
||||
void perform_getBlock(BlockIdExt blkid);
|
||||
void continue_getBlock(BlockIdExt blkid, Ref<BlockData> block);
|
||||
void perform_getBlockHeader(BlockIdExt blkid, int mode);
|
||||
|
|
|
@ -255,7 +255,7 @@ void ValidateQuery::start_up() {
|
|||
LOG(DEBUG) << "sending wait_block_state() query #" << i << " for " << prev_blocks[i].to_str() << " to Manager";
|
||||
++pending;
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, prev_blocks[i], priority(),
|
||||
timeout, [self = get_self(), i](td::Result<Ref<ShardState>> res) -> void {
|
||||
timeout, [ self = get_self(), i ](td::Result<Ref<ShardState>> res)->void {
|
||||
LOG(DEBUG) << "got answer to wait_block_state_short query #" << i;
|
||||
td::actor::send_closure_later(
|
||||
std::move(self), &ValidateQuery::after_get_shard_state, i, std::move(res));
|
||||
|
@ -269,16 +269,16 @@ void ValidateQuery::start_up() {
|
|||
// 5. request masterchain state referred to in the block
|
||||
if (!is_masterchain()) {
|
||||
++pending;
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(), timeout,
|
||||
[self = get_self()](td::Result<Ref<ShardState>> res) {
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(),
|
||||
timeout, [self = get_self()](td::Result<Ref<ShardState>> res) {
|
||||
LOG(DEBUG) << "got answer to wait_block_state() query for masterchain block";
|
||||
td::actor::send_closure_later(std::move(self), &ValidateQuery::after_get_mc_state,
|
||||
std::move(res));
|
||||
});
|
||||
// 5.1. request corresponding block handle
|
||||
++pending;
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_handle, mc_blkid_, true,
|
||||
[self = get_self()](td::Result<BlockHandle> res) {
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_handle, mc_blkid_,
|
||||
true, [self = get_self()](td::Result<BlockHandle> res) {
|
||||
LOG(DEBUG) << "got answer to get_block_handle() query for masterchain block";
|
||||
td::actor::send_closure_later(std::move(self), &ValidateQuery::got_mc_handle,
|
||||
std::move(res));
|
||||
|
@ -722,19 +722,32 @@ bool ValidateQuery::fetch_config_params() {
|
|||
{
|
||||
// compute compute_phase_cfg / storage_phase_cfg
|
||||
auto cell = config_->get_config_param(is_masterchain() ? 20 : 21);
|
||||
block::gen::GasLimitsPrices::Record rec;
|
||||
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
|
||||
if (cell.is_null()) {
|
||||
return fatal_error("cannot fetch current gas prices and limits from masterchain configuration");
|
||||
}
|
||||
compute_phase_cfg_.gas_limit = rec.gas_limit;
|
||||
compute_phase_cfg_.gas_credit = rec.gas_credit;
|
||||
compute_phase_cfg_.gas_price = rec.gas_price;
|
||||
auto f = [self = this](const auto& r, td::uint64 spec_limit) {
|
||||
self->compute_phase_cfg_.gas_limit = r.gas_limit;
|
||||
self->compute_phase_cfg_.special_gas_limit = spec_limit;
|
||||
self->compute_phase_cfg_.gas_credit = r.gas_credit;
|
||||
self->compute_phase_cfg_.gas_price = r.gas_price;
|
||||
self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
|
||||
self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit};
|
||||
};
|
||||
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
|
||||
if (tlb::unpack_cell(cell, rec)) {
|
||||
f(rec, rec.special_gas_limit);
|
||||
} else {
|
||||
block::gen::GasLimitsPrices::Record_gas_prices rec0;
|
||||
if (tlb::unpack_cell(std::move(cell), rec0)) {
|
||||
f(rec0, rec0.gas_limit);
|
||||
} else {
|
||||
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
|
||||
}
|
||||
}
|
||||
compute_phase_cfg_.compute_threshold();
|
||||
compute_phase_cfg_.block_rand_seed = rand_seed_;
|
||||
compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
|
||||
compute_phase_cfg_.global_config = config_->get_root_cell();
|
||||
storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, rec.freeze_due_limit};
|
||||
storage_phase_cfg_.delete_due_limit = td::RefInt256{true, rec.delete_due_limit};
|
||||
}
|
||||
{
|
||||
// compute action_phase_cfg
|
||||
|
@ -1167,7 +1180,7 @@ bool ValidateQuery::request_neighbor_queues() {
|
|||
LOG(DEBUG) << "neighbor #" << i << " : " << descr.blk_.to_str();
|
||||
++pending;
|
||||
send_closure_later(manager, &ValidatorManager::wait_block_message_queue_short, descr.blk_, priority(), timeout,
|
||||
[self = get_self(), i](td::Result<Ref<MessageQueue>> res) {
|
||||
[ self = get_self(), i ](td::Result<Ref<MessageQueue>> res) {
|
||||
td::actor::send_closure(std::move(self), &ValidateQuery::got_neighbor_out_queue, i,
|
||||
std::move(res));
|
||||
});
|
||||
|
@ -1285,13 +1298,12 @@ bool ValidateQuery::request_aux_mc_state(BlockSeqno seqno, Ref<MasterchainStateQ
|
|||
CHECK(blkid.is_valid_ext() && blkid.is_masterchain());
|
||||
LOG(DEBUG) << "sending auxiliary wait_block_state() query for " << blkid.to_str() << " to Manager";
|
||||
++pending;
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, blkid, priority(), timeout,
|
||||
[self = get_self(), blkid](td::Result<Ref<ShardState>> res) {
|
||||
LOG(DEBUG) << "got answer to wait_block_state query for " << blkid.to_str();
|
||||
td::actor::send_closure_later(std::move(self),
|
||||
&ValidateQuery::after_get_aux_shard_state, blkid,
|
||||
std::move(res));
|
||||
});
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, blkid, priority(), timeout, [
|
||||
self = get_self(), blkid
|
||||
](td::Result<Ref<ShardState>> res) {
|
||||
LOG(DEBUG) << "got answer to wait_block_state query for " << blkid.to_str();
|
||||
td::actor::send_closure_later(std::move(self), &ValidateQuery::after_get_aux_shard_state, blkid, std::move(res));
|
||||
});
|
||||
state.clear();
|
||||
return true;
|
||||
}
|
||||
|
@ -1627,8 +1639,8 @@ bool ValidateQuery::check_shard_layout() {
|
|||
WorkchainId wc_id{ton::workchainInvalid};
|
||||
Ref<block::WorkchainInfo> wc_info;
|
||||
|
||||
if (!new_shard_conf_->process_sibling_shard_hashes([self = this, &wc_set, &wc_id, &wc_info, &ccvc](
|
||||
block::McShardHash& cur, const block::McShardHash* sibling) {
|
||||
if (!new_shard_conf_->process_sibling_shard_hashes([ self = this, &wc_set, &wc_id, &wc_info, &ccvc ](
|
||||
block::McShardHash & cur, const block::McShardHash* sibling) {
|
||||
if (!cur.is_valid()) {
|
||||
return -2;
|
||||
}
|
||||
|
@ -4619,8 +4631,8 @@ bool ValidateQuery::check_one_library_update(td::ConstBitPtr key, Ref<vm::CellSl
|
|||
old_publishers = std::make_unique<vm::Dictionary>(256);
|
||||
}
|
||||
if (!old_publishers->scan_diff(*new_publishers,
|
||||
[this, lib_key = key](td::ConstBitPtr key, int key_len, Ref<vm::CellSlice> old_val,
|
||||
Ref<vm::CellSlice> new_val) {
|
||||
[ this, lib_key = key ](td::ConstBitPtr key, int key_len, Ref<vm::CellSlice> old_val,
|
||||
Ref<vm::CellSlice> new_val) {
|
||||
CHECK(key_len == 256);
|
||||
if (old_val.not_null() && !old_val->empty_ext()) {
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue