1
0
mirror of https://github.com/danog/toncontest.git synced 2024-11-30 04:29:14 +01:00
toncontest/wallet/wallet-code.fc
2019-10-14 23:34:00 +02:00

233 lines
8.1 KiB
Plaintext

;; Multisig wallet smart contract (Daniil Gentili @danogentili <daniil@daniil.it>)
;; (Now unused) tuple manipulation primitives for integers
tuple tuple_set(tuple t, int index, int value) asm(t value index) "SETINDEXVARQ";
(tuple, ()) ~tuple_set(tuple t, int index, int value) asm(t value index) "SETINDEXVARQ";
;; Tuple manipulation primitives for cells
tuple tuple_setcell(tuple t, int index, cell value) asm(t value index) "SETINDEXVARQ";
(tuple, ()) ~tuple_setcell(tuple t, int index, cell value) asm(t value index) "SETINDEXVARQ";
int tuple_len(tuple t) asm "TLEN";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
int udict_has?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT" "NIP";
;; Cleanup expired partial orders
;; messages
(cell, ()) ~collect_garbage(cell messages) {
var hash = -1;
do {
(hash, var cs, int ok) = messages.udict_get_next?(256, hash);
if (ok) {
;; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X;
;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X;
;; multiSigWrapperStorage$_ count:(## 4) signatures:(HashmapE 4 Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X;
;;
;; Skip signatures, check expiry
;;
;; expiry <= now
if (cs.skip_bits(4).skip_dict().preload_uint(32) <= now()) {
messages~udict_delete?(256, hash);
}
}
} until (~ ok);
return (messages, ());
}
() store_db(int seqno, cell keys, cell messages) {
set_data(begin_cell().store_uint(seqno, 32).store_dict(keys).store_dict(messages).end_cell());
}
() recv_internal(slice message) impure {
;; do nothing for internal messages
}
;; multiSigWrapper$0 signatures:(HashmapE 4 Signature) message:(WrappedMessage X) = MultiSigWrapper X;
() recv_external(slice message) impure {
;; Check if multiSigWrapper$0, multiSigInit$10 or future unsupported protocol
int tag = message~load_uint(1);
if (tag) {
;; Throw if multiSigFuture$11
throw_if(32, message~load_uint(1));
;; Accept if multiSigInit$10
accept_message();
slice stored_data = get_data().begin_parse();
(int stored_seqno, int min_sigs, var keys, var messages) = (stored_data~load_uint(32), stored_data~load_uint(4), stored_data~load_dict(), stored_data~load_dict());
stored_data.end_parse();
set_data(begin_cell().store_uint(stored_seqno, 32).store_uint(min_sigs, 4).store_dict(keys).store_dict(messages).end_cell());
return ();
}
;; Check if is hme_empty$0 or hme_root$1
;; Throw if empty signature list
throw_unless(33, message.preload_uint(1));
var signatures = message~load_dict();
slice message_copy = message;
;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X;
(int expires_at, int msg_seqno) = (message_copy~load_uint(32), message_copy~load_uint(32));
;; Message expired
throw_if(34, expires_at <= now());
;; We will need the hash anyway
int hash = slice_hash(message);
;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 PubKey) messages:(HashmapE 256 (MultiSigWrapperStorage X)) = Storage X;
slice stored_data = get_data().begin_parse();
(int stored_seqno, int min_sigs, var keys, var messages) = (stored_data~load_uint(32), stored_data~load_uint(4), stored_data~load_dict(), stored_data~load_dict());
stored_data.end_parse();
;; This is a new message, so there will be no stored message
var storedSignatureCount = 0;
var storedMessageSignatures = new_dict();
;; If new message, check and increase seqno
if (stored_seqno == msg_seqno) {
stored_seqno += 1;
;; If old message, seqno WILL be different, check if exists in unsigned messages dict
} else {
(message, int ok) = messages.udict_get?(256, hash);
;; Throw if old message and doesn't exist in db
throw_unless(35, ok);
;; multiSigWrapperStorage$_ count:(## 4) signatures:(HashmapE 4 Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X;
;;
;; Load signatures
storedSignatureCount = message~load_uint(4);
storedMessageSignatures = message.preload_dict();
}
accept_message();
int idx = -1;
do {
(idx, slice signature, int ok) = signatures.udict_get_next?(4, idx);
if (ok) {
(var public_key, ok) = keys.udict_get?(4, idx);
throw_unless(36, ok);
var slice_copy = signature;
throw_unless(37, check_signature(hash, slice_copy, public_key.preload_uint(256)));
if (~ storedMessageSignatures.udict_has?(4, idx)) {
storedMessageSignatures~udict_set(4, idx, signature);
storedSignatureCount += 1;
}
}
} until (~ ok);
if (storedSignatureCount >= min_sigs) {
if (message_copy~load_uint(1)) {
;; Code upgrade
;; codeMessage$1 minSigs:(## 4) keys:(HashmapE 4 PubKey) code:^Cell = ModeMessage X;
;;
min_sigs = message_copy~load_uint(4);
keys = message_copy~load_dict();
set_code(message_copy~load_ref());
} else {
;; Simple message
;; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X;
;;
var (mode, message) = (message_copy~load_uint(8), message_copy~load_ref());
send_raw_message(message, mode);
}
message_copy.end_parse();
messages~udict_delete?(256, hash);
} else {
;; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X;
;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X;
;; multiSigWrapperStorage$_ count:(## 4) signatures:(HashmapE 4 Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X;
;;
messages~udict_set_builder(
256,
hash,
begin_cell()
.store_uint(storedSignatureCount, 4)
.store_dict(storedMessageSignatures)
.store_uint(expires_at, 32)
.store_uint(msg_seqno, 32)
.store_slice(message_copy)
);
}
messages~collect_garbage();
;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 PubKey) messages:(HashmapE 256 (MultiSigWrapperStorage X)) = Storage X;
set_data(begin_cell().store_uint(stored_seqno, 32).store_uint(min_sigs, 4).store_dict(keys).store_dict(messages).end_cell());
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
(int, int) getKey(int id) method_id {
(var res, int ok) = get_data().begin_parse().skip_bits(36).preload_dict().udict_get?(4, id);
if (~ ok) {
return (ok, 0);
}
return (-1, res.preload_uint(256));
}
(int, int) getId(int key) method_id {
var keys = get_data().begin_parse().skip_bits(36).preload_dict();
int idx = -1;
int ok = 0;
int found = 0;
do {
(idx, var keyRead, ok) = keys.udict_get_next?(4, idx);
if (ok) {
;; I could actually return here, but the funcompiler won't let me
found = keyRead.preload_uint(256) == key;
}
} until ((~ ok) | found);
if (found) {
return (-1, idx);
}
return (0, 0);
}
(cell) getPartials() method_id {
return get_data().begin_parse().skip_bits(36).skip_dict().preload_dict();
}
(int, cell) getPartialsByKeyId(int id) method_id {
cell messages = get_data().begin_parse().skip_bits(36).skip_dict().preload_dict();
cell messages_found = new_dict();
int idx = -1;
int ok = 0;
int found = 0;
do {
(idx, slice message, ok) = messages.udict_get_next?(256, idx);
if (ok) {
if (message.skip_bits(4).preload_dict().udict_has?(4, id)) {
found += 1;
messages_found~udict_set_builder(256, idx, begin_cell().store_slice(message));
}
}
} until (~ ok);
return (found, messages_found);
}
(int, cell) getPartialByKey(int key) method_id {
(int ok, int id) = getId(key);
ifnot (ok) {
return (0, new_dict());
}
return getPartialsByKeyId(id);
}
(int, cell) getPartialByHash(int hash) method_id {
(slice message, int ok) = get_data().begin_parse().skip_bits(36).skip_dict().preload_dict().udict_get?(256, hash);
ifnot (ok) {
return (0, begin_cell().end_cell());
}
return (ok, begin_cell().store_slice(message).end_cell());
}