Skip to content

Nomad Coders - Nomad Coin - A fully-featured blockchain and cryptocurrency in Golang.

License

Notifications You must be signed in to change notification settings

librity/nc_nomadcoin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nomad Coders - Nomad Coin

Table of Contents

About

A fully-featured blockchain and cryptocurrency using the Go programming language.

Features

  • HTML Explorer
  • REST API
  • CLI
  • Database Backend
  • Mining
  • Transactions
  • Wallets
  • P2P (Websockets)
  • Unit Testing

Future

  • Finish README Notes
  • Verify Transactions
  • Verify Peer Blocks
  • Fully Tested

Getting Started

Go environment

Install the latest version of Go with the asdf version manager:

$ cd
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf
$ cd ~/.asdf
$ git checkout "$(git describe --abbrev=0 --tags)"

# For Ubuntu or other linux distros
$ echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc
$ echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc
$ source ~/.bashrc

$ asdf plugin-add golang https://github.com/kennyp/asdf-golang.git
$ asdf install golang latest
$ asdf list
$ asdf global golang 1.17.1
$ asdf current
$ go help

Installing

$ git clone https://github.com/librity/nc_nomadcoin
$ cd nc_nomadcoin
$ go get
$ asdf reshim golang
$ go run main.go both

Analyze race conditions

Automagically analyze race conditions during execution:

$ go run -race main.go rest -port=5001
$ go run -race main.go rest -port=5002
# or
$ go build -race && nc_nomadcoin rest -port=5001
$ go build -race && nc_nomadcoin rest -port=5002

Godocs

You can browse the documentation of all local packages and projects with the Godocs package:

$ go install golang.org/x/tools/godoc
$ godoc -http=:6060

This will install the executable and start a server listening on http://localhost:6060

Testing

Run all tests with coverage report:

$ go test -v -coverprofile cover.out ./... && go tool cover -html=cover.out

Usage

CLI

$ go run main.go rest -port=PORT                # Start the REST API (recommended)
$ go run main.go explorer -port=PORT            # Start the HTML Explorer
$ go run main.go both -ePort=PORT -rPort=PORT   # Start both REST API and HTML Explorer

HTML Explorer

A simple webserver that lets you browse all the blocks, transactions and wallets.

REST API

It has the same functionality of the HTML Explorer. It also lets you create transactions, mine blocks, and list connected peers.

Notes

Golang is awesome

I really enjoy Go, it's probably my favorite language right now. It blows me away how easy it is to write and refactor code using VSCode's Go extension. I'm catching over 95% of bugs and errors before compilation. I only ever need to worry about:

  • Data races
  • Deadlocks
  • Uninitialized maps and *pointers
  • Functions that receive interface{}s params

Every other mistake gets highlighted the moment I type it. Golang only surprises me when it doesn't work.

Go Routines and Channels

  • Reading from a channel without an active go routines will create a panic.
  • Reading from a closed channel will return nil or the type equivalent (0, "", etc.)
  • Closing a closed channel will create a panic.
  • Sending to a closed channel will create a panic.
  • Channels can be Read-only (<-chan) or Send-only (chan<-).
  • Both sending and receiving are blocking operations for unbuffered channels.
  • Buffered channels have a non-blocking queue of messages (make(chan int, BUFFER_SIZE)). Sending and receiving become blocking operations once the queue is full.

One-way hash functions

Deterministic, easy to compute, hard to invert:

hashFunction("sexy") => "dsdj21321wq0wjdw0jw9djcosaniqij0"
hashFunction("sexy") => "dsdj21321wq0wjdw0jw9djcosaniqij0"
hashFunction("sexyy") => "ri3j9rj2302j0ginvin0n00ivwn0inv0u"
inverseHashFunction("ri3j9rj2302j0ginvin0n00ivwn0inv0u") => UNDEFINED

Many examples exists: MD5, SHA-X, Whirlpool, BLAKEX, etc. Like Bitcoin, we use SHA-256.

Blockchain

newBlockHash := hashFunction(NOnce + previousBlockHash + timestamp + ...)

data could be anything, usually transactions and smart contracts. Any alteration to a previous block's data will avalanche obvious changes to the next blocks' hashes. This makes it so any node in the network can locally verify the integrity of the blockchain.

Bitcoin has its own Block hashing algorithm, where a header of 80 bytes (containing bitcoin version number, the previous block hash, the hash of the block's transactions, the timestamp, the difficulty, and the NOnce) is hashed. In our implementation we transform the entire block instance to Golang's default string representation and hash that.

Mining (Proof of Work)

The only thing a miner is allowed to change in a block (besides adding transactions) is a special number called NOnce. The objective of mining is to change the value of the NOnce so that the block has a hash that start with a determined number of zeros.

The amount of zeroes required to mine a block determines the difficulty of the blockchain, and is adjusted by some mechanism determined by the developer. This consensus mechanisms is called Proof of Work, and it's the one used by most cryptocurrencies. The other major one is Proof of stake. You can play with this mining simulator to get a better idea:

Accounting model

We use the UTXO (Unspent Transaction Output) accounting model, the same one used in BitCoin and Cardano.

Coins are created by a special type of transaction: the coinbase transaction.

type Transaction struct {
	Id     string
	Input  []string
	Output []string
}

coinbaseTx = Transaction{
	Id:     "0001"
	Input:  []string{"$10(blockchain)"},
	Output: []string{"$10(miner)"},
}

Transactions have multiple inputs and outputs. Input is the money you have before the transaction. Output is the money everyone has by the end of the transaction.

txs := []Transaction{}
txs = append(txs, Transaction{
	Id:     "0002"
	Input:  []string{"$10(lior),txId(0001)"},
	Output: []string{"$1(drugDealer)", "$2(landLord)", "$7(lior)"},
})
txs = append(txs, Transaction{
	Id:     "0003"
	Input:  []string{"$7(lior),txId(0002)"},
	Output: []string{"$7(waiFu)"},
})

A transaction Input is a reference to a previous transaction Output. We can only use an Input from a previous Output that's not being used by another transaction in the blockchain or the mempool: An Output becomes "spent" once it's referenced by an Input.

Mempool

Unconfirmed transactions wait on the Mempool until they are added to the blockchain by miners, becoming confirmed.

Digital signing

  1. Hash any digital object (string, picture, json, etc.)
  2. Generate a Public-Private key pair
  3. Sign the hash with the private key
  4. Verify signature with the public key
messageHash := hashFunction("i like turtles")
publicKey, privateKey := makeNewKeys()
signature := sign(messageHash, privateKey)
checksOut := verify(messageHash, signature, publicKey)

All these functions are cryptographic black boxes made with very cool math. There are many different Public-key cryptography algorithms with which to sign and verify data. We will use Elliptic-curve cryptography with the NIST P-256 curve, while Bitcoin uses Secp256k1.

Elliptic Curve Cryptography

We start with some Elliptic curve E, a set of points defined by an equation:

The overall shape of the curve is determined by the real numbers a and b. There's a lot of active research that goes into finding curves without any backdoors and that are hard to brute force.

Because Elliptic curves are Algebraic groups, we can add two arbitrary curve points P and Q and get the curve point R:

If we make P = Q, R will equal the mirror(negative) of the interception of the the tangent line at P with the curve, or 2P:

We can then add some arbitrary point G to itself as many d times as we want, and we will always end up with another point in the curve d . G. We've just invented scalar multiplication of an elliptic curve point:

The bigger d gets, the more points in the curve we end up hitting before reaching the point d . G. This bouncing around the curve looks very pseudo-random, much like the modular arithmetic of Diffie–Hellman key exchange algorithm.

The point of this is that it's computationally impractical to calculate the value of d from d . G, given a large-enough d. It's also very easy for a computer to generate d . G from d and G. This is very similar to a hash function, in that the output is practically non-invertible:

ellipticCurveScalarMultiplication(G, d) => x, y
inversEellipticCurveScalarMultiplication(x, y) => UNDEFINED

These "trapdoor function" are the basis of asymmetric cryptography schemes. When you generate a Private ECDSA key, you are picking a random number d between zero and a very large prime n. The computer then traverses the curve by adding G to itself d times, generating the Public Key K of coordinates (x,y).

With these two asymmetric keys we can securely:

There's some miscellaneous mathematical trickery to make this more secure and/or efficient, but this is fundamentally how it works.

We often project the curve over a finite field n, which is a very large prime number. This means that we perform the scalar multiplication of the points, then take the modulus n of the result. This turn it into a map of discrete values, or affine points:

Elliptic Curve Digital Signature Algorithm (ECDSA)

Alice wants to send a message M to Bob. They both agree on an Elliptic curve E, a generator point G on the curve, and a very large prime number n.

Generate Keys

Alice generates the Private key a by picking a very large number between 1 and n-1. She then generates the Public key P and sends it to Bob.

Sign

Alice calculates the message hash e and transforms it into a number z by taking the leftmost bits of e.

She then picks another random number k between 1 and n-1 (called the NOnce because it's only used once), and multiplies it with G modulus n to get the coordinate r, the first part of the signature.

The second part of the signature s is a combination of k, r, z and the private key a:

Alice then sends Bob the message M, the hash e and the signature (r, s).

Verify

Bob does some basic verifications of the public key P, like checking that it's on the curve and it's not the Identity Element O. He then checks that r and s are integers between 1 and n-1, and that e is the hash of M.

He then calculates z from e the same way Alice did, and uses it to calculate u1 and u2:

And with the public key P calculates the coordinate x1 of the signature point: if x1 is congruent to r modulo n (which means the same as x1 and r differ by a multiple of n) then the signature is valid.

If we didn't project the curve E on a field n (if we didn't modulate the operations), the private key a would be recoverable from the signature (r, s), the hash e and the value of k. This is also why we pick a new random value of k for every signature.

How we securely generate random values of a and k is an important problem, and one that's exploitable by back doors like the one in Dual_EC_DRBG, a random number generator the NSA bugged.

Many devices would still be using that garbage if not for Edward Snowden.

Data races

Data races can occur when:

  • two or more threads (go routines in this case) in a single process access the same memory location concurrently, and
  • at least one of the accesses is for writing, and
  • the threads are not using any exclusive locks to control their accesses to that memory.

When these three conditions hold, the order of accesses is non-deterministic, and the computation may give different results from run to run depending on that order. Source

In Golang we fix data races with blocking channels, sync.WaitGroup or sync.Mutex. They all do pretty much the same thing: lock the variable during read and write to guarantee that only one routine access it at a time.

Peer 2 Peer

This the most important part of understanding blockchain. Bitcoin is fundamentally a peer 2 peer protocol that allows a decentralized network to agree on the data that will be added to a write-only database.

We use Web Sockets to connect the peers and broadcast new transactions and mined blocks.

When node Jr. connects to node Sr., node Jr. send the last block it has. Node Sr. then checks whether Jr. is ahead, behind or on the same block as Jr. Whichever one's ahead end up sending a copy of their blockchain to the other. Then they share a list of the other nodes they're connected to, and initiate this handshake again.

When someone creates a transaction in a node it broadcasts to all the other nodes, creating a "Global Mempool". Each node then receives the transaction and verifies the transaction's signature: if it's valid the transaction is added to the Mempool and relayed to the other nodes.

The nodes then try to mine the next block, and the one that get's lucky broadcasts the block to all other. The other nodes verify the new block's nonce and transactions: if it's valid it is added to the local blockchain and relayed to the other nodes.

Cryptocurrency Investment Advice

- THIS IS NOT INVESTMENT ADVICE.
- I'M NOT RESPONSIBLE FOR YOUR BAD DECISIONS.

This what I've gathered from all my research, merely my opinion:

  • Only bet what you can afford to loose.
  • Don't bet on anything you don't understand.
  • Don't bet on anything you haven't read the code.
  • Don't bet on new currencies if you don't understand the problems they're trying to solve and what they're doing differently.
  • A blockchain is only as good as its dev community.
  • Bet on engineering, not marketability.
  • "I'm an adult and everything I do is my responsibility."
  • Don't be this guy:

Watch the video

To his credit, it's pretty unreasonable to expect someone without any technical knowledge to successfully speculate on shitcoins.

Libs

Docs

Resources

Key-value DB

Blockchain

Go

Go strings

Go gobs

Go HTTP

Go maps

Go import cycles

Go arrays & slices

Go packages

Go templates

Go data races

Go deadlocks

Go big.Int

Go testing

Go crypto

Cryptography

Hash Functions

Encryption

Public-key cryptography

Digital signatures

Elliptic curve cryptography

Elliptic Curve Digital Signature Algorithm - ECDSA

ECDSA Videos

Bitcoin

Accounting models

Favicons

Javascript

ASDF version manager

Design patterns

Golang's http.server architecture

About

Nomad Coders - Nomad Coin - A fully-featured blockchain and cryptocurrency in Golang.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages