This repository contains some studies in blockchain that i created based on a Udemy course. It's a toy example of how a blockchain would work.
I decided to change some points from what is shown in the course:
This project :
- Uses
yarn
instead ofnpm
- Uses the babel transpiler, so I can use the latest JavasScript version.
- Applies digital signature to do transactions (the course didn't addressed this point :( so anyone could send a transaction from anyone else to themselves)
- Has no UI (the course just gave the UI ready to use, not explaining how is it done, due to incompatibilities I decided to not implement an UI just now)
# clone the repo
yarn install
yarn node1 #nodes 1 to 5 will start example nodes on ports 5001 to 5005
# Manual start
# on babel
babel-node ./dev/api.js <port> <node address>
# on nodemon
nodemon --watch dev -e js --exec yarn start <port> <node address>
Creating a network
This will add http://localhost:5002
to the known nodes
//[POST] http://localhost:5001/broadcast/nodes
// SENDING
{
url: "http://localhost:5002" // :5003 :5004 :5005
}
Getting the credentials
[GET] http://localhost:5001/user
You will get
- A public key
- A private key
- Pseudonym
Keep the private key safe and don't share it with anyone.
The public key will be used to check signed transactions and the pseudonym is a random name that identifies accounts, something like PSE-DUMLE-DOLPHIN-689DD726702ED04
.
Signing transactions
// POST http://localhost:5001/sign
// Body:
{
transaction: {
sender: "<sender's pseudonym>",
recipient: "<recipient's pseudonym>",
amount: 0,
},
privateKey: "-----BEGIN PRIVATE KEY-----...=\n-----END PRIVATE KEY-----"
}
Your private key should be sent only to your private node. The node will respond with a signature for this transaction.
Broadcasting the transaction
Now that you have the signature, you can broadcast the transaction to the other nodes, the content on transaction:{ ... }
should be the same as before. Remove the field privateKey
and add publicKey
, with a string containing your public key and then add another field called signature
, this should contain the signature that you just received.
// POST http://localhost:5001/broadcast/transaction
//Body:
{
transaction: {
sender: "<sender's pseudonym>",
recipient: "<recipient's pseudonym>",
amount: 0,
},
publicKey: "",
signature: ""
}
Your node will broadcast the message to the known nodes, they will check the signature and add the transaction to their own chain if they reach a consensus.
Mining a new block and receiving a mining reward
When you mine a new block, a transaction will be automatically added, crediting you with some coins.
[GET] http://localhost:5001/mine/<pseudonym>
Recovers the user's pseudonym giving the public key
Response
{
"message": "someCoin API",
"stats": {
"blocksCreated": 0,
"pendingTransactions": 0,
}
}
Creates and returns a new key pair, not saving them.
Response
{
"keyPair": {
"publicKey": "string",
"privateKey": "string",
},
"pseudonym": "string"
}
The pseudonym is a short string derived from your public key, you will need it to do transactions, using it as the sender identifier.
Recovers the user's pseudonym given the public key
Request
{
publicKey: "string"
}
Response
{
"pseudonym":"string"
}
Recovers the user's pseudonym giving the public key
Request
{
publicKey: "string"
}
Response
{
"pseudonym":"..."
}
Generates a user balance given its identifier
Response
{
transactions: {
mined: minedTransactions,
pending: pendingTransactions,
},
ballance: {
ballance: 0,
sended: 0,
received: 0,
miningRewards: 0
}
}
Gets a transaction and the block where it resides given its id
Response
If the transaction's block was mined
{
transaction: <Transaction>,
block: <Block>,
status: 'MINED'
}
If it's a pending transaction
{
transaction: <Transaction>,
status: 'PENDING'
}
Gets a mined block from its hash value
{
"block": <Block>
}
This is the endpoint that you should be using to add new nodes to the network. It will:
- Save the new node's URL address
- Broadcast the URL to the network
- Send the addresses of the network nodes to the new node
Request
{
url: "string"
}
Response
// A success message
Adds a new node to the network without saying it to anyone.
Request
{
node: {
url: ""
},
senderUrl: "",
}
Response
// Success message
Adds network nodes in bulk mode
Request
{
nodesUri: ["string"]
}
Response
// Success message
A special case where you want to add a node and wants that they add you.
Request
{
url: "string"
}
Response
// Success message
Gets the network saved onto a node
{
connectedToNodes: [""],
nodeUrl: ""
}
Gets a mined block from its hash value
{
"block": <Block>
}
Gets all blockchain data and the chain from a node
{
...
}
Creates the digital signature for a transaction to be made
Request
{
transaction:{
sender: "",
recipient: "",
amount: ""
},
privateKey: "string"
}
Response
{
transaction:{
sender: "",
recipient: "",
amount: ""
},
signature: ""
}
Only the first one should be used by a person, the second one is called on broadcasts and adds the transaction to the node where it's called only.
Request
{
transaction:{
amount: "amount",
sender: "sender",
recipient: "recipient",
signature: "signature"
},
publicKey: "string"
}
Response
// Success message
Mines a block to the pending transactions, broadcasts the new block's data to the network and broadcasts a new transaction with the mining reward to itself
Request
{
coinReceiver: ""
}
Response
// Success message
Called when the node registers an error in the chain construction. It requests the chains of all nodes and uses the longest chain rule to replace its chain by the longest one
// Chain replaced or not replaced