mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| ;; Advanced TestGiver smart contract with Proof-of-Work verification
 | |
| 
 | |
| int ufits(int x, int bits) impure asm "UFITSX";
 | |
| 
 | |
| () recv_internal(slice in_msg) impure {
 | |
|   ;; do nothing for internal messages
 | |
| }
 | |
| 
 | |
| () check_proof_of_work(slice cs) impure inline_ref {
 | |
|   var hash = slice_hash(cs);
 | |
|   var ds = get_data().begin_parse();
 | |
|   var (stored_seqno_sw, public_key, seed, pow_complexity) = (ds~load_uint(64), ds~load_uint(256), ds~load_uint(128), ds~load_uint(256));
 | |
|   throw_unless(24, hash < pow_complexity);  ;; hash problem NOT solved
 | |
|   var (op, flags, expire, whom, rdata1, rseed, rdata2) = (cs~load_uint(32), cs~load_int(8), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256), cs~load_uint(128), cs~load_uint(256));
 | |
|   cs.end_parse();
 | |
|   ufits(expire - now(), 10);
 | |
|   throw_unless(25, (rseed == seed) & (rdata1 == rdata2));
 | |
|   ;; Proof of Work correct
 | |
|   accept_message();
 | |
|   randomize_lt();
 | |
|   randomize(rdata1);
 | |
|   var (last_success, xdata) = (ds~load_uint(32), ds~load_ref());
 | |
|   ds.end_parse();
 | |
|   ds = xdata.begin_parse();
 | |
|   var (amount, target_delta, min_cpl, max_cpl) = (ds~load_grams(), ds~load_uint(32), ds~load_uint(8), ds~load_uint(8));
 | |
|   ds.end_parse();
 | |
|   ;; recompute complexity
 | |
|   int delta = now() - last_success;
 | |
|   if (delta > 0) {
 | |
|     int factor = muldivr(delta, 1 << 128, target_delta);
 | |
|     factor = min(max(factor, 7 << 125), 9 << 125);  ;; factor must be in range 7/8 .. 9/8
 | |
|     pow_complexity = muldivr(pow_complexity, factor, 1 << 128);  ;; rescale complexity
 | |
|     pow_complexity = min(max(pow_complexity, 1 << min_cpl), 1 << max_cpl);
 | |
|   }
 | |
|   ;; update data
 | |
|   set_data(begin_cell()
 | |
|     .store_uint(stored_seqno_sw, 64)
 | |
|     .store_uint(public_key, 256)
 | |
|     .store_uint(random() >> 128, 128)  ;; new seed
 | |
|     .store_uint(pow_complexity, 256)
 | |
|     .store_uint(now(), 32)   ;; new last_success
 | |
|     .store_ref(xdata)
 | |
|     .end_cell());
 | |
|   commit();
 | |
|   ;; create outbound message
 | |
|   send_raw_message(begin_cell()
 | |
|     .store_uint(((flags & 1) << 6) | 0x84, 9)
 | |
|     .store_int(flags >> 2, 8)
 | |
|     .store_uint(whom, 256)
 | |
|     .store_grams(amount)
 | |
|     .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
 | |
|   .end_cell(), 3);
 | |
| }
 | |
| 
 | |
| () rescale_complexity(slice cs) impure inline_ref {
 | |
|   var (op, expire) = (cs~load_uint(32), cs~load_uint(32));
 | |
|   cs.end_parse();
 | |
|   int time = now();
 | |
|   throw_unless(28, time > expire);
 | |
|   var ds = get_data().begin_parse();
 | |
|   var (skipped_data, pow_complexity, last_success, xdata) = (ds~load_bits(64 + 256 + 128), ds~load_uint(256), ds~load_uint(32), ds~load_ref());
 | |
|   ds.end_parse();
 | |
|   throw_unless(29, expire > last_success);
 | |
|   ds = xdata.begin_parse();
 | |
|   var (amount, target_delta) = (ds~load_grams(), ds~load_uint(32));
 | |
|   int delta = time - last_success;
 | |
|   throw_unless(30, delta >= target_delta * 16);
 | |
|   accept_message();
 | |
|   var (min_cpl, max_cpl) = (ds~load_uint(8), ds~load_uint(8));
 | |
|   ds.end_parse();
 | |
|   int factor = muldivr(delta, 1 << 128, target_delta);
 | |
|   int max_complexity = (1 << max_cpl);
 | |
|   int max_factor = muldiv(max_complexity, 1 << 128, pow_complexity);
 | |
|   pow_complexity = (max_factor < factor ? max_complexity : muldivr(pow_complexity, factor, 1 << 128));
 | |
|   last_success = time - target_delta;
 | |
|   set_data(begin_cell()
 | |
|     .store_slice(skipped_data)
 | |
|     .store_uint(pow_complexity, 256)
 | |
|     .store_uint(last_success, 32)   ;; new last_success
 | |
|     .store_ref(xdata)
 | |
|     .end_cell());
 | |
| }
 | |
| 
 | |
| (slice, ()) ~update_params(slice ds, cell pref) inline_ref {
 | |
|   var cs = pref.begin_parse();
 | |
|   var reset_cpl = cs~load_uint(8);
 | |
|   var (seed, pow_complexity, last_success) = (ds~load_uint(128), ds~load_uint(256), ds~load_uint(32));
 | |
|   if (reset_cpl) {
 | |
|     randomize(seed);
 | |
|     pow_complexity = (1 << reset_cpl);
 | |
|     seed = (random() >> 128);
 | |
|   }
 | |
|   var c = begin_cell()
 | |
|     .store_uint(seed, 128)
 | |
|     .store_uint(pow_complexity, 256)
 | |
|     .store_uint(now(), 32)
 | |
|     .store_ref(begin_cell().store_slice(cs).end_cell())
 | |
|     .end_cell();
 | |
|   return (begin_parse(c), ());
 | |
| }
 | |
| 
 | |
| () recv_external(slice in_msg) impure {
 | |
|   var op = in_msg.preload_uint(32);
 | |
|   if (op == 0x4d696e65) {
 | |
|     ;; Mine = Obtain test grams by presenting valid proof of work
 | |
|     return check_proof_of_work(in_msg);
 | |
|   }
 | |
|   if (op == 0x5253636c) {
 | |
|     ;; RScl = Rescale complexity if no success for long time
 | |
|     return rescale_complexity(in_msg);
 | |
|   }
 | |
|   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) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
 | |
|   throw_unless(33, msg_seqno == stored_seqno);
 | |
|   throw_unless(34, (subwallet_id == stored_subwallet) | (subwallet_id == 0));
 | |
|   throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
 | |
|   accept_message();
 | |
|   cs~touch();
 | |
|   while (cs.slice_refs()) {
 | |
|     var ref = cs~load_ref();
 | |
|     var mode = cs~load_uint(8);
 | |
|     if (mode < 0xff) {
 | |
|       send_raw_message(ref, mode);
 | |
|     } else {
 | |
|       ds~update_params(ref);
 | |
|     }
 | |
|   }
 | |
|   set_data(begin_cell()
 | |
|     .store_uint(stored_seqno + 1, 32)
 | |
|     .store_uint(stored_subwallet, 32)
 | |
|     .store_uint(public_key, 256)
 | |
|     .store_slice(ds)
 | |
|     .end_cell());
 | |
| }
 | |
| 
 | |
| ;; Get methods
 | |
| 
 | |
| int seqno() method_id {
 | |
|   return get_data().begin_parse().preload_uint(32);
 | |
| }
 | |
| 
 | |
| ;; gets (seed, pow_complexity, amount, interval)
 | |
| (int, int, int, int) get_pow_params() method_id {
 | |
|   var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
 | |
|   var (seed, pow_complexity, xdata) = (ds~load_uint(128), ds~load_uint(256), ds.preload_ref());
 | |
|   ds = xdata.begin_parse();
 | |
|   return (seed, pow_complexity, ds~load_grams(), ds.preload_uint(32));
 | |
| }
 | |
| 
 | |
| int get_public_key() method_id {
 | |
|   var ds = get_data().begin_parse();
 | |
|   ds~load_uint(32 + 32);
 | |
|   return ds.preload_uint(256);
 | |
| }
 |