Skip to content

Commit

Permalink
Merge pull request #150 from CosmWasm/separate-multisig-from-group
Browse files Browse the repository at this point in the history
Separate multisig from group
  • Loading branch information
ethanfrey authored Dec 4, 2020
2 parents 4904145 + 6adc4ab commit 56d52ac
Show file tree
Hide file tree
Showing 20 changed files with 2,179 additions and 21 deletions.
36 changes: 36 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ workflows:
- contract_cw1_subkeys
- contract_cw1_whitelist
- contract_cw3_fixed_multisig
- contract_cw3_flex_multisig
- contract_cw4_group
- contract_cw20_atomic_swap
- contract_cw20_base
Expand Down Expand Up @@ -176,6 +177,41 @@ jobs:
- target
key: cargocache-cw3-fixed-multisig-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}

contract_cw3_flex_multisig:
docker:
- image: rust:1.47.0
working_directory: ~/project/contracts/cw3-flex-multisig
steps:
- checkout:
path: ~/project
- run:
name: Version information
command: rustc --version; cargo --version; rustup --version
- restore_cache:
keys:
- cargocache-cw3-flex-multisig-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}
- run:
name: Unit Tests
env: RUST_BACKTRACE=1
command: cargo unit-test --locked
- run:
name: Build and run schema generator
command: cargo schema --locked
- run:
name: Ensure checked-in schemas are up-to-date
command: |
CHANGES_IN_REPO=$(git status --porcelain)
if [[ -n "$CHANGES_IN_REPO" ]]; then
echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:"
git status && git --no-pager diff
exit 1
fi
- save_cache:
paths:
- /usr/local/cargo/registry
- target
key: cargocache-cw3-flex-multisig-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}

contract_cw4_group:
docker:
- image: rust:1.47.0
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ incremental = false
codegen-units = 1
incremental = false

[profile.release.package.cw3-flex-multisig]
codegen-units = 1
incremental = false

[profile.release.package.cw4-group]
codegen-units = 1
incremental = false
Expand Down
4 changes: 2 additions & 2 deletions contracts/cw20-escrow/src/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cw_multi_test::{App, Contract, ContractWrapper, SimpleBank};

use crate::msg::{CreateMsg, DetailsResponse, HandleMsg, InitMsg, QueryMsg, ReceiveMsg};

fn mock_router() -> App {
fn mock_app() -> App {
let env = mock_env();
let api = Box::new(MockApi::default());
let bank = SimpleBank {};
Expand Down Expand Up @@ -36,7 +36,7 @@ pub fn contract_cw20() -> Box<dyn Contract> {
#[test]
// receive cw20 tokens and release upon approval
fn escrow_happy_path_cw20_tokens() {
let mut router = mock_router();
let mut router = mock_app();

// set personal balance
let owner = HumanAddr::from("owner");
Expand Down
10 changes: 5 additions & 5 deletions contracts/cw3-fixed-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ fn list_voters(

#[cfg(test)]
mod tests {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR};
use cosmwasm_std::{coin, from_binary, BankMsg};

use cw0::Duration;
Expand Down Expand Up @@ -598,7 +598,7 @@ mod tests {
setup_test_case(deps.as_mut(), info.clone(), required_weight, voting_period).unwrap();

let bank_msg = BankMsg::Send {
from_address: OWNER.into(),
from_address: MOCK_CONTRACT_ADDR.into(),
to_address: SOMEBODY.into(),
amount: vec![coin(1, "BTC")],
};
Expand Down Expand Up @@ -689,7 +689,7 @@ mod tests {

// Propose
let bank_msg = BankMsg::Send {
from_address: OWNER.into(),
from_address: MOCK_CONTRACT_ADDR.into(),
to_address: SOMEBODY.into(),
amount: vec![coin(1, "BTC")],
};
Expand Down Expand Up @@ -843,7 +843,7 @@ mod tests {

// Propose
let bank_msg = BankMsg::Send {
from_address: OWNER.into(),
from_address: MOCK_CONTRACT_ADDR.into(),
to_address: SOMEBODY.into(),
amount: vec![coin(1, "BTC")],
};
Expand Down Expand Up @@ -946,7 +946,7 @@ mod tests {

// Propose
let bank_msg = BankMsg::Send {
from_address: OWNER.into(),
from_address: MOCK_CONTRACT_ADDR.into(),
to_address: SOMEBODY.into(),
amount: vec![coin(1, "BTC")],
};
Expand Down
6 changes: 6 additions & 0 deletions contracts/cw3-flex-multisig/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
wasm-debug = "build --target wasm32-unknown-unknown"
unit-test = "test --lib"
integration-test = "test --test integration"
schema = "run --example schema"
34 changes: 34 additions & 0 deletions contracts/cw3-flex-multisig/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "cw3-flex-multisig"
version = "0.3.2"
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
edition = "2018"
description = "Implementing cw3 with multiple voting patterns and dynamic groups"
license = "Apache-2.0"
repository = "https://github.com/CosmWasm/cosmwasm-plus"
homepage = "https://cosmwasm.com"
documentation = "https://docs.cosmwasm.com"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
backtraces = ["cosmwasm-std/backtraces"]
# use library feature to disable all init/handle/query exports
library = []

[dependencies]
cw0 = { path = "../../packages/cw0", version = "0.3.2" }
cw2 = { path = "../../packages/cw2", version = "0.3.2" }
cw3 = { path = "../../packages/cw3", version = "0.3.2" }
cw4 = { path = "../../packages/cw4", version = "0.3.2" }
cw-storage-plus = { path = "../../packages/storage-plus", version = "0.3.2", features = ["iterator"] }
cosmwasm-std = { version = "0.12.0", features = ["iterator"] }
schemars = "0.7"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.20" }

[dev-dependencies]
cosmwasm-schema = { version = "0.12.0" }
cw4-group = { path = "../cw4-group", version = "0.3.2" }
cw-multi-test = { path = "../../packages/multi-test", version = "0.3.2" }
14 changes: 14 additions & 0 deletions contracts/cw3-flex-multisig/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CW3-Flex-Whitelist: Implementing cw3 with multiple voting patterns and dynamic groups
Copyright (C) 2020 Confio OÜ

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
83 changes: 83 additions & 0 deletions contracts/cw3-flex-multisig/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# CW3 Flexible Multisig

This builds on [cw3-fixed-multisig](../cw3-fixed-multisig) with a more
powerful implementation of the [cw3 spec](../../packages/cw3/README.md).
It is a multisig contract that is backed by a
[cw4 (group)](../../packages/cw4/README.md) contract, which independently
maintains the voter set.

This provides 2 main advantages:

* You can create two different multisigs with different voting thresholds
backed by the same group. Thus, you can have a 50% vote and a 67% vote
that always use the same voter set, but can take other actions.
* TODO: It allows dynamic multisig groups. Since the group can change,
we can set one of the multisigs as the admin of the group contract,
and the


Besides the dynamic voting set, the main difference with the native
Cosmos SDK multisig, is that it aggregates the signatures on chain with
visible proposals (like `x/gov` in the Cosmos SDK), rather than requiring
signers to share signatures off chain.

## Init

The first step to create such a multisig is to instantiate a cw4 contract
with the desired member set. For now, this only is supported by
[cw4-group](../cw4-group), but we will add a token-weighted group contract
(TODO).

If you create a `cw4-group` contract and want a multisig to be able
to modify it's own group, set the admin as your personal key, then init the
multisig pointing to the group, then modify the admin of the group to point
back to the multisig. This is the current practice to create such circular
dependencies (TODO: document better).

When creating the multisig, you must set the required weight to pass a vote
as well as the max/default voting period. (TODO: allow more threshold types)

## Handle Process

First, a registered voter must submit a proposal. This also includes the
first "Yes" vote on the proposal by the proposer. The proposer can set
an expiration time for the voting process, or it defaults to the limit
provided when creating the contract (so proposals can be closed after several
days).

Before the proposal has expired, any voter with non-zero weight can add their
vote. Only "Yes" votes are tallied. If enough "Yes" votes were submitted before
the proposal expiration date, the status is set to "Passed".

Once a proposal is "Passed", anyone may submit an "Execute" message. This will
trigger the proposal to send all stored messages from the proposal and update
it's state to "Executed", so it cannot run again. (Note if the execution fails
for any reason - out of gas, insufficient funds, etc - the state update will
be reverted and it will remain "Passed" so you can try again).

Once a proposal has expired without passing, anyone can submit a "Close"
message to mark it closed. This has no effect beyond cleaning up the UI/database.

TODO: this contract currently assumes the group membership is static during
the lifetime of one proposal. If the membership changes when a proposal is
open, this will calculate incorrect values (future PR).

## Running this contract

You will need Rust 1.44.1+ with `wasm32-unknown-unknown` target installed.

You can run unit tests on this via:

`cargo test`

Once you are happy with the content, you can compile it to wasm via:

```
RUSTFLAGS='-C link-arg=-s' cargo wasm
cp ../../target/wasm32-unknown-unknown/release/cw3_fixed_multisig.wasm .
ls -l cw3_fixed_multisig.wasm
sha256sum cw3_fixed_multisig.wasm
```

Or for a production-ready (optimized) build, run a build command in the
the repository root: https://github.com/CosmWasm/cosmwasm-plus#compiling.
17 changes: 17 additions & 0 deletions contracts/cw3-flex-multisig/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::env::current_dir;
use std::fs::create_dir_all;

use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for};

use cw3_flex_multisig::msg::{HandleMsg, InitMsg, QueryMsg};

fn main() {
let mut out_dir = current_dir().unwrap();
out_dir.push("schema");
create_dir_all(&out_dir).unwrap();
remove_schemas(&out_dir).unwrap();

export_schema(&schema_for!(InitMsg), &out_dir);
export_schema_with_title(&mut schema_for!(HandleMsg), &out_dir, "HandleMsg");
export_schema_with_title(&mut schema_for!(QueryMsg), &out_dir, "QueryMsg");
}
Loading

0 comments on commit 56d52ac

Please sign in to comment.