Nino Filiu

Posted on

# Writing a blockchain in 60 readable lines of TypeScript

Blockchain: it gets more confusing every year because people explain it with weird metaphors instead of talking 5mn about the actual maths & code involved

And it frustrate me a lot because the concept of a blockchain is actually pretty simple:

• a blockchain is a list of blocks
• a block is
• some data
• the hash of the previous block
• some token
• the hash of the above

A block is valid if the hash has a magic prefix, which is when it begins by a certain number of zeros, so if you want to add some data to the blockchain, you'll have to pack it into a block, and to do so you'll have to find a token that produces a block hash that begins by a certain number of zeros, which is computationally intensive, and that's how you get proof of work.

Ok to be honest, I'm just talking about a particular type of blockchain there, but that's not the point. Coding the following blockchain helped me understand some crypto basics and I hope that'll help you too. I am not bragging about how clever I am to code the same tech as Bitcoin in 60 lines or whatever.

# Anyway here's the code

I am using Deno and TypeScript.

Let's start by importing some hasher we'll need later on

``````import { createHash } from "https://deno.land/std@0.91.0/hash/mod.ts";
``````

Let's define blocks and agents. An agent represents the kind of programs running on your computer that can trade Bitcoins with other agents around the world.

``````type Block = {
data: string;
prev: string;
token: string;
hash: string;
};
type Agent = {
};
``````

We're gonna use md5 as our hash function. It's definitely not the most secure one but we don't really care. Let's define our magic prefix `00000` there too so we don't repeat it afterward. The more `0` there are, the more difficult it is to mine a new block.

``````const hashOf = (str: string) => createHash("md5")
.update(str)
.toString();
const magicPrefix = Array(5).fill("0").join("");
``````

Now let's create our agent factory. Internally, it keeps the whole chain in memory, and a list of all agents it needs to broadcast to when mining a new block.

The first block is the "Genesis Block" which is exempted from pointing to the previous block's hash, or having a magic prefix.

``````const createAgent = (): Agent => {
const chain: Block[] = [{
data: "",
prev: "",
token: "",
hash: hashOf(""),
}];
const agents: Agent[] = [];
return {
addAgent(agent) { /* ... */ },
addData(data) { /* ... */ },
receiveBlock(block) { /* ... */ },
};
};
``````

The `addAgent` method does not need further explanations:

``````addAgent(agent) {
agents.push(agent);
},
``````

`addData` is basically where the mining happens. It's the computationally intensive loop to find the token that'll produce a block hash with a magic prefix.

I chose `hashOf(Math.random().toString())` to generate a random string because that's a very concise way to do so but hashing is not needed there.

``````addData(data) {
while (true) {
const prev = chain[chain.length - 1].hash;
const token = hashOf(Math.random().toString());
const hash = hashOf(data + prev + token);
if (hash.startsWith(magicPrefix)) {
const block: Block = { data, prev, token, hash };
chain.push(block);
for (const agent of agents) {
}
return;
}
}
},
``````

`receiveBlock` validates if a new block can be added on top of the chain according to the conditions above, and if everything is ok, adds it, else throws.

``````receiveBlock(block) {
if (block.prev != chain[chain.length - 1].hash) {
throw new Error(
"Hash does not point to the previous hash in the chain",
);
}
if (!block.hash.startsWith(magicPrefix)) {
}
const actualHash = hashOf(block.data + block.prev + block.token);
if (actualHash !== block.hash) {
throw new Error("Hash is not the hash of data|prev|token");
}
chain.push(block);
},
``````

And... that's it!

You can run "simulations" with such agents, for example this one where two agents add greeting blocks. It should run without printing anything and without throwing:

``````const alice = createAgent();
const bob = createAgent();
``````

or this "simulation" where we try to inject a malevolent block into the blockchain but it gets caught:

``````const alice = createAgent();
const prev = hashOf("");
data,
prev,
token: "",
hash: hashOf(data + prev),
});
// error: Uncaught Error: Hash does not start with the magic prefix
``````

# So... that's it?

Well, yes, at the core, there's not a lot more to blockchain than this. But in order to go from there to building a real cryptocurrency, you'd probably have to

• replace the string data payload by signed and timestamped transactions
• find ways to compensate miners for their work
• build the p2p network layer
• convince people that this cryptocurrency has value
• etc

TL;DR it's usually not the blockchain part that is hard but rather what's around it

Kristiqn Tachev

It is pretty nice explanation :)

Awesome!

Keep it up!

Forpee

How do I upload this blockchain online? For others to interact with

Nino Filiu

That's what precised in the last paragraph

But in order to go from there to building a real cryptocurrency, you'd probably have to

• build the p2p network layer

I do not cover bringing this blockchain online in this article, I just explain the core algorithm as it would run on one particular machine

Building a blockchain network is actually where blockchain gets complicated algorithmically ^^