
Recently, I dove head-first into the crypto scene and — boom — an entirely new universe opened up. Everything feels so fresh and fun (well…except for the times my short positions got liquidated 🙃).
Naturally, that got me wondering: How does a blockchain actually work under the hood? And what’s this “decentralization” everyone keeps chanting about?
So, I rolled up my sleeves and hacked together a Swift-powered implementation — both to learn and to jot down my thoughts. These are my personal notes; corrections and discussion are more than welcome!
Here’s the Swift + Vapor API Server and SwiftUI Demo App. You’re welcome to clone the full project and play along:
Blockchain Server:https://github.com/xiaohsun/Swift-Blockchain
Blockchain App:https://github.com/xiaohsun/Blockchain_Demo_App
Spin up the server from the command line (port 8080 by default), then open the app and start poking around. Full details are in each repo’s README — ping me if you get stuck.
Big shout-out to Daniel van Flymen for his excellent article that kicked this off; Python folks should definitely give it a read.

How does a blockchain tick?
A blockchain is an ordered list of blocks, each linked to the next by a hash value — think of it as a digital fingerprint. Any blob of data can be pushed through a hash function to produce a fixed-length, unique string.
/// Compute the hash value for a Block
static func hash(block: Block) -> String {
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
guard let blockData = try? encoder.encode(block) else {
return ""
}
let hash = SHA256.hash(data: blockData)
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
What lives inside a block?
Every chain has its quirks, but most blocks carry five fields:
- Index
- Proof (Proof-of-work nonce)
- Timestamp
- Transactions
- PreviousHash (Fingerprint of the prior block)
Let’s start with PreviousHash. This means each block stores the digital fingerprint of the block that came before it.
Think about it: if someone tampers with the data in a block, its Hash Value will change. Then, the PreviousHash recorded in the next block won’t match, invalidating that block and all subsequent blocks in the chain.
This makes any tampering easily detectable, which is a cornerstone of blockchain’s trust and a key aspect of “decentralization.”
Of course, the very first block in the chain doesn’t have a PreviousHash; it’s called Block 0 (the Genesis Block).
/// Create a new Block and append it to the chain
func newBlock(proof: Int, previousHash: String? = nil) -> Block {
let block = Block(
index: chain.count + 1,
timestamp: Date().timeIntervalSince1970,
transactions: currentTransactions,
proof: proof,
previousHash: previousHash ?? Blockchain.hash(block: chain.last!)
)
// Clear the Transactions waiting area
currentTransactions = []
// Attach the new Block to the end of the chain
chain.append(block)
return block
}
init() {
// Genesis block
_ = newBlock(proof: 100, previousHash: "1")
}
Transactions are records of exchanges. Whenever Alice sends Bob some coins, the transaction lands in a pending pool. The next block that gets mined will scoop up everything currently pending, stamp it with the current Timestamp plus previousHash, compute its own hash, and latch on to the chain.
/// Create a new transaction and add it to the waiting area
func newTransaction(sender: String, recipient: String, amount: Int) -> Int {
let transaction = Transaction(
sender: sender,
recipient: recipient,
amount: amount
)
currentTransactions.append(
Transaction(sender: sender, recipient: recipient, amount: amount)
)
// Index of the block that will include this transaction (the next one to be mined)
return lastBlock.index + 1
}
So…how do we “mine” a new block?
This brings us to PoW (Proof of Work). In plain English, PoW says: “Show me you burned real compute to earn this block.”
Imagine the puzzle:
Given x = 10, find y such that 10 + y = 100.
In blockchain-speak that morphs into:
Given x = 10, find y such that hash(10 · y) starts with 0000.
Solve the riddle, win the right to forge the next block — and pocket the block reward!
/// Find a PoW value
func proofOfWork(lastProof: Int) -> Int {
var proof = 0
while !Blockchain.validProof(lastProof: lastProof, proof: proof) {
proof += 1
}
return proof
}
/// Verify if the Proof of Work is correct
static func validProof(lastProof: Int, proof: Int) -> Bool {
let guess = "\(lastProof)\(proof)".data(using: .utf8)!
let hash = SHA256.hash(data: guess)
let hashString = hash.compactMap { String(format: "%02x", $0) }.joined()
// Check if the hash value starts with 4 zeros
return hashString.prefix(4) == "0000"
}
However, the process of calculating PoW involves a lot of computation (like the loop above). While this ensures blocks aren’t created too easily, it also consumes a lot of time and energy. That’s why some chains use different validation methods, like PoS (Proof of Stake). But that’s a story for another day.
But, There Might Be More Than One Solution to the PoW Puzzle
When thousands of computers are trying to solve the puzzle simultaneously, they might find different valid solutions and thus create different blocks, leading to a fork.
That’s where the longest-chain consensus kicks in.
Longest-chain consensus
In a blockchain network, every participating computer (node) needs to register itself so the network is aware of its presence. We can then query other nodes, download their chains, and check if their version is longer and more valid (meaning all PoWs meet the criteria) than our own.
If we find such a chain, we then have to discard our chain and adopt the longer one.
So, even if the blockchain temporarily forks, the longest chain consensus mechanism ensures that the network eventually converges and synchronizes to that single, longest chain. This is how a decentralized network achieves consensus without needing a central authority to make decisions.
(Colloquially, we say “longest chain,” but the actual rule is often the chain with the most accumulated work (total work) that gets adopted, which isn’t necessarily the longest by block count.)
/// Register a new node, e.g. "http://192.168.0.5:5000"
func registerNode(address: String) {
if let url = URL(string: address), let host = url.host {
let node = url.port != nil ? "\(host):\(url.port!)" : host
nodes.insert(node)
}
}
/// Resolve conflicts. Returns true if our chain was replaced.
func resolveConflicts() async throws -> Bool {
var newChain: [Block]? = nil
var maxLength = chain.count
// Get and verify the chains from all nodes in the network
for node in nodes {
let urlString = "http://\(node)/chain"
guard let url = URL(string: urlString) else { continue }
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
continue
}
let decoder = JSONDecoder()
let chainResponse = try decoder.decode(ChainResponse.self, from: data)
// Check if the chain is longer and valid
if chainResponse.length > maxLength && Blockchain.validChain(chain: chainResponse.chain) {
maxLength = chainResponse.length
newChain = chainResponse.chain
}
}
// If we found a longer valid chain, replace our chain
if let newChain = newChain {
chain = newChain
return true
}
return false
}
That’s everything I’ve dug up so far — more to come!
Blockchain may look arcane at first, but once you see how those fingerprints interlock, it becomes clear how the system tackles some of the pain points of traditional, centralized finance. Hats off to the genius who first pieced this all together.
As a total crypto rookie I can’t wait to watch how blockchain reshapes daily life. Stay tuned!