Khi mình bắt đầu tập tành lập trình blockchain, việc verify signatures trong solidity khiến mình khá bối rối. Sau một thời gian tự mày mò, mình đã hiểu được cách hoạt động cũng như cách triển khai nó. Trong bài viết này, mình sẽ chia sẻ lại mọi thứ theo đúng hành trình học và “giác ngộ” của mình, để bạn nào mới tìm hiểu về cách verify signatures trong Solidity cũng có thể nắm bắt nhanh chóng!
Cách ký một message
Trước khi tìm hiểu cách verify signature Solidity, đầu tiên mình phải học cách ký (sign) một message.
Ở phía frontend (ví dụ như trên website), quá trình ký sẽ diễn ra như thế này:
javascript
CopyEdit
async function signMessage() {
if (!window.ethereum) return alert("Please cài Metamask!");
// Kết nối tới ví
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
// Message cần ký
const message = "hello";
console.log({ message });
// Hash message
const hashedMessage = Web3.utils.sha3(message);
console.log({ hashedMessage });
// Ký hashed message
const signature = await ethereum.request({
method: "personal_sign",
params: [hashedMessage, accounts[0]],
});
console.log({ signature });
// Tách signature ra thành r, s, v
const r = signature.slice(0, 66);
const s = "0x" + signature.slice(66, 130);
const v = parseInt(signature.slice(130, 132), 16);
console.log({ r, s, v });
}
Build Smart Contract để Verify
Sau khi đã có message đã được ký, việc tiếp theo là xây smart contract để verify signatures solidity.
Mình đã viết một contract mẫu đơn giản như sau:
solidity
CopyEdit
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Verify {
function VerifyMessage(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public pure returns (address) {
bytes memory prefix = "\\x19Ethereum Signed Message:\\n32";
bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
address signer = ecrecover(prefixedHashMessage, _v, _r, _s);
return signer;
}
}
Một vài điểm quan trọng mình muốn nhấn mạnh:
- Phải thêm prefix trước khi hash lại message.
- Hàm
ecrecover
sẽ trả về địa chỉ Ethereum đã ký message đó. - Nếu address trả về đúng với người mà bạn mong đợi => signature hợp lệ.
Có thể bạn quan tâm: Quy trình 6 bước để tổ chức ICO token
Ứng dụng thực tế: Game Unity phát thưởng
Một ví dụ thực tế mà mình rất thích: sử dụng verify signature solidity trong game blockchain.
Giả sử bạn lập trình viên game:
- Khi người chơi thắng, server game sẽ ký một message chứng nhận chiến thắng (giống như phát “phiếu thưởng”).
- Người chơi gửi signature đó lên smart contract để claim phần thưởng.
Ví dụ contract:
solidity
CopyEdit
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Verify {
address public owner = 0xdD4c825203f97984e7867F11eeCc813A036089D1; // Địa chỉ của server game
function claimPrize(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public view returns (bool) {
bytes memory prefix = "\\x19Ethereum Signed Message:\\n32";
bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
address signer = ecrecover(prefixedHashMessage, _v, _r, _s);
if (signer == owner) {
return true; // Người chơi hợp lệ => thưởng
}
return false; // Không hợp lệ
}
}
Ở client side (ví dụ Unity), mình sẽ tách chữ ký ra như thế này:
csharp
CopyEdit
string signature = "0xb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c91c";
string r = signature.Substring(0, 66);
string s = "0x" + signature.Substring(66, 64);
int v = int.Parse(signature.Substring(130, 2), System.Globalization.NumberStyles.HexNumber);
Sau đó người chơi chỉ cần gửi _r, _s, _v
và hashedMessage
vào contract để nhận thưởng.
Tổng kết
Việc verify signatures in Solidity thực ra không quá phức tạp, bạn chỉ cần lưu ý:
- Hash message đúng chuẩn.
- Thêm prefix
"\\x19Ethereum Signed Message:\\n32"
trước khi xác thực. - Dùng
ecrecover
để lấy địa chỉ người ký.
Nếu bạn đang muốn làm các dự án liên quan tới xác thực người dùng, phát coupon, kiểm tra quyền truy cập… thì việc phải master kỹ năng này là rất hữu ích đấy!
Bài viết liên quan: