Sau vụ sàn FTX sập, mọi người đều có nhu cầu chuyển tài sản về Ví phi tập trung và mình cũng vậy. Trong các tài sản thì Bitcoin là tài sản cần ưu tiên chuyển đầu tiên. Nói chung token thuộc hệ nào tốt nhất nên để trên ví thuộc blockchain đó, như Bitcoin thì để trên Ví trên mạng Bitcoin, Ethereum thì nên để trên ví thuộc mạng Ethereum…
Mình đã tìm hiểu có một số ví hỗ trợ Bitcoin như:
- TrustWallet: Ví uy tín được sở hữu bởi Binance. Ví đáng tin cậy này được sử dụng miễn phí, thân thiện và rẻ. Mình đánh giá thì đây có thể là ví an toàn nhất trong các Hot Wallet. Chỉ ứng dụng trên điện thoại mới hỗ trợ mạng Bitcoin, còn Extension trên trình duyệt chưa hỗ trợ.
- Coinbase Wallet: Ví được sở hữu bởi CoinBase. Cũng là một trong những ví Hot Wallet đáng tin cậy.
- SafePal: Cũng là một trong các ví được nhiều người sử dụng.
- Coin98 Wallet: Đây là sản phẩm của Việt Nam, nhưng sử dụng khá rối dễ nhầm thao tác, không cẩn thận lại mất tiền oan.
- Blockchain.com: Ví bitcoin online hỗ trợ cả trên Web và trên Điện thoại. Mình không thích lắm vì nó yêu cầu phải xác thực người dùng.
- Ví lạnh: Ledger và Trezor. Mình đang mua thử một cái về dùng thử xem sao.
Nói chung các Ví phi tập trung, kể cả Ví Lạnh ở trên bạn có thể sử dụng, nhưng dù sao thì vẫn là được phát triển bởi một bên thứ ba. Nó có rủi ro lớn liên quan tới vấn đề bảo mật của ứng dụng, đã có nhiều vụ hack liên quan tới ví phi tập trung: Các Ví Crypto Wallet bị lộ Private Key và vấn đề bảo mật. Các công ty đứng sau thì họ không bao giờ có ý định lưu giữ Private Key của bạn cả, nhưng với ngành này, nhân sự Dev thay đổi liên tục, nhiều khi có những Dev có ý đồ không tốt, hoặc các Dev yếu hay lập trình cho xong việc mà không để ý tới bảo mật,… Các ví thường có các bản nâng cấp liên tục, có thể phiên bản này okie nhưng sau khi nâng cấp phiên bản mới phát sinh lỗi bảo mật gây lộ Private key.
Ngày 2023-05-16, xuất hiện nhiều Drama liên quan đến công ty Ledger, công ty sản xuất ví trữ lạnh mà nhiều người sử dụng. Có rất nhiều sự phẫn nộ liên quan tới bản cập nhật phần mềm mới nhất bản 2.2.1 dành cho Ví Ledger: LATEST FIRMWARE UPDATES FOR LEDGER NANO S, X, AND S PLUS. Bản cập nhật này có chức năng mới mà nhiều người không thích, đó là “Ledger Recover” (Cho phép phục hồi cụm từ bảo mật Recovery phrase). Chức năng này sẽ thực hiện mã hóa phiên bản khóa cá nhân của bạn, rồi chia nó thành 3 phần gửi tới 3 công ty khác nhau để lưu trữ. Khi bạn quên khóa cá nhân, thì chức năng này sẽ lấy 2 trong 3 phần và thực hiện khôi phục trên thiết bị. Điều này làm rất nhiều phản ứng từ cộng đồng, bởi các lý do:
- Cách lưu trữ khóa cá nhân an toàn nhất là Offline và thiết bị ưu tiên hàng đầu đó chính là Ví Lạnh vì nó đơn giản, không kết nối Internet, khi bạn kết nối thì khóa cá nhân không rời ví. Từ trước đến giờ Recovery Phrase của các Ví Lạnh là thứ không thể hack vì không có phiên bản nào trên mạng, ngoài trừ do bạn tự để lộ. Nhưng với chức năng mới này thì đã có phiên bản khác ở trên mạng.
- Chức năng này cho thấy rằng, phần mềm có thể lấy thông tin Khóa cá nhân của Ví. Và chỉ cần cập nhật phần mềm thì Ledger có thể lấy được thông tin khóa của người dùng và họ có thể làm gì với nó.
- Có thể Ledger là công ty tốt, và người dùng nhiều năm qua không gặp sự cố bị hack nếu lưu trữ khóa cá nhân cẩn thận. Nhưng nhiều người vẫn không an tâm vì lịch sử cho thấy Ledger cũng như các công ty khác, đã bị hack thông tin người dùng trước đây. Vì vậy việc lưu trữ khóa cá nhân ở bên thứ 3 là khá rủi ro.
- Ngoài ra để dùng chức năng trên phải thực hiện KYC bằng Passport thì làm mất đi tính ẩn danh.
Theo mình thấy, nếu tự mình code tạo ra ví thì vẫn cứ là an toàn nhất. Cũng vì thế mà mình quyết định tìm hiểu để tự lập trình tạo ra ví Bitcoin cho riêng mình.
Mục lục
Các loại địa chỉ Bitcoin và nên sử dụng địa chỉ Bitcoin nào?
Ngay đầu tiên bắt đầu với ý tưởng này, mình đã gặp vấn đề:
- Trên ví TrustWallet và ví Coinbase, mình thấy địa chỉ ví Bitcoin bắt đầu bằng kí tự “bc1q“
- Trong khi đó trên Binance, khi mình lấy địa chỉ để Deposit Bitcoin thì thấy địa chỉ của nó lại bắt đầu bằng kí tự “1“
Vậy tại sao lại có nhiều địa chỉ Bitcoin khác nhau như vậy, và mình nên dùng địa chỉ nào? Thực tế sinh ra nhiều loại địa chỉ ví khác nhau liên quan tới các lần cập nhật (Hard fork / Soft fork) trên mạng lưới Bitcoin. Chi tiết bạn xem thêm: Bitcoin War: Các cuộc chiến trên mạng lưới Bitcoin
Sau khi lục tìm trên mạng, mình biết được rằng có nhiều kiểu địa chỉ ví Bitcoin khác nhau ứng với các chuẩn khác nhau:
Legacy Address (P2PKH)
Các địa chỉ theo chuẩn này thì bắt đầu bằng số 1, giống như địa chỉ Deposit trên sàn Binance. Ví dụ: 15e15hWo6CShMgbAfo8c2Ykj4C6BLq6Not
Địa chỉ Legacy Address chỉ đơn giản là hàm băm của Public Key đối với Private Key của bạn. Khi Bitcoin ra mắt vào năm 2009, đây là cách duy nhất để tạo địa chỉ, kiểu này sử dụng nhiều không gian nhất trong một giao dịch và do đó là loại địa chỉ đắt nhất.
Ngày nay không có lý do chính đáng để sử dụng loại địa chỉ này, vì các loại mới tốt hơn về mọi mặt. Hầu hết mọi người sẽ chỉ sử dụng loại địa chỉ này nếu họ có ví cũ, loại ví này từng không tương thích với các loại địa chỉ mới hơn.
Pay to Script Hash (P2SH)
Pay-to-Script-Hash là các địa chỉ bắt đầu với số 3. Ví dụ: 35PBEaofpUeH8VnnNSorM1QZsadrZoQp4N
Địa chỉ Pay-to-Script-Hash là một script liên quan đến các điều kiện chi tiêu nhất định, điều kiện này bị ẩn đối với chủ sở hữu. Các điều kiện chi tiêu này có thể rất đơn giản (chủ sở hữu khóa công khai A có thể tiêu số bitcoin này) hoặc khá phức tạp (chủ sở hữu khóa công khai B có thể tiêu số bitcoin này sau khoảng thời gian X nếu anh ta tiết lộ một bí mật đã định trước). Sử dụng tập lệnh này, các địa chỉ P2SH thậm chí có thể sử dụng SegWit và tiết kiệm phí giao dịch. Gửi đến địa chỉ P2SH rẻ hơn khoảng 26% so với sử dụng ví có địa chỉ cũ.
Native SegWit (P2WPKH)
Native SegWit là các địa chỉ bắt đầu bằng cụm kí tự “bc1q“, giống như các địa chỉ sinh ra trên các ví TrustWallet, Coinbase Wallet. Ví dụ: bc1q42lja79elem0anu8q8s3h2n687re9jax556pcc
Loại địa chỉ này làm giảm lượng thông tin được lưu trữ trong giao dịch nhiều hơn bằng cách không giữ chữ ký và chữ viết trong giao dịch, nhưng trong nhân chứng.
Sử dụng loại này, bạn có thể tiết kiệm thêm 16% so với địa chỉ P2SH, tiết kiệm thêm tới hơn 38% so với địa chỉ cũ. Vì những tiết kiệm này, đây hiện là tiêu chuẩn được sử dụng nhiều nhất cho các địa chỉ.
Vì một số sàn giao dịch và ví chưa hỗ trợ địa chỉ loại này nên họ nhắc người dùng gửi cho họ địa chỉ P2SH để thay thế. Đây là lý do tại sao hầu hết các ví vẫn bao gồm tùy chọn tạo P2SH hoặc thậm chí là ví địa chỉ cũ.
Taproot (P2TR)
Taproot là các địa chỉ bắt đầu bằng cụm kí tự “bc1p“. Ví dụ: bc1pmzfrwwndsqmk5yh69yjr5lfgfg4ev8c0tsc06e
Địa chỉ Taproot được sử dụng từ khoảng tháng 11/2021, khi mạng bitcoin sẽ thực hiện soft-fork taproot. Điều này sẽ kích hoạt nhiều khả năng hợp đồng thông minh mới cho các địa chỉ bitcoin và cải thiện tính riêng tư khi chi tiêu các giao dịch đó.
Các giao dịch taproot thông thường lớn hơn một chút so với segwit gốc, nhưng nhỏ hơn các địa chỉ cũ. Điều này là do chúng được gắn với Public key thay vì băm Public key. Đối với các giao dịch phức tạp liên quan đến tập lệnh đa chữ ký chẳng hạn, địa chỉ taproot tiết kiệm rất nhiều dung lượng, khiến chúng rẻ hơn.
Cách sinh một địa chỉ ví và các công cụ hỗ trợ sinh địa chỉ ví Bitcoin
Hầu hết tất cả các địa chỉ ví Bitcoin hoặc các loại ví khác hiện nay đều bắt đầu từ việc sinh ra một chuỗi 32 số ngẫu nhiên, mỗi số ứng với 1 byte có giá trị từ 0 đến 255. Chuỗi số này chính là Private Key của ví. Từ Private Key này thì sẽ tạo ra Public Key và địa chỉ ví, cái này tùy thuộc vào từng mạng lưới blockchain khác nhau.
Ngoài các ví đã nêu ở trên bạn có thể sử dụng một số công cụ sau để tạo địa chỉ Bitcoin:
Mnemonic là gì? Tại sao Mnemonic được sử dụng để sinh tạo địa chỉ ví Blockchain
Mnemonic là danh sách các từ được sắp xếp theo thứ tự với độ dài tiêu chuẩn từ 12 đến 24 từ đơn, chúng ta dùng nó để tạo ví mới để lưu trữ tiền điện tử. Nó là cụm từ nên nó dễ nhớ hơn nhiều so với số hoặc các kí tự hexa của Private Key.
Vì thế hiện nay, mnemonic được sử dụng trong hầu hết các ví Phi tập trung. Chuẩn được dùng phổ biến để tạo mnemonic là chuẩn BIP39 được sử dụng trong các ví Bitcoin, Ethereum, v..v. Chuẩn BIP39 hỗ trợ tạo mnemonic dưới nhiều ngôn ngữ khác nhau như Tiếng Anh, Tiếng Pháp, Tiếng Italy, Tiếng Nhật, Tiếng Trung giản thể, Tiếng Trung phổn thể, Tiếng Hàn. Ví dụ về một đoạn mnemonic: army van defense carry jealous true garbage claim echo media make crunch
.
Mnemonic được tạo ra như thế nào ?
Đầu tiên ví HD cần tạo một chuỗi nhị phân entropy, độ dài của entropy phụ thuộc vào độ dài mnemonic cần tạo, bên dưới là bảng thể hiện mối quan hệ giữa độ dài entropy và mnemonic tương ứng. Ở phần này chúng ta sẽ mô tả cách tạo mnemonic với độ dài 12 từ, quá trình tạo ra các mnemomic dài hơn cũng diễn ra tương tự. Ví dụ: Entropy 01110110010110011000101011101011110010101001111010100011011000110000000111101101010010011010101000110011100001110110110010100110
dài 128 bits để tạo ra mnemonic dài 12 từ.
TIếp theo:
- B1: Băm entropy với hàm SHA-256, lấy 4 bit đầu của hàm băm gán thêm vào entropy => Entropy mới dài 132 bit
- B2: Chia entropy thành 12 phần, mỗi phần dài 11 bit
- B3: Mỗi phần khi chuyển sang hệ thập phân sẽ tương ứng với thứ tự các từ trong từ điển của chuẩn BIP39.
11111111111
bằng 2047 tương ứng với từzoo
, từ thứ 2048 của từ điển tiếng anh trong BIP39.
Từ Mnemonic tạo ra các tài khoản như thế nào?
Với mnemomic thu được ở bước trên, chúng ta tiếp tục tìm hiểu quá trình tạo ra seed (dài 512 bit).
- B1: Cụm mnemonic sẽ được cộng với 1 chuỗi salt (bắt đầu bằng từ “mnemonic” cộng thêm 1 chuỗi passphare tùy ý). Chức năng của salt tăng độ khó trước các cuộc tấn công từ điển hay vét cạn.
- B2: Băm chuỗi gồm mnemonic và salt với thuật toán HMAC-SHA512 2048 lần.
- B3: Kết quả cuối cùng thu được seed dài 512 bit.
Tiếp theo:
- Từ seed đã có, băm tiếp bằng HMAC-SHA512.
- Lấy 256 bit đầu làm Master Private Key, 256 bit sau làm Master Chaincode.
- Từ Master Private Key sinh ra Master Public Key dựa theo thuật toán ECDSA
Quá trình tạo khóa con:
- Băm Master Public Key, Master Chaincode cộng thêm số thứ tự độ dài 32 bit với HMAC-SHA512 (số thứ tự sau này dùng để phân biệt thứ tự các khóa con tạo ra). Vậy 1 khóa mẹ có thể có 2^32 tức hơn 4 tỷ khóa con.
- Lấy 256 bit đầu làm private key, 256 bit sau làm chain code
- private key con sinh ra public key con bằng thuật toán ECDSA
Lập trình NodeJs để tạo địa chỉ Bitcoin
Chúng ta sẽ sử dụng các thư viện chính như sau:
- bitcoinjs-lib: Thư viện Bitcoin cho NodeJs, thư viện có số lượng download khá lớn.
- tiny-secp256k1: Hỗ trợ chuẩn secp256k1
- bip39: Thư viện hỗ trợ sinh chuỗi kí tự mnemonic
- axios: Thư viện HTTP
- minimist: Thư viện hỗ trợ parse các tham số truyền vào từ dòng lệnh
Mình có tham khảo trust-wallet-core- registry.json để lấy “derivation path” chuẩn khớp với ví tạo ra trên TrustWallet. Trong code này mình có hiển thị địa chỉ ví theo tất cả các chuẩn.
Ghi chú: Để an toàn hơn bạn không nên lưu lại Private Key hay cụm Mnemonic gốc mà nên thay đổi chút như đổi kí tự/từ thứ 3 sáng thứ 11, 5 sang 23,…. Cách đổi do bạn và chỉ bạn mới biết. Khi code bạn tự động thực hiện hoán đổi luôn, chỉ hiển thị trên giao diện Private Key hay cụm đã hoán đổi. Như vậy sẽ an toàn hơn rất nhiều.
Chúng ta tạo tệp bitcoin-wallet.js với nội dung như sau:
const ECPairFactory = require('ecpair').ECPairFactory;
const secp256k1 = require('tiny-secp256k1');
const bitcoin = require('bitcoinjs-lib');
const ECPair = ECPairFactory(secp256k1);
const parseArgs = require('minimist');
const axios = require('axios');
const bip39 = require('bip39');
const BIP32Factory = require('bip32').BIP32Factory;
const bip32 = BIP32Factory(secp256k1);
const bitcoinDerivePath = "m/84'/0'/0'/0/0";
function _mnemonicToPrivateWif(mnemonic) {
let seed = bip39.mnemonicToSeedSync(mnemonic);
let root = bip32.fromSeed(seed);
let child1 = root.derivePath(bitcoinDerivePath);
let wif = child1.toWIF();
return wif;
}
async function _getAccountInfo(address) {
const result = await axios.get(`https://blockchain.info/rawaddr/${address}`);
if (result) {
let data = result.data;
if (data.final_balance) data.final_balance = data.final_balance / 10**8;
if (data.total_received) data.total_received = data.total_received / 10**8;
if (data.total_sent) data.total_sent = data.total_sent / 10**8;
return data;
}
return {};
}
async function _showBitcoinAccountInfoFromKeyPair(keyPair) {
let result1 = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey
});
let result2 = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: keyPair.publicKey
})
});
let result3 = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey });
console.log("Bitcoin Account:");
console.log("\tPublic Address 1 (P2PKH):", result1.address);
console.log("\tPublic Address 2 (P2SH):", result2.address);
console.log("\tPublic Address 3 (P2WPKH):", result3.address);
console.log("\tAccount Info:", `https://blockchain.info/rawaddr/${result1.address}`);
if (keyPair.mnemonic) {
console.log("\tMnemonic:", keyPair.mnemonic);
}
console.log("\tPrivateWif:", keyPair.toWIF());
console.log("\tPrivate Key:", '0x' + keyPair.privateKey.toString('hex'));
let accountInfo = await _getAccountInfo(result1.address);
console.log("\tBalance:", accountInfo.final_balance);
console.log("\tTotal Sent:", accountInfo.total_sent);
console.log("\tTotal Received:", accountInfo.total_received);
console.log("\tTrans Num:", accountInfo.total_received);
}
async function showAccountInfo(opts) {
if (!opts.address) return false;
let accountInfo = await _getAccountInfo(opts.address);
console.log(`Account Info of ${opts.address}:`)
console.log("\tBalance:", accountInfo.final_balance);
console.log("\tTotal Sent:", accountInfo.total_sent);
console.log("\tTotal Received:", accountInfo.total_received);
console.log("\tTrans Num:", accountInfo.n_tx);
return true;
}
async function generateBitcoinAccount(opts) {
let keyPair = null;
if (opts.type=="mnemonic") {
let mnemonic = bip39.generateMnemonic();
let wif = _mnemonicToPrivateWif(mnemonic);
keyPair = ECPair.fromWIF(wif);
keyPair.mnemonic = mnemonic;
} else {
keyPair = ECPair.makeRandom();
}
await _showBitcoinAccountInfoFromKeyPair(keyPair);
}
async function restoreBitcoinAccount(opts) {
if (!opts.mnemonic && !opts.privateKey && !opts.privateWif) return false;
let mnemonic = opts.mnemonic;
let privateKey = opts.privateKey;
let privateWif = opts.privateWif;
var keyPair = null;
if (mnemonic) {
let wif = _mnemonicToPrivateWif(mnemonic);
keyPair = ECPair.fromWIF(wif);
keyPair.mnemonic = mnemonic;
} else if (privateKey) {
if (privateKey.startsWith("0x") || privateKey.startsWith("0X")) {
privateKey = privateKey.substr(2);
}
let bytes = Buffer.from(privateKey, "hex");
keyPair = ECPair.fromPrivateKey(bytes);
} else if (privateWif) {
keyPair = ECPair.fromWIF(privateWif);
}
await _showBitcoinAccountInfoFromKeyPair(keyPair);
return true;
}
function showHelp() {
console.log("Command to run:");
console.log("\tnode bitcoin-wallet.js <options>");
console.log("\tnode bitcoin-wallet.js --help");
console.log("For examples:")
console.log("\tnode bitcoin-wallet.js --act=generate-account");
console.log("\tnode bitcoin-wallet.js --act=generate-account --type=mnemonic");
console.log("\tnode bitcoin-wallet.js --act=restore-account --mnemonic=\"abc xyz\"");
console.log("\tnode bitcoin-wallet.js --act=restore-account --privateKey=xxxxxxxxxxxxxxxx");
console.log("\tnode bitcoin-wallet.js --act=restore-account --privateWif=xxxxxxxxxxxxxxxx");
console.log("\tnode bitcoin-wallet.js --act=show-account --address=1LQoWist8KkaUXSPKZHNvEyfrEkPHzSsCd");
}
async function main() {
var opts = parseArgs(process.argv.slice(2), {
string: [ "address", "privateKey" ]
});
// console.log("Options:", opts);
if (opts.help) {
showHelp();
process.exit(0);
}
if (opts.network) network = opts.network;
let act = opts.act;
if (act=="generate-account") {
await generateBitcoinAccount(opts);
} else if (act=="restore-account") {
if (!await restoreBitcoinAccount(opts)) showHelp();
} else if (act=="show-account") {
if (!await showAccountInfo(opts)) showHelp();
} else {
showHelp();
}
}
main();
Trước khi chạy, bạn cần đánh lệnh sau để cài đặt thư viện cần thiết:
npm install bitcoinjs-lib ecpair bip32 bip39 tiny-secp256k1 axios minimist
Tiếp theo, bạn đánh lệnh sau để xem các lệnh:
node bitcoin-wallet.js –help

Có hai cách tạo ví mới, nếu bạn muốn sinh ngẫu nhiên 1 Private Key:
node bitcoin-wallet.js –act=generate-account

Sinh địa chỉ ví Bitcoin mới bằng cách tạo cụm từ mnemonic:
node bitcoin-wallet.js –act=generate-account –type=mnemonic

Bạn có thể sử dụng cụm từ mnemonic ở trên để import vào ví TrustWallet bạn sẽ nhìn thấy địa chỉ Bitcoin giống địa chỉ Public Address 3 ở trên.
Ngoài ra chúng ta có thể khôi phục account từ mnemonic, private key hay private wif:
node bitcoin-wallet.js –act=restore-account –mnemonic=”honey bomb payment heart gown notable symptom spot sorry giraffe stuff vicious”

Tham khảo:
Trả lời