# Hello, Zinc! ### Intro Zinc 是一套程式語言用來開發 1. Smart contract on zkSync. 2. General-purpose zero-knowledge proof circuits. 有鑒於現存的 ZKP frameworks 缺乏支持 smart contract 的特定功能。以及近期的新興 smart contract 語言,例如 Simplicity or Libra's Move,其設計上比起開發可讀性,更重視 safety 以及程式本身的正規可驗證性。 >The goal of Zinc is to make writing safe zero-knowledge circuits and ZKP-based smart contracts easy. ### Zinc 特點 - Type safety * 字串跟整數無法交互運算 - Type inference * 根據數值直接決定型別 - Immutability - Movable resources as a first-class citizen - Module definition and import - Expressive syntax - Industrial-grade compiler optimizations - Flat learning curve for Rust/JS/Solidity/C++ developers - **Turing incompleteness: no recursion or unbounded looping** ### 語法使用 以下使用的版本為 zinc 0.2.3 1. const and variable ``` let x = 5566; x = 2266; // compile error! let mut y = 5566; y = 2266; // ok! ``` 2. types - Unit `()` * 類似 void * 沒辦法做運算也不能被 cast ``` fn check(value: bool) { // several statements }; let y = check(true); // y is () ``` - Boolean: true/false, 0/1 * 基於 type safety,不能 cast 為 integer 做運算 - Integer * u8 .. u248: unsigned integers * 8 的倍數 * i8 .. i248: signed integers * 8 的倍數 * field: the native field integer. * It represents an unsigned integer of bitlength equal to the field modulus length (e.g. for BN256 the field modulus length is 254 bit). * decimal: 0, 1, 122, 574839572494237242 * hexadecimal: 0x0, 0xfa, 0x0001, 0x1fffDEADffffffffffBEEFffff * Casting: only between integer <-> field. If the value does not fit into the target type, it is truncated. * If the literal type is not specified, the minimal possible bitlength is inferred. ``` let a = 0; // u8 let a: i24 = 0; // i24 let b = 256; // u16 let c = -1; // i8 let c = -129; // i16 let d = 0xff as field; // field let e: field = 0; // field ``` - Array ``` let mut fibbonaci = [0, 1, 1, 2, 3, 5, 8, 13]; let element = fibbonaci[3]; fibbonaci[2] = 1; ``` * bit array: ``` let preimage_bits: [bool; 156] ``` - Tuple ``` let mut tuple: (u8, field) = (0xff, 0 as field); tuple.0 = 42; dbg!("{}", tuple.1); ``` - Struct ``` struct Arithmetic { x: field, y: field, } impl Arithmetic { pub fn add(self) -> field { self.a + self.b } } fn main() { let arithmetic = Arithmetic { a: 10 as field, b: 5 as field }; } ``` - Enum ``` enum Prize { Gold = 1, Silver = 2, } impl Prize { pub fn first() -> Self { Self::Gold } pub fn second() -> Self { Self::Silver } } fn main(score: field) -> field { let x = Prize::Gold; List::first() as field * score } ``` - String * 使用非常受限,目前只能用在 debug or require message ``` dbg!("{}", 42); // format string require(true != false, "a very obvious fact"); // optional error message ``` - Map ``` use std::collections::MTreeMap; contract Test { data: MTreeMap<u8, field>; pub fn new(owner: u160) -> Self { Self { data: MTreeMap, } } pub fn example(mut self) { let (old1, existed1) = self.data.insert(42, 9 as field); let (value, exists1) = self.data.get(42); let exists2 = self.data.contains(42); let (old2, existed2) = self.data.remove(42); } } ``` - Type alias ``` type ComplexType = [(u8, [bool; 8], field); 16]; ``` 3. Fucntion - public function: with `pub` keyword ``` impl Data { pub fn sum(self) -> u8 { self.a + self.b + self.c + self.d } } ``` - const function * Called at compile-time, thus they may only accept and return constant expressions. * Useful when you need to use a lot of similar parameterized values. ``` const fn cube(x: u64) -> u64 { x * x * x } fn main() { let cubed_ten = cube(10 as u64); // 1000 let cubed_twenty = cube(20 as u64); // 8000 } ``` 4. Conditions - If ``` let x = 1; let c = if x == 1 { let a = 5; a } else if x == 2 { let b = 10; b }; ``` - match * constant (e.g. 42) * path (e.g. MyEnum::ValueOne) * variable binding (e.g. value) * wildcard (_) ``` const SCRUTINEE: u8 = 42; const MATCH: u8 = match SCRUTINEE { 0 => 10, 1 => 20, 42 => 100, VALUE => 255 - VALUE, }; ``` 5. Control - for-while loop ``` for {identifier} in {range} [while {expression}] { ... } ``` ``` let x = 7; for i in 0..10 while i % x != 2 { // do something }; ``` 6. Debugging Use special function `dbg!` ``` // a = 5, b = 3 fn print_sum(a: u8, b: u8) { dbg!("{} + {} = {}", a, b, a + b); // prints '5 + 3 = 8' } ``` 7. Testing - Unit tests are just simple functions marked with the `#[test]` - `#[should_panic]` such test must fail in order to succeed, e.g. by passing a false value to the require function or causing an overflow. - `#[ignore]` such test is just ignored. ``` #[test] fn ordinar() { require(2 + 2 == 4, "The laws of the Universe have been broken"); } #[test] #[should_panic] fn panicking() { require(2 + 2 == 5, "And it's okay"); } #[test] #[ignore] fn ignored() { require(2 + 2 > 4, "So we'll just ignore it"); } ``` 8. Smart contract - Implicit storage fields * contract address: u160, ex: 0x224a0b11b36156c7fa4c4d784a8b85c93978e424 * the contract balances: Map<u160, u248>, <address, amount> * Ref: https://rinkeby.zkscan.io/explorer/accounts/0x224a0b11b36156c7fa4c4d784a8b85c93978e424 - Explicit storage fields * 每一個 smart contract 的 storage 都會被 Zinc Zandbox server 寫入 persistent databases. ``` contract Example { pub tokens: (u8, u64); data: [u8; 1000]; //... } ``` - Constructor ``` contract Example { pub value: u64; pub fn new(_value: u64) -> Self { Self { value: _value, } } } ``` - Public methods * The contract must have at least one public function. ``` contract Example { //... pub fn deposit(mut self, amount: u64) -> bool { ... } } ``` - Private methods ``` contract Example { //... fn get_balance(address: u160) -> bool { ... } } ``` - Builtin methods * transfer * fetch * dbg * require - Global variables * zksync::msg ``` zksync::msg:sender //u160 zksync::msg:recipient //u160 zksync::msg:token_address //u160 zksync::msg:amount //u160 ``` - Constants ``` contract Example { //... pub const VERSION: u8 = 1; // public constant const LIMIT: u8 = 255; // private constant } ``` ### Hello, World! #### Install zargo 下載對應的版本,解壓縮之後會有三個 bin: zargo, znc, zvm 實際上開發只需要 zargo https://github.com/matter-labs/zinc/releases/tag/0.2.3 > macos 第一次執行會被 Security&Privacy 擋住,需要先 approve #### Create project ``` $ zargo new --type contract constant_price ``` ``` //! //! The 'constant_price' contract entry. //! contract ConstantPrice { pub value: u64; pub fn new(value: u64) -> Self { Self { value: value, } } } ``` #### Try to code something - removing the redundant value auto-generated field - adding the fee parameter - adding two mutable methods for making exchanges and deposits - adding an immutable method for getting the contract fee value - declaring the Address and Balance type aliases - declaring the TokenAddress enumeration type with zkSync token addresses Ref: https://zinc.zksync.io/07-smart-contracts/02-minimal-example.html #### Compile ``` $ zargo build ``` 會產生 - private_key: 用來部署 smart contract - ./data/input.json: 部署 contract 的初始化參數 #### 部署到 Zinc testnet 1. Create zkSync account zkSync on rinkeby: https://rinkeby.zksync.io/ Mint tokens on rinkeby: https://mint.zksync.dev/mint > NOTE: unlock account by transfering at first time 2. Publish contract to rinkeby ``` $ zargo publish --network rinkeby --instance default ``` Response: ``` Compiling hello_swap v0.1.0 Finished release [optimized] target Uploading the instance `default` of `hello_swap v0.1.0` to network `rinkeby` Address 0x9035a2b7cf9365e879ec26415d97a954cd68955f Account ID 190788 ``` #### Query contract storage ``` $ zargo query --network rinkeby --address 0x9035a2b7cf9365e879ec26415d97a954cd68955f ``` Response: ``` { "address": "0x9035a2b7cf9365e879ec26415d97a954cd68955f", "balances": [ { "key": "0x0", "value": "64000000000000" } ], "fee": "100" } ``` #### Calling a non-mutable contract method ``` $ zargo query --network rinkeby --address 0x9035a2b7cf9365e879ec26415d97a954cd68955f --method get_fee ``` Response: ``` { "output": "100" } ``` #### Calling a mutable contract method 修改 ./data/input.json ``` { "msg": { "sender": "0x009a0256ca6b16455f5b3a46b7aee448f929a45b", "recipient": "0x9035a2b7cf9365e879ec26415d97a954cd68955f", "token_address": "0xeb8f08a975ab53e34d8a0330e0d34de942c95926", // USDC "amount": "1_E6" } } ``` Request: ``` $ zargo call --network rinkeby --address 0x9035a2b7cf9365e879ec26415d97a954cd68955f --method deposit ``` Response: ``` { "output": { "result": null, "root_hash": "0x0" } } ``` Query storage again: ``` { "address": "0x9035a2b7cf9365e879ec26415d97a954cd68955f", "balances": [ { "key": "0x0", "value": "64000000000000" }, { "key": "0xeb8f08a975ab53e34d8a0330e0d34de942c95926", "value": "1000000" } ], "fee": "100" } ``` #### Calling a `exchange` method ``` $ zargo call --network rinkeby --address 0x9035a2b7cf9365e879ec26415d97a954cd68955f --method exchange ``` 實際上會產生出 2 筆 tx Ref: https://rinkeby.zkscan.io/explorer/accounts/0x009a0256ca6b16455f5b3a46b7aee448f929a45b ### Discussion 1. 如何做合約升級? proxy contract? 2. 無限 fetch 另一個 contract? ### Reference - https://github.com/matter-labs/zinc - zkSync wallet: https://wallet.zksync.io/ - mint Token: https://mint.zksync.dev/ - zinc v0.2.3: https://zinc.zksync.io/index.html - zinc v0.1.5: https://zinc.matterlabs.dev/09-zargo-circuit-manager/00-overview.html - zkSync 1.x rinkeby: https://rinkeby.zkscan.io/ - zkSync 2.0 rinkeby: https://zksync2-alpha.zkscan.io/explorer/ - EthCC: ZKSYNC — FIRST EVM-COMPATIBLE ZKROLLUP, Alex Gluchowski @ July 21st 11:00 am * https://ethcc.io/agenda - 更多期待 https://medium.com/matter-labs/zksync-2-0-hello-ethereum-ca48588de179