Download ZIP (506.4 KB)

Testing and Issues

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

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




This is a smart-contract for TON blockchain implementing basic multi-signature wallet.

Multi-signature wallet is a wallet managed by some number N of private keys (fixed at the moment of wallet creation). It accepts requests for money transfers (or any other internal message from this wallet), signed by any subset of those keys.

If a request was signed by M keys, where M > some number K (fixed at the moment of wallet creation as well), the corresponding message will be sent. Otherwise it will be stored as a pending request.

Owners of the wallet can add their signatures to pending requests at any moment.

Each request can optionally have expiry time set, so it will be void after that moment (even if missing signatures will arrive). Current implementation will keep it among pending requests until the next valid message will arrive.

For more details about usage of this smart contract, please refer to the included file.
The content of the submitted archive is also available at
You have not added any comments yet...
by rating


Magic Python Nov 4, 2019 at 10:02
The author thought about a lot of details and the code is very nicely written.
All required features are implemented.
A request may contain just hash of a query
Getters are in a separate file (would not be used in a real case, but a good idea)
Bitmask and bit count of signers are stored and incrementally updated.
Despite being (almost) correct it is fast (0.039G for a transfer with n=16, k=10).
It is possible to add a signature to order without modifying seqno.
accept_message() is called only after one signature was verified.
A root signature is used to sign the whole message.

Garbage collection could take quadratic time.
A replay of messages is possible between different smart contracts that share keys.
There is no protection from a malicious owner of a smart contract.
It is really good. The only thing that isn't clear is why do you keep track of pending_count if you don't use it?
Hip Hyena Oct 19, 2019 at 06:12
It is returned in the "pending_count" get method, to let a user know how many orders are pending (without requesting the whole list).
Oh... about throws. After throw contract state is reverted (including seqno). So that message will be replayed. No attacker is needed. As I understand network will happily replay message that caused an error until there is no funds left.
Hip Hyena Oct 19, 2019 at 06:24
These exceptions can happen only if one of the owners send an invalid message. For an owner it's not really in his interests to drain funds from a wallet he has access to, so it only can happen by mistake (which should be prevented by using the provided Fift scripts).

At the same time, while testing a smart-contract, these throws provide useful debug information. Of course, before using the wallet in a real-world scenario, it would be reasonable to replace them with a simple boolean flag (to further protect wallet from owners' mistakes).
Hello Hip Hyena, good submission, especially I liked your fift libraries (utils). I remember you from previous contests and wish you good luck!
Hip Hyena Oct 19, 2019 at 12:35
Thank you! I'm especially proud of my system for easy parsing of command-line switches and arguments.

If someone is interested, it's declared in common-utils.fif, and used in almost every other Fift script. I hope it will be useful for making other command-line utilities.
You can run quite heavy computations (e. g. scanning dictionary) in get methods. They are not executed on-chain but instead client downloads state and code of contract and executes them locally.
Hip Hyena Oct 19, 2019 at 12:29
Storing 16 bits of data is cheaper than storing the code for scanning the dictionary (get methods are still stored as part of the contract's state).

Of course, it's possible to drop get methods altogether and use Fift to decode contract's state. There's room for different design choices.
There is owner variable which initially is set to the message sender. Then at the end of do-while loop it is replaced by additional signature owner. There is also a bigger while loop allowing to send two batches of additional signatures. So on next iteration you assume that owner is still referencing the message sender, when in fact it's referencing last signature owner from previous batch.

It's not exploitable because it's already read. That bigger while loop isn't really working I guess.
Magic Python Oct 24, 2019 at 13:57
Is it possible to create a query with valid_until > req_expiry?
Given that one uses only supplied fift scripts?
Hip Hyena Oct 24, 2019 at 15:35
The submitted scripts (new-order.fif and sign-sent-order.fif) have hardcoded value of -1 (or 4294967295 seconds, when interpreted as unsigned unixtime) for valid_until field. (See line 90 in new-order.fif and line 55 in sign-sent-order.fif.)

In other words, generated external messages to the wallet will be valid forever.

However, the actual orders being created can have limited "lifespan". It is achieved by passing -T (or --timeout) option to new-order.fif, followed by an arbitrary number of seconds. This number of seconds is added to the current time and stored to req_expiry field.
Magic Python Oct 24, 2019 at 18:29
I was worried about the case when valid_until > now > req_expiry and an otherwise valid message would lead to throw after accept_message. But it seems that the script takes valid_until from req_expiry, so in practice they always will be equal.
Why all files are for OSX? Any for Win10?
Nobody added any issues yet...