Skip to content

Commit

Permalink
Added value validation (0.5TON < val < 1TON); Added set source item c…
Browse files Browse the repository at this point in the history
…ode op; Non-sequential op codes; Non-empty set code validation (#2)

* added value validation

* -

* -

* e2e

* fixed tests
  • Loading branch information
shaharyakir authored Nov 2, 2022
1 parent ffe3d9b commit 80f8324
Show file tree
Hide file tree
Showing 20 changed files with 750 additions and 79 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
# TON SRC Contracts
nft-based contracts for registering the sources data url for a given code cell hash
nft-based contracts for registering the sources data url for a given code cell hash

## E2E tests
(e2e.ts in test/e2e)
1. Pre-deploy (using `npm run build && npm run deploy`) the sources registry contract -> change min_tons in sources_registry.fc to a small amount, otherwise tests are too costly.
2. Provide two mnemonics via .env
3. Flows carried out:
* (Sender: wallet1) Change admin from wallet1 to wallet2
* (Sender: wallet2) Change verifier address from actual to zero address; then revert to actual
* (Sender: wallet2) Set code to `sources-registry-only-set-code.fc`; then revert to original
* (Sender: wallet2) Change admin from wallet2 to wallet1
* (Sender: wallet1) Set source item code to `...?.fc`; then revert to original
6 changes: 3 additions & 3 deletions build/sources-registry.deploy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as sourcesRegistry from "../contracts/sources-registry";
import { Address, toNano, TupleSlice, WalletContract } from "ton";
import { sendInternalMessageWithWallet } from "../test/helpers";
import { sendInternalMessageWithWallet, zeroAddress } from '../test/unit/helpers';

// return the init Cell of the contract storage (according to load_data() contract method)
// EQBfL6AgP-lNiYXFADmcD5yFPwK9DXhaLNlZl-9cWJAJEmQe
export function initData() {
return sourcesRegistry.data({
verifierRegistryAddress: Address.parse("EQCcHtD_e3LfV_bJivWwRbZg-RpZD20QiXjjpG47EqRFUPq_"),
verifierRegistryAddress: zeroAddress,
admin: Address.parse("EQBnLd2ta0Od6LkhaeO1zDQ4wcvoUReK8Z8k881BIMrTfjb8"),
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as veriferRegistry from "../contracts/verifier-registry";
import * as sourcesRegistry from "../contracts/sources-registry";
import { Address, toNano, TupleSlice, WalletContract, beginCell } from "ton";
import { sendInternalMessageWithWallet } from "../test/helpers";
import { sendInternalMessageWithWallet } from "../test/unit/helpers";
import nacl from "tweetnacl";
import { keyToAddress } from "../contracts/sources-registry";

This comment has been minimized.

Copy link
@hossen71

hossen71 Dec 24, 2024

UQA5NWl5eH6KvxwnMBCj7vDbb-Xebok-pWGfFuHhq1zMxmv5


Expand Down
69 changes: 69 additions & 0 deletions contracts/source-item-dummy.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
;;
;; Source item smart contract - DUMMY FOR TESTS
;;

#pragma version >=0.2.0;
#include "imports/stdlib.fc";

const int error::access_denied = 401;
const int error::unknown_op = 0xffff;

;;
;; Storage
;;
;; uint256 verifier_id
;; uint256 verified_code_cell_hash
;; MsgAddressInt source_item_registry
;; cell content
;;
(int, int, int, slice, cell) load_data() {
slice ds = get_data().begin_parse();
var (verifier_id, verified_code_cell_hash, source_item_registry) = (ds~load_uint(256), ds~load_uint(256), ds~load_msg_addr());
if (ds.slice_refs() > 0) {
return (-1, verifier_id, verified_code_cell_hash, source_item_registry, ds~load_ref());
} else {
return (0, verifier_id, verified_code_cell_hash, source_item_registry, null()); ;; not initialized yet
}
}

() store_data(int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) impure {
set_data(
begin_cell()
.store_uint(verifier_id, 256)
.store_uint(verified_code_cell_hash, 256)
.store_slice(source_item_registry)
.store_ref(content)
.end_cell()
);
}

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}

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();

(int init?, int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) = load_data();
if (~ init?) {
throw_unless(error::access_denied, equal_slices(source_item_registry, sender_address));
store_data(verifier_id, verified_code_cell_hash, source_item_registry, begin_cell().store_slice(in_msg_body).end_cell());
return ();
}

throw(error::unknown_op);
}

;;
;; GET Methods
;;
(int, int, int, slice, cell) get_dummy_data() method_id {
(int init?, int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) = load_data();
return (init?, verifier_id, verified_code_cell_hash, source_item_registry, content);
}
29 changes: 11 additions & 18 deletions contracts/source-item.fc
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@ const int error::unknown_op = 0xffff;
;;
;; uint256 verifier_id
;; uint256 verified_code_cell_hash
;; MsgAddressInt collection_address
;; MsgAddressInt source_item_registry
;; cell content
;;

(int, int, int, slice, cell) load_data() {
slice ds = get_data().begin_parse();
var (verifier_id, verified_code_cell_hash, collection_address) = (ds~load_uint(256), ds~load_uint(256), ds~load_msg_addr());
var (verifier_id, verified_code_cell_hash, source_item_registry) = (ds~load_uint(256), ds~load_uint(256), ds~load_msg_addr());
if (ds.slice_refs() > 0) {
return (-1, verifier_id, verified_code_cell_hash, collection_address, ds~load_ref());
return (-1, verifier_id, verified_code_cell_hash, source_item_registry, ds~load_ref());
} else {
return (0, verifier_id, verified_code_cell_hash, collection_address, null()); ;; nft not initialized yet
return (0, verifier_id, verified_code_cell_hash, source_item_registry, null()); ;; not initialized yet
}
}

() store_data(int verifier_id, int verified_code_cell_hash, slice collection_address, cell content) impure {
() store_data(int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) impure {
set_data(
begin_cell()
.store_uint(verifier_id, 256)
.store_uint(verified_code_cell_hash, 256)
.store_slice(collection_address)
.store_slice(source_item_registry)
.store_ref(content)
.end_cell()
);
Expand All @@ -51,16 +50,10 @@ const int error::unknown_op = 0xffff;
}
slice sender_address = cs~load_msg_addr();

cs~load_msg_addr(); ;; skip dst
cs~load_coins(); ;; skip value
cs~skip_bits(1); ;; skip extracurrency collection
cs~load_coins(); ;; skip ihr_fee
int fwd_fee = cs~load_coins(); ;; we use message fwd_fee for estimation of forward_payload costs

(int init?, int verifier_id, int verified_code_cell_hash, slice collection_address, cell content) = load_data();
(int init?, int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) = load_data();
if (~ init?) {
throw_unless(error::access_denied, equal_slices(collection_address, sender_address));
store_data(verifier_id, verified_code_cell_hash, collection_address, begin_cell().store_slice(in_msg_body).end_cell());
throw_unless(error::access_denied, equal_slices(source_item_registry, sender_address));
store_data(verifier_id, verified_code_cell_hash, source_item_registry, begin_cell().store_slice(in_msg_body).end_cell());
return ();
}

Expand All @@ -71,6 +64,6 @@ const int error::unknown_op = 0xffff;
;; GET Methods
;;
(int, int, int, slice, cell) get_source_item_data() method_id {
(int init?, int verifier_id, int verified_code_cell_hash, slice collection_address, cell content) = load_data();
return (init?, verifier_id, verified_code_cell_hash, collection_address, content);
(int init?, int verifier_id, int verified_code_cell_hash, slice source_item_registry, cell content) = load_data();
return (init?, verifier_id, verified_code_cell_hash, source_item_registry, content);
}
38 changes: 38 additions & 0 deletions contracts/sources-registry-only-set-code.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
;; DUMMY CODE for tests only

;; storage scheme
;; storage#_ verifier_registry_address:MsgAddress
;; source_item_code:^Cell
;; = Storage;
#pragma version >=0.2.0;
#include "imports/stdlib.fc";
#include "imports/params.fc";

() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
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();

int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);

if (op == 9988) {
cell new_code = in_msg_body~load_ref();
in_msg_body.end_parse();
set_code(new_code);
return ();
}

throw(203);
}

int get_am_i_replaced() method_id {
return 742;
}
33 changes: 25 additions & 8 deletions contracts/sources-registry.fc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;; Sources collection smart contract (based on nft collection)
;; Sources registry smart contract (based on nft collection)

;; storage scheme
;; storage#_ verifier_registry_address:MsgAddress
Expand All @@ -8,13 +8,20 @@
#include "imports/stdlib.fc";
#include "imports/params.fc";

const int op::deploy_source_item = 1;
const int op::change_verifier_registry = 3;
const int op::change_admin = 4;
const int op::set_code = 5;
const int op::deploy_source_item = 1002;
const int op::change_verifier_registry = 2003;
const int op::change_admin = 3004;
const int op::set_source_item_code = 4005;
const int op::set_code = 5006;
const int error::invalid_cell_code = 902;
const int error::too_much_value = 901;
const int error::not_enough_value = 900;
const int error::access_denied = 401;
const int error::unknown_op = 0xffff;

const int max_tons = 1000000000;
const int min_tons = 500000000;

(slice, slice, cell) load_data() inline {
var ds = get_data().begin_parse();
return (
Expand Down Expand Up @@ -45,7 +52,6 @@ slice calculate_source_item_address(int wc, cell state_init) {
.begin_parse();
}

;; TODO what amount should we validate for the transfer?
() deploy_source_item(int verifier_id, int verified_code_cell_hash, cell source_item_code, cell source_content) impure {
cell state_init = calculate_source_item_state_init(verifier_id, verified_code_cell_hash, source_item_code);
slice source_address = calculate_source_item_address(workchain(), state_init);
Expand All @@ -59,7 +65,7 @@ slice calculate_source_item_address(int wc, cell state_init) {
send_raw_message(msg.end_cell(), 64);
}

() recv_internal(cell in_msg_full, slice in_msg_body) impure {
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
Expand All @@ -78,6 +84,8 @@ slice calculate_source_item_address(int wc, cell state_init) {

if (op == op::deploy_source_item) {
throw_unless(error::access_denied, equal_slices(sender_address, verifier_registry));
throw_if(error::too_much_value, msg_value > max_tons);
throw_if(error::not_enough_value, msg_value < min_tons);
int verifier_id = in_msg_body~load_uint(256);
int verified_code_cell_hash = in_msg_body~load_uint(256);
cell source_content = in_msg_body~load_ref();
Expand All @@ -86,7 +94,6 @@ slice calculate_source_item_address(int wc, cell state_init) {
return ();
}

;; TODO op codes => CRC?
if (op == op::change_verifier_registry) {
throw_unless(error::access_denied, equal_slices(sender_address, admin));
slice new_verifier_registry = in_msg_body~load_msg_addr();
Expand All @@ -103,9 +110,19 @@ slice calculate_source_item_address(int wc, cell state_init) {
return ();
}

if (op == op::set_source_item_code) {
throw_unless(error::access_denied, equal_slices(sender_address, admin));
cell new_source_item_code = in_msg_body~load_ref();
throw_if(error::invalid_cell_code, new_source_item_code.begin_parse().slice_empty?());
save_data(admin, verifier_registry, new_source_item_code);
in_msg_body.end_parse();
return ();
}

if (op == op::set_code) {
throw_unless(error::access_denied, equal_slices(sender_address, admin));
cell new_code = in_msg_body~load_ref();
throw_if(error::invalid_cell_code, new_code.begin_parse().slice_empty?());
in_msg_body.end_parse();
set_code(new_code);
return ();
Expand Down
12 changes: 8 additions & 4 deletions contracts/sources-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const toSha256Buffer = (s: string) => {
// message encoders for all ops (see contracts/imports/constants.fc for consts)
export function deploySource(verifierId: string, codeCellHash: string, jsonURL: string): Cell {
return beginCell()
.storeUint(0x1, 32)
.storeUint(1002, 32)
.storeUint(0, 64)
.storeBuffer(toSha256Buffer(verifierId))
.storeUint(new BN(Buffer.from(codeCellHash, "base64")), 256)
Expand All @@ -67,13 +67,17 @@ export function deploySource(verifierId: string, codeCellHash: string, jsonURL:
}

export function changeVerifierRegistry(newVerifierRegistry: Address): Cell {
return beginCell().storeUint(0x3, 32).storeUint(0, 64).storeAddress(newVerifierRegistry).endCell();
return beginCell().storeUint(2003, 32).storeUint(0, 64).storeAddress(newVerifierRegistry).endCell();
}

export function changeAdmin(newAdmin: Address): Cell {
return beginCell().storeUint(0x4, 32).storeUint(0, 64).storeAddress(newAdmin).endCell();
return beginCell().storeUint(3004, 32).storeUint(0, 64).storeAddress(newAdmin).endCell();
}

export function setSourceItemCode(newCode: Cell): Cell {
return beginCell().storeUint(4005, 32).storeUint(0, 64).storeRef(newCode).endCell();
}

export function changeCode(newCode: Cell): Cell {
return beginCell().storeUint(0x5, 32).storeUint(0, 64).storeRef(newCode).endCell();
return beginCell().storeUint(5006, 32).storeUint(0, 64).storeRef(newCode).endCell();
}
Loading

0 comments on commit 80f8324

Please sign in to comment.