Skip to content

Commit

Permalink
Add integration tests (3) (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
duguorong009 authored Jun 20, 2022
1 parent 9bfb00f commit 7f3e6cf
Show file tree
Hide file tree
Showing 14 changed files with 14,593 additions and 2,716 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions contracts/signature-bridge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ schemars = "0.8.3"
serde = { version = "1.0.127", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.26" }
getrandom = { version = "0.2", features = ["js"] }
arkworks-setups = { version = "0.5.3", default-features = false }

protocol-cosmwasm = { version = "0.1.0", path = "../../packages/protocol_cosmwasm"}

Expand Down
99 changes: 28 additions & 71 deletions contracts/signature-bridge/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use cw2::set_contract_version;
use crate::state::{State, RESOURCEID2HANDLERADDR, STATE};
use protocol_cosmwasm::error::ContractError;
use protocol_cosmwasm::executor::ExecuteMsg as ExecutorExecMsg;
use protocol_cosmwasm::keccak::Keccak256;
use protocol_cosmwasm::signature_bridge::{
ExecProposalWithSigMsg, ExecuteMsg, InstantiateMsg, QueryMsg, SetResourceWithSigMsg,
StateResponse,
};
use protocol_cosmwasm::utils::{
compute_chain_id, compute_chain_id_type, element_encoder, get_chain_id_type,
};
// Essentially, this is from "tiny_keccak" crate.
use arkworks_setups::common::keccak_256;

// version info for migration info
const CONTRACT_NAME: &str = "crates.io:cosmwasm-signature-bridge";
Expand All @@ -25,6 +26,9 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
// ChainType info
pub const COSMOS_CHAIN_TYPE: [u8; 2] = [4, 0]; // 0x0400

const COMPRESSED_PUBKEY_LEN: usize = 33;
const UNCOMPRESSED_PUBKEY_LEN: usize = 65;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
Expand All @@ -39,20 +43,24 @@ pub fn instantiate(
return Err(ContractError::UnnecessaryFunds {});
}

if msg.initial_governor.len() != COMPRESSED_PUBKEY_LEN
&& msg.initial_governor.len() != UNCOMPRESSED_PUBKEY_LEN
{
return Err(ContractError::Std(StdError::generic_err(
"Pubkey length does not match.",
)));
}

// Set "state"
let governor = deps.api.addr_validate(&msg.initial_governor)?;
STATE.save(
deps.storage,
&State {
governor,
governor: msg.initial_governor,
proposal_nonce: 0,
},
)?;

Ok(Response::new().add_attributes(vec![
attr("method", "instantiate"),
attr("governor", msg.initial_governor),
]))
Ok(Response::new().add_attributes(vec![attr("method", "instantiate")]))
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down Expand Up @@ -81,37 +89,27 @@ fn admin_set_resource_with_signature(
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&msg.resource_id);
data.extend_from_slice(&msg.function_sig);
data.extend_from_slice(&element_encoder(&msg.nonce.to_le_bytes()));
data.extend_from_slice(&msg.nonce.to_be_bytes());
data.extend_from_slice(&msg.new_resource_id);
data.extend_from_slice(msg.handler_addr.as_bytes());
data.extend_from_slice(msg.execution_context_addr.as_bytes());

if !signed_by_governor(deps.branch(), &data, &msg.sig, state.governor.as_str())? {
if !signed_by_governor(deps.branch(), &data, &msg.sig, &state.governor)? {
return Err(ContractError::Std(StdError::GenericErr {
msg: "Invalid sig from governor".to_string(),
}));
}

if msg.nonce != state.proposal_nonce + 1048 {
if msg.nonce <= state.proposal_nonce || state.proposal_nonce + 1048 < msg.nonce {
return Err(ContractError::InvalidNonce);
}

let func_sig = Keccak256::hash(
b"adminSetResourceWithSignature(bytes32,bytes4,uint32,bytes32,address,address,bytes)",
)
.map_err(|_| ContractError::HashError)?;
if msg.function_sig != func_sig[0..4] {
return Err(ContractError::Std(StdError::GenericErr {
msg: "Invalid function signature".to_string(),
}));
if msg.function_sig != [0u8; 4] {
return Err(ContractError::InvalidArbitraryData);
}

// Save the info of "resource_id -> handler(contract)" in this contract.
RESOURCEID2HANDLERADDR.save(
deps.storage,
&msg.new_resource_id,
&deps.api.addr_validate(&msg.handler_addr)?,
)?;
RESOURCEID2HANDLERADDR.save(deps.storage, &msg.new_resource_id, &msg.handler_addr)?;

state.proposal_nonce = msg.nonce;
STATE.save(deps.storage, &state)?;
Expand All @@ -121,7 +119,7 @@ fn admin_set_resource_with_signature(
contract_addr: msg.handler_addr,
funds: vec![],
msg: to_binary(&ExecutorExecMsg::SetResource {
resource_id: msg.resource_id,
resource_id: msg.new_resource_id,
contract_addr: msg.execution_context_addr,
})
.unwrap(),
Expand All @@ -140,7 +138,7 @@ fn exec_proposal_with_signature(
let state = STATE.load(deps.storage)?;

// Validations
if !signed_by_governor(deps.branch(), &msg.data, &msg.sig, state.governor.as_str())? {
if !signed_by_governor(deps.branch(), &msg.data, &msg.sig, &state.governor)? {
return Err(ContractError::Std(StdError::GenericErr {
msg: "Invalid sig from governor".to_string(),
}));
Expand All @@ -162,9 +160,7 @@ fn exec_proposal_with_signature(
}

// Execute the "proposal" in "handler" contract
let handler_addr = RESOURCEID2HANDLERADDR
.load(deps.storage, &resource_id)?
.to_string();
let handler_addr = RESOURCEID2HANDLERADDR.load(deps.storage, &resource_id)?;
let msgs: Vec<CosmosMsg> = vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: handler_addr,
funds: vec![],
Expand All @@ -190,7 +186,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
fn get_state(deps: Deps) -> StdResult<StateResponse> {
let state = STATE.load(deps.storage)?;
Ok(StateResponse {
governor: state.governor.to_string(),
governor: state.governor,
proposal_nonce: state.proposal_nonce,
})
}
Expand All @@ -200,49 +196,10 @@ fn signed_by_governor(
deps: DepsMut,
data: &[u8],
sig: &[u8],
governor: &str,
governor: &[u8],
) -> Result<bool, ContractError> {
let hashed_data = Keccak256::hash(data).map_err(|_| ContractError::HashError)?;
let verify_result = deps
.api
.secp256k1_verify(&hashed_data, sig, governor.as_bytes());
let hashed_data = keccak_256(data);
let verify_result = deps.api.secp256k1_verify(&hashed_data, sig, governor);

verify_result.map_err(|e| ContractError::Std(StdError::VerificationErr { source: e }))
}

#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{attr, from_binary};
use protocol_cosmwasm::signature_bridge::StateResponse;

const GOVERNOR: &str = "governor";

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

let msg = InstantiateMsg {
initial_governor: GOVERNOR.to_string(),
};
let info = mock_info("creator", &[]);

// we can just call .unwrap() to assert this was a success
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
assert_eq!(
res.attributes,
vec![
attr("method", "instantiate"),
attr("governor", GOVERNOR.to_string())
]
);

// it worked, let's query the state
let res = query(deps.as_ref(), mock_env(), QueryMsg::GetState {}).unwrap();
let state: StateResponse = from_binary(&res).unwrap();
assert_eq!(state.governor, GOVERNOR.to_string());
assert_eq!(state.proposal_nonce, 0);
}
}
3 changes: 3 additions & 0 deletions contracts/signature-bridge/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
pub mod contract;
pub mod state;

#[cfg(test)]
pub mod tests;
8 changes: 3 additions & 5 deletions contracts/signature-bridge/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use cw_storage_plus::{Item, Map};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct State {
pub governor: Addr,
pub governor: Vec<u8>,
pub proposal_nonce: u32,
}

Expand All @@ -16,4 +14,4 @@ pub const STATE: Item<State> = Item::new("state");
pub const COUNTS: Map<&[u8], [u8; 32]> = Map::new("counts");

/// resourceID => handler address
pub const RESOURCEID2HANDLERADDR: Map<&[u8], Addr> = Map::new("resourceIDToHandlerAddress");
pub const RESOURCEID2HANDLERADDR: Map<&[u8], String> = Map::new("resourceIDToHandlerAddress");
30 changes: 30 additions & 0 deletions contracts/signature-bridge/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{attr, from_binary};

use protocol_cosmwasm::signature_bridge::StateResponse;
use protocol_cosmwasm::signature_bridge::{InstantiateMsg, QueryMsg};

use super::contract::{instantiate, query};

const GOVERNOR: [u8; 33] = [0u8; 33];

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

let msg = InstantiateMsg {
initial_governor: GOVERNOR.to_vec(),
};
let info = mock_info("creator", &[]);

// we can just call .unwrap() to assert this was a success
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
assert_eq!(res.attributes, vec![attr("method", "instantiate"),]);

// it worked, let's query the state
let res = query(deps.as_ref(), mock_env(), QueryMsg::GetState {}).unwrap();
let state: StateResponse = from_binary(&res).unwrap();
assert_eq!(state.governor, GOVERNOR.to_vec());
assert_eq!(state.proposal_nonce, 0);
}
4 changes: 2 additions & 2 deletions packages/protocol_cosmwasm/src/signature_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {
pub initial_governor: String,
pub initial_governor: Vec<u8>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
Expand Down Expand Up @@ -43,6 +43,6 @@ pub enum QueryMsg {

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct StateResponse {
pub governor: String,
pub governor: Vec<u8>,
pub proposal_nonce: u32,
}
2 changes: 1 addition & 1 deletion packages/protocol_cosmwasm/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn compute_chain_id_type(chain_id: u64, chain_type: &[u8]) -> u64 {
let mut buf = [0u8; 8];
#[allow(clippy::needless_borrow)]
buf[2..4].copy_from_slice(&chain_type);
buf[4..8].copy_from_slice(&chain_id_value.to_le_bytes());
buf[4..8].copy_from_slice(&chain_id_value.to_be_bytes());
u64::from_be_bytes(buf)
}

Expand Down
Loading

0 comments on commit 7f3e6cf

Please sign in to comment.