Skip to content

Commit fd7a8de

Browse files
author
ton
committed
updated tonlib, block routing
- upated tonlib - fixed bug in message routing
1 parent ac3eb1a commit fd7a8de

File tree

33 files changed

+1003
-382
lines changed

33 files changed

+1003
-382
lines changed

crypto/block/block.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,9 @@ bool MsgProcessedUpto::already_processed(const EnqueuedMsgDescr& msg) const {
552552
if (msg.lt_ == last_inmsg_lt && last_inmsg_hash < msg.hash_) {
553553
return false;
554554
}
555-
if (ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
555+
if (msg.same_workchain() && ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
556+
// this branch is needed only for messages generated in the same shard
557+
// (such messages could have been processed without a reference from the masterchain)
556558
// ? enable this branch only if an extra boolean parameter is set ?
557559
return true;
558560
}

crypto/block/block.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ struct EnqueuedMsgDescr {
145145
return false;
146146
}
147147
bool unpack(vm::CellSlice& cs);
148+
bool same_workchain() const {
149+
return cur_prefix_.workchain == next_prefix_.workchain;
150+
}
148151
};
149152

150153
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;

crypto/block/check-proof.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace block {
3232
using namespace std::literals::string_literals;
3333

3434
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to,
35-
bool check_state_hash) {
35+
bool check_state_hash, td::uint32* save_utime) {
3636
ton::RootHash vhash{root->get_hash().bits()};
3737
if (vhash != blkid.root_hash) {
3838
return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash "
@@ -47,6 +47,9 @@ td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blki
4747
if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) {
4848
return td::Status::Error(std::string{"cannot unpack header for block "} + blkid.to_str());
4949
}
50+
if (save_utime) {
51+
*save_utime = info.gen_utime;
52+
}
5053
if (store_shard_hash_to) {
5154
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
5255
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
@@ -153,7 +156,8 @@ td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td:
153156
}
154157

155158
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
156-
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash) {
159+
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash,
160+
td::uint32* save_utime) {
157161
TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof");
158162
if (Q_roots.size() != 2) {
159163
return td::Status::Error(PSLICE() << "account state proof must have exactly two roots");
@@ -169,9 +173,9 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const
169173
return td::Status::Error("account state proof is invalid");
170174
}
171175
ton::Bits256 state_hash = state_root->get_hash().bits();
172-
TRY_STATUS_PREFIX(
173-
check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, &state_hash, true),
174-
"error in account shard block header proof : ");
176+
TRY_STATUS_PREFIX(check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk,
177+
&state_hash, true, save_utime),
178+
"error in account shard block header proof : ");
175179
block::gen::ShardStateUnsplit::Record sstate;
176180
if (!(tlb::unpack_cell(std::move(state_root), sstate))) {
177181
return td::Status::Error("cannot unpack state header");
@@ -233,8 +237,8 @@ td::Result<AccountState::Info> AccountState::validate(ton::BlockIdExt ref_blk, b
233237
TRY_STATUS(block::check_shard_proof(blk, shard_blk, shard_proof.as_slice()));
234238

235239
Info res;
236-
TRY_STATUS(
237-
block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, &res.last_trans_hash));
240+
TRY_STATUS(block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt,
241+
&res.last_trans_hash, &res.gen_utime));
238242
res.root = std::move(root);
239243

240244
return res;

crypto/block/check-proof.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ namespace block {
2525
using td::Ref;
2626

2727
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid,
28-
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false);
28+
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false,
29+
td::uint32* save_utime = nullptr);
2930
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof);
3031
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
3132
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt = nullptr,
32-
ton::Bits256* last_trans_hash = nullptr);
33+
ton::Bits256* last_trans_hash = nullptr, td::uint32* save_utime = nullptr);
3334
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof);
3435
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data);
3536

@@ -47,6 +48,7 @@ struct AccountState {
4748
td::Ref<vm::Cell> root;
4849
ton::LogicalTime last_trans_lt = 0;
4950
ton::Bits256 last_trans_hash;
51+
td::uint32 gen_utime{0};
5052
};
5153

5254
td::Result<Info> validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const;

crypto/block/transaction.cpp

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,12 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
10271027
break;
10281028
case block::gen::OutAction::action_send_msg:
10291029
err_code = try_action_send_msg(cs, ap, cfg);
1030+
if (err_code == -2) {
1031+
err_code = try_action_send_msg(cs, ap, cfg, 1);
1032+
if (err_code == -2) {
1033+
err_code = try_action_send_msg(cs, ap, cfg, 2);
1034+
}
1035+
}
10301036
break;
10311037
case block::gen::OutAction::action_reserve_currency:
10321038
err_code = try_action_reserve_currency(cs, ap, cfg);
@@ -1240,22 +1246,60 @@ bool Transaction::check_rewrite_dest_addr(Ref<vm::CellSlice>& dest_addr, const A
12401246
return true;
12411247
}
12421248

