;; Multisig wallet smart contract ;; Cleanup expired partial orders ;; messages (cell, ()) ~collect_garbage(cell messages) { var hash = -1; do { (hash, var cs, var ok) = messages.udict_get_next?(256, hash); if (ok) { ;; modeMessage$_ mode:uint8 body:^(Message X) = ModeMessage X; ;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; ;; multiSigWrapperStorage$_ signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X; ;; ;; Skip signatures, check expiry ;; ;; expiry <= now if (cs.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 in_msg) impure { ;; do nothing for internal messages } ;; multiSigWrapper$0 keys_signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapper X; () recv_external(slice in_msg) impure { ;; Check if multiSigWrapper$0 or future unsupported protocol throw_unless(32, in_msg~load_uint(1)); ;; Check if is hme_empty$0 or hme_root$1 ;; Throw if empty signature list throw_unless(33, in_msg.preload_uint(1)); var signatures = in_msg~load_dict(); var message_data = in_msg; ;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:^(Message X) = WrappedMessage X; var (expires_at, msg_seqno) = (message_data~load_uint(32), message_data~load_uint(32)); ;; Message expired throw_if(34, expires_at <= now()); ;; We will need the hash anyway var hash = slice_hash(in_msg); ;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 ^PubKey) messages:(HashmapE 256 ^(StoredMessage X)) = Storage X; var stored_data = get_data().begin_parse(); var (stored_seqno, min_sigs, keys, 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 messages var storedMessageSignatures = new_dict(); ;; If new message, increase seqno if (stored_seqno == msg_seqno) { stored_seqno += 1; ;; If old message } else { var (storedMessage, ok) = messages.udict_get?(256, hash); ;; Throw if old message and doesn't exist in db throw_unless(35, ok); ;; multiSigWrapperStorage$_ signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X; ;; ;; Load signatures var storedMessageSignatures = storedMessage~load_ref().begin_parse().preload_dict(); storedMessage.end_parse(); } accept_message(); message_data~touch(); var idx = -1; do { (idx, var signature, var ok) = signatures.udict_get_next?(4, idx); if (ok) { var (public_key, kok) = keys.udict_get?(4, idx); throw_unless(36, kok); var key = public_key~load_ref().begin_parse().preload_uint(256); var signature_cell = signature~load_ref(); var signature_slice = signature_cell.begin_parse(); var slice_copy = signature_slice; signature_slice.end_parse(); public_key.end_parse(); throw_unless(37, check_signature(hash, slice_copy, key)); storedMessageSignatures~udict_set_ref(4, idx, signature_cell); } } until (~ ok); var (mode, message) = (message_data~load_uint(8), message_data~load_ref()); message_data.end_parse(); var count = 0; var sent = 0; var idx = -1; do { (idx, var signature, var ok) = storedMessageSignatures.udict_get_next?(4, idx); if (ok) { count += 1; if (count >= min_sigs) { send_raw_message(message, mode); messages~udict_delete?(256, hash); ok = 0; } } } until (~ ok); ;; modeMessage$_ mode:uint8 body:^(Message X) = ModeMessage X; ;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; ;; multiSigWrapperStorage$_ signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X; ;; if (count < min_sigs) { messages~udict_set_ref(256, hash, begin_cell().store_dict(storedMessageSignatures).store_uint(expires_at, 32).store_uint(msg_seqno, 32).store_uint(mode, 8).store_ref(message).end_cell()); } messages~collect_garbage(); ;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 ^PubKey) messages:(HashmapE 256 ^(Message X)) = Storage X; set_data(begin_cell().store_uint(stored_seqno + 1, 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); }