- vừa được xem lúc

Tạo Transaction, tiền thưởng Mining, Phí gas, Mint trên Blockchain với Javascript

0 0 65

Người đăng: Phu Minh

Theo Viblo Asia

Xin chào tất cả các bạn!, vào bài viết trước của mình, mình đã nói về cách tạo một Blockchain đơn giản trong 60 dòng code. Do đó, hôm nay, mình sẽ làm hệ thống giao dịch nhé!

Hãy đọc bài viết trước nếu bạn chưa biết gì về blockchain nhé.

Đồng thời, bạn cũng nên xem hướng dẫn của mình trên Youtube để biết thêm một số thông tin chi tiết nữa nhé:

Mình sẽ cần làm những gì?

Chúng ta sẽ cần một mô hình của một giao dịch bao gồm địa chỉ của người gửi, người nhận, và số lượng tiền mà chúng ta cần gửi. Chúng ta sẽ cho nó vào một pool (bể chứa) các giao dịch.

Để phòng chống các giao dịch lỗi, chúng ta sẽ dùng một signing algorithm (thuận toán ký) kèm với một cặp key. Cặp key này sẽ gồm một key bí mật (private key) và một key công khai (public key). Public key có thể được dùng để làm địa chỉ ví và kiểm tra tính xác thực của chữ kí. Private key sẽ được dùng để kí. Vì chỉ bạn có private key, chỉ có bạn có thể kí được giao dịch của mình, đảm bảo được tính bảo mật.

Chúng ta cũng sẽ nói về khái niệm minting, lần phát hành tiền đầu tiên và phí gas.

Cùng bắt đầu nào!

Transaction class - class cho giao dịch

Mình sẽ có một form như sau:

class Transaction { constructor(from, to, amount) { this.from = from; this.to = to; this.amount = amount; }
}

Đào các giao dịch

Quay trở lại với class Blockchain, trước hết, chúng ta phải tạo một pool giao dịch để chứa các giao dịch đang chờ.

 this.transactions = [];

Mình cũng sẽ tạo một method để đẩy giao dịch vào pool.

 addTransaction(transaction) { this.transactions.push(transaction); }

Và một method để mine:

 mineTransactions() { this.addBlock(new Block(Date.now().toString(), this.transactions)); this.transactions = []; }

Đơn giản là chúng ta sẽ chuyển các giao dịch từ pool vào một block mới rồi clear pool kia đi.

Tiền thưởng

Không một ai muốn mine cho chain của bạn miễn phí cả, bạn cần phải có phần thưởng cho miners.

Đầu tiên, chúng ta sẽ tạo một prop reward, bạn có thể set thành gì cũng được, mình sẽ set thành 297.

 this.reward = 297;

Bây giờ, chúng ta sẽ tạo một giao dịch để chuyển tiền thưởng đến miner.

 mineTransactions(rewardAddress) { this.addBlock(new Block(Date.now().toString(), [new Transaction(CREATE_REWARD_ADDRESS, rewardAddress, this.reward), ...this.transactions])); // Hiện tại, mình sẽ đặt địa chỉ gửi như thế này, chúng ta sẽ quay lại về vấn đề này trong phần tiếp theo của bài. this.transactions = []; }

Minting

Đây là một khái niệm rất nổi tiếng trong blockchain development, nó có nghĩa là in thêm tiền. Chain sẽ mint (in tiền ra) để tạo ra tiền thưởng cho bạn.

Trước tiên, bạn cũng nên tìm hiểu về signing trước, nó là một thuật ngữ rất quan trọng trong cryptography.

Mình sẽ sử dụng một thuật toán dùng bởi Bitcoin và Ethereum để tạo các cặp key - secp256k1.

Vì bài viết này mang tính đơn giản hóa nên chúng ta sẽ không tự tạo lại hàm để sign, thay vào đó, ta sẽ sử dụng package elliptic.

(elliptic cũng hỗ trợ một thuật toán mạnh hơn là Curve25519).

Tải về dùng npm:

npm i elliptic

Tạo một cặp key

Ví dụ:

const EC = require("elliptic").ec, ec = new EC("secp256k1"); const keyPair = ec.genKeyPair();
// public key: keyPair.getPublic("hex")
// private key: keyPair.getPrivate("hex")

Tạo một method sign trong class Transaction:

 sign(keyPair) { // Kiểm tra xem public key có giống với địa chỉ gửi không if (keyPair.getPublic("hex") === this.from) { // Kí this.signature = keyPair.sign(SHA256(this.from + this.to + this.amount), "base64").toDER("hex"); } }

Xác thực

Chain sẽ được xác thực khi mọi block chứa các giao dịch đã được xác thực, và nó được xác thực khi:

  • From, to, amount không bị bỏ trống.
  • Người gửi có đủ số tiền.
  • Chữ ký được verify.

Đầu tiên, ta sẽ tạo một method trong class Blockchain để tính số dư của một địa chỉ.

Mình có thể tạo nó dựa vào lịch sử giao dịch của chain:

 getBalance(address) { let balance = 0; this.chain.forEach(block => { block.data.forEach(transaction => { // Vì bạn là người gửi, bạn đang gửi tiền đi, nên số dư của bạn sẽ bị trừ. if (transaction.from === address) { balance -= transaction.amount; } // Vì bạn là người nhận, bạn đang nhận tiền, nên số dư của bạn sẽ được cộng. if (transaction.to === address) { balance += transaction.amount; } }) }); return balance; }

Mình sẽ có một method như sau trong class Transaction:

 isValid(tx, chain) { return ( tx.from && tx.to && tx.amount && chain.getBalance(tx.from) >= tx.amount && ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount + tx.gas), tx.signature) ); }

Trong class Block, ta cần tạo một method để kiểm tra xem block có chứa các transactions hợp lệ hay không.

 hasValidTransactions(chain) { return this.data.every(transaction => transaction.isValid(transaction, chain)); }

Sửa lại isValid của Blockchain nữa nhé:

 if ( currentBlock.hash !== currentBlock.getHash() || prevBlock.hash !== currentBlock.prevHash || currentBlock.hasValidTransactions(blockchain) ) { return false; }

Bây giờ, chúng ta cũng cần check xem một giao dịch có hợp lệ hay không trước khi cho nó vào pool:

 addTransaction(transaction) { if (transaction.isValid(transaction, this)) { this.transactions.push(transaction); } }

Quay trở lại với minting, mình sẽ tạo một địa chỉ chỉ để mint.

const MINT_KEY_PAIR = ec.genKeyPair();
const MINT_PUBLIC_ADDRESS = MINT_KEY_PAIR.getPublic("hex");

Method mới:

 mineTransactions(rewardAddress) { // Tạo một transaction làm phần thưởng. const rewardTransaction = new Transaction(MINT_PUBLIC_ADDRESS, rewardAddress, this.reward); rewardTransaction.sign(MINT_KEY_PAIR); // Mình sẽ gộp giao dịch trên với pool this.addBlock(new Block(Date.now().toString(), [rewardTransaction, ...this.transactions])); this.transactions = []; }

Địa chỉ để mint sẽ được 1 ngoại lệ: Số dư của nó sẽ không bị kiểm tra vì chúng ta đang in tiền, nên chúng ta cần sửa lại method isValid của Transaction. Ngoài ra, số lượng tiền chuyển cũng phải giống y hệt phần thưởng định sẵn.

 isValid(tx, chain) { return ( tx.from && tx.to && tx.amount && (chain.getBalance(tx.from) >= tx.amount || tx.from === MINT_PUBLIC_ADDRESS && tx.amount === this.reward) && ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount), tx.signature) ); }

Phát hành những đồng coin đầu tiên

Quay trở lại với class Blockchain, mình sẽ có một chút thay đổi với genesis block. Mình sẽ mint một lượng coin cho một địa chỉ của chúng ta. Nếu ai muốn mua coin của chúng ta, họ sẽ phải chuyển tiền cho ta, và chúng ta sẽ gửi lại coins. Đây còn được gọi là một hợp đồng.

Tạo cặp key đó trước.

const holderKeyPair = ec.genKeyPair();

Trong genesis block, tạo một giao dịch để phát hành coin.

 // Chúng ta sẽ ra mắt 100000 coin const initalCoinRelease = new Transaction(MINT_PUBLIC_ADDRESS, holderKeyPair.getPublic("hex"), 100000); this.chain = [new Block(Date.now().toString(), [initalCoinRelease])];

Vấn đề với minting

Nếu bạn đang thắc mắc rằng nếu bất kỳ ai có thể sử dụng minting key, thì chẳng phải mình có thể in ra rất nhiều tiền cho bản thân đúng không? Thực chất thì đúng vậy, nhưng chúng ta sẽ xử lí vấn đề này với một server p2p. Mình sẽ build nó trong bài viết tiếp theo.

Server p2p sẽ khắc phục vấn đề này bằng cách bài trừ các chain có những block:

  • Có lớn hơn hoặc ít hơn 1 transaction cho minting.
  • Có 1 transaction duy nhất, tức miner đã tự tạo mint transaction rồi mine nó chứ không thực sự mine các transaction thật.

Gas fees

Cũng có một loại tiền thưởng khác cho miner gọi là phí gas, nhưng nó có một chút khác biệt. Nó đơn giản chỉ là tiền thưởng riêng của user dành cho miner. Nó sẽ khiến làm cho việc đào coin thêm thu hút hơn với các miners, và nó cũng trả cho tiền điện dùng để mine, và người dùng sẽ phải trả phí gas cao hơn nếu muốn được pick nhanh hơn.

Chúng ta sẽ cho prop gas vào:

 class Transaction { constructor(from, to, amount, gas = 0) { this.from = from; this.to = to; this.amount = amount; this.gas = gas; } sign(keyPair) { if (keyPair.getPublic("hex") === this.from) { // Thêm gas vào this.signature = keyPair.sign(SHA256(this.from + this.to + this.amount + this.gas), "base64").toDER("hex"); } } isValid(tx, chain) { return ( tx.from && tx.to && tx.amount && // Thêm gas vào (chain.getBalance(tx.from) >= tx.amount + tx.gas || tx.from === MINT_PUBLIC_ADDRESS && tx.amount === this.reward) && ec.keyFromPublic(tx.from, "hex").verify(SHA256(tx.from + tx.to + tx.amount + tx.gas), tx.signature) ); } }

Chúng ta cũng sẽ sửa lại method getBalance:

 getBalance(address) { let balance = 0; this.chain.forEach(block => { block.data.forEach(transaction => { if (transaction.from === address) { balance -= transaction.amount; balance -= transaction.gas } if (transaction.to === address) { balance += transaction.amount; } }) }); return balance; }

Chúng ta sẽ đưa gas fee cho miner:

 mineTransactions(rewardAddress) { let gas = 0; this.transactions.forEach(transaction => { gas += transaction.gas; }); const rewardTransaction = new Transaction(MINT_PUBLIC_ADDRESS, rewardAddress, this.reward + gas); rewardTransaction.sign(MINT_KEY_PAIR); // Ngăn chặn hành dộng mint coin và mine luôn transaction đó if (this.transactions.length !== 0) this.addBlock(new Block(Date.now().toString(), [rewardTransaction, ...this.transactions])); this.transactions = []; }

Testing

// Số dư gốc của bạn là 100000 const girlfriendWallet = ec.genKeyPair(); // Tạo giao dịch
const transaction = new Transaction(holderKeyPair.getPublic("hex"), girlfriendWallet.getPublic("hex"), 100, 10);
// Kí giao dịch
transaction.sign(holderKeyPair);
// Cho giao dịch vào pool
JeChain.addTransaction(transaction);
// Đào
JeChain.mineTransactions(holderKeyPair.getPublic("hex")); // In ra số dư của hai tài khoản
console.log("Your balance:", JeChain.getBalance(holderKeyPair.getPublic("hex")));
console.log("Your girlfriend's balance:", JeChain.getBalance(girlfriendWallet.getPublic("hex")));

Nó sẽ trông thế này:

Support me!

Nếu bạn muốn mình ra thêm nhiều bài viết chất lượng hơn, bạn có thể mua cho mình cốc trà sữa bằng cryptos nhé!

Địa chỉ BEP-20 là: 0x1848Ba922d6eD66D4910176998fF4fa77AEb82D5.

Resources

Find me on:

Video trên Youtube:

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 153

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 145

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 110

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 245