Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polls RFC 69 #320

Open
wants to merge 61 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
230c8ba
Create 69
toadlyBroodle Feb 27, 2023
1fa1d05
Rename 69 to 69.md
toadlyBroodle Feb 27, 2023
bac216d
Update README.md
toadlyBroodle Feb 27, 2023
71cfaae
Update README.md
toadlyBroodle Feb 27, 2023
455d2f4
Update 69.md
toadlyBroodle Feb 27, 2023
72260b2
Update 69.md
toadlyBroodle Feb 27, 2023
255c4d9
add RFC
toadlyBroodle Feb 27, 2023
8fa390a
definition
toadlyBroodle Feb 28, 2023
9573ad3
add outcomes
toadlyBroodle Feb 28, 2023
ce4c128
enhance definitions
toadlyBroodle Mar 2, 2023
4f337ed
standardize poll format
toadlyBroodle Mar 2, 2023
a55a79e
adjust poll format
toadlyBroodle Mar 2, 2023
4d6da84
zap vote format
toadlyBroodle Mar 2, 2023
25ad6d7
rename closing_time->closed_at
toadlyBroodle Mar 2, 2023
4ad8b36
format clarifications
toadlyBroodle Mar 2, 2023
ae55cbf
codify tally_method
toadlyBroodle Mar 2, 2023
edd1353
section clarifications
toadlyBroodle Mar 2, 2023
4d173a5
add poll tags
toadlyBroodle Mar 3, 2023
e92e50d
reformat tag table
toadlyBroodle Mar 3, 2023
a38ff0b
fix poll option values
toadlyBroodle Mar 3, 2023
aa293a6
add nip links
toadlyBroodle Mar 3, 2023
cdd6281
sync master
toadlyBroodle Mar 3, 2023
510b3d9
spell check
toadlyBroodle Mar 3, 2023
d39fa6d
spell check
toadlyBroodle Mar 3, 2023
19dfd63
typo fix
toadlyBroodle Mar 3, 2023
fe6bd67
add header labels
toadlyBroodle Mar 3, 2023
2bae130
update TODOs
toadlyBroodle Mar 3, 2023
980e132
Apply suggestions from code review
toadlyBroodle Mar 5, 2023
d061eb0
convert poll_options array to json serialized key-value array, change…
toadlyBroodle Mar 5, 2023
5d0ad89
merge with remote master
toadlyBroodle Mar 5, 2023
a2f6b39
add value_minimum attribute, poll_option syntax fix
toadlyBroodle Mar 5, 2023
9ce8a86
elaborate on purpose of value/count tally_methods based on feedback
toadlyBroodle Mar 5, 2023
f292a7e
remove tally_method tag, add value_maximum tag, rewrite affected sect…
toadlyBroodle Mar 5, 2023
e20343c
add poll_option tag to zap event format and README, minor changes to …
toadlyBroodle Mar 5, 2023
6410475
more precisely define consensus_threshold, remove reference to fraudu…
toadlyBroodle Mar 6, 2023
f435bc8
require e/p tags be specified, require primary hosting relay be speci…
toadlyBroodle Mar 7, 2023
b8d39fd
require ots tags accompany closed_at tags
toadlyBroodle Mar 7, 2023
cf17e33
allow for additional p-keys to be included in poll events and tallies
toadlyBroodle Mar 7, 2023
c5027b9
add description of multiple p-keys to introduction section
toadlyBroodle Mar 7, 2023
db4741d
elaborate on purpose of multiple-p keys
toadlyBroodle Mar 7, 2023
c7d8700
update poll_options value to json serialized Map<Int, String>
toadlyBroodle Mar 19, 2023
b61e25c
add Zap Voting section, change poll_options tag from nested json to m…
toadlyBroodle Mar 23, 2023
c2e8269
add clarifying requirements for voting- tallying-clients
toadlyBroodle Mar 25, 2023
313ae7c
clarify that `e` tag is optional
toadlyBroodle Mar 29, 2023
0de2196
refine overview section, alter atomic poll tally rules
toadlyBroodle Apr 2, 2023
cd8ed5e
move poll_option tag from 9735->9734
toadlyBroodle Apr 6, 2023
4d22b2d
rename to Zap Polls
toadlyBroodle Apr 6, 2023
900268c
Merge branch 'master' into master
toadlyBroodle Apr 6, 2023
4782bd4
dont allow poll authors to vote on own polls, fix event table formatting
toadlyBroodle Apr 6, 2023
678f0c0
Merge remote-tracking branch 'nostr-protocol/master'
toadlyBroodle Apr 29, 2023
df236f4
merge recent changes
toadlyBroodle Apr 29, 2023
5429dce
rename poll->zap poll
toadlyBroodle Apr 29, 2023
141b296
fix CRLFs
toadlyBroodle Apr 30, 2023
a0de6c0
fix 01.md CRLFs
toadlyBroodle Apr 30, 2023
cf1d475
revert 57.md example formatting
toadlyBroodle Apr 30, 2023
948c9eb
revert remaining 57.md formatting
toadlyBroodle Apr 30, 2023
ecbf7dd
revert 57.md changes and add poll_option references
toadlyBroodle Apr 30, 2023
e47f23a
remove windows newlines from 69.md
toadlyBroodle May 6, 2023
1be5e63
Merge branch 'master' into master
toadlyBroodle Jul 12, 2023
dff65f4
remove ots requirement
toadlyBroodle Sep 20, 2023
8268215
remove additional ots references
toadlyBroodle Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions 57.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ In addition, the event MAY include the following tags:

- `e` is an optional hex-encoded event id. Clients MUST include this if zapping an event rather than a person.
- `a` is an optional NIP-33 event coordinate that allows tipping parameterized replaceable events such as NIP-23 long-form notes.
- `poll_option` is a tag used for voting by [zap poll events](69.md).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason to modify NIP-57 for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is under optional MAY conditional list, for documentation purposes. It is a required field for zap-poll event zap requests: which is why it's included here. Still want it removed?


Example:

Expand All @@ -49,7 +50,8 @@ Example:
["amount", "21000"],
["lnurl", "lnurl1dp68gurn8ghj7um5v93kketj9ehx2amn9uh8wetvdskkkmn0wahz7mrww4excup0dajx2mrv92x9xp"],
["p", "04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],
["e", "9ae37aa68f48645127299e9453eb5d908a0cbb6058ff340d528ed4d37c8994fb"]
["e", "9ae37aa68f48645127299e9453eb5d908a0cbb6058ff340d528ed4d37c8994fb"],
["poll_option","0"]
],
"pubkey": "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322",
"created_at": 1679673265,
Expand Down Expand Up @@ -132,7 +134,8 @@ The following should be true of the `zap receipt` event:
- The `zap receipt` MUST have a `bolt11` tag containing the description hash bolt11 invoice.
- The `zap receipt` MUST contain a `description` tag which is the JSON-encoded invoice description.
- `SHA256(description)` MUST match the description hash in the bolt11 invoice.
- The `zap receipt` MAY contain a `preimage` tag to match against the payment hash of the bolt11 invoice. This isn't really a payment proof, there is no real way to prove that the invoice is real or has been paid. You are trusting the author of the `zap receipt` for the legitimacy of the payment.
- The zap receipt MAY contain a `preimage` tag to match against the payment hash of the bolt11 invoice. This isn't really a payment proof, there is no real way to prove that the invoice is real or has been paid. You are trusting the author of the zap receipt for the legitimacy of the payment.
- The zap receipt MAY contain a `poll_option` tag used for voting on [zap poll events](69.md).

The `zap receipt` is not a proof of payment, all it proves is that some nostr user fetched an invoice. The existence of the `zap receipt` implies the invoice as paid, but it could be a lie given a rogue implementation.

