Ton
266 строк · 9.3 Кб
1#include "stdlib.fc";
2#include "constants.fc";
3
4const CHUNK_SIZE = 64;
5const fee::receipt_value = 20000000;
6const fee::storage = 10000000;
7
8
9
10{-
11storage#_ active:Bool
12balance:Coins provider:MsgAddress
13merkle_hash:uint256 file_size:uint64 next_proof_byte:uint64
14rate_per_mb_day:Coins
15max_span:uint32 last_proof_time:uint32
16^[client:MsgAddress torrent_hash:uint256] = Storage;
17-}
18
19(slice, int) begin_parse_special(cell c) asm "x{D739} s,";
20
21int check_proof(int merkle_hash, int byte_to_proof, int file_size, cell file_dict_proof) {
22(slice cs, int special) = file_dict_proof.begin_parse_special();
23if (~ special) {
24return false;
25}
26if (cs~load_uint(8) != 3) { ;; Merkle proof
27return false;
28}
29if (cs~load_uint(256) != merkle_hash) {
30return false;
31}
32cell file_dict = cs~load_ref();
33int key_len = 0;
34while ((CHUNK_SIZE << key_len) < file_size) {
35key_len += 1;
36}
37(slice data, int found?) = file_dict.udict_get?(key_len, byte_to_proof / CHUNK_SIZE);
38if(found?) {
39return true;
40}
41return false;
42}
43
44() add_to_balance(int amount) impure inline_ref {
45var ds = get_data().begin_parse();
46var (active, balance, residue) = (ds~load_int(1), ds~load_grams(), ds);
47balance += amount;
48begin_cell()
49.store_int(active, 1)
50.store_coins(balance)
51.store_slice(residue)
52.end_cell().set_data();
53}
54
55(slice, int) get_client_data(ds) {
56ds = ds.preload_ref().begin_parse();
57return (ds~load_msg_addr(), ds~load_uint(256));
58}
59
60() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
61slice cs = in_msg_full.begin_parse();
62int flags = cs~load_uint(4);
63
64if (flags & 1) { ;; ignore all bounced messages
65return ();
66}
67slice sender_address = cs~load_msg_addr();
68
69if (in_msg_body.slice_empty?()) {
70return add_to_balance(msg_value);
71}
72int op = in_msg_body~load_uint(32);
73if (op == 0) {
74return add_to_balance(msg_value);
75}
76
77int query_id = in_msg_body~load_uint(64);
78
79if(op == op::offer_storage_contract) {
80add_to_balance(msg_value - 2 * fee::receipt_value);
81var (client, torrent_hash) = get_client_data(get_data().begin_parse());
82var msg = begin_cell()
83.store_uint(0x18, 6)
84.store_slice(client)
85.store_coins(fee::receipt_value)
86.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
87.store_uint(op::contract_deployed, 32)
88.store_uint(query_id, 64)
89.store_uint(torrent_hash, 256)
90.end_cell();
91send_raw_message(msg, 0);
92}
93
94if (op == op::accept_storage_contract) {
95var ds = get_data().begin_parse();
96(int active, int balance, slice provider, slice rest) =
97(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
98throw_unless(error::contract_already_active, ~ active);
99throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
100begin_cell()
101.store_int(true, 1)
102.store_coins(balance)
103.store_slice(provider)
104.store_slice(rest)
105.end_cell().set_data();
106var (client, torrent_hash) = get_client_data(rest);
107var msg = begin_cell()
108.store_uint(0x18, 6)
109.store_slice(client)
110.store_coins(fee::receipt_value)
111.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
112.store_uint(op::storage_contract_confirmed, 32)
113.store_uint(cur_lt(), 64)
114.store_uint(torrent_hash, 256)
115.end_cell();
116send_raw_message(msg, 0);
117}
118
119if (op == op::close_contract) {
120var ds = get_data().begin_parse();
121(int active, int balance, slice provider, slice rest) =
122(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
123var (client, torrent_hash) = get_client_data(rest);
124throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider) | equal_slice_bits(sender_address, client));
125var client_msg = begin_cell()
126.store_uint(0x18, 6)
127.store_slice(client)
128.store_coins(balance)
129.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
130.store_uint(op::storage_contract_terminated, 32)
131.store_uint(cur_lt(), 64)
132.store_uint(torrent_hash, 256)
133.end_cell();
134if(~ active) {
135return send_raw_message(client_msg, 128 + 32);
136}
137send_raw_message(client_msg, 64);
138var provider_msg = begin_cell()
139.store_uint(0x18, 6)
140.store_slice(provider)
141.store_coins(0)
142.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
143.store_uint(op::storage_contract_terminated, 32)
144.store_uint(cur_lt(), 64)
145.store_uint(torrent_hash, 256)
146.end_cell();
147return send_raw_message(provider_msg, 128 + 32);
148}
149
150if (op == op::withdraw) {
151var ds = get_data().begin_parse();
152(int active, int balance, slice provider) = (ds~load_int(1), ds~load_coins(), ds~load_msg_addr());
153throw_unless(error::contract_not_active, active);
154throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
155if(balance > 0) {
156raw_reserve(balance + fee::storage, 2);
157}
158var msg = begin_cell()
159.store_uint(0x18, 6)
160.store_slice(provider)
161.store_coins(fee::receipt_value)
162.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
163.store_uint(op::reward_withdrawal, 32)
164.store_uint(query_id, 64)
165.end_cell();
166send_raw_message(msg, 128 + 32);
167}
168
169if (op == op::proof_storage) {
170cell file_dict_proof = in_msg_body~load_ref();
171var ds = get_data().begin_parse();
172var (active,
173balance,
174provider,
175merkle_hash,
176file_size,
177next_proof,
178rate_per_mb_day,
179max_span,
180last_proof_time,
181client_data) = (ds~load_int(1),
182ds~load_coins(),
183ds~load_msg_addr(),
184ds~load_uint(256),
185ds~load_uint(64),
186ds~load_uint(64),
187ds~load_coins(),
188ds~load_uint(32),
189ds~load_uint(32),
190ds~load_ref());
191throw_unless(error::contract_not_active, active);
192throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
193throw_unless(error::wrong_proof, check_proof(merkle_hash, next_proof, file_size, file_dict_proof));
194next_proof = rand(file_size);
195int actual_span = min(now() - last_proof_time, max_span);
196int bounty = muldiv(file_size * rate_per_mb_day, actual_span, 24 * 60 * 60 * 1024 * 1024);
197balance = max(0, balance - bounty);
198last_proof_time = now();
199begin_cell()
200.store_int(true, 1)
201.store_coins(balance)
202.store_slice(provider)
203.store_uint(merkle_hash, 256)
204.store_uint(file_size, 64)
205.store_uint(next_proof, 64)
206.store_coins(rate_per_mb_day)
207.store_uint(max_span, 32)
208.store_uint(last_proof_time, 32)
209.store_ref(client_data)
210.end_cell().set_data();
211
212;; Send remaining balance back
213cell msg = begin_cell()
214.store_uint(0x18, 6)
215.store_slice(sender_address)
216.store_uint(0, 4 + 1 + 4 + 4 + 64 + 32 + 1 + 1)
217.end_cell();
218send_raw_message(msg, 64 + 2);
219}
220}
221
222_ get_storage_contract_data() method_id {
223var ds = get_data().begin_parse();
224var (active,
225balance,
226provider,
227merkle_hash,
228file_size,
229next_proof,
230rate_per_mb_day,
231max_span,
232last_proof_time,
233rest) = (ds~load_int(1),
234ds~load_coins(),
235ds~load_msg_addr(),
236ds~load_uint(256),
237ds~load_uint(64),
238ds~load_uint(64),
239ds~load_coins(),
240ds~load_uint(32),
241ds~load_uint(32),
242ds);
243var (client, torrent_hash) = get_client_data(rest);
244return (active, balance, provider, merkle_hash, file_size,
245next_proof, rate_per_mb_day, max_span, last_proof_time,
246client, torrent_hash);
247}
248
249_ get_torrent_hash() method_id {
250var (active, balance, provider, merkle_hash, file_size,
251next_proof, rate_per_mb_day, max_span, last_proof_time,
252client, torrent_hash) = get_storage_contract_data();
253return torrent_hash;
254}
255
256_ is_active() method_id {
257return get_data().begin_parse().preload_int(1);
258}
259
260;; next_proof, last_proof_time, max_span
261_ get_next_proof_info() method_id {
262var (active, balance, provider, merkle_hash, file_size,
263next_proof, rate_per_mb_day, max_span, last_proof_time,
264client, torrent_hash) = get_storage_contract_data();
265return (next_proof, last_proof_time, max_span);
266}
267