Skip to content

Instantly share code, notes, and snippets.

@tywkeene
Last active February 7, 2017 20:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tywkeene/80b8c5693a11dc43563bb0164a83a5f3 to your computer and use it in GitHub Desktop.
Save tywkeene/80b8c5693a11dc43563bb0164a83a5f3 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
)
type Block struct {
Path string
ID int
Previous string
DataHash string
BlockHash string
Nonce int
}
type BlockChain struct {
Filepath string
Signature string
Blocks []*Block
}
func TimeTrack(start time.Time, name string) {
elapsed := time.Since(start)
fmt.Printf("%s took %s\n", name, elapsed)
}
func NewBlockChain(filepath string, signature string) *BlockChain {
return &BlockChain{
Filepath: filepath,
Signature: signature,
Blocks: make([]*Block, 0),
}
}
func (c *BlockChain) Write() error {
outfile, err := os.Create(c.Filepath)
if err != nil {
return err
}
defer outfile.Close()
output, _ := json.MarshalIndent(c, " ", " ")
_, err = outfile.WriteString(string(output))
if err != nil {
return err
}
return nil
}
func ReadBlockChain(filepath string) (*BlockChain, error) {
if _, err := os.Stat(filepath); err != nil {
return nil, err
}
serial, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, err
}
chain := NewBlockChain(filepath, "")
json.Unmarshal(serial, &chain)
return chain, nil
}
func (b *Block) Print() {
fmt.Printf("Path:%s\nID:%d\nPrevious:%s\nDataHash:%s\nBlockHash:%s\nNonce:%d\n",
b.Path, b.ID, b.Previous, b.DataHash, b.BlockHash, b.Nonce)
}
func GetFileChecksum(filepath string) string {
file, err := os.Open(filepath)
if err != nil {
panic(err)
}
defer file.Close()
hash := sha512.New()
buf := bufio.NewReader(file)
_, err = buf.WriteTo(hash)
if err != nil {
panic(err)
}
return hex.EncodeToString(hash.Sum(nil))
}
func (b *Block) GetBlockHash(signature string) {
var nonce int
hash := sha512.New()
b.DataHash = GetFileChecksum(b.Path)
for nonce = 0; nonce < 100000000; nonce++ {
try := (b.DataHash + string(nonce))
hash.Write([]byte(try))
sum := hex.EncodeToString(hash.Sum(nil))
if string(sum[:4]) == signature {
b.BlockHash = sum
b.Nonce = nonce
break
}
}
}
func NewBlock(filename string, ID int, previous string) *Block {
return &Block{
Path: filename,
ID: ID,
Previous: previous,
DataHash: "",
BlockHash: "",
}
}
func (c *BlockChain) InitializeBlock(filename string) *Block {
var ID int
var previous string
var latest *Block = c.LatestBlock()
if latest == nil {
ID = 0
previous = "FIRST_BLOCK"
} else {
previous = c.Blocks[latest.ID].BlockHash
ID = (latest.ID + 1)
}
block := NewBlock(filename, ID, previous)
block.GetBlockHash(c.Signature)
return block
}
func (c *BlockChain) AddBlock(new *Block) {
fmt.Printf("Adding to chain with %d blocks\n", len(c.Blocks))
c.Blocks = append(c.Blocks, new)
}
func (c *BlockChain) AddBlockFromFile(filename string) error {
block := c.InitializeBlock(filename)
c.AddBlock(block)
return nil
}
func (c *BlockChain) CompareDataHashWithLatest(compare *Block) bool {
if c.LatestDataHash() != compare.DataHash {
return false
}
return true
}
func (c *BlockChain) LatestBlock() *Block {
if len(c.Blocks) > 0 {
return c.Blocks[len(c.Blocks)-1]
}
return nil
}
func (c *BlockChain) LatestBlockHash() string {
return c.LatestBlock().BlockHash
}
func (c *BlockChain) LatestDataHash() string {
return c.LatestBlock().DataHash
}
func (c *BlockChain) PrintBlocks() {
if len(c.Blocks) > 0 {
fmt.Println("------------------------------------")
for _, block := range c.Blocks {
block.Print()
fmt.Println("------------------------------------")
}
}
}
func (c *BlockChain) VerifyOrder() (*Block, *Block) {
defer TimeTrack(time.Now(), "VerifyOrder()")
for i := (len(c.Blocks) - 1); i > 0; i-- {
ptr := c.Blocks[i]
if (i - 1) == -1 {
break
}
previous := c.Blocks[i-1]
if ptr.Previous != previous.BlockHash {
return ptr, previous
}
}
return nil, nil
}
func (c *BlockChain) VerifySignatures() *Block {
defer TimeTrack(time.Now(), "VerifySignatures()")
for _, block := range c.Blocks {
if block.BlockHash[:4] != c.Signature {
return block
}
}
return nil
}
func (c *BlockChain) Verify() {
if invalid := c.VerifySignatures(); invalid != nil {
fmt.Printf("Block %d:Invalid blockhash signature:[%s] Actual:[%s]\n",
invalid.ID, invalid.BlockHash[:4], c.Signature)
return
}
invalid, previous := c.VerifyOrder()
if invalid != nil && previous != nil {
fmt.Printf("Block %d:Invalid previous block:[%s] Actual:[%s]\n",
invalid.ID, invalid.Previous[:8], previous.BlockHash[:8])
return
}
}
func main() {
var chain *BlockChain
var err error
var blockChainData string = "./blockchain.json"
var testFile string = "test"
chain, err = ReadBlockChain(blockChainData)
if err != nil || chain == nil {
fmt.Println("No existing blockchain, creating new one")
chain = NewBlockChain(blockChainData, "1337")
chain.AddBlockFromFile(testFile)
chain.Write()
} else {
chain.Verify()
fmt.Printf("Read blockchain with %d blocks from %s\n", len(chain.Blocks), blockChainData)
if len(chain.Blocks) > 0 {
current := chain.InitializeBlock(testFile)
if chain.CompareDataHashWithLatest(current) == false {
fmt.Println("Changes have been made, adding new block to chain")
chain.AddBlock(current)
chain.PrintBlocks()
} else {
fmt.Println("No changes have been made to the block chain")
}
} else {
fmt.Println("No blocks in chain to compare")
}
}
chain.Verify()
if err := chain.Write(); err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment