2019-11-21 22:42:15 +04:00
|
|
|
// storage type
|
|
|
|
type threshold_t is nat
|
2019-11-26 13:37:25 +04:00
|
|
|
type max_proposal_t is nat
|
|
|
|
type max_message_size_t is nat
|
2019-11-26 14:51:49 +04:00
|
|
|
type state_hash_t is bytes
|
2019-11-21 22:42:15 +04:00
|
|
|
type addr_set_t is set(address)
|
2019-11-26 13:37:25 +04:00
|
|
|
type message_store_t is map(bytes,addr_set_t)
|
|
|
|
type counter_store_t is map(address,nat)
|
2019-11-21 22:42:15 +04:00
|
|
|
|
|
|
|
type storage_t is record
|
2019-11-26 14:51:49 +04:00
|
|
|
state_hash : state_hash_t ;
|
2019-11-21 22:42:15 +04:00
|
|
|
threshold : threshold_t ;
|
2019-11-26 13:37:25 +04:00
|
|
|
max_proposal : max_proposal_t ;
|
|
|
|
max_message_size : max_message_size_t ;
|
2019-11-21 22:42:15 +04:00
|
|
|
auth : addr_set_t ;
|
|
|
|
message_store : message_store_t ;
|
2019-11-26 13:37:25 +04:00
|
|
|
counter_store : counter_store_t ;
|
2019-11-21 22:42:15 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
// I/O types
|
2019-11-26 14:51:49 +04:00
|
|
|
type message_t is (bytes -> list(operation))
|
2019-11-21 22:42:15 +04:00
|
|
|
type send_pt is message_t
|
2019-11-26 13:37:25 +04:00
|
|
|
type withdraw_pt is message_t
|
2019-11-26 15:14:42 +04:00
|
|
|
type default_pt is unit
|
2019-11-21 22:42:15 +04:00
|
|
|
|
|
|
|
type contract_return_t is (list(operation) * storage_t)
|
|
|
|
|
|
|
|
type entry_point_t is
|
|
|
|
| Send of send_pt
|
2019-11-26 13:37:25 +04:00
|
|
|
| Withdraw of withdraw_pt
|
2019-11-26 15:14:42 +04:00
|
|
|
| Default of default_pt
|
2019-11-21 22:42:15 +04:00
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
|
2019-11-21 22:42:15 +04:00
|
|
|
function send (const param : send_pt; const s : storage_t) : contract_return_t is block {
|
2019-11-22 19:02:53 +04:00
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// check sender against the authorized addresses
|
2019-11-22 19:02:53 +04:00
|
|
|
if not set_mem(sender,s.auth) then failwith("Unauthorized address") else skip ;
|
2019-11-21 22:42:15 +04:00
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// check message size against the stored limit
|
|
|
|
var message : message_t := param ;
|
2019-11-26 13:37:25 +04:00
|
|
|
const packed_msg : bytes = bytes_pack(message) ;
|
|
|
|
if size(packed_msg) > s.max_message_size then failwith("Message size exceed maximum limit")
|
|
|
|
else skip ;
|
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// compute the new set of addresses associated with the message and update counters
|
|
|
|
var new_store : addr_set_t := set_empty ;
|
2019-11-26 13:37:25 +04:00
|
|
|
case map_get(packed_msg, s.message_store) of
|
2019-11-26 16:59:00 +04:00
|
|
|
| Some(voters) -> block { // the message is already stored
|
|
|
|
// increment the counter only if the sender isn't already associated with the message
|
2019-11-26 13:37:25 +04:00
|
|
|
if set_mem(sender,voters) then skip
|
|
|
|
else s.counter_store[sender] := get_force(sender,s.counter_store) + 1n ;
|
2019-11-26 16:59:00 +04:00
|
|
|
|
2019-11-26 13:37:25 +04:00
|
|
|
new_store := set_add(sender,voters)
|
2019-11-26 16:59:00 +04:00
|
|
|
}
|
|
|
|
| None -> block { // the message has never been received before
|
2019-11-26 13:37:25 +04:00
|
|
|
s.counter_store[sender] := get_force(sender,s.counter_store) + 1n ;
|
|
|
|
new_store := set [sender];
|
|
|
|
}
|
|
|
|
end ;
|
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// check sender counters against the maximum number of proposal
|
2019-11-26 13:37:25 +04:00
|
|
|
var sender_proposal_counter : nat := get_force(sender,s.counter_store) ;
|
|
|
|
if sender_proposal_counter > s.max_proposal then failwith("Maximum number of proposal reached")
|
|
|
|
else skip ;
|
2019-11-22 19:02:53 +04:00
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// check the threshold
|
|
|
|
var ret_ops : list(operation) := (nil : list(operation)) ;
|
2019-11-22 19:02:53 +04:00
|
|
|
if size(new_store) >= s.threshold then block {
|
|
|
|
remove packed_msg from map s.message_store ;
|
2019-11-26 14:51:49 +04:00
|
|
|
ret_ops := message(s.state_hash) ;
|
2019-11-26 16:59:00 +04:00
|
|
|
// update the state hash
|
2019-11-26 14:51:49 +04:00
|
|
|
s.state_hash := sha_256 ( bytes_concat (s.state_hash , packed_msg) ) ;
|
2019-11-26 16:59:00 +04:00
|
|
|
// reset the counter
|
2019-11-26 13:37:25 +04:00
|
|
|
s.counter_store[sender] := abs (sender_proposal_counter - 1n) ;
|
2019-11-22 19:02:53 +04:00
|
|
|
} else
|
|
|
|
s.message_store[packed_msg] := new_store
|
2019-11-26 13:37:25 +04:00
|
|
|
|
2019-11-21 22:42:15 +04:00
|
|
|
} with ( ret_ops , s)
|
|
|
|
|
2019-11-26 13:37:25 +04:00
|
|
|
function withdraw (const param : withdraw_pt; const s : storage_t) : contract_return_t is block {
|
|
|
|
|
|
|
|
var message : message_t := param ;
|
|
|
|
const packed_msg : bytes = bytes_pack(message) ;
|
|
|
|
|
|
|
|
case map_get(packed_msg, s.message_store) of
|
2019-11-26 16:59:00 +04:00
|
|
|
| Some(voters) -> block { // the message is stored
|
2019-11-26 13:37:25 +04:00
|
|
|
const new_set : addr_set_t = set_remove(sender,voters) ;
|
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// decrement the counter only if the sender was already associated with the message
|
2019-11-26 13:37:25 +04:00
|
|
|
if size(voters) =/= size(new_set) then
|
|
|
|
s.counter_store[sender] := abs (get_force(sender,s.counter_store) - 1n)
|
|
|
|
else skip ;
|
|
|
|
|
2019-11-26 16:59:00 +04:00
|
|
|
// if the message is left without any associated addresses, remove the corresponding message_store field
|
2019-11-26 13:37:25 +04:00
|
|
|
if size(new_set) = 0n then remove packed_msg from map s.message_store
|
|
|
|
else s.message_store[packed_msg] := new_set
|
|
|
|
}
|
2019-11-26 16:59:00 +04:00
|
|
|
| None -> skip end // the message isn't stored, ignore
|
2019-11-26 13:37:25 +04:00
|
|
|
|
|
|
|
} with ( (nil: list(operation)) , s)
|
|
|
|
|
2019-11-26 15:14:42 +04:00
|
|
|
function default (const p : default_pt; const s : storage_t) : contract_return_t is
|
|
|
|
((nil: list(operation)) , s)
|
|
|
|
|
2019-11-21 22:42:15 +04:00
|
|
|
function main(const param : entry_point_t; const s : storage_t) : contract_return_t is
|
|
|
|
case param of
|
2019-11-26 16:59:00 +04:00
|
|
|
// propagate message p if the number authorized addresses having
|
|
|
|
// voted for the same message p equals the threshold
|
2019-11-26 15:14:42 +04:00
|
|
|
| Send (p) -> send(p,s)
|
2019-11-26 16:59:00 +04:00
|
|
|
// withraw vote for message p
|
2019-11-26 13:37:25 +04:00
|
|
|
| Withdraw (p) -> withdraw(p,s)
|
2019-11-26 16:59:00 +04:00
|
|
|
// use this entry-point to transfer tez to the contract
|
2019-11-26 15:14:42 +04:00
|
|
|
| Default (p) -> default(p,s)
|
2019-11-21 22:42:15 +04:00
|
|
|
end
|