Lập trình Blockchain - Bài 03: Solidity cơ bản - Các kiểu dữ liệu
Solidity là ngôn ngữ lập trình có cú pháp, cấu trúc tập lệnh giống Javascript và C, được sử dụng để xây dựng contract - thành phần cơ bản để tạo block của ứng dụng Ethereum chạy trên EVM (Ethereum Virtual Machine).
Trong bài viết này chúng ta sẽ tập trung vào việc nêu các kiểu dữ liệu, cách định nghĩa và sử dụng phương thức trong Solidity để các bạn có kiến thức cơ bản trước khi bắt tay vào xây dựng ứng dụng Blockchain trên nền tảng Ethereum.
Một tập tin mã nguồn Solidity được lưu dưới tên có kết thúc với “.sol” nên bắt đầu cùng với “version pragma” để định nghĩa phiên bản trình biên dịch Solidity được sử dụng.
1. Contracts
Trong Solidity mọi thứ đều xoanh quanh contract, cũng tương tự như các ngôn ngữ hướng đối tượng (đối tượng cụ thể ở đây là contact). Về cơ bản, contract tương tự class trong OOP với các thuộc tính (
state variables
) và các phương thức (methods
). Ngoài khái niệm Contract, Solidity cũng cung cấp 2 đối tượng khác tương tự Contract bao gồm:- interface: Đặc tả cấu trúc của hợp đồng, bao gồm các hàm mà không có thân hàm, không xa lạ với giới lập trình viên nên có lẽ không cần giải thích nhiều.
- library: Deploy 1 lần và được sử dụng bởi các hợp đồng khác thông qua
DELEGATECALL
.
- Mã của Solidity được đóng gói trong các hợp đồng (Contract). Một hợp đồng là khối xây dựng cơ bản của các ứng dụng Ethereum – tất cả các biến và chức năng thuộc về một hợp đồng, và đây là điểm khởi đầu của tất cả các dự án của bạn.
- Thí dụ: contract HelloWorld {}
Ví dụ: Tạo một file
Contract.sol
Đầu tiên bạn phải xác định phiên bản bạn đang dùng. Đây là info mà compiler cần.
1pragma solidity ^0.4.22;Mọi code trong Ethereum đều thuộc về một Contract. Hãy tạo một contract và xác định một vài variable trong đó.
pragma solidity ^0.4.22;
contract DeveloperFactory {
// Let's create a Developer!
uint dnaDigits = 16;
uint ageDigits = 2;
}
2. Version Pragma
- Tất cả các mã nguồn đều bắt đầu với một phiên bản “version pragma“-một tuyên bố của phiên bản của trình biên dịch Solidity. Đây là để ngăn chặn các vấn đề với các phiên bản trình biên dịch tương lai có khả năng giới thiệu những thay đổi có thể phá vỡ mã của bạn.
- Ví dụ: pragma solidity ^0.4.22;
3. Biến
- Kiểu dữ liệu uint là 1 số nguyên không dấu, có nghĩa là giá trị của nó phải không âm. Ngoài ra còn có 1 kiểu dữ liệu int cho số nguyên .
- Lưu ý: Trong Solidity, uint thực sự là bí danh cho uint256, một số nguyên unsigned 256 bit. Bạn có thể khai báo uint với các bit như – uint8, uint16, uint32, v.v.v
4. Kiểu dữ liệu
4.1. Mảng:
pragma solidity ^0.4.22;
contract DeveloperFactory {
// Let's create a Developer!
uint dnaDigits = 16;
uint ageDigits = 2;
struct Developer {
string name;
uint dna;
uint age;
}
Developer[] public developers;
}
Biến struct
cho phép chúng ta xác định nhiều cấu trúc data hơn. Ở đây Developer lấy một string
được gọi là name
, một uint
tên dna
và một uint
tên age
.
Solidity cũng có các array. Bạn có thể tạo các array cố định hoạt linh hoạt tùy ý. Mãng Developer
của chúng ta là linh hoạt vì không xác định chiều dài. Vì thế chúng ta có thể add thêm các Developer
vào vô số mà không có giới hạn.
Developer[5] public developers
là một array cố định có thể chứa đến 5 Developer struct
.
4.2. Địa chỉ - address
- Các blockchain Ethereum được tạo thành từ các tài khoản, ví dụ tài khoản ngân hàng. Một tài khoản có một số dư của Ether (đơn vị tiền tệ được sử dụng trên Ethereum blockchain), bạn có thể gửi và nhận thanh toán Ether cho các tài khoản khác, giống như tài khoản ngân hàng của bạn có thể chuyển khoản tiền vào các tài khoản ngân hàng khác. Và mỗi tài khoản có Address.
- Ví dụ: Ethereum blockchain được tạo nên từ các address. Mỗi account có một address riêng. Nó có dạng dưới đây:
0x0cE440255306E921F41612C46F1v6df9Cc969183
. Mỗi address có một lượng Ether nhất định, là số cryptocurrency được dùng trên blockchain, và có thể cho và nhận Ether từ các address khác. - Address là chuỗi các ký tự hexa có kích thước 160 bit tương đương với uint160
4.3. Mảng
Khi muốn lưu trữ tập các giá trị có kiểu dữ liệu giống nhau, bạn có thể sử dụng mảng - array. Solidity có 2 kiểu dữ liệu mảng đó là mảng với kích thước cố định và mảng động.
Mảng tĩnh A với kích thước cố định k được viết như sau: A[k]
Mảng kích thước không cố định A được viết là: A[]
bytes và string là các mảng đặc biệt trong đó bytes tương tự byte[] có thể truy cập phần tử thông qua giá trị index còn string hiện tại không cho phép thực hiện điều này.
Nếu bạn muốn truy xuất giá trị dưới dạng mảng các bytes của một string có tên S, ta có thể chuyển đổi nó sang bytes như sau:
bytes bytePresentation = bytes (S)
Nhưng bạn nên nhớ rằng giá trị sau khi chuyển đổi sẽ là mảng các byte chứ không phải mảng các ký tự của string ở trên.
4.4. Mapping
Mappings là một cách khác để lưu trữ dữ liệu có tổ chức trong Solidity.
Ví dụ:
// For a financial app, storing a uint that holds the user’s account balance:mapping (address => uint) public accountBalance;
// Or could be used to store / lookup usernames based on userId
mapping (uint => string) userIdToName;
Mappings là một kho lưu trữ giá trị chính để lưu trữ và tìm kiếm dữ liệu. Trong ví dụ đầu tiên, khóa là address và giá trị là một uint, và trong ví dụ thứ hai, khoá là một uint và giá trị là string.
4.5. Structs
- Cấu trúc Struct cho phép bạn tạo các kiểu dữ liệu phức tạp hơn có nhiều thuộc tính.
Nó là tập hợp các biến được nhóm với nhau và lưu trữ dưới một tham chiếu, thành phần của cấu trúc được định nghĩa tương tự như cấu trúc trong ngôn ngữ lập trình C/C++
Định nghĩa cấu trúc:
struct [tên cấu trúc] {
// Định nghĩa thành phần của cấu trúc
}
Ví dụ:
struct Customer {
string customerName;
unit customerId;
unit discount;
}
4.6. Memory và Storage
Trong Solidity, có 2 chỗ lưu variable: trong storage hoặc trong memory. Một variable lưu trên memory chỉ là tạm thời, nó còn khi function được dùng đến, sau đó sẽ bị xoá. Một variable lưu trên storage thì ở đó vĩnh viễn. Bạn cũng không cần phải lo lắng về việc variable được lưu ở đâu, vì Solidity sẽ xử lý nó giúp bạn.
Ví dụ như, các state variable (maxAge, minAge, Developer), được khai báo ngoài function thì sẽ lưu trên storage. Các variables như randId, id, rand được store trên memory.
Tuy nhiên, trong một số trường hợp, bạn muốn cần chi tiết chỗ lưu một variable nhất định. Solidity sẽ show cho bạn bằng keyword
memory
vàstorage
.4.7. Struct packing to save gas
- Inside Struct: Nếu bạn có nhiều uints bên trong một struct, sử dụng một uint nhỏ hơn khi có thể sẽ cho phép Solidity đóng gói các biến này cùng nhau để chiếm ít bộ nhớ hơn.
- Bạn cũng sẽ muốn nhóm các loại dữ liệu giống nhau lại với nhau (tức là đặt chúng bên cạnh nhau trong cấu trúc) để Solidity có thể giảm thiểu không gian lưu trữ yêu cầu.
- Ví dụ, một struct với các trường uint c; uint32 a; uint32b; sẽ chi phí ít khí hơn một cấu trúc với các lĩnh vực uint32 a; uint c; uint32b; bởi vì các trường uint32 được nhóm lại với nhau.