This entry is part 4 of 6 in the series Lập trình smart contract

Trong các bài viết trước, mình đã thực hiện việc codetriển khai hợp đồng thông minh trên Remix - một online text editor cho Solidity. Trong phần này, mình sẽ hướng dẫn việc cài đặt môi trường phát triển hợp đồng thông minh ở máy tính cá nhân.

Tại sao cần phải cài đặt trên máy tính cá nhân?

Các phần trước mình đã đề cập đến những lợi ích của việc sử dụng Remix: không cần cài đặt môi trường, mở trình duyệt lên là code ngay và hỗ trợ triển khai hợp đồng thông minh lên mạng lưới của Ethereum. Vây tại sao chúng ta cần phải cài đặt môi trường một cách cục bộ để làm gì nữa?

Lí do chính để thực hiện việc này là: Chúng ta cần dùng Git để quản lí source code. Ngoài ra, chúng ta cần áp dụng thêm HTML,CSS, Javascript để tạo giao diện giao tiếp với hợp động thông minh.

Text Editor:

Để code thì đầu tiên chúng ta cần có công cụ để code. Mình thì sử dụng text editor có tên là Visual Studio Code. Theo ý kiến cá nhân mình, thì Visual Studio Code là text editor tốt nhất do: miễn phí, cập nhật thường xuyên và có thể cài thêm tiện ích (extension).

 

Trên Visual Studio Code, chúng ta có thể cài thêm extension có tên là solidity để code ngôn ngữ solidity dễ dàng hơn. Để cài đặt extention, bạn click vào biểu tượng hình vuông  hoặc ấn tổ hợp 3 phím Ctrl+Shift+X :

Cài đặt môi trường:

Có nhiều nền tảng có thể dùng để phát triển hợp đồng thông minh. Mình thì chọn NodeJS do nền tảng này có nhiều package hỗ trợ. Nếu chọn NodeJS thì bạn có tí kiến thức về Javascript nhé. Bởi vậy, mấy bạn thấy Javascript thần thánh dã man chưa.

Đầu tiên, chúng ta cần cặt môi trường NodeJS. Nếu bạn nào cài đặt rồi thì phải chắc rằng phiên bản của mình lớn 8.0. Chúng ta có thể kiểm tra bằng cách mở cmd hoặc PowerShell lên và gõ lệnh:

node -v

Cấu trúc project:

Đầu tiên, cần lưu ý rằng nếu bạn đang sử dụng hệ điều hành Windows, bạn phải cài đặt thêm windows-build-tools. Nếu bạn xài hệ điều hành MacOS hoặc Linux thì không cần cài đặt cáy này. Để cài đặt, chúng ta mở PowerShell ở chế độ Administrator và dùng lệnh sau:

npm install --global --production windows-build-tools

Để tiện theo dõi mình đẩy toàn bộ source code lên github tại đây. Nhớ cho mình 1 sao nhé. ?

Ta sẽ tạo cấu trúc project gồm các thư mục và tập tin như sau:

Thư mục Hello sẽ chứa toàn bộ source code.

Thư mục contracts chứa các file code hợp đồng thông minh, có phần mở rộng là .sol.

Thư mục test chứa các file có chức năng kiểm thử cho các file trong thư mục contracts.

Tập tin package.json thì quá quen thuộc với những ai làm NodeJS, file này dùng để ghi lại nhừng package được cài đặt. Để tạo file package.json, ta mở cmd hoặc PowerShell trong thư mục này và tạo file package.json bằng dòng lệnh:

npm init 

Nó sẽ hỏi bạn vài thông tin, cứ nhấn phím Enter vài lần cho qua là được.

Tập tin compile.js có chức năng biên dịch các file trong thực mục constract từ solitity to bytecode.

Tập tin deploy.js có chức năng triển khai các bytecode lên mạng lưới của Ethereum.

Tạo smart contract:

Ta tạo tập tin hello.sol trong thư mục constracts. Nội dung file tương tự như các bài trước:

pragma solidity ^0.4.17;
contract Hello {
    string private message;
    
    function Hello(string mes) public {
        message = mes;
    }
    
    function setMessage(string mes) public {
        message = mes;
    }
 
    function getMessage() public view returns(string) {
        return message;
    }
}

Compile:

Để có thể biên dịch được solidity, ta cần thêm package có tên là solc. Để cài đặt, ta dùng lệnh sau:

npm install solc --save

Tạo file compile.js để biên dịch solidity có nội dung như sau:

const path = require('path');
const fs = require('fs');
const solc = require('solc');

const helloPath = path.resolve(__dirname, 'contracts', 'hello.sol');
const source = fs.readFileSync(helloPath, 'utf8');
const compiledSource = solc.compile(source, 1).contracts[':Hello'];

module.exports = compiledSource;

console.log(compiledSource);

Biến helloPath chứa đường dẫn đến file hello.sol

Biến source chứa kết quả đọc của việc đọc file có đường dần là helloPath, tức là file hello.sol đó.

