Info

Source on GitHub

https://github.com/EmelyanenkoK/TheChatGame

Smart Contract Address

kQChp8oK-nB-Avs1rCL8Q9IieH8oAwnntwIHmYvDzD07wqUf

Source on GitHub

https://github.com/EmelyanenkoK/OracleHub

Smart Contract Address

0:bc2b1afd7b59a288293e2b72d43ed02c50c3421f09c46ac34544e5a3f4b6c152

Testing and Issues

You can test this entry and submit issues during the testing period of the Blockchain Contest, Stage 2 contest.

Entries with serious issues will not be able to win the contest, but even minor issues might be important for overall results.

Voting

2

Comments

Project 1
Blockchain infrastructure for the game is submitted to contest. The game is based on various RPG mechanics, including collectible items. Since user inventories are implemented as open registry on TON chain, users may hoard, transfer and subject their items to arbitrary complex operations governed by smartcontracts. Our game is using @combot with more than 60000 active groups to issue items to users.
Project 2
OracleHub solves similar problem as contract for The Chat Game. If the latter aims to connect game reality with smart-contracts on TON blockchain, the former aims to connect our usual boring reality with smart-contracts on TON blockchain. Since for real world situation is more complicated and everybody has it's own view on what is "the real situation", instead of creating single source of information we propose OracleHub: the place where data providers and data consumers meet.
Smartcontract and infrastructure for running independent oracle is submitted to contest.
1
You have not added any comments yet...
by rating

Issues

Great submission, I believe, RPG games in chats have a great future! And thank you for all of your efforts helping the developers community!
So your stateinit does not contain public key. You pick that from constructor message. I can pull a trick if I know specifications and inventories beforehand, or when I see your yet unprocessed stateinit message.

I can publish my own contract instead of yours. And because you do have set_code in there, I can even trick you into believing that it was published successfully, only leaving small backdoor to myself.

It will work as usual, the only thing giving away will be the contract code hash that will not match the one expected.
Mellow Squid Dec 27, 2019 at 11:42
Note, that your 1st idea of account hijacking is not that simple to implement. In init-*.fif we store timestamp at the end of the storage, and generally may store anything at all(it will be overwritten during first save_data), that way address is no longer deterministic result of code only, and thus prediction of 'code' by 'address' is impossible.
Your second idea about intercepting of init message, rewriting it on-fly and resending it to the network is much more viable (still very hard to implement). Note, though, that attacker can not make this attack invisible (additional transactions on network still will be visible) and developer only risks uninit funds, which, according to guidelines should be quite small. At the same time reusing of public key as init flag greatly simplifies code and thus reduces risk of bugs. So, keeping in mind that after deploy developer should check lack of suspicious txses to eliminate hijacking, I believe this approach is quite good.
That replay protection... If I understood correctly, query_id can be viewed as valid_since 32 u, nonce 32 u, and valid_until is basically defined as valid_since + 18 hours. So you store that query_id to protect from replays, and you have function to remove expired. But that function removes queries which are only 64 seconds old. Meaning that during time window between 64 seconds and 18 hours message can be replayed. Am I right?
Mellow Squid Dec 27, 2019 at 12:00
No. Note that 1) contract throw if query has timestamp (first 32 bit of id) less than now(); 2) if query has timestamp higher than now()+ 18 hours and delete queries that expired more than 64 sec ago. That way if we deleted query it can't be used anyway: it is too old, thus no replay for deleted queries is possible.
You are right, it's valid_until (not valid_since) encoded in query_id and you don't allow valid_until to be higher than 18 hours.
> At the same time reusing of public key as init flag greatly simplifies code and thus reduces risk of bugs.

I am assuming that's because you had no seqno, and so you couldn't tie your constructor to anything else. You may wanna use following constructor envelope:

<{
SETCP0 ACCEPT
nonce INT
"code.fif" include PUSHREF SETCODE
}>c

It's an envelope around contract code (code.fif here) that immediately replaces itself with actual contract code (PUSHREF SETCODE). So actual contract code doesn't need constructor checks then, and envelope is discarded after first message (constructor message).

I also add nonce (nonce INT) instead of timestamp to it, so I can pick address that I like. You may replace that with timestamp (now INT).
Mellow Squid Dec 27, 2019 at 13:05
Yes, it is quite elegant way to do the same thing. Thank you.
(Developer still need to check absence of suspicious txses on address in case of on-fly overwritting).
Also probably it will be better to use hash(code) (that is `"code.fif" include hashu`) instead of nonce to avoid collision if many developers will use this approach.
> Also probably it will be better to use hash(code) (that is `"code.fif" include hashu`) instead of nonce to avoid collision if many developers will use this approach.

No, because the actual contract code is still stored within envelope's code as reference ("code.fif" include PUSHREF actually instructs Asm.fif to include code.fif cell as reference into envelope and put TVM instruction to load that reference during runtime). So actual contract code is sort-of included already into address too. You will not have any collisions.
Mellow Squid Dec 27, 2019 at 13:12
Agree with you, my bad. I misread and thought that we provide code in the init message.
Small performance hint, there is udict_set_builder instruction that allows you to save 100+ gas if you use it instead of udict::set that you redefined.
Disregard that deleted message. I actually have no idea how raw_reserve(..., 3) and send_raw_message(..., 64) play together.

Anyway, I could not find anything serious to exploit in collectibles.fc And I really liked optimistic executions (will throw here automatically comments). And also the fact that you handle improbable, but not impossible lockout.
In oracle hub when you return money after response is provided by oracle you set spend_limit to min_fee_per_operation. Which means that it's impossible to return more than 0.025 grams. Also when you clean up expired requests, you don't return money back too. I'm really confused why you are holding them in the first place.
Mellow Squid Dec 27, 2019 at 15:05
Good catch, you are right, it is a bug I introduced after submission in 9ffce55 commit. Idea was that malicious oracle may return very big response and thus deplete OracleHub balance: spend more for processing than OH get as fee. In correct code there should be get_balance().[0]() - spend_limit - sum.
However, now I believe that initial submission and smartcontract deployed on 0:bc2b1afd7b59a288293e2b72d43ed02c50c3421f09c46ac34544e5a3f4b6c152 is good enough:
since mode 2 for response is used, user will pay fees for transferring response by himself. Simultaneously if amount is not enough for processing - request will be marked as processed, but user will not get any response. Probably this situation should be caught and log message should be emitted.
This is not for the contest: we want to show you the web wallet gram-wallet.org developed by Mellow Squid and I.

Wallet has UI like wallet.ton.org. All wallet functionality is implemented on the client side on JS (including serialize/deserialize TLB, Bag of Cells, signing, etc.),

Own service toncenter.com used as http TON api provider.

We want to know the opinion of the TON team about this project and find out whether it is possible to become the official TON web wallet.
Clever Turkey Feb 11, 2020 at 02:03
Oracle contract can run out of funds if `internal_query_id = cs~load_uint(96);` throws, because this is executed after accept_message() call.
Nobody added any issues yet...