Tạo 1 blockchain với Java (Part 3)

Trong 2 bài viết trước mình đã giới thiệu qua về việc tạo block chain với Java và việc khai thác các block. Các bạn có thể tham khảo bài viết trước để biết thêm chi tiết. Bài viết này mình sẽ tiếp tục giới thiệu về:

  • Tạo một ví lưu trữ
  • Gửi các giao dịch sử dụng blockchain.

Căn cứ vào các bài viết trước, chúng ta đã đã có 1 blockchain với khả năng có thể kiểm tra tính toàn vẹn. Nhưng hiện tại blockchain của chúng ta chỉ chứa những message đơn giản. Bây giờ chúng ta sẽ thay thế dữ liệu này bằng các giao dịch(block của chúng ta có thể nắm giữ nhiều giao dịch), cho phép tạo ra một đồng tiền ảo ở mức rất rất đơn giản 😄.

Lưu ý: Bài viết này giả định bạn đã thực hiện theo hướng dẫn ở bài viết trước 😛 Để thực hiện được các đoạn code dưới đây bạn phải import thêm thư viện bounceycastle. Vậy Bouncy Castle là gì? Bouncy Castle là một API mật mã cho Java, được cung cấp bởi một tổ chức từ thiện phi lợi nhuận có trụ sở tại Úc. Nó cung cấp một số công cụ mã hoá, như chữ ký số ...

Bạn có thể download thư viện này tại bcprov-jdk15on-159.jar và import vào eclipse như sau. Trong Eclipse mở Windows >preferences trên menu, chọn Java >Build path > User Libraries tab. Click new và nhập tên mới cho User Library: như “bouncycastle_lib” và click ok. Với “bouncycastle_lib” được chọn và click Add External JARs sau đó chọn đến file bcprov-jdk15on-159.jar bạn vừa download. Click Apply và Close. Right click your package in package explorer > Build path > Add Libraries. Chọn User libraries sau đó click next, giờ chỉ cần tick “bouncycastle_lib” và finish thế là xong.

1. Chuẩn bị ví

Trong các loại tiền tệ kĩ thuật số, quyền sở hữu đồng tiền được chuyển giao trên Blockchain như các giao dịch, người tham gia có địa chỉ ví mà đồng tiền có thể được gửi đến và đi. Trong ví dụ cơ bản, ví của họ có thể lưu trữ các địa chỉ này, hầu hết các ví đều có thể thực hiện các giao dịch mới trên Blockchain.

Đừng lo lắng về thông tin về giao dịch, điều này sẽ sớm được giải thích :)

Nào hãy cùng tạo Wallet Class nắm giữ public key(khóa công khai) và private keys(khóa bí mật)

package vutachain;

import java.security.*;

public class Wallet {
	public PrivateKey privateKey;
	public PublicKey publicKey;
}

Vậy public key(khóa công khai) và private keys(khóa bí mật) dùng để làm gì?

Đối với đồng tiền kĩ thuật số của chúng ta, public key(khóa công khai) sẽ hoạt động như địa chỉ. Bạn có thể chia sẻ khóa công khai này với những người khác để nhận thanh toán. Private keys(khóa bí mật) được sử dụng để ký các giao dịch, để không ai có thể chi tiêu đồng tiền ngoài chủ sở hữu của chúng. Người dùng sẽ phải giữ bí mật Private keys(khóa bí mật) của họ! Chúng ta cũng gửi khóa công khai cùng với giao dịch và nó có thể sử dụng để xác minh rằng chữ ký hợp lệ và dữ liệu không bị giả mạo.

Private keys(khóa bí mật) được sử dụng để ký trên dữ liệu mà chúng ta không muốn bị giả mạo. Public key(khóa công khai) xác minh chứ ký

Chúng ta sẽ sinh ra cặp khóa như sau

package vutachain;

import java.security.*;
import java.security.spec.ECGenParameterSpec;

public class Wallet {
	public PrivateKey privateKey;
	public PublicKey publicKey;

