Contract ABI là gì
Đang trong lúc ngâm cứu về Solidity, Ethereum, cũng nhân tiện mấy vụ đất đai đang hot trên mạng, mình nảy sinh ý tưởng bá chủ toàn cầu, nghĩ ra game đơn giản gọi là Crypto World, thích thằng nào là mình mua luôn. Trong tutorial này mình sẽ trình bày 1 cách đơn giản triển khai từ ý tưởng đến lúc deploy hoàn thành. Show
Cái game hoàn thành sẽ trông như thế này (Do làm trong mấy ngày để ra Tut nên còn đơn giản) Mình hi vọng Tutorial này sẽ giúp ta có cái nhìn tổng quan để tạo 1 DApp hoàn chỉnh trên nền Ethereum Network, do là Tutorial nên đối tượng là người đã có chút hiểu biết về Ethereum và Blockchain ở các mức khái niệm, vì mình sẽ không giải thích lại nữa, đầy rẫy trên internet rồi. Các công nghệ sẽ được sử dụng trong tutorial này:
Các bước để hoàn thành series này:
1. SetupÝ tưởng Game Crypto World:
Ý tưởng ban đầu khá phức tạp, vậy nên mình sẽ phải thực hiện từ mức đơn giản là mua chiếm vùng đất trược đã(phạm vi Tutorial này) Đảm bảo rằng môi trường trước hết cần có Node.js Install trufle framework với npm Chi tiết hơn, có thể đọc tại đây Tạo thư mục chính của Project cd /your-dev/path mkdir cryptoworld cd /your-dev/path/cryptoworld Có 2 cách để tạo ra Smart contract Project, from scratch hoặc template
Từ template gọi là các truffle box, ở đây mình dùng cách này cho đơn giản, vì mình thấy series pet-shop này cũng tương tự Cấu trúc thư mục1 project Truffle sẽ có các thư mục như sau:
Sau khi clone template xong thì có những task cơ bản như sau:
Chạy thử template với lite-serverInstall lại các dependencies cần thiết Khởi động lite-server Truy cập localhost:3000 để xem có gì xẩy ra Tạo bản đồ chạy được từ thư viện JsmapsTruy cập vào localhost:3000, bạn sẽ thấy đấy hoàn toàn là template, vậy cần phải sửa lại nó để hiển thị bản đồ như ý muốn của ta bằng cách download library từ jsmaps rồi nhập vào. Do phần này liên quan đến kỹ năng web cơ bản nên mình ko muốn trình bày dài, có thể tham khảo ở repo của mình https://github.com/kienphan/cryptoworld 2. Code Smart Contract với SolidityTrước tiên xoá những smart contract thừa thãi từ template đi hết. Để đơn giản trước tiên mình thiết kế 1 smart contract duy nhất, gọi là KuniLord.sol, mình định nghĩa 1 vùng đất là 1 Kuni. Nếu xem smart contract tương ứng như khái niệm Class trông OOP, thì việc thiết kế smart contract cũng gần tượng tự như thiết kế Class vậy. pragma solidity ^0.4.20; contract KuniLord { // TODO }Thiết lập các thuộc tính là thông tin của Smart Contractpragma solidity ^0.4.20; contract KuniLord { uint private constant INIT_BUY_PRICE = 1000; address public owner; struct Lord { address addr; bytes32 nickname; uint totalSpending; } struct Kuni { bytes32 name; Lord lord; uint currentBuyPrice; } mapping (address => Lord) lords; mapping (bytes32 => Kuni) kunies; Kuni[] public occupiedKunies; Lord[] public occupingLords; }Giải thích khái quát về các thông tin
Constructor khi khởi tạo contractconstructor() public { owner = msg.sender; }Set Nickname và get Nickname người chơifunction setNickname(bytes32 _nickname) public payable { lords[msg.sender].addr = msg.sender; lords[msg.sender].nickname = _nickname; emit SetNickname(msg.sender, _nickname); } function getNickname() public view returns (bytes32) { return lords[msg.sender].nickname; }Kiểm tra 1 Kuni đã bị chiếm đóng và lưu trên blockchain hay chưafunction checkKuniExist(bytes32 _name) internal view returns(bool){ for (uint i = 0; i< occupiedKunies.length; i++) { if (occupiedKunies[i].name == _name) { return true; } } return false; }Lấy thông tin của 1 KuniBao gồm việc Kuni đang bị ai chiếm đóng, thằng đấy địa chỉ ở đâu, giá hiện tại là bao nhiêu function getKuniInfo(bytes32 kuni) public view returns(bytes32 name, address lordAddr, bytes32 lordName, uint currentBuyPrice) { require(kuni != ""); if (checkKuniExist(kuni)) { return (kuni, kunies[kuni].lord.addr, kunies[kuni].lord.nickname, kunies[kuni].currentBuyPrice); } return (kuni, 0, 0, kunies[kuni].currentBuyPrice); }Kiếm tra 1 Lord đã tồn tại chưafunction checkLordExist(address lordAddr) internal view returns(bool) { for(uint i = 0; i < occupingLords.length; i++) { if (occupingLords[i].addr == lordAddr) { return true; } } return false; }Cuối cùng hàm quan trọng nhất là hàm mua Kunifunction occupy(bytes32 nation) public payable { require(nation != ""); if (checkKuniExist(nation)) { require(msg.value > kunies[nation].currentBuyPrice); kunies[nation].currentBuyPrice = msg.value; kunies[nation].lord = lords[msg.sender]; } else { require(msg.value > INIT_BUY_PRICE); Kuni memory newKuni = Kuni({ name: nation, currentBuyPrice: msg.value, lord: lords[msg.sender] }); kunies[nation] = newKuni; occupiedKunies.push(newKuni); } lords[msg.sender].totalSpending += msg.value; msg.sender.transfer(msg.value); if (!checkLordExist(lords[msg.sender].addr)) { occupingLords.push(lords[msg.sender]); } emit OccupyKuni(msg.sender, nation, msg.value); }Smart Contract hoàn chỉnh có thể tham khảo ở đây https://github.com/kienphan/cryptoworld/blob/master/contracts/KuniLord.sol Việc viết smart contract đã xong, giờ có 2 cách để test:
Để đơn giản mình sẽ dùng Remix IDE để test nhanh các funtion và deploy luôn. Thực tế thì smart contract phải luôn được test kỹ lưỡng mới có thể triển khai được, do đặc thù của blockchain là không thể sửa đổi. Để có thể tiếp tục, chúng ta phải cài metamask.io, là tool để tương tác với ví Ethereum của bạn với smart contract. Phần cài đặt và khởi tạo Metamask, mình sẽ bỏ qua. Do Metamask có tạo cả account trên mainnet nên hãy nhớ bảo mật tài khoản của mình nếu có sử dụng sau này. Giao diện Remix IDE sẽ trông như thế này Chắc chắn rằng trên metamask đã chuyển sang dùng Ropsten Testnet Bạn có thể đến trang https://faucet.metamask.io/ để request free 1 ETH trên testnet, như mình cũng có khoảng 8 ETH, tầm 4k8 $ Trump, làm cá con được rồi. :D Trên Remix IDE chắc chắn là đã chọn môi trường là Injected web3 để nhúng blockchain từ Metamask vào Trên Remix, tab Run, click Deploy, để Deploy smart contract lên blockchain, ta sẽ thấy popup metamask bật lên. Thời gian deploy có thể nhanh hoặc chậm tuỳ vào tình hình network, phí GAS bạn trả là nhiều hay ít. Contract instance khi deploy xong sẽ có thể thấy như thế này Ta có thể điền tham số, bytes32, string vào để thử test các returns của các function đã chạy đúng chưa 3. Frontend App với HTML&CSSDo phần đầu mình đã nêu rút gọn phần tạo bản đồ, cho nên phần này hợp với phần đầu tiên luôn Kết nối phần ứng dụng với blockchainTa có thể sử dụng truffle để deploy contract và lấy thông tin, nhưng như thế thì đồng nghĩa ta phải tạo ra 1 node, và phải đồng bộ mạng Blockchain về node (phải hơn chục GB và 2-3h sync), nên ở đây ta sẽ lựa chọn deploy contract bằng Remix IDE Từ Tab Compile trên Remix IDE, click Detail để thấy chi tiết về contract, ta copy ABI Paste ra 1 file JSON, gọi là KuniLord.json, mình để nó ở cùng cấp với index.html Trong /js/app.js, định nghĩa cái ABI này cùng với contract address mà ta đã deploy $.getJSON('KuniLord.json', function(data) { // Get the necessary contract artifact file and instantiate it with truffle-contract var kuniLordArtifact = data; var kuniLordContract = web3.eth.contract(kuniLordArtifact); // Cái address phía dưới là địa chỉ mà ta đã deploy App.contracts.kuniLordInstance = kuniLordContract.at("0x03d769b962efbf5f4844c161c809c41edaf3ab57") ... } Vậy là dù trên localhost:3000, thì app của bạn cũng đã trực tiếp thao tác trên blockchain rồi đấy. 4. Deploy ứng dụng lên IPFSỨng dụng đã hoàn thành, bây giờ phải làm cho nó online với tất cả mọi người, mà lại chẳng muốn tốn thêm chi phí :’( Với static web thì ta có thể nghĩ ngày đến IPFS IPFS là gì? Định nghĩa từ trang chủ
Vắn tắt, 1 tool xây dựng hệ thống web phân tán, giúp web bạn tồn tại vĩnh viễn, máy chủ của bạn die, web bạn vẫn sống. Chi tiết và Cài đặt liên quan, tham khảo tại https://ipfs.io/docs/install/ Sau khi cài đặt, cần khởi động ipfs để tạo ra 1 node trong mạng lưới Trên terminal khác, có thể chạy lệnh sau để xác nhận xem hiện tại có thể có bao nhiêu peers sẽ share content mà mình sẽ deploy Vì tất cả source code của mình đặt trong thư mục /src nên mình sẽ add /src này lên IPFS This will add your dist folder to the network. You’ll see a long hash that’s been generated for you. The last hash is a unique identifier for that folder: Nó sẽ add src lên mạng lưới với rất nhiều chuỗi Hash. Chuỗi hash cuối cùng là định danh thư mục src của bạn. added Qmb3fJpXVGvUnNeRLC3P5sTXMzjpf5zq4tKt9XjhtYFf1k src/fonts added QmZP2xGY12cC2h7ef4L3K4UdgysUxbhqQwRedSaX9gX4cn src/images added QmaRjCe3twqVdVHWgBiPBDAvPKfRPCwwy2d9bNgjDgAifr src/js added QmRyNG9gMfKZUy6HjTJthyQgqpQ7M7YR56DYhn7BtMP8gC src/jsmaps added QmfU5NcL3To1k7MVKvf2uswhpbpF8FGyeo6h5JrGZu4qHP src/maps added QmZVYrxQZgcVqy6kkq7s547kov3RMQ9fXRaHtrJAbd9rvi src Copy hash cuối cùng kia và thực hiện ipfs name publish QmZVYrxQZgcVqy6kkq7s547kov3RMQ9fXRaHtrJAbd9rvi Ta sẽ nhận được kết quả trả về trên Terminal như sau: Published to QmY2E2Yb3dNSkToo7VjooSfDUsnZRqaC9NYAgkiH8G22zG: /ipfs/QmZVYrxQZgcVqy6kkq7s547kov3RMQ9fXRaHtrJAbd9rvi Say oh yeah, content của bạn đã lên sóng. Có thể truy cập để dùng ngay và luôn https://gateway.ipfs.io/ipns/QmY2E2Yb3dNSkToo7VjooSfDUsnZRqaC9NYAgkiH8G22zG/ Khi có sự thay đổi ở thư mục src thì ta sẽ cần làm các thao tác ipfs add -r src/ và ipfs name publish ... lại từ đầu. Có thể kiếm tra số lượng peers đang kết nối tại http://localhost:5001/ipfs/QmQLXHs7K98JNQdWrBB2cQLJahPhmupbDjRuH1b9ibmwVa/#/connections 5. Dùng thử DApp xem thế nào
Written on March 5, 2018 |