SaCoin (XSC)
With the increasing use and popularity around blockchain (and by extension, cryptocurrencies), I decided to learn more about it, what better ways are there other than learning that by building one? This project would mainly be for educational purposes whilst being usable for production purposes.
Introduction
Blockchain, what is that?
Well it's an immutable singly-linked chain of blocks but how is that different to data-structures like linked-lists and arrays?
Blocks are containers associated to a hash based on its information (such as the time it was created, the data it contains, the hash of the previous block, ...) which depends on the hash of the previous (parent) block it's connected to.
A blockchain is also (usually) decentralised, in other words built on a P2P (Peer-to-Peer) network but for the sake of understanding and experimentation, I will also try to build a REST-based version.
There's several types of blockchains, the main one being the transaction based one (i.e. bitcoin and altcoin) which is what I'll be focusing on.
Also, isn't it just a single chain of blocks like linked-lists? No, it's more like a tree (a merkel tree to be exact) where the longest chain is considered as the blockchain while the rest are orphans.
Pre-requisite
It would be preferable if you have a foundation in the following:
- data-structures
- variable scopes
- cryptography
- JavaScript ES5 and 6
- NodeJS
Step 0
Choosing the right data-structure and approach. You may have seen some implementations using arrays storing blocks, hash tables storing references from blocks to their hashes, tree based ones, ...
So let's look at the complexity of each useful data-structures and comparing them:
Average/Worst | Merkel trees | Linked-lists | Arrays | Hash tables |
---|---|---|---|---|
Space | O(n) | O(n) | O(n) | O(n) |
Search | O(log2(n))/O(logk(n)) | Θ(n)/O(n) | Θ(n)/O(n) | Θ(1)/O(n) |
Traversal/Access | O(n) | Θ(n)/O(n) | Θ(1)/O(1) | Θ(n)/O(n+k) |
Insert | O(log2(n))/O(logk(n)) | Θ(1)/O(1) | Θ(n)/O(n) | Θ(1)/O(n) |
Delete | O(log2(n))/O(logk(n)) | Θ(1)/O(1) | Θ(n)/O(n) | Θ(1)/O(n) |
Sync | O(log2(n))/O(n) |
Now you might be thinking well, there's no clear winner and it's now down to what operations matters to you the most. But since we're going on about blockchain, we can ignore the delete row.
Why should you care about Merkel trees? As this thread points out, it's what is the best when it comes to distributed crypto-systems but we'll also use arrays (at least for the blocks).
Step 1
As mentioned above, the foundation of blockchain are blocks so we'll start with that:
So here's the main part of the code:
const DIFFICULTY = DIFFICULTY; let prvProps = ;const ROOT_HASH = 'b076b4ac5dfd570677538e23b54818022a379d2e8da1ef6f1b40f08965b528ff'; /** * @description Block. * @param * @param * @param * @param */ { prvProps; this; }
So here you can see that each blocks has a previous hash, a list of transactions, difficulty (I'll come to this later), nonce (used to get a specific hash) and the height.
Note: I'll be using a WeakMap
(identified as prvProps
) based approach to ensure the privacy of the fields by restricting what can be accessed
and changed using get
/set
methods as such:
/** * @description Get the block's hash which also acts as its header. * @return */ { return prvPropshash}
Now we need the chain which is going to contain the blocks.
const Block = DIFFICULTY = DIFFICULTY; /** * @description Creates a blockchain * @param * @param * @param * @param */ { // genesisBlock.mine(); prvProps; }
With that in mind we need the object that will allow coins to be moved from A to B, which is where transactions come into play:
const TRANSACTION_FEE = sign = SHA256 = ; /** *@description Crypto transaction. * @param * @param * @param * @param * @param * @param */ { prvProps; this; }
As you can see a transaction doesn't only require public keys (unlike some other blockchain implementations), this is because
by adding addresses (fromAddr
and toAddr
, so a bit like what Bitcoin has), the visibility of both parties involved (read the sender and receiver) would be
more anonymous and thus less likely to get spoofed by someone else (since their public keys are visible) so it ensures more security.
There's only one public key (the sender's) because he/she owns the amount
of coins being transferred so we'll need him/her to sign it
but the problem is that he/she would need to do so when the transaction's hash is obtained from the other information as shown below:
/** * @description Calculate the hash of the transaction. * @return */ { return ;}
So it could then be signed like so:
/** * @description Sign this transaction with a given private key. * @param */ { prvPropssignature = ;}
We also need to bar in mind that in order to add blocks we need to mine them which would generate a transaction with a reward for the miner as well as allowing him/her to keep fees on transactions within the block he/she mined.
But wait, how can users know how much money they have without necessarily having to go through the whole chain and doing some maths to know how many coins they have? That's where wallets come into play, but unlike real ones, they don't contain any coins but know (based on arithmetic operations) how many coins people have.
/** * @description Electronic wallet. * @param * @param {{pk: Key, sk: Key}} [keyPair=genKey()] * @param * @param */ { prvProps; } /** * @description Generate a Wallet address. * @param * @param * @return */ static { return ; } /** * @description Calculate the wallet's balance by going through its associated blockchain. * @param * @return */ { let balance = 0; //Loop over each block and each transaction inside the block for const block of blockchainchain for const tx of blocktransactions //If the given address is the sender -> reduce the balance if txfromAddr === thisaddress balance -= txamount; //If the given address is the receiver -> increase the balance if txtoAddr === thisaddress balance += txamount; return balance; }
But wait, in order to know how much coin I has, my wallet would go through the whole chain every time I call that method.
There must be a way to save some computation time by saving those amounts into an object like a hash table that links
addresses to amounts of unspent coins.
And how is are the transactions reflected on the blockchain? Well they are placed in a pool of pending transactions within the blockchain and then placed into a new block that was mined. It's possible using the Proof of Work which would require the miner to prove that he/she has enough computational power to mine a block and add it to the chain (otherwise anyone could add any blocks and it would be a mess) but since it takes some computational power to do this, it's necessary to have an incentive for miners to mine blocks (in other words, a reward).
//block.js/** * @description Increment the nonce until a valid hash is obtained with enough 0's at the beginning (based on the difficulty). */ { let diff = prvPropsdifficulty; while thishash !== '0' prvPropsnonce++; this; } //blockchain.js/** * @description Add a transaction to the list of pending ones. * @param * @throws */ { if transactionamount < 0 throw `Negative transactions aren\'t doable (from to )`; //throw new Error('Negative transactions aren\'t doable'); //senderBalance is the balance of the sender if transactionfromPubKey !== BANK && senderBalance < transactionamount throw `The transaction requires more coins than the sender () has ( off )`;//throw new Error(`The transaction requires more coins than the sender has (${transaction.amount} ${this.currencySymbol} off ${senderBalance} ${this.currencySymbol})`); prvPropspendingTransactions;} /** * @description Mine a new block and prepare the miner's reward. * @param */ { //Create a new block with all pending transactions and mine it and add the newly mined block to the chain this; //Reset the pending transactions and send the mining reward let rewardTx = BANKaddress BANKpk minerWalletaddress thisminingReward '' 0; rewardTx; prvPropspendingTransactions = rewardTx;} /** * @description Add a new block. * @param * @param */ { let prevBlock = this ba = beneficiaryAddr || prevBlockbeneficiaryAddr newBlock = prevBlockhash transactions 0 prevBlockheight + 1 ba; newBlock; prvPropschain; //Update the UT pool let pool = thisutpool; transactions;}
As you can see the UT (Unspent Transaction) pool is being used here to permit having an up-to-date idea of how many coins each
addresses has that are interacting with that blockchain (this is what permits users in having an idea of how many coins they have
without having to go through the whole chain even via Wallet.calculateBalance()
).
The way I went about this UT pool is as follows:
/** * @description Unspent Transaction Pool. * @param */ { prvProps } /** * @description Get the UT's pool. * @return */ { return prvPropspool; } /** * @description Add an unspent transaction * @param * @param * @throws */ { if typeof amount !== 'number' throw `The UT amount needs to be a number not `; let pool = prvPropspool; if pooladdr if typeof pooladdr !== 'number' pooladdr = Numberpooladdr; pooladdr += amount; else pooladdr = amount }
Contributing
If you think that I did/got something wrong or want to suggest X or Y changes/additions then feel free to create an issue or PR while following what this says.
Contributors
Maximilian Berkmann 🐛 💻 📖 🤔 💬 👀 🛡️ ⚠️ |
Dependabot 🔧 |
Semantic Release Bot 📖 📦 |
ImgBot 🎨 |
Codacy Badger 📖 |
---|
Resources
- nodejscrypto
- build-a-bitcoin-blockchain-in-javascript-part-1
- blockchain-in-js
- naivecoin
- building-a-blockchain-the-grey-paper
- how-to-run-a-blockchain-on-a-deserted-island-with-pen-and-paper
- code-a-bitcoin-blockchain-in-javascript
- How-are-Merkle-trees-used-in-a-blockchain
- Writing-tiny-blockchain-in-JavaScript
- Implementing-proof-of-work-javascript-blockchain
- Transactions-and-mining-rewards
- simple-blockchain-javascript
- blockchaindemo.io
- naivechain
- create-tiny-blockchain-using-javascript
- blockchain-cli
- money-blockchains-and-social-scalability