Lập trình smart contract: Phần 5 — Viết test case cho smart contracts

Updated: 30/04/2018 at 21:00

Tiếp tục loạt bài thực tế phát triển dApp, giờ chúng ta sẽ nâng độ khó lên một chút xíu khi đi vào hoàn thiện nội dung của smart contracts và viết test case kiểm thử.

Smart contracts

Events

Các smart contracts được thực thi trong môi trường của EVM, hoàn toàn tách biệt với môi trường bên ngoài. Để có thể trigger một sự kiệu ra bên ngoài EVM ta gửi các events được định nghĩa trước:

//New job append
 event NewJob(
 uint256 indexed id,
 address creator,
 uint256 salary,
 uint256 timeOut);

//An woker start working
 event TakeJob(
 uint256 indexed id,
 address indexed labor);

Các event này có thể lắng nghe thông qua các filter cung cấp bởi web3js, việc này sẽ cung cấp thêm thông tin, trigger các hệ thống third party hoặc đơn giản là điều chỉnh lại front-end.

Modifier

Để smart contract của mình bảo mật hơn, mình thêm các modifier. Mục tiêu là kiểm tra arguments trước khi method được trigger (Ethereum là một hệ hoàn toàn thụ động, không có khả năng tự tính toán).

//Transaction must contant Ethereum
 modifier onlyHaveFund {
 require(msg.value > MINIUM_SALARY);
 _;
 }

Trên đây bạn có thể đọc thấy modifier rất rõ nghĩa, với modifier này mình check số Ethereum có trong transaction. Biến msg sẽ chứa vài thông tin hữu ích của transaction (eg. msg.sender: address của người gửi, msg.value: số ethereum tính bằng wei). Để biết rõ thêm global variable các bạn xem ở đây.

Smart contract

Mình viết lại smart contract, thêm hai function mới takeJob() và viewJob():

//Take job

function

takeJob (uint256 jobId)

public onlyValidMortgage(jobId) onlyValidId(jobId) onlyValidJob(jobId)

{

//Trigger event to log labor

TakeJob(jobId, msg.sender);



//Change working state

jobData[jobId].start = block.timestamp;

}



//Veiw job data

function

viewJob(uint256 jobId)

public onlyValidId(jobId) constant returns (

uint256 id,

address creator,

uint256 salary,

uint256 start,

uint256 end,

uint256 timeOut,

bytes title,

bytes description)

{

Job memory jobReader = jobData[jobId];

return (jobReader.id,

jobReader.creator,

jobReader.salary,

jobReader.start,

jobReader.end,

jobReader.timeOut,

jobReader.title,

jobReader.description);

}

Trong phần struct mình cũng update thêm các biến liên quan tới thời gian, và để tiện xử lý mình sẽ sữ dụng Unix timestamp (Solidity không có kiểu datime nên dùng Unix time là tiện nhất):

uint256 start;

uint256 end;

uint256 timeOut;

Chúng ta có toàn bộ mã nguồn như sau:

pragma solidity ^0.4.18;





contract PartTime {



//Job structure

struct Job {

uint256 id;

address creator;

uint256 salary;

uint256 start;

uint256 end;

uint256 timeOut;

bytes title;

bytes description;

}



//New job append

event NewJob(uint256 indexed id,

address creator,

uint256 salary,

uint256 timeOut);



//An woker start working

event TakeJob(

uint256 indexed id,

address indexed labor);



//Minium accept salary

uint256 constant public MINIUM_SALARY = 0.1 ether;



//The number of jobs

uint256 public totalJob;



//Mapped data

mapping (uint256 => Job) public jobData;



//Transaction must contant Ethereum

modifier onlyHaveFund {

require(msg.value > MINIUM_SALARY);

_;

}



//Valid timeOut should be greater than 3 days

modifier onlyValidTimeOut(uint256 timeOut) {

require(timeOut > 3 days);

_;

}



//Check valid job Id

modifier onlyValidId(uint256 jobId) {

require(jobId < totalJob);

_;

}



//Mortgage should be greater than 1/10

modifier onlyValidMortgage(uint256 jobId) {

require(msg.value > jobData[jobId].salary/10);

_;

}



//Check is it a taked job

modifier onlyValidJob(uint256 jobId) {

require(jobData[jobId].end == 0);

require(jobData[jobId].start == 0);

_;

}



//Append new job to mapping

function

createJob (uint256 timeOut, bytes title, bytes description)

public onlyHaveFund onlyValidTimeOut(timeOut) payable returns(uint256 jobId)

{

// Saving a little gas by create a temporary object

Job memory newJob;



// Assign jobId

jobId = totalJob;



newJob.id = jobId;

newJob.id = timeOut;

newJob.title = title;

newJob.description = description;

newJob.salary = msg.value;

newJob.creator = msg.sender;



//Trigger event

NewJob(jobId, msg.sender, msg.value, timeOut);



// Append newJob to jobData

jobData[totalJob++] = newJob;



return jobId;

}



//Take job

function

takeJob (uint256 jobId)

public onlyValidMortgage(jobId) onlyValidId(jobId) onlyValidJob(jobId)

{

//Trigger event to log labor

TakeJob(jobId, msg.sender);



//Change working state

jobData[jobId].start = block.timestamp;

}



//Veiw job data

function

viewJob(uint256 jobId)

public onlyValidId(jobId) constant returns (

uint256 id,

address creator,

uint256 salary,

uint256 start,

uint256 end,

uint256 timeOut,

bytes title,

bytes description)

{

Job memory jobReader = jobData[jobId];

return (jobReader.id,

jobReader.creator,

jobReader.salary,

jobReader.start,

jobReader.end,

jobReader.timeOut,

jobReader.title,

jobReader.description);

}



}

Và mình viết thêm test case bằng JavaScript:

var Partime = artifacts.require("./PartTime.sol");



function createTx(from, to, value = 0, gas = 1000000, gasPrice = 20000000) {

return {

from: from,

to: to,

gas: gas,

gasPrice: gasPrice,

value: value

};

}



contract('Partime', function (accounts) {



it('should have 0 total part time job', function () {

return Partime.deployed().then(function (instance) {

return instance.totalJob.call();

}).then(function (totalJob) {

assert.equal(totalJob.valueOf(), 0, 'Total job was not equal to 0');

});

});



it('should able to add new job', function () {

return Partime.deployed().then(function (instance) {

return instance.createJob(((new Date()).getTime() / 1000 + 432000),

"This is tittle",

"This is description",

createTx(accounts[0], instance.address, web3.toWei('1', 'ether')));

}).then(function (totalJob) {

assert.equal(typeof (totalJob.valueOf()), 'object', 'Transaction was not triggered success');

});

});



it('should have total part time job geater than 0', function () {

return Partime.deployed().then(function (instance) {

return instance.totalJob.call();

}).then(function (totalJob) {

assert.equal(totalJob.valueOf() > 0, true, 'Total job was equal to 0' );

});

});



});

Bạn chú ý thấy mình đang thêm 1 job mới:

return instance.createJob(((new Date()).getTime() / 1000 + 432000),

"This is tittle",

"This is description",

createTx(accounts[0], instance.address, web3.toWei('1', 'ether')));

Đoạn code này có nghĩa là mình tạo ra 1 job có

  • Timeout: 5 ngày
  • Title: This is tittle
  • Description: This is description
  • Value: 1 Ethereum

Thực thi kiểm thử:

Smart contracts

Mình đã update source code tại https://github.com/chiro-hiro/part-time-dapp. Các bạn có thể clone về hoặc fork về viết cùng mình cho vui.

Dislaimer: Đây là thông tin cung cấp dưới dạng blog cá nhân, không phải thông tin tổng hợp hay lời khuyên đầu tư. Chúng tôi không chịu trách nhiệm về các quyết định đầu tư của bạn.

