ligo/src/test/contracts/multisig-v2.ligo
2020-02-27 19:09:14 +01:00

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