Expand All @@ -149,8 +152,10 @@ Example `zap receipt`:
"tags": [
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
["e", "3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8"],
["bolt11", "lnbc10u1p3unwfusp5t9r3yymhpfqculx78u027lxspgxcr2n2987mx2j55nnfs95nxnzqpp5jmrh92pfld78spqs78v9euf2385t83uvpwk9ldrlvf6ch7tpascqhp5zvkrmemgth3tufcvflmzjzfvjt023nazlhljz2n9hattj4f8jq8qxqyjw5qcqpjrzjqtc4fc44feggv7065fqe5m4ytjarg3repr5j9el35xhmtfexc42yczarjuqqfzqqqqqqqqlgqqqqqqgq9q9qxpqysgq079nkq507a5tw7xgttmj4u990j7wfggtrasah5gd4ywfr2pjcn29383tphp4t48gquelz9z78p4cq7ml3nrrphw5w6eckhjwmhezhnqpy6gyf0"],
["description", "{\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"]]}"],
["bolt11",
"lnbc10u1p3unwfusp5t9r3yymhpfqculx78u027lxspgxcr2n2987mx2j55nnfs95nxnzqpp5jmrh92pfld78spqs78v9euf2385t83uvpwk9ldrlvf6ch7tpascqhp5zvkrmemgth3tufcvflmzjzfvjt023nazlhljz2n9hattj4f8jq8qxqyjw5qcqpjrzjqtc4fc44feggv7065fqe5m4ytjarg3repr5j9el35xhmtfexc42yczarjuqqfzqqqqqqqqlgqqqqqqgq9q9qxpqysgq079nkq507a5tw7xgttmj4u990j7wfggtrasah5gd4ywfr2pjcn29383tphp4t48gquelz9z78p4cq7ml3nrrphw5w6eckhjwmhezhnqpy6gyf0"],
["description",
"{\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"],[\"poll_option\",\"0\"]]}"],
["preimage", "5d006d2cf1e73c7148e7519a4c68adc81642ce0e25a432b2434c99f97344c15f"]
],
"content": "",
Expand Down
119 changes: 119 additions & 0 deletions 69.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Zap Poll event

`draft` `optional` `author:toadlyBroodle`

A zap poll note is a [nostr event](01.md) (kind `6969`) for conducting paid polls—herein referred to simply as 'polls'. A poll presents two or more voting options, which participants my vote on by sending regular [zap events](57.md) which include an additional `poll_option` vote tag. Polls may include multiple recipients which participants may choose from when zapping their votes. Polls may specify `value_maximum` and `value_minimum` satoshi valuations for determining which zaps are included in the tally. Polls may specify a `consensus_threshold` for assessing the state of consensus. Polls should specify a `closed_at` time, after which results be unblinded, closed to new votes, and the tally considered final.

## Purpose

The purpose of poll notes is to conduct quantitative public opinion polls over nostr by requiring voters pay to participate. By tying results to real satoshi valuations, nostr polls intend to provide superior signal compared to other free polling models. Imposing real monetary costs for participation should also discourage undesired attempts to sway results.

Pollers may specify multiple `p` keys, to allow participants to choose which recipient they zap, regardless of their vote choice. The option for such distribution of funds should also incentivize participation by increasing voter optionality, poller authenticity, and legitimacy of results by mitigating certain unauthentic attack vectors.

By including a `value_maximum` limit, polls can stop single 'whale' votes discounting many smaller 'shrimp' votes. Likewise, by including a `value_minimum` limit, polls can make automated low-value vote flooding attacks prohibitively expensive. However, both limits remain optional to allow for freedom in poll design.

By setting `value_maximum` and `value_minimum` equal, a more traditional style poll may be designed, which weights each vote equal and limits each participant to a single vote.

The optional `consensus_threshold` is intended as a simple 'measuring bar', defined as the minimum percentage for any single `poll_option`'s percentage of the total tally to attain poll consensus status.

A careful balancing of all poll attributes should enable pollers to conduct tailored polls that deliver meaningful and valuable outcomes.

## Zap poll format

A poll event:
* MUST specify 1 or more `p` tags, each including the same primary hosting relay
* if an `e` tag is specified, it:
* MUST include the same primary hosting relay URL as the `p` tags
* MUST contain a primary description string, specified in the `content` field
* MUST contain at least 2 `poll_option` tags, each specifying an index and a unique description string, formatted as below
toadlyBroodle marked this conversation as resolved.
Show resolved Hide resolved
* SHOULD specify a `closed_at` time:
* when specified, MAY include a valid [`ots` tag](03.md), proving original poll publishing time
* a `closed_at` value of null or less than or equal to the `created_at` field indicates a poll SHOULD NOT be closed.
* MAY specify a `value_maximum` satoshi value for zapped votes to be included in the tally
* MAY specify a `value_minimum` satoshi value for zapped votes to be included in the tally
* `value_minimum` MUST be less than or equal to `value_maximum`, when both are specified
* MAY include a `consensus_threshold` (0-100), representing a percentage threshold for any single vote option to achieve poll consensus
toadlyBroodle marked this conversation as resolved.
Show resolved Hide resolved
* a `consensus_threshold` of '0' indicates no threshold is specified.

