The proof of unanimity is a transaction on the blockchain, emitted if and only if all declared signatories have signed a value close enough to the average signed value.
Let
n be the number of signatories
a be the average signed value
d be the percentage of deviation toward a
S be the set of signed values.
The unanimity is reached if:
For example, say 2 signatories must sign a value with an authorized deviation of 2%. Unanimity is reached only in situation 2 below:
The unanimity logic is encoded by a smart contract that manages as many unanimity processes as necessary.
The contract admin address creates a specification for a consensus with:
a unique consensus identifier
the list of required signatories
A signatory signs with:
the consensus id
the value
A “unanimity” transaction is generated when all signatories haves signed a quasi-equal value.
Below is the archetype implementation:
archetype unanimityvariable admin : role = @tz1ZXf37ZNfVupt5GVyrPJ76q8kbjFuD2z7Rasset sig identified by id signatory {id : string;signatory : address;value : int;}asset sig_spec {sid : string;signatories : list<address>;dev : int; /* deviation toward avg. in nb. of percent */expiration : date;signatures : partition<sig> = [];} with {s0 : 0 <= dev <= 100;}entry create_sig (i : string, s : list<address>, d : int) {called by admineffect {sig_spec.add({ sid = i; signatories = s; dev = d; expiration = (now + 1d) });}}function get_signatures(spid : string) : list<address * int> {var l : list<address * int> = [];for s in sig_spec[spid].signatures dol := prepend(l, (sig[s].signatory, sig[s].value));done;return l}function check_sig_data(spid : string) : bool {var avg = sig_spec[spid].signatures.sum(value) / sig_spec[spid].signatures.count();var min = (1 - sig_spec[spid].dev / 200) * avg;var max = (1 + sig_spec[spid].dev / 200) * avg;var check = true;for s in sig_spec[spid].signatures docheck &= min <= sig[s].value <= maxdone;return check}entry unanimity (spid : string, l : list<address * int>, d : int) {called by selfaddress}entry failed (spid : string, l : list<address * int>, d : int) {called by selfaddress}entry sign (spid : string, v : int) {require {r0 : contains(sig_spec[spid].signatories, caller);}effect {sig_spec[spid].signatures.add({spid; caller; v });if sig_spec[spid].signatures.count() = length (sig_spec[spid].signatories) then (var sigs = get_signatures(spid);var d = sig_spec[spid].dev;var selfentry = if check_sig_data (spid) then self.unanimity else self.failed;transfer 0tz to entry selfentry((spid, sigs, d));sig_spec.remove(spid););}}entry clear_expired () {called by admineffect {sig_spec.select(the.expiration < now).clear();}}