From 4b4eed7d7d703b64733f16437d5da63179e3390d Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sun, 3 Nov 2024 17:29:51 +0000 Subject: [PATCH 01/16] started to fix status logic - need to test all status possibilities --- src/app/delrespond/delrespond.page.html | 13 ++++++------- src/app/delrespond/delrespond.page.ts | 10 ++++++++-- src/environments/environment.ts | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/app/delrespond/delrespond.page.html b/src/app/delrespond/delrespond.page.html index 9b54b89f0..07cd99616 100644 --- a/src/app/delrespond/delrespond.page.html +++ b/src/app/delrespond/delrespond.page.html @@ -265,8 +265,7 @@

+

@@ -297,7 +296,7 @@

+

@@ -316,7 +315,7 @@

+

@@ -343,8 +342,8 @@

+ + @@ -357,7 +356,7 @@

+ Date: Mon, 4 Nov 2024 12:30:57 +0000 Subject: [PATCH 02/16] Update delegation.service.ts --- src/app/delegation.service.ts | 111 +++++++++++++++++----------------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index ca4a00f05..4b393b2cd 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -212,65 +212,62 @@ export class DelegationService { // RESPONDING TO A DELEGATION REQUEST: get_incoming_request_status(pid: string, did: string): Array { - if (pid in this.G.P.polls) { - const p = this.G.P.polls[pid]; - if (p.state != 'running') { - return ["closed"]; - } else { - // check if request has been retrieved from db: - const agreement = this.G.Del.get_delegation_agreements_cache(pid).get(did); - if (agreement) { - // check if already answered: - if (agreement.status == 'agreed') { - return ["accepted"]; - } - // check if already delegating (in)directly back to client_vid for at least one option: - var status: Array; - const dirdelmap = this.G.D.direct_delegation_map_caches[pid], - effdelmap = this.G.D.effective_delegation_map_caches[pid], - inveffdelmap = this.G.D.inv_effective_delegation_map_caches[pid], - myvid = p.myvid, - client_vid = agreement.client_vid; - if (client_vid == myvid) { - return ["impossible", "is-self"]; - } - let two_way = false, cycle = false, weight_exceeded = false; - for (let oid of p.oids) { - const effdel_vid = effdelmap.get(oid).get(myvid) || myvid; - const thisinveffdelmap = inveffdelmap.get(oid) || new Map(); - if (1 + (thisinveffdelmap.get(client_vid)||new Set([client_vid])).size - + (thisinveffdelmap.get(effdel_vid)||new Set([effdel_vid])).size - > environment.delegation.max_weight) { - weight_exceeded = true; - break; - } - if ((dirdelmap.get(oid) || new Map()).get(myvid) == client_vid) { - two_way = true; - } else if (effdel_vid == client_vid) { - cycle = true; - } - } - if (weight_exceeded) { - status = ["impossible", "weight-exceeded"]; - } else if (two_way) { - status = ["possible", "two-way"]; - } else if (cycle) { - status = ["possible", "cycle"]; - } else { - status = ["possible", "acyclic"]; - } - if (agreement.status == 'declined') { - status[0] = "declined, " + status[0]; - return status; - } else { - return status; - } - } else { - return ["impossible", "not-in-db"]; - } + if (!(pid in this.G.P.polls)) { + return ["impossible", "poll-unknown"]; + } + const p = this.G.P.polls[pid]; + if (p.state != 'running') { + return ["closed"]; + } + // check if request has been retrieved from db: + const agreement = this.G.Del.get_delegation_agreements_cache(pid).get(did); + if (!agreement) { + return ["impossible", "not-in-db"]; + } + // check if already answered: + if (agreement.status == 'agreed') { + return ["accepted"]; + } + // check if already delegating (in)directly back to client_vid for at least one option: + var status: Array; + const dirdelmap = this.G.D.direct_delegation_map_caches[pid], + effdelmap = this.G.D.effective_delegation_map_caches[pid], + inveffdelmap = this.G.D.inv_effective_delegation_map_caches[pid], + myvid = p.myvid, + client_vid = agreement.client_vid; + if (client_vid == myvid) { + return ["impossible", "is-self"]; + } + let two_way = false, cycle = false, weight_exceeded = false; + for (let oid of p.oids) { + const effdel_vid = effdelmap.get(oid).get(myvid) || myvid; + const thisinveffdelmap = inveffdelmap.get(oid) || new Map(); + if (1 + (thisinveffdelmap.get(client_vid)||new Set([client_vid])).size + + (thisinveffdelmap.get(effdel_vid)||new Set([effdel_vid])).size + > environment.delegation.max_weight) { + weight_exceeded = true; + break; } + if ((dirdelmap.get(oid) || new Map()).get(myvid) == client_vid) { + two_way = true; + } else if (effdel_vid == client_vid) { + cycle = true; + } + } + if (weight_exceeded) { + status = ["impossible", "weight-exceeded"]; + } else if (two_way) { + status = ["possible", "two-way"]; + } else if (cycle) { + status = ["possible", "cycle"]; } else { - return ["impossible", "poll-unknown"]; + status = ["possible", "acyclic"]; + } + if (agreement.status == 'declined') { + status[0] = "declined, " + status[0]; + return status; + } else { + return status; } } From 6cd84f96dacf6d16467008ab2eda37f998fa32c4 Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Tue, 5 Nov 2024 16:07:52 +0000 Subject: [PATCH 03/16] temporarily disabled cycle checking when revoking delegations --- couchdb/_config.json | 3 ++- src/app/poll.service.ts | 10 ++++++---- src/app/poll/poll.page.html | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/couchdb/_config.json b/couchdb/_config.json index 68b397f6f..ae81988ee 100644 --- a/couchdb/_config.json +++ b/couchdb/_config.json @@ -5,7 +5,8 @@ "socket_options": "[{nodelay, true}]" }, "chttpd": { - "require_valid_user": true + "require_valid_user": false, + "delayed_commits": false }, "cors": { "origins": "*", diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 02cd2a1a2..d48d2c5dc 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -556,13 +556,15 @@ export class Poll { } get am_abstaining(): boolean { - /** whether or not I'm currently abstaining */ + /** whether or not I'm currently abstaining and haven't delegated */ + if (this.have_delegated) { + return false; + } if (!!this.T.votes_map) { const myvote = this.T.votes_map.get(this.myvid); return myvote === undefined; - } else { - return false; } + return false; } get my_n_rated_positive(): number { @@ -1076,7 +1078,7 @@ export class Poll { inv_eff_d_map = this.inv_effective_delegation_map.get(oid), inv_eff_ds_of_client = inv_eff_d_map.get(client_vid), inv_eff_ds_of_old_eff_d_of_client = inv_eff_d_map.get(old_eff_d_of_client), - is_on_cycle = ind_d_map.get(old_d_vid).has(client_vid); + is_on_cycle = false; this.G.L.debug("del_delegation entry", this.pid, oid, client_vid, old_d_vid, is_on_cycle); diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index 7de22cb6b..97e5ac198 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -151,6 +151,10 @@ + + +

DelegatedNum: {{this.n_delegated}} {{oidsorted.length}}

+

From e8262b7195045b0345770468cdab428c2660ddf2 Mon Sep 17 00:00:00 2001 From: Hemanath Peddireddi Date: Fri, 8 Nov 2024 12:53:05 +0000 Subject: [PATCH 04/16] Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index d974fa7cc..92d42fcdc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,4 +1,4 @@ -# Installation +# Installatio *End users: On our end user website [vodle.it](http://vodle.it), you can directly use vodle in your browser.* (Later on, we will also provide a smartphone app.) From c23b878b245e800554ecddfdab82c71cc9c71f0e Mon Sep 17 00:00:00 2001 From: Hemanath Peddireddi Date: Fri, 8 Nov 2024 12:57:08 +0000 Subject: [PATCH 05/16] Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 92d42fcdc..d974fa7cc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,4 +1,4 @@ -# Installatio +# Installation *End users: On our end user website [vodle.it](http://vodle.it), you can directly use vodle in your browser.* (Later on, we will also provide a smartphone app.) From cc6c2329f2d5c23e90103787faa9b205d60aaebb Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Thu, 21 Nov 2024 15:01:50 +0000 Subject: [PATCH 06/16] working for non-cycles --- couchdb/_config.json | 3 +- src/app/data.service.ts | 11 ++- src/app/delegation.service.ts | 38 +++++++++- src/app/delrespond/delrespond.page.html | 14 +++- src/app/mypolls/mypolls.page.ts | 11 +++ src/app/poll.service.ts | 95 +++++++++++++++++++++---- src/app/poll/poll.page.html | 7 +- src/app/poll/poll.page.ts | 8 ++- src/assets/i18n/en.json | 5 +- 9 files changed, 160 insertions(+), 32 deletions(-) diff --git a/couchdb/_config.json b/couchdb/_config.json index ae81988ee..68b397f6f 100644 --- a/couchdb/_config.json +++ b/couchdb/_config.json @@ -5,8 +5,7 @@ "socket_options": "[{nodelay, true}]" }, "chttpd": { - "require_valid_user": false, - "delayed_commits": false + "require_valid_user": true }, "cors": { "origins": "*", diff --git a/src/app/data.service.ts b/src/app/data.service.ts index 684619564..b41a2bec0 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1557,6 +1557,7 @@ export class DataService implements OnDestroy { } getv(pid: string, key: string, vid?: string): string { + console.log("getv", pid, key, vid) // get own voter data item let value = null; if (this.pid_is_draft(pid)) { @@ -1568,10 +1569,11 @@ export class DataService implements OnDestroy { // other polls' data is stored in poll's own database. // construct key for poll db: const pkey = this.get_voter_key_prefix(pid, vid) + key; -// this.G.L.trace("getv", pid, key, vid, pkey) + // this.G.L.trace("getv", pid, key, vid, pkey) this.ensure_poll_cache(pid); value = this.poll_caches[pid][pkey] || ''; } + console.log("getv", pid, key, vid, value); return value; } @@ -1591,8 +1593,11 @@ export class DataService implements OnDestroy { } } - delv(pid: string, key: string) { - // delete a voter data item + delv(pid: string, key: string, vid?: string) { + if(!this.getv(pid, key)) { + this.G.L.warn("DataService.delv nothing to delete", pid, key); + return; + } if (this.pid_is_draft(pid)) { const ukey = get_poll_key_prefix(pid) + this.get_voter_key_prefix(pid) + key; delete this.user_cache[ukey]; diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index 4b393b2cd..8eb0626ac 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -187,6 +187,10 @@ export class DelegationService { revoke_delegation(pid: string, did: string, oid: string) { this.G.L.entry("DelegationService.revoke_delegation", pid, did); const a = this.get_delegation_agreements_cache(pid).get(did); + if (!a) { + this.G.L.error("DelegationService.revoke_delegation without agreement", pid, did); + return; + } const p = this.G.P.polls[pid]; if ((a.client_vid != p.myvid)) { this.G.L.error("DelegationService.revoke_delegation without request from me", pid, did); @@ -207,6 +211,9 @@ export class DelegationService { if (dcache) { dcache.delete(oid); } + + this.G.D.direct_delegation_map_caches[pid].get(oid).delete(p.myvid); + this.G.D.setv(pid, "del_status." + did, "revoked"); // used to update the delegatee's screen; } // RESPONDING TO A DELEGATION REQUEST: @@ -224,10 +231,20 @@ export class DelegationService { if (!agreement) { return ["impossible", "not-in-db"]; } + + // check if request has already been revoked + const revoked = this.G.D.getv(pid, "del_status." + did, agreement.client_vid); + if (revoked == "revoked") { + return ["impossible", "revoked"]; + } + // check if already answered: if (agreement.status == 'agreed') { return ["accepted"]; } + + const a = this.get_agreement(pid, did); + // check if already delegating (in)directly back to client_vid for at least one option: var status: Array; const dirdelmap = this.G.D.direct_delegation_map_caches[pid], @@ -250,16 +267,24 @@ export class DelegationService { } if ((dirdelmap.get(oid) || new Map()).get(myvid) == client_vid) { two_way = true; - } else if (effdel_vid == client_vid) { + } + } + // check for cycles: + for (let oid of p.oids){ + const thisindirdelmap = this.G.D.inv_indirect_delegation_map_caches[pid].get(oid).get(a.delegate_vid) || new Set(); + if (thisindirdelmap.has(a.client_vid)) { cycle = true; + break; } } + if (weight_exceeded) { status = ["impossible", "weight-exceeded"]; } else if (two_way) { - status = ["possible", "two-way"]; + status = ["impossible", "two-way"]; } else if (cycle) { - status = ["possible", "cycle"]; + console.log("cycle detected"); + status = ["impossible", "cycle"]; } else { status = ["possible", "acyclic"]; } @@ -619,6 +644,13 @@ export class DelegationService { return this.G.D.outgoing_dids_caches[pid]; } + get_my_incoming_dids_cache(pid:string) { + if (!this.G.D.incoming_dids_caches[pid]) { + this.G.D.incoming_dids_caches[pid] = new Map(); + } + return this.G.D.incoming_dids_caches[pid]; + } + get_delegation_agreements_cache(pid:string) { if (!this.G.D.delegation_agreements_caches[pid]) { this.G.D.delegation_agreements_caches[pid] = new Map(); diff --git a/src/app/delrespond/delrespond.page.html b/src/app/delrespond/delrespond.page.html index 07cd99616..14c8709b2 100644 --- a/src/app/delrespond/delrespond.page.html +++ b/src/app/delrespond/delrespond.page.html @@ -70,9 +70,9 @@

+

@@ -111,7 +111,7 @@

@@ -365,5 +365,13 @@

  + + + + + +   + + diff --git a/src/app/mypolls/mypolls.page.ts b/src/app/mypolls/mypolls.page.ts index 3caeb53c9..e5722b8f5 100644 --- a/src/app/mypolls/mypolls.page.ts +++ b/src/app/mypolls/mypolls.page.ts @@ -87,14 +87,25 @@ export class MypollsPage implements OnInit { this.unanswered_requests = []; for (const pid in this.G.P.polls) { const cache = this.G.D.incoming_dids_caches[pid]; + var newcache = this.G.D.incoming_dids_caches[pid]; if (cache) { for (let [did, [from, url, status]] of cache) { if (["possible","two-way","cycle"].includes(status)) { + console.log("onDataChange", pid, did, from, url, status); this.G.L.trace("MypollsPage.onDataChange found unanswered request", did, from, url, status); + // check if request has been revoked: + const a = this.G.Del.get_agreement(pid, did); + if (this.G.D.getv(pid, "del_status." + did, a.client_vid) === 'revoked') { + // delete request from cache: + newcache.delete(did); + continue; + } + this.unanswered_requests.push({pid:pid, did:did, from:from, url:url, status:status}); } } } + this.G.D.incoming_dids_caches[pid] = newcache; // update cache to remove revoked requests } } diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index d48d2c5dc..4a7451d12 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -469,18 +469,18 @@ export class Poll { // will only be called by the option itself to self-register in its poll! if (o.oid in this._options) { return false; - } else { - this._options[o.oid] = o; - if (!this.own_ratings_map.has(o.oid)) this.own_ratings_map.set(o.oid, new Map()); - if (!this.proxy_ratings_map.has(o.oid)) this.proxy_ratings_map.set(o.oid, new Map()); - if (!this.direct_delegation_map.has(o.oid)) this.direct_delegation_map.set(o.oid, new Map()); - if (!this.inv_direct_delegation_map.has(o.oid)) this.inv_direct_delegation_map.set(o.oid, new Map()); - if (!this.indirect_delegation_map.has(o.oid)) this.indirect_delegation_map.set(o.oid, new Map()); - if (!this.inv_indirect_delegation_map.has(o.oid)) this.inv_indirect_delegation_map.set(o.oid, new Map()); - if (!this.effective_delegation_map.has(o.oid)) this.effective_delegation_map.set(o.oid, new Map()); - if (!this.inv_effective_delegation_map.has(o.oid)) this.inv_effective_delegation_map.set(o.oid, new Map()); - return true; } + + this._options[o.oid] = o; + if (!this.own_ratings_map.has(o.oid)) this.own_ratings_map.set(o.oid, new Map()); + if (!this.proxy_ratings_map.has(o.oid)) this.proxy_ratings_map.set(o.oid, new Map()); + if (!this.direct_delegation_map.has(o.oid)) this.direct_delegation_map.set(o.oid, new Map()); + if (!this.inv_direct_delegation_map.has(o.oid)) this.inv_direct_delegation_map.set(o.oid, new Map()); + if (!this.indirect_delegation_map.has(o.oid)) this.indirect_delegation_map.set(o.oid, new Map()); + if (!this.inv_indirect_delegation_map.has(o.oid)) this.inv_indirect_delegation_map.set(o.oid, new Map()); + if (!this.effective_delegation_map.has(o.oid)) this.effective_delegation_map.set(o.oid, new Map()); + if (!this.inv_effective_delegation_map.has(o.oid)) this.inv_effective_delegation_map.set(o.oid, new Map()); + return true; } get options(): Record { return this._options; } @@ -596,6 +596,74 @@ export class Poll { return (agreement.status == "agreed") && (agreement.active_oids.size == agreement.accepted_oids.size); } + getDidFromUrl(url: string): string | null { + // Match the structure of the URL to extract the second item after '/delrespond/' + const regex = /\/delrespond\/[^/]+\/([^/]+)/; + + const match = url.match(regex); + if (match && match[1]) { + return match[1]; // Return the 'did' + } + + return null; // Return null if no match is found + } + + have_been_delegated(clientVid: string, listOfDelInv: object[]) { + var ret = false; + this.G.L.entry("Poll.have_been_delegated", clientVid, listOfDelInv); + // check if clientVid has been delegated to by any of the vids in listOfDelInv + for (const delInv of listOfDelInv) { + const url = delInv["url"]; + const did = this.getDidFromUrl(url); + console.log("url: ", url); + + const agreement = this.G.Del.get_agreement(this._pid, did); + + const delId = agreement.client_vid; + const s = this.G.D.getv(this._pid, "del_status." + did, delId); + if (!s){ + ret = ret || agreement.active_oids.size > 0; + continue; + } + if(["agreed", "pending"].includes(agreement.status) && s == "revoked"){ + this.G.D.incoming_dids_caches[this.pid].get(did)[2] = "revoked"; + ret = false; + + // remove from local cache + for (const oid of this.oids) { + // remove from direct + this.direct_delegation_map.get(oid).delete(delId); + + // remove from indirect + const clientInvDelMap = this.inv_indirect_delegation_map.get(oid).get(clientVid); + const delInvMap = this.inv_indirect_delegation_map.get(oid).get(agreement.client_vid); + var setDiff = new Set([]); + clientInvDelMap.forEach((value, key) => { + console.log("key: ", key); + console.log("value: ", value); + if(!delInvMap.has(key) && value != delId){ + setDiff.add(value); + } + }); + this.inv_indirect_delegation_map.get(oid).set(clientVid, setDiff); + this.G.D.inv_direct_delegation_map_caches[this.pid].get(oid).set(clientVid, setDiff); + } + + // add a notification that the delegation has been revoked + this.G.N.add({ + class: 'delegation_declined', + pid: this.pid, + title: this.G.Del.translate.instant('news-title.delegation_revoked', {nickname: delInv["from"]}) + }); + }else{ + ret = true; + } + } + return ret; + } + + + ratings_have_changed = false; // OTHER HOOKS: @@ -1061,6 +1129,7 @@ export class Poll { } del_delegation(client_vid: string, oid: string) { + console.log("del_delegation", client_vid, oid); // Called whenever a voter revokes her delegation for some option const dir_d_map = this.direct_delegation_map.get(oid), eff_d_map = this.effective_delegation_map.get(oid); @@ -1078,11 +1147,13 @@ export class Poll { inv_eff_d_map = this.inv_effective_delegation_map.get(oid), inv_eff_ds_of_client = inv_eff_d_map.get(client_vid), inv_eff_ds_of_old_eff_d_of_client = inv_eff_d_map.get(old_eff_d_of_client), + // is_on_cycle = ind_d_map.get(old_d_vid).has(client_vid); is_on_cycle = false; this.G.L.debug("del_delegation entry", this.pid, oid, client_vid, old_d_vid, is_on_cycle); // deregister DIRECT delegation and inverse of vid: + this.direct_delegation_map.get(oid).delete(client_vid); dir_d_map.delete(client_vid); inv_dir_d_map.get(old_d_vid).delete(client_vid); @@ -1165,7 +1236,7 @@ export class Poll { this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); } } - + get_n_indirect_option_clients(vid: string, oid: string): number { /** count how many voters have indirectly delegated to vid for oid */ return (this.inv_indirect_delegation_map.get(oid).get(vid)||new Set()).size; diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index 97e5ac198..c1c643896 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -151,11 +151,6 @@ - - -

DelegatedNum: {{this.n_delegated}} {{oidsorted.length}}

-
-

@@ -466,7 +461,7 @@

- diff --git a/src/app/poll/poll.page.ts b/src/app/poll/poll.page.ts index 79085c878..9e52ad659 100644 --- a/src/app/poll/poll.page.ts +++ b/src/app/poll/poll.page.ts @@ -56,7 +56,8 @@ export class PollPage implements OnInit { delegate: string; delegation_status = "none"; - n_indirect_clients = 1; + have_been_delegated = false; + n_indirect_clients = 0; accepted_requests = []; declined_requests = []; @@ -184,6 +185,7 @@ export class PollPage implements OnInit { if (this.p.has_results) { this.p.have_seen_results = true; } + this.have_been_delegated = this.p.have_been_delegated(this.p.myvid, this.accepted_requests); this.G.L.exit("PollPage.onDataReady"); } @@ -191,6 +193,7 @@ export class PollPage implements OnInit { this.G.L.entry("PollPage.onDataChange"); this.p.tally_all(); this.update_order(); + this.have_been_delegated = this.p.have_been_delegated(this.p.myvid, this.accepted_requests); this.update_delegation_info(); this.news = this.G.N.filter({pid: this.pid}); this.changeDetector.detectChanges(); @@ -772,9 +775,12 @@ export class PollPage implements OnInit { text: this.translate.instant('OK'), role: 'Ok', handler: () => { + console.log('Confirm Ok.'); this.G.Del.revoke_delegation(this.pid, this.G.Del.get_my_outgoing_dids_cache(this.pid).get("*"), '*'); + console.log('Delegation revoked.'); this.delegate = null; this.delegation_status = 'none'; + console.log("id: ", this.p.myvid); this.update_delegation_info(); this.G.D.save_state(); } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 852dc261d..0278b649d 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -232,7 +232,8 @@ "poll-unknown": "You are not participating in this poll yet. Please check if you have received an invitation link for this poll. If so, use that link first and then try this one again.", "try-again": "We apologize, vodle is still waiting for some data on this request. Please try this link again later.", "closed": "Unfortunately, this poll has already ended.", - "is-self": "This request was created by yourself :-) Please send this link to the person youʼd like to ask for delegation!" + "is-self": "This request was created by yourself :-) Please send this link to the person youʼd like to ask for delegation!", + "been-revoked": "Unfortunately, this request has already been revoked." }, "draftpoll": { "_COMMENT_SECTION_": "[COMMENT] strings used on the Edit Draft Poll page:", @@ -506,7 +507,7 @@ "delegation_accepted": "{{nickname}} accepted your delegation request.", "delegation_declined": "Unfortunately, {{nickname}} declined your delegation request.", "delegation_accepted_after_all": "{{nickname}} has now accepted your delegation request after all.", - "delegation_revoked": "Unfortunately, {{nickname}} revoked her earlier agreement to your delegation request." + "delegation_revoked": "Unfortunately, {{nickname}} revoked their delegation to you." }, "news-body": { "_COMMENT_SECTION_": "[COMMENT] strings used as bodies of News items:", From c604de6b86d9f64f46ae34bca77e5d81706bf991 Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Thu, 21 Nov 2024 16:57:10 +0000 Subject: [PATCH 07/16] Cycle error checking implemented after clicking on delegation link --- src/app/delegation.service.ts | 16 ++++++++++++++-- src/app/delrespond/delrespond.page.ts | 7 +++++++ src/app/poll/poll.page.ts | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index 8eb0626ac..1aa8197cf 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -271,8 +271,10 @@ export class DelegationService { } // check for cycles: for (let oid of p.oids){ - const thisindirdelmap = this.G.D.inv_indirect_delegation_map_caches[pid].get(oid).get(a.delegate_vid) || new Set(); - if (thisindirdelmap.has(a.client_vid)) { + console.log("delID: ", p.myvid); + const invindirdelmap = p.inv_effective_delegation_map.get(oid).get(a.client_vid) || new Map(); + console.log("MAP: ", invindirdelmap); + if (invindirdelmap.has(p.myvid)) { cycle = true; break; } @@ -334,6 +336,16 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); } + decline_due_to_error(pid: string, did: string, private_key?: string) { + if (!private_key) { + private_key = this.get_private_key(pid, did); + } + const response = {option_spec: {type: "+", oids: []}} as del_response_t, // i.e., accept NO oids + signed_response = this.sign_response(response, private_key); + this.G.L.info("DelegationService.decline_due_to_error", pid, did, response); + this.set_my_signed_response(pid, did, signed_response); + } + // DATA HANDLING: get_delegate_nickname(pid: string, did: string): string { diff --git a/src/app/delrespond/delrespond.page.ts b/src/app/delrespond/delrespond.page.ts index 1038a169f..d31cd0026 100644 --- a/src/app/delrespond/delrespond.page.ts +++ b/src/app/delrespond/delrespond.page.ts @@ -117,6 +117,13 @@ export class DelrespondPage implements OnInit { this.router.navigate(["/poll/" + this.pid]); } + // TODO: use to send a different message to the delegator + decline_due_to_error() { + /** store negative response and go to poll page */ + this.G.Del.decline_due_to_error(this.pid, this.did, this.private_key); + this.router.navigate(["/poll/" + this.pid]); + } + dismiss() { this.router.navigate(["/mypolls"]); } diff --git a/src/app/poll/poll.page.ts b/src/app/poll/poll.page.ts index 9e52ad659..e5f2cfb70 100644 --- a/src/app/poll/poll.page.ts +++ b/src/app/poll/poll.page.ts @@ -195,6 +195,7 @@ export class PollPage implements OnInit { this.update_order(); this.have_been_delegated = this.p.have_been_delegated(this.p.myvid, this.accepted_requests); this.update_delegation_info(); + this.update_data(); this.news = this.G.N.filter({pid: this.pid}); this.changeDetector.detectChanges(); this.G.L.exit("PollPage.onDataChange"); @@ -234,6 +235,13 @@ export class PollPage implements OnInit { this.G.L.exit("PollPage.ionViewDidLeave"); } + update_data(){ + const m = this.G.D.inv_direct_delegation_map_caches[this.pid] + for (const oid of this.oidsorted) { + const oidm = m.get(oid); + } + } + update_delegation_info() { // determine own weight: this.n_indirect_clients = this.p.get_n_indirect_clients(this.p.myvid); From f9d52a68677f408e85ec31c478b1cbd01120c4da Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sun, 1 Dec 2024 13:29:08 +0000 Subject: [PATCH 08/16] added a shared map shared map holds all the indirect delegations and is stored in the db --- .../_design/vodle/validate_doc_update.js | 6 +- src/app/data.service.ts | 81 +++++- src/app/delegation.service.ts | 156 ++++++++--- src/app/delrespond/delrespond.page.ts | 1 + src/app/poll.service.ts | 242 +++++++++++------- src/app/poll/poll.page.html | 4 +- src/app/poll/poll.page.ts | 32 ++- src/assets/i18n/en.json | 4 +- src/environments/environment.ts | 3 +- 9 files changed, 382 insertions(+), 147 deletions(-) diff --git a/couchdb/vodle/_design/vodle/validate_doc_update.js b/couchdb/vodle/_design/vodle/validate_doc_update.js index 672453666..a2038fd60 100644 --- a/couchdb/vodle/_design/vodle/validate_doc_update.js +++ b/couchdb/vodle/_design/vodle/validate_doc_update.js @@ -38,9 +38,9 @@ function (newDoc, savedDoc, userCtx) { } */ } else { - // if doc already exists, let noone update or delete it: - if (savedDoc) { - throw ({forbidden: 'Noone may update or delete existing poll documents.'}) + // if doc already exists, let noone update or delete it, unless its delegation map: + if (savedDoc && !_id.endsWith("shared_map")) { + throw ({forbidden: 'Noone may update or delete existing poll documents.'}); } // let only the voters create it: let doc_pid = _id.substring(pollprefix.length, _id.indexOf("§")); diff --git a/src/app/data.service.ts b/src/app/data.service.ts index b41a2bec0..4243f3433 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -214,12 +214,12 @@ function myhash(what): string { export type del_option_spec_t = {type: "+" | "-", oids: Array}; export type del_request_t = {option_spec: del_option_spec_t, public_key: string}; -export type del_response_t = {option_spec: del_option_spec_t}; +export type del_response_t = {option_spec: del_option_spec_t, status: "agreed" | "declined" | "revoked", "decline_cycle", "decline_self"}; export type del_signed_response_t = string; export type del_agreement_t = { // by pid, did client_vid?: string, delegate_vid?: string, - status?: "pending" | "agreed" | "declined" | "revoked", + status?: "pending" | "agreed" | "declined" | "revoked" | "declined_cycle" | "declined_self", accepted_oids?: Set, // oids accepted for delegation by delegate active_oids?: Set // among those, oids currently activated for delegation by client }; @@ -1522,7 +1522,9 @@ export class DataService implements OnDestroy { } else { this.G.L.error("DataService.setp change option attempted for existing entry", pid, key, value); } - } else { + } if (key == 'shared_map') { + return this._setp_in_polldb(pid, key, value); + }else { this.G.L.error("DataService.setp non-local attempted for non-draft poll", pid, key, value); } } @@ -1578,6 +1580,7 @@ export class DataService implements OnDestroy { } setv(pid: string, key: string, value: string): boolean { + console.log("setv", pid, key, value) /** Set a voter data item. * If necessary, mark the database entry with poll's due date * to allow couchdb validating that due date is not passed. @@ -1593,6 +1596,59 @@ export class DataService implements OnDestroy { } } + set_shared_map(pid: string, val: Map) { + const mapKey = `poll.${pid}.shared_map`; + this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries()))); + } + + set_effective_delegation(pid: string, vid: string, val: string[]) { + const mapKey = `poll.${pid}.shared_map`; + const currentMap = this.getSharedMap(pid); + + currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair + // this.setp(pid, 'shared_map', JSON.stringify(Array.from(currentMap.entries()))); + // this.setv_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries()))); + // this.store_poll_data(pid, mapKey, currentMap, mapKey, false); + this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries()))); + } + + getSharedMap(pid: string): Map { + const cache = this.poll_caches[pid]['poll.' + pid + '.shared_map'] || '[]'; + const ps = cache ? JSON.parse(cache) : {}; + const mp = new Map(ps); + console.log("getSharedMap", pid, mp); + return mp; + let result = new Map(); // Default value if async fails or doesn't resolve in time + + this.getSharedMapAsync(pid) + .then(map => { + result = map; // Set the resolved map to result + }) + .catch(err => { + console.error('Error fetching shared map:', err); + }); + + return result; // Return the default or updated result + } + + async getSharedMapAsync(pid: string): Promise> { + console.log("getSharedMap_pid", pid); + const db = this.get_local_poll_db(pid); // Retrieve the local PouchDB instance for the poll. + // const docId = '~vodle.poll.e031c7§poll.e031c7.shared_map'; + const docId = '~vodle.poll.' + pid + '§poll.' + pid + '.shared_map'; + return db.get(docId).then(doc => { + console.log(doc); + const val = decrypt(doc.value, this.user_cache[get_poll_key_prefix(pid) + 'password']); + console.log("shared_val:", val); + const map = new Map(JSON.parse(val)); + console.log("shared_fin_map:", map); + return map; + } + ).catch(err => { + console.log("getSharedMap", err); + }); + } + delv(pid: string, key: string, vid?: string) { if(!this.getv(pid, key)) { this.G.L.warn("DataService.delv nothing to delete", pid, key); @@ -1659,6 +1715,18 @@ export class DataService implements OnDestroy { return this.store_user_data(ukey, this.user_cache, ukey); } + setv_global_for_poll(pid: string, key: string, value: string): boolean { + // set global voter data item in poll db: + value = value || ''; + // construct key for poll db: + // return 'voter.' + (vid ? vid : this.getp(pid, 'myvid')) + "§"; + const pkey = "§"; + this.ensure_poll_cache(pid); + this.G.L.trace("DataService.setv_global_for_poll", pid, key, value); + this.poll_caches[pid][pkey] = value; + return this.store_poll_data(pid, pkey, this.poll_caches[pid], pkey, false); + } + setv_in_polldb(pid: string, key: string, value: string, vid?: string): boolean { /** Set voter data item in poll db. * If necessary, mark the database entry with poll's due date @@ -1775,6 +1843,9 @@ export class DataService implements OnDestroy { this.page.onDataChange(); } } + if (change.docs.some((doc) => doc._id.endsWith('shared_set'))) { + console.log('shared_set has been updated', change.docs); + } this.G.L.exit("DataService.handle_poll_db_change", pid, this.pending_changes); } @@ -2322,7 +2393,7 @@ export class DataService implements OnDestroy { // key existed in poll db, check whether update is allowed. const value = dict[dict_key]; const enc_value = encrypt(value, poll_pw); - if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value)) { + if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value) && (key.indexOf("shared_map") == -1)) { // this is not allowed for poll docs! this.G.L.error("DataService.store_poll_data tried changing an existing poll data item", pid, key, value); } else if ((key == 'due') && (doc.due != value)) { @@ -2384,7 +2455,7 @@ export class DataService implements OnDestroy { // check which voter's data this is: const vid_prefix = key.slice(0, key.indexOf("§")), vid = this.user_cache[get_poll_key_prefix(pid) + 'myvid']; - if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true') { + if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true' && key.indexOf("shared_map") == -1) { // it is not allowed to alter other voters' data! this.G.L.error("DataService.store_poll_data tried changing another voter's data item", pid, key); diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index 1aa8197cf..dcf815cbf 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -186,34 +186,68 @@ export class DelegationService { revoke_delegation(pid: string, did: string, oid: string) { this.G.L.entry("DelegationService.revoke_delegation", pid, did); - const a = this.get_delegation_agreements_cache(pid).get(did); - if (!a) { - this.G.L.error("DelegationService.revoke_delegation without agreement", pid, did); - return; + // const a = this.get_delegation_agreements_cache(pid).get(did); + // if (!a) { + // this.G.L.error("DelegationService.revoke_delegation without agreement", pid, did); + // return; + // } + // const p = this.G.P.polls[pid]; + // if ((a.client_vid != p.myvid)) { + // this.G.L.error("DelegationService.revoke_delegation without request from me", pid, did); + // } else { + // this.G.D.delv(pid, "del_request." + did); + // const acache = this.get_delegation_agreements_cache(pid); + // if (acache) { + // const oids = acache.get(did).active_oids; + // if (oids) { + // for (const oid of oids) { + // p.del_delegation(p.myvid, oid); + // } + // } + // acache.delete(did); + // } + // } + // const dcache = this.get_my_outgoing_dids_cache(pid); + // if (dcache) { + // dcache.delete(oid); + // } + + const private_key = this.get_private_key(pid, did); + const response = {status:"revoked"} as del_response_t, // i.e., exclude no oids, meaning accept all oids. TODO: allow partial acceptance for only some options + signed_response = this.sign_response(response, private_key); + this.set_my_signed_response(pid, did, signed_response); + this.G.D.setv(pid, "del_status." + did, "revoked"); // store in db that delegation was revoked + // update effectve delegation for delegate_vid + const a = this.get_agreement(pid, did); + const sm = this.G.D.getSharedMap(pid); + const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); + + var stack = []; + for (let id of sm.keys()) { + if (JSON.parse(sm.get(id) || "[]").includes(a.client_vid)) { + stack.push(id); + } } - const p = this.G.P.polls[pid]; - if ((a.client_vid != p.myvid)) { - this.G.L.error("DelegationService.revoke_delegation without request from me", pid, did); - } else { - this.G.D.delv(pid, "del_request." + did); - const acache = this.get_delegation_agreements_cache(pid); - if (acache) { - const oids = acache.get(did).active_oids; - if (oids) { - for (const oid of oids) { - p.del_delegation(p.myvid, oid); - } + while (stack.length > 0) { + const curr_del = stack.pop(); + console.log("shared_revoked_curr_del: ", curr_del); + const old_delegate_eff_set = new Set(JSON.parse(sm.get(curr_del) || "[]")); + var new_delegate_eff_set = new Set(); + for (let id of old_delegate_eff_set) { + if (id == a.client_vid || eff_set.has(id)) { + continue; } - acache.delete(did); + new_delegate_eff_set.add(id); } + sm.set(curr_del, JSON.stringify(Array.from(new_delegate_eff_set))); } - const dcache = this.get_my_outgoing_dids_cache(pid); - if (dcache) { - dcache.delete(oid); - } - this.G.D.direct_delegation_map_caches[pid].get(oid).delete(p.myvid); - this.G.D.setv(pid, "del_status." + did, "revoked"); // used to update the delegatee's screen; + this.G.D.set_shared_map(pid, sm); + + // this.G.D.setv_in_polldb(pid, "del_effective." + a.delegate_vid, JSON.stringify(Array.from(new_delegate_eff_set)), "vodle"); + // this.handle_deleted_delegation(pid, did); + //console.log("revoked: ", this.G.D.getSharedMap(pid).get(a.delegate_vid)); + this.G.L.exit("DelegationService.revoke_delegation"); } // RESPONDING TO A DELEGATION REQUEST: @@ -270,11 +304,10 @@ export class DelegationService { } } // check for cycles: + const map = this.G.D.getSharedMap(pid); for (let oid of p.oids){ - console.log("delID: ", p.myvid); - const invindirdelmap = p.inv_effective_delegation_map.get(oid).get(a.client_vid) || new Map(); - console.log("MAP: ", invindirdelmap); - if (invindirdelmap.has(p.myvid)) { + const set = new Set(JSON.parse(map.get(client_vid) || "[]")); + if (set.has(myvid)) { cycle = true; break; } @@ -285,7 +318,6 @@ export class DelegationService { } else if (two_way) { status = ["impossible", "two-way"]; } else if (cycle) { - console.log("cycle detected"); status = ["impossible", "cycle"]; } else { status = ["possible", "acyclic"]; @@ -310,19 +342,36 @@ export class DelegationService { } update_incoming_request_status(pid: string, did: string, status: string) { - const cache = this.G.D.incoming_dids_caches[pid], - [from, url, old_status] = cache.get(did); + const cache = this.G.D.incoming_dids_caches[pid]; + if (!cache) { + return; + } + const [from, url, old_status] = cache.get(did); if (status != old_status[0]) { this.store_incoming_request(pid, did, from, url, status); } } accept(pid: string, did: string, private_key: string) { + console.log("accepting delegation"); /** accept a delegation request, store response in db */ const response = {option_spec: {type: "-", oids: []}} as del_response_t, // i.e., exclude no oids, meaning accept all oids. TODO: allow partial acceptance for only some options signed_response = this.sign_response(response, private_key); this.G.L.info("DelegationService.accept", pid, did, response); this.set_my_signed_response(pid, did, signed_response); + // update effective delegation + const a = this.get_agreement(pid, did); + const eff_set = new Set(JSON.parse(this.G.D.getSharedMap(pid).get(a.client_vid) || "[]")); + const old_delegate_eff_set = new Set(JSON.parse(this.G.D.getSharedMap(pid).get(a.delegate_vid) || "[]")); + var new_delegate_eff_set = new Set(); + for (let id of eff_set) { + new_delegate_eff_set.add(id); + } + for (let id of old_delegate_eff_set) { + new_delegate_eff_set.add(id); + } + new_delegate_eff_set.add(a.client_vid); + this.G.D.set_effective_delegation(pid, a.delegate_vid, Array.from(new_delegate_eff_set) as string[]); } decline(pid: string, did: string, private_key?: string) { @@ -346,6 +395,36 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); } + handle_deleted_delegation(pid:string, did:string){ + var updated_ids = new Set(); + const a = this.get_agreement(pid, did); + var eff_del_map = new Map>(); + // iterate through all voters and populate map; + let p = this.G.P.polls[pid]; + for (let id of p.T.all_vids_set) { + eff_del_map.set(id, new Set(JSON.parse(this.G.D.getv(pid, "del_effective." + id, "vodle") || "[]"))); + } + // update effective delegation for any voter that delegate was delegating to + for (let id of eff_del_map.get(a.client_vid)) { + if (!eff_del_map.get(id).has(a.client_vid)) { + continue; + } + var new_eff_del_set = new Set(); + for (let id2 of eff_del_map.get(id)) { + if (id2 != a.client_vid && eff_del_map.get(a.client_vid).has(id2)) { + new_eff_del_set.add(id2); + } + } + this.G.D.setv_in_polldb(pid, "del_effective." + id, JSON.stringify(Array.from(new_eff_del_set)), "vodle"); + updated_ids.add(id); + } + + for (let id of updated_ids.values()){ + console.log("hd_updated id: ", id); + console.log("hd_updated set: ", this.G.D.getv(pid, "del_effective." + id, "vodle")); + } + } + // DATA HANDLING: get_delegate_nickname(pid: string, did: string): string { @@ -465,8 +544,7 @@ export class DelegationService { this.update_agreement(pid, did, a, request, signed_response); } - update_agreement(pid: string, did: string, agreement: del_agreement_t, - request: del_request_t, signed_response: del_signed_response_t) { + update_agreement(pid: string, did: string, agreement: del_agreement_t, request: del_request_t, signed_response: del_signed_response_t) { /** after changes to request or response, * compare request and response, set status, extract accepted and active oids */ this.G.L.entry("DelegationService.update_agreement", pid, did, agreement, request, signed_response); @@ -497,8 +575,11 @@ export class DelegationService { } const pair = JSON.parse(this.G.D.open_signed(signed_response, request.public_key)); const response = {option_spec: {type: pair[0], oids: pair[1]}} as del_response_t; - if (!response.option_spec) { - a.status = "declined"; + if (pair.status == "revoked") { + a.status = "revoked"; + return; + } else if (!response.option_spec) { + a.status = "declined" } else { if (response.option_spec.type == "+") { // oids specifies accepted options @@ -590,9 +671,7 @@ export class DelegationService { } a.status = (a.accepted_oids.size > 0) ? "agreed" : "declined"; } - } - // if voter affected directly, add news item: if (a.client_vid == p.myvid) { if ((old_status=="pending") && (a.status=="agreed")) { @@ -624,7 +703,6 @@ export class DelegationService { }); } } - // TODO: update tally! this.G.L.exit("DelegationService.update_agreement", a.status, [...a.accepted_oids], [...a.active_oids]); @@ -646,6 +724,10 @@ export class DelegationService { response2string(response: del_response_t): string { /** turn response data without signature deterministically into a string message that can be signed: */ + // if response is a status message, return it as is: + if (response.status) { + return JSON.stringify(response); + } return JSON.stringify([response.option_spec.type, response.option_spec.oids]); } diff --git a/src/app/delrespond/delrespond.page.ts b/src/app/delrespond/delrespond.page.ts index d31cd0026..a97632213 100644 --- a/src/app/delrespond/delrespond.page.ts +++ b/src/app/delrespond/delrespond.page.ts @@ -103,6 +103,7 @@ export class DelrespondPage implements OnInit { // GUI callbacks: + // TODO: verify that it is still possible to accept the request accept() { /** store positive response and go to poll page */ this.G.Del.accept(this.pid, this.did, this.private_key); diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 4a7451d12..3cc26b0fc 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -615,9 +615,7 @@ export class Poll { for (const delInv of listOfDelInv) { const url = delInv["url"]; const did = this.getDidFromUrl(url); - console.log("url: ", url); - - const agreement = this.G.Del.get_agreement(this._pid, did); + const agreement = this.G.Del.get_agreement(this.pid, did); const delId = agreement.client_vid; const s = this.G.D.getv(this._pid, "del_status." + did, delId); @@ -659,6 +657,7 @@ export class Poll { ret = true; } } + this.G.L.exit("Poll.have_been_delegated", ret); return ret; } @@ -1128,6 +1127,65 @@ export class Poll { } } + recalculate_indirect_after_deletion(oid: string, client_vid: string) { + const dir_d_map = this.direct_delegation_map.get(oid); + const ind_d_map = this.indirect_delegation_map.get(oid); + const inv_ind_d_map = this.inv_indirect_delegation_map.get(oid); + console.log("OLD: ", ind_d_map); + + if (!dir_d_map || !ind_d_map || !inv_ind_d_map) { + this.G.L.error("Invalid maps for oid:", oid); + return; + } + + dir_d_map.delete(client_vid); + + const affected_subtree = new Set(); + function dfs(node: string) { + if (!affected_subtree.has(node)) { + affected_subtree.add(node); + if (inv_ind_d_map.has(node)) { + for (const child of inv_ind_d_map.get(node)) { + dfs(child); + } + } + } + } + dfs(client_vid); + + for (const node of affected_subtree) { + ind_d_map.set(node, new Set()); + + // Recalculate indirect delegations for the node + const new_indirect = new Set(); + const direct_delegate = dir_d_map.get(node); + if (direct_delegate) { + new_indirect.add(direct_delegate); + if (ind_d_map.has(direct_delegate)) { + for (const grand_delegate of ind_d_map.get(direct_delegate)) { + new_indirect.add(grand_delegate); + } + } + } + ind_d_map.set(node, new_indirect); + + for (const grand_delegate of new_indirect) { + if (!inv_ind_d_map.has(grand_delegate)) { + inv_ind_d_map.set(grand_delegate, new Set()); + } + inv_ind_d_map.get(grand_delegate).add(node); + } + } + + // add to db + for (const node of affected_subtree) { + const set = ind_d_map.get(node); + this.G.D.setv(this._pid, "indirect_delegation_map." + oid + "." + node, Array.from(set).toString()); + } + + this.G.L.exit("Poll.recalculate_indirect_after_deletion", oid, client_vid); + } + del_delegation(client_vid: string, oid: string) { console.log("del_delegation", client_vid, oid); // Called whenever a voter revokes her delegation for some option @@ -1153,88 +1211,90 @@ export class Poll { this.G.L.debug("del_delegation entry", this.pid, oid, client_vid, old_d_vid, is_on_cycle); // deregister DIRECT delegation and inverse of vid: - this.direct_delegation_map.get(oid).delete(client_vid); - dir_d_map.delete(client_vid); - inv_dir_d_map.get(old_d_vid).delete(client_vid); - - // deregister INDIRECT delegation of client_vid to others: - for (const vid of old_ind_ds_of_client) { - inv_ind_d_map.get(vid).delete(client_vid); - } - ind_d_map.delete(client_vid); - - // deregister INDIRECT delegations no longer valid: - if (is_on_cycle) { - // we're on a cycle, so - // first find cycle elements in correct forward order: - let vid = old_d_vid, - former_cycle = [vid]; - // loop through cycle members: - while (true) { - vid = dir_d_map.get(vid); - former_cycle.push(vid); - if (vid == client_vid) break; - } - if (former_cycle.includes(this.myvid)) { - this.T.my_cycle_len = null; - } - // now for each vid indirectly delegating to client, ... - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - // follow delegation path to cycle: - let cycle_vid = vid, - cycle_pos = -1; - while (true) { - cycle_pos = former_cycle.indexOf(cycle_vid); - if (cycle_pos != -1) { - break; - } - cycle_vid = dir_d_map.get(cycle_vid); - } - // then deregister indirect delegations to all earlier cycle members: - const ind_ds_of_vid = ind_d_map.get(vid); - for (let pos = 0; pos < cycle_pos; pos++) { - const vid2 = former_cycle[pos]; - if (ind_ds_of_vid.has(vid2)) { - ind_ds_of_vid.delete(vid2); - inv_ind_d_map.get(vid2).delete(vid); - } - } - } - } - } else { - // we're not on a cycle, so - // deregister INDIRECT delegation of voters who indirectly delegated to client_vid - // to all old indirect delegates of vid: - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - const ind_ds_of_vid = ind_d_map.get(vid); - for (const vid2 of old_ind_ds_of_client) { - ind_ds_of_vid.delete(vid2); - inv_ind_d_map.get(vid2).delete(vid); - } - } - } - } - - // deregister EFFECTIVE delegation and inverse of vid and reset proxy rating to own rating: - const new_proxy_rating = this.own_ratings_map.get(oid).get(client_vid) || 0; - eff_d_map.delete(client_vid); - inv_eff_ds_of_old_eff_d_of_client.delete(client_vid); - this.update_proxy_rating(client_vid, oid, new_proxy_rating); - - // rewire EFFECTIVE delegation and inverse of voters who indirectly delegated to vid, - // and update proxy ratings: - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - inv_eff_ds_of_old_eff_d_of_client.delete(vid); - eff_d_map.set(vid, client_vid); - inv_eff_ds_of_client.add(vid); - this.update_proxy_rating(vid, oid, new_proxy_rating); - } - } - this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); - } + // this.direct_delegation_map.get(oid).delete(client_vid); + // dir_d_map.delete(client_vid); + // inv_dir_d_map.get(old_d_vid).delete(client_vid); + + // // deregister INDIRECT delegation of client_vid to others: + // for (const vid of old_ind_ds_of_client) { + // inv_ind_d_map.get(vid).delete(client_vid); + // } + // ind_d_map.delete(client_vid); + + // // deregister INDIRECT delegations no longer valid: + // if (is_on_cycle) { + // // we're on a cycle, so + // // first find cycle elements in correct forward order: + // let vid = old_d_vid, + // former_cycle = [vid]; + // // loop through cycle members: + // while (true) { + // vid = dir_d_map.get(vid); + // former_cycle.push(vid); + // if (vid == client_vid) break; + // } + // if (former_cycle.includes(this.myvid)) { + // this.T.my_cycle_len = null; + // } + // // now for each vid indirectly delegating to client, ... + // if (inv_ind_ds_of_client) { + // for (const vid of inv_ind_ds_of_client) { + // // follow delegation path to cycle: + // let cycle_vid = vid, + // cycle_pos = -1; + // while (true) { + // cycle_pos = former_cycle.indexOf(cycle_vid); + // if (cycle_pos != -1) { + // break; + // } + // cycle_vid = dir_d_map.get(cycle_vid); + // } + // // then deregister indirect delegations to all earlier cycle members: + // const ind_ds_of_vid = ind_d_map.get(vid); + // for (let pos = 0; pos < cycle_pos; pos++) { + // const vid2 = former_cycle[pos]; + // if (ind_ds_of_vid.has(vid2)) { + // ind_ds_of_vid.delete(vid2); + // inv_ind_d_map.get(vid2).delete(vid); + // } + // } + // } + // } + // } else { + // // we're not on a cycle, so + // // deregister INDIRECT delegation of voters who indirectly delegated to client_vid + // // to all old indirect delegates of vid: + // if (inv_ind_ds_of_client) { + // for (const vid of inv_ind_ds_of_client) { + // const ind_ds_of_vid = ind_d_map.get(vid); + // for (const vid2 of old_ind_ds_of_client) { + // ind_ds_of_vid.delete(vid2); + // inv_ind_d_map.get(vid2).delete(vid); + // } + // } + // } + // } + + // // deregister EFFECTIVE delegation and inverse of vid and reset proxy rating to own rating: + // const new_proxy_rating = this.own_ratings_map.get(oid).get(client_vid) || 0; + // eff_d_map.delete(client_vid); + // inv_eff_ds_of_old_eff_d_of_client.delete(client_vid); + // this.update_proxy_rating(client_vid, oid, new_proxy_rating); + + // // rewire EFFECTIVE delegation and inverse of voters who indirectly delegated to vid, + // // and update proxy ratings: + // if (inv_ind_ds_of_client) { + // for (const vid of inv_ind_ds_of_client) { + // inv_eff_ds_of_old_eff_d_of_client.delete(vid); + // eff_d_map.set(vid, client_vid); + // inv_eff_ds_of_client.add(vid); + // this.update_proxy_rating(vid, oid, new_proxy_rating); + // } + // } + // this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); + // } + } + this.recalculate_indirect_after_deletion(oid, client_vid); } get_n_indirect_option_clients(vid: string, oid: string): number { @@ -1244,13 +1304,11 @@ export class Poll { get_n_indirect_clients(vid: string): number { /** count how many voters have indirectly delegated to vid for some oid */ - let clients = new Set(); - for (const oid of this.oids) { - for (const vid2 of (this.inv_indirect_delegation_map.get(oid).get(vid)||new Set())) { - clients.add(vid2); - } - } - return clients.size; + console.log("poll_id", this._pid); + const map = this.G.D.getSharedMap(this._pid); + const set = new Set(JSON.parse(map.get(vid) || "[]")); + console.log("getSharedMapNINDSET", vid, set); + return set.size; } tally_all() { diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index c1c643896..88c9819b3 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -461,7 +461,7 @@

- @@ -478,7 +478,7 @@

size="smaller" class="ion-no-margin" style="color:black; position: relative; left: -2px; top: 2px;"> - (JSON.parse(map.get(vid) || "[]"))); + } this.G.L.entry("PollPage.onDataChange"); this.p.tally_all(); this.update_order(); @@ -243,6 +248,7 @@ export class PollPage implements OnInit { } update_delegation_info() { + this.G.L.entry("PollPage.update_delegation_info"); // determine own weight: this.n_indirect_clients = this.p.get_n_indirect_clients(this.p.myvid); // find incoming delegations: @@ -251,11 +257,25 @@ export class PollPage implements OnInit { this.declined_requests = []; if (cache) { for (const [did, [from, url, status]] of cache) { - if (status == 'agreed') { - this.accepted_requests.push({from:from, url:url}); - } else if (status.startsWith('declined')) { - this.declined_requests.push({from:from, url:url}); - } + const st = this.G.D.getv(this.pid, "del_status."+did); + if (st && st != status) { + if (status == 'agreed') { + if (st == 'revoked') { + this.G.N.add({ + class: 'delegation_declined', + pid: this.pid, + title: this.translate.instant('news-title.delegation_revoked', {nickname: from}), + }); + cache.delete(did); + } + } + }else{ + if (status == 'agreed') { + this.accepted_requests.push({from:from, url:url}); + } else if (status.startsWith('declined')) { + this.declined_requests.push({from:from, url:url}); + } + } } } // find outgoing delegation: diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0278b649d..ee8924f11 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -507,7 +507,9 @@ "delegation_accepted": "{{nickname}} accepted your delegation request.", "delegation_declined": "Unfortunately, {{nickname}} declined your delegation request.", "delegation_accepted_after_all": "{{nickname}} has now accepted your delegation request after all.", - "delegation_revoked": "Unfortunately, {{nickname}} revoked their delegation to you." + "delegation_revoked": "Unfortunately, {{nickname}} revoked their delegation to you.", + "delegation_declined_cycle": "Unfortunately, {{nickname}} declined your delegation request as it would have created a cycle.", + "delegation_decline_self": "Unfortunately, {{nickname}} declined your delegation request as they are already delegating to you." }, "news-body": { "_COMMENT_SECTION_": "[COMMENT] strings used as bodies of News items:", diff --git a/src/environments/environment.ts b/src/environments/environment.ts index e3e293c31..7f1bfb043 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -53,7 +53,8 @@ export const environment = { }, delegation: { enabled: true, - max_weight: 300 + max_weight: 300, + max_delegations: 3 }, no_more_options_time_fraction: 1/2, db_put_retry_delay_ms: 100, From 04243a17833b5a0b8da0343a6c0e2ccbc4407c1c Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sun, 1 Dec 2024 14:04:30 +0000 Subject: [PATCH 09/16] fixed adding a delegation adding a delegation now updates the whole delegation chain --- src/app/delegation.service.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index dcf815cbf..7f149cf3c 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -361,17 +361,24 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); // update effective delegation const a = this.get_agreement(pid, did); - const eff_set = new Set(JSON.parse(this.G.D.getSharedMap(pid).get(a.client_vid) || "[]")); - const old_delegate_eff_set = new Set(JSON.parse(this.G.D.getSharedMap(pid).get(a.delegate_vid) || "[]")); - var new_delegate_eff_set = new Set(); - for (let id of eff_set) { - new_delegate_eff_set.add(id); - } - for (let id of old_delegate_eff_set) { - new_delegate_eff_set.add(id); + const sm = this.G.D.getSharedMap(pid); + const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); + var new_sm = sm; + for (let id of sm.keys()) { + if (id == a.client_vid) { + continue; + } + const old_eff_set = new Set(JSON.parse(sm.get(id) || "[]")); + if (id == a.delegate_vid || old_eff_set.has(a.delegate_vid)) { + var new_eff_set = old_eff_set; + for (const id2 of eff_set){ + new_eff_set.add(id2); + } + new_eff_set.add(a.client_vid); + new_sm.set(id, JSON.stringify(Array.from(new_eff_set))); + } } - new_delegate_eff_set.add(a.client_vid); - this.G.D.set_effective_delegation(pid, a.delegate_vid, Array.from(new_delegate_eff_set) as string[]); + this.G.D.set_shared_map(pid, new_sm); } decline(pid: string, did: string, private_key?: string) { @@ -385,6 +392,7 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); } + // todo: make this give a different news item to the client decline_due_to_error(pid: string, did: string, private_key?: string) { if (!private_key) { private_key = this.get_private_key(pid, did); From c6df625d475f3790aa5ae1f6422a1cb86107a9ff Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Mon, 16 Dec 2024 15:39:28 +0000 Subject: [PATCH 10/16] more shared map stuff --- src/app/data.service.ts | 16 +-- src/app/delegation.service.ts | 62 ++++----- src/app/poll.service.ts | 240 ++++++++++++++-------------------- src/app/poll/poll.page.html | 2 +- src/app/poll/poll.page.ts | 20 ++- 5 files changed, 136 insertions(+), 204 deletions(-) diff --git a/src/app/data.service.ts b/src/app/data.service.ts index 4243f3433..371dc65c9 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1606,35 +1606,21 @@ export class DataService implements OnDestroy { const currentMap = this.getSharedMap(pid); currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair - // this.setp(pid, 'shared_map', JSON.stringify(Array.from(currentMap.entries()))); - // this.setv_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries()))); - // this.store_poll_data(pid, mapKey, currentMap, mapKey, false); this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries()))); } getSharedMap(pid: string): Map { const cache = this.poll_caches[pid]['poll.' + pid + '.shared_map'] || '[]'; + console.log("shared_map_cache", this.poll_caches[pid]); const ps = cache ? JSON.parse(cache) : {}; const mp = new Map(ps); console.log("getSharedMap", pid, mp); return mp; - let result = new Map(); // Default value if async fails or doesn't resolve in time - - this.getSharedMapAsync(pid) - .then(map => { - result = map; // Set the resolved map to result - }) - .catch(err => { - console.error('Error fetching shared map:', err); - }); - - return result; // Return the default or updated result } async getSharedMapAsync(pid: string): Promise> { console.log("getSharedMap_pid", pid); const db = this.get_local_poll_db(pid); // Retrieve the local PouchDB instance for the poll. - // const docId = '~vodle.poll.e031c7§poll.e031c7.shared_map'; const docId = '~vodle.poll.' + pid + '§poll.' + pid + '.shared_map'; return db.get(docId).then(doc => { console.log(doc); diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index 7f149cf3c..a05d7bc9d 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -186,42 +186,34 @@ export class DelegationService { revoke_delegation(pid: string, did: string, oid: string) { this.G.L.entry("DelegationService.revoke_delegation", pid, did); - // const a = this.get_delegation_agreements_cache(pid).get(did); - // if (!a) { - // this.G.L.error("DelegationService.revoke_delegation without agreement", pid, did); - // return; - // } - // const p = this.G.P.polls[pid]; - // if ((a.client_vid != p.myvid)) { - // this.G.L.error("DelegationService.revoke_delegation without request from me", pid, did); - // } else { - // this.G.D.delv(pid, "del_request." + did); - // const acache = this.get_delegation_agreements_cache(pid); - // if (acache) { - // const oids = acache.get(did).active_oids; - // if (oids) { - // for (const oid of oids) { - // p.del_delegation(p.myvid, oid); - // } - // } - // acache.delete(did); - // } - // } - // const dcache = this.get_my_outgoing_dids_cache(pid); - // if (dcache) { - // dcache.delete(oid); - // } - - const private_key = this.get_private_key(pid, did); - const response = {status:"revoked"} as del_response_t, // i.e., exclude no oids, meaning accept all oids. TODO: allow partial acceptance for only some options - signed_response = this.sign_response(response, private_key); - this.set_my_signed_response(pid, did, signed_response); - this.G.D.setv(pid, "del_status." + did, "revoked"); // store in db that delegation was revoked // update effectve delegation for delegate_vid const a = this.get_agreement(pid, did); + + const p = this.G.P.polls[pid]; + if ((a.client_vid != p.myvid)) { + this.G.L.error("DelegationService.revoke_delegation without request from me", pid, did); + } else { + this.G.D.delv(pid, "del_request." + did); + const acache = this.get_delegation_agreements_cache(pid); + if (acache) { + const oids = acache.get(did).active_oids; + if (oids) { + for (const oid of oids) { + p.del_delegation(p.myvid, oid); + } + } + acache.delete(did); + } + } + const dcache = this.get_my_outgoing_dids_cache(pid); + if (dcache) { + dcache.delete(oid); + } + const sm = this.G.D.getSharedMap(pid); const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); + // gets all voters that are affected by the delegation being var stack = []; for (let id of sm.keys()) { if (JSON.parse(sm.get(id) || "[]").includes(a.client_vid)) { @@ -243,10 +235,6 @@ export class DelegationService { } this.G.D.set_shared_map(pid, sm); - - // this.G.D.setv_in_polldb(pid, "del_effective." + a.delegate_vid, JSON.stringify(Array.from(new_delegate_eff_set)), "vodle"); - // this.handle_deleted_delegation(pid, did); - //console.log("revoked: ", this.G.D.getSharedMap(pid).get(a.delegate_vid)); this.G.L.exit("DelegationService.revoke_delegation"); } @@ -343,7 +331,7 @@ export class DelegationService { update_incoming_request_status(pid: string, did: string, status: string) { const cache = this.G.D.incoming_dids_caches[pid]; - if (!cache) { + if (!cache){ return; } const [from, url, old_status] = cache.get(did); @@ -364,7 +352,7 @@ export class DelegationService { const sm = this.G.D.getSharedMap(pid); const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); var new_sm = sm; - for (let id of sm.keys()) { + for (let id of this.G.P.polls[pid].T.all_vids_set) { if (id == a.client_vid) { continue; } diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 3cc26b0fc..03d61f671 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -182,6 +182,8 @@ export class Poll { _state: string; // cache for state since it is asked very often syncing: boolean = false; allow_voting: boolean = false; + delegate_id: string = null; + did: string = null; constructor (G:GlobalService, pid?:string) { this.G = G; @@ -528,7 +530,15 @@ export class Poll { } get_my_proxy_rating(oid: string): number { - return (this.proxy_ratings_map.get(oid) || new Map()).get(this.myvid) || 0; + if (this.delegate_id){ + const agr = this.G.Del.get_agreement(this.pid, this.did); + console.log("shared_oid: ", agr.active_oids); + if (agr.active_oids.has(oid)){ + return +this.G.D.getv(this.pid, "rating."+oid, this.delegate_id)||0; + } + } + return this.get_my_own_rating(oid); + // return (this.proxy_ratings_map.get(oid) || new Map()).get(this.myvid) || 0; } get_my_effective_rating(oid: string): number { @@ -593,6 +603,8 @@ export class Poll { const did = this.G.Del.get_my_outgoing_dids_cache(this.pid).get("*"); if (!did) return false; const agreement = this.G.Del.get_agreement(this.pid, did); + this.did = did; + this.delegate_id = agreement.delegate_vid; return (agreement.status == "agreed") && (agreement.active_oids.size == agreement.accepted_oids.size); } @@ -609,6 +621,7 @@ export class Poll { } have_been_delegated(clientVid: string, listOfDelInv: object[]) { + return false; var ret = false; this.G.L.entry("Poll.have_been_delegated", clientVid, listOfDelInv); // check if clientVid has been delegated to by any of the vids in listOfDelInv @@ -1127,65 +1140,6 @@ export class Poll { } } - recalculate_indirect_after_deletion(oid: string, client_vid: string) { - const dir_d_map = this.direct_delegation_map.get(oid); - const ind_d_map = this.indirect_delegation_map.get(oid); - const inv_ind_d_map = this.inv_indirect_delegation_map.get(oid); - console.log("OLD: ", ind_d_map); - - if (!dir_d_map || !ind_d_map || !inv_ind_d_map) { - this.G.L.error("Invalid maps for oid:", oid); - return; - } - - dir_d_map.delete(client_vid); - - const affected_subtree = new Set(); - function dfs(node: string) { - if (!affected_subtree.has(node)) { - affected_subtree.add(node); - if (inv_ind_d_map.has(node)) { - for (const child of inv_ind_d_map.get(node)) { - dfs(child); - } - } - } - } - dfs(client_vid); - - for (const node of affected_subtree) { - ind_d_map.set(node, new Set()); - - // Recalculate indirect delegations for the node - const new_indirect = new Set(); - const direct_delegate = dir_d_map.get(node); - if (direct_delegate) { - new_indirect.add(direct_delegate); - if (ind_d_map.has(direct_delegate)) { - for (const grand_delegate of ind_d_map.get(direct_delegate)) { - new_indirect.add(grand_delegate); - } - } - } - ind_d_map.set(node, new_indirect); - - for (const grand_delegate of new_indirect) { - if (!inv_ind_d_map.has(grand_delegate)) { - inv_ind_d_map.set(grand_delegate, new Set()); - } - inv_ind_d_map.get(grand_delegate).add(node); - } - } - - // add to db - for (const node of affected_subtree) { - const set = ind_d_map.get(node); - this.G.D.setv(this._pid, "indirect_delegation_map." + oid + "." + node, Array.from(set).toString()); - } - - this.G.L.exit("Poll.recalculate_indirect_after_deletion", oid, client_vid); - } - del_delegation(client_vid: string, oid: string) { console.log("del_delegation", client_vid, oid); // Called whenever a voter revokes her delegation for some option @@ -1211,90 +1165,88 @@ export class Poll { this.G.L.debug("del_delegation entry", this.pid, oid, client_vid, old_d_vid, is_on_cycle); // deregister DIRECT delegation and inverse of vid: - // this.direct_delegation_map.get(oid).delete(client_vid); - // dir_d_map.delete(client_vid); - // inv_dir_d_map.get(old_d_vid).delete(client_vid); - - // // deregister INDIRECT delegation of client_vid to others: - // for (const vid of old_ind_ds_of_client) { - // inv_ind_d_map.get(vid).delete(client_vid); - // } - // ind_d_map.delete(client_vid); - - // // deregister INDIRECT delegations no longer valid: - // if (is_on_cycle) { - // // we're on a cycle, so - // // first find cycle elements in correct forward order: - // let vid = old_d_vid, - // former_cycle = [vid]; - // // loop through cycle members: - // while (true) { - // vid = dir_d_map.get(vid); - // former_cycle.push(vid); - // if (vid == client_vid) break; - // } - // if (former_cycle.includes(this.myvid)) { - // this.T.my_cycle_len = null; - // } - // // now for each vid indirectly delegating to client, ... - // if (inv_ind_ds_of_client) { - // for (const vid of inv_ind_ds_of_client) { - // // follow delegation path to cycle: - // let cycle_vid = vid, - // cycle_pos = -1; - // while (true) { - // cycle_pos = former_cycle.indexOf(cycle_vid); - // if (cycle_pos != -1) { - // break; - // } - // cycle_vid = dir_d_map.get(cycle_vid); - // } - // // then deregister indirect delegations to all earlier cycle members: - // const ind_ds_of_vid = ind_d_map.get(vid); - // for (let pos = 0; pos < cycle_pos; pos++) { - // const vid2 = former_cycle[pos]; - // if (ind_ds_of_vid.has(vid2)) { - // ind_ds_of_vid.delete(vid2); - // inv_ind_d_map.get(vid2).delete(vid); - // } - // } - // } - // } - // } else { - // // we're not on a cycle, so - // // deregister INDIRECT delegation of voters who indirectly delegated to client_vid - // // to all old indirect delegates of vid: - // if (inv_ind_ds_of_client) { - // for (const vid of inv_ind_ds_of_client) { - // const ind_ds_of_vid = ind_d_map.get(vid); - // for (const vid2 of old_ind_ds_of_client) { - // ind_ds_of_vid.delete(vid2); - // inv_ind_d_map.get(vid2).delete(vid); - // } - // } - // } - // } - - // // deregister EFFECTIVE delegation and inverse of vid and reset proxy rating to own rating: - // const new_proxy_rating = this.own_ratings_map.get(oid).get(client_vid) || 0; - // eff_d_map.delete(client_vid); - // inv_eff_ds_of_old_eff_d_of_client.delete(client_vid); - // this.update_proxy_rating(client_vid, oid, new_proxy_rating); - - // // rewire EFFECTIVE delegation and inverse of voters who indirectly delegated to vid, - // // and update proxy ratings: - // if (inv_ind_ds_of_client) { - // for (const vid of inv_ind_ds_of_client) { - // inv_eff_ds_of_old_eff_d_of_client.delete(vid); - // eff_d_map.set(vid, client_vid); - // inv_eff_ds_of_client.add(vid); - // this.update_proxy_rating(vid, oid, new_proxy_rating); - // } - // } - // this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); - // } - } - this.recalculate_indirect_after_deletion(oid, client_vid); + this.direct_delegation_map.get(oid).delete(client_vid); + dir_d_map.delete(client_vid); + inv_dir_d_map.get(old_d_vid).delete(client_vid); + + // deregister INDIRECT delegation of client_vid to others: + for (const vid of old_ind_ds_of_client) { + inv_ind_d_map.get(vid).delete(client_vid); + } + ind_d_map.delete(client_vid); + + // deregister INDIRECT delegations no longer valid: + if (is_on_cycle) { + // we're on a cycle, so + // first find cycle elements in correct forward order: + let vid = old_d_vid, + former_cycle = [vid]; + // loop through cycle members: + while (true) { + vid = dir_d_map.get(vid); + former_cycle.push(vid); + if (vid == client_vid) break; + } + if (former_cycle.includes(this.myvid)) { + this.T.my_cycle_len = null; + } + // now for each vid indirectly delegating to client, ... + if (inv_ind_ds_of_client) { + for (const vid of inv_ind_ds_of_client) { + // follow delegation path to cycle: + let cycle_vid = vid, + cycle_pos = -1; + while (true) { + cycle_pos = former_cycle.indexOf(cycle_vid); + if (cycle_pos != -1) { + break; + } + cycle_vid = dir_d_map.get(cycle_vid); + } + // then deregister indirect delegations to all earlier cycle members: + const ind_ds_of_vid = ind_d_map.get(vid); + for (let pos = 0; pos < cycle_pos; pos++) { + const vid2 = former_cycle[pos]; + if (ind_ds_of_vid.has(vid2)) { + ind_ds_of_vid.delete(vid2); + inv_ind_d_map.get(vid2).delete(vid); + } + } + } + } + } else { + // we're not on a cycle, so + // deregister INDIRECT delegation of voters who indirectly delegated to client_vid + // to all old indirect delegates of vid: + if (inv_ind_ds_of_client) { + for (const vid of inv_ind_ds_of_client) { + const ind_ds_of_vid = ind_d_map.get(vid); + for (const vid2 of old_ind_ds_of_client) { + ind_ds_of_vid.delete(vid2); + inv_ind_d_map.get(vid2).delete(vid); + } + } + } + } + + // deregister EFFECTIVE delegation and inverse of vid and reset proxy rating to own rating: + const new_proxy_rating = this.own_ratings_map.get(oid).get(client_vid) || 0; + eff_d_map.delete(client_vid); + inv_eff_ds_of_old_eff_d_of_client.delete(client_vid); + this.update_proxy_rating(client_vid, oid, new_proxy_rating); + + // rewire EFFECTIVE delegation and inverse of voters who indirectly delegated to vid, + // and update proxy ratings: + if (inv_ind_ds_of_client) { + for (const vid of inv_ind_ds_of_client) { + inv_eff_ds_of_old_eff_d_of_client.delete(vid); + eff_d_map.set(vid, client_vid); + inv_eff_ds_of_client.add(vid); + this.update_proxy_rating(vid, oid, new_proxy_rating); + } + } + this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); + } } get_n_indirect_option_clients(vid: string, oid: string): number { diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index 88c9819b3..0118b59c5 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -716,7 +716,7 @@

[disabled]="!p.allow_voting" [id]="'slider_'+oidsorted[i]+'_'+sortingcounter" [color]="show_live?slidercolor[oidsorted[i]]:'vodleblue'" - [value]="p.get_my_proxy_rating(oidsorted[i])" + [value]="get_my_rating(oidsorted[i])" (ionFocus)="onRatingSliderFocus(oidsorted[i])" (ionChange)="onRatingSliderChange(oidsorted[i])" (ionBlur)="onRatingSliderBlur(oidsorted[i])" diff --git a/src/app/poll/poll.page.ts b/src/app/poll/poll.page.ts index 3074c616d..e7b92b0f2 100644 --- a/src/app/poll/poll.page.ts +++ b/src/app/poll/poll.page.ts @@ -185,16 +185,11 @@ export class PollPage implements OnInit { if (this.p.has_results) { this.p.have_seen_results = true; } - this.have_been_delegated = this.p.have_been_delegated(this.p.myvid, this.accepted_requests); this.G.L.exit("PollPage.onDataReady"); } onDataChange() { this.G.L.entry("PollPage.onDataChangeshared"); - const map = this.G.D.getSharedMap(this.pid); - for (let vid of this.p.T.all_vids_set){ - console.log("shared_effective delegates of", vid, new Set(JSON.parse(map.get(vid) || "[]"))); - } this.G.L.entry("PollPage.onDataChange"); this.p.tally_all(); this.update_order(); @@ -338,7 +333,9 @@ export class PollPage implements OnInit { // update n_delegated: // TODO: make more efficient let sum = 0; for (let [oid, b] of Object.entries(this.rate_yourself_toggle)) { - if (!b) sum++; + if (!b) { + sum++; + } } this.n_delegated = sum; } @@ -387,7 +384,8 @@ export class PollPage implements OnInit { delegate_vid = this.G.Del.get_potential_effective_delegate(this.pid, oid); // this.G.L.trace("PollPage.show_stats needle know delegate_vid", needle, knob, delegate_vid); if (delegate_vid) { - const rating = (this.p.proxy_ratings_map.get(oid)||new Map()).get(delegate_vid)||0; + // const rating = (this.p.proxy_ratings_map.get(oid)||new Map()).get(delegate_vid)||0; + const rating = this.G.D.getv(this.pid, "rating."+oid, this.p.delegate_id)||0; this.G.L.trace("PollPage.show_stats rating", rating); if (needle) { needle.x2.baseVal.valueAsString = (rating).toString() + '%'; @@ -661,6 +659,14 @@ export class PollPage implements OnInit { return true; } + get_my_rating(oid: string) { + if (this.delegate && !this.rate_yourself_toggle[oid]) { + this.G.D.setv(this.pid, "rating."+oid, "" + this.get_slider_value(oid)); + return this.G.D.getv(this.pid, "rating."+oid, this.p.delegate_id); + } + return this.p.get_my_own_rating(oid); + } + get_knob_pos(oid: string) { /** get the slider knob position (left and right pixel coordinates) * to be able to compare with click/touch coordinate: From c828c32adc4da5f35d9b0ce6c0d21ed9aeae666b Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Mon, 16 Dec 2024 20:06:00 +0000 Subject: [PATCH 11/16] ranked delegation implementation started - When creating a poll, the user is asked whether they want to allow ranked delegations - Delegation acceptance screen logic adjusted --- src/app/data.service.ts | 25 ++++------------ src/app/delegation.service.ts | 6 +++- src/app/delrespond/delrespond.page.html | 38 ++++++++++++++++++++++++- src/app/draftpoll/draftpoll.page.html | 7 +++++ src/app/draftpoll/draftpoll.page.ts | 19 +++++++++++-- src/app/poll.service.ts | 3 ++ src/app/poll/poll.page.html | 5 +++- src/assets/i18n/en.json | 4 ++- 8 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/app/data.service.ts b/src/app/data.service.ts index 371dc65c9..18b850f86 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1522,7 +1522,7 @@ export class DataService implements OnDestroy { } else { this.G.L.error("DataService.setp change option attempted for existing entry", pid, key, value); } - } if (key == 'shared_map') { + } else if (key == 'shared_map') { return this._setp_in_polldb(pid, key, value); }else { this.G.L.error("DataService.setp non-local attempted for non-draft poll", pid, key, value); @@ -1611,30 +1611,11 @@ export class DataService implements OnDestroy { getSharedMap(pid: string): Map { const cache = this.poll_caches[pid]['poll.' + pid + '.shared_map'] || '[]'; - console.log("shared_map_cache", this.poll_caches[pid]); const ps = cache ? JSON.parse(cache) : {}; const mp = new Map(ps); - console.log("getSharedMap", pid, mp); return mp; } - async getSharedMapAsync(pid: string): Promise> { - console.log("getSharedMap_pid", pid); - const db = this.get_local_poll_db(pid); // Retrieve the local PouchDB instance for the poll. - const docId = '~vodle.poll.' + pid + '§poll.' + pid + '.shared_map'; - return db.get(docId).then(doc => { - console.log(doc); - const val = decrypt(doc.value, this.user_cache[get_poll_key_prefix(pid) + 'password']); - console.log("shared_val:", val); - const map = new Map(JSON.parse(val)); - console.log("shared_fin_map:", map); - return map; - } - ).catch(err => { - console.log("getSharedMap", err); - }); - } - delv(pid: string, key: string, vid?: string) { if(!this.getv(pid, key)) { this.G.L.warn("DataService.delv nothing to delete", pid, key); @@ -1652,6 +1633,10 @@ export class DataService implements OnDestroy { } } + get_ranked_delegation_allowed(pid: string): boolean { + return this.poll_caches[pid]['allow_ranked'] == 'true'; + } + // TODO: delv! get_example_docs(): Promise { diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index a05d7bc9d..cd232bac5 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -263,7 +263,11 @@ export class DelegationService { // check if already answered: if (agreement.status == 'agreed') { return ["accepted"]; - } + } + + if (this.G.D.get_ranked_delegation_allowed(pid)){ + return ["ranked"]; + } const a = this.get_agreement(pid, did); diff --git a/src/app/delrespond/delrespond.page.html b/src/app/delrespond/delrespond.page.html index 14c8709b2..d96518d30 100644 --- a/src/app/delrespond/delrespond.page.html +++ b/src/app/delrespond/delrespond.page.html @@ -31,6 +31,42 @@ + + + +

+

+

+

+
+
+ + + +   + +    + +   + + + + + + +

 

+

 

+

+ +   + +
+
+
@@ -72,7 +108,7 @@

+

diff --git a/src/app/draftpoll/draftpoll.page.html b/src/app/draftpoll/draftpoll.page.html index 8318eebe5..906f63042 100644 --- a/src/app/draftpoll/draftpoll.page.html +++ b/src/app/draftpoll/draftpoll.page.html @@ -289,6 +289,13 @@ style="font-size: smaller"> + + + + +
diff --git a/src/app/draftpoll/draftpoll.page.ts b/src/app/draftpoll/draftpoll.page.ts index 20f65cb5f..b9483100d 100644 --- a/src/app/draftpoll/draftpoll.page.ts +++ b/src/app/draftpoll/draftpoll.page.ts @@ -83,7 +83,8 @@ export class DraftpollPage implements OnInit { is_test?, type?, language?, title?, desc?, url?, - due_type?, due_custom?, + due_type?, due_custom?, + allow_ranked?, db?, db_from_pid?, db_custom_server_url?, db_custom_password?, options?: option_data_t[] }; @@ -142,7 +143,7 @@ export class DraftpollPage implements OnInit { this.stage = 0; if (!this.pd) { this.G.L.info("DraftpollPage editing new draft"); - this.pd = { db:'default', language:this.G.S.language }; + this.pd = { db:'default', language:this.G.S.language, allow_ranked:false}; } else { this.G.L.info("DraftpollPage editing draft with data", this.pd); this.pd.due_custom = (this.pd.due_custom||'')!=''?(new Date(this.pd.due_custom)):null; @@ -158,7 +159,8 @@ export class DraftpollPage implements OnInit { pid:p.pid, type:p.type, language:p.language, title:p.title, desc:p.desc, url:p.url, - due_type:p.due_type, due_custom:p.due_custom, + due_type:p.due_type, due_custom:p.due_custom, + allow_ranked:p.allow_ranked, db:p.db, db_from_pid:p.db_from_pid, db_custom_server_url:p.db_custom_server_url, db_custom_password:p.db_custom_password, options: [] }; @@ -186,6 +188,7 @@ export class DraftpollPage implements OnInit { poll_url: this.pd.url||'', poll_due_type: this.pd.due_type||'', poll_due_custom: (!this.pd.due_custom)?'':this.pd.due_custom.toISOString(), + poll_allow_ranked_delegation: this.pd.allow_ranked||false, }); if (this.pd.language||this.pd.db_from_pid||this.pd.db_custom_server_url) { this.advanced_expanded = true; @@ -233,6 +236,7 @@ export class DraftpollPage implements OnInit { db_from_pid: this.pd.db_from_pid||'', db_custom_server_url: this.pd.db_custom_server_url||'', db_custom_password: this.pd.db_custom_password||'', + db_allow_ranked: this.pd.allow_ranked||false, }); } } @@ -266,6 +270,7 @@ export class DraftpollPage implements OnInit { p.url = this.pd.url; p.due_type = this.pd.due_type; p.due_custom = this.pd.due_custom; + p.allow_ranked = this.pd.allow_ranked || false; p.set_due(); p.db = this.pd.db; p.db_from_pid = this.pd.db_from_pid; @@ -320,6 +325,7 @@ export class DraftpollPage implements OnInit { this.G.L.warn("DraftpollPage.ionViewWillLeave localNotifications.schedule failed:", err); }); } + console.log("DraftpollPage.ionViewWillLeave p object:", p); this.G.L.trace("DraftpollPage.ionViewWillLeave D.pids:", [...this.G.D.pids]); this.G.L.exit("DraftpollPage.ionViewWillLeave"); } @@ -379,6 +385,12 @@ export class DraftpollPage implements OnInit { if (c.valid) this.pd.due_custom = new Date(c.value); } + set_poll_allow_ranked_delegation(){ + this.G.L.trace("set_poll_allow_ranked_delegation",this.pd.allow_ranked); + this.pd.allow_ranked = this.formGroup.get('poll_allow_ranked_delegation').value; + this.G.L.trace("set_poll_allow_ranked_delegation result",this.pd.allow_ranked); + } + set_option_name(i: number) { let c = this.formGroup.get('option_name'+i); this.G.L.trace("set_option_name",i,c.value); @@ -720,6 +732,7 @@ export class DraftpollPage implements OnInit { poll_url: new UntypedFormControl('', Validators.pattern(this.G.urlRegex)), poll_due_type: new UntypedFormControl('', Validators.required), poll_due_custom: new UntypedFormControl('', this.allowed_date.bind(this)), + poll_allow_ranked_delegation: new UntypedFormControl(false), }); this.G.P.update_ref_date(); } diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 03d61f671..5df57c22a 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -431,6 +431,9 @@ export class Poll { get due_type(): poll_due_type_t { return this.G.D.getp(this._pid, 'due_type') as poll_due_type_t; } set due_type(value: poll_due_type_t) { this.G.D.setp(this._pid, 'due_type', value); } + get allow_ranked(): boolean { return this.G.D.getp(this._pid, 'allow_ranked') == 'true'; } + set allow_ranked(value: boolean) { this.G.D.setp(this._pid, 'allow_ranked', value.toString()); } + // Date objects are stored as ISO strings: get start_date(): Date { diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index 0118b59c5..1d3fc75c1 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -151,6 +151,10 @@ + +

@@ -163,7 +167,6 @@

-
“{{poll_title}}”?", "intro": "Your waps will then also control the other participantʼs waps.", "details": "If you accept, you can still choose whether you want to set your waps yourself or delegate your (and the other participantʼs) waps further to some third person.", + "details_ranked": "Note that this poll allows ranked delegation. [Insert explanation of how cycles and stuff are allowed]", "check-first": "You can also check your waps first and then return here to accept or decline.", "accept": "Accept", "decline": "Decline", @@ -287,7 +288,8 @@ "import-options-msg": "The file must be a .csv file.

Each row must specify one option, either in the format
   \"Name\"
or
   \"Name\", \"Description\"
or
   \"Name\", \"Description\", \"URL\"", "use-example-from-db": "Use an example:", "use-example-from-db-none": "(Donʼt use an example)", - "notification-saved-title": "Draft saved under “My polls”." + "notification-saved-title": "Draft saved under “My polls”.", + "del_ranked": "Allow ranked delegation?" }, "external-link": { "_COMMENT_SECTION_": "[COMMENT] strings concerning external links:", From 0d55ee1c3da4b5c189df70d43f7432d11b98ee81 Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Tue, 17 Dec 2024 15:46:01 +0000 Subject: [PATCH 12/16] rename sharedMap to inverse_indirect_map for clarity --- src/app/data.service.ts | 6 +++--- src/app/delegation.service.ts | 10 +++++----- src/app/poll.service.ts | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app/data.service.ts b/src/app/data.service.ts index 18b850f86..ed6c70c47 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1596,20 +1596,20 @@ export class DataService implements OnDestroy { } } - set_shared_map(pid: string, val: Map) { + set_inverse_indirect_map(pid: string, val: Map) { const mapKey = `poll.${pid}.shared_map`; this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries()))); } set_effective_delegation(pid: string, vid: string, val: string[]) { const mapKey = `poll.${pid}.shared_map`; - const currentMap = this.getSharedMap(pid); + const currentMap = this.get_inverse_indirect_map(pid); currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries()))); } - getSharedMap(pid: string): Map { + get_inverse_indirect_map(pid: string): Map { const cache = this.poll_caches[pid]['poll.' + pid + '.shared_map'] || '[]'; const ps = cache ? JSON.parse(cache) : {}; const mp = new Map(ps); diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index cd232bac5..cbbe408bb 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -210,7 +210,7 @@ export class DelegationService { dcache.delete(oid); } - const sm = this.G.D.getSharedMap(pid); + const sm = this.G.D.get_inverse_indirect_map(pid); const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); // gets all voters that are affected by the delegation being @@ -234,7 +234,7 @@ export class DelegationService { sm.set(curr_del, JSON.stringify(Array.from(new_delegate_eff_set))); } - this.G.D.set_shared_map(pid, sm); + this.G.D.set_inverse_indirect_map(pid, sm); this.G.L.exit("DelegationService.revoke_delegation"); } @@ -296,7 +296,7 @@ export class DelegationService { } } // check for cycles: - const map = this.G.D.getSharedMap(pid); + const map = this.G.D.get_inverse_indirect_map(pid); for (let oid of p.oids){ const set = new Set(JSON.parse(map.get(client_vid) || "[]")); if (set.has(myvid)) { @@ -353,7 +353,7 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); // update effective delegation const a = this.get_agreement(pid, did); - const sm = this.G.D.getSharedMap(pid); + const sm = this.G.D.get_inverse_indirect_map(pid); const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); var new_sm = sm; for (let id of this.G.P.polls[pid].T.all_vids_set) { @@ -370,7 +370,7 @@ export class DelegationService { new_sm.set(id, JSON.stringify(Array.from(new_eff_set))); } } - this.G.D.set_shared_map(pid, new_sm); + this.G.D.set_inverse_indirect_map(pid, new_sm); } decline(pid: string, did: string, private_key?: string) { diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 5df57c22a..ab74ac5be 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -1260,9 +1260,8 @@ export class Poll { get_n_indirect_clients(vid: string): number { /** count how many voters have indirectly delegated to vid for some oid */ console.log("poll_id", this._pid); - const map = this.G.D.getSharedMap(this._pid); + const map = this.G.D.get_inverse_indirect_map(this._pid); const set = new Set(JSON.parse(map.get(vid) || "[]")); - console.log("getSharedMapNINDSET", vid, set); return set.size; } From a5f43a65c1e078202a79fa2426e9039b6887620e Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sat, 21 Dec 2024 18:11:33 +0000 Subject: [PATCH 13/16] more ranked delegation --- .../_design/vodle/validate_doc_update.js | 2 +- src/app/data.service.ts | 36 ++- .../delegation-dialog.page.html | 14 ++ .../delegation-dialog.page.ts | 48 +++- src/app/delegation.service.ts | 211 +++++++++++++++--- src/app/poll.service.ts | 24 +- src/app/poll/poll.page.html | 2 +- src/app/poll/poll.page.ts | 96 +++++++- src/assets/i18n/en.json | 1 + 9 files changed, 369 insertions(+), 65 deletions(-) diff --git a/couchdb/vodle/_design/vodle/validate_doc_update.js b/couchdb/vodle/_design/vodle/validate_doc_update.js index a2038fd60..8162de78d 100644 --- a/couchdb/vodle/_design/vodle/validate_doc_update.js +++ b/couchdb/vodle/_design/vodle/validate_doc_update.js @@ -39,7 +39,7 @@ function (newDoc, savedDoc, userCtx) { */ } else { // if doc already exists, let noone update or delete it, unless its delegation map: - if (savedDoc && !_id.endsWith("shared_map")) { + if (savedDoc && !_id.endsWith("inverse_indirect_map") && !_id.endsWith("direct_delegation_map")) { throw ({forbidden: 'Noone may update or delete existing poll documents.'}); } // let only the voters create it: diff --git a/src/app/data.service.ts b/src/app/data.service.ts index ed6c70c47..9dab7c0f3 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1522,7 +1522,7 @@ export class DataService implements OnDestroy { } else { this.G.L.error("DataService.setp change option attempted for existing entry", pid, key, value); } - } else if (key == 'shared_map') { + } else if (key == 'inverse_indirect_map') { return this._setp_in_polldb(pid, key, value); }else { this.G.L.error("DataService.setp non-local attempted for non-draft poll", pid, key, value); @@ -1559,7 +1559,6 @@ export class DataService implements OnDestroy { } getv(pid: string, key: string, vid?: string): string { - console.log("getv", pid, key, vid) // get own voter data item let value = null; if (this.pid_is_draft(pid)) { @@ -1575,12 +1574,10 @@ export class DataService implements OnDestroy { this.ensure_poll_cache(pid); value = this.poll_caches[pid][pkey] || ''; } - console.log("getv", pid, key, vid, value); return value; } setv(pid: string, key: string, value: string): boolean { - console.log("setv", pid, key, value) /** Set a voter data item. * If necessary, mark the database entry with poll's due date * to allow couchdb validating that due date is not passed. @@ -1596,13 +1593,14 @@ export class DataService implements OnDestroy { } } + // inverse_indirect_map:- key: voterid, value: [voterid1, voterid2, voterid3] voterids that have effectively delegated to the voterid set_inverse_indirect_map(pid: string, val: Map) { - const mapKey = `poll.${pid}.shared_map`; + const mapKey = `poll.${pid}.inverse_indirect_map`; this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries()))); } set_effective_delegation(pid: string, vid: string, val: string[]) { - const mapKey = `poll.${pid}.shared_map`; + const mapKey = `poll.${pid}.inverse_indirect_map`; const currentMap = this.get_inverse_indirect_map(pid); currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair @@ -1610,12 +1608,32 @@ export class DataService implements OnDestroy { } get_inverse_indirect_map(pid: string): Map { - const cache = this.poll_caches[pid]['poll.' + pid + '.shared_map'] || '[]'; + const cache = this.poll_caches[pid]['poll.' + pid + '.inverse_indirect_map'] || '[]'; const ps = cache ? JSON.parse(cache) : {}; const mp = new Map(ps); + console.log("inverse_indirect_map: ", mp); return mp; } + // direct_delegation_map:- key: voterid, value: [[voterid, rank, is_current_delegation], [voterid2, rank, is_current_delegate]] of the voter that the key has delegated to. + // When ranked delegation is not allowed, the rank is always 0 and the size of the value is 1. + set_direct_delegation_map(pid: string, val: Map>) { + const mapKey = `poll.${pid}.direct_delegation_map`; + this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries()))); + } + + get_direct_delegation_map(pid: string): Map> { + const cache = this.poll_caches[pid]['poll.' + pid + '.direct_delegation_map'] || '[]'; + const ps = cache ? JSON.parse(cache) : {}; + const mp = new Map>(ps); + return mp; + } + + clear_direct_delegation_map(pid: string) { + const mapKey = `poll.${pid}.direct_delegation_map`; + this._setp_in_polldb(pid, mapKey, JSON.stringify([])); + } + delv(pid: string, key: string, vid?: string) { if(!this.getv(pid, key)) { this.G.L.warn("DataService.delv nothing to delete", pid, key); @@ -2364,7 +2382,7 @@ export class DataService implements OnDestroy { // key existed in poll db, check whether update is allowed. const value = dict[dict_key]; const enc_value = encrypt(value, poll_pw); - if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value) && (key.indexOf("shared_map") == -1)) { + if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value) && (key.indexOf("inverse_indirect_map") == -1) && (key.indexOf("direct_delegation_map") == -1)) { // this is not allowed for poll docs! this.G.L.error("DataService.store_poll_data tried changing an existing poll data item", pid, key, value); } else if ((key == 'due') && (doc.due != value)) { @@ -2426,7 +2444,7 @@ export class DataService implements OnDestroy { // check which voter's data this is: const vid_prefix = key.slice(0, key.indexOf("§")), vid = this.user_cache[get_poll_key_prefix(pid) + 'myvid']; - if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true' && key.indexOf("shared_map") == -1) { + if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true' && key.indexOf("inverse_indirect_map") == -1) { // it is not allowed to alter other voters' data! this.G.L.error("DataService.store_poll_data tried changing another voter's data item", pid, key); diff --git a/src/app/delegation-dialog/delegation-dialog.page.html b/src/app/delegation-dialog/delegation-dialog.page.html index 594d35d17..351e19482 100644 --- a/src/app/delegation-dialog/delegation-dialog.page.html +++ b/src/app/delegation-dialog/delegation-dialog.page.html @@ -60,6 +60,20 @@

+ + + + + + + {{i}} + + + + + diff --git a/src/app/delegation-dialog/delegation-dialog.page.ts b/src/app/delegation-dialog/delegation-dialog.page.ts index 95747c05d..33ab1d945 100644 --- a/src/app/delegation-dialog/delegation-dialog.page.ts +++ b/src/app/delegation-dialog/delegation-dialog.page.ts @@ -18,7 +18,7 @@ along with vodle. If not, see . */ import { Component, OnInit, Input, ViewChild } from '@angular/core'; -import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl } from '@angular/forms'; +import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl, Form } from '@angular/forms'; import { IonInput, PopoverController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; @@ -59,6 +59,8 @@ export class DelegationDialogPage implements OnInit { message_title: string; message_body: string; mailto_url: string; + rank: number; + rank_options: number[]; constructor( private popover: PopoverController, @@ -75,8 +77,25 @@ export class DelegationDialogPage implements OnInit { this.can_share = Capacitor.isNativePlatform() || this.can_use_web_share; this.formGroup = this.formBuilder.group({ delegate_nickname: new UntypedFormControl('', Validators.required), - from: new UntypedFormControl(this.G.S.email) + from: new UntypedFormControl(this.G.S.email), }); + + // checks if ranked delegation is allowed and if so, initialises the values needed for the drop-down menu + if (this.G.D.get_ranked_delegation_allowed(this.parent.pid)) { + const ddm = this.G.D.get_direct_delegation_map(this.parent.pid); + console.log("dir_delegation_map", ddm); + console.log("THIS USER a.dir_", this.G.P.polls[this.parent.pid].myvid); + for (const [uid, dels] of ddm) { + console.log("user.dir_", uid); + for (const del of dels) { + console.log("a.dir_", del); + const a = this.G.Del.get_agreement(this.parent.pid, del[0]); + console.log("a.dir_", a); + } + } + this.initialise_rank_values(); + } + // TODO: what if already some delegation active or pending? // prepare a new delegation: [this.p, this.did, this.request, this.private_key, this.agreement] = this.G.Del.prepare_delegation(this.parent.pid); @@ -92,6 +111,24 @@ export class DelegationDialogPage implements OnInit { setTimeout(() => this.focus_element.setFocus(), 100); } + initialise_rank_values() { + const uid = this.parent.p.myvid; + const dir_del_map = this.G.D.get_direct_delegation_map(this.parent.pid); + const dir_del = dir_del_map.get(uid) || []; + var ranks = Array.from({ length: environment.delegation.max_delegations }, (_, i) => i + 1); + for (const entry of dir_del) { + if (entry === undefined) { + continue; + } + const indexToRemove: number = ranks.indexOf(Number(entry[1])); + if (indexToRemove !== -1) { + ranks.splice(indexToRemove, 1); + } + } + this.rank = ranks[0]; + this.rank_options = ranks; + } + delegate_nickname_changed() { const delegate_nickname = this.formGroup.get('delegate_nickname').value; this.G.D.setp(this.p.pid, "del_nickname." + this.did, delegate_nickname); @@ -105,6 +142,10 @@ export class DelegationDialogPage implements OnInit { this.update_request(); } + rank_changed(e) { + this.rank = e.detail.value; + } + update_request() { this.G.L.entry("DelegationDialogPage.update_request"); this.mailto_url = "mailto:" + encodeURIComponent(this.formGroup.get('delegate_nickname').value) + "?subject=" + encodeURIComponent(this.message_title) + "&body=" + encodeURIComponent(this.message_body); @@ -147,6 +188,7 @@ export class DelegationDialogPage implements OnInit { }).then(res => { this.G.L.info("DelegationDialogPage.share_button_clicked succeeded", res); this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement); + this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank); this.popover.dismiss(); }).catch(err => { this.G.L.error("DelegationDialogPage.share_button_clicked failed", err); @@ -159,6 +201,7 @@ export class DelegationDialogPage implements OnInit { this.from_changed(); window.navigator.clipboard.writeText(this.delegation_link); this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement); + this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank); LocalNotifications.schedule({ notifications: [{ title: this.translate.instant("delegation-request.notification-copied-link-title"), @@ -182,6 +225,7 @@ export class DelegationDialogPage implements OnInit { this.delegate_nickname_changed(); this.from_changed(); this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement); + this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank); this.parent.update_delegation_info(); this.popover.dismiss(); this.G.L.exit("DelegationDialogPage.email_button_clicked"); diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index cbbe408bb..3614fbe55 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -36,6 +36,7 @@ import { environment } from '../environments/environment'; import { GlobalService } from './global.service'; import { del_request_t, del_signed_response_t, del_response_t, del_option_spec_t, del_agreement_t } from './data.service'; import { Poll } from './poll.service'; +import { min } from 'rxjs/operators'; @Injectable({ providedIn: 'root' @@ -126,14 +127,13 @@ export class DelegationService { return a.delegate_vid; } - update_my_delegation(pid: string, oid: string, activate: boolean) { + update_my_delegation(pid: string, oid: string, activate: boolean, did? :string) { /** Called when voter toggles an option's delegation switch. * (De)activate an option's delegation */ const p = this.G.P.polls[pid]; - let did = this.get_my_outgoing_dids_cache(pid).get(oid); - if (!did) { - did = this.get_my_outgoing_dids_cache(pid).get("*"); - } + const dir_del_map = this.G.D.get_direct_delegation_map(pid); + const list = dir_del_map.get(p.myvid) || []; + if (!did) { this.G.L.error("DelegationService.update_my_delegation without existing did", pid, oid, activate); } else { @@ -213,7 +213,7 @@ export class DelegationService { const sm = this.G.D.get_inverse_indirect_map(pid); const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); - // gets all voters that are affected by the delegation being + // gets all voters that are affected by the delegation being deleted var stack = []; for (let id of sm.keys()) { if (JSON.parse(sm.get(id) || "[]").includes(a.client_vid)) { @@ -234,7 +234,38 @@ export class DelegationService { sm.set(curr_del, JSON.stringify(Array.from(new_delegate_eff_set))); } - this.G.D.set_inverse_indirect_map(pid, sm); + this.G.D.set_inverse_indirect_map(pid, sm); + + // update direct delegation map + var dir_del_map = this.G.D.get_direct_delegation_map(pid); + var dir_del = dir_del_map.get(this.G.P.polls[pid].myvid) || []; + console.log("revoked_dir_del: ", dir_del); + var new_list = []; + for (var entry of dir_del) { + console.log("revoked_dir_del_entry: ", entry); + if (entry[2] === '2') { + continue; + } + new_list.push(entry); + } + console.log("revoked_dir_del_new_list: ", new_list); + dir_del_map.set(this.G.P.polls[pid].myvid, new_list); + console.log("revoked_dir_del_map: ", dir_del_map); + this.G.D.set_direct_delegation_map(pid, dir_del_map); + + if (this.G.D.get_ranked_delegation_allowed(pid)){ + this.find_path(pid); + + const dm = this.G.D.get_direct_delegation_map(pid); + const list = dm.get(this.G.P.polls[pid].myvid) || []; + var d; + for (const [did2, rank, active] of list) { + if (active === '2') { + d = did2; + } + } + } + this.G.L.exit("DelegationService.revoke_delegation"); } @@ -345,7 +376,6 @@ export class DelegationService { } accept(pid: string, did: string, private_key: string) { - console.log("accepting delegation"); /** accept a delegation request, store response in db */ const response = {option_spec: {type: "-", oids: []}} as del_response_t, // i.e., exclude no oids, meaning accept all oids. TODO: allow partial acceptance for only some options signed_response = this.sign_response(response, private_key); @@ -371,6 +401,24 @@ export class DelegationService { } } this.G.D.set_inverse_indirect_map(pid, new_sm); + + // update direct delegation map + var dir_del_map = this.G.D.get_direct_delegation_map(pid); + var dir_del = dir_del_map.get(a.client_vid) || []; + console.log("accepted_dir_del: ", dir_del_map); + for (var entry of dir_del) { + if (entry[0] === did) { + entry[2] = '1'; + break; + } + } + dir_del_map.set(a.client_vid, dir_del); + console.log("accepted_dir_del_after: ", dir_del_map); + this.G.D.set_direct_delegation_map(pid, dir_del_map); + + if (this.G.D.get_ranked_delegation_allowed(pid)){ + this.find_path(pid); + } } decline(pid: string, did: string, private_key?: string) { @@ -395,34 +443,117 @@ export class DelegationService { this.set_my_signed_response(pid, did, signed_response); } - handle_deleted_delegation(pid:string, did:string){ - var updated_ids = new Set(); + private find_path(pid: string) { + this.min_sum_all(pid); + } + + private delegating_voters(pid: string) : Set { + let del_voters = new Set(); + const dir_del_map = this.G.D.get_direct_delegation_map(pid); + for (const vid of this.G.P.polls[pid].T.all_vids_set) { + for (const [did, rank, active] of dir_del_map.get(vid) || []) { + if (active != '0') { + del_voters.add(vid); + break; + } + } + } + return del_voters; + } + + private is_casting_voter(pid: string, vid: string) { + console.log("is_casting_voter: dir_", vid); + const dir_del_map = this.G.D.get_direct_delegation_map(pid); + for (const [did, rank, active] of dir_del_map.get(vid) || []) { + if (active == '0') { + continue; + } + return false; + } + return true; + } + + private find_all_paths(pid: string, vid: string, current_path: string[], paths: string[][]) { + const dm = this.G.D.get_direct_delegation_map(pid); + console.log("current_path: dir_", current_path); + for (const [did, _, active] of dm.get(vid) || []) { + if (active == '0') { + continue; + } + const a = this.get_agreement(pid, did); + let new_path: string[] = [...current_path, did]; + if (this.is_casting_voter(pid, a.delegate_vid)) { + paths.push(new_path); + console.log("paths_added: dir_", paths, new_path); + } else if (!current_path.includes(did)) { + this.find_all_paths(pid, a.delegate_vid, new_path, paths); + } + } + } + + private get_rank_from_did(pid: string, did: string) : number { + const dm = this.G.D.get_direct_delegation_map(pid); const a = this.get_agreement(pid, did); - var eff_del_map = new Map>(); - // iterate through all voters and populate map; - let p = this.G.P.polls[pid]; - for (let id of p.T.all_vids_set) { - eff_del_map.set(id, new Set(JSON.parse(this.G.D.getv(pid, "del_effective." + id, "vodle") || "[]"))); - } - // update effective delegation for any voter that delegate was delegating to - for (let id of eff_del_map.get(a.client_vid)) { - if (!eff_del_map.get(id).has(a.client_vid)) { + for (const [did2, rank, active] of dm.get(a.client_vid) || []) { + if (did2 == did) { + console.log("path_dir_rank", rank, did); + return Number(rank); + } + } + return 0; + } + + private min_sum(pid: string, vid: string) : Array { + const dm = this.G.D.get_direct_delegation_map(pid); + let paths: string[][] = [[]]; + this.find_all_paths(pid, vid, [], paths); + + let minSumPath: string[] = []; + let minSum = Number.MAX_VALUE; + + for (const path of paths) { + if (path.length == 0) { continue; } - var new_eff_del_set = new Set(); - for (let id2 of eff_del_map.get(id)) { - if (id2 != a.client_vid && eff_del_map.get(a.client_vid).has(id2)) { - new_eff_del_set.add(id2); - } + console.log("Checking path: dir_", path); + let pathSum = 0; + for (const did of path) { + pathSum += this.get_rank_from_did(pid, did); + } + console.log("pathSum: dir_", pathSum); + if (pathSum < minSum) { + minSum = pathSum; + minSumPath = path; } - this.G.D.setv_in_polldb(pid, "del_effective." + id, JSON.stringify(Array.from(new_eff_del_set)), "vodle"); - updated_ids.add(id); } - - for (let id of updated_ids.values()){ - console.log("hd_updated id: ", id); - console.log("hd_updated set: ", this.G.D.getv(pid, "del_effective." + id, "vodle")); + return minSumPath; + } + + private min_sum_all(pid: string) { + const dm = this.G.D.get_direct_delegation_map(pid); + for (let vid of this.delegating_voters(pid)) { + const minSumPath = this.min_sum(pid, vid); + console.log("minSumPath: dir_", minSumPath, vid); + for (const did of minSumPath) { + const a = this.get_agreement(pid, did); + const delegations = dm.get(a.client_vid) || []; + var newDelegations = []; + console.log("min_sum_all_path_deleg: dir_", delegations, a.client_vid, this.G.P.polls[pid].myvid, a); + for (const [did2, rank, active] of delegations) { + if (did2 == did) { + newDelegations.push([did2, rank, '2']); + } else if (active === '2') { + newDelegations.push([did2, rank, '1']); + } else { + newDelegations.push([did2, rank, active]); + } + } + dm.set(a.client_vid, newDelegations); + } } + console.log("min_sum_all_end_path: dir_", dm); + this.G.D.set_direct_delegation_map(pid, dm); + console.log("min_sum_all_end_path2: dir_", this.G.D.get_direct_delegation_map(pid)); } // DATA HANDLING: @@ -443,6 +574,25 @@ export class DelegationService { this.G.D.setp(pid, "del_private_key." + did, value); } + get_delegate_rank(pid: string, did: string): number { + const rank = Number(this.G.D.getv(pid, "del_rank." + did)); + console.log("dir_del", rank); + console.log("dir_del_wo", this.G.D.getv(pid, "del_rank." + did)); + return rank; + } + + set_delegate_rank(pid: string, did: string, value: number) { + // this.G.D.setv(pid, "del_rank." + did, JSON.stringify(value)); + var direct_del_map = this.G.D.get_direct_delegation_map(pid); + var myvid = this.G.P.polls[pid].myvid; + var dir_del = direct_del_map.get(myvid) || []; + dir_del.push([did, JSON.stringify(value), '0']); + direct_del_map.set(myvid, dir_del); + console.log("dir_del_set", direct_del_map); + // direct_del_map.set(myvid, [[did, JSON.stringify(value), '0']]); + this.G.D.set_direct_delegation_map(pid, direct_del_map); + } + get_request(pid: string, did: string, client_vid?: string): del_request_t { if (!client_vid) { client_vid = this.get_agreement(pid, did).client_vid; @@ -545,6 +695,7 @@ export class DelegationService { } update_agreement(pid: string, did: string, agreement: del_agreement_t, request: del_request_t, signed_response: del_signed_response_t) { + console.log("UPDATE AGREEMENT a.dir_", did, agreement, request, signed_response); /** after changes to request or response, * compare request and response, set status, extract accepted and active oids */ this.G.L.entry("DelegationService.update_agreement", pid, did, agreement, request, signed_response); diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index ab74ac5be..ec5870eae 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -534,8 +534,7 @@ export class Poll { get_my_proxy_rating(oid: string): number { if (this.delegate_id){ - const agr = this.G.Del.get_agreement(this.pid, this.did); - console.log("shared_oid: ", agr.active_oids); + const agr = this.G.Del.get_agreement(this.pid, this.delegate_id); if (agr.active_oids.has(oid)){ return +this.G.D.getv(this.pid, "rating."+oid, this.delegate_id)||0; } @@ -603,12 +602,16 @@ export class Poll { } get have_delegated(): boolean { - const did = this.G.Del.get_my_outgoing_dids_cache(this.pid).get("*"); - if (!did) return false; - const agreement = this.G.Del.get_agreement(this.pid, did); - this.did = did; - this.delegate_id = agreement.delegate_vid; - return (agreement.status == "agreed") && (agreement.active_oids.size == agreement.accepted_oids.size); + const dir_del = this.G.D.get_direct_delegation_map(this.pid); + const list = dir_del.get(this.myvid) || []; + for (const entry of list) { + if (entry[2] == "2") { + const a = this.G.Del.get_agreement(this.pid, entry[0]); + this.delegate_id = a.delegate_vid; + return true; + } + } + return false; } getDidFromUrl(url: string): string | null { @@ -1013,7 +1016,7 @@ export class Poll { // make sure no delegation exists yet: // (we no longer require that delegation would not create a cycle) - if (dir_d_map.has(client_vid)) { + if (dir_d_map.has(client_vid) && !this.G.D.get_ranked_delegation_allowed(this.pid)) { if (dir_d_map.get(client_vid) == delegate_vid) { this.G.L.warn("PollService.add_delegation of existing delegation", this._pid, client_vid, oid, delegate_vid, dir_d_map.get(client_vid)); @@ -1160,7 +1163,7 @@ export class Poll { inv_ind_d_map = this.inv_indirect_delegation_map.get(oid), inv_ind_ds_of_client = inv_ind_d_map.get(client_vid), inv_eff_d_map = this.inv_effective_delegation_map.get(oid), - inv_eff_ds_of_client = inv_eff_d_map.get(client_vid), + inv_eff_ds_of_client = inv_eff_d_map.get(client_vid) || new Set(), inv_eff_ds_of_old_eff_d_of_client = inv_eff_d_map.get(old_eff_d_of_client), // is_on_cycle = ind_d_map.get(old_d_vid).has(client_vid); is_on_cycle = false; @@ -1259,7 +1262,6 @@ export class Poll { get_n_indirect_clients(vid: string): number { /** count how many voters have indirectly delegated to vid for some oid */ - console.log("poll_id", this._pid); const map = this.G.D.get_inverse_indirect_map(this._pid); const set = new Set(JSON.parse(map.get(vid) || "[]")); return set.size; diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index 1d3fc75c1..edb3ced7b 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -88,7 +88,7 @@ - { console.log('Confirm Ok.'); - this.G.Del.revoke_delegation(this.pid, this.G.Del.get_my_outgoing_dids_cache(this.pid).get("*"), '*'); + var did = null; + const dm = this.G.D.get_direct_delegation_map(this.pid); + const list = dm.get(this.p.myvid) || []; + for (const [did_, _, status] of list) { + if (status == '2') { + did = did_; + break; + } + } + console.log("revoke_found_did", did); + this.G.Del.revoke_delegation(this.pid, did, "*"); console.log('Delegation revoked.'); - this.delegate = null; - this.delegation_status = 'none'; + // this.delegate = null; + this.set_delegate(); + this.delegation_status = this.delegate ? 'agreed' : 'none'; console.log("id: ", this.p.myvid); this.update_delegation_info(); this.G.D.save_state(); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index cc455a518..d4f9deaf7 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -175,6 +175,7 @@ "nickname-placeholder": "Please enter the nickname of that person (or their true name)", "from-label": "The delegate will see the following as the sender of this request:", "from-placeholder": "Enter your own e-mail address, name or nickname", + "rank-label": "Rank of delegation:", "request-options-with-share": "The simplest way to ask that person to act as your delegate is via the share button. Alternatively, you can write them an e-mail. Or you copy the delegation link and send it to them in any way you like.", "request-options-without-share": "The simplest way to ask that person to act as your delegate is via e-mail. Or you copy the delegation link and send it to them in any way you like.", "share": "Use a messenger app", From b46f7741128b0935d6a886556beede9968800683 Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sat, 21 Dec 2024 18:37:56 +0000 Subject: [PATCH 14/16] deleted some unnecessary caches --- src/app/data.service.ts | 13 -- src/app/delegation.service.ts | 53 ++--- src/app/poll.service.ts | 422 +--------------------------------- src/app/poll/poll.page.html | 4 - src/app/poll/poll.page.ts | 8 - 5 files changed, 17 insertions(+), 483 deletions(-) diff --git a/src/app/data.service.ts b/src/app/data.service.ts index 9dab7c0f3..ac36d3808 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -312,13 +312,6 @@ export class DataService implements OnDestroy { delegation_agreements_caches: Record>; // by pid, did - direct_delegation_map_caches: Record>>; // redundant storage of direct delegation data, not stored in database - inv_direct_delegation_map_caches: Record>>>; // redundant storage of inverse direct delegation data, not stored in database - indirect_delegation_map_caches: Record>>>; // redundant storage of indirect delegation data, not stored in database - inv_indirect_delegation_map_caches: Record>>>; // redundant storage of inverse indirect delegation data, not stored in database - effective_delegation_map_caches: Record>>; // redundant storage of effective delegation data, not stored in database - inv_effective_delegation_map_caches: Record>>>; // redundant storage of inverse effective delegation data, not stored in database - tally_caches: Record; // temporary storage of tally data, not stored in database news_keys: Set; @@ -436,12 +429,6 @@ export class DataService implements OnDestroy { this.outgoing_dids_caches = {}; this.incoming_dids_caches = {}; this.delegation_agreements_caches = {}; - this.direct_delegation_map_caches = {}; - this.inv_direct_delegation_map_caches = {}; - this.indirect_delegation_map_caches = {}; - this.inv_indirect_delegation_map_caches = {}; - this.effective_delegation_map_caches = {}; - this.inv_effective_delegation_map_caches = {}; this.proxy_ratings_map_caches = {}; this.max_proxy_ratings_map_caches = {}; this.argmax_proxy_ratings_map_caches = {}; diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index 3614fbe55..d5df75848 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -197,11 +197,6 @@ export class DelegationService { const acache = this.get_delegation_agreements_cache(pid); if (acache) { const oids = acache.get(did).active_oids; - if (oids) { - for (const oid of oids) { - p.del_delegation(p.myvid, oid); - } - } acache.delete(did); } } @@ -304,26 +299,22 @@ export class DelegationService { // check if already delegating (in)directly back to client_vid for at least one option: var status: Array; - const dirdelmap = this.G.D.direct_delegation_map_caches[pid], - effdelmap = this.G.D.effective_delegation_map_caches[pid], - inveffdelmap = this.G.D.inv_effective_delegation_map_caches[pid], - myvid = p.myvid, + const myvid = p.myvid, client_vid = agreement.client_vid; if (client_vid == myvid) { return ["impossible", "is-self"]; } let two_way = false, cycle = false, weight_exceeded = false; - for (let oid of p.oids) { - const effdel_vid = effdelmap.get(oid).get(myvid) || myvid; - const thisinveffdelmap = inveffdelmap.get(oid) || new Map(); - if (1 + (thisinveffdelmap.get(client_vid)||new Set([client_vid])).size - + (thisinveffdelmap.get(effdel_vid)||new Set([effdel_vid])).size - > environment.delegation.max_weight) { - weight_exceeded = true; - break; - } - if ((dirdelmap.get(oid) || new Map()).get(myvid) == client_vid) { - two_way = true; + const dirdelmap = this.G.D.get_direct_delegation_map(pid); + + if (!this.G.D.get_ranked_delegation_allowed(pid)){ + const list = dirdelmap.get(myvid) || []; + for (const [did2, _, active] of list) { + const a = this.get_agreement(pid, did2); + if (a.client_vid == client_vid) { + two_way = true; + break; + } } } // check for cycles: @@ -666,11 +657,6 @@ export class DelegationService { const acache = this.get_delegation_agreements_cache(pid); if (acache) { const oids = acache.get(did).active_oids; - if (oids) { - for (const oid of oids) { - p.del_delegation(client_vid, oid); - } - } acache.delete(did); } } @@ -783,19 +769,13 @@ export class DelegationService { if (!(a.accepted_oids.has(oid) && request.option_spec.oids.includes(oid))) { // oid no longer active: a.active_oids.delete(oid); - p.del_delegation(a.client_vid, oid); this.G.L.trace("DelegationService.update_agreement deactivated oid", pid, oid); } } for (const oid of request.option_spec.oids) { if (a.accepted_oids.has(oid) && !a.active_oids.has(oid)) { // oid newly active: - if (p.add_delegation(a.client_vid, oid, a.delegate_vid)) { - a.active_oids.add(oid); - this.G.L.trace("DelegationService.update_agreement activated oid", pid, oid); - } else { - this.G.L.warn("DelegationService.update_agreement couldn't activate oid", pid, oid); - } + a.active_oids.add(oid); } } } else if (request.option_spec.type == "-") { @@ -804,19 +784,12 @@ export class DelegationService { if (request.option_spec.oids.includes(oid) || !a.accepted_oids.has(oid)) { // oid no longer active: a.active_oids.delete(oid); - p.del_delegation(a.client_vid, oid); this.G.L.trace("DelegationService.update_agreement deactivated oid", pid, oid); } } for (const oid of a.accepted_oids) { if ((!a.active_oids.has(oid)) && (!request.option_spec.oids.includes(oid))) { - // oid newly active: - if (p.add_delegation(a.client_vid, oid, a.delegate_vid)) { - a.active_oids.add(oid); - this.G.L.trace("DelegationService.update_agreement activated oid", pid, oid); - } else { - this.G.L.warn("DelegationService.update_agreement couldn't activate oid", pid, oid); - } + a.active_oids.add(oid); } } } diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index ec5870eae..4321bafda 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -479,12 +479,6 @@ export class Poll { this._options[o.oid] = o; if (!this.own_ratings_map.has(o.oid)) this.own_ratings_map.set(o.oid, new Map()); if (!this.proxy_ratings_map.has(o.oid)) this.proxy_ratings_map.set(o.oid, new Map()); - if (!this.direct_delegation_map.has(o.oid)) this.direct_delegation_map.set(o.oid, new Map()); - if (!this.inv_direct_delegation_map.has(o.oid)) this.inv_direct_delegation_map.set(o.oid, new Map()); - if (!this.indirect_delegation_map.has(o.oid)) this.indirect_delegation_map.set(o.oid, new Map()); - if (!this.inv_indirect_delegation_map.has(o.oid)) this.inv_indirect_delegation_map.set(o.oid, new Map()); - if (!this.effective_delegation_map.has(o.oid)) this.effective_delegation_map.set(o.oid, new Map()); - if (!this.inv_effective_delegation_map.has(o.oid)) this.inv_effective_delegation_map.set(o.oid, new Map()); return true; } @@ -628,56 +622,6 @@ export class Poll { have_been_delegated(clientVid: string, listOfDelInv: object[]) { return false; - var ret = false; - this.G.L.entry("Poll.have_been_delegated", clientVid, listOfDelInv); - // check if clientVid has been delegated to by any of the vids in listOfDelInv - for (const delInv of listOfDelInv) { - const url = delInv["url"]; - const did = this.getDidFromUrl(url); - const agreement = this.G.Del.get_agreement(this.pid, did); - - const delId = agreement.client_vid; - const s = this.G.D.getv(this._pid, "del_status." + did, delId); - if (!s){ - ret = ret || agreement.active_oids.size > 0; - continue; - } - if(["agreed", "pending"].includes(agreement.status) && s == "revoked"){ - this.G.D.incoming_dids_caches[this.pid].get(did)[2] = "revoked"; - ret = false; - - // remove from local cache - for (const oid of this.oids) { - // remove from direct - this.direct_delegation_map.get(oid).delete(delId); - - // remove from indirect - const clientInvDelMap = this.inv_indirect_delegation_map.get(oid).get(clientVid); - const delInvMap = this.inv_indirect_delegation_map.get(oid).get(agreement.client_vid); - var setDiff = new Set([]); - clientInvDelMap.forEach((value, key) => { - console.log("key: ", key); - console.log("value: ", value); - if(!delInvMap.has(key) && value != delId){ - setDiff.add(value); - } - }); - this.inv_indirect_delegation_map.get(oid).set(clientVid, setDiff); - this.G.D.inv_direct_delegation_map_caches[this.pid].get(oid).set(clientVid, setDiff); - } - - // add a notification that the delegation has been revoked - this.G.N.add({ - class: 'delegation_declined', - pid: this.pid, - title: this.G.Del.translate.instant('news-title.delegation_revoked', {nickname: delInv["from"]}) - }); - }else{ - ret = true; - } - } - this.G.L.exit("Poll.have_been_delegated", ret); - return ret; } @@ -833,102 +777,6 @@ export class Poll { return this._own_ratings_map; } - // for each oid and vid, the direct delegate's vid (default: null, meaning no delegation): - _direct_delegation_map: Map>; - get direct_delegation_map(): Map> { - if (!this._direct_delegation_map) { - if (this._pid in this.G.D.direct_delegation_map_caches) { - this._direct_delegation_map = this.G.D.direct_delegation_map_caches[this._pid]; - } else { - this.G.D.direct_delegation_map_caches[this._pid] = this._direct_delegation_map = new Map(); - for (const oid of this.oids) { - this._direct_delegation_map.set(oid, new Map()); - } - } - } - return this._direct_delegation_map; - } - - // for each oid and vid, the set of vids who directly delegated to this vid (default: null, meaning no delegation): - _inv_direct_delegation_map: Map>>; - get inv_direct_delegation_map(): Map>> { - if (!this._inv_direct_delegation_map) { - if (this._pid in this.G.D.inv_direct_delegation_map_caches) { - this._inv_direct_delegation_map = this.G.D.inv_direct_delegation_map_caches[this._pid]; - } else { - this.G.D.inv_direct_delegation_map_caches[this._pid] = this._inv_direct_delegation_map = new Map(); - for (const oid of this.oids) { - this._inv_direct_delegation_map.set(oid, new Map()); - } - } - } - return this._inv_direct_delegation_map; - } - - // for each oid and vid, the set of vids who this voter directly or indirectly delegated to (default: null, meaning no delegation): - _indirect_delegation_map: Map>>; - get indirect_delegation_map(): Map>> { - if (!this._indirect_delegation_map) { - if (this._pid in this.G.D.indirect_delegation_map_caches) { - this._indirect_delegation_map = this.G.D.indirect_delegation_map_caches[this._pid]; - } else { - this.G.D.indirect_delegation_map_caches[this._pid] = this._indirect_delegation_map = new Map(); - for (const oid of this.oids) { - this._indirect_delegation_map.set(oid, new Map()); - } - } - } - return this._indirect_delegation_map; - } - - // for each oid and vid, the set of vids who have directly or indirectly delegated to this voter (default: null, meaning no delegation): - _inv_indirect_delegation_map: Map>>; - get inv_indirect_delegation_map(): Map>> { - if (!this._inv_indirect_delegation_map) { - if (this._pid in this.G.D.inv_indirect_delegation_map_caches) { - this._inv_indirect_delegation_map = this.G.D.inv_indirect_delegation_map_caches[this._pid]; - } else { - this.G.D.inv_indirect_delegation_map_caches[this._pid] = this._inv_indirect_delegation_map = new Map(); - for (const oid of this.oids) { - this._inv_indirect_delegation_map.set(oid, new Map()); - } - } - } - return this._inv_indirect_delegation_map; - } - - // for each oid and vid, the effective delegate's vid (default: null, meaning no delegation): - _effective_delegation_map: Map>; - get effective_delegation_map(): Map> { - if (!this._effective_delegation_map) { - if (this._pid in this.G.D.effective_delegation_map_caches) { - this._effective_delegation_map = this.G.D.effective_delegation_map_caches[this._pid]; - } else { - this.G.D.effective_delegation_map_caches[this._pid] = this._effective_delegation_map = new Map(); - for (const oid of this.oids) { - this._effective_delegation_map.set(oid, new Map()); - } - } - } - return this._effective_delegation_map; - } - - // for each oid and vid, the set of vids who effectively delegated to this vid, excluding the vid itself (default: null, meaning no delegation): - _inv_effective_delegation_map: Map>>; - get inv_effective_delegation_map(): Map>> { - if (!this._inv_effective_delegation_map) { - if (this._pid in this.G.D.inv_effective_delegation_map_caches) { - this._inv_effective_delegation_map = this.G.D.inv_effective_delegation_map_caches[this._pid]; - } else { - this.G.D.inv_effective_delegation_map_caches[this._pid] = this._inv_effective_delegation_map = new Map(); - for (const oid of this.oids) { - this._inv_effective_delegation_map.set(oid, new Map()); - } - } - } - return this._inv_effective_delegation_map; - } - // for each oid and vid, the proxy (post-delegation) rating (default: 0): _proxy_ratings_map: Map>; get proxy_ratings_map(): Map> { @@ -1001,265 +849,6 @@ export class Poll { // Methods dealing with changes to the delegation graph: - add_delegation(client_vid:string, oid:string, delegate_vid:string): boolean { - if (!environment.delegation.enabled) { - this.G.L.error("PollService.add_delegation when delegation is disabled", this._pid, client_vid, oid, delegate_vid); - return false; - } - /** Called whenever a delegation shall be added. Returns whether this succeeded */ - - this.G.L.debug("add_delegation entry", this.pid, oid, client_vid, delegate_vid); - - const dir_d_map = this.direct_delegation_map.get(oid), - eff_d_map = this.effective_delegation_map.get(oid), - new_eff_d_vid = eff_d_map.get(delegate_vid) || delegate_vid; - - // make sure no delegation exists yet: - // (we no longer require that delegation would not create a cycle) - if (dir_d_map.has(client_vid) && !this.G.D.get_ranked_delegation_allowed(this.pid)) { - - if (dir_d_map.get(client_vid) == delegate_vid) { - this.G.L.warn("PollService.add_delegation of existing delegation", this._pid, client_vid, oid, delegate_vid, dir_d_map.get(client_vid)); - return true; - } else { - this.G.L.error("PollService.add_delegation when delegation already exists", this._pid, client_vid, oid, delegate_vid, dir_d_map.get(client_vid)); - return false; - } - - /* - } else if (new_eff_d_vid == client_vid) { - - this.G.L.error("PollService.add_delegation when this would create a cycle", this._pid, client_vid, oid, delegate_vid); - return false; - */ - } else { - - this.G.L.trace("PollService.add_delegation feasible", this._pid, client_vid, oid, delegate_vid); - - // register DIRECT delegation and inverse: - dir_d_map.set(client_vid, delegate_vid); - const inv_dir_d_map = this.inv_direct_delegation_map.get(oid); - if (!inv_dir_d_map.has(delegate_vid)) { - inv_dir_d_map.set(delegate_vid, new Set()); - } - inv_dir_d_map.get(delegate_vid).add(client_vid); - - // update INDIRECT delegations and inverses: - const ind_d_map = this.indirect_delegation_map.get(oid), - ind_ds_of_delegate = ind_d_map.get(delegate_vid), - inv_ind_d_map = this.inv_indirect_delegation_map.get(oid), - inv_eff_d_map = this.inv_effective_delegation_map.get(oid); - if (!inv_ind_d_map.has(delegate_vid)) { - inv_ind_d_map.set(delegate_vid, new Set()); - } - const inv_ind_ds_of_delegate = inv_ind_d_map.get(delegate_vid), - inv_eff_ds_of_client = inv_eff_d_map.get(client_vid); - // vid: - const ind_ds_of_client = new Set([delegate_vid]); - ind_d_map.set(client_vid, ind_ds_of_client); - inv_ind_ds_of_delegate.add(client_vid); - if (ind_ds_of_delegate) { - for (const vid of ind_ds_of_delegate) { - if (vid != client_vid) { // avoid self-reference entries - ind_ds_of_client.add(vid); - if (!inv_ind_d_map.has(vid)) { - inv_ind_d_map.set(vid, new Set()); - } - inv_ind_d_map.get(vid).add(client_vid); - } - } - } - // voters dependent on client: - if (inv_eff_ds_of_client) { - for (const vid of inv_eff_ds_of_client) { - const ind_ds_of_vid = ind_d_map.get(vid); - if (vid != delegate_vid) { // avoid self-reference entries - ind_ds_of_vid.add(delegate_vid); - inv_ind_ds_of_delegate.add(vid); - } - if (ind_ds_of_delegate) { - for (const vid2 of ind_ds_of_delegate) { - if (vid2 != vid) { // avoid self-reference entries - ind_ds_of_vid.add(vid2); - if (!inv_ind_d_map.has(vid2)) { - inv_ind_d_map.set(vid2, new Set()); - } - inv_ind_d_map.get(vid2).add(vid); - } - } - } - } - } - - // update EFFECTIVE delegations, inverses, and proxy ratings: - if (new_eff_d_vid == client_vid) { - // a cycle is created - const rmap = this.own_ratings_map.get(oid); - let vid = delegate_vid, - new_proxy_rating = rmap.get(vid) || 0, - cycle_len = 0, - includes_me = false; - // loop through cycle members: - for (cycle_len = 1; cycle_len <= this.T.all_vids_set.size; cycle_len++) { - vid = dir_d_map.get(vid); - if (vid == delegate_vid) break; - // use max rating on cycle as new proxy rating: - new_proxy_rating = Math.max(new_proxy_rating, this.own_ratings_map.get(oid).get(vid) || 0); - if (vid == this.myvid) { - includes_me = true; - } - } - this.G.L.info("add_delegation created cycle of length", cycle_len, includes_me, this.pid, oid, client_vid, delegate_vid); - if (includes_me) { - this.T.my_cycle_len = cycle_len; - } - this.update_proxy_rating(client_vid, oid, new_proxy_rating); - // update proxy rating of all dependent voters: - if (inv_eff_ds_of_client) { - for (const vid of inv_eff_ds_of_client) { - this.update_proxy_rating(vid, oid, new_proxy_rating); - } - } - } else { - // determine new proxy rating: - const new_proxy_rating = this.own_ratings_map.get(oid).get(new_eff_d_vid) || 0; - // update eff_d_map and inverse: - if (!inv_eff_d_map.has(new_eff_d_vid)) { - inv_eff_d_map.set(new_eff_d_vid, new Set()); - } - const inv_eff_ds_of_new_eff_d = inv_eff_d_map.get(new_eff_d_vid); - // this vid: - eff_d_map.set(client_vid, new_eff_d_vid); - inv_eff_ds_of_new_eff_d.add(client_vid); - this.update_proxy_rating(client_vid, oid, new_proxy_rating); - // dependent voters: - if (inv_eff_ds_of_client) { - for (const vid of inv_eff_ds_of_client) { - eff_d_map.set(vid, new_eff_d_vid); - inv_eff_ds_of_new_eff_d.add(vid); - this.update_proxy_rating(vid, oid, new_proxy_rating); - } - } - } - this.G.L.debug("add_delegation exit", this.pid, oid, client_vid, delegate_vid); - return true; - } - } - - del_delegation(client_vid: string, oid: string) { - console.log("del_delegation", client_vid, oid); - // Called whenever a voter revokes her delegation for some option - const dir_d_map = this.direct_delegation_map.get(oid), - eff_d_map = this.effective_delegation_map.get(oid); - // make sure a delegation exists: - if (!dir_d_map.has(client_vid)) { - this.G.L.error("PollService.del_delegation when no delegation exists", client_vid, oid); - } else { - const old_d_vid = dir_d_map.get(client_vid), - old_eff_d_of_client = eff_d_map.get(client_vid), - inv_dir_d_map = this.inv_direct_delegation_map.get(oid), - ind_d_map = this.indirect_delegation_map.get(oid), - old_ind_ds_of_client = ind_d_map.get(client_vid), - inv_ind_d_map = this.inv_indirect_delegation_map.get(oid), - inv_ind_ds_of_client = inv_ind_d_map.get(client_vid), - inv_eff_d_map = this.inv_effective_delegation_map.get(oid), - inv_eff_ds_of_client = inv_eff_d_map.get(client_vid) || new Set(), - inv_eff_ds_of_old_eff_d_of_client = inv_eff_d_map.get(old_eff_d_of_client), - // is_on_cycle = ind_d_map.get(old_d_vid).has(client_vid); - is_on_cycle = false; - - this.G.L.debug("del_delegation entry", this.pid, oid, client_vid, old_d_vid, is_on_cycle); - - // deregister DIRECT delegation and inverse of vid: - this.direct_delegation_map.get(oid).delete(client_vid); - dir_d_map.delete(client_vid); - inv_dir_d_map.get(old_d_vid).delete(client_vid); - - // deregister INDIRECT delegation of client_vid to others: - for (const vid of old_ind_ds_of_client) { - inv_ind_d_map.get(vid).delete(client_vid); - } - ind_d_map.delete(client_vid); - - // deregister INDIRECT delegations no longer valid: - if (is_on_cycle) { - // we're on a cycle, so - // first find cycle elements in correct forward order: - let vid = old_d_vid, - former_cycle = [vid]; - // loop through cycle members: - while (true) { - vid = dir_d_map.get(vid); - former_cycle.push(vid); - if (vid == client_vid) break; - } - if (former_cycle.includes(this.myvid)) { - this.T.my_cycle_len = null; - } - // now for each vid indirectly delegating to client, ... - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - // follow delegation path to cycle: - let cycle_vid = vid, - cycle_pos = -1; - while (true) { - cycle_pos = former_cycle.indexOf(cycle_vid); - if (cycle_pos != -1) { - break; - } - cycle_vid = dir_d_map.get(cycle_vid); - } - // then deregister indirect delegations to all earlier cycle members: - const ind_ds_of_vid = ind_d_map.get(vid); - for (let pos = 0; pos < cycle_pos; pos++) { - const vid2 = former_cycle[pos]; - if (ind_ds_of_vid.has(vid2)) { - ind_ds_of_vid.delete(vid2); - inv_ind_d_map.get(vid2).delete(vid); - } - } - } - } - } else { - // we're not on a cycle, so - // deregister INDIRECT delegation of voters who indirectly delegated to client_vid - // to all old indirect delegates of vid: - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - const ind_ds_of_vid = ind_d_map.get(vid); - for (const vid2 of old_ind_ds_of_client) { - ind_ds_of_vid.delete(vid2); - inv_ind_d_map.get(vid2).delete(vid); - } - } - } - } - - // deregister EFFECTIVE delegation and inverse of vid and reset proxy rating to own rating: - const new_proxy_rating = this.own_ratings_map.get(oid).get(client_vid) || 0; - eff_d_map.delete(client_vid); - inv_eff_ds_of_old_eff_d_of_client.delete(client_vid); - this.update_proxy_rating(client_vid, oid, new_proxy_rating); - - // rewire EFFECTIVE delegation and inverse of voters who indirectly delegated to vid, - // and update proxy ratings: - if (inv_ind_ds_of_client) { - for (const vid of inv_ind_ds_of_client) { - inv_eff_ds_of_old_eff_d_of_client.delete(vid); - eff_d_map.set(vid, client_vid); - inv_eff_ds_of_client.add(vid); - this.update_proxy_rating(vid, oid, new_proxy_rating); - } - } - this.G.L.debug("del_delegation exit", this.pid, oid, client_vid, old_d_vid, is_on_cycle); - } - } - - get_n_indirect_option_clients(vid: string, oid: string): number { - /** count how many voters have indirectly delegated to vid for oid */ - return (this.inv_indirect_delegation_map.get(oid).get(vid)||new Set()).size; - } - get_n_indirect_clients(vid: string): number { /** count how many voters have indirectly delegated to vid for some oid */ const map = this.G.D.get_inverse_indirect_map(this._pid); @@ -1369,16 +958,13 @@ export class Poll { rs_map.set(vid, value); this.G.L.trace("Poll.update_own_rating new ratings map", this.pid, oid, [...rs_map.entries()]); // check whether vid has not delegated: - if (!this.direct_delegation_map.get(oid)) { - this.direct_delegation_map.set(oid, new Map()); - } - if (!this.direct_delegation_map.get(oid).has(vid)) { - this.G.L.trace("Poll.update_own_rating voter has not delegated", this.pid, vid, oid); - + const dir_d_map = this.G.D.get_direct_delegation_map(this.pid); + if (!dir_d_map.has(vid)) { // vid has not delegated this rating, // so update all dependent voters' effective ratings: this.update_proxy_rating(vid, oid, value, update_tally); - const vid2s = (this.inv_effective_delegation_map.get(oid)||new Map()).get(vid); + // const vid2s = (this.G.D.get_inv_effective_delegation_map(this.pid).get(oid)||new Map()).get(vid); + const vid2s = this.G.D.get_inverse_indirect_map(this.pid).get(vid); if (vid2s) { for (const vid2 of vid2s) { // vid2 effectively delegates their rating of oid to vid, diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index edb3ced7b..f981af024 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -151,10 +151,6 @@ - -

diff --git a/src/app/poll/poll.page.ts b/src/app/poll/poll.page.ts index 22cd0a8a7..1a01a89dd 100644 --- a/src/app/poll/poll.page.ts +++ b/src/app/poll/poll.page.ts @@ -195,7 +195,6 @@ export class PollPage implements OnInit { this.update_order(); this.have_been_delegated = this.p.have_been_delegated(this.p.myvid, this.accepted_requests); this.update_delegation_info(); - this.update_data(); this.news = this.G.N.filter({pid: this.pid}); this.changeDetector.detectChanges(); this.G.L.exit("PollPage.onDataChange"); @@ -235,13 +234,6 @@ export class PollPage implements OnInit { this.G.L.exit("PollPage.ionViewDidLeave"); } - update_data(){ - const m = this.G.D.inv_direct_delegation_map_caches[this.pid] - for (const oid of this.oidsorted) { - const oidm = m.get(oid); - } - } - update_delegation_info() { this.G.L.entry("PollPage.update_delegation_info"); // determine own weight: From 8d54e8afec15f7cc599db2aedb1b1cea2e27733e Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sat, 28 Dec 2024 19:51:01 +0000 Subject: [PATCH 15/16] removed logs --- src/app/data.service.ts | 1 - .../delegation-dialog.page.ts | 11 - src/app/delegation.service.ts | 211 ++++++++++++------ src/app/delrespond/delrespond.page.html | 2 +- src/app/delrespond/delrespond.page.ts | 7 + src/app/poll.service.ts | 28 ++- src/app/poll/poll.page.ts | 25 ++- 7 files changed, 189 insertions(+), 96 deletions(-) diff --git a/src/app/data.service.ts b/src/app/data.service.ts index ac36d3808..648483e29 100644 --- a/src/app/data.service.ts +++ b/src/app/data.service.ts @@ -1598,7 +1598,6 @@ export class DataService implements OnDestroy { const cache = this.poll_caches[pid]['poll.' + pid + '.inverse_indirect_map'] || '[]'; const ps = cache ? JSON.parse(cache) : {}; const mp = new Map(ps); - console.log("inverse_indirect_map: ", mp); return mp; } diff --git a/src/app/delegation-dialog/delegation-dialog.page.ts b/src/app/delegation-dialog/delegation-dialog.page.ts index 33ab1d945..4c463d5c1 100644 --- a/src/app/delegation-dialog/delegation-dialog.page.ts +++ b/src/app/delegation-dialog/delegation-dialog.page.ts @@ -82,17 +82,6 @@ export class DelegationDialogPage implements OnInit { // checks if ranked delegation is allowed and if so, initialises the values needed for the drop-down menu if (this.G.D.get_ranked_delegation_allowed(this.parent.pid)) { - const ddm = this.G.D.get_direct_delegation_map(this.parent.pid); - console.log("dir_delegation_map", ddm); - console.log("THIS USER a.dir_", this.G.P.polls[this.parent.pid].myvid); - for (const [uid, dels] of ddm) { - console.log("user.dir_", uid); - for (const del of dels) { - console.log("a.dir_", del); - const a = this.G.Del.get_agreement(this.parent.pid, del[0]); - console.log("a.dir_", a); - } - } this.initialise_rank_values(); } diff --git a/src/app/delegation.service.ts b/src/app/delegation.service.ts index d5df75848..a2f8f8ce5 100644 --- a/src/app/delegation.service.ts +++ b/src/app/delegation.service.ts @@ -131,8 +131,6 @@ export class DelegationService { /** Called when voter toggles an option's delegation switch. * (De)activate an option's delegation */ const p = this.G.P.polls[pid]; - const dir_del_map = this.G.D.get_direct_delegation_map(pid); - const list = dir_del_map.get(p.myvid) || []; if (!did) { this.G.L.error("DelegationService.update_my_delegation without existing did", pid, oid, activate); @@ -184,6 +182,34 @@ export class DelegationService { } } + set_delegation_pending(pid:string, did:string){ + const a = this.get_agreement(pid, did); + var dm = this.G.D.get_direct_delegation_map(pid); + var list = dm.get(a.client_vid) || []; + var new_list = []; + for (var entry of list) { + if (entry[0] === did) { + entry[2] = '0'; + } + new_list.push(entry); + } + dm.set(a.client_vid, new_list); + this.G.D.set_direct_delegation_map(pid, dm); + + // update inverse map + const sm = this.G.D.get_inverse_indirect_map(pid); + const eff_set = new Set(JSON.parse(sm.get(a.delegate_vid) || "[]")); + const client_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); + var new_eff_set = new Set(); + for (let id of eff_set) { + if (id == a.client_vid || client_set.has(id)) { + continue; + } + new_eff_set.add(id); + } + sm.set(a.delegate_vid, JSON.stringify(Array.from(new_eff_set))); + } + revoke_delegation(pid: string, did: string, oid: string) { this.G.L.entry("DelegationService.revoke_delegation", pid, did); // update effectve delegation for delegate_vid @@ -209,59 +235,101 @@ export class DelegationService { const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); // gets all voters that are affected by the delegation being deleted - var stack = []; - for (let id of sm.keys()) { - if (JSON.parse(sm.get(id) || "[]").includes(a.client_vid)) { - stack.push(id); + if (!this.G.D.get_ranked_delegation_allowed(pid)){ + var stack = []; + for (let id of sm.keys()) { + if (JSON.parse(sm.get(id) || "[]").includes(a.client_vid)) { + stack.push(id); + } } - } - while (stack.length > 0) { - const curr_del = stack.pop(); - console.log("shared_revoked_curr_del: ", curr_del); - const old_delegate_eff_set = new Set(JSON.parse(sm.get(curr_del) || "[]")); - var new_delegate_eff_set = new Set(); - for (let id of old_delegate_eff_set) { - if (id == a.client_vid || eff_set.has(id)) { - continue; + while (stack.length > 0) { + const curr_del = stack.pop(); + const old_delegate_eff_set = new Set(JSON.parse(sm.get(curr_del) || "[]")); + var new_delegate_eff_set = new Set(); + for (let id of old_delegate_eff_set) { + if (id == a.client_vid || eff_set.has(id)) { + continue; + } + new_delegate_eff_set.add(id); } - new_delegate_eff_set.add(id); + sm.set(curr_del, JSON.stringify(Array.from(new_delegate_eff_set))); } - sm.set(curr_del, JSON.stringify(Array.from(new_delegate_eff_set))); - } - this.G.D.set_inverse_indirect_map(pid, sm); + this.G.D.set_inverse_indirect_map(pid, sm); + } // update direct delegation map var dir_del_map = this.G.D.get_direct_delegation_map(pid); - var dir_del = dir_del_map.get(this.G.P.polls[pid].myvid) || []; - console.log("revoked_dir_del: ", dir_del); + var dir_del = dir_del_map.get(a.client_vid) || []; var new_list = []; for (var entry of dir_del) { - console.log("revoked_dir_del_entry: ", entry); if (entry[2] === '2') { continue; } new_list.push(entry); } - console.log("revoked_dir_del_new_list: ", new_list); - dir_del_map.set(this.G.P.polls[pid].myvid, new_list); - console.log("revoked_dir_del_map: ", dir_del_map); + dir_del_map.set(a.client_vid, new_list); this.G.D.set_direct_delegation_map(pid, dir_del_map); if (this.G.D.get_ranked_delegation_allowed(pid)){ - this.find_path(pid); - - const dm = this.G.D.get_direct_delegation_map(pid); - const list = dm.get(this.G.P.polls[pid].myvid) || []; - var d; - for (const [did2, rank, active] of list) { - if (active === '2') { - d = did2; + this.recalculate_delegation_map(pid); + } + + this.G.L.exit("DelegationService.revoke_delegation"); + } + + recalculate_delegation_map(pid: string) { + const active_delegations = new Map(); + this.find_path(pid); + const dm = this.G.D.get_direct_delegation_map(pid); + for (const [vid, dels] of dm) { + active_delegations.set(vid, ""); + } + for (const [vid, dels] of dm) { + for (const del of dels) { + if (del[2] === '2') { + const a = this.get_agreement(pid, del[0]); + active_delegations.set(a.delegate_vid, a.client_vid); + break; } } } - this.G.L.exit("DelegationService.revoke_delegation"); + let visited = new Set(); + let inverse_map = new Map>(); + for (const vid of active_delegations.keys()) { + if (visited.has(vid)) { + continue; + } + this.bfs(pid, vid, active_delegations, visited, inverse_map); + } + // stringifying the inverse map + let new_sm = new Map(); + for (const [vid, set] of inverse_map) { + new_sm.set(vid, JSON.stringify(Array.from(set))); + } + this.G.D.set_inverse_indirect_map(pid, new_sm); + } + + // recursive function to calculate the inverse indirect map + bfs(pid: string, vid: string, active_delegations: Map, visited: Set, inverse_map: Map>) { + if (visited.has(vid)) { + return; + } + visited.add(vid); + const client_vid = active_delegations.get(vid); + if (!client_vid) { + return; + } + if (!visited.has(client_vid)) { + this.bfs(pid, client_vid, active_delegations, visited, inverse_map); + } + if (!inverse_map.has(client_vid)) { + inverse_map.set(client_vid, new Set()); + } + let s = new Set(inverse_map.get(client_vid) || []); + s.add(client_vid); + inverse_map.set(vid, s); } // RESPONDING TO A DELEGATION REQUEST: @@ -372,31 +440,45 @@ export class DelegationService { signed_response = this.sign_response(response, private_key); this.G.L.info("DelegationService.accept", pid, did, response); this.set_my_signed_response(pid, did, signed_response); - // update effective delegation + const a = this.get_agreement(pid, did); - const sm = this.G.D.get_inverse_indirect_map(pid); - const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); - var new_sm = sm; - for (let id of this.G.P.polls[pid].T.all_vids_set) { - if (id == a.client_vid) { - continue; + // update effective delegation + if (!this.G.D.get_ranked_delegation_allowed(pid)){ + const sm = this.G.D.get_inverse_indirect_map(pid); + const eff_set = new Set(JSON.parse(sm.get(a.client_vid) || "[]")); + var new_sm = sm; + for (let id of this.G.P.polls[pid].T.all_vids_set) { + if (id === a.client_vid) { + continue; + } + const old_eff_set = new Set(JSON.parse(sm.get(id) || "[]")); + if (id === a.delegate_vid || old_eff_set.has(a.delegate_vid)) { + var new_eff_set = old_eff_set; + for (const id2 of eff_set){ + new_eff_set.add(id2); + } + new_eff_set.add(a.client_vid); + new_sm.set(id, JSON.stringify(Array.from(new_eff_set))); + } } - const old_eff_set = new Set(JSON.parse(sm.get(id) || "[]")); - if (id == a.delegate_vid || old_eff_set.has(a.delegate_vid)) { - var new_eff_set = old_eff_set; - for (const id2 of eff_set){ - new_eff_set.add(id2); + this.G.D.set_inverse_indirect_map(pid, new_sm); + + // update direct delegation map + var dir_del_map = this.G.D.get_direct_delegation_map(pid); + var dir_del = dir_del_map.get(a.client_vid) || []; + for (var entry of dir_del) { + if (entry[0] === did) { + entry[2] = '2'; + break; } - new_eff_set.add(a.client_vid); - new_sm.set(id, JSON.stringify(Array.from(new_eff_set))); } + dir_del_map.set(a.client_vid, dir_del); + this.G.D.set_direct_delegation_map(pid, dir_del_map); + return; } - this.G.D.set_inverse_indirect_map(pid, new_sm); - // update direct delegation map var dir_del_map = this.G.D.get_direct_delegation_map(pid); var dir_del = dir_del_map.get(a.client_vid) || []; - console.log("accepted_dir_del: ", dir_del_map); for (var entry of dir_del) { if (entry[0] === did) { entry[2] = '1'; @@ -404,12 +486,9 @@ export class DelegationService { } } dir_del_map.set(a.client_vid, dir_del); - console.log("accepted_dir_del_after: ", dir_del_map); this.G.D.set_direct_delegation_map(pid, dir_del_map); - if (this.G.D.get_ranked_delegation_allowed(pid)){ - this.find_path(pid); - } + this.recalculate_delegation_map(pid); } decline(pid: string, did: string, private_key?: string) { @@ -421,6 +500,11 @@ export class DelegationService { signed_response = this.sign_response(response, private_key); this.G.L.info("DelegationService.decline", pid, did, response); this.set_my_signed_response(pid, did, signed_response); + + // update map + if (this.G.D.get_ranked_delegation_allowed(pid)){ + this.recalculate_delegation_map(pid); + } } // todo: make this give a different news item to the client @@ -453,7 +537,6 @@ export class DelegationService { } private is_casting_voter(pid: string, vid: string) { - console.log("is_casting_voter: dir_", vid); const dir_del_map = this.G.D.get_direct_delegation_map(pid); for (const [did, rank, active] of dir_del_map.get(vid) || []) { if (active == '0') { @@ -466,7 +549,6 @@ export class DelegationService { private find_all_paths(pid: string, vid: string, current_path: string[], paths: string[][]) { const dm = this.G.D.get_direct_delegation_map(pid); - console.log("current_path: dir_", current_path); for (const [did, _, active] of dm.get(vid) || []) { if (active == '0') { continue; @@ -475,7 +557,6 @@ export class DelegationService { let new_path: string[] = [...current_path, did]; if (this.is_casting_voter(pid, a.delegate_vid)) { paths.push(new_path); - console.log("paths_added: dir_", paths, new_path); } else if (!current_path.includes(did)) { this.find_all_paths(pid, a.delegate_vid, new_path, paths); } @@ -487,7 +568,6 @@ export class DelegationService { const a = this.get_agreement(pid, did); for (const [did2, rank, active] of dm.get(a.client_vid) || []) { if (did2 == did) { - console.log("path_dir_rank", rank, did); return Number(rank); } } @@ -506,12 +586,10 @@ export class DelegationService { if (path.length == 0) { continue; } - console.log("Checking path: dir_", path); let pathSum = 0; for (const did of path) { pathSum += this.get_rank_from_did(pid, did); } - console.log("pathSum: dir_", pathSum); if (pathSum < minSum) { minSum = pathSum; minSumPath = path; @@ -524,12 +602,10 @@ export class DelegationService { const dm = this.G.D.get_direct_delegation_map(pid); for (let vid of this.delegating_voters(pid)) { const minSumPath = this.min_sum(pid, vid); - console.log("minSumPath: dir_", minSumPath, vid); for (const did of minSumPath) { const a = this.get_agreement(pid, did); const delegations = dm.get(a.client_vid) || []; var newDelegations = []; - console.log("min_sum_all_path_deleg: dir_", delegations, a.client_vid, this.G.P.polls[pid].myvid, a); for (const [did2, rank, active] of delegations) { if (did2 == did) { newDelegations.push([did2, rank, '2']); @@ -542,9 +618,7 @@ export class DelegationService { dm.set(a.client_vid, newDelegations); } } - console.log("min_sum_all_end_path: dir_", dm); this.G.D.set_direct_delegation_map(pid, dm); - console.log("min_sum_all_end_path2: dir_", this.G.D.get_direct_delegation_map(pid)); } // DATA HANDLING: @@ -567,8 +641,6 @@ export class DelegationService { get_delegate_rank(pid: string, did: string): number { const rank = Number(this.G.D.getv(pid, "del_rank." + did)); - console.log("dir_del", rank); - console.log("dir_del_wo", this.G.D.getv(pid, "del_rank." + did)); return rank; } @@ -579,8 +651,6 @@ export class DelegationService { var dir_del = direct_del_map.get(myvid) || []; dir_del.push([did, JSON.stringify(value), '0']); direct_del_map.set(myvid, dir_del); - console.log("dir_del_set", direct_del_map); - // direct_del_map.set(myvid, [[did, JSON.stringify(value), '0']]); this.G.D.set_direct_delegation_map(pid, direct_del_map); } @@ -619,7 +689,7 @@ export class DelegationService { get_agreement(pid: string, did: string): del_agreement_t { const cache = this.get_delegation_agreements_cache(pid); let a = cache.get(did); - this.G.L.entry("DelegationService.get_agreement", pid, did, a); + // this.G.L.entry("DelegationService.get_agreement", pid, did, a); if (!a) { a = { status: "pending", @@ -681,7 +751,6 @@ export class DelegationService { } update_agreement(pid: string, did: string, agreement: del_agreement_t, request: del_request_t, signed_response: del_signed_response_t) { - console.log("UPDATE AGREEMENT a.dir_", did, agreement, request, signed_response); /** after changes to request or response, * compare request and response, set status, extract accepted and active oids */ this.G.L.entry("DelegationService.update_agreement", pid, did, agreement, request, signed_response); diff --git a/src/app/delrespond/delrespond.page.html b/src/app/delrespond/delrespond.page.html index d96518d30..860d0800c 100644 --- a/src/app/delrespond/delrespond.page.html +++ b/src/app/delrespond/delrespond.page.html @@ -238,7 +238,7 @@

+ (click)="revoke()">      diff --git a/src/app/delrespond/delrespond.page.ts b/src/app/delrespond/delrespond.page.ts index a97632213..aa348d7e1 100644 --- a/src/app/delrespond/delrespond.page.ts +++ b/src/app/delrespond/delrespond.page.ts @@ -125,6 +125,13 @@ export class DelrespondPage implements OnInit { this.router.navigate(["/poll/" + this.pid]); } + revoke() { + /** store negative response and go to poll page */ + this.G.Del.set_delegation_pending(this.pid, this.did); + this.G.Del.decline(this.pid, this.did, this.private_key); + this.router.navigate(["/poll/" + this.pid]); + } + dismiss() { this.router.navigate(["/mypolls"]); } diff --git a/src/app/poll.service.ts b/src/app/poll.service.ts index 4321bafda..ccf458c6d 100644 --- a/src/app/poll.service.ts +++ b/src/app/poll.service.ts @@ -564,7 +564,7 @@ export class Poll { get am_abstaining(): boolean { /** whether or not I'm currently abstaining and haven't delegated */ if (this.have_delegated) { - return false; + return this.T.votes_map.get(this.myvid) === undefined; } if (!!this.T.votes_map) { const myvote = this.T.votes_map.get(this.myvid); @@ -959,17 +959,35 @@ export class Poll { this.G.L.trace("Poll.update_own_rating new ratings map", this.pid, oid, [...rs_map.entries()]); // check whether vid has not delegated: const dir_d_map = this.G.D.get_direct_delegation_map(this.pid); - if (!dir_d_map.has(vid)) { + var have_delegated = false; + if (dir_d_map.has(vid)) { + for (const entry of dir_d_map.get(vid)) { + if (entry[2] == "2") { + have_delegated = true; + break; + } + } + } + + this.update_proxy_rating(vid, oid, value, update_tally); + if (!have_delegated) { // vid has not delegated this rating, // so update all dependent voters' effective ratings: - this.update_proxy_rating(vid, oid, value, update_tally); // const vid2s = (this.G.D.get_inv_effective_delegation_map(this.pid).get(oid)||new Map()).get(vid); - const vid2s = this.G.D.get_inverse_indirect_map(this.pid).get(vid); + const vid2s = new Set(JSON.parse(this.G.D.get_inverse_indirect_map(this.pid).get(vid) || "[]")); if (vid2s) { for (const vid2 of vid2s) { // vid2 effectively delegates their rating of oid to vid, // hence we store vid's new rating of oid as vid2's effective rating of oid: - this.update_proxy_rating(vid2, oid, value, update_tally); + const list = this.G.D.get_direct_delegation_map(this.pid).get(vid2) || []; + for (const entry of list) { + const a = this.G.Del.get_agreement(this.pid, entry[0]); + if (!(a.delegate_vid == vid) || !a.active_oids.has(oid) || entry[2] != "2") { + continue; + } + this.update_proxy_rating(vid2, oid, value, update_tally); + break; + } } } } diff --git a/src/app/poll/poll.page.ts b/src/app/poll/poll.page.ts index 1a01a89dd..ae74de6bf 100644 --- a/src/app/poll/poll.page.ts +++ b/src/app/poll/poll.page.ts @@ -267,12 +267,14 @@ export class PollPage implements OnInit { } // find outgoing delegation: var did; + var pendingSet = new Set(); const dir_del_map = this.G.D.get_direct_delegation_map(this.pid); const list = dir_del_map.get(this.p.myvid) || []; for (const [did_, rank, status] of list) { if (status == '2') { did = did_; - break; + } else if (status == '0') { + pendingSet.add(did_); } } if (did) { @@ -289,6 +291,11 @@ export class PollPage implements OnInit { } } this.delegation_status = st == "agreed" ? "agreed" : agreement.status; + } else if (pendingSet.size > 0) { + this.delegation_status = "pending"; + this.delegate = this.G.Del.get_delegate_nickname(this.pid, pendingSet.values().next().value); + } else { + this.delegation_status = "none"; } this.update_delegation_toggles(); } @@ -338,12 +345,11 @@ export class PollPage implements OnInit { } on_rate_yourself_toggle_change(oid:string) { -// const new_rating = this.p.own_ratings_map.get(oid).get(this.p.myvid); + //const new_rating = this.p.own_ratings_map.get(oid).get(this.p.myvid); // update delegation data: var did = null; const dm = this.G.D.get_direct_delegation_map(this.pid); const list = dm.get(this.p.myvid) || []; - console.log("list", list); for (const [did_, _, status] of list) { if (status == '2') { did = did_; @@ -351,10 +357,18 @@ export class PollPage implements OnInit { } } + // set rating + if (this.rate_yourself_toggle[oid]) { + this.p.set_my_own_rating(oid, this.p.get_my_effective_rating(oid), true); + } else { + this.p.set_my_own_rating(oid, +this.G.D.getv(this.pid, "rating."+oid, this.p.delegate_id)||0, true); + } + this.G.Del.update_my_delegation(this.pid, oid, !this.rate_yourself_toggle[oid], did); // update slider value: // this.get_slider(oid).value = new_rating.toString(); this.on_delegate_toggle_change(); + this.G.D.save_state(); } on_delegate_toggle_change() { @@ -514,7 +528,7 @@ export class PollPage implements OnInit { } } this.delegate = d ? this.G.Del.get_delegate_nickname(this.pid, d) : null; - this.p.delegate_id = d; + this.p.delegate_id = d? d : null; } // CONTROLS: @@ -874,13 +888,10 @@ export class PollPage implements OnInit { break; } } - console.log("revoke_found_did", did); this.G.Del.revoke_delegation(this.pid, did, "*"); - console.log('Delegation revoked.'); // this.delegate = null; this.set_delegate(); this.delegation_status = this.delegate ? 'agreed' : 'none'; - console.log("id: ", this.p.myvid); this.update_delegation_info(); this.G.D.save_state(); } From d892ba4b175ba9da83566da2062b3acee8b07fa3 Mon Sep 17 00:00:00 2001 From: HEMPED~ Date: Sat, 28 Dec 2024 20:06:54 +0000 Subject: [PATCH 16/16] display error --- src/app/poll/poll.page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/poll/poll.page.html b/src/app/poll/poll.page.html index f981af024..3374c90fa 100644 --- a/src/app/poll/poll.page.html +++ b/src/app/poll/poll.page.html @@ -335,7 +335,7 @@

- +