Biến compiledSource dùng để chứa nội dung biên dịch chứa trong biến source. Ta sử dụng sol.compile() để biên dịch. [':Hello'] là tên của contract ta khai báo trong file hello.sol.

Dòng console.log() được thêm vào chỉ để in ra màn hình cmd xem chúng ta có biên dịch thành công hay không. Để biên dịch, ta chỉ cần dùng lệnh biên dịch của nodeJS như sau:

node ./compile.js

Nếu bạn thấy một tá dòng code như sau thì coi như compile thành công rồi đó:

Giờ thì chúng ta có thể xóa dòng console.log(compiledSource); không cần thiết đi rồi đấy.

Quá trình compile code sẽ tạo 2 cái mới: ABI và Bytecode:

ABI:

ABI là từ viết tắt của Application Binary Interface. API dùng để xác định hàm nào trong hợp đồng thông minh được gọi và trả về dữ liệu theo định dạng mong đợi.

Bytecode:

Bytecode sẽ được dùng để triển khai lên mạng lưới của Ethreum.

Deploy:

Để triển khai hợp đồng thông minh ta cần làm 2 việc: triển khai bytecode lên test network và kết nối ABI với test network:

 

Ta sẽ tiến hành cài đặt thêm mạng lưới kiểm thử  cục bộ (local test network) ở máy tính có tên là Ganache (tên trước đây là TestRPC):

npm install --save ganache-cli

Ta cài thư viện để kết nối ABI với mạng lưới kiểm thử có tên Web3. Điểm hạn chế hiện nay của Web3 là có quá nhiều thay đổi qua mỗi phiên bản. Do đó, để chắc ăn, chúng ta cần chỉ ra cụ thể phiên bản của Web3 muốn cài đặt:

npm install --save web3@1.0.0-beta.26

Unit test:

Để viết unit test, chúng ta sử dụng nền tảng có tên là MochaĐây là một nền tảng dùng để viết unit test bằng Javascript chạy trên NodeJS cực kì phổ biến hiện nay.

Để cài đặt Mocha, ta sử dụng lệnh sau:

npm install --save mocha

Trong thư mục test, ta tạo tập tin có tên là hello.test.js dùng để test cho hợp đồng thông minh trong của hello.sol.

Nội dung của tập tin hello.test.js như sau:

const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');

const provider = ganache.provider();
const web3 = new Web3(provider);

const { interface, bytecode } = require('../compile');

let accounts;
let hello;
const INITIAL_THINGS = 'Hi there!';

beforeEach(async () => {
    // get a list of all accounts
    accounts = await web3.eth.getAccounts();

    hello = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({ data: bytecode, arguments: [INITIAL_THINGS] })
        .send({ from: accounts[0], gas: '1000000' })

    hello.setProvider(provider);
})

describe('Hello', () => {
    it('deploys a contract', () => {
        assert.ok(hello.options.address);
    });

    it ('has a default message', async () => {
        const message = await hello.methods.getMessage().call();
        assert.equal(message, INITIAL_THINGS);
    });

    it('can change the message' , async()=>{
        await hello.methods.setMessage('bye').send({from: accounts[0]});
        const message = await hello.methods.getMessage().call();
        assert.equal(message, 'bye');
    });
})




Trong đoạn code trên, chúng ta có tất cả 3 test case. Mỗi test case sẽ bắt đầu bằng hàm it( )

Hàm beforeEach để được chạy trước mỗi test case. Hàm beforeEach trong đoạn code trên sẽ thực hiện việc deploy contract.

Để bắt đầu chạy test, chúng ta dùng lệnh:

 mocha

Để tránh bị quên lệnh này, chúng ta có thể lưu vào tập tin package.json ở phần script như sau:

{
  "name": "hello",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ganache-cli": "^6.1.0",
    "mocha": "^5.0.4",
    "solc": "^0.4.21",
    "truffle-hdwallet-provider": "0.0.3",
    "web3": "^1.0.0-beta.26"
  }
}

Khi đó, để chạy test case, ta sử dụng lệnh:

npm run test

Nếu cả 3 test case để pass thì có nghĩa là chúng ta đã triển khai thành công trên local test network (ở đây chính là Ganache). Toàn bộ quá trình triển khai mô tả bằng ảnh sau:

Triển khai lên mạng lưới Ethereum:

Để triển khai lên mạng lưới của Ethereum, ta cần thay đổi một số thứ:

  • Sử dụng Provider của tài khoản ví thay cho Ganache Provider.
  • Sử dụng API có tên là Infura để kết nối đến mạng lưới Ethereum. Về bản chất thì Unfura API sẽ có một điểm xử lí trên mạng lưới của Ethereum. Chúng ta chỉ cần thông qua API của Infura cung cấp để triển khai thông qua điểm xử lí của Infura. Nếu không sử dụng Infura, chúng ta phải dùng máy tính cá nhân tham gia vào mạng lưới để trở thành một điểm xử lí trên mạng lưới. Điều này sẽ tốn khá nhiều thời gian và công sức.

Toàn bộ quá trình triển khai sẽ được đổi lại như sau:

Bước 1: Đăng kí Infura:

Để đăng kí tài khoản Infura ta truy cập vào địa chỉ https://infura.io/signup

Sau khi hoàn tất đăng kí, bạn sẽ nhận được email chứa thông tin có dạng URL dùng để deploy lên các mạng lưới khác nhau của Ethreum:

Phần chữ cuối cùng của đường dẫn chính là token dùng để xác thực tài khoản nên tuyệt đối đừng cho ai biết hết nhé.

Bước 2: Tạo provider của tài khoản ví:

Chúng ta cần cài đặt thêm 1 package có tên là truffle-hdwallet-provider. Chúng ta cần chỉ định phiên bản là 0.0.3, phiên bản mới nhất là 0.0.5 đang bị lỗi. Để cài đặt, ta sử dụng lệnh:

npm install --save truffle-hdwallet-provider@0.0.3

Bước 3: Tạo file delpoy.js:

File deploy.js có chức năng là sử dụng provider mà chúng ta cài đặt ở bước 2 và thông quan Unfura để deploy lên mạng lưới Ethereum.

Nội dung của file deploy.js như sau:

const HDWalletProvider = require('truffle-hdwallet-provider');
const Web3 = require('web3');
const { interface, bytecode } = require('./compile');

const provider = new HDWalletProvider(
    process.env.MNEMONIC,
    process.env.INFURA_URL
);

const web3 = new Web3(provider);

const deploy = async () => {
    const accounts = await web3.eth.getAccounts();
    console.log('Attemping to deploy from account', accounts[0]);

    const result = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({ data: bytecode, arguments: ['Hi there!'] })
        .send({ gas: '1000000', from: accounts[0] });
        
    console.log(interface);
    console.log('Contract deployed to ', result.options.address);
};

deploy();

Khi khai báo const provider = new HDWalletProvider( ..... ) , vì lí do bảo mật nên mình sử biến môi trường của NodeJS để lưu trữ Mnemonic và Infura Url.

Để thiết lập giá trị biến môi trường, đối với hệ điều hành Windows, ta cần sử dụng lệnh tương như sau trên PowerShell (Nhớ thay đổi lại giá trị của các biến lại cho đúng với thực tế đấy nhé):

$env:MNEMONIC="This is my Mnemonic"
$env:INFURA_URL="https://rinkeby.infura.io/yourtoken"

Ta tiến hành thực thi file deploy.js bằng lệnh bên dưới, Quá trình thực thi có thể khá lâu

node ./deploy.js

Khi deploy thành công, bạn sẽ nhận được địa chỉ mà hợp đồng thông minh được triển khai, ví dụ: "Contract deployed to 0x42873EF29A531dbcB6Bef4cF51A1bC9Fc6A45e80"

Để xác nhận lại việc triển khai lên mạng lưới chính thành công hay không, ta truy vào rinkeby.etherscan.io sau đó dùng địa chỉ mà hợp đồng minh được deploy để kiểm tra:

Kiểm tra thử:

Hiện tại chúng ta chưa xây dựng giao diện để sử dụng hợp đồng thông minh mà chúng ta vừa mới triển khai. Do đó, chúng ta đành phải sử dụng lại Remix để kiểm tra lại. ?

Remix có một chức năng rất là hay đó là có thể load smart constract ở bất kì địa chỉ nào.

Bên tab Run, hãy chắc rằng bạn chọn Injected Web3 cho Environment:

Còn phần Account bạn chọn phải tài khoản nào có ether đấy nhé( Cách nhận ether miễn phí như thế nào, hãy xem lại bài hướng dẫn hôm trước nhé):

Để load được hợp đồng thông minh đã triển khai, chúng ta sao chép địa chỉ deploy và dán vào textbox "Load contract from Address" và nhấn váo nút màu tìm "At Address":

Ok, giờ chúng ta sử dụng được hợp đồng thông minh đã triển khai trên mạng lưới Ethereum rồi đấy. Lưu ý, như đã đề cập trong bài trước, dữ liệu thay đổi sẽ không cập nhật liền do phải chờ mạng lưới Ethereum xác nhận giao dịch, quá trình này có thể tốn khoản 1 phút.

Kết:

Thay vì sử dụng Remix, chúng ta có thể cài đặt môi trường phát triển ở máy tính cá nhân.

Một trong những kiến trúc dự án có thể áp dụng Solidity đó là áp dụng nến tảng NodeJS.

Trong bài viết này, bạn thấy chúng ta phải sử dụng giao diện có sẵn của Remix để giao tiếp với hợp đồng minh. Nếu sản phẩm cần đưa ra thị trường thì chúng ta không thể bắt người dùng sử dụng Remix để load hợp đồng thông minh được. Do đó, trong bài viết tiếp theo, chúng ta sẽ đi xây dựng một giao diện ứng dụng web có khả năng sử dụng hợp đồng thông minh.

Series Navigation<< Triển khai smart contract lên mạng lưới của EthereumXây dựng Front End tương tác với smart contract >>