Kiến trúc Blockchain – Tạo blockchain với bài toán PoW bằng javascript – Phần 2
■ Gợi nhớ
Trong bài viết trước, chúng ta đã làm quen với cấu trúc cơ bản của một Blockchain. Mình xem lại cấu trúc Blockchain cơ bản một xíu nhé. Đơn vị cấu thành nhỏ nhất trong blockchain là Block. Một Blockchain có nhiều Block nối với nhau. Mỗi Block sẽ chứa những thông tin cơ bản như index, prevHash, timestamp, data, hash. Như vậy, để tạo một Block mới gắn vào tiếp chuỗi Blockchain, thì chúng ta chỉ cần tạo 1 block gồm như thông tin như trên, trong đó, hash được tạo ra bằng thuật toán SHA. Sau đó, gắn block mới tạo vào chuỗi blockchain. Thật là đơn giản phải không ^^.Bạn có thể xem lại phần 1 ở đây.
Nhưng đi sâu hơn một xíu trong thị trường tiền điện tử, điển hình như Bitcoin, chắc hẳn bạn cũng nghe loáng thoáng về máy đào coin, những máy đó phải giải thuật toán để xác nhận các transaction, giải xong bài toàn thì sẽ được thưởng bitcoin, độ khó đào coin ngày càng khó nên việc giải thuật toán cũng càng khó, v.v… Vậy với cái Blockchain cơ bản ở trong phần một mình đã trình bày, thì độ khó nằm đâu? phần thưởng cho máy đào nằm đâu? Cứ bình tĩnh, đọc tiếp phần dưới này nhé.
■ Blockchain với thuật toán Proof-of-Work
Trước tiên mình muốn đưa ra bài toán đơn giản cho dễ hiểu như vầy:
● Bài toán dạng 1: A bằng bao nhiêu?
1 + 2 + 3 + 4 + 5 = A 111 + 222 + 333 + 444 + 555 = A
● Bài toán dạng 2: A bằng bao nhiêu?
1 + 2 + 3 + 4 + A = 111 222 + A + 333 + 444 + 555 = 9999
● Bài toán dạng 3: A bằng bao nhiêu? Trong đó, B là một số tuỳ ý nhưng phải thoả điều kiện
4 + 5 + 8 + 9 + A = B | với điều kiện B phải chia hết cho 3 11 + 22 + 33 + 44 + A = B | với điều kiện chữ số đầu của B phải bằng 3
Trong 3 dạng toán trên, bạn có cảm nhận độ khó tăng dần không. Bài toán dạng một là một bài toàn dạng xuôi khá đơn giản. Nhưng đến bài toán thứ hai, thì bạn phải truy ngược lại để tìm số A. Đến dạng toán thứ 3 thì không những phải truy ngược lại mà còn kèm theo điều kiện. Vì vậy ai giải nhanh nhất sẽ là người chiến thắng và sẽ được thưởng.
Trong Blockchain có áp dụng thuật toán Proof-of-Work cũng vậy. Nhìn vào hình vẽ bên dưới, bạn sẽ thấy trong cấu trúc có thêm một param nonce. Nonce là một con số tuỳ ý, giống như số A trong bài toán ví dụ. Để hiểu rõ hơn bạn nhìn vào hình bên dưới nhé.
Khi có một Block mới muốn add vào Blockchain, Block đó sẽ được tạo ra với những tham số cơ bản và một số nonce. Lúc này, nonce và hash vẫn là ẩn số. Hash sẽ có công thức tính như sau:
hash = SHA256(index + prevHash + timestamp + JSON.stringify(data) + nonce);
và hash tìm được phải thoả kiều kiện 2 chữ số đầu của hash phải bằng 00. Điều điện đó ta gọi nó là độ khó. Độ khó càng tăng khi ta thay đổi điều kiện thoả càng phức tạp.
Vd:
2 chữ số đầu
của hash phải bằng dc
=> độ khó *
5 chữ số đầu
của hash phải bằng dc48e
=> độ khó **
5 chữ số đầu
của hash phải bằng dc48e
và 2 chữ số cuối
của hash phải bằng 00
=> độ khó n*
^^
Để tìm ra hash thoả điều kiện, máy tính sẽ chạy liên tục công thức tính hash bằng SHA256 như trên với con số nonce nào đó. Nếu hash tìm ra không thoả, máy tính sẽ thay đổi số nonce khác rồi tính lại công thức cho đến khi hash tìm được thoả điều kiện. Sẽ có nhiều máy tính tham gia giải bài toán này. Máy tính nào tìm thấy con số nonce tạo ra hash thoả điều kiện đầu tiên sẽ là máy tính tính chiến thằng và sẽ được thưởng.
■ Blockchain với Demo
Viết thử cái demo để chạy xem nhé, cho dễ hình dung hơn.Code toàn bộ ở đây nhé.
Dựa vào demo ở phần 1, chúng ta chỉnh sửa lại chút xíu nhé. Đầu tiên xem flowchart để hình dung code chạy như thế nào nhé.
Đầu tiên, ví dụ có
address_1
gửi đến address_2
100 coin => là 1 transaction
address_3
gửi đến address_4
100 coin => là 1 transaction khác
những transaction sẽ vào hàng đợi, khi block mới được tạo, các transaction sẽ được bỏ vào block đóng vai trò là tham số data. Sau đó miner sẽ bắt đầu giải toán cho block mới đó bằng cách thay đổi con số nonce cho phù hợp để hash thoả điều kiện. Sau khi giải toán thành công, block đó sẽ đuọc add vào blockchain và 1 transaction mới được tạo để chuyển tiền thưởng cho miner.
Dựa vào code ở phần 1, code của chúng ta sẽ thêm như sau:
Định nghĩa thêm class Transaction
// định nghĩa thêm class transaction
// gồm các thông tin cần thiết:
// địa chỉ gửi, địa chỉ đến, số tiền, phí giao dịch
class Transaction{
constructor(fromAddress, toAddress, amount, fee){
this.fromAddress = fromAddress;
this.toAddress = toAddress;
this.amount = amount;
this.fee = fee;
}
}
Trong class Block
, chúng ta thêm một tẹo như sau:
class Block {
constructor(timestamp, transactions, previousHash = '') {
this.previousHash = previousHash;
this.timestamp = timestamp;
this.transactions = transactions;
this.hash = this.calculateHash();
this.nonce = 0; // <------- thêm nonce } calculatehash() { return sha256( this.previoushash + this.timestamp json.stringify(this.transactions) this.nonce <------- ).tostring(); hàm này sẽ chạy loop tìm con số để tạo hash thoả độ khó mineblock(difficulty) while (this.hash.substring(0, difficulty) !="=" array(difficulty 1).join("0")) this.nonce++; this.hash="this.calculateHash();" }< code>
Trong class Blockchain
, sẽ thêm như sau:
class Blockchain{
constructor() {
this.chain = [this.createGenesisBlock()];
// độ khó là 2 chữ số đầu của hash == 00 this.difficulty = 2;
// list transaction đang chờ xác nhận
this.pendingTransactions = [];
// phần thưởng
this.miningReward;
}
//
// sau khi giải được nonce cho block
// miner sẽ được thưởng dựa vào fee chuyển của các transaction
// trong block đã mine thành công
getminingReward() {
let reward = 0;
for(const trans of this.pendingTransactions){
reward += trans.fee;
}
return reward;
}
//
minePendingTransactions(miningRewardAddress){
// tạo block mới và mine tìm số nonce phù hợp
let block = new Block(Date.now(), this.pendingTransactions, this.getLatestBlock().hash);
block.mineBlock(this.difficulty);
console.log('Block successfully mined!');
this.chain.push(block);
// tạo 1 transaction chuyển tiền thưởng
//cho address đã tham gia mine thành công
var miningReward = Number(this.getminingReward())*0.8;
this.pendingTransactions = [
new Transaction(null, miningRewardAddress, miningReward, miningReward*0.1)
];
}
//
// add transaction muốn xác nhận vào hàng đợi
createTransaction(transaction){
this.pendingTransactions.push(transaction);
}
//
// đọc lại lịch sử các block trong chuỗi blockchain
// tính toán số tiền hiện tại của address đó
getBalanceOfAddress(address){
let balance = 0;
for(const block of this.chain){
for(const trans of block.transactions){
if(trans.fromAddress === address){
balance -= trans.amount;
}
if(trans.toAddress === address){
balance += trans.amount;
}
}
}
return balance;
}
}
Còn lại giữ nguyên như code ở phần một nhé.
Chạy thử xem nhé
let landthCoin = new Blockchain();
landthCoin.createTransaction(new Transaction('address1', 'address2', 100, 10));
landthCoin.createTransaction(new Transaction('address2', 'address1', 50, 5));
console.log('\n Starting the miner...');
landthCoin.minePendingTransactions('landth-address');
console.log('\nBalance of landth is', landthCoin.getBalanceOfAddress('landth-address'));
console.log('\n Starting the miner again...');
landthCoin.minePendingTransactions('xaviers-address');
console.log('\nBalance of landth is', landthCoin.getBalanceOfAddress('landth-address'));
Kết quả sau khi chạy console sẽ in ra như sau:
Starting the miner...
BLOCK MINED: 008b274d75d856de3911f9066b97e587c2d02b4b8b1a86fc4c7a13eca8739060
Block successfully mined!
Balance of landth is 0
Starting the miner again...
BLOCK MINED: 00d687f3c63bfecc871217494e807306eb2d73e952087399a81820dfbd252504
Block successfully mined!
Balance of landth is 12