- Implement a blockchain on top of Git
- Pizza Hackathon project
- Proof of work
- Peer directory:
git remote -v
- To add peer:
git remote add
- To add peer:
- Block reward = 1,000,000
Blockchain | Git |
---|---|
A node | Git repository |
Blockchain | master branch |
Blockchain state | repository contents of master branch |
Connecting to other nodes | Git remote + git fetch |
Account | GPG keypair |
Account ID | GPG key fingerprint |
Transaction | commit ****that message starts with “transaction” |
Transaction sender | commit author fingerprint |
Transaction broadcast | branch with |
Previous transaction | previous commit |
Transaction ID | commit hash |
Signed transaction | merge commit “successful/failed transaction [id]” |
Block | merge commit message is with “block N” or “genesis” |
Proof of work | commit hash starts with 55 |
Parent block | parent commit |
- accounts
- [account id]
- balance
- [contract id]
- contract.json
- state.json
- balance
- code
- [account id]
- User publish transaction
{type: 'transfer', to: 'recipient'}
- For each node
git fetch $NODE_URL
- We get everyone’s branches
- Branch = transaction
- Must have 1 commit
- Commit must be signed
- Commit hash = transaction ID
- No file change (empty repo)
- Branch name:
transaction/${commit_hash}
git checkout -f master
git reset
--``hard
master
git checkout -b my-block
- For each transaction
git merge --no-commit ${transaction_id}
- Update state files.
git add --all .
git commit
- At this point,
my-block
branch would contain every transaction received. git checkout -f master
git checkout -b master-staging
git merge
--no-ff
--no-commit my-block
- Update state file to add block reward.
git commit -S -m 'block
**1**
[nonce 0]'
- Loop
git commit --amend -S -m 'block
**1**
[nonce=``i++``]'
- If commit hash starts with
55
- break
git reset --hard master
- Try again
git checkout master
git merge --ff-only master-staging
mkdir -p accounts/<ID>
echo -n
**<REWARD>**
> accounts/<ID>/balance
git commit
- Loop
- If commit hash starts with
55
- break
git commit
--``amend
- For each node
git fetch $NODE_URL
- We get everyone’s master branch
- For each master branch of each node
git checkout their-branch
- Verify the branch
git merge-base their-branch master
- Verify each commit
- For each node
- The node clones the plugin, run the initialization script
1. The script run “git clone” to clone the chain from another running node.
2. should be something like
gith init [chain-repo-dir]
- The node starts a running process to operate the blockchain
1. The process periodically pulls changes from other nodes listed in the chain repo’s remotes, do some magic, be happy
2. should be something like
gith node
- For each miner node
- Initialize similar to normal nodes
- The running process acts differently:
1. The process periodically attempt to create a new “block” (which is basically a commit that changes the state)
2. The process fetches all pending transactions, verifies them, and tries to put them in the block
3. The process then merges the created “block” into the master branch
4. might be sonething like
gith node
--``mine
- To interact with the chain:
- Done through some client software (probably a CLI or a Script)
- the software should be sending commands to an actual node, so users does not have to run a node to interact with the chain
Note: GPG 2.2.9 doesn’t work. Have to install 2.2.8.
brew unlink gnupg # if already installed another version
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/4451447c6a069b0f7ce5d8c88e4da7c4c89fbe52/Formula/gnupg.rb
$ gpg --gen-key
Note: your key should not have a passphrase How to remove passphrase from existing key: https://lists.gnupg.org/pipermail/gnupg-users/2003-April/017623.html
$ gpg --list-keys --fingerprint
pub 2048R/67B513AE 2018-08-25
Key fingerprint = FEC3 82DA ED6E 95D8 E188 2263 5186 4AC2 67B5 13AE
git config --global user.signingkey 67B513AE
Replace space with /, and take only last 4 chunks (16 digits): 5186/4AC2/67B5/13AE
- Increate your balance by
1000000
by updatingaccounts/<Account ID>/balance
and add 1000000 to the number. - Create a commit:
git commit -S -m 'block
**1**
[nonce``=``0]'
- Replace block 1 with (previous block number + 1)
- While
git rev-parse HEAD
not start with '55'
-
git commit --amend -S -m 'block
**1**
[nonce=``**1**``]'
i = 0 block_number = 2 loop do system "git commit --amend -S -m 'block #{block_number} [nonce=#{i += 1}]'" break if
git rev-parse HEAD
.start_with?('55') end
$ git checkout --orphan my-transaction
$ git reset
$ git commit -m 'transaction transfer {"to":"34B0/AF0B/3E84/D636","amount":555}' --allow-empty -S
[my-transaction 04d3195] transaction transfer {"to":"34B0/AF0B/3E84/D636","amount":555}
$ git branch -m transactions/$(git rev-parse HEAD)
# 1. Allow
cd chain
touch .git/git-daemon-export-ok
git daemon --reuseaddr --base-path=$(pwd) $(pwd)
git remote add <ip> git://<ip>/
- The block’s commit ID must start with
55
.
Commit message | |
---|---|
Genesis block | genesis [nonce=X] |
Subsequent block | block 1 [nonce=X] |
Transaction | transaction transfer {"to":"","amount":999} |
Transaction being put into a block | successful ```transaction COMMIT_ID` |
failed transaction COMMIT_ID: reason
|
Branch name | |
---|---|
Transaction | transaction/<commit_id> |
Main chain | master |
Example contract:
- When 2 people send money to that contract, it will send total received money to one of randomly-chosen person.
// contract.js
module.exports = {
initialState: { members: [] },
/**
* @param {*} state - copy of current state (you can mutate it)
* @param {Object} action - user-specified payload
* @param {Object} context
* @param {{ id: string }} context.sender - user who invoked contract
* @param {string} context.hash - the current sha of "my-block" branch (can use as random source)
* @param {string} context.minerId - the ID of miner (can also use as random source)
* @param {number} context.balance - the amount of money in contract
* @param {(id: string, amount: number)} context.transferTo - send money
* @return the next state
*/
reducer(state, action, context) {
state.members.push(context.sender.id)
if (state.members.length >= 2) {
const random = parseInt(context.hash.substr(0, 4), 16) ^ parseInt(context.minerId.substr(0, 4), 16)
const target = random % state.members.length
context.transferTo(state.members[target], context.balance)
state.members = []
}
return state
}
}
Contract ID:
git hash-object contract.js
- → 8446bc10a60d5bcc28b07ad552b881b13ee112a3
- Take last 16 digits
- → 52B8/81B1/3EE1/12A3
Creating contract:
- commit title: “create contract 52B8/81B1/3EE1/12A3”
- commit contains contract.js in
accounts/52B8/81B1/3EE1/12A3/contract.js
- contract ID must match contract.js
- commit contains contract.js in
When transferring money to contract…
- Update its balance first
- If it contains
contract.js
then require and run it :P
$ node ../gitthereum/bin/check-balance '<user id>'
1999863
$ node ../gitthereum/bin/create-transaction transfer --to='...' --amount=123
04d3195 # (commit hash = transaction id)
$ node ../gitthereum/bin/verify-transaction 04d3195
pending / success / failed: reason
$ node ../gitthereum/bin/sync-remotes
$ node ../gitthereum/bin/process-transactions
# [reset to master, creates "my-block" branch,
# and process transactions that haven’t been processed yet
# by running `git branch --all` and look for `transactions/*` branches]
$ node ../gitthereum/bin/mine-block
# [mines a block... if "my-block" branch is available, then mine with
# transactions. otherwise, just mine without processing transaction.
# make sure to update your transaction]
$ node ../gitthereum/bin/verify-block <commit>
# [verifies that a block is valid]