2022-12-22 09:24:13 +00:00
|
|
|
;; Storage contract fabric
|
|
|
|
|
|
|
|
#include "constants.fc";
|
|
|
|
|
2023-05-19 13:09:10 +00:00
|
|
|
const min_deploy_amount = 60000000;
|
2022-12-22 09:24:13 +00:00
|
|
|
|
2023-02-02 09:28:41 +00:00
|
|
|
;; cell storage_contract_code() asm """ "storage-contract-code.boc" file>B B>boc PUSHREF """;
|
|
|
|
;; the same constant but more "compiler" friendly
|
|
|
|
cell storage_contract_code() asm " B{B5EE9C72010213010002FD000114FF00F4A413F4BCF2C80B0102016202030202CE04050201200D0E020120060700115D74CD0FA40D3FF30804E1007434C0C05C6C2497C0F83E900C0871C023A0D6F6CF380074C7C8700023A117C0F6CF3834CFC8A084391D237C6EA3AD4120829896802876CF3B51343C00E0842FDEF4305C20063232C1540133C5A0824C4B403E8084F2DA84B2C7D48832CFF2FFF25C3EC0244D388860841E8D85A22EA008080809006F35CE6CE4D7C11C3834C1C070C0E4D7C11C3834FFC12F64D7C0DC3800B50C1C25A010086B092E64693A0CC06AC140BD039BE84C645FF81C20002CED44D0D200FA0003A001C8CA0001FA0201CF16C9ED5403E68E4FED44D0D200FA00FA4003B3F2E3EF5350C705F2E1917FC8CA0058FA0201CF1621CF16C9ED54F003F8258210D4CAEDCD708018C8CB055005CF168209312D00FA0214CB6A13CB1F12CB3FCBFFC970FB00DE21821079F937EABAE30221821046ED2E94BA9130E30D8210419D5D4DBA915BE30D0A0B0C00EA10235F03ED44D0D200FA00FA40F0035352C7055162C70516B1F2E191F8258210B6236D63708018C8CB055004CF165005FA0212CB6A13CB1F12CB3F5230CBFFC902B39730318100A0FB00E0018040FB00F8258210B6236D63708018C8CB055004CF1623FA0213CB6A12CB1FCB3FCBFFC98100A0FB000092ED44D0D200FA00FA403002F2E3EB5341C705F2E19120C200998208989680A072FB029130E28210A91BAF56708018C8CB055003CF168209312D00FA0212CB6ACB1FCB3FC98100A0FB0000FA01D430ED44D0D200FA00FA40D3FFD33FD33FFA00D31FD31FD43009F2E3EB53A6C705F2E191544540525BF001F2E3EA22F811F8235003A128B6085331A8018102A3AA1AA984067007A116B609F8237FC8CA0058FA025005CF1613CBFFCB3FCB3F58FA0213CB1F12CB1FCCC9ED54708018C8CB0558CF16CB6EC98042FB000201200F100011BEE6576A2686B8500402012011120033B9241ED44D0D200FA00FA40D3FFD33FD33FFA00D31FD31FF00380017B6E4F0402A483DA87B0D9430001BB7CA50402A483DA87B0B664D8A70} B>boc PUSHREF ";
|
|
|
|
|
2022-12-22 09:24:13 +00:00
|
|
|
|
|
|
|
slice calculate_address_by_stateinit(cell state_init) {
|
|
|
|
return begin_cell().store_uint(4, 3)
|
|
|
|
.store_int(0, 8)
|
|
|
|
.store_uint(cell_hash(state_init), 256)
|
|
|
|
.end_cell()
|
|
|
|
.begin_parse();
|
|
|
|
}
|
|
|
|
|
|
|
|
cell build_storage_contract_stateinit(int merkle_hash, int file_size, int rate_per_mb_day,
|
|
|
|
int max_span, slice client, int torrent_hash) {
|
|
|
|
cell data = begin_cell()
|
|
|
|
.store_int(0, 1) ;; active
|
|
|
|
.store_coins(0) ;; client balance
|
|
|
|
.store_slice(my_address())
|
|
|
|
.store_uint(merkle_hash, 256)
|
|
|
|
.store_uint(file_size, 64)
|
|
|
|
.store_uint(0, 64) ;; next_proof
|
|
|
|
.store_coins(rate_per_mb_day)
|
|
|
|
.store_uint(max_span, 32)
|
|
|
|
.store_uint(now(), 32) ;; last_proof_time
|
|
|
|
.store_ref(begin_cell()
|
|
|
|
.store_slice(client)
|
|
|
|
.store_uint(torrent_hash, 256)
|
|
|
|
.end_cell())
|
|
|
|
.end_cell();
|
|
|
|
|
|
|
|
cell state_init = begin_cell()
|
|
|
|
.store_uint(0, 2)
|
|
|
|
.store_maybe_ref(storage_contract_code())
|
|
|
|
.store_maybe_ref(data)
|
|
|
|
.store_uint(0, 1) .end_cell();
|
|
|
|
return state_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
() deploy_storage_contract (slice client, int query_id, int file_size, int merkle_hash, int torrent_hash,
|
|
|
|
int expected_rate, int expected_max_span) impure {
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var (wallet_data,
|
|
|
|
accept_new_contracts?,
|
|
|
|
rate_per_mb_day,
|
|
|
|
max_span,
|
|
|
|
minimal_file_size,
|
|
|
|
maximal_file_size) = (ds~load_bits(32 + 32 + 256),
|
|
|
|
ds~load_int(1),
|
|
|
|
ds~load_coins(),
|
|
|
|
ds~load_uint(32),
|
|
|
|
ds~load_uint(64),
|
|
|
|
ds~load_uint(64));
|
|
|
|
throw_unless(error::no_new_contracts, accept_new_contracts?);
|
|
|
|
throw_unless(error::file_too_small, file_size >= minimal_file_size);
|
|
|
|
throw_unless(error::file_too_big, file_size <= maximal_file_size);
|
|
|
|
throw_unless(error::provider_params_changed, expected_rate == rate_per_mb_day);
|
|
|
|
throw_unless(error::provider_params_changed, expected_max_span == max_span);
|
|
|
|
cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day,
|
|
|
|
max_span, client, torrent_hash);
|
|
|
|
cell msg = begin_cell()
|
|
|
|
.store_uint(0x18, 6)
|
|
|
|
.store_slice(calculate_address_by_stateinit(state_init))
|
|
|
|
.store_coins(0)
|
|
|
|
.store_uint(4 + 2, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
|
|
|
|
.store_ref(state_init)
|
2023-02-02 09:28:41 +00:00
|
|
|
.store_uint(op::deploy_storage_contract, 32)
|
2022-12-22 09:24:13 +00:00
|
|
|
.store_uint(query_id, 64)
|
|
|
|
.end_cell();
|
|
|
|
send_raw_message(msg, 64);
|
|
|
|
}
|
|
|
|
|
|
|
|
() 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) | in_msg_body.slice_empty?()) { ;; ignore all bounced and empty messages
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
slice sender_address = cs~load_msg_addr();
|
|
|
|
|
|
|
|
int op = in_msg_body~load_uint(32);
|
|
|
|
if (op == 0) { ;; transfer with text message
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
int query_id = in_msg_body~load_uint(64);
|
|
|
|
|
|
|
|
if(op == op::offer_storage_contract) {
|
|
|
|
throw_unless(error::not_enough_money, msg_value >= min_deploy_amount);
|
|
|
|
;; torrent_info piece_size:uint32 file_size:uint64 root_hash:(## 256) header_size:uint64 header_hash:(## 256)
|
|
|
|
;; microchunk_hash:(Maybe (## 256)) description:Text = TorrentInfo;
|
|
|
|
;;
|
|
|
|
;; new_storage_contract#00000001 query_id:uint64 info:(^ TorrentInfo) microchunk_hash:uint256
|
|
|
|
;; expected_rate:Coins expected_max_span:uint32 = NewStorageContract;
|
|
|
|
cell torrent_info = in_msg_body~load_ref();
|
|
|
|
int torrent_hash = cell_hash(torrent_info);
|
|
|
|
slice info_cs = torrent_info.begin_parse();
|
|
|
|
info_cs~skip_bits(32);
|
|
|
|
int file_size = info_cs~load_uint(64);
|
|
|
|
int merkle_hash = in_msg_body~load_uint(256);
|
|
|
|
|
|
|
|
int expected_rate = in_msg_body~load_coins();
|
|
|
|
int expected_max_span = in_msg_body~load_uint(32);
|
|
|
|
deploy_storage_contract(sender_address, query_id, file_size, merkle_hash, torrent_hash,
|
|
|
|
expected_rate, expected_max_span);
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
if(op == op::storage_contract_terminated) {
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(op == op::update_pubkey) {
|
|
|
|
if(~ equal_slice_bits(my_address(), sender_address)) {
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var (seqno_subwallet,
|
|
|
|
_,
|
|
|
|
non_wallet_data) = (ds~load_bits(32 + 32),
|
|
|
|
ds~load_uint(256),
|
|
|
|
ds);
|
|
|
|
int new_pubkey = in_msg_body~load_uint(256);
|
|
|
|
set_data(begin_cell()
|
|
|
|
.store_slice(seqno_subwallet)
|
|
|
|
.store_uint(new_pubkey, 256)
|
|
|
|
.store_slice(non_wallet_data)
|
|
|
|
.end_cell());
|
|
|
|
}
|
|
|
|
if(op == op::update_storage_params) {
|
|
|
|
if(~ equal_slice_bits(my_address(), sender_address)) {
|
|
|
|
return ();
|
|
|
|
}
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var wallet_data = ds~load_bits(32 + 32 + 256);
|
|
|
|
var(accept_new_contracts?,
|
|
|
|
rate_per_mb_day,
|
|
|
|
max_span,
|
|
|
|
minimal_file_size,
|
|
|
|
maximal_file_size) = (in_msg_body~load_int(1),
|
|
|
|
in_msg_body~load_coins(),
|
|
|
|
in_msg_body~load_uint(32),
|
|
|
|
in_msg_body~load_uint(64),
|
|
|
|
in_msg_body~load_uint(64));
|
|
|
|
set_data(begin_cell()
|
|
|
|
.store_slice(wallet_data)
|
|
|
|
.store_int(accept_new_contracts?, 1)
|
|
|
|
.store_coins(rate_per_mb_day)
|
|
|
|
.store_uint(max_span, 32)
|
|
|
|
.store_uint(minimal_file_size, 64)
|
|
|
|
.store_uint(maximal_file_size, 64)
|
|
|
|
.end_cell());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
() recv_external(slice in_msg) impure {
|
|
|
|
var signature = in_msg~load_bits(512);
|
|
|
|
var cs = in_msg;
|
|
|
|
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
|
|
|
|
throw_if(35, valid_until <= now());
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var (stored_seqno,
|
|
|
|
stored_subwallet,
|
|
|
|
public_key,
|
|
|
|
non_wallet_data) = (ds~load_uint(32),
|
|
|
|
ds~load_uint(32),
|
|
|
|
ds~load_uint(256),
|
|
|
|
ds);
|
|
|
|
throw_unless(33, msg_seqno == stored_seqno);
|
|
|
|
throw_unless(34, subwallet_id == stored_subwallet);
|
|
|
|
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
|
|
|
|
accept_message();
|
|
|
|
cs~touch();
|
|
|
|
while (cs.slice_refs()) {
|
|
|
|
var mode = cs~load_uint(8);
|
|
|
|
send_raw_message(cs~load_ref(), mode);
|
|
|
|
}
|
|
|
|
set_data(begin_cell()
|
|
|
|
.store_uint(stored_seqno + 1, 32)
|
|
|
|
.store_uint(stored_subwallet, 32)
|
|
|
|
.store_uint(public_key, 256)
|
|
|
|
.store_slice(non_wallet_data)
|
|
|
|
.end_cell());
|
|
|
|
}
|
|
|
|
|
|
|
|
;; Get methods
|
|
|
|
|
|
|
|
int seqno() method_id {
|
|
|
|
return get_data().begin_parse().preload_uint(32);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_public_key() method_id {
|
|
|
|
var cs = get_data().begin_parse();
|
|
|
|
cs~load_uint(64);
|
|
|
|
return cs.preload_uint(256);
|
|
|
|
}
|
|
|
|
|
|
|
|
;; seqno, subwallet, key
|
|
|
|
_ get_wallet_params() method_id {
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
|
|
|
|
return (stored_seqno, stored_subwallet, public_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ get_storage_params() method_id {
|
|
|
|
var ds = get_data().begin_parse();
|
|
|
|
var (wallet_data,
|
|
|
|
accept_new_contracts?,
|
|
|
|
rate_per_mb_day,
|
|
|
|
max_span,
|
|
|
|
minimal_file_size,
|
|
|
|
maximal_file_size) = (ds~load_bits(32 + 32 + 256),
|
|
|
|
ds~load_int(1),
|
|
|
|
ds~load_coins(),
|
|
|
|
ds~load_uint(32),
|
|
|
|
ds~load_uint(64),
|
|
|
|
ds~load_uint(64));
|
|
|
|
return (accept_new_contracts?, rate_per_mb_day, max_span, minimal_file_size, maximal_file_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
slice get_storage_contract_address(int merkle_hash, int file_size, slice client, int torrent_hash) method_id {
|
|
|
|
var (_, rate_per_mb_day, max_span, _, _) = get_storage_params();
|
|
|
|
cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day, max_span, client, torrent_hash);
|
|
|
|
return calculate_address_by_stateinit(state_init);
|
|
|
|
}
|