	public Wallet() {
		generateKeyPair();
	}

	public void generateKeyPair() {
		try {
			KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "BC");
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
			// Initialize the key generator and generate a KeyPair
			keyGen.initialize(ecSpec, random); // 256 bytes provides an
												// acceptable security level
			KeyPair keyPair = keyGen.generateKeyPair();
			// Set the public and private keys from the keyPair
			privateKey = keyPair.getPrivate();
			publicKey = keyPair.getPublic();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

Bây giờ chúng ta có các phác thảo của một ví giờ là lúc cần quan tâm đến các giao dịch.

2. Các giao dịch và chữ ký số

Mỗi giao dịch sẽ mang một lượng dữ liệu nhất định:

  • Khóa công khai (địa chỉ) của người gửi tiền.
  • Khóa công khai (địa chỉ) của người nhận tiền.
  • Giá trị / số tiền cần chuyển.
  • Đầu vào, là các tham chiếu đến các giao dịch trước đó chứng minh người gửi có tiền để gửi.
  • Kết quả, cho thấy số lượng địa chỉ liên quan nhận được trong giao dịch. (Những đầu ra này được tham chiếu như đầu vào trong các giao dịch mới)
  • Một chữ ký mã hoá, chứng minh chủ sở hữu địa chỉ là người gửi giao dịch này và dữ liệu không bị thay đổi. (ví dụ: ngăn cản một bên thứ ba thay đổi số tiền đã gửi)

Giờ hãy cùng tạo 2 class rỗng TransactionInput và TransactionOutput(đừng lo lắng về 2 class này vội nhé) Tiếp đến là Transaction class

package vutachain;

import java.security.*;
import java.util.ArrayList;

public class Transaction {

	public String transactionId; // this is also the hash of the transaction.
	public PublicKey sender; // senders address/public key.
	public PublicKey reciepient; // Recipients address/public key.
	public float value;
	public byte[] signature; // this is to prevent anybody else from spending
								// funds in our wallet.

	public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
	public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();

	private static int sequence = 0; // a rough count of how many transactions
										// have been generated.

	// Constructor:
	public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
		this.sender = from;
		this.reciepient = to;
		this.value = value;
		this.inputs = inputs;
	}

	// This Calculates the transaction hash (which will be used as its Id)
	private String calulateHash() {
		sequence++; // increase the sequence to avoid 2 identical transactions
					// having the same hash
		return StringUtil.applySha256(StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient)
				+ Float.toString(value) + sequence);
	}
}

Lớp giao dịch của chúng ta sẽ bao gồm các phương pháp có liên quan để tạo / xác minh chữ ký và xác minh giao dịch.

Nhưng chờ chút ...

Mục đích của chữ ký là gì và nó làm việc như thế nào?

Chữ ký thực hiện hai nhiệm vụ rất quan trọng trên blockchain: Thứ nhất, nó chỉ cho phép chủ sở hữu chi tiêu tiền của họ, thứ hai, nó ngăn chặn người khác can thiệp vào giao dịch trước khi một block mới được khai thác.

Ví dụ: Bob muốn gửi 2 coin đến Sally, do đó ví của họ tạo ra giao dịch và gửi cho người khai thác mỏ để thêm dữ liệu vào trong block tiếp theo. Một thợ mỏ cố gắng để thay đổi người nhận của 2 đồng tiền là John. Tuy nhiên, may mắn thay, Bob đã ký kết dữ liệu giao dịch với khóa riêng của mình, cho phép bất cứ ai kiểm tra xem dữ liệu giao dịch đã được thay đổi bằng khóa công khai của Bob hay không.

Chúng ta có thể thấy trong đoạn code trên bị thiếu mất phương thức StringUtil.getStringFromKey, hãy cùng thêm vào StringUtil class như sau nhé

package vutachain;

import java.security.Key;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;

