Skip to content

Commit

Permalink
Proposal based votable state (#725)
Browse files Browse the repository at this point in the history
# Motivation

There are multiple reports from users who can’t vote. We believe the
root cause of this issue is the 100-ballot limit for neurons. If a user
has voted on more than 100 proposals (a likely scenario over a 4-day
period), they don’t receive ballots for proposals they’ve already voted
on, which remain technically votable due to this limit. The suggested
solution is to switch from using neuron ballots to proposal ballots when
calculating the votable state.

# Changes

- Use proposal ballots to get voting state.

# Tests

- Updated.

# Todos

- [ ] Add entry to changelog (if necessary).
Not necessary.

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
mstrasinskis and github-actions[bot] authored Oct 2, 2024
1 parent b273a69 commit 95b5fac
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 159 deletions.
12 changes: 6 additions & 6 deletions packages/nns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Parameters:
- `params.neurons`: The neurons to filter.
- `params.proposal`: The proposal to match against the selected neurons.

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L39)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L29)

#### :gear: votableNeurons

Expand All @@ -111,22 +111,22 @@ Parameters:
- `params.neurons`: The neurons to filter.
- `params.proposal`: The proposal to match against the selected neurons.

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L68)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L58)

#### :gear: votedNeurons

Filter the neurons that have voted for a proposal.

| Function | Type |
| -------------- | ------------------------------------------------------------------------------------------------------------------ |
| `votedNeurons` | `({ neurons, proposal: { id: proposalId }, }: { neurons: NeuronInfo[]; proposal: ProposalInfo; }) => NeuronInfo[]` |
| Function | Type |
| -------------- | ---------------------------------------------------------------------------------------------- |
| `votedNeurons` | `({ neurons, proposal, }: { neurons: NeuronInfo[]; proposal: ProposalInfo; }) => NeuronInfo[]` |

Parameters:

- `params.neurons`: The neurons to filter.
- `params.proposal`: The proposal for which some neurons might have already voted.

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L94)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/nns/src/utils/neurons.utils.ts#L81)

### :factory: GenesisTokenCanister

Expand Down
245 changes: 121 additions & 124 deletions packages/nns/src/utils/neurons.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,169 +40,166 @@ describe("neurons-utils", () => {
} as unknown as NeuronInfo,
];

const eligibleNeuronsDate: NeuronInfo[] = [
const eligibleNeuronsData: NeuronInfo[] = [
{
createdTimestampSeconds: proposalTimestampSeconds - BigInt(1),
neuronId: proposalNeuronId,
recentBallots: [],
votingPower: BigInt(1),
} as unknown as NeuronInfo,
{
createdTimestampSeconds: proposalTimestampSeconds - BigInt(2),
neuronId: proposalNeuronId + 1n,
recentBallots: [],
votingPower: BigInt(1),
} as unknown as NeuronInfo,
{
createdTimestampSeconds: proposalTimestampSeconds - BigInt(3),
neuronId: proposalNeuronId + 2n,
recentBallots: [],
votingPower: BigInt(1),
} as unknown as NeuronInfo,
];

it("should has an ineligible neuron because created after proposal", () => {
const ineligible = ineligibleNeurons({
proposal,
neurons: ineligibleNeuronsDate,
describe("ineligibleNeurons", () => {
it("should has an ineligible neuron because created after proposal", () => {
const ineligible = ineligibleNeurons({
proposal,
neurons: ineligibleNeuronsDate,
});
expect(ineligible.length).toEqual(1);
});
expect(ineligible.length).toEqual(1);
});

it("should has an ineligible neuron because dissolve too short", () => {
const ineligible = ineligibleNeurons({
proposal,
neurons: ineligibleNeuronsTooShort,
it("should has an ineligible neuron because dissolve too short", () => {
const ineligible = ineligibleNeurons({
proposal,
neurons: ineligibleNeuronsTooShort,
});
expect(ineligible.length).toEqual(1);
});
expect(ineligible.length).toEqual(1);
});

it("should has not ineligible neuron because empty", () => {
const ineligible = ineligibleNeurons({ proposal, neurons: [] });
expect(ineligible.length).toEqual(0);
it("should has not ineligible neuron because empty", () => {
const ineligible = ineligibleNeurons({ proposal, neurons: [] });
expect(ineligible.length).toEqual(0);
});
});

it("should not have votable neurons because ineligible", () => {
let votable = votableNeurons({
proposal,
neurons: ineligibleNeuronsDate,
describe("votableNeurons", () => {
it("should not have votable neurons because ineligible", () => {
let votable = votableNeurons({
proposal,
neurons: ineligibleNeuronsDate,
});
expect(votable.length).toEqual(0);

votable = votableNeurons({
proposal,
neurons: ineligibleNeuronsTooShort,
});
expect(votable.length).toEqual(0);
});
expect(votable.length).toEqual(0);

votable = votableNeurons({
proposal,
neurons: ineligibleNeuronsTooShort,
it("should not have votable neurons because already voted", () => {
const votable = votableNeurons({
proposal,
neurons: [
{
...eligibleNeuronsData[0],
recentBallots: [
{
proposalId,
vote: Vote.No,
},
],
},
],
});
expect(votable.length).toEqual(0);
});
expect(votable.length).toEqual(0);
});

it("should not have votable neurons because already voted", () => {
const votable = votableNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [
it("should have votable neurons because not yet voted", () => {
const votable = votableNeurons({
proposal: {
...proposal,
ballots: [
{
proposalId,
vote: Vote.No,
neuronId: eligibleNeuronsData[0].neuronId,
vote: Vote.Unspecified,
votingPower: BigInt(1),
},
{
neuronId: eligibleNeuronsData[1].neuronId,
vote: Vote.Yes,
votingPower: BigInt(1),
},
],
},
],
});
expect(votable.length).toEqual(0);
});

it("should have votable neurons because not yet voted", () => {
const votable = votableNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [
{
proposalId: BigInt(4),
neuronId: eligibleNeuronsData[2].neuronId,
vote: Vote.No,
votingPower: BigInt(1),
},
],
},
],
});
expect(votable.length).toEqual(1);
});

it("should have votable neurons because never voted", () => {
const votable = votableNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [],
},
],
neurons: eligibleNeuronsData,
});
expect(votable.length).toEqual(1);
expect(votable).toEqual([eligibleNeuronsData[0]]);
});
expect(votable.length).toEqual(1);
});

it("should have votable neurons regardless of voting power", () => {
const votable = votableNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [],
votingPower: BigInt(0),
},
{
...eligibleNeuronsDate[0],
recentBallots: [],
votingPower: BigInt(1),
},
{
...eligibleNeuronsDate[0],
recentBallots: [],
votingPower: BigInt(0),
},
],
});
expect(votable.length).toEqual(3);
});

it("should not have voted neurons because votable", () => {
const voted = votedNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [
it("should have votable neurons regardless of voting power", () => {
const votable = votableNeurons({
proposal: {
...proposal,
ballots: [
{
proposalId: BigInt(4),
vote: Vote.No,
neuronId: eligibleNeuronsData[0].neuronId,
vote: Vote.Unspecified,
votingPower: BigInt(0),
},
{
neuronId: eligibleNeuronsData[1].neuronId,
vote: Vote.Yes,
votingPower: BigInt(1),
},
{
neuronId: eligibleNeuronsData[2].neuronId,
vote: Vote.Unspecified,
votingPower: BigInt(2),
},
],
},
],
neurons: eligibleNeuronsData,
});
expect(votable.length).toEqual(2);
expect(votable).toEqual([eligibleNeuronsData[0], eligibleNeuronsData[2]]);
});
expect(voted.length).toEqual(0);
});

it("should not have voted neurons because never voted", () => {
const voted = votedNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [],
},
],
});
expect(voted.length).toEqual(0);
});

it("should have voted neurons because has voted", () => {
const voted = votedNeurons({
proposal,
neurons: [
{
...eligibleNeuronsDate[0],
recentBallots: [
describe("votedNeurons", () => {
it("should have only voted neurons", () => {
const voted = votedNeurons({
proposal: {
...proposal,
ballots: [
{
neuronId: eligibleNeuronsData[0].neuronId,
vote: Vote.Unspecified,
votingPower: BigInt(1),
},
{
neuronId: eligibleNeuronsData[1].neuronId,
vote: Vote.Yes,
votingPower: BigInt(1),
},
{
proposalId,
neuronId: eligibleNeuronsData[2].neuronId,
vote: Vote.No,
votingPower: BigInt(1),
},
],
},
],
neurons: eligibleNeuronsData,
});
expect(voted).toEqual([eligibleNeuronsData[1], eligibleNeuronsData[2]]);
});
expect(voted.length).toEqual(1);
});
});
Loading

0 comments on commit 95b5fac

Please sign in to comment.