150 lines
4.6 KiB
Plaintext
150 lines
4.6 KiB
Plaintext
// storage type
|
|
|
|
type threshold is nat
|
|
type max_proposal is nat
|
|
type max_message_size is nat
|
|
type state_hash is bytes
|
|
type addr_set is set (address)
|
|
type message_store is map (bytes, addr_set)
|
|
type proposal_counters is map (address, nat)
|
|
|
|
type storage is
|
|
record [
|
|
state_hash : state_hash;
|
|
threshold : threshold;
|
|
max_proposal : max_proposal;
|
|
max_message_size : max_message_size;
|
|
authorized_addresses : addr_set;
|
|
message_store : message_store;
|
|
proposal_counters : proposal_counters
|
|
]
|
|
|
|
// I/O types
|
|
|
|
type message is bytes -> list (operation)
|
|
type send_pt is message
|
|
type withdraw_pt is message
|
|
type default_pt is unit
|
|
|
|
type return is list (operation) * storage
|
|
|
|
type parameter is
|
|
Send of send_pt
|
|
| Withdraw of withdraw_pt
|
|
| Default of default_pt
|
|
|
|
function send (const param : send_pt; const s : storage) : return is
|
|
block {
|
|
// check sender against the authorized addresses
|
|
|
|
if not Set.mem (Tezos.sender, s.authorized_addresses)
|
|
then failwith("Unauthorized address")
|
|
else skip;
|
|
|
|
// check message size against the stored limit
|
|
|
|
var message : message := param;
|
|
const packed_msg : bytes = Bytes.pack (message);
|
|
if Bytes.length (packed_msg) > s.max_message_size
|
|
then failwith ("Message size exceed maximum limit")
|
|
else skip;
|
|
|
|
(* compute the new set of addresses associated with the message and
|
|
update counters *)
|
|
|
|
var new_store : addr_set := set [];
|
|
|
|
case map_get (packed_msg, s.message_store) of
|
|
Some (voters) ->
|
|
block {
|
|
(* The message is already stored.
|
|
Increment the counter only if the sender is not already
|
|
associated with the message. *)
|
|
if Set.mem (Tezos.sender, voters)
|
|
then skip
|
|
else s.proposal_counters[Tezos.sender] :=
|
|
get_force (Tezos.sender, s.proposal_counters) + 1n;
|
|
new_store := Set.add (Tezos.sender,voters)
|
|
}
|
|
| None ->
|
|
block {
|
|
// the message has never been received before
|
|
s.proposal_counters[sender] :=
|
|
get_force (Tezos.sender, s.proposal_counters) + 1n;
|
|
new_store := set [Tezos.sender]
|
|
}
|
|
end;
|
|
|
|
// check sender counters against the maximum number of proposal
|
|
|
|
var sender_proposal_counter : nat :=
|
|
get_force (Tezos.sender, s.proposal_counters);
|
|
|
|
if sender_proposal_counter > s.max_proposal
|
|
then failwith ("Maximum number of proposal reached")
|
|
else skip;
|
|
|
|
// check the threshold
|
|
|
|
var ret_ops : list (operation) := nil;
|
|
|
|
if Set.cardinal (new_store) >= s.threshold then {
|
|
remove packed_msg from map s.message_store;
|
|
ret_ops := message (s.state_hash);
|
|
// update the state hash
|
|
s.state_hash := Crypto.sha256 (Bytes.concat (s.state_hash, packed_msg));
|
|
// decrement the counters
|
|
for addr -> ctr in map s.proposal_counters block {
|
|
if Set.mem (addr, new_store) then
|
|
s.proposal_counters[addr] := abs (ctr - 1n)
|
|
else skip
|
|
}
|
|
} else s.message_store[packed_msg] := new_store
|
|
} with (ret_ops, s)
|
|
|
|
function withdraw (const param : withdraw_pt; const s : storage) : return is
|
|
block {
|
|
var message : message := param;
|
|
const packed_msg : bytes = Bytes.pack (message);
|
|
|
|
case s.message_store[packed_msg] of
|
|
Some (voters) ->
|
|
block {
|
|
// The message is stored
|
|
const new_set : addr_set = Set.remove (Tezos.sender, voters);
|
|
|
|
(* Decrement the counter only if the sender was already
|
|
associated with the message *)
|
|
|
|
if Set.cardinal (voters) =/= Set.cardinal (new_set)
|
|
then s.proposal_counters[Tezos.sender] :=
|
|
abs (get_force (Tezos.sender, s.proposal_counters) - 1n)
|
|
else skip;
|
|
|
|
(* If the message is left without any associated addresses,
|
|
remove the corresponding message_store field *)
|
|
|
|
if Set.cardinal (new_set) = 0n
|
|
then remove packed_msg from map s.message_store
|
|
else s.message_store[packed_msg] := new_set
|
|
}
|
|
| None -> skip
|
|
end // The message is not stored, ignore.
|
|
} with ((nil : list (operation)), s)
|
|
|
|
function default (const p : default_pt; const s : storage) : return is
|
|
((nil : list (operation)), s)
|
|
|
|
function main (const param : parameter; const s : storage) : return is
|
|
case param of
|
|
(* Propagate message p if the number of authorized addresses having
|
|
voted for the same message p equals the threshold. *)
|
|
| Send (p) -> send (p, s)
|
|
|
|
(* Withraw vote for message p *)
|
|
| Withdraw (p) -> withdraw (p, s)
|
|
|
|
(* Use this action to transfer tez to the contract *)
|
|
| Default (p) -> default (p, s)
|
|
end
|