mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-13 11:42:18 +00:00
* Add legacy_tester for existing funC contracts * Add storage-contracts and pragma options
266 lines
9.3 KiB
Text
266 lines
9.3 KiB
Text
#include "stdlib.fc";
|
|
#include "constants.fc";
|
|
|
|
const CHUNK_SIZE = 64;
|
|
const fee::receipt_value = 20000000;
|
|
const fee::storage = 10000000;
|
|
|
|
|
|
|
|
{-
|
|
storage#_ active:Bool
|
|
balance:Coins provider:MsgAddress
|
|
merkle_hash:uint256 file_size:uint64 next_proof_byte:uint64
|
|
rate_per_mb_day:Coins
|
|
max_span:uint32 last_proof_time:uint32
|
|
^[client:MsgAddress torrent_hash:uint256] = Storage;
|
|
-}
|
|
|
|
(slice, int) begin_parse_special(cell c) asm "x{D739} s,";
|
|
|
|
int check_proof(int merkle_hash, int byte_to_proof, int file_size, cell file_dict_proof) {
|
|
(slice cs, int special) = file_dict_proof.begin_parse_special();
|
|
if (~ special) {
|
|
return false;
|
|
}
|
|
if (cs~load_uint(8) != 3) { ;; Merkle proof
|
|
return false;
|
|
}
|
|
if (cs~load_uint(256) != merkle_hash) {
|
|
return false;
|
|
}
|
|
cell file_dict = cs~load_ref();
|
|
int key_len = 0;
|
|
while ((CHUNK_SIZE << key_len) < file_size) {
|
|
key_len += 1;
|
|
}
|
|
(slice data, int found?) = file_dict.udict_get?(key_len, byte_to_proof / CHUNK_SIZE);
|
|
if(found?) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
() add_to_balance(int amount) impure inline_ref {
|
|
var ds = get_data().begin_parse();
|
|
var (active, balance, residue) = (ds~load_int(1), ds~load_grams(), ds);
|
|
balance += amount;
|
|
begin_cell()
|
|
.store_int(active, 1)
|
|
.store_coins(balance)
|
|
.store_slice(residue)
|
|
.end_cell().set_data();
|
|
}
|
|
|
|
(slice, int) get_client_data(ds) {
|
|
ds = ds.preload_ref().begin_parse();
|
|
return (ds~load_msg_addr(), ds~load_uint(256));
|
|
}
|
|
|
|
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
|
|
slice cs = in_msg_full.begin_parse();
|
|
int flags = cs~load_uint(4);
|
|
|
|
if (flags & 1) { ;; ignore all bounced messages
|
|
return ();
|
|
}
|
|
slice sender_address = cs~load_msg_addr();
|
|
|
|
if (in_msg_body.slice_empty?()) {
|
|
return add_to_balance(msg_value);
|
|
}
|
|
int op = in_msg_body~load_uint(32);
|
|
if (op == 0) {
|
|
return add_to_balance(msg_value);
|
|
}
|
|
|
|
int query_id = in_msg_body~load_uint(64);
|
|
|
|
if(op == op::offer_storage_contract) {
|
|
add_to_balance(msg_value - 2 * fee::receipt_value);
|
|
var (client, torrent_hash) = get_client_data(get_data().begin_parse());
|
|
var msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(client)
|
|
.store_coins(fee::receipt_value)
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.store_uint(op::contract_deployed, 32)
|
|
.store_uint(query_id, 64)
|
|
.store_uint(torrent_hash, 256)
|
|
.end_cell();
|
|
send_raw_message(msg, 0);
|
|
}
|
|
|
|
if (op == op::accept_storage_contract) {
|
|
var ds = get_data().begin_parse();
|
|
(int active, int balance, slice provider, slice rest) =
|
|
(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
|
|
throw_unless(error::contract_already_active, ~ active);
|
|
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
|
|
begin_cell()
|
|
.store_int(true, 1)
|
|
.store_coins(balance)
|
|
.store_slice(provider)
|
|
.store_slice(rest)
|
|
.end_cell().set_data();
|
|
var (client, torrent_hash) = get_client_data(rest);
|
|
var msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(client)
|
|
.store_coins(fee::receipt_value)
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.store_uint(op::storage_contract_confirmed, 32)
|
|
.store_uint(cur_lt(), 64)
|
|
.store_uint(torrent_hash, 256)
|
|
.end_cell();
|
|
send_raw_message(msg, 0);
|
|
}
|
|
|
|
if (op == op::close_contract) {
|
|
var ds = get_data().begin_parse();
|
|
(int active, int balance, slice provider, slice rest) =
|
|
(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
|
|
var (client, torrent_hash) = get_client_data(rest);
|
|
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider) | equal_slice_bits(sender_address, client));
|
|
var client_msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(client)
|
|
.store_coins(balance)
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.store_uint(op::storage_contract_terminated, 32)
|
|
.store_uint(cur_lt(), 64)
|
|
.store_uint(torrent_hash, 256)
|
|
.end_cell();
|
|
if(~ active) {
|
|
return send_raw_message(client_msg, 128 + 32);
|
|
}
|
|
send_raw_message(client_msg, 64);
|
|
var provider_msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(provider)
|
|
.store_coins(0)
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.store_uint(op::storage_contract_terminated, 32)
|
|
.store_uint(cur_lt(), 64)
|
|
.store_uint(torrent_hash, 256)
|
|
.end_cell();
|
|
return send_raw_message(provider_msg, 128 + 32);
|
|
}
|
|
|
|
if (op == op::withdraw) {
|
|
var ds = get_data().begin_parse();
|
|
(int active, int balance, slice provider) = (ds~load_int(1), ds~load_coins(), ds~load_msg_addr());
|
|
throw_unless(error::contract_not_active, active);
|
|
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
|
|
if(balance > 0) {
|
|
raw_reserve(balance + fee::storage, 2);
|
|
}
|
|
var msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(provider)
|
|
.store_coins(fee::receipt_value)
|
|
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.store_uint(op::reward_withdrawal, 32)
|
|
.store_uint(query_id, 64)
|
|
.end_cell();
|
|
send_raw_message(msg, 128 + 32);
|
|
}
|
|
|
|
if (op == op::proof_storage) {
|
|
cell file_dict_proof = in_msg_body~load_ref();
|
|
var ds = get_data().begin_parse();
|
|
var (active,
|
|
balance,
|
|
provider,
|
|
merkle_hash,
|
|
file_size,
|
|
next_proof,
|
|
rate_per_mb_day,
|
|
max_span,
|
|
last_proof_time,
|
|
client_data) = (ds~load_int(1),
|
|
ds~load_coins(),
|
|
ds~load_msg_addr(),
|
|
ds~load_uint(256),
|
|
ds~load_uint(64),
|
|
ds~load_uint(64),
|
|
ds~load_coins(),
|
|
ds~load_uint(32),
|
|
ds~load_uint(32),
|
|
ds~load_ref());
|
|
throw_unless(error::contract_not_active, active);
|
|
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
|
|
throw_unless(error::wrong_proof, check_proof(merkle_hash, next_proof, file_size, file_dict_proof));
|
|
next_proof = rand(file_size);
|
|
int actual_span = min(now() - last_proof_time, max_span);
|
|
int bounty = muldiv(file_size * rate_per_mb_day, actual_span, 24 * 60 * 60 * 1024 * 1024);
|
|
balance = max(0, balance - bounty);
|
|
last_proof_time = now();
|
|
begin_cell()
|
|
.store_int(true, 1)
|
|
.store_coins(balance)
|
|
.store_slice(provider)
|
|
.store_uint(merkle_hash, 256)
|
|
.store_uint(file_size, 64)
|
|
.store_uint(next_proof, 64)
|
|
.store_coins(rate_per_mb_day)
|
|
.store_uint(max_span, 32)
|
|
.store_uint(last_proof_time, 32)
|
|
.store_ref(client_data)
|
|
.end_cell().set_data();
|
|
|
|
;; Send remaining balance back
|
|
cell msg = begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(sender_address)
|
|
.store_uint(0, 4 + 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
|
.end_cell();
|
|
send_raw_message(msg, 64 + 2);
|
|
}
|
|
}
|
|
|
|
_ get_storage_contract_data() method_id {
|
|
var ds = get_data().begin_parse();
|
|
var (active,
|
|
balance,
|
|
provider,
|
|
merkle_hash,
|
|
file_size,
|
|
next_proof,
|
|
rate_per_mb_day,
|
|
max_span,
|
|
last_proof_time,
|
|
rest) = (ds~load_int(1),
|
|
ds~load_coins(),
|
|
ds~load_msg_addr(),
|
|
ds~load_uint(256),
|
|
ds~load_uint(64),
|
|
ds~load_uint(64),
|
|
ds~load_coins(),
|
|
ds~load_uint(32),
|
|
ds~load_uint(32),
|
|
ds);
|
|
var (client, torrent_hash) = get_client_data(rest);
|
|
return (active, balance, provider, merkle_hash, file_size,
|
|
next_proof, rate_per_mb_day, max_span, last_proof_time,
|
|
client, torrent_hash);
|
|
}
|
|
|
|
_ get_torrent_hash() method_id {
|
|
var (active, balance, provider, merkle_hash, file_size,
|
|
next_proof, rate_per_mb_day, max_span, last_proof_time,
|
|
client, torrent_hash) = get_storage_contract_data();
|
|
return torrent_hash;
|
|
}
|
|
|
|
_ is_active() method_id {
|
|
return get_data().begin_parse().preload_int(1);
|
|
}
|
|
|
|
;; next_proof, last_proof_time, max_span
|
|
_ get_next_proof_info() method_id {
|
|
var (active, balance, provider, merkle_hash, file_size,
|
|
next_proof, rate_per_mb_day, max_span, last_proof_time,
|
|
client, torrent_hash) = get_storage_contract_data();
|
|
return (next_proof, last_proof_time, max_span);
|
|
}
|