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

test: ism_multisig #9

Merged
merged 18 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ cw-storage-plus = "1.0.1"
cw-utils = "0.16.0"
cw2 = "1.0.0"
sha3 = "0.10.8"
sha2 = { version = "0.10.6", default-features = false }
byeongsu-hong marked this conversation as resolved.
Show resolved Hide resolved
ripemd = "0.1.3"
bech32 = "0.9.1"
serde = "1.0.162"
thiserror = { version = "1.0.37" }
secp256k1 = "0.27.0"

cw-multi-test = "0.16.1"
cosmwasm-schema = "1.1.9"
Expand Down
4 changes: 2 additions & 2 deletions contracts/ism-multisig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
sha3 = { workspace = true }
sha2 = { workspace = true }
ripemd = { workspace = true }
bech32 = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
cosmwasm-schema = { workspace = true }
byeongsu-hong marked this conversation as resolved.
Show resolved Hide resolved

secp256k1 = { workspace = true }
byeongsu-hong marked this conversation as resolved.
Show resolved Hide resolved
hpl-interface = { workspace = true }

[dev-dependencies]
Expand Down
60 changes: 9 additions & 51 deletions contracts/ism-multisig/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
use std::collections::HashSet;

#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response};
use cw2::set_contract_version;
use hpl_interface::{
ism::{
multisig::{ExecuteMsg, InstantiateMsg, MigrateMsg},
ISMQueryMsg, ISMType, VerifyResponse,
},
types::{message::Message, metadata::MessageIdMultisigIsmMetadata},
use hpl_interface::ism::{
multisig::{ExecuteMsg, InstantiateMsg, MigrateMsg},
ISMQueryMsg,
};

use crate::{
error::ContractError,
execute,
state::{Config, CONFIG, THRESHOLD, VALIDATORS},
state::{Config, CONFIG},
CONTRACT_NAME, CONTRACT_VERSION,
};

Expand All @@ -30,7 +25,7 @@ pub fn instantiate(

let config = Config {
owner: deps.api.addr_validate(&msg.owner)?,
chain_hpl: msg.chain_hpl,
addr_prefix: msg.addr_prefix,
};

CONFIG.save(deps.storage, &config)?;
Expand Down Expand Up @@ -75,51 +70,14 @@ pub fn execute(
/// Handling contract query
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: ISMQueryMsg) -> Result<Binary, ContractError> {
use crate::query;
use ISMQueryMsg::*;

match msg {
ModuleType {} => Ok(to_binary(&ISMType::Owned)?),
ModuleType {} => query::get_module_type(),
Verify {
metadata: raw_metadata,
message: raw_message,
} => {
let metadata: MessageIdMultisigIsmMetadata = raw_metadata.into();
let message: Message = raw_message.into();

let threshold = THRESHOLD.load(deps.storage, message.origin_domain.into())?;
let validators = VALIDATORS.load(deps.storage, message.origin_domain.into())?;

let mut signatures: Vec<Binary> = Vec::new();
for i in 0..metadata.signatures_len().unwrap() {
signatures.push(metadata.signature_at(i))
}

let unique_vali_pubkey: HashSet<_> =
validators.0.into_iter().map(|v| v.signer_pubkey).collect();

let unique_meta_pubkey: HashSet<_> = signatures
.into_iter()
.flat_map(|sig| {
[
deps.api
.secp256k1_recover_pubkey(&message.id(), sig.as_slice(), 0)
.unwrap(),
deps.api
.secp256k1_recover_pubkey(&message.id(), sig.as_slice(), 1)
.unwrap(),
]
})
.map(Binary::from)
.collect();

let success = unique_vali_pubkey
.intersection(&unique_meta_pubkey)
.collect::<Vec<_>>()
.len();

Ok(to_binary(&VerifyResponse {
verified: success >= usize::from(threshold),
})?)
}
} => query::verify_message(deps, raw_metadata, raw_message),
}
}
17 changes: 16 additions & 1 deletion contracts/ism-multisig/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,26 @@ pub enum ContractError {
VerificationError(#[from] VerificationError),

#[error("Unauthorized")]
Unauthorized {},
Unauthorized,

#[error("Wrong length")]
WrongLength,

#[error("Invalid pubkey")]
InvalidPubKey,

#[error("Ownership transfer not started")]
OwnershipTransferNotStarted,

#[error("Ownership transfer already started")]
OwnershipTransferAlreadyStarted,

#[error("Validator pubkey mismatched")]
ValidatorPubKeyMismatched,

#[error("Duplicate Validator")]
ValidatorDuplicate,

#[error("Validator not exists")]
ValidatorNotExist,
}
178 changes: 168 additions & 10 deletions contracts/ism-multisig/src/execute/gov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::{
emit_finish_transfer_ownership, emit_init_transfer_ownership,
emit_revoke_transfer_ownership,
},
state::{Config, CONFIG, PENDING_OWNER},
state::{
assert_owned, assert_pending_owner, assert_pending_owner_empty, assert_pending_owner_exist,
Config, CONFIG, PENDING_OWNER,
},
ContractError,
};

Expand All @@ -14,8 +17,8 @@ pub fn init_transfer_ownership(
info: MessageInfo,
next_owner: String,
) -> Result<Response, ContractError> {
assert_eq!(info.sender, CONFIG.load(deps.storage)?.owner);
assert!(PENDING_OWNER.may_load(deps.storage)?.is_none());
assert_owned(deps.storage, info.sender)?;
assert_pending_owner_empty(deps.storage)?;

PENDING_OWNER.save(deps.storage, &deps.api.addr_validate(&next_owner)?)?;

Expand All @@ -26,17 +29,15 @@ pub fn finish_transfer_ownership(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
let pending_owner = PENDING_OWNER.may_load(deps.storage)?;

assert!(pending_owner.is_some());
assert_eq!(info.sender, PENDING_OWNER.load(deps.storage)?);
assert_pending_owner(deps.storage, info.sender.clone())?;

let config = CONFIG.load(deps.storage)?;
let pending_owner = PENDING_OWNER.load(deps.storage)?;

CONFIG.save(
deps.storage,
&Config {
owner: pending_owner.unwrap(),
owner: pending_owner,
..config
},
)?;
Expand All @@ -49,10 +50,167 @@ pub fn revoke_transfer_ownership(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
assert_eq!(info.sender, CONFIG.load(deps.storage)?.owner);
assert!(PENDING_OWNER.may_load(deps.storage)?.is_some());
assert_owned(deps.storage, info.sender)?;
assert_pending_owner_exist(deps.storage)?;

PENDING_OWNER.remove(deps.storage);

Ok(Response::new().add_event(emit_revoke_transfer_ownership()))
}

#[cfg(test)]
mod test {
use cosmwasm_std::{
testing::{mock_dependencies, mock_info},
Addr, Storage,
};

use super::*;
const ADDR1_VAULE: &str = "addr1";
const ADDR2_VAULE: &str = "addr2";

fn mock_owner(storage: &mut dyn Storage, owner: Addr) {
let config = Config {
owner,
addr_prefix: String::new(),
};

CONFIG.save(storage, &config).unwrap();
}

#[test]
fn test_init_transfer_ownership_failed() {
let mut deps = mock_dependencies();

let owner = Addr::unchecked(ADDR1_VAULE);
let abuser = Addr::unchecked(ADDR2_VAULE);

mock_owner(deps.as_mut().storage, owner.clone());

// NOT OWNED ASSERT
let not_owned_info = mock_info(abuser.as_str(), &[]);
let not_owned_assert =
init_transfer_ownership(deps.as_mut(), not_owned_info, "ADDR3".to_string())
.unwrap_err();

assert!(matches!(not_owned_assert, ContractError::Unauthorized {}));

// Transfer Already Started
PENDING_OWNER.save(deps.as_mut().storage, &owner).unwrap();
let duplicated_info = mock_info(owner.as_str(), &[]);

let already_started_assert =
init_transfer_ownership(deps.as_mut(), duplicated_info, "ADDR3".to_string())
.unwrap_err();

assert!(matches!(
already_started_assert,
ContractError::OwnershipTransferAlreadyStarted {}
));
}

#[test]
fn test_init_transfer_ownership_success() {
let mut deps = mock_dependencies();
let owner = Addr::unchecked(ADDR1_VAULE);
let next_owner = "osmo1pe6jpke2wvufly7y6k3xuhlfy7n2knpts222g4".to_string();

mock_owner(deps.as_mut().storage, owner.clone());

let info = mock_info(owner.as_str(), &[]);
let result = init_transfer_ownership(deps.as_mut(), info, next_owner.clone()).unwrap();

assert_eq!(
result.events,
vec![emit_init_transfer_ownership(next_owner)]
)
}

#[test]
fn test_finish_transfer_ownership_failed() {
let mut deps = mock_dependencies();
let new_owner = Addr::unchecked(ADDR1_VAULE);

// Transfer not started yet
let info = mock_info(new_owner.as_str(), &[]);
let not_start_assert = finish_transfer_ownership(deps.as_mut(), info).unwrap_err();

assert!(matches!(
not_start_assert,
ContractError::OwnershipTransferNotStarted {}
));

// Wrong new owner
PENDING_OWNER
.save(deps.as_mut().storage, &new_owner)
.unwrap();

let info = mock_info(ADDR2_VAULE, &[]);
let wrong_owner = finish_transfer_ownership(deps.as_mut(), info).unwrap_err();
assert!(matches!(wrong_owner, ContractError::Unauthorized {}))
}

#[test]
fn test_finish_transfer_ownership_success() {
let mut deps = mock_dependencies();
let new_owner = Addr::unchecked(ADDR1_VAULE);
mock_owner(deps.as_mut().storage, Addr::unchecked(ADDR2_VAULE));

PENDING_OWNER
.save(deps.as_mut().storage, &new_owner)
.unwrap();

let info = mock_info(new_owner.as_str(), &[]);
let result = finish_transfer_ownership(deps.as_mut(), info).unwrap();

// Validate response
assert_eq!(
result.events,
vec![emit_finish_transfer_ownership(new_owner.clone())]
);

// Validate actual value
let saved_owner = CONFIG.load(&deps.storage).unwrap().owner;
assert_eq!(saved_owner, new_owner);
}

#[test]
fn test_revoke_transfer_ownership_failed() {
let mut deps = mock_dependencies();
let owner = Addr::unchecked(ADDR1_VAULE);
mock_owner(deps.as_mut().storage, owner.clone());

// wrong owner
let info = mock_info(ADDR2_VAULE, &[]);
let unauthorized_assert = revoke_transfer_ownership(deps.as_mut(), info).unwrap_err();
assert!(matches!(
unauthorized_assert,
ContractError::Unauthorized {}
));

// transfer not started yet
let info = mock_info(owner.as_str(), &[]);
let not_start_assert = revoke_transfer_ownership(deps.as_mut(), info).unwrap_err();
assert!(matches!(
not_start_assert,
ContractError::OwnershipTransferNotStarted {}
))
}

#[test]
fn test_revoke_transfer_ownership_success() {
let mut deps = mock_dependencies();
let owner = Addr::unchecked(ADDR1_VAULE);
mock_owner(deps.as_mut().storage, owner.clone());

PENDING_OWNER
.save(deps.as_mut().storage, &Addr::unchecked(ADDR2_VAULE))
.unwrap();

let info = mock_info(owner.as_str(), &[]);
let result = revoke_transfer_ownership(deps.as_mut(), info).unwrap();

assert_eq!(result.events, vec![emit_revoke_transfer_ownership()]);
assert!(PENDING_OWNER.may_load(&deps.storage).unwrap().is_none())
}
}
Loading
Loading