forked from wormhole-foundation/native-token-transfers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinbox.move
129 lines (111 loc) · 4.53 KB
/
inbox.move
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
module ntt::inbox {
use sui::clock::Clock;
use sui::table::{Self, Table};
use ntt_common::bitmap::{Self, Bitmap};
use ntt_common::ntt_manager_message::NttManagerMessage;
#[error]
const ETransferCannotBeRedeemed: vector<u8>
= b"Transfer cannot be redeemed yet";
#[error]
const ETransferAlreadyRedeemed: vector<u8>
= b"Transfer already redeemed";
// === Inbox ===
/// The inbox.
/// It's a key-value store where the key is a message containing some
/// payload 'K', and the value is the value 'K' along with information about
/// how many transceivers have voted for it, and whether it has been processed yet.
///
/// For security reasons, a mutable reference to an inbox should never be exposed publicly.
/// This is because priviliged functions are gated by a mutable reference to the inbox.
///
/// In practice, 'K' is instantiated with NativeTokenTransfer, but is written generically
/// here to make reasoning about the code easier (as the inbox doesn't care
/// about the things it stores)
public struct Inbox<K: store + copy + drop> has store {
entries: Table<InboxKey<K>, InboxItem<K>>
}
public fun new<K: store + copy + drop>(ctx: &mut TxContext): Inbox<K> {
Inbox {
entries: table::new(ctx),
}
}
// === Inbox key ===
/// The inbox key is a message from a chain. We include the entire ntt
/// manager message here, not just the payload 'K', because there might be
/// multiple messages with the same content, so the manager message metadata
/// (message id in particular) helps disambiguate. Similarly, manager
/// message ids are only locally unique (per chain), so we also include the
/// origin chain in the key.
///
/// By having transceivers `vote` on messages keyed by the message content,
/// we guarantee that when a particular message receives two votes, both of
/// those votes are actually for the exact same message.
public struct InboxKey<K> has store, copy, drop {
chain_id: u16,
message: NttManagerMessage<K>
}
/// A public constructor for inbox key.
/// No action is privileged by holding an `InboxKey`, so it's safe to make
/// its constructor public.
public fun new_inbox_key<K>(
chain_id: u16,
message: NttManagerMessage<K>
): InboxKey<K> {
InboxKey {
chain_id,
message
}
}
// === Inbox item ===
public struct InboxItem<K> has store {
votes: Bitmap,
release_status: ReleaseStatus,
data: K,
}
public enum ReleaseStatus has copy, drop, store {
NotApproved,
ReleaseAfter(u64),
Released,
}
public fun count_enabled_votes<K>(self: &InboxItem<K>, enabled: &Bitmap): u8 {
let both = self.votes.and(enabled);
both.count_ones()
}
public fun try_release<K>(inbox_item: &mut InboxItem<K>, clock: &Clock): bool {
match (inbox_item.release_status) {
ReleaseStatus::NotApproved => false,
ReleaseStatus::ReleaseAfter(release_timestamp) => {
if (release_timestamp <= clock.timestamp_ms()) {
inbox_item.release_status = ReleaseStatus::Released;
true
} else {
false
}
},
ReleaseStatus::Released => abort ETransferAlreadyRedeemed
}
}
public fun release_after<K>(inbox_item: &mut InboxItem<K>, release_timestamp: u64) {
if (inbox_item.release_status != ReleaseStatus::NotApproved) {
abort ETransferCannotBeRedeemed
};
inbox_item.release_status = ReleaseStatus::ReleaseAfter(release_timestamp);
}
public fun vote<K: store + copy + drop>(inbox: &mut Inbox<K>, transceiver_index: u8, entry: InboxKey<K>) {
let (_, _, data) = entry.message.destruct();
if (!inbox.entries.contains(entry)) {
inbox.entries.add(entry, InboxItem {
votes: bitmap::empty(),
release_status: ReleaseStatus::NotApproved,
data
})
};
inbox.entries.borrow_mut(entry).votes.enable(transceiver_index);
}
public fun borrow_inbox_item_mut<K: store + copy + drop>(inbox: &mut Inbox<K>, key: InboxKey<K>): &mut InboxItem<K> {
inbox.entries.borrow_mut(key)
}
public fun borrow_inbox_item<K: store + copy + drop>(inbox: &Inbox<K>, key: InboxKey<K>): &InboxItem<K> {
inbox.entries.borrow(key)
}
}