public class StringUtil {
	// Applies Sha256 to a string and returns the result.
	public static String applySha256(String input) {
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-256");
			// Applies sha256 to our input,
			byte[] hash = digest.digest(input.getBytes("UTF-8"));
			StringBuffer hexString = new StringBuffer(); // This will contain
															// hash as
															// hexidecimal
			for (int i = 0; i < hash.length; i++) {
				String hex = Integer.toHexString(0xff & hash[i]);
				if (hex.length() == 1)
					hexString.append('0');
				hexString.append(hex);
			}
			return hexString.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// Applies ECDSA Signature and returns the result ( as bytes ).
	public static byte[] applyECDSASig(PrivateKey privateKey, String input) {
		Signature dsa;
		byte[] output = new byte[0];
		try {
			dsa = Signature.getInstance("ECDSA", "BC");
			dsa.initSign(privateKey);
			byte[] strByte = input.getBytes();
			dsa.update(strByte);
			byte[] realSig = dsa.sign();
			output = realSig;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return output;
	}

	// Verifies a String signature
	public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
		try {
			Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
			ecdsaVerify.initVerify(publicKey);
			ecdsaVerify.update(data.getBytes());
			return ecdsaVerify.verify(signature);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static String getStringFromKey(Key key) {
		return Base64.getEncoder().encodeToString(key.getEncoded());
	}
}

Đừng lo lắng quá nhiều về việc hiểu nội dung của các phương thức này. Tất cả những gì bạn thực sự cần biết là: applyECDSASig lấy khoá cá nhân và chuỗi nhập của người gửi, ký hiệu nó và trả về một mảng các byte. verifySETSASig lấy chữ ký, khoá công khai và chuỗi dữ liệu và trả về true hoặc false nếu chữ ký là hợp lệ. getStringFromKey trả về chuỗi được mã hoá từ bất kỳ khóa nào.

Giờ thì thêm vào Transaction class, generateSignature() và verifiySignature():

// Signs all the data we dont wish to be tampered with.
	public void generateSignature(PrivateKey privateKey) {
		String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient)
				+ Float.toString(value);
		signature = StringUtil.applyECDSASig(privateKey, data);
	}

	// Verifies the data we signed hasnt been tampered with
	public boolean verifiySignature() {
		String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient)
				+ Float.toString(value);
		return StringUtil.verifyECDSASig(sender, data, signature);
	}

Chữ ký sẽ được xác minh bởi các thợ mỏ như là một giao dịch mới được thêm vào một khối.

3.Test ví và chữ ký

Trong class VutaChain hãy thay thế nội dung phương thức main như sau

public static Wallet walletA;
	public static Wallet walletB;

	public static void main(String[] args) {
		// Setup Bouncey castle as a Security Provider
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		// Create the new wallets
		walletA = new Wallet();
		walletB = new Wallet();
		// Test public and private keys
		System.out.println("Private and public keys:");
		System.out.println(StringUtil.getStringFromKey(walletA.privateKey));
		System.out.println(StringUtil.getStringFromKey(walletA.publicKey));
		// Create a test transaction from WalletA to walletB
		Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
		transaction.generateSignature(walletA.privateKey);
		// Verify the signature works and verify it from the public key
		System.out.println("Is signature verified");
		System.out.println(transaction.verifiySignature());

	}

Chúng ta tạo ra 2 ví, walletA và walletB sau đó in ra private and public keys của walletA. Sinh ra một giao dịch và ký sử dụng private key của walletA.

Private and public keys: MHsCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEYTBfAgEBBBj9J2k6kMubBHFx7kf5YbeVm7MSJqMV+p6gCgYIKoZIzj0DAQGhNAMyAAQZJuK0dfBV2ZwJCsTBXEMxr4JFV7c/Uqf7k6IXYFpZgL/Gv27cVLqDIo+YfsNcVsU= MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEGSbitHXwVdmcCQrEwVxDMa+CRVe3P1Kn+5OiF2BaWYC/xr9u3FS6gyKPmH7DXFbF Is signature verified true

To be continued... Hẹn gặp các bạn trong bài viết tiếp theo 😄