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

Add threshold to cw3 flex #180

Merged
merged 28 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e4ff708
Integrate Threshold into msg/state
ethanfrey Dec 14, 2020
185b6ab
cw3-flex compiles with threshold
ethanfrey Dec 14, 2020
09aff63
Existing tests updated and pass
ethanfrey Dec 14, 2020
b1e05c1
Test cases on message validation
ethanfrey Dec 14, 2020
4e1ec11
All message logic tested
ethanfrey Dec 14, 2020
a41b125
Properly calculate passing for Quorum - different if voting open/closed
ethanfrey Dec 14, 2020
011f27f
Test vote/tally logic, fix rounding issue
ethanfrey Dec 14, 2020
f798ddc
Extensive tests and fixes on threshold quorum
ethanfrey Dec 14, 2020
7dc1c75
Add test coverage to list/reverse proposals
ethanfrey Dec 15, 2020
31607dc
Test threshold, vote detail queries
ethanfrey Dec 15, 2020
65a7ee8
Scenario test with absolute percentage and dynamic group
ethanfrey Dec 15, 2020
01a77e7
Add scenario tests for percentage and quorum threshold variants
ethanfrey Dec 15, 2020
478fc3c
Update typos, rename some fields
ethanfrey Dec 15, 2020
3f3b379
Properly handle missing quorum and hitting threshold edge case
ethanfrey Dec 15, 2020
94af010
Extensive rustdoc on ThresholdResponse in cw3 spec
ethanfrey Dec 15, 2020
df6c9d3
cw3: Update query data types and more rustdoc
ethanfrey Dec 15, 2020
23de1be
Update cw3-fixed to match updated cw3 spec
ethanfrey Dec 15, 2020
09e9163
Update cw3-flex to new cw3 spec
ethanfrey Dec 15, 2020
0719092
Merge pull request #188 from CosmWasm/update-cw3-spec
maurolacy Dec 16, 2020
382cbf1
Format / fix cw3 spec details
maurolacy Dec 16, 2020
8a190f1
Update schemas
maurolacy Dec 16, 2020
8290c57
Update contracts/cw3-flex-multisig/src/state.rs
ethanfrey Dec 16, 2020
0fd5a88
Consider abstained votes when computing AbsoluteThreshold passing weight
maurolacy Dec 16, 2020
5ae15c6
Simplify branches in ThresholdQuora is_passed
ethanfrey Dec 16, 2020
526b0ff
Rename ThresholdQuora to ThresholdQuorum
ethanfrey Dec 16, 2020
e574ff7
Pull out PRECISION_FACTOR as mod level const
ethanfrey Dec 16, 2020
d87e498
Add one test for AbsolutePercentage and abstain, cargo schema
ethanfrey Dec 16, 2020
f33e3aa
Remove duplicate descriptions
maurolacy Dec 16, 2020
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
48 changes: 36 additions & 12 deletions contracts/cw3-fixed-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use cw0::{maybe_canonical, Expiration};
use cw2::set_contract_version;
use cw3::{
ProposalListResponse, ProposalResponse, Status, ThresholdResponse, Vote, VoteInfo,
VoteListResponse, VoteResponse, VoterInfo, VoterListResponse, VoterResponse,
VoteListResponse, VoteResponse, VoterDetail, VoterListResponse, VoterResponse,
};
use cw_storage_plus::Bound;

Expand Down Expand Up @@ -292,22 +292,28 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
fn query_threshold(deps: Deps) -> StdResult<ThresholdResponse> {
let cfg = CONFIG.load(deps.storage)?;
Ok(ThresholdResponse::AbsoluteCount {
weight_needed: cfg.required_weight,
weight: cfg.required_weight,
total_weight: cfg.total_weight,
})
}

fn query_proposal(deps: Deps, env: Env, id: u64) -> StdResult<ProposalResponse> {
let prop = PROPOSALS.load(deps.storage, id.into())?;
let status = prop.current_status(&env.block);

let cfg = CONFIG.load(deps.storage)?;
let threshold = ThresholdResponse::AbsoluteCount {
weight: cfg.required_weight,
total_weight: cfg.total_weight,
};
Ok(ProposalResponse {
id,
title: prop.title,
description: prop.description,
msgs: prop.msgs,
expires: prop.expires,
// TODO: check
status,
expires: prop.expires,
threshold,
})
}

Expand All @@ -321,12 +327,18 @@ fn list_proposals(
start_after: Option<u64>,
limit: Option<u32>,
) -> StdResult<ProposalListResponse> {
let cfg = CONFIG.load(deps.storage)?;
let threshold = ThresholdResponse::AbsoluteCount {
weight: cfg.required_weight,
total_weight: cfg.total_weight,
};

let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive_int);
let props: StdResult<Vec<_>> = PROPOSALS
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|p| map_proposal(&env.block, p))
.map(|p| map_proposal(&env.block, &threshold, p))
.collect();

