2019-10-03 21:54:25 +02:00
|
|
|
;; Multisig wallet smart contract
|
|
|
|
|
2019-10-04 16:12:48 +02:00
|
|
|
;; Cleanup expired partial orders
|
|
|
|
;; messages
|
2019-10-04 19:09:10 +02:00
|
|
|
(cell, ()) ~collect_garbage(cell messages) {
|
2019-10-03 21:54:25 +02:00
|
|
|
var hash = -1;
|
|
|
|
do {
|
2019-10-04 16:12:48 +02:00
|
|
|
(hash, var cs, var ok) = messages.udict_get_next?(256, hash);
|
2019-10-03 21:54:25 +02:00
|
|
|
if (ok) {
|
2019-10-04 19:09:10 +02:00
|
|
|
|
|
|
|
;; 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
|
|
|
|
;;
|
2019-10-03 21:54:25 +02:00
|
|
|
;; expiry <= now
|
2019-10-04 19:09:10 +02:00
|
|
|
if (cs.skip_dict().preload_uint(32) <= now()) {
|
2019-10-04 18:00:14 +02:00
|
|
|
messages~udict_delete?(256, hash);
|
2019-10-03 21:54:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} until (~ ok);
|
|
|
|
|
2019-10-04 19:09:10 +02:00
|
|
|
return (messages, ());
|
2019-10-03 21:54:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
() 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
|
2019-10-04 16:12:48 +02:00
|
|
|
throw_unless(32, in_msg~load_uint(1));
|
2019-10-03 21:54:25 +02:00
|
|
|
|
|
|
|
;; Check if is hme_empty$0 or hme_root$1
|
2019-10-04 16:12:48 +02:00
|
|
|
;; Throw if empty signature list
|
|
|
|
throw_unless(33, in_msg.preload_uint(1));
|
2019-10-03 21:54:25 +02:00
|
|
|
|
|
|
|
var signatures = in_msg~load_dict();
|
2019-10-04 16:12:48 +02:00
|
|
|
var message_data = in_msg;
|
2019-10-03 21:54:25 +02:00
|
|
|
|
2019-10-04 16:12:48 +02:00
|
|
|
;; 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));
|
2019-10-03 21:54:25 +02:00
|
|
|
;; Message expired
|
|
|
|
throw_if(34, expires_at <= now());
|
|
|
|
|
2019-10-04 16:12:48 +02:00
|
|
|
;; We will need the hash anyway
|
2019-10-03 21:54:25 +02:00
|
|
|
var hash = slice_hash(in_msg);
|
2019-10-04 16:12:48 +02:00
|
|
|
|
2019-10-04 18:21:48 +02:00
|
|
|
;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 ^PubKey) messages:(HashmapE 256 ^(StoredMessage X)) = Storage X;
|
2019-10-04 16:12:48 +02:00
|
|
|
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();
|
2019-10-03 21:54:25 +02:00
|
|
|
|
2019-10-04 16:12:48 +02:00
|
|
|
;; This is a new message, so there will be no stored messages
|
|
|
|
var storedMessageSignatures = new_dict();
|
2019-10-03 21:54:25 +02:00
|
|
|
;; If new message, increase seqno
|
|
|
|
if (stored_seqno == msg_seqno) {
|
|
|
|
stored_seqno += 1;
|
2019-10-04 16:12:48 +02:00
|
|
|
|
2019-10-03 21:54:25 +02:00
|
|
|
;; If old message
|
|
|
|
} else {
|
2019-10-04 18:00:14 +02:00
|
|
|
var (storedMessage, ok) = messages.udict_get?(256, hash);
|
2019-10-04 16:12:48 +02:00
|
|
|
;; Throw if old message and doesn't exist in db
|
|
|
|
throw_unless(35, ok);
|
|
|
|
|
2019-10-04 19:09:10 +02:00
|
|
|
;; multiSigWrapperStorage$_ signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X;
|
2019-10-03 21:54:25 +02:00
|
|
|
;;
|
|
|
|
;; Load signatures
|
2019-10-04 19:09:10 +02:00
|
|
|
var storedMessageSignatures = storedMessage~load_ref().begin_parse().preload_dict();
|
2019-10-04 18:00:14 +02:00
|
|
|
storedMessage.end_parse();
|
2019-10-03 21:54:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
accept_message();
|
2019-10-04 16:12:48 +02:00
|
|
|
message_data~touch();
|
|
|
|
|
|
|
|
var idx = -1;
|
|
|
|
do {
|
|
|
|
(idx, var signature, var ok) = signatures.udict_get_next?(4, idx);
|
|
|
|
if (ok) {
|
2019-10-04 18:00:14 +02:00
|
|
|
var (public_key, kok) = keys.udict_get?(4, idx);
|
2019-10-04 16:12:48 +02:00
|
|
|
throw_unless(36, kok);
|
2019-10-04 18:00:14 +02:00
|
|
|
|
|
|
|
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);
|
2019-10-04 16:12:48 +02:00
|
|
|
}
|
|
|
|
} until (~ ok);
|
2019-10-03 21:54:25 +02:00
|
|
|
|
2019-10-04 18:00:14 +02:00
|
|
|
var (mode, message) = (message_data~load_uint(8), message_data~load_ref());
|
2019-10-04 16:12:48 +02:00
|
|
|
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);
|
2019-10-04 18:00:14 +02:00
|
|
|
messages~udict_delete?(256, hash);
|
2019-10-04 16:12:48 +02:00
|
|
|
ok = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} until (~ ok);
|
2019-10-03 21:54:25 +02:00
|
|
|
|
2019-10-04 19:09:10 +02:00
|
|
|
;; 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;
|
2019-10-04 16:12:48 +02:00
|
|
|
;;
|
|
|
|
if (count < min_sigs) {
|
2019-10-04 19:09:10 +02:00
|
|
|
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());
|
2019-10-04 16:12:48 +02:00
|
|
|
}
|
2019-10-04 19:09:10 +02:00
|
|
|
messages~collect_garbage();
|
2019-10-04 16:12:48 +02:00
|
|
|
|
2019-10-04 18:21:48 +02:00
|
|
|
;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 ^PubKey) messages:(HashmapE 256 ^(Message X)) = Storage X;
|
2019-10-04 16:12:48 +02:00
|
|
|
set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(min_sigs, 4).store_dict(keys).store_dict(messages).end_cell());
|
2019-10-03 21:54:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
;; Get methods
|
|
|
|
|
|
|
|
int seqno() method_id {
|
|
|
|
return get_data().begin_parse().preload_uint(32);
|
|
|
|
}
|