Ethereum là gì? Tìm hiểu cơ bản về Blockchain (P1)
Kì trước, tôi đã có bài viết Solidity cơ bản. Nó được thiết kế để dùng cho Ethereum Virtual machine (EVM). Trong bài này, chúng ta sẽ đi sâu hơn tìm hiểu Ethereum là gì và dùng test environment cho contract của mình. Từ đó chúng ta sẽ hiểu được cách thức Ethereum hoạt động như thế nào.
Ethereum là gì?
Ethereum (ETH) là một loại cryptocurrency được xây dựng vào năm 2013 bởi Vitalik Buterin, thường được gọi là Bitcoin 2.0. Đây không chỉ là một đồng tiền tệ mà nó còn là nền tảng tạo ra nhiều ứng dụng khác thông qua ngôn ngữ lập trình của mình.
Nó hoạt động trên một Blockchain tương tự như Bitcoin, chúng ta có thể khai thác ETH thông qua đơn vị tiền tệ Ether.
Ethereum còn là một nền tảng ứng dụng hữu ích và đã tạo ra được một hệ sinh thái tài chính phân tán cho riêng mình.
Lí do Ethereum ra đời là dựa trên ý tưởng Vitalik Buterin muốn khắc phục những nhược điểm của Bitcoin như phí thanh toán, thời gian thanh toán chậm và khuyến khích khai thác thông qua các mining-pool thay vì khai thác riêng lẻ như Bitcoin.
>>> Xem thêm Blockchain là gì?
Các tool
Trong bài này, chúng ta sử dụng truffle
và ganache-cli
. Truffle
là một framework của lập trình Ethereum, cho phép tạo test environment, viết test cho contract và nhiều thứ khác nữa. Bài viết này chúng ta chỉ dùng nó để tạo test environment.
ganache-cli
sẽ được dùng kết hợp với truffle để tạo ra một test environment đầy đủ tính năng. Phần chúng ta chuẩn bị làm sẽ không thật sự giống với blockchain trên thực tế, nhưng sẽ phần nào hình dung được cách thức hoạt động của nó.
Install các tool
- truffle:
[sudo] npm install -g truffle
- ganache-cli:
[sudo] npm install -g ganache-cli
Chuẩn bị
Đầu tiên, chúng ta cần mở một truffle project. Để làm được, tạo một directory mới, và trong nó, hãy run truffle init
.
Bây giờ bạn sẽ có khá nhiều folder. Mở truffle-config.js
và dán đoạn này vào:
module.exports = { // See <http://truffleframework.com/docs/advanced/configuration> // to customize your Truffle configuration! networks: { development: { host: '127.0.0.1', port: 7545, network_id: '*' } } }
Đối với người Windows, bạn có thể remove file truffle.js
để tránh conflict. Đối với những hệ điều hành khác, bạn có thể giữ lại cả hai, và bỏ đoạn code này vào truffle.js, hoặc cứ làm như người dùng Windows không sao cả.
File này thể hiện rằng development network của chúng ta sẽ chạy trên localhost:7545.
Tiếp theo, trong folder contracts
, tạo một file DeveloperFactory.sol
. Đây cũng là chỗ chúng ta sẽ viết contract của mình. Hãy bỏ đoạn code này vào:
pragma solidity ^0.4.18; contract DeveloperFactory { // Let's create a Developer! event NewDeveloper(uint devId, string name, uint age); uint maxAge = 100; uint minAge = 5; struct Developer { string name; uint id; uint age; } Developer[] public developers; mapping (uint => address) public devToOwner; mapping (address => uint) public ownerDevCount; function _createDeveloper( string _name, uint _id, uint _age ) private{ uint id = developers.push( Developer( _name, _id, _age ) ) - 1; ownerDevCount[msg.sender]++; devToOwner[id] = msg.sender; NewDeveloper(id, _name, _age); } function _generateRandomId( string _str ) private pure returns (uint){ uint rand = uint(keccak256(_str)); return rand; } function createRandomDeveloper( string _name, uint _age ) public payable { require(_age > minAge); require(_age < maxAge); require(msg.value == 5000000000000000000); uint randId = _generateRandomId( _name ); _createDeveloper(_name, randId, _age ); } function getAllDevelopers() public view returns (uint) { return developers.length; } }
Nếu bạn muốn biết chi tiết chuyện gì đang xảy ra hãy xem bài viết về Solidity của tôi. Nói ngắn gọn thì, contract này được call để tạo một Developer struct có name và tag. Trong ví dụ này, để tạo một Developer mới cần 5 ether (5000000000000000000 wei, hạng thấp nhất trên Ethereum).
Tiếp theo, vào folder migrations
và tạo ra một file 2_deploy_contracts.js:
const DeveloperFactory = artifacts.require('./DeveloperFactory.sol') module.exports = function(deployer){ deployer.deploy(DeveloperFactory) }
Trong file này, ta sẽ import contract và deploy nó trên blockchain.
Launch test environment
Mở một cửa sổ terminal mới và chạy ganache-cli -p 7545
. Nó sẽ chạy ganache-cli
trên port 7545 (tương tự như cái chúng ta xác định trên file truffle-config.js
) và tạo một vài accounts. Mỗi account có 100 ether mặc định.
Bạn sẽ thấy trong console một cái giống thế này:
Available Accounts ================== (0) 0x473c0be352f997aa0b194786c27d26e29a3f75b1 (1) 0x9657290da5570b17a03198f490b0a2d7eea84ecf (2) 0x516c0e0152d7b85facb7e3da2d30f67e42a80ca9 (3) 0xf81be8bbe99d2302b85f7cb0f60103c435ae703b (4) 0xcfacf5ac5567cfdd70ee5a8a9fe4bf7f74d80b02 (5) 0x623e18e34b2de07933fe179862f038230cc69012 (6) 0xd7100dbc1d6f72777ae2a6f5d95c4b8d71f7ce07 (7) 0x7f40df6c6042888a37124821130910e77051b1cf (8) 0x26a2c2be1f31571f289b7fb60e41f31f7c57a5be (9) 0x08a945825a28166466987d5fc77b016fe3d80aa5
Dĩ nhiên, các address của account sẽ khác nhau, nhưng bạn sẽ có khoảng 10 giây để tìm hiểu.
Bây giờ, hãy quay lại cửa sổ terminal ban đầu. Hãy đảm bảo rằng bạn đã ở trong folder bạn tạo và chạy: truffle compile
, và chạy truffle migrate --network development
. Nó compile code chúng ta thành một ngôn ngữ mà Ethereum Virtual Machine (EVM) có thể hiểu được, ở đây ganache sẽ mô phỏng EVM.
Nếu mọi thứ suôn sẻ thì terminal của bạn sẽ trông như sau:
truffle migrate --network development Using network 'development'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xc83617394674cd65f751ff9c05438e16339414ccf1e1662ba66479d79335af13 Migrations: 0xe982e78028e0dfcbdb135e7a3c1e1ed3d98e36e5 Saving successful migration to network... ... 0x78bdff98e4dac310de4650048a0856075a460bed9de0c4d4ea879ea399d142c4 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying DeveloperFactory... ... 0x0a314c5ed99c772019ea358ac98e002a1442e26903122528d705bf3ff7ed02ed DeveloperFactory: 0xc34cc3e53850673db1dea31d267ea1738edc629f Saving successful migration to network... ... 0x95a3cd861f067cdbe8c96f13477526eacd2a7936662f31724e1354922af49664 Saving artifacts...
Chú ý ganache-cli
output mà contract đã tạo ra:
Transaction: 0xc83617394674cd65f751ff9c05438e16339414ccf1e1662ba66479d79335af13 Contract created: 0xe982e78028e0dfcbdb135e7a3c1e1ed3d98e36e5 Gas usage: 269607 Block Number: 1 Block Time: Thu May 03 2018 21:04:55 GMT+0200 (CEST)
Console
Hãy chạy truffle console --network development
ngay trên terminal window mà bạn đã chạy các truffle command. Nó sẽ launch truffle console và cho phép bạn tương tác với blockchain của mình.
Chúng ta sẽ dùng Web3 Javascript API cho dễ. Đầu tiên, hãy dùng một account và bỏ nó vào 1 variable:
account = web3.eth.accounts[4]
Rồi chạy command sau:
DeveloperFactory.deployed().then(inst => {Factory = inst})
Nó sẽ assign contract vào Factory
variable. Nhớ rằng account có 100 ether:
truffle(development)> web3.fromWei(web3.eth.getBalance(account).toNumber()) '100' truffle(development)>
Method getBalance
sẽ trả về kiểu BigNumber
. toNumber()
sẽ đưa balance về cho account trong Wei
. Sau đó ta sẽ covert nó về ether bằng fromWei()
.
- Tạo một Developer
Hãy gọi function này là createRandomDeveloper
. Như bạn thấy, function này cần 2 parameter, một string _name
và một uint _age
. Vì chúng ta cần đến 5 ether để call function này, ta phải làm rõ nó trong function call:
truffle(development)> Factory.createRandomDeveloper('Damien', 26, {from: account, value: web3.toWei(5, "ether")})
Factory
là một contract instance. Chúng ta đưa vào 3 parameters vào function. Damien là _name
, 26 là _age
. Cái thứ ba là một object có key from
để biết được account nào đang call nó, và một key value
để xác định value được gửi bởi account đó.
Ở đây, from
value là account
, variable chúng ta đã tạo trước đó. Chúng ta sẽ covert 5 ether Wei để tương thích với value cần trong contract.
Terminal của bạn sẽ hiện như sau:
{ tx: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e', receipt: { transactionHash: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e', transactionIndex: 0, blockHash: '0x3ef1c41cbc79d65c1282a86da3a68120a5c069709a6a5bd3e206ed85d9c270c5', blockNumber: 5, gasUsed: 148160, cumulativeGasUsed: 148160, contractAddress: null, logs: [ [Object] ], status: '0x01', logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000200000000000000000000000000000000000000000000000000000002000000000000000000000' }, logs: [ { logIndex: 0, transactionIndex: 0, transactionHash: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e', blockHash: '0x3ef1c41cbc79d65c1282a86da3a68120a5c069709a6a5bd3e206ed85d9c270c5', blockNumber: 5, address: '0x033711f6fd408b10cc94a21a3e8c20f0e75a4615', type: 'mined', event: 'NewDeveloper', args: [Object] } ] } truffle(development)>
Việc chuyển đổi đã thành công. Có rất nhiều info ở đây. Chúng ta có thể thấy event ‘NewDeveloper’ đã bị bỏ đi như dự kiến, có thêm hash, block hash, lượng gas
đã dùng,…
Còn balance của account là như sau:
truffle(development)> web3.fromWei(web3.eth.getBalance(account).toNumber()) '94.985184'
Chú ý rằng nó không phải 95 ether. Đó là do khi bạn tương tác với contract, bạn cũng phải trả thêm ether để thực hiện chuyển đổi. Chúng ta có cumulativeGasUsed
(148160) trong thông tin của giao dịch, có nghĩa là 148160 Wei đã được dùng để thực hiện giao dịch này. Chúng ta lấy số này nhân với gasPrice
. Mọi giao dịch đều có gasPrice
.
Có thể lấy giá trị này bằng transactionHash
, rồi nhân nó với cumulativeGasUsed
để biết chi phí giao dịch bằng đơn vị ether:
truffle(development)> web3.eth.getTransaction('0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e').gasPrice.toNumber() * 148160 14816000000000000 truffle(development)> web3.fromWei(14816000000000000) '0.014816' truffle(development)> 100 - 0.014816 99.985184
Và chúng ta có thể tính balance như vậy. Chúng ta còn có thể đảm bảo balance của account là 5 ether như sau:
truffle(development)> web3.fromWei(web3.eth.getBalance('0x033711f6fd408b10cc94a21a3e8c20f0e75a4615').toNumber()) '5'
Bạn có thể lấy address của contract trong log field address
của giao dịch trên. Cuối cùng, nếu như chúng ta không gửi 5 ether trong contract thì sao? Hãy lấy một account mới:
truffle(development)> account1 = web3.eth.accounts[9] '0x5e273389dba808789a27cb792faaf31429c8de8c' truffle(development)> web3.fromWei(web3.eth.getBalance(account1).toNumber()) '100'
Hãy call function createRandomDeveloper
:
truffle(development)> Factory.createRandomDeveloper('Johnny', 43, {from: account1, value: web3.toWei(10, "ether")})
Error: VM Exception while processing transaction: revert
at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:41484:16)
truffle(development)> web3.fromWei(web3.eth.getBalance(account1).toNumber())
'99.9976828'
Có một lỗi xảy ra. Tuy nhiên, gas
dùng để bắt đầu giao dịch kiểu gì chẳng bị mất đi! Bạn có thể thấy balance của account không còn là 100 ether nữa.
To be contiuned…
Có thể bạn quan tâm:
- Ethereum là gì? Tìm hiểu cơ bản về Blockchain (P2)
- Nhược điểm của Blockchain
- Ứng dụng công nghệ Blockchain xây dựng nền tảng giao dịch phi tập trung
- Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!
Xem thêm tuyển dụng lập trình blockchain hấp dẫn trên TopDev
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- F Framework nào tốt nhất cho dự án của bạn? – Checklist chi tiết
- K Kinh nghiệm xử lý responsive table hiệu quả
- S Stackoverflow là gì? Bí kíp tận dụng Stack Overflow hiệu quả
- 7 7 kinh nghiệm hữu ích khi làm việc với GIT trong dự án
- B Bài tập Python từ cơ bản đến nâng cao (có lời giải)
- B Bảo mật API là gì? Một số nguyên tắc và kỹ thuật cần biết
- H Hướng dẫn cài đặt và tự học lập trình Python cơ bản từ A-Z
- C Chinh Phục Phân Tích Dữ Liệu Với Pandas Trong Python: Hướng Dẫn Từng Bước
- D Display CSS là gì? Cách khai báo và sử dụng thuộc tính display trong CSS