Let's think about this for a second
In this final part, we'll actually wire up the public/private key concepts you learned in the wallets, keys, and wallet-safety chapters. A real wallet signs a transaction with a private key, and everyone else can verify it with the public key (wallet address). We'll also prove out immutability, blockchain's core security property, by writing an isChainValid() function — it recalculates each block's hash and checks it against the original hash, and checks whether the previousHash link is correct. We'll wrap up the whole project by manually tampering with data in the chain and testing whether the validation function catches it.
Let's build it
Generate a wallet keyPair with crypto.generateKeyPairSync('ec', {namedCurve:'secp256k1'}). Add three methods to the Transaction class — calculateHash(), sign(signingKey), and isValid() — where sign() signs the transaction hash with the private key and stores it in the signature field, and isValid() verifies the signature against the public key. In the Blockchain class, write an isChainValid() method — using a for loop, recalculate chain[i].hash with calculateHash() and compare it, and check whether chain[i].previousHash === chain[i-1].hash. Finally, console.log(myChain.isChainValid()) should print true; then manually change chain[1].data and call isChainValid() again — it should now return false.
Code Example
const crypto = require('crypto');
const { generateKeyPairSync, sign, verify } = crypto;
// 1. Wallet key pair
const { publicKey, privateKey } = generateKeyPairSync('ec', {
namedCurve: 'secp256k1',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});
class Transaction {
constructor(fromAddress, toAddress, amount) {
this.fromAddress = fromAddress;
this.toAddress = toAddress;
this.amount = amount;
}
calculateHash() {
return crypto
.createHash('sha256')
.update(this.fromAddress + this.toAddress + this.amount)
.digest('hex');
}
sign(signingKeyPem) {
this.signature = sign('sha256', Buffer.from(this.calculateHash()), signingKeyPem).toString('hex');
}
isValid(publicKeyPem) {
if (!this.signature) return false;
return verify('sha256', Buffer.from(this.calculateHash()), publicKeyPem, Buffer.from(this.signature, 'hex'));
}
}
class Blockchain {
// ... chain, difficulty, mineBlock လို part 2 ကလာတဲ့ property/method တွေ ဆက်သုံးပါ
isChainValid() {
for (let i = 1; i < this.chain.length; i++) {
const current = this.chain[i];
const previous = this.chain[i - 1];
if (current.hash !== current.calculateHash()) return false;
if (current.previousHash !== previous.hash) return false;
}
return true;
}
}
const tx = new Transaction('wallet-A', 'wallet-B', 25);
tx.sign(privateKey);
console.log('Transaction valid?', tx.isValid(publicKey));
// tamper test (myChain က Part 1-2 ကနေ ဆက်ခံထားတဲ့ instance)
console.log('Blockchain valid?', myChain.isChainValid());
myChain.chain[1].data = { amount: 999999 };
console.log('Blockchain valid after tamper?', myChain.isChainValid());
Before tampering, isChainValid() prints true. After manually editing chain[1].data and calling it again, it prints false — proof that the blockchain's tamper detection is working.5-Minute Try-It
Generate two wallet key pairs, sign a transaction from wallet A to wallet B and send it, then spend 5 minutes checking whether isValid() returns true.
A Quick Word of Caution
Never print a private key with console.log(), commit it to a GitHub repo, or anything like that in a real project — this demo generates a throwaway key purely for local learning.