1243-
int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) {
1249+
int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, const ActionPhaseConfig& cfg,
1250+
int redoing) {
12441251
block::gen::OutAction::Record_action_send_msg act_rec;
12451252
// mode: +128 = attach all remaining balance, +64 = attach all remaining balance of the inbound message, +1 = pay message fees, +2 = skip if message cannot be sent
1253+
vm::CellSlice cs{cs0};
12461254
if (!tlb::unpack_exact(cs, act_rec) || (act_rec.mode & ~0xc3) || (act_rec.mode & 0xc0) == 0xc0) {
12471255
return -1;
12481256
}
12491257
bool skip_invalid = (act_rec.mode & 2);
1250-
auto cs2 = vm::load_cell_slice(act_rec.out_msg);
1251-
// try to parse suggested message in cs2
1258+
// try to parse suggested message in act_rec.out_msg
12521259
td::RefInt256 fwd_fee, ihr_fee;
1253-
bool ext_msg = cs2.prefetch_ulong(1);
1260+
block::gen::MessageRelaxed::Record msg;
1261+
if (!tlb::type_unpack_cell(act_rec.out_msg, block::gen::t_MessageRelaxed_Any, msg)) {
1262+
return -1;
1263+
}
1264+
if (redoing >= 1) {
1265+
if (msg.init->size_refs() >= 2) {
1266+
LOG(DEBUG) << "moving the StateInit of a suggested outbound message into a separate cell";
1267+
// init:(Maybe (Either StateInit ^StateInit))
1268+
// transform (just (left z:StateInit)) into (just (right z:^StateInit))
1269+
CHECK(msg.init.write().fetch_ulong(2) == 2);
1270+
vm::CellBuilder cb;
1271+
Ref<vm::Cell> cell;
1272+
CHECK(cb.append_cellslice_bool(std::move(msg.init)) // StateInit
1273+
&& cb.finalize_to(cell) // -> ^StateInit
1274+
&& cb.store_long_bool(3, 2) // (just (right ... ))
1275+
&& cb.store_ref_bool(std::move(cell)) // z:^StateInit
1276+
&& cb.finalize_to(cell));
1277+
msg.init = vm::load_cell_slice_ref(std::move(cell));
1278+
} else {
1279+
redoing = 2;
1280+
}
1281+
}
1282+
if (redoing >= 2 && msg.body->size_ext() > 1 && msg.body->prefetch_ulong(1) == 0) {
1283+
LOG(DEBUG) << "moving the body of a suggested outbound message into a separate cell";
1284+
// body:(Either X ^X)
1285+
// transform (left x:X) into (right x:^X)
1286+
CHECK(msg.body.write().fetch_ulong(1) == 0);
1287+
vm::CellBuilder cb;
1288+
Ref<vm::Cell> cell;
1289+
CHECK(cb.append_cellslice_bool(std::move(msg.body)) // X
1290+
&& cb.finalize_to(cell) // -> ^X
1291+
&& cb.store_long_bool(1, 1) // (right ... )
1292+
&& cb.store_ref_bool(std::move(cell)) // x:^X
1293+
&& cb.finalize_to(cell));
1294+
msg.body = vm::load_cell_slice_ref(std::move(cell));
1295+
}
1296+
12541297
block::gen::CommonMsgInfoRelaxed::Record_int_msg_info info;
1298+
bool ext_msg = msg.info->prefetch_ulong(1);
12551299
if (ext_msg) {
12561300
// ext_out_msg_info$11 constructor of CommonMsgInfoRelaxed
12571301
block::gen::CommonMsgInfoRelaxed::Record_ext_out_msg_info erec;
1258-
if (!tlb::unpack(cs2, erec)) {
1302+
if (!tlb::csr_unpack(msg.info, erec)) {
12591303
return -1;
12601304
}
12611305
info.src = std::move(erec.src);
@@ -1267,7 +1311,7 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A
12671311
fwd_fee = ihr_fee = td::RefInt256{true, 0};
12681312
} else {
12691313
// int_msg_info$0 constructor
1270-
if (!tlb::unpack(cs2, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) {
1314+
if (!tlb::csr_unpack(msg.info, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) {
12711315
return -1;
12721316
}
12731317
fwd_fee = block::tlb::t_Grams.as_integer(info.fwd_fee);
@@ -1295,12 +1339,11 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A
12951339
// compute size of message
12961340
vm::CellStorageStat sstat; // for message size
12971341
// preliminary storage estimation of the resulting message
1298-
sstat.compute_used_storage(cs2); // message body
1342+
sstat.add_used_storage(msg.init, true, 3); // message init
1343+
sstat.add_used_storage(msg.body, true, 3); // message body (the root cell itself is not counted)
12991344
if (!ext_msg) {
13001345
sstat.add_used_storage(info.value->prefetch_ref());
13011346
}
1302-
sstat.bits -= cs2.size(); // bits in the root cells are free
1303-
sstat.cells--; // the root cell itself is not counted as a cell
13041347
LOG(DEBUG) << "storage paid for a message: " << sstat.cells << " cells, " << sstat.bits << " bits";
13051348
if (sstat.bits > max_msg_bits || sstat.cells > max_msg_cells) {
13061349
LOG(DEBUG) << "message too large, invalid";
@@ -1397,17 +1440,15 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A
13971440

13981441
// re-pack message value
13991442
CHECK(req.pack_to(info.value));
1400-
vm::CellBuilder cb;
1401-
CHECK(block::tlb::t_Grams.store_integer_ref(cb, fwd_fee_remain) &&
1402-
(info.fwd_fee = load_cell_slice_ref(cb.finalize())).not_null());
1403-
CHECK(block::tlb::t_Grams.store_integer_ref(cb, ihr_fee) &&
1404-
(info.ihr_fee = load_cell_slice_ref(cb.finalize())).not_null());
1443+
CHECK(block::tlb::t_Grams.pack_integer(info.fwd_fee, fwd_fee_remain));
1444+
CHECK(block::tlb::t_Grams.pack_integer(info.ihr_fee, ihr_fee));
14051445

14061446
// serialize message
1407-
CHECK(tlb::pack(cb, info));
1408-
if (!cb.append_cellslice_bool(cs2)) {
1447+
CHECK(tlb::csr_pack(msg.info, info));
1448+
vm::CellBuilder cb;
1449+
if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) {
14091450
LOG(DEBUG) << "outbound message does not fit into a cell after rewriting";
1410-
return 39;
1451+
return redoing < 2 ? -2 : (skip_invalid ? 0 : 39);
14111452
}
14121453

14131454
new_msg_bits = cb.size();
@@ -1438,11 +1479,11 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A
14381479
erec.dest = info.dest;
14391480
erec.created_at = info.created_at;
14401481
erec.created_lt = info.created_lt;
1482+
CHECK(tlb::csr_pack(msg.info, erec));
14411483
vm::CellBuilder cb;
1442-
CHECK(tlb::pack(cb, erec));
1443-
if (!cb.append_cellslice_bool(cs2)) {
1484+
if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) {
14441485
LOG(DEBUG) << "outbound message does not fit into a cell after rewriting";
1445-
return 39;
1486+
return redoing < 2 ? -2 : (skip_invalid ? 0 : 39);
14461487
}
14471488

14481489
new_msg_bits = cb.size();
@@ -1461,6 +1502,8 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A
14611502
}
14621503
if (!block::gen::t_Message_Any.validate_ref(new_msg)) {
14631504
LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to automated check";
1505+
block::gen::t_Message_Any.print_ref(std::cerr, new_msg);
1506+
vm::load_cell_slice(new_msg).print_rec(std::cerr);
14641507
return -1;
14651508
}
14661509
if (verbosity > 2) {

crypto/block/transaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ struct Transaction {
390390
Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
391391
bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
392392
int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
393-
int try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
393+
int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0);
394394
int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
395395
bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
396396
bool check_rewrite_dest_addr(Ref<vm::CellSlice>& dest_addr, const ActionPhaseConfig& cfg,

crypto/fift/IntCtx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct IntCtx {
7777
vm::TonDb* ton_db{nullptr};
7878
Dictionary* dictionary{nullptr};
7979
SourceLookup* source_lookup{nullptr};
80+
int* now{nullptr};
8081

8182
private:
8283
std::string str;

crypto/fift/SourceLookup.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <iostream>
2121

2222
#include "td/utils/Status.h"
23+
#include "td/utils/Time.h"
2324

2425
namespace fift {
2526
class FileLoader {
@@ -43,10 +44,21 @@ class OsFileLoader : public FileLoader {
4344
bool is_file_exists(td::CSlice filename) override;
4445
};
4546

47+
class OsTime {
48+
public:
49+
virtual ~OsTime() = default;
50+
virtual td::uint32 now() = 0;
51+
};
52+
53+
//TODO: rename SourceLookup
4654
class SourceLookup {
4755
public:
4856
SourceLookup() = default;
49-
explicit SourceLookup(std::unique_ptr<FileLoader> file_loader) : file_loader_(std::move(file_loader)) {
57+
explicit SourceLookup(std::unique_ptr<FileLoader> file_loader, std::unique_ptr<OsTime> os_time = {})
58+
: file_loader_(std::move(file_loader)), os_time_(std::move(os_time)) {
59+
}
60+
void set_os_time(std::unique_ptr<OsTime> os_time) {
61+
os_time_ = std::move(os_time);
5062
}
5163
void add_include_path(td::string path);
5264
td::Result<FileLoader::File> lookup_source(std::string filename, std::string current_dir);
@@ -63,9 +75,16 @@ class SourceLookup {
6375
bool is_file_exists(td::CSlice filename) {
6476
return file_loader_->is_file_exists(filename);
6577
}
78+
td::uint32 now() {
79+
if (os_time_) {
80+
return os_time_->now();
81+
}
82+
return static_cast<td::uint32>(td::Time::now());
83+
}
6684

6785
protected:
6886
std::unique_ptr<FileLoader> file_loader_;
87+
std::unique_ptr<OsTime> os_time_;
6988
std::vector<std::string> source_include_path_;
7089
};
7190
} // namespace fift

crypto/fift/words.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,8 +1289,8 @@ void interpret_file_exists(IntCtx& ctx) {
12891289

12901290
// custom and crypto
12911291

1292-
void interpret_now(vm::Stack& stack) {
1293-
stack.push_smallint(std::time(nullptr));
1292+
void interpret_now(IntCtx& ctx) {
1293+
ctx.stack.push_smallint(ctx.source_lookup->now());
12941294
}
12951295

12961296
void interpret_new_keypair(vm::Stack& stack) {
@@ -2534,7 +2534,7 @@ void init_words_common(Dictionary& d) {
25342534
d.def_ctx_word("B>file ", interpret_write_file);
25352535
d.def_ctx_word("file-exists? ", interpret_file_exists);
25362536
// custom & crypto
2537-
d.def_stack_word("now ", interpret_now);
2537+
d.def_ctx_word("now ", interpret_now);
25382538
d.def_stack_word("newkeypair ", interpret_new_keypair);
25392539
d.def_stack_word("priv>pub ", interpret_priv_key_to_pub);
25402540
d.def_stack_word("ed25519_sign ", interpret_ed25519_sign);

crypto/smartcont/new-wallet-v2.fif

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env fift -s
2+
"TonUtil.fif" include
3+
"Asm.fif" include
4+
5+
{ ."usage: " @' $0 type ." <workchain-id> [<filename-base>]" cr
6+
."Creates a new advanced wallet in specified workchain, with private key saved to or loaded from <filename-base>.pk" cr
7+
."('new-wallet.pk' by default)" cr 1 halt
8+
} : usage
9+
$# 1- -2 and ' usage if
10+
11+
$1 parse-workchain-id =: wc // set workchain id from command line argument
12+
def? $2 { @' $2 } { "new-wallet" } cond constant file-base
13+
14+
."Creating new advanced wallet in workchain " wc . cr
15+
16+
// Create new advanced wallet; code adapted from `wallet-code.fif`
17+
<{ SETCP0 DUP IFNOTRET // return if recv_internal
18+
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
19+
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
20+
}>
21+
INC 32 THROWIF // fail unless recv_external
22+
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs
23+
SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs
24+
c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key
25+
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
26+
EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno
27+
s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash
28+
s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key
29+
ACCEPT
30+
s0 s2 XCHG // public_key stored_seqno cs
31+
WHILE:<{
32+
DUP SREFS // public_key stored_seqno cs _40
33+
}>DO<{ // public_key stored_seqno cs
34+
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance
35+
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode
36+
SENDRAWMSG // public_key stored_seqno cs
37+
}>
38+
ENDS INC // public_key seqno'
39+
NEWC 32 STU 256 STU ENDC c4 POP
40+
}>c // >libref
41+
// code
42+
<b 0 32 u,
43+
file-base +".pk" load-generate-keypair
44+
constant wallet_pk
45+
B,
46+
b> // data
47+
null // no libraries
48+
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
49+
dup ."StateInit: " <s csr. cr
50+
dup hash wc swap 2dup 2constant wallet_addr
51+
."new wallet address = " 2dup .addr cr
52+
2dup file-base +".addr" save-address-verbose
53+
."Non-bounceable address (for init): " 2dup 7 .Addr cr
54+
."Bounceable address (for later access): " 6 .Addr cr
55+
<b 0 32 u, -1 32 i, b>
56+
dup ."signing message: " <s csr. cr
57+
dup hash wallet_pk ed25519_sign_uint rot
58+
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
59+
dup ."External message for initialization is " <s csr. cr
60+
2 boc+>B dup Bx. cr
61+
file-base +"-query.boc" tuck B>file
62+
."(Saved wallet creating query to file " type .")" cr

0 commit comments

Comments
 (0)