Được đề cập trong bài viết
Mới cập nhật

Giữa làn sóng dòng vốn ETF hàng tỷ USD liên tục đổ vào, nhóm công ty mid-cap châu Á đang nổi lên như lực cầu mang tính cấu trúc mới cho lượng Bitcoin lưu hành tự do. Tại Nhật Bản, Metaplanet đã nắm giữ hơn 30.000 BTC trên bảng cân... ...

Sau khi mô hình Stock-to-Flow (S2F) bị loại bỏ, mô hình Power Law (Luật sức mạnh) hiện tại cho thấy giá Bitcoin đang thấp hơn khoảng 20% so với giá trị hợp lý, trong khi dòng vốn ETF có thể đẩy giá đến cả hai cực của mô hình. Theo... ...

Phe bò Bitcoin (BTC) đang nỗ lực bảo vệ mốc 110.000 USD, song áp lực từ phe gấu vẫn chưa hề suy giảm. Diễn biến này đã khiến BTC đóng nến tháng 10 trong sắc đỏ — lần đầu tiên sau 7 năm. Theo dữ liệu từ CoinGlass, sau một... ...

Sự bất ổn trên thị trường đang khiến các nhà đầu tư Bitcoin (BTC) cảm thấy đứng ngồi không yên. Chỉ trong chưa đầy một tuần, tổng vốn hóa thị trường tiền điện tử (TOTAL) đã bốc hơi khoảng 300 tỷ USD, tụt xuống còn xấp xỉ 3,5 nghìn tỷ... ...

Hai quỹ ETF Solana vừa gia nhập thị trường trong tuần này, đánh dấu bước khởi đầu cho một giai đoạn cạnh tranh gay gắt hơn, khi hàng chục quỹ crypto khác đang chờ được cơ quan quản lý phê duyệt. Tuy nhiên, theo ông Zach Pandl, Giám đốc nghiên... ...

Các meme coin nổi bật như Dogecoin (DOGE), Shiba Inu (SHIB) và Pepe (PEPE) đang trải qua những tổn thất nặng nề trong đợt bán tháo lan rộng trên thị trường tiền điện tử. Hiện tại, DOGE, SHIB và PEPE đang chạm tới các ngưỡng hỗ trợ then chốt, đối... ...

Ngành blockchain đang cho thấy dấu hiệu trưởng thành rõ rệt — ít nhất là theo một thước đo thường bị bỏ qua — phản ánh sự mở rộng ứng dụng trong tài chính phi tập trung (DeFi), ứng dụng tiêu dùng và các lĩnh vực mới nổi. Theo báo... ...

Ethereum (ETH) vẫn đối mặt với áp lực điều chỉnh quanh ngưỡng 3.800 USD, dù các nhà phát triển đã chính thức ấn định ngày 3/12 là thời điểm triển khai mainnet Fusaka. Ra mắt mainnet Fusaka vào tháng 12 Trong cuộc họp All Core Developers Consensus (ACDC) lần thứ... ...

Vào thời điểm viết bài ngày thứ Sáu, giá Bitcoin (BTC) dao động quanh mức 109.000 USD, đánh dấu mức giảm gần 5% kể từ đầu tuần. Cùng chiều, Ethereum (ETH) và Ripple (XRP) cũng suy yếu, giảm lần lượt gần 8% và 7%. Ba tiền điện tử hàng đầu... ...

Stablecoin USDC của Circle đang tăng trưởng nhanh hơn USDT của Tether cả về vốn hóa thị trường lẫn hoạt động on-chain, nhờ khung pháp lý minh bạch và sự gia tăng của dòng vốn tổ chức, theo báo cáo mới của các nhà phân tích JPMorgan. Theo nhóm nghiên... ...

Xem thêm bài viết

Chọn chế độ hiển thị:
Bình thường Bảo vệ mắt Dark Mode