[CosmWasm] CosmWasm 실제 코드로 분석 - 3

iguigu·2022년 6월 13일
0

CosmWasm

목록 보기
3/3

cw-plus

  • Production level의 cosmwasm 컨트랙트 분석
  • 공식 github 링크

프로젝트 구조

  • src
    • contract.rs : 컨트랙트의 로직을 포함
    • lib.rs : 컨트랙트에 생성된 .rs 파일들 저장
    • msg.rs : 컨트랙트가 받아들이는 메세지 정의
    • state.rs : 컨트랙트가 저장하고 있는 상태 정의
    • error.rs : 기본적으로 다양한 std error들이 지원되며 커스텀 에러까지 생성 가능
  • schema : 스마트 컨트랙트와 인터랙트하는 메세지 형태를 보여 줌
  • example/schema : schema.rs 에서 schema.json 들을 자동적으로 생성해 줌

컨트랙트 설계시 주의사항

  • 컨트랙트로 해선 안되는 것들
    • 랜덤 값 생성 : deterministic한 블록체인의 특성상 완전 랜덤한 값은 생성불가. pseudo-random한 값을 생성하는 다양한 기법에 대한 조사 필요
    • 일시적인 값 저장 : 서비스에서 일시적으로 사용되는 (아바타의 옷 색깔, 다크/라이트 모드 등) 값들은 스마트 컨트랙트로 처리하는 것은 비효율적임. IPFS 같은 서비스를 써서 off-chain에서 분산화된 형태로 이런 값들을 저장하는 것이 맞음
    • 복잡한 연산 : 모든 연산은 블록타임 안에 완료되야 함. 복잡한 연산이 필요할 경우엔 다른 방법을 찾아야함
  • 컨트랙트가 지원하는 것
    • immutability
    • seizure resistance
    • censorship resistance
    • auditability

CosmWasm Library

  • CosmWasm 기반 컨트랙트를 만들기 위해 필요한 다양한 라이브러리들 포함
  • 자세한 내용은 코드, API docs 확인

cosmwasm_std::Addr

  • Addr은 어드레스의 유효성 검사 등과 같은 유용한 기능을 제공하는 wrapper임
let checked : Addr = deps.api.addr_validate(input)?
  • 어드레스는 bech32 형태로 인코딩 되어있음. 하지만 multi-chain 스마트 컨트랙트에서는 UTF-8 인코딩이나 합리적인 길이가 아닌 것은 고려되어선 안됨
  • CosmWasm 컨트랙트는 HumanAddr (human-readable), CanonicalAddr (optional)을 사용

cosmwasm_std:Coin

  • Coin constructor
pub struct Coin{
    pub denom : String,
    pub amount : Uint128,
}

let tip = vec![
    coin(123,"ucosm"),
    coin(24,"ustake"),
];

let mut response: Response = Default::default();
response.messages = vec![SubMsg::new(BankMsg::Send{
    to_address: info.sender.into(),
    amount: tip,
})]

cosmwasm_storage::Singleton

  • 일반적인 singleton 패턴: 클래스가 최초에 메모리를 static하게 할당하고, 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴
  • Singleton은 singleton storage key로 동작하는 TypedStorage와 함께 PrefixedStorag와 효과적으로 동작함
  • 주어진 name에서 to_length_prefixed transformation을 collision없이 동작하고 표준 TypedStorage를 key를 요구하지 않으면서 제공함

cw_storage_plus::Item

  • state storage에서 단일 리소스를 저장하는 아이템
  • 단순한 state나 컨트랙트 셋팅과 같은 singleton이 사용되어지는 곳에 적절함
pub const STATE: Item<State> = Item::new(b"state")  // state는 key에 해당하며 b는 바이너리 의미

cw_storage_plus::Map

  • Map 은 Bucket 보다 robust한 key-value 시스템
  • 이더리움과 달리 Map은 iterate하는 것이 가능. 즉, Map안의 아이템들은 list out되거나 process되는 것이 가능
  • tuple이 composite key에 사용 가능. IndexedMap은 MultiIndex 기능을 사용하는 데 사용 됨

Contract 분석

Airdrop Contract

state.rs

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
    pub admin: String,
    pub denom: String,
}

pub const CONFIG: Item<Config> = Item::new("config");
pub const LATEST_STAGE: Item<u8> = Item::new("latest_stage");

pub const MERKLE_ROOT: Map<&[u8], String> = Map::new("merkle_root");
pub const CLAIM_INDEX: Map<(&[u8], &[u8]), bool> = Map::new("claim_index");
  • Config
  • LATEST_STAGE
  • MERKLE_ROOT
  • CLAIM_INDEX

msg.rs

pub struct InstantiateMsg {
    pub admin: String,
    pub denom: String,
}
  • contract의 admin과 token의 denom을 생성하는 메시지
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    UpdateConfig {
        admin: Option<String>,
    },
    UpdateMerkleRoot {
        stage: u8,
        merkle_root: String,
    },
    RegisterMerkleRoot {
        merkle_root: String,
    },
    Claim {
        stage: u8,
        amount: Uint128,
        proof: Vec<String>,
    },
    CreateVestingAccount {
        recipient: String,
        periods: Vec<(i64, String)>,
    },
}
  • Config의 admin을 업데이트하거나, 각 storage들 업데이트 하는 executeMsg들
  • ExecuteMsg가 실행되 그에 따른 Custom Response가 존재함
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    Config {},
    MerkleRoot { stage: u8 },
    LatestStage {},
    IsClaimed { stage: u8, address: String },
}
  • 컨트랙트의 storage에 저장된 값들을 알기 위한 메시지
profile
2929

0개의 댓글