jeffCoin
*** THE REPO IS UNDER CONSTRUCTION - CHECK BACK SOON ***
A cryptocurrency (transaction based data) built on decentralized multi-node P2P open Network using a sha256 Proof of Work (PoW) blockchain with a REST JSON API and a TCP Server to communicate between the Nodes over IP.
Or more simply, a distributed decentralized public ledger.
To dive right in, head down to RUN.
Table of Contents,
- IMPORTANT
- PREREQUISITES
- OVERVIEW
- 1. BLOCKCHAIN
- 2. MINER
- 3. ROUTINGNODE
- 4. WALLET
- 5. WEBSERVER
- RUN
- RUN ON GOOGLE COMPUTE ENGINE (GCE) (OPTIONAL)
This project was built from some of my other projects,
- The BLOCKCHAIN is built from my
single-node-blockchain-with-REST
- The BLOCKCHAIN TRANSACTIONS is built from my bitcoin-ledger
- The ecdsa signature verification from my ecdsa-digital-signature
- The ROUTINGNODE (TCP Server) is built from my simple-tcp-ip-server
- The WALLET for generating keys and creating the jeffCoin address is built from my create-bitcoin-address-from-ecdsa-publickey
- The WEBSERVER (GUI & REST JSON API) is built from my simple-webserver-with-REST
- Other projects I used are my errors, logrus, and flag projects.
Documentation and reference,
- Refer to my cheat sheet on blockchains
- I got a lot of inspiration here
IMPORTANT
Your private keys are kept in /wallet
. The .gitignore
file does ignore them, but just be aware were they live.
There are a few mock keys there used for testing.
PREREQUISITES
go get -v -u github.com/btcsuite/btcutil/base58
go get -v -u golang.org/x/crypto/ripemd160
go get -u -v github.com/gorilla/mux
go get -u -v github.com/sirupsen/logrus
go get -u -v github.com/pkg/errors
OVERVIEW
jeffCoin
(JEFC) is my interpretation of a transaction based (ledger) using a blockchain.
This is a work in progress I feel can be used as a foundation to
build bigger and better things.
Coins (a.k.a jeffCoins) are minted as follows,
- A grand total of 1,000,000 jeffCoins
- The blockchain will not store jeffCoins but addies which are 1/1000 of a jeffCoin (.001 JEFC).
- The founders wallet will start with 100,000 jeffCoins (100,000,000 addies) (10% of all jeffCoins)
- Rewards 1 jeffCoin (1000 addies) every 10 minutes (144 jeffCoins/day or 52,560 jeffCoins/year)
- Will take 17.12 years to mint all the jeffCoins (900,000/52,560 = 17.12)
jeffCoin uses the following technology,
- Written in golang
- Implements a blockchain using a sha256 hash
- A decentralized multi-node P2P architecture maintaining a Network of Nodes
- A Webserver with both a GUI and a REST API
- A TCP Server for inter-node communication
- ECDSA Private & Public Key generation
- Creates a jeffCoin Address from the ECDSA Public Key (Just like bitcoin)
- ECDSA Digital Signature Verification
- Mining uses Proof of Work (PoW)
- Transaction as stored using the unspent transaction output model
What jeffCoin does not have,
- No database, so if the entire Network dies, the chain dies
- Rigorous testing of all corner cases
The following illustration shows how the code is broken up into five main areas,
- 1. BLOCKCHAIN The blockchain and transactions
- 2. MINER To mine the cryptocurrency using PoW
- 3. ROUTINGNODE To maintain a list of nodes & communicate between (Network)
- 4. WALLET To create an jeffCoin Address and keep the private Keys
- 5. WEBSERVER The API and GUI
1. BLOCKCHAIN
The blockchain section is the heart of the entire design. It will keep the transactions secure. A transaction is a transfer of value (jeffCoins) between jeffCoin Addresses. Like bitcoin, the value (jeffCoins) is contained in the ledger. The wallets just hold the public/private keys to request a transaction.
This blockchain section has two main parts, the blockchain and the transactions (the data on the blockchain).
1.1 BLOCKCHAIN
This blockchain is built from my single-node-blockchain-with-REST.
A block in the blockchain is the following go struct,
type blockStruct struct {
BlockID int64 `json:"blockID"`
Timestamp string `json:"timestamp"`
Transactions []transactionStruct `json:"transactions"`
Hash string `json:"hash"`
PrevHash string `json:"prevhash"`
Difficulty int `json:"difficulty"`
Nonce string `json:"nonce"`
}
The states of a block are,
- pendingBlock Receiving transactions and not part of blockchain
- lockedBlock To be mined and added to the blockchain
- Part of Chain Already in the blockchain
This illustration may help,
BLOCKCHAIN-INTERFACE FUNCTIONS
- BLOCKCHAIN
- GetBlockchain() Gets the blockchain
- GenesisBlockchain() Creates the blockchain
- RequestBlockchain()
Requests the blockchain and the pendingBlock from a Network Node
SEND-BLOCKCHAIN Request
- BLOCK
- GetBlock() Gets a block (via Index number) from the blockchain
- LOCKED BLOCK
- GetLockedBlock() Gets the lockedBlock
- AppendLockedBlock() Appends the lockedBlock to the blockchain
- PENDING BLOCK
- GetPendingBlock() Gets the pendingBlock
- ResetPendingBlock() Resets the pendingBlock
- AddTransactionToPendingBlock() Adds a transaction to the pendingBlock and makes change
- LockPendingBlock() Moves the pendingBlock to the lockedBlock
- JEFFCOINS
- GetAddressBalance() Gets the jeffCoin Address balance
- TRANSACTIONS
- ProcessTxRequestMessage() Request to transfer jeffCoins to a jeffCoin Address
- BLOCKCHAIN
- getBlockchain() Gets the blockchain
- loadBlockchain() Loads the entire blockchain
- replaceBlockchain() Replaces blockchain with the longer one
- BLOCK
- getBlock() Gets a block in the blockchain
- calculateBlockHash() Calculates SHA256 hash on a block
- isBlockValid() Checks if block is valid
- LOCKED BLOCK
- getLockedBlock() Gets the lockedBlock
- appendLockedBlock() Appends the lockedBlock to the blockchain
- PENDING BLOCK
- getPendingBlock() Gets the pendingBlock
- loadPendingBlock() Loads the pendingBlock
- resetPendingBlock() Resets the pendingBlock
- addTransactionToPendingBlock() Adds a transaction to the pendingBlock and makes change
- lockPendingBlock() Moves the pendingBlock to the lockedBlock
- JEFFCOINS
- getAddressBalance() Gets the jeffCoin Address balance
1.2 TRANSACTIONS
Transaction are at the heart of jeffCoin, allowing the transfer of value (jeffCoins) from one jeffCoin Address to another. A transaction request comes from the wallet which holds the private key. All transaction requests are broadcast to the entire Network before it is validated. Each Node does its own Proof of Work (PoW).
The transactions are stored in the block using the unspent transaction output model. Basically a chain of ledger transactions.
This was built using my ecdsa signature verification from ecdsa-digital-signature and the transaction ledger was built from my bitcoin-ledger.
A transaction for a block is the following go struct,
type transactionStruct struct {
TxID int64 `json:"txID"`
Inputs []inputsStruct `json:"inputs"`
Outputs []outputsStruct `json:"outputs"`
}
type inputsStruct struct {
RefTxID int64 `json:"refTxID"`
InPubKey string `json:"inPubKey"`
Signature string `json:"signature"`
}
type outputsStruct struct {
OutPubKey string `json:"outPubKey"`
Value int64 `json:"value"`
}
And the transaction request message is,
type txRequestMessageSignedStruct struct {
TxRequestMessage txRequestMessageStruct `json:"txRequestMessage"`
Signature string `json:"signature"`
}
type txRequestMessageStruct struct {
SourceAddress string `json:"sourceAddress"`
Destinations []destinationStruct `json:"destinations"`
}
type destinationStruct struct {
DestinationAddress string `json:"destinationAddress"`
Value int64 `json:"value"`
}
- TRANSACTIONS
- processTxRequestMessage() Request to transfer jeffCoins to a jeffCoin Address
- SIGNATURE
- verifySignature() Verifies a ECDSA Digital Signature
- UNSPENT OUTPUTS
- pickUnspentOutputs() Pick the Unspent Outputs to use and provide change
This illustration shows transaction requests, verification for that request and addition onto the pendingBlock. A transaction is never valid until the transaction is added onto the blockchain.
2. MINER
The miner section has the following features,
- Miner automatically tells blockchain-interface to place pendingBlock into lockedBlock
- Performs the
mining (poW)
on the lockedBlocked - Difficulty is how many zero are needed at the beginning of the hash
- When block is solved, broadcast block to entire Network to check for
consensus
The proof of work structure is,
type tbd struct {
tbd
}
- MINING
- tbd() tbd
- MINING
- tbd() tbd
3. ROUTINGNODE
The Routingnode section has two main parts, the nodeList and the ability to handle the Node TCP Requests (TCP Server). The nodeList keeps a listing of all Nodes in the Network. The TCP Server handles requests from other Nodes.
The routingnode is built from my simple-tcp-ip-server.
3.1 NODELIST
A Node in the nodeList is the following go struct,
type nodeStruct struct {
Index int `json:"index"`
Status string `json:"status"`
Timestamp string `json:"timestamp"`
NodeName string `json:"nodename"`
ToolVersion string `json:"toolversion"`
IP string `json:"ip"`
HTTPPort string `json:"httpport"`
TCPPort string `json:"tcpport"`
}
ROUTINGNODE-INTERFACE FUNCTIONS
- NODELIST
- GetNodeList() Gets the nodeList
- GenesisNodeList() Creates the nodeList
- RequestsNodeList()
Requests the nodeList from a Network Node
SEND-NODELIST Request
- NODE
- GetNode() Gets a Node (via Index number) from the nodeList
- AppendNewNode() Appends a new Node to the nodeList
- THIS NODE
- GetThisNode() Gets thisNode
- LoadThisNode() Loads thisNode
- AppendThisNode() Appends thisNode to the nodeList
- BroadcastThisNode()
Broadcasts thisNode to the Network
BROADCAST-ADD-NEW-NODE Request
- NODELIST
- getNodeList() Gets the nodeList
- loadNodeList() Loads the entire nodeList
- NODE
- getNode() Gets a Node in the nodeList
- appendNewNode() Appends a new Node to the nodeList
- THIS NODE
- getThisNode() Gets thisNode
- loadThisNode() Loads thisNode
- appendThisNode() Appends thisNode to the nodeList
- checkIfThisNodeinNodeList() Check if thisNode is already in the nodeList
3.2 TCP REQUESTS & HANDLERS
Incoming requests to the TCP server from other Nodes or TCP connection.
An illustration of client-server handshakes,
- FROM BLOCKCHAIN I/F
- handleSendBlockchain() SEND-BLOCKCHAIN (SBC)- Sends the blockchain and pendingBlock to another Node
- FROM ROUTINGNODE I/F
- handleBroadcastAddNewNode() BROADCAST-ADD-NEW-NODE (BANN) - Adds a Node to the nodeList
- handleSendNodeList() SEND-NODELIST (SNL) - Sends the nodeList to another Node
- handleBroadcastVerifiedBlock() BROADCAST-VERIFIED-BLOCK (BVB) - A Node verified the next block, get block and verify
- handleBroadcastConsensus() BROADCAST-CONSENSUS (BC) - 51% Consensus reached, get block to add to blockchain
- handleBroadcastTransactionRequest() BROADCAST-TRANSACTION-REQUEST (BTR) - Request from a Node to transfer jeffCoins to a jeffCoin Address
- FROM WALLET I/F
- handleSendAddressBalance() SEND-ADDRESS-BALANCE (SAB) - Sends the jeffCoin balance for a jeffCoin Address
- handleTransactionRequest() TRANSACTION-REQUEST (TR) - Request from Wallet to transfer jeffCoins to a jeffCoin Address
- EOF
- EOF Close Connection
4. WALLET
The wallet section holds the Public Key, the Private Key and the jeffCoin Address. Like bitcoin, wallets do not have or hold any jeffCoins. The jeffCoins are in the blockchain transactions (ledger).
Generating keys and creating the jeffCoin address is built from my create-bitcoin-address-from-ecdsa-publickey.
Your wallet will be saved in the following file based on your nodename,
/wallet/{nodename}-wallet.json
.
A wallet has the following go struct,
type walletStruct struct {
PrivateKeyHex string `json:"privateKeyHex"`
PublicKeyHex string `json:"publicKeyHex"`
JeffCoinAddress string `json:"jeffCoinAddress"`
}
- WALLET
- GetWallet() Gets the wallet
- GenesisWallet() Creates the wallet and writes to file (Keys and jeffCoin Address)
- ReadWalletFile() Reads the wallet from a file
- KEYS
- EncodeKeys() Encodes privateKeyRaw & publicKeyRaw to privateKeyHex & publicKeyHex
- DecodeKeys() Decodes privateKeyHex & publicKeyHex to privateKeyRaw & publicKeyRaw
- JEFFCOINS
- RequestAddressBalance()
Requests the jeffCoin balance for a jeffCoin Address
SEND-ADDRESS-BALANCE Request
- TransactionRequest()
Request to transfer jeffCoins to a jeffCoin Address
TRANSACTION-REQUEST Request
- RequestAddressBalance()
Requests the jeffCoin balance for a jeffCoin Address
- SIGNATURE
- CreateSignature() Creates a ECDSA Digital Signature
- WALLET
- getWallet() Gets the wallet
- makeWallet() Creates the wallet and writes to file (Keys and jeffCoin Address)
- readWalletFile() Reads the wallet from a file
- KEYS
- generateECDSASKeys() Generate privateKeyHex and publicKeyHex
- encodeKeys() Encodes privateKeyRaw & publicKeyRaw to privateKeyHex & publicKeyHex
- decodeKeys() Decodes privateKeyHex & publicKeyHex to privateKeyRaw & publicKeyRaw
- JEFFCOIN ADDRESS
- generateJeffCoinAddress() Creates a jeffCoin Address
- hashPublicKey() Hashes publicKeyHex
- checksumKeyHash() Checksums verPublicKeyHash
- encodeKeyHash() Encodes verPublicKeyHash & checkSum
- SIGNATURE
- createSignature() Creates a ECDSA Digital Signature
5. WEBSERVER
The webserver section has two main parts, the GUI and the REST API.
This webserver is built from my simple-webserver-with-REST.
5.1 GUI
Currently, there is the main page that also lists the available APIs.
The screen should look similar to the following,
5.2 REST API
- BLOCKCHAIN
- /showBlockchain
- /showBlock/{blockID}
- PENDING AND LOCKED BLOCK
- /showpendingblock
- /showlockedblock
- NODELIST
- /shownodelist
- /shownode/{nodeID}
- /showthisnode
- WALLET (THIS NODE)
- /showwallet
- /showjeffcoinaddress
- /showbalance
- /transactionrequest/{destinationaddress1,destinationaddress2,...}/{value1,value2,...}
- WALLET (OTHER)
- /showaddressbalance/{jeffcoinaddress}
RUN
If this is you first time running, you need to create the first Node (Genesis Node). You only do this once. You can set the log level (info, debug, trace) to cut down on the amount of logging.
GENESIS NODE
go run jeffCoin.go \
-loglevel debug \
-genesis \
-nodename Founders \
-ip 127.0.0.1 \
-httpport 2000 \
-tcpport 3000
This will created the first Node (the Founders node) in the Network.
It will also create a wallet and save the credentials in /wallet
.
But having one node is boring so create more.
ADDING NEW NODES
To hook up to the Network. You need the IP of any
working Network Node. If you have the above running
on 127.0.0.1:3000
, adding a second Node
"Jeff" in your network could look like,
go run jeffCoin.go \
-loglevel debug \
-nodename Jeff \
-ip 127.0.0.1 \
-httpport 2001 \
-tcpport 3001 \
-netip 127.0.0.1 \
-netport 3000
Might as well add a third Node,
go run jeffCoin.go \
-loglevel debug \
-nodename Matt \
-ip 127.0.0.1 \
-httpport 2002 \
-tcpport 3002 \
-netip 127.0.0.1 \
-netport 3000
Each node has it's own wallet, so now you can send jeffCoins/Value. To do this, use the webserver and API interface.
WEBSERVER & REST API
The GUI for the three nodes you just created are,
127.0.0.1:2000 / 127.0.0.1:2001 / 127.0.0.1:2002
The main page will list the various API commands. For example, to show a particular block,
SWITCHES (REFERENCE)
-h
prints the following,
-gce
Is this Node on GCE-genesis
Create your first Node-httpport
string Node Web Port (default "2001")-ip
string Node IP (default "127.0.0.1")-loglevel
string LogLevel (info, debug or trace) (default "info")-netip
string Network IP (default "127.0.0.1")-netport
string Network TCP Port (default "3000")-nodename
string Node Name (default "Jeff")-tcpport
string Node TCP Port (default "3001")-test
Loads the blockchain with test data (SEE BELOW)-v
prints current version
CONNECT USING TCP (OPTIONAL)
You can also bypass the REST API and just open a connection to the TCP server itself,
netcat -q -1 127.0.0.1 3000
And request commands such as,
--- Waiting for command: SBC, BANN, SNL, BVB, BC, BTR, SAB, TR, EOF
SNL
[...nodeList...]
thank you
Notice you will need to handshake it with a thank you
at the end.
There is a complete list of commands up above in TCP REQUESTS & HANDLERS.
TEST MOCK TRANSACTIONS (OPTIONAL)
If you add the -test
switch you will run some mock transactions from mock wallets.
Those wallets are located in /wallets
and just used for testing.
You must use the MockFounders nodename,
go run jeffCoin.go \
-loglevel debug \
-genesis \
-nodename MockFounders \
-ip 127.0.0.1 \
-httpport 2000 \
-tcpport 3000 \
-test
These transactions are the same I used in my bitcoin-ledger example.
So your blockchain and pendingBlock should look similar to blockchain-output.txt.
And the balances in the blockchain should be,
The balance for MockFounders PubKey (Address) is 99657000
The balance for MockJeffs PubKey (Address) is 42500
The balance for MockMatts PubKey (Address) is 265000
The balance for MockJills PubKey (Address) is 35000
The balance for MockCoinVaults PubKey (Address) is 500
Remember, the pendingBlock is pending, so it's not part of this calculation. Transaction do not have value if they are not part of the blockchain.
RUN ON GOOGLE COMPUTE ENGINE (GCE) (OPTIONAL)
Make sure your create a firewall rule and have your instance use it as a network tag,
gcloud compute firewall-rules create jeffs-firewall-settings-rule \
--action allow \
--rules tcp:1234,tcp:3334 \
--priority 1000 \
--source-ranges 0.0.0.0/0 \
--target-tags "jeffs-firewall-settings" \
--description "Jeffs firewall rules"
The IP 0.0.0.0
gets forwarded to your external IP, hence I added a
-gce switch
to deal with this,
go run jeffCoin.go \
-gce \
-loglevel debug \
-genesis \
-nodename Founders \
-ip 35.203.189.193 \
-httpport 1234 \
-tcpport 3334
Add another node (not at gce) with,
go run jeffCoin.go \
-loglevel debug \
-nodename Jeff \
-ip 192.168.20.100 \
-httpport 1235 \
-tcpport 3335 \
-netip 35.203.189.193 \
-netport 3334
I have a gce build example here.
UPDATE GITHUB WEBPAGE USING CONCOURSE (OPTIONAL)
For fun, I use concourse to update jeffCoin GitHub Webpage and alert me of the changes via repo status and slack.
A pipeline file pipeline.yml shows the entire ci flow. Visually, it looks like,
The jobs
and tasks
are,
job-readme-github-pages
runs task readme-github-pages.sh.
The concourse resources types
are,
jeffCoin
uses a resource type docker-image to PULL a repo from github.resource-slack-alert
uses a resource type docker image that will notify slack on your progress.resource-repo-status
uses a resource type docker image that will update your git status for that particular commit.
For more information on using concourse for continuous integration, refer to my cheat sheet on concourse.