Closed alexgleason closed 2 months ago
@abhay-raizada
Copy pasting from the merged PR:
This will cause issues with expiration, this way we can discard any responses after an expiration time. (NIP-40). Although someone could just send in lower created_at values. But at-least this method keeps open the scope for expiration, where as there's no scope with parameterized replaceable events.
@abhay-raizada This comment does not make sense to me. Why would someone want to expire their previous vote in order to have their new vote take over? I don't think users normally want to do this, and it doesn't seem to me like a good reason to avoid using a core feature of Nostr.
@alexgleason Sorry for the misunderstanding I meant "Expiring Polls". We wouldn't want to tally votes or edits that come in after the expiration of the poll.
Ah, got it. Then I think even NIP-40 is not relevant, since that would delete the poll itself. You would want to add a different tag to the poll event, like ["ends", <unix-timestap>]
. This has its own set of problems, but either way I don't think it's related at all to whether the response event is regular or replaceable.
but either way I don't think it's related at all to whether the response event is regular or replaceable.
In the context of knowing the last valid vote from a pubkey, it is right? Since a replaceable event would just replace the older event? The current approach is a little less manipulate-able.
Agreed about NIP-40, meant an "ends" tag, got confused with NIP-40
@abhay-raizada Either way the created_at is a freeform field. Clients can put anything they want into that, and if other clients query it they will see it.
Other poll proposals simply did not include an end date for this reason. It's something people are already ready to accept.
But it is still possible for relays to dispose of any event where the created_at is not recent (eg ±10s of created_at to current time), and since your poll event already suggests specific relays to use, it is possible for clients to have a fair poll with an end time as long as the relays are also cooperative.
But either way, this does not depend on the response event being a non-replaceable event, and the current spec doesn't even support an "end" tag yet anyway. (For the record, I support the idea of an "end" tag and using well-behaved relays)
@alexgleason From NIP-01:
for kind n such that 30000 <= n < 40000, events are parameterized replaceable, which means that, for each combination of pubkey, kind and the d tag's first value, only the latest event MUST be stored by relays, older versions MAY be discarded.
READ: only the latest event MUST be stored by relays, older versions MAY be discarded.
If the relays only store the latest event, we have lost the information of what was the last vote before the "ends" time of a poll. If a user updates their vote post the ends time It also updates the tally. Which i think is a bad design.
I agree,the "ends" time with "counting last vote manually" is not a perfect solution, but it is still achievable by using compliant relays, using parameterized replaceable events makes this impossible.
@abhay-raizada Who is going to submit a vote before the poll closes, submit a replacement after the poll closes, and then be sad their vote was lost? It is up to clients to respect the "end" tag and prevent the user from doing that.
This is an edge case about a hypothetical situation that already has a solution: the client must respect the "end" tag, if it hypothetically were to ever exist.
There is no downside to using a replaceable event, except that you would need to adapt the code of your app. But it's much worse to avoid using a core feature of Nostr the way it was intended to be used by all client and relay developers forever.
@alexgleason The user themselves might not care, infact they might be doing it maliciously. But it WILL skew the results. Let's say it's a poll for a football match England V Spain, and people try to manipulate the results post the match?
I don't mind updating my code. I just don't want to move to a standard, without gauging the downsides, and the edge case is barely hypothetical, It becomes real as soon as someone adds an expiring polls feature (probably me, probably soon).
I was toying with Parameterized replaceable events as responses on formstr as added on this NIP (https://github.com/nostr-protocol/nips/pull/1190) , I purposefully changed it for this feature
@abhay-raizada People can already do that with regular events by signing an event with a backdated created_at. The only way to solve that is with relay protections on created_at, and client filtering. Changing to parameterized doesn't change this!
I know it is stupid idea, but how about actually using polls for this discussion?
otherwise people will have to do a lot of extra work to add special support for polls.
@alexgleason It's not a lot of extra work, it's just a pass over all votes events.
It's the code here for pollerama: https://github.com/abhay-raizada/nostr-polls/blob/main/src/components/PollResults/index.tsx#L17
The attack with parameterized replaceable events:
Poll: Team A Vs Team B
Suppose the poll were to expire 15 mins before the match concluded.
The client does this by adding an ["ends", "<end_timestamp>]
tag on to the poll event,
suppose a user adds a vote to A as a kind 30068
during the constraints of the poll.
at end_timestamp
the results are read: A 60% , B 40% .
Now 15 mins later A loses the match. A lot of people who originally voted "A" attempt to change their votes to "B", they update their kind:30068
events. The relays will update the parameterized replaceable event.
The client may choose to only query votes up-til end_timestamp
,
but in their tally they will lose the events that were originally present at end_timestamp - 15 minutes.
Losing Votes that would have gone to A in this example. The tally would read A: 50 B: 50%. but that was not truly the case for when the poll was designed to finish.
The current implementation preserves this scenario.
This attack has nothing to do with the authenticity of the timestamp. That one is a completely different attack, that can happen with any event kind.
don't forget to account for NIP-26 delegations 😂
It's the code here for pollerama: https://github.com/abhay-raizada/nostr-polls/blob/main/src/components/PollResults/index.tsx#L17
This proves my point even more. You're asking developers to reimplement a core feature of Nostr.
You're speaking nonsense. I'm not sure you fully understand Nostr.
Since the original MR was unilaterally merged, I am unilaterally merging this one. If anyone thinks I am wrong, feel free to make another MR reverting it and explain why.
https://pollerama.fun would not support this, which would mean we won't have interoperable polls.
I'll leave it to individual client devs, to make their own choices.
I'm sorry if I'm not making sense, I don't know how to explain this further.
Also I don't think the PR was unilaterally merged? It was sitting in an approved state for months?
I can see arguments for both ways of doing things, although I'm leaning more on the side of having the votes be normal events -- just like reactions (or should we make reactions replaceable too, and also normal notes, everything?), but I didn't really follow @abhay-raizada and it looks like he wasn't getting @alexgleason's points.
Anyway, I've removed the NIP until we can all agree with a format and be happy again.
@abhay-raizada NIPs are supposed to have two implementations to be merged. NIP-88 only has one implementation.
Let me recap the points here:
In your own posts you argued against yourself while trying to argue against my points. Like I can see the point in the post where you started to realize maybe you're wrong, but instead of editing your post you made me read all of that.
Any feature of Nostr can be implemented if you just loop through the entire database every single time. That's not a reason to do it. I don't want a subdatabase of my database. I want a fully functioning database.
Wait, but if the poll type is multiple or ranked choice there could/will be more than one answer per author per poll, right?
In those cases, the PRE will just get on the way.
@alexgleason I don't mean to make any arguments at all, I want a good polls implementation, I see a problem with using parameterized replaceable events in cases where there is an expiration for a poll, I'm just pointing out that problem.
Wait, but if the poll type is multiple or ranked choice there could/will be more than one answer per author per poll, right? In those cases, the PRE will just get on the way.
@vitorpamplona No it's still one vote event per pubkey, the vote event just contains multiple response tags.
When there is a d
tag set to an event id that is a sign that something is out of place.
@fiatjaf It's legitimate to react to one post with multiple reactions (like on Slack), and maybe even to react to one post with the same reaction multiple times (like a Poke).
Voting in a poll is different because you care about the results of the poll more than you care about the votes. It should be optimized for figuring out the winning choice, not for highlighting the individual votes.
@vitorpamplona Look closer at the spec. God it would be messy if you created multiple events to vote in a multiple-choice poll. It's one event to tally all choices.
When there is a
d
tag set to an event id that is a sign that something is out of place.
I understand this concern. It is awkward because things like the relay URL, markers, etc would probably go in the "d" tag, so we're breaking a convention either way. But I think it's worse to break the convention of "replaceable kind" on a thing that cares so much about its pubkey and timestamp.
Ohh I see.
So, if we do PRE the user can keep switching while the poll is going, right? Like I could switch my vote every 100ms if I wanted to.
The user could do that by deleting and recreating the regular event as well. But there is a chance they just keep adding 1000s of events without deleting them. Clients would be forced to download everything and look at the latest only.
Is that what we want? If yes, then PREs are better.
If not, then the guidance of just considering the last vote is bad. It should discard the user entirely if it has more than one vote.
Yeah, now I think @alexgleason is right, but I think we should still have an e
tag, I don't know what should go on the d
though, I guess it must necessarily be the poll id again or is there another way?
Is there some magic we can invent now to address this and future similar cases in a more elegant way?
@fiatjaf I think the d
tag has to be the event ID, but I am in favor of the idea to also include a separate e
tag.
The user could do that by deleting and recreating the regular event as well. But there is a chance they just keep adding 1000s of events without deleting them. Clients would be forced to download everything and look at the latest only. Is that what we want? If yes, then PREs are better.
@vitorpamplona I want to power other features, like "only three edits allowed", or as i pointed out "polls where voting expires". I think this is the only reasonable way to power these features.
Ok, but then the author of the poll is now controlling who, how and when people can vote (or vote again).
To me that control should yield a separate spec.
Also, I don't think regular events get what you want. You are going to need a design where the author "accepts" a vote by reposting it, signed by the author's account. In that case, the tally should only consider accepted posts and votes are just requests to vote.
Ok, but then the author of the poll is now controlling who, how and when people can vote (or vote again).
@vitorpamplona Here's how i see it. The poll author expresses their "wish" (poll may expire on timestamp) on a pollEvent, The voter can keep voting beyond the time(maybe through a malicious/favorable client), and regular clients can choose to ignore such events.
If you wanted you could choose to disrespect the poll authors wish.
EVERYBODY WINS.
But there is a chance they just keep adding 1000s of events without deleting them. Clients would be forced to download everything and look at the latest only.
@vitorpamplona :100:. Once there's polls with a few hundred votes it will get harder. It's going to be a challenge either way, but there's no need to cause so many developers so much pain.
I want to power other features, like "only three edits allowed", or as i pointed out "forms where voting expires". I think this is the only reasonable way to power these features.
@abhay-raizada Now I am understanding you. Your vision and goals still do not match mine for polls, but at least this is an argument. I do not think the shared spec for polls should be based on this requirement.
Ok, but then the author of the poll is now controlling who, how and when people can vote (or vote again).
@vitorpamplona Here's how i see it. The poll author expresses their "wish" (poll may expire on timestamp) on a pollEvent, The voter can keep voting beyond the time(maybe through a malicious/favorable client), and regular clients can choose to ignore such events.
But that is not an issue for the PRE. If the user voted again after the end time, the previous legal vote should not be considered anymore. It's a punishment for voting outside the rules.
But that is not an issue for the PRE. If the user voted again after the end time, the previous legal vote should not be considered anymore. It's a punishment for voting outside the rules.
I don't like to punish people for expression. This also leads to loss of signal. You don't have the answer to the question "What people were thinking till 15 minutes the match got over?" anymore. Which was the poll authors wish.
You also get the answer to the question of "what happens ffteen minutes after" if you were looking for it for any reason.
All of these use cases are interesting but they must be solved on the relay side (and honestly that is the only way of solving them).
We could add an optional tag specifying a relay that the author indicates the votes should be sent to (if it is not present they go to the author's default inbox relays), the author arranges it so that relay has a special way of handling votes and disallows overwriting votes, disallow voting after the poll expiration, blocks spam etc.
I just read this entire thread for the first time and I think poll responses must be regular events. If there are multiple votes by the same person, there is a way to deal with that (only accept the last one, for example, or the last one within the time limit). If we went the other way (PRE) then yes some stuff is cleaner but too many hands are tied, the flexibility is lost.
Follow up to https://github.com/nostr-protocol/nips/pull/1346
Change kind 1018 to 30068.
We already have existing code, relays, systems etc that apply the right semantics for replacing an event, so we should use that, otherwise people will have to do a lot of extra work to add special support for polls.