```json
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <unix timestamp in seconds>,
"kind": 6969,
"tags": [
["e", <32-bytes hex of the id of the poll event>, <primary poll host relay URL>],
["p", <32-bytes hex of the key>, <primary poll host relay URL>],
["poll_option", "0", "poll option 0 description string"],
["poll_option", "1", "poll option 1 description string"],
["poll_option", "n", "poll option <n> description string"],
["value_maximum", "maximum satoshi value for inclusion in tally"],
["value_minimum", "minimum satoshi value for inclusion in tally"],
["consensus_threshold", "required percentage to attain consensus <0..100>"],
["closed_at", "unix timestamp in seconds"],
],
"ots": <base64-encoded OTS file data>
"content": <primary poll description string>,
"sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
}
```

## Zap voting

Poll options are voted on by sending [zap events](57.md) (referencing the original poll event's `e` and `p` values) that indicate their chosen vote option in a `poll_option` tag. To ensure all eligible votes are included in the tally, all `e` and `p` tags must specify the same primary hosting relay.

A voting client:
* SHOULD NOT allow submission of zap events with amounts greater than `value_maximum` (when specified)
* SHOULD NOT allow submission of zap events with amounts less than `value_minimum` (when specified)
* SHOULD NOT allow submission of zap events after `closed_at` time (when specified)
* SHOULD NOT allow poll author to vote on their own polls
* SHOULD hide tally results, until after a user has zapped the note

## Zap vote format

The zap request event (kind `9734`):
* MUST specify an `e` tag that references the original poll event
* MUST include a primary hosting relay URL in the `e` tag
* MUST include at least 1 `p` tag specifying the recipient's key, chosen from the original poll event's list of keys
* MUST include the same primary hosting relay URL in all `p` tags as is specified in the `e` tag
* MUST include exactly 1 `poll_option` tag which references the chosen vote option by its corresponding index `n`

```json
...
"tags": [
["e", <32-bytes hex of the id of the original poll event>, <primary poll host relay URL>],
["p", <32-bytes hex of the recipient's key>, <primary poll host relay URL>],
["poll_option", "n"],
...
],
"ots": <base64-encoded OTS file data>
...
```

## Zap poll outcome

Polls results are tallied by summing the exact satoshi values from all eligible zaps for each `poll_option`. The total tally is the sum of all individual `poll_option` tallies, and `poll_option` results are calculated by their percentage of the total tally value.

To avoid ambiguity of results, strict adherence to the following rules is vital when tallying and rendering poll outcomes.

A tallying client:
* MUST ONLY include full satoshi value amounts in option tallies from ALL eligible zaps that meet ALL following criteria:
* MUST ONLY tally zaps that reference the original poll event by its `e` tag value
* MUST ONLY tally zaps sent to a `p` key specified in the original poll event
* MUST NOT tally zaps from the poll author's `p`
* MUST ONLY include zap amounts less than or equal to `value_maximum`, if specified
* MUST ONLY include zap amounts greater than or equal to `value_minimum`, if specified
* if both `value_maximum` and `value_minimum` are specified AND are equal:
* MUST ONLY count 1 zap per option, per participant
* if a `closed_at` time is specified, clients:
* MUST ONLY tally zaps including a valid `created_at` time greater than or equal to the original poll event's `created_at` time
* MUST ONLY tally zaps including a valid `created_at` time less than or equal to the original polle event's `closed_at` time

Additionally, a tallying client:
* MUST display the distribution percentages, from the tally total, for each vote option tally
* MUST display the `consensus_threshold` (if specified) relative to the winning vote percentage
* SHOULD show tally results to all note zappers, even if they haven't voted on an option
* SHOULD publicly show results after the `closed_at` time has passed (if specified)
* MAY display the counts of zap events received for each option, along with other poll statistics

Strict adherence to these requirements should enable a standardized means of quantitatively assessing the distribution of opinion regarding a poll's content amongst poll participants, determining a winning outcome, and possibly achieving consensus. However, until this protocol is further tested, refined, and proven robust, polls should probably not be considered authoritative nor binding.
Loading