This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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