Ok(ProposalListResponse { proposals: props? })
Expand All @@ -338,19 +350,26 @@ fn reverse_proposals(
start_before: Option<u64>,
limit: Option<u32>,
) -> StdResult<ProposalListResponse> {
let cfg = CONFIG.load(deps.storage)?;
let threshold = ThresholdResponse::AbsoluteCount {
weight: cfg.required_weight,
total_weight: cfg.total_weight,
};

let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let end = start_before.map(Bound::exclusive_int);
let props: StdResult<Vec<_>> = PROPOSALS
.range(deps.storage, None, end, Order::Descending)
.take(limit)
.map(|p| map_proposal(&env.block, p))
.map(|p| map_proposal(&env.block, &threshold, p))
.collect();

Ok(ProposalListResponse { proposals: props? })
}

fn map_proposal(
block: &BlockInfo,
threshold: &ThresholdResponse,
item: StdResult<(Vec<u8>, Proposal)>,
) -> StdResult<ProposalResponse> {
let (key, prop) = item?;
Expand All @@ -360,15 +379,20 @@ fn map_proposal(
title: prop.title,
description: prop.description,
msgs: prop.msgs,
expires: prop.expires,
status,
expires: prop.expires,
threshold: threshold.clone(),
})
}

fn query_vote(deps: Deps, proposal_id: u64, voter: HumanAddr) -> StdResult<VoteResponse> {
let voter_raw = deps.api.canonical_address(&voter)?;
let prop = BALLOTS.may_load(deps.storage, (proposal_id.into(), &voter_raw))?;
let vote = prop.map(|b| b.vote);
let ballot = BALLOTS.may_load(deps.storage, (proposal_id.into(), &voter_raw))?;
let vote = ballot.map(|b| VoteInfo {
voter,
vote: b.vote,
weight: b.weight,
});
Ok(VoteResponse { vote })
}

Expand Down Expand Up @@ -400,10 +424,10 @@ fn list_votes(
Ok(VoteListResponse { votes: votes? })
}

fn query_voter(deps: Deps, voter: HumanAddr) -> StdResult<VoterInfo> {
fn query_voter(deps: Deps, voter: HumanAddr) -> StdResult<VoterResponse> {
let voter_raw = deps.api.canonical_address(&voter)?;
let weight = VOTERS.may_load(deps.storage, &voter_raw)?;
Ok(VoterInfo { weight })
Ok(VoterResponse { weight })
}

fn list_voters(
Expand All @@ -421,7 +445,7 @@ fn list_voters(
.take(limit)
.map(|item| {
let (key, weight) = item?;
Ok(VoterResponse {
Ok(VoterDetail {
addr: api.human_address(&CanonicalAddr::from(key))?,
weight,
})
Expand Down
2 changes: 1 addition & 1 deletion contracts/cw3-flex-multisig/schema/handle_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
}
},
{
"description": "handle update hook messages from the group contract",
"description": "Handles update hook messages from the group contract",
"type": "object",
"required": [
"member_changed_hook"
Expand Down
83 changes: 78 additions & 5 deletions contracts/cw3-flex-multisig/schema/init_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"required": [
"group_addr",
"max_voting_period",
"required_weight"
"threshold"
],
"properties": {
"group_addr": {
Expand All @@ -14,13 +14,15 @@
"max_voting_period": {
"$ref": "#/definitions/Duration"
},
"required_weight": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
"threshold": {
"$ref": "#/definitions/Threshold"
}
},
"definitions": {
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Duration": {
"description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined",
"anyOf": [
Expand Down Expand Up @@ -54,6 +56,77 @@
},
"HumanAddr": {
"type": "string"
},
"Threshold": {
"description": "This defines the different ways tallies can happen.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation). See also `ThresholdResponse` in the cw3 spec.",
"anyOf": [
{
"description": "Declares that a fixed weight of Yes votes is needed to pass. See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.",
"type": "object",
"required": [
"absolute_count"
],
"properties": {
"absolute_count": {
"type": "object",
"required": [
"weight"
],
"properties": {
"weight": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
}
}
}
},
{
"description": "Declares a percentage of the total weight that must cast Yes votes in order for a proposal to pass. See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.",
"type": "object",
"required": [
"absolute_percentage"
],
"properties": {
"absolute_percentage": {
"type": "object",
"required": [
"percentage"
],
"properties": {
"percentage": {
"$ref": "#/definitions/Decimal"
}
}
}
}
},
{
"description": "Declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.",
"type": "object",
"required": [
"threshold_quorum"
],
"properties": {
"threshold_quorum": {
"type": "object",
"required": [
"quorum",
"threshold"
],
"properties": {
"quorum": {
"$ref": "#/definitions/Decimal"
},
"threshold": {
"$ref": "#/definitions/Decimal"
}
}
}
}
}
]
}
}
}
Loading