Ton
228 строк · 8.4 Кб
1;; Storage contract fabric
2
3#include "stdlib.fc";
4#include "constants.fc";
5
6const min_deploy_amount = 50000000;
7
8cell storage_contract_code() asm """ "storage-contract-code.boc" file>B B>boc PUSHREF """;
9
10slice calculate_address_by_stateinit(cell state_init) {
11return begin_cell().store_uint(4, 3)
12.store_int(0, 8)
13.store_uint(cell_hash(state_init), 256)
14.end_cell()
15.begin_parse();
16}
17
18cell build_storage_contract_stateinit(int merkle_hash, int file_size, int rate_per_mb_day,
19int max_span, slice client, int torrent_hash) {
20cell data = begin_cell()
21.store_int(0, 1) ;; active
22.store_coins(0) ;; client balance
23.store_slice(my_address())
24.store_uint(merkle_hash, 256)
25.store_uint(file_size, 64)
26.store_uint(0, 64) ;; next_proof
27.store_coins(rate_per_mb_day)
28.store_uint(max_span, 32)
29.store_uint(now(), 32) ;; last_proof_time
30.store_ref(begin_cell()
31.store_slice(client)
32.store_uint(torrent_hash, 256)
33.end_cell())
34.end_cell();
35
36cell state_init = begin_cell()
37.store_uint(0, 2)
38.store_maybe_ref(storage_contract_code())
39.store_maybe_ref(data)
40.store_uint(0, 1) .end_cell();
41return state_init;
42}
43
44() deploy_storage_contract (slice client, int query_id, int file_size, int merkle_hash, int torrent_hash,
45int expected_rate, int expected_max_span) impure {
46var ds = get_data().begin_parse();
47var (wallet_data,
48accept_new_contracts?,
49rate_per_mb_day,
50max_span,
51minimal_file_size,
52maximal_file_size) = (ds~load_bits(32 + 32 + 256),
53ds~load_int(1),
54ds~load_coins(),
55ds~load_uint(32),
56ds~load_uint(64),
57ds~load_uint(64));
58throw_unless(error::no_new_contracts, accept_new_contracts?);
59throw_unless(error::file_too_small, file_size >= minimal_file_size);
60throw_unless(error::file_too_big, file_size <= maximal_file_size);
61throw_unless(error::provider_params_changed, expected_rate == rate_per_mb_day);
62throw_unless(error::provider_params_changed, expected_max_span == max_span);
63cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day,
64max_span, client, torrent_hash);
65cell msg = begin_cell()
66.store_uint(0x18, 6)
67.store_slice(calculate_address_by_stateinit(state_init))
68.store_coins(0)
69.store_uint(4 + 2, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
70.store_ref(state_init)
71.store_uint(op::offer_storage_contract, 32)
72.store_uint(query_id, 64)
73.end_cell();
74send_raw_message(msg, 64);
75}
76
77() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
78slice cs = in_msg_full.begin_parse();
79int flags = cs~load_uint(4);
80
81if ((flags & 1) | in_msg_body.slice_empty?()) { ;; ignore all bounced and empty messages
82return ();
83}
84slice sender_address = cs~load_msg_addr();
85
86int op = in_msg_body~load_uint(32);
87if (op == 0) { ;; transfer with text message
88return ();
89}
90int query_id = in_msg_body~load_uint(64);
91
92if(op == op::offer_storage_contract) {
93throw_unless(error::not_enough_money, msg_value >= min_deploy_amount);
94;; torrent_info piece_size:uint32 file_size:uint64 root_hash:(## 256) header_size:uint64 header_hash:(## 256)
95;; microchunk_hash:(Maybe (## 256)) description:Text = TorrentInfo;
96;;
97;; new_storage_contract#00000001 query_id:uint64 info:(^ TorrentInfo) microchunk_hash:uint256
98;; expected_rate:Coins expected_max_span:uint32 = NewStorageContract;
99cell torrent_info = in_msg_body~load_ref();
100int torrent_hash = cell_hash(torrent_info);
101slice info_cs = torrent_info.begin_parse();
102info_cs~skip_bits(32);
103int file_size = info_cs~load_uint(64);
104int merkle_hash = in_msg_body~load_uint(256);
105
106int expected_rate = in_msg_body~load_coins();
107int expected_max_span = in_msg_body~load_uint(32);
108deploy_storage_contract(sender_address, query_id, file_size, merkle_hash, torrent_hash,
109expected_rate, expected_max_span);
110return ();
111}
112if(op == op::storage_contract_terminated) {
113return ();
114}
115
116if(op == op::update_pubkey) {
117if(~ equal_slice_bits(my_address(), sender_address)) {
118return ();
119}
120var ds = get_data().begin_parse();
121var (seqno_subwallet,
122_,
123non_wallet_data) = (ds~load_bits(32 + 32),
124ds~load_uint(256),
125ds);
126int new_pubkey = in_msg_body~load_uint(256);
127set_data(begin_cell()
128.store_slice(seqno_subwallet)
129.store_uint(new_pubkey, 256)
130.store_slice(non_wallet_data)
131.end_cell());
132}
133if(op == op::update_storage_params) {
134if(~ equal_slice_bits(my_address(), sender_address)) {
135return ();
136}
137var ds = get_data().begin_parse();
138var wallet_data = ds~load_bits(32 + 32 + 256);
139var(accept_new_contracts?,
140rate_per_mb_day,
141max_span,
142minimal_file_size,
143maximal_file_size) = (in_msg_body~load_int(1),
144in_msg_body~load_coins(),
145in_msg_body~load_uint(32),
146in_msg_body~load_uint(64),
147in_msg_body~load_uint(64));
148set_data(begin_cell()
149.store_slice(wallet_data)
150.store_int(accept_new_contracts?, 1)
151.store_coins(rate_per_mb_day)
152.store_uint(max_span, 32)
153.store_uint(minimal_file_size, 64)
154.store_uint(maximal_file_size, 64)
155.end_cell());
156}
157}
158
159() recv_external(slice in_msg) impure {
160var signature = in_msg~load_bits(512);
161var cs = in_msg;
162var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
163throw_if(35, valid_until <= now());
164var ds = get_data().begin_parse();
165var (stored_seqno,
166stored_subwallet,
167public_key,
168non_wallet_data) = (ds~load_uint(32),
169ds~load_uint(32),
170ds~load_uint(256),
171ds);
172throw_unless(33, msg_seqno == stored_seqno);
173throw_unless(34, subwallet_id == stored_subwallet);
174throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
175accept_message();
176cs~touch();
177while (cs.slice_refs()) {
178var mode = cs~load_uint(8);
179send_raw_message(cs~load_ref(), mode);
180}
181set_data(begin_cell()
182.store_uint(stored_seqno + 1, 32)
183.store_uint(stored_subwallet, 32)
184.store_uint(public_key, 256)
185.store_slice(non_wallet_data)
186.end_cell());
187}
188
189;; Get methods
190
191int seqno() method_id {
192return get_data().begin_parse().preload_uint(32);
193}
194
195int get_public_key() method_id {
196var cs = get_data().begin_parse();
197cs~load_uint(64);
198return cs.preload_uint(256);
199}
200
201;; seqno, subwallet, key
202_ get_wallet_params() method_id {
203var ds = get_data().begin_parse();
204var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
205return (stored_seqno, stored_subwallet, public_key);
206}
207
208_ get_storage_params() method_id {
209var ds = get_data().begin_parse();
210var (wallet_data,
211accept_new_contracts?,
212rate_per_mb_day,
213max_span,
214minimal_file_size,
215maximal_file_size) = (ds~load_bits(32 + 32 + 256),
216ds~load_int(1),
217ds~load_coins(),
218ds~load_uint(32),
219ds~load_uint(64),
220ds~load_uint(64));
221return (accept_new_contracts?, rate_per_mb_day, max_span, minimal_file_size, maximal_file_size);
222}
223
224slice get_storage_contract_address(int merkle_hash, int file_size, slice client, int torrent_hash) method_id {
225var (_, rate_per_mb_day, max_span, _, _) = get_storage_params();
226cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day, max_span, client, torrent_hash);
227return calculate_address_by_stateinit(state_init);
228}
229