Skip to content

Commit

Permalink
Implement the chain_id webb proposal for Cosmos SDK blockchains (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
duguorong009 authored Jun 6, 2022
1 parent e4fddf6 commit 9bfb00f
Show file tree
Hide file tree
Showing 23 changed files with 192 additions and 168 deletions.
5 changes: 0 additions & 5 deletions contracts/anchor-handler/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ use protocol_cosmwasm::structs::{
const CONTRACT_NAME: &str = "crates.io:cosmwasm-anchor-handler";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

// ChainType info
pub const COSMOS_CHAIN_TYPE: [u8; 2] = [4, 0]; // 0x0400

pub const MOCK_CHAIN_ID: u64 = 1;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
Expand Down
12 changes: 6 additions & 6 deletions contracts/anchor-handler/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ pub struct State {
pub const STATE: Item<State> = Item::new("state");

/* ----- Handlers common ----- */
// resourceID => token contract address
/// resourceID => token contract address
pub const RESOURCEID2CONTRACTADDRESS: Map<&[u8], Addr> = Map::new("resourceIDToContractAddress");

// Execution contract address => resourceID
/// Execution contract address => resourceID
pub const CONTRACTADDRESS2RESOURCEID: Map<Addr, [u8; 32]> = Map::new("contractAddressToResourceID");

// Execution contract address => is whitelisted
/// Execution contract address => is whitelisted
pub const CONTRACTWHITELIST: Map<Addr, bool> = Map::new("contract_whitelist");

pub fn set_resource(
Expand All @@ -54,11 +54,11 @@ pub fn read_whitelist(store: &dyn Storage, contract_addr: Addr) -> StdResult<boo
/* --------------------------- */

/* ---------- Anchor-Handler specific DS ---------- */
// sourceChainID => height => Update Record
// (src_chain_id, height) -> UpdateRecord
/// sourceChainID => height => Update Record
/// (src_chain_id, height) -> UpdateRecord
pub const UPDATE_RECORDS: Map<(String, String), UpdateRecord> = Map::new("update_records");

// source chain ID => number of updates
/// source chain ID => number of updates
pub const COUNTS: Map<u64, u64> = Map::new("counts");

pub fn read_update_record(
Expand Down
18 changes: 10 additions & 8 deletions contracts/anchor/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ use protocol_cosmwasm::token_wrapper::{
QueryMsg as TokenWrapperQueryMsg,
};
use protocol_cosmwasm::utils::{
compute_chain_id_type, element_encoder, parse_string_to_uint128, truncate_and_pad,
compute_chain_id, compute_chain_id_type, element_encoder, parse_string_to_uint128,
truncate_and_pad,
};
use protocol_cosmwasm::zeroes::zeroes;

Expand Down Expand Up @@ -84,7 +85,6 @@ pub fn instantiate(
// Initialize the Anchor
let deposit_size = msg.deposit_size;
let anchor = Anchor {
chain_id: msg.chain_id,
linkable_tree: linkable_merkle_tree,
proposal_nonce: 0_u32,
deposit_size,
Expand Down Expand Up @@ -116,7 +116,7 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
// Withdraw the cw20 token with proof
ExecuteMsg::Withdraw(msg) => withdraw(deps, info, msg),
ExecuteMsg::Withdraw(msg) => withdraw(deps, env, info, msg),
// Unwrap the "TokenWrapper" token
ExecuteMsg::UnwrapIntoToken { token_addr, amount } => {
unwrap_into_token(deps, info.sender.to_string(), token_addr, amount)
Expand All @@ -143,7 +143,7 @@ pub fn execute(
),

// Withdraws the deposit & unwraps to valid token for `sender`
ExecuteMsg::WithdrawAndUnwrap(msg) => withdraw_and_unwrap(deps, info, msg),
ExecuteMsg::WithdrawAndUnwrap(msg) => withdraw_and_unwrap(deps, env, info, msg),

// Sets a new handler for the contract
ExecuteMsg::SetHandler { handler, nonce } => set_handler(deps, info, handler, nonce),
Expand Down Expand Up @@ -297,6 +297,7 @@ fn unwrap_into_token(
/// Withdraw a deposit from the contract
pub fn withdraw(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: WithdrawMsg,
) -> Result<Response, ContractError> {
Expand Down Expand Up @@ -329,8 +330,9 @@ pub fn withdraw(
}

// Format the public input bytes
let chain_id = compute_chain_id(&env.block.chain_id);
let chain_id_type_bytes =
element_encoder(&compute_chain_id_type(anchor.chain_id, &COSMOS_CHAIN_TYPE).to_le_bytes());
element_encoder(&compute_chain_id_type(chain_id.into(), &COSMOS_CHAIN_TYPE).to_le_bytes());
let recipient_bytes = truncate_and_pad(recipient.as_bytes());
let relayer_bytes = truncate_and_pad(relayer.as_bytes());

Expand Down Expand Up @@ -612,6 +614,7 @@ fn wrap_and_deposit_cw20(
/// Withdraws the deposit & unwraps into valid token for `sender`
fn withdraw_and_unwrap(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: WithdrawMsg,
) -> Result<Response, ContractError> {
Expand Down Expand Up @@ -653,8 +656,9 @@ fn withdraw_and_unwrap(
};

// Format the public input bytes
let chain_id = compute_chain_id(&env.block.chain_id);
let chain_id_type_bytes =
element_encoder(&compute_chain_id_type(anchor.chain_id, &COSMOS_CHAIN_TYPE).to_le_bytes());
element_encoder(&compute_chain_id_type(chain_id.into(), &COSMOS_CHAIN_TYPE).to_le_bytes());
let recipient_bytes = truncate_and_pad(recipient.as_bytes());
let relayer_bytes = truncate_and_pad(relayer.as_bytes());

Expand Down Expand Up @@ -857,7 +861,6 @@ pub fn get_config(deps: Deps) -> StdResult<ConfigResponse> {
Ok(ConfigResponse {
handler: anchor.handler.to_string(),
proposal_nonce: anchor.proposal_nonce,
chain_id: anchor.chain_id,
tokenwrapper_addr: anchor.tokenwrapper_addr.to_string(),
deposit_size: anchor.deposit_size.to_string(),
})
Expand Down Expand Up @@ -926,7 +929,6 @@ pub fn validate_and_store_commitment(
ANCHOR.save(
deps.storage,
&Anchor {
chain_id: anchor.chain_id,
deposit_size: anchor.deposit_size,
linkable_tree: anchor.linkable_tree,
tokenwrapper_addr: anchor.tokenwrapper_addr,
Expand Down
36 changes: 19 additions & 17 deletions contracts/anchor/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,25 @@ pub const MERKLEROOTS: Map<String, [u8; 32]> = Map::new("merkle_roots");
pub const FILLED_SUBTREES: Map<String, [u8; 32]> = Map::new("filled_subtrees");
pub const NULLIFIERS: Map<Vec<u8>, bool> = Map::new("used_nullifers");

// "Anchor"
// Connected instances that contains an on-chain merkle tree and
// tracks a set of connected _anchors_ across chains (through edges)
// in its local storage.

// NOTE: The `chain_id` field is just for temporary development purpose.
// In the future, it should be removed & the contract should use the
// `chain_id`(blockchain-unique ID) obtained inside the contract.
/// "Anchor"
/// Connected instances that contains an on-chain merkle tree and
/// tracks a set of connected _anchors_ across chains (through edges)
/// in its local storage.
///
/// "handler" Address of "anchor-handler", which add/updte the `edge` info
/// "proposal_nonce" Nonce value to track the proposals
/// "deposit_size" Minimum `deposit` amount for tx
/// "merkle_tree" Tree data structure to hold the `deposit` info
/// "linkable_tree" Tree data structure to hold the `edge` info
/// "tokenwrapper_addr" Cw20 token address used for wrapping native & any cw20 token
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Anchor {
pub chain_id: u64, // ChainID of underlying blockchain(Temporary field)
pub handler: Addr, // Address of "anchor-handler", which add/updte the `edge` info
pub proposal_nonce: u32, // Proposal nonce
pub deposit_size: Uint128, // Minimum `deposit` amount for tx
pub merkle_tree: MerkleTree, // Tree data structure to hold the `deposit` info
pub linkable_tree: LinkableMerkleTree, // Tree data structure to hold the `edge` info
pub tokenwrapper_addr: Addr, // Cw20 token address used for wrapping native & any cw20 token
pub handler: Addr,
pub proposal_nonce: u32,
pub deposit_size: Uint128,
pub merkle_tree: MerkleTree,
pub linkable_tree: LinkableMerkleTree,
pub tokenwrapper_addr: Addr,
}

pub fn read_edge(store: &dyn Storage, k: ChainId) -> StdResult<Edge> {
Expand Down Expand Up @@ -77,7 +79,7 @@ pub fn save_neighbor_roots(
NEIGHBOR_ROOTS.save(store, (id.to_string(), num.to_string()), &data)
}

// LinkableMerkleTree
/// LinkableMerkleTree
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LinkableMerkleTree {
pub max_edges: u32,
Expand Down Expand Up @@ -198,7 +200,7 @@ impl LinkableMerkleTree {
}
}

// MerkleTree
/// MerkleTree
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct MerkleTree {
pub levels: u32,
Expand Down
18 changes: 8 additions & 10 deletions contracts/anchor/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::contract::{execute, instantiate};
use crate::test_util::Element;

const MAX_EDGES: u32 = 2;
const CHAIN_ID: u64 = 1;
const CHAIN_ID: u64 = 2145598729; // chain_id: "cosmos-testnet-14002"
const LEVELS: u32 = 30;
const TOKENWRAPPER_ADDR: &str = "terra1340t6lqq6jxhm8d6gtz0hzz5jzcszvm27urkn2"; // Cw20 token
const DEPOSIT_SIZE: u128 = 1_000_000;
Expand All @@ -44,7 +44,6 @@ fn create_anchor() -> OwnedDeps<MockStorage, MockApi, crate::mock_querier::WasmM
let info = mock_info("anyone", &[]);
let instantiate_msg = InstantiateMsg {
max_edges: MAX_EDGES,
chain_id: CHAIN_ID,
levels: LEVELS,
deposit_size: Uint128::from(DEPOSIT_SIZE),
tokenwrapper_addr: TOKENWRAPPER_ADDR.to_string(),
Expand All @@ -64,7 +63,6 @@ fn test_anchor_proper_initialization() {
let info = mock_info("anyone", &[]);
let instantiate_msg = InstantiateMsg {
max_edges: MAX_EDGES,
chain_id: CHAIN_ID,
levels: LEVELS,
deposit_size: Uint128::from(DEPOSIT_SIZE),
tokenwrapper_addr: TOKENWRAPPER_ADDR.to_string(),
Expand Down Expand Up @@ -113,7 +111,7 @@ fn test_anchor_should_be_able_to_deposit() {
fn test_anchor_fail_when_any_byte_is_changed_in_proof() {
let curve = Curve::Bn254;
let (pk_bytes, _) = crate::test_util::setup_environment(curve);
let src_chain_id = compute_chain_id_type(1u64, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let recipient_bytes = RECIPIENT.as_bytes();
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
Expand Down Expand Up @@ -194,7 +192,7 @@ fn test_anchor_fail_when_any_byte_is_changed_in_proof() {
fn test_anchor_fail_when_invalid_merkle_roots() {
let curve = Curve::Bn254;
let (pk_bytes, _) = crate::test_util::setup_environment(curve);
let src_chain_id = compute_chain_id_type(1u64, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let recipient_bytes = RECIPIENT.as_bytes();
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
Expand Down Expand Up @@ -271,7 +269,7 @@ fn test_anchor_fail_when_invalid_merkle_roots() {
fn test_anchor_works_with_wasm_utils() {
let curve = Curve::Bn254;
let (pk_bytes, _) = crate::test_util::setup_environment(curve);
let src_chain_id = compute_chain_id_type(1u64, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let recipient_bytes = RECIPIENT.as_bytes();
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
Expand Down Expand Up @@ -363,7 +361,7 @@ fn test_anchor_works() {
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
let refund_value = 0;
let src_chain_id = compute_chain_id_type(1, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let commitment_bytes = vec![0u8; 32];
let commitment_element = Element::from_bytes(&commitment_bytes);

Expand Down Expand Up @@ -450,7 +448,7 @@ fn test_anchor_fail_when_relayer_is_diff_from_that_in_proof_generation() {
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
let refund_value = 0;
let src_chain_id = compute_chain_id_type(1, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let commitment_bytes = vec![0u8; 32];
let commitment_element = Element::from_bytes(&commitment_bytes);

Expand Down Expand Up @@ -526,7 +524,7 @@ fn test_anchor_fail_when_fee_submitted_is_changed() {
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
let refund_value = 0;
let src_chain_id = compute_chain_id_type(1, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let commitment_bytes = vec![0u8; 32];
let commitment_element = Element::from_bytes(&commitment_bytes);

Expand Down Expand Up @@ -760,7 +758,7 @@ fn test_anchor_withdraw_and_unwrap_native() {
let relayer_bytes = RELAYER.as_bytes();
let fee_value = 0;
let refund_value = 0;
let src_chain_id = compute_chain_id_type(1, &COSMOS_CHAIN_TYPE);
let src_chain_id = compute_chain_id_type(CHAIN_ID, &COSMOS_CHAIN_TYPE);
let commitment_bytes = vec![0u8; 32];
let commitment_element = Element::from_bytes(&commitment_bytes);

Expand Down
Binary file modified contracts/anchor/tests/cosmwasm_anchor.wasm
Binary file not shown.
4 changes: 1 addition & 3 deletions contracts/anchor/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use arkworks_setups::Curve;
static WASM: &[u8] = include_bytes!("./cosmwasm_anchor.wasm");

const MAX_EDGES: u32 = 2;
const CHAIN_ID: u64 = 1;
const CHAIN_ID: u64 = 3620629146; // chain_id: "cosmos-testnet-14002"
const LEVELS: u32 = 30;
const CW20_ADDRESS: &str = "terra1fex9f78reuwhfsnc8sun6mz8rl9zwqh03fhwf3";
const DEPOSIT_SIZE: u128 = 1_000_000;
Expand All @@ -40,7 +40,6 @@ fn integration_test_instantiate_anchor() {

let msg = InstantiateMsg {
max_edges: MAX_EDGES,
chain_id: CHAIN_ID,
levels: LEVELS,
deposit_size: Uint128::from(DEPOSIT_SIZE),
tokenwrapper_addr: CW20_ADDRESS.to_string(),
Expand All @@ -65,7 +64,6 @@ fn test_deposit_cw20() {
let info = mock_info("anyone", &[]);
let instantiate_msg = InstantiateMsg {
max_edges: MAX_EDGES,
chain_id: CHAIN_ID,
levels: LEVELS,
deposit_size: Uint128::from(DEPOSIT_SIZE),
tokenwrapper_addr: CW20_ADDRESS.to_string(),
Expand Down
4 changes: 2 additions & 2 deletions contracts/mixer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use protocol_cosmwasm::poseidon::Poseidon;
use protocol_cosmwasm::structs::ROOT_HISTORY_SIZE;
use protocol_cosmwasm::zeroes;

// Mixer
/// Mixer
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Mixer {
pub deposit_size: Uint128,
Expand All @@ -19,7 +19,7 @@ pub struct Mixer {
pub merkle_tree: MerkleTree,
}

// MerkleTree
/// MerkleTree
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct MerkleTree {
pub levels: u32,
Expand Down
25 changes: 9 additions & 16 deletions contracts/signature-bridge/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use protocol_cosmwasm::signature_bridge::{
ExecProposalWithSigMsg, ExecuteMsg, InstantiateMsg, QueryMsg, SetResourceWithSigMsg,
StateResponse,
};
use protocol_cosmwasm::utils::{compute_chain_id_type, element_encoder, get_chain_id_type};
use protocol_cosmwasm::utils::{
compute_chain_id, compute_chain_id_type, element_encoder, get_chain_id_type,
};

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

pub const MOCK_CHAIN_ID: u64 = 1;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
Expand Down Expand Up @@ -58,15 +58,15 @@ pub fn instantiate(
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::AdminSetResourceWithSig(msg) => {
admin_set_resource_with_signature(deps, info, msg)
}
ExecuteMsg::ExecProposalWithSig(msg) => exec_proposal_with_signature(deps, info, msg),
ExecuteMsg::ExecProposalWithSig(msg) => exec_proposal_with_signature(deps, env, msg),
}
}

Expand Down Expand Up @@ -134,7 +134,7 @@ fn admin_set_resource_with_signature(

fn exec_proposal_with_signature(
mut deps: DepsMut,
_info: MessageInfo,
env: Env,
msg: ExecProposalWithSigMsg,
) -> Result<Response, ContractError> {
let state = STATE.load(deps.storage)?;
Expand All @@ -154,17 +154,10 @@ fn exec_proposal_with_signature(
let execution_chain_id_type: u64 = get_chain_id_type(&resource_id_bytes[26..32]);

// Verify current chain matches chain ID from resource ID
//
// NOTE:
// This part is prone to future changes since the current implementation
// is based on assumption that the `chain_id` is number.
// In fact, the `chain_id` of Cosmos SDK blockchains is string, not number.
// For example, the `chain_id` of Terra blockchain(mainnet) is `columbus-5`.
// Eventually, it should replace the `MOCK_CHAIN_ID` with `chain_id` obtained
// inside contract(here).
if compute_chain_id_type(MOCK_CHAIN_ID, &COSMOS_CHAIN_TYPE) != execution_chain_id_type {
let chain_id = compute_chain_id(&env.block.chain_id);
if compute_chain_id_type(chain_id.into(), &COSMOS_CHAIN_TYPE) != execution_chain_id_type {
return Err(ContractError::Std(StdError::GenericErr {
msg: "executing on wrong chain".to_string(),
msg: "Executing on wrong chain".to_string(),
}));
}

Expand Down
Loading

0 comments on commit 9bfb00f

Please sign in to comment.