Bài trước chúng ta đã nói khá nhiều về lập trình Ethereum trong blockchain là gì và cách dùng Truffle
cũng như command line Ganache
. Bài này chúng ta sẽ dùng environment này để nói về 2 concept khác của Solidity: Interface
và các Function Modifier
.
Cài đặt sẵn sàng
Sau khi bạn đã cài Truffle
và ganache-cli
, tạo một folder mới và chạy truffle init
. Chúng ta sẽ tạo 2 contract trong folder truffle
mới tạo.
Answers.sol
pragma solidity ^0.4.18;
contract Answers {
uint answerUniverse;
function setAnswerUniverse(uint _answer) external {
answerUniverse = _answer;
}
function getAnswerUniverse() public view returns (uint){
return answerUniverse;
}
}
Questions.sol
pragma solidity ^0.4.18;
contract AnswersInterface {
function getAnswerUniverse() public view returns (uint);
}
contract Questions {
AnswersInterface answersContract;
function setAnswersContractAddress(address _address) external{
answersContract = AnswersInterface(_address);
}
function whatIsTheAnswerUniverse() public view returns (uint){
uint answer = answersContract.getAnswerUniverse();
return answer;
}
}
Contract Answers.sol
rất đơn giản. Một variable answerUniverse
có cả getter và setter. Lưu ý rằng, bởi vì nó quan trọng rằng setter là nhân tố ngoài, nghĩa là nó phải được call từ ngoài contract, còn getter thì public nghĩa là ai call nó cũng được.
Contract Questions.sol
gồm 2 thứ: contract Questions
, và interface AnswersInterface
.
Vậy, interface là gì? Một interface cho phép chúng ta nói về một contract khác trên blockchain. Như bạn có thể thấy, để xác định một interface, bạn phải bắt đầu từ một contract bình thường, với keyword contract
.
>>> Xem thêm Interface và Abstract là gì?
Trong contract này, bạn chỉ xác định các function bạn muốn tương tác không cần phần thân. Các function này cần là public hoặc từ bên ngoài để có thể call chúng từ ngoài contract gốc. Bạn không thể tương tác với các function riêng hoặc nội bộ course được.
Bên trong contract Questions
, chúng ta sẽ tạo một interface mẫu trong answersContract
. Function setAnswersContractAddress
sẽ cho contract biết chỗ tìm được contract Answer
gốc. Function whatIsTheAnswerUniverse
thu về variable answerUniverse
trong contract Answers
.
Note: Dĩ nhiên chúng ta cần phải set địa chỉ contract trước khi lấy variable. Nếu không chúng ta sẽ chả biết nó ở đâu trên blockchain!
- Tiếp đến, trong file
truffle-config.js
:
module.exports = {
networks: {
development: {
host: '127.0.0.1',
port: 7545,
network_id: '*'
}
}
};
Với người dùng Windows, bạn sẽ phải remove file truffle.js để tránh mâu thuẫn. Với hệ khác, bạn có thể giữ lại cả hau và bỏ vào code trong truffle.js, hoặc lằm giống như user Windows chẳng sao cả.
- Trong folder migrations, tạo 2 file: 2_deploy_questions.js và 3_deploy_answers.js.
- 2_deploy_questions.js
var Questions = artifacts.require("Questions")
module.exports = function(deployer) {
deployer.deploy(Questions)
};
var Questions = artifacts.require("Questions")
module.exports = function(deployer) {
deployer.deploy(Questions)
};
- 3_deploy_answers.js
var Answers = artifacts.require("./Answers.sol")
module.exports = function(deployer) {
deployer.deploy(Answers)
}
Triển khai
Mở một cửa sổ terminal mới và chạy ganache-cli -p 7545
. Quay lại folder của project và chạy:
truffle compile
truffle migrate --network development
truffle console --network development
Bây giờ chúng ta có thể chơi với các contract và interface của nó. Đầu tiên, tạo một instance cho mỗi contract.
truffle(development)> Questions.deployed().then(inst => Questions = inst)
truffle(development)> Answers.deployed().then(inst => Answers = inst)
Sau khi launch Answers instance, bạn sẽ thấy vài thứ mới xuất hiện trong console. Sẽ có cả field address. Đây là address của contract Answers. Đây có thể là những gì ta cần để call setAnswersContractAddress function:
truffle(development)> Questions.setAnswersContractAddress('0x2e91a07090cfbbc0839e0d76d8110e2518bae18c')
Note: Hãy thế address bằng bất cứ address nào bạn thấy trong field.
Hãy xem answersUniverse variable trong Answers contract:
truffle(development)> Answers.setAnswerUniverse(76)
Và lấy nó từ contract Questions:
truffle(development)> Questions.whatIsTheAnswerUniverse().then(answer => answer.toNumber())
76
Hãy chỉnh lại answer và retrieve lần nữa:
truffle(development)> Answers.setAnswerUniverse(42)
truffle(development)> Questions.whatIsTheAnswerUniverse().then(answer => answer.toNumber())
42
Và bạn đã có một interface rồi.
Các function modifier
Có thể dễ thấy một vấn đề về bảo mật trong project của chúng ta: function setAnswersContractAddress
là yếu tố từ bên ngoài, nghĩa là bất kì ai từ ngoài contract cũng gọi nó được, cũng có nghĩa là ai cũng có thể call function và đổi address được cả. Để giải quyết được việc này, chúng ta phải add một function modifier, sẽ được call khi function được thực hiện.
Căn bản là, nó sẽ chạy một vài đợt check để đảm bảo rằng mọi thứ vẫn ổn. Trong trường hợp này, chúng ta phải đảm bảo rằng chỉ có người sở hữu mới gọi được function. Để vậy chúng ta sẽ dùng contract từ thư viện OpenZeppelin Solidity gọi là Ownable
. Copy và dán nó vào.
Đừng hoảng lên nếu bạn không hiểu gì trong contract. Chỉ cần biết rằng nó sẽ đảm bảo cho function chỉ có thể kích hoạt được bởi chủ.
- Tạo một file
Ownable.sol
trong các contract
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
}
- Import contract Ownable vào Questions và xác định function modifier cho
setAnswersContractAddress
:
pragma solidity ^0.4.18;
import "./Ownable.sol";
contract AnswersInterface {
function getAnswerUniverse() public view returns (uint);
}
contract Questions is Ownable{
AnswersInterface answersContract;
function setAnswersContractAddress(address _address) external onlyOwner{
answersContract = AnswersInterface(_address);
}
function whatIsTheAnswerUniverse() public view returns (uint){
uint answer = answersContract.getAnswerUniverse();
return answer;
}
}
Relaunch command ganache-cli, và các command truffle. Deploy các contract Answers và Questions. Mặc định chủ contract sẽ là account đầu tiên được tạo bởi ganache-cli. Sau đó lấy một account khác:
truffle(development)> account = web3.eth.accounts[3]
Bây giờ, nếu chúng ta set interface address từ account này, bạn sẽ bị lỗi:
truffle(development)> Questions.setAnswersContractAddress('0x37eba1bb7d4c779474a4955437e524fbdbac0dc2', {from: account})
Error: VM Exception while processing transaction: revert
Nếu bạn remove cái object argument, hoặc dùng accounts[0] address trong key, bạn vẫn có thể set address ổn thoả.
Kết luận
Hy vọng qua 2 bài viết này các bạn có thể hiểu hơn về Ethereum trong Blockchain là gì.
Cảm ơn các bạn đã theo dõi bài viết!
Có thể bạn quan tâm:
- Blockchain là gì? Hiểu đơn giản về Blockchain và các ứng dụng
- Ethereum là gì? Tìm hiểu cơ bản về Blockchain (P1)
- Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!
- Ứng dụng công nghệ Blockchain xây dựng nền tảng giao dịch phi tập trung
Xem thêm việc làm blockchain hot nhất trên TopDev