From d58a22741fbd2474459abcc28d0b6d52ca9a11b4 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Wed, 24 Oct 2018 21:18:05 -0400 Subject: [PATCH 01/26] added proof of work --- blockchain/block.go | 37 +++++++++++++++++ blockchain/proof.go | 99 +++++++++++++++++++++++++++++++++++++++++++++ main.go | 51 +++++------------------ 3 files changed, 147 insertions(+), 40 deletions(-) create mode 100644 blockchain/block.go create mode 100644 blockchain/proof.go diff --git a/blockchain/block.go b/blockchain/block.go new file mode 100644 index 00000000..343d6988 --- /dev/null +++ b/blockchain/block.go @@ -0,0 +1,37 @@ +package blockchain + +type BlockChain struct { + Blocks []*Block +} + +type Block struct { + Hash []byte + Data []byte + PrevHash []byte + Nonce int +} + +func CreateBlock(data string, prevHash []byte) *Block { + block := &Block{[]byte{}, []byte(data), prevHash, 0} + pow := NewProof(block) + nonce, hash := pow.Run() + + block.Hash = hash[:] + block.Nonce = nonce + + return block +} + +func (chain *BlockChain) AddBlock(data string) { + prevBlock := chain.Blocks[len(chain.Blocks)-1] + new := CreateBlock(data, prevBlock.Hash) + chain.Blocks = append(chain.Blocks, new) +} + +func Genesis() *Block { + return CreateBlock("Genesis", []byte{}) +} + +func InitBlockChain() *BlockChain { + return &BlockChain{[]*Block{Genesis()}} +} diff --git a/blockchain/proof.go b/blockchain/proof.go new file mode 100644 index 00000000..b59a0a69 --- /dev/null +++ b/blockchain/proof.go @@ -0,0 +1,99 @@ +package blockchain + +import ( + "bytes" + "crypto/sha256" + "encoding/binary" + "fmt" + "log" + "math" + "math/big" +) + +// Take the data from the block + +// create a counter (nonce) which starts at 0 + +// create a hash of the data plus the counter + +// check the hash to see if it meets a set of requirements + +// Requirements: +// The First few bytes must contain 0s + +const Difficulty = 18 + +type ProofOfWork struct { + Block *Block + Target *big.Int +} + +func NewProof(b *Block) *ProofOfWork { + target := big.NewInt(1) + target.Lsh(target, uint(256-Difficulty)) + + pow := &ProofOfWork{b, target} + + return pow +} + +func (pow *ProofOfWork) InitData(nonce int) []byte { + data := bytes.Join( + [][]byte{ + pow.Block.PrevHash, + pow.Block.Data, + ToHex(int64(nonce)), + ToHex(int64(Difficulty)), + }, + []byte{}, + ) + + return data +} + +func (pow *ProofOfWork) Run() (int, []byte) { + var intHash big.Int + var hash [32]byte + + nonce := 0 + + for nonce < math.MaxInt64 { + data := pow.InitData(nonce) + hash = sha256.Sum256(data) + + fmt.Printf("\r%x", hash) + intHash.SetBytes(hash[:]) + + if intHash.Cmp(pow.Target) == -1 { + break + } else { + nonce++ + } + + } + fmt.Println() + + return nonce, hash[:] +} + +func (pow *ProofOfWork) Validate() bool { + var intHash big.Int + + data := pow.InitData(pow.Block.Nonce) + + hash := sha256.Sum256(data) + intHash.SetBytes(hash[:]) + + return intHash.Cmp(pow.Target) == -1 +} + +func ToHex(num int64) []byte { + buff := new(bytes.Buffer) + err := binary.Write(buff, binary.BigEndian, num) + if err != nil { + log.Panic(err) + + } + + return buff.Bytes() +} diff --git a/main.go b/main.go index 8a275b19..ed0c3bf6 100644 --- a/main.go +++ b/main.go @@ -1,57 +1,28 @@ package main import ( - "bytes" - "crypto/sha256" "fmt" -) - -type BlockChain struct { - blocks []*Block -} - -type Block struct { - Hash []byte - Data []byte - PrevHash []byte -} - -func (b *Block) DeriveHash() { - info := bytes.Join([][]byte{b.Data, b.PrevHash}, []byte{}) - hash := sha256.Sum256(info) - b.Hash = hash[:] -} - -func CreateBlock(data string, prevHash []byte) *Block { - block := &Block{[]byte{}, []byte(data), prevHash} - block.DeriveHash() - return block -} + "strconv" -func (chain *BlockChain) AddBlock(data string) { - prevBlock := chain.blocks[len(chain.blocks)-1] - new := CreateBlock(data, prevBlock.Hash) - chain.blocks = append(chain.blocks, new) -} - -func Genesis() *Block { - return CreateBlock("Genesis", []byte{}) -} - -func InitBlockChain() *BlockChain { - return &BlockChain{[]*Block{Genesis()}} -} + "github.com/tensor-programming/golang-blockchain/blockchain" +) func main() { - chain := InitBlockChain() + chain := blockchain.InitBlockChain() chain.AddBlock("First Block after Genesis") chain.AddBlock("Second Block after Genesis") chain.AddBlock("Third Block after Genesis") - for _, block := range chain.blocks { + for _, block := range chain.Blocks { + fmt.Printf("Previous Hash: %x\n", block.PrevHash) fmt.Printf("Data in Block: %s\n", block.Data) fmt.Printf("Hash: %x\n", block.Hash) + + pow := blockchain.NewProof(block) + fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) + fmt.Println() + } } From 7965bf65c43ccb986e4cfd96ae18f8961fc5cb00 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Fri, 26 Oct 2018 00:55:43 -0400 Subject: [PATCH 02/26] added command line interface and badgerDB --- .gitignore | 1 + blockchain/block.go | 42 +++++++++++---- blockchain/blockchain.go | 109 +++++++++++++++++++++++++++++++++++++++ go.mod | 9 ++++ go.sum | 12 +++++ main.go | 88 +++++++++++++++++++++++++++---- 6 files changed, 241 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 blockchain/blockchain.go create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c0363794 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tmp/ \ No newline at end of file diff --git a/blockchain/block.go b/blockchain/block.go index 343d6988..118258bf 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -1,8 +1,10 @@ package blockchain -type BlockChain struct { - Blocks []*Block -} +import ( + "bytes" + "encoding/gob" + "log" +) type Block struct { Hash []byte @@ -22,16 +24,34 @@ func CreateBlock(data string, prevHash []byte) *Block { return block } -func (chain *BlockChain) AddBlock(data string) { - prevBlock := chain.Blocks[len(chain.Blocks)-1] - new := CreateBlock(data, prevBlock.Hash) - chain.Blocks = append(chain.Blocks, new) -} - func Genesis() *Block { return CreateBlock("Genesis", []byte{}) } -func InitBlockChain() *BlockChain { - return &BlockChain{[]*Block{Genesis()}} +func (b *Block) Serialize() []byte { + var res bytes.Buffer + + encoder := gob.NewEncoder(&res) + err := encoder.Encode(b) + + if err != nil { + log.Panic(err) + } + + return res.Bytes() + +} + +func Deserialize(data []byte) *Block { + var block Block + + decoder := gob.NewDecoder(bytes.NewReader(data)) + + err := decoder.Decode(&block) + + if err != nil { + log.Panic(err) + } + + return &block } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go new file mode 100644 index 00000000..3f1e76e9 --- /dev/null +++ b/blockchain/blockchain.go @@ -0,0 +1,109 @@ +package blockchain + +import ( + "fmt" + "log" + + "github.com/dgraph-io/badger" +) + +const ( + databaseName = "blockchain.db" + dbPath = "./tmp/blocks" +) + +type BlockChain struct { + LastHash []byte + Database *badger.DB +} + +type BlockChainIterator struct { + CurrentHash []byte + Database *badger.DB +} + +func (chain *BlockChain) AddBlock(data string) { + var lastHash []byte + + err := chain.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("l")) + lastHash, err = item.Value() + + return err + }) + handle(err) + + newBlock := CreateBlock(data, lastHash) + + err = chain.Database.Update(func(txn *badger.Txn) error { + err := txn.Set(newBlock.Hash, newBlock.Serialize()) + err = txn.Set([]byte("l"), newBlock.Hash) + + chain.LastHash = newBlock.Hash + + return err + }) + +} + +func InitBlockChain() *BlockChain { + var lastHash []byte + + opts := badger.DefaultOptions + opts.Dir = dbPath + opts.ValueDir = dbPath + + db, err := badger.Open(opts) + handle(err) + + err = db.Update(func(txn *badger.Txn) error { + if _, err := txn.Get([]byte("l")); err == badger.ErrKeyNotFound { + fmt.Println("No existing blockchain found") + genesis := Genesis() + fmt.Println("Genesis proved") + err = txn.Set(genesis.Hash, genesis.Serialize()) + + err = txn.Set([]byte("l"), genesis.Hash) + + lastHash = genesis.Hash + + return err + } else { + item, err := txn.Get([]byte("l")) + lastHash, err = item.Value() + return err + } + }) + handle(err) + + blockchain := BlockChain{lastHash, db} + return &blockchain +} + +func (chain *BlockChain) Iterator() *BlockChainIterator { + iter := &BlockChainIterator{chain.LastHash, chain.Database} + + return iter +} + +func (iter *BlockChainIterator) Next() *Block { + var block *Block + + err := iter.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get(iter.CurrentHash) + encodedBlock, err := item.Value() + block = Deserialize(encodedBlock) + + return err + }) + handle(err) + + iter.CurrentHash = block.PrevHash + return block +} + +func handle(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/go.mod b/go.mod index a665fc59..61f3006f 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,10 @@ module github.com/tensor-programming/golang-blockchain + +require ( + github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect + github.com/dgraph-io/badger v1.5.4 + github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect + github.com/golang/protobuf v1.2.0 // indirect + github.com/pkg/errors v0.8.0 // indirect + golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..aad3a217 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= +github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0= +github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/main.go b/main.go index ed0c3bf6..e1db7c08 100644 --- a/main.go +++ b/main.go @@ -1,28 +1,98 @@ package main import ( + "flag" "fmt" + "log" + "os" + "runtime" "strconv" "github.com/tensor-programming/golang-blockchain/blockchain" ) -func main() { - chain := blockchain.InitBlockChain() +type CommandLine struct { + blockchain *blockchain.BlockChain +} - chain.AddBlock("First Block after Genesis") - chain.AddBlock("Second Block after Genesis") - chain.AddBlock("Third Block after Genesis") +func (cli *CommandLine) printUsage() { + fmt.Println("Usage:") + fmt.Println(" add -block BLOCK_DATA - add a block to the chain") + fmt.Println(" print - Prints the blocks in the chain") +} - for _, block := range chain.Blocks { +func (cli *CommandLine) validateArgs() { + if len(os.Args) < 2 { + cli.printUsage() + runtime.Goexit() + } +} - fmt.Printf("Previous Hash: %x\n", block.PrevHash) - fmt.Printf("Data in Block: %s\n", block.Data) - fmt.Printf("Hash: %x\n", block.Hash) +func (cli *CommandLine) addBlock(data string) { + cli.blockchain.AddBlock(data) + fmt.Println("Added Block!") +} + +func (cli *CommandLine) printChain() { + iter := cli.blockchain.Iterator() + + for { + block := iter.Next() + fmt.Printf("Prev. hash: %x\n", block.PrevHash) + fmt.Printf("Data: %s\n", block.Data) + fmt.Printf("Hash: %x\n", block.Hash) pow := blockchain.NewProof(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) fmt.Println() + if len(block.PrevHash) == 0 { + break + } } } + +func (cli *CommandLine) run() { + cli.validateArgs() + + addBlockCmd := flag.NewFlagSet("add", flag.ExitOnError) + printChainCmd := flag.NewFlagSet("print", flag.ExitOnError) + addBlockData := addBlockCmd.String("block", "", "Block data") + + switch os.Args[1] { + case "add": + err := addBlockCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "print": + err := printChainCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + default: + cli.printUsage() + runtime.Goexit() + } + + if addBlockCmd.Parsed() { + if *addBlockData == "" { + addBlockCmd.Usage() + runtime.Goexit() + } + cli.addBlock(*addBlockData) + } + + if printChainCmd.Parsed() { + cli.printChain() + } +} + +func main() { + defer os.Exit(0) + chain := blockchain.InitBlockChain() + defer chain.Database.Close() + + cli := CommandLine{chain} + cli.run() +} From f7ac665375bbed5c0f39253e96bb8f1b199f456e Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 26 Oct 2018 22:13:19 -0400 Subject: [PATCH 03/26] initial commit --- README.md | 11 ++ blockchain/block.go | 115 ++++++++++----------- blockchain/blockchain.go | 216 +++++++++++++++++++-------------------- blockchain/proof.go | 198 +++++++++++++++++------------------ main.go | 193 +++++++++++++++++----------------- 5 files changed, 370 insertions(+), 363 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..c3f3c9ad --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Golang Blockchain + +### In this tutorial, we look at how we can build a blockchain with Golang. + +## Run `go run main.go` to run the app, run `go build main.go` to build an executable file. + +### Check out the Youtube Tutorial for this [Go Program](https://www.youtube.com/embed/aE4eDTUAE70). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. + +### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). + +### Our [Twitter](https://twitter.com/TensorProgram), our [facebook](https://www.facebook.com/Tensor-Programming-1197847143611799/) and our [Steemit](https://steemit.com/@tensor). diff --git a/blockchain/block.go b/blockchain/block.go index 118258bf..05b056fa 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -1,57 +1,58 @@ -package blockchain - -import ( - "bytes" - "encoding/gob" - "log" -) - -type Block struct { - Hash []byte - Data []byte - PrevHash []byte - Nonce int -} - -func CreateBlock(data string, prevHash []byte) *Block { - block := &Block{[]byte{}, []byte(data), prevHash, 0} - pow := NewProof(block) - nonce, hash := pow.Run() - - block.Hash = hash[:] - block.Nonce = nonce - - return block -} - -func Genesis() *Block { - return CreateBlock("Genesis", []byte{}) -} - -func (b *Block) Serialize() []byte { - var res bytes.Buffer - - encoder := gob.NewEncoder(&res) - err := encoder.Encode(b) - - if err != nil { - log.Panic(err) - } - - return res.Bytes() - -} - -func Deserialize(data []byte) *Block { - var block Block - - decoder := gob.NewDecoder(bytes.NewReader(data)) - - err := decoder.Decode(&block) - - if err != nil { - log.Panic(err) - } - - return &block -} +package blockchain + +import ( + "bytes" + "encoding/gob" + "log" +) + +type Block struct { + Hash []byte + Data []byte + PrevHash []byte + Nonce int +} + +func CreateBlock(data string, prevHash []byte) *Block { + block := &Block{[]byte{}, []byte(data), prevHash, 0} + pow := NewProof(block) + nonce, hash := pow.Run() + + block.Hash = hash[:] + block.Nonce = nonce + + return block +} + +func Genesis() *Block { + return CreateBlock("Genesis", []byte{}) +} + +func (b *Block) Serialize() []byte { + var res bytes.Buffer + encoder := gob.NewEncoder(&res) + + err := encoder.Encode(b) + + Handle(err) + + return res.Bytes() +} + +func Deserialize(data []byte) *Block { + var block Block + + decoder := gob.NewDecoder(bytes.NewReader(data)) + + err := decoder.Decode(&block) + + Handle(err) + + return &block +} + +func Handle(err error) { + if err != nil { + log.Panic(err) + } +} diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 3f1e76e9..088ddbec 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1,109 +1,107 @@ -package blockchain - -import ( - "fmt" - "log" - - "github.com/dgraph-io/badger" -) - -const ( - databaseName = "blockchain.db" - dbPath = "./tmp/blocks" -) - -type BlockChain struct { - LastHash []byte - Database *badger.DB -} - -type BlockChainIterator struct { - CurrentHash []byte - Database *badger.DB -} - -func (chain *BlockChain) AddBlock(data string) { - var lastHash []byte - - err := chain.Database.View(func(txn *badger.Txn) error { - item, err := txn.Get([]byte("l")) - lastHash, err = item.Value() - - return err - }) - handle(err) - - newBlock := CreateBlock(data, lastHash) - - err = chain.Database.Update(func(txn *badger.Txn) error { - err := txn.Set(newBlock.Hash, newBlock.Serialize()) - err = txn.Set([]byte("l"), newBlock.Hash) - - chain.LastHash = newBlock.Hash - - return err - }) - -} - -func InitBlockChain() *BlockChain { - var lastHash []byte - - opts := badger.DefaultOptions - opts.Dir = dbPath - opts.ValueDir = dbPath - - db, err := badger.Open(opts) - handle(err) - - err = db.Update(func(txn *badger.Txn) error { - if _, err := txn.Get([]byte("l")); err == badger.ErrKeyNotFound { - fmt.Println("No existing blockchain found") - genesis := Genesis() - fmt.Println("Genesis proved") - err = txn.Set(genesis.Hash, genesis.Serialize()) - - err = txn.Set([]byte("l"), genesis.Hash) - - lastHash = genesis.Hash - - return err - } else { - item, err := txn.Get([]byte("l")) - lastHash, err = item.Value() - return err - } - }) - handle(err) - - blockchain := BlockChain{lastHash, db} - return &blockchain -} - -func (chain *BlockChain) Iterator() *BlockChainIterator { - iter := &BlockChainIterator{chain.LastHash, chain.Database} - - return iter -} - -func (iter *BlockChainIterator) Next() *Block { - var block *Block - - err := iter.Database.View(func(txn *badger.Txn) error { - item, err := txn.Get(iter.CurrentHash) - encodedBlock, err := item.Value() - block = Deserialize(encodedBlock) - - return err - }) - handle(err) - - iter.CurrentHash = block.PrevHash - return block -} - -func handle(err error) { - if err != nil { - log.Fatal(err) - } -} +package blockchain + +import ( + "fmt" + + "github.com/dgraph-io/badger" +) + +const ( + dbPath = "./tmp/blocks" +) + +type BlockChain struct { + LastHash []byte + Database *badger.DB +} + +type BlockChainIterator struct { + CurrentHash []byte + Database *badger.DB +} + +func InitBlockChain() *BlockChain { + var lastHash []byte + + opts := badger.DefaultOptions + opts.Dir = dbPath + opts.ValueDir = dbPath + + db, err := badger.Open(opts) + Handle(err) + + err = db.Update(func(txn *badger.Txn) error { + if _, err := txn.Get([]byte("lh")); err == badger.ErrKeyNotFound { + fmt.Println("No existing blockchain found") + genesis := Genesis() + fmt.Println("Genesis proved") + err = txn.Set(genesis.Hash, genesis.Serialize()) + Handle(err) + err = txn.Set([]byte("lh"), genesis.Hash) + + lastHash = genesis.Hash + + return err + } else { + item, err := txn.Get([]byte("lh")) + Handle(err) + lastHash, err = item.Value() + return err + } + }) + + Handle(err) + + blockchain := BlockChain{lastHash, db} + return &blockchain +} + +func (chain *BlockChain) AddBlock(data string) { + var lastHash []byte + + err := chain.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("lh")) + Handle(err) + lastHash, err = item.Value() + + return err + }) + Handle(err) + + newBlock := CreateBlock(data, lastHash) + + err = chain.Database.Update(func(txn *badger.Txn) error { + err := txn.Set(newBlock.Hash, newBlock.Serialize()) + Handle(err) + err = txn.Set([]byte("lh"), newBlock.Hash) + + chain.LastHash = newBlock.Hash + + return err + }) + Handle(err) +} + +func (chain *BlockChain) Iterator() *BlockChainIterator { + iter := &BlockChainIterator{chain.LastHash, chain.Database} + + return iter +} + +func (iter *BlockChainIterator) Next() *Block { + var block *Block + + err := iter.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get(iter.CurrentHash) + Handle(err) + encodedBlock, err := item.Value() + block = Deserialize(encodedBlock) + + return err + }) + Handle(err) + + iter.CurrentHash = block.PrevHash + + return block +} diff --git a/blockchain/proof.go b/blockchain/proof.go index b59a0a69..e2ce0c50 100644 --- a/blockchain/proof.go +++ b/blockchain/proof.go @@ -1,99 +1,99 @@ -package blockchain - -import ( - "bytes" - "crypto/sha256" - "encoding/binary" - "fmt" - "log" - "math" - "math/big" -) - -// Take the data from the block - -// create a counter (nonce) which starts at 0 - -// create a hash of the data plus the counter - -// check the hash to see if it meets a set of requirements - -// Requirements: -// The First few bytes must contain 0s - -const Difficulty = 18 - -type ProofOfWork struct { - Block *Block - Target *big.Int -} - -func NewProof(b *Block) *ProofOfWork { - target := big.NewInt(1) - target.Lsh(target, uint(256-Difficulty)) - - pow := &ProofOfWork{b, target} - - return pow -} - -func (pow *ProofOfWork) InitData(nonce int) []byte { - data := bytes.Join( - [][]byte{ - pow.Block.PrevHash, - pow.Block.Data, - ToHex(int64(nonce)), - ToHex(int64(Difficulty)), - }, - []byte{}, - ) - - return data -} - -func (pow *ProofOfWork) Run() (int, []byte) { - var intHash big.Int - var hash [32]byte - - nonce := 0 - - for nonce < math.MaxInt64 { - data := pow.InitData(nonce) - hash = sha256.Sum256(data) - - fmt.Printf("\r%x", hash) - intHash.SetBytes(hash[:]) - - if intHash.Cmp(pow.Target) == -1 { - break - } else { - nonce++ - } - - } - fmt.Println() - - return nonce, hash[:] -} - -func (pow *ProofOfWork) Validate() bool { - var intHash big.Int - - data := pow.InitData(pow.Block.Nonce) - - hash := sha256.Sum256(data) - intHash.SetBytes(hash[:]) - - return intHash.Cmp(pow.Target) == -1 -} - -func ToHex(num int64) []byte { - buff := new(bytes.Buffer) - err := binary.Write(buff, binary.BigEndian, num) - if err != nil { - log.Panic(err) - - } - - return buff.Bytes() -} +package blockchain + +import ( + "bytes" + "crypto/sha256" + "encoding/binary" + "fmt" + "log" + "math" + "math/big" +) + +// Take the data from the block + +// create a counter (nonce) which starts at 0 + +// create a hash of the data plus the counter + +// check the hash to see if it meets a set of requirements + +// Requirements: +// The First few bytes must contain 0s + +const Difficulty = 18 + +type ProofOfWork struct { + Block *Block + Target *big.Int +} + +func NewProof(b *Block) *ProofOfWork { + target := big.NewInt(1) + target.Lsh(target, uint(256-Difficulty)) + + pow := &ProofOfWork{b, target} + + return pow +} + +func (pow *ProofOfWork) InitData(nonce int) []byte { + data := bytes.Join( + [][]byte{ + pow.Block.PrevHash, + pow.Block.Data, + ToHex(int64(nonce)), + ToHex(int64(Difficulty)), + }, + []byte{}, + ) + + return data +} + +func (pow *ProofOfWork) Run() (int, []byte) { + var intHash big.Int + var hash [32]byte + + nonce := 0 + + for nonce < math.MaxInt64 { + data := pow.InitData(nonce) + hash = sha256.Sum256(data) + + fmt.Printf("\r%x", hash) + intHash.SetBytes(hash[:]) + + if intHash.Cmp(pow.Target) == -1 { + break + } else { + nonce++ + } + + } + fmt.Println() + + return nonce, hash[:] +} + +func (pow *ProofOfWork) Validate() bool { + var intHash big.Int + + data := pow.InitData(pow.Block.Nonce) + + hash := sha256.Sum256(data) + intHash.SetBytes(hash[:]) + + return intHash.Cmp(pow.Target) == -1 +} + +func ToHex(num int64) []byte { + buff := new(bytes.Buffer) + err := binary.Write(buff, binary.BigEndian, num) + if err != nil { + log.Panic(err) + + } + + return buff.Bytes() +} diff --git a/main.go b/main.go index e1db7c08..72d43bfa 100644 --- a/main.go +++ b/main.go @@ -1,98 +1,95 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" - "runtime" - "strconv" - - "github.com/tensor-programming/golang-blockchain/blockchain" -) - -type CommandLine struct { - blockchain *blockchain.BlockChain -} - -func (cli *CommandLine) printUsage() { - fmt.Println("Usage:") - fmt.Println(" add -block BLOCK_DATA - add a block to the chain") - fmt.Println(" print - Prints the blocks in the chain") -} - -func (cli *CommandLine) validateArgs() { - if len(os.Args) < 2 { - cli.printUsage() - runtime.Goexit() - } -} - -func (cli *CommandLine) addBlock(data string) { - cli.blockchain.AddBlock(data) - fmt.Println("Added Block!") -} - -func (cli *CommandLine) printChain() { - iter := cli.blockchain.Iterator() - - for { - block := iter.Next() - - fmt.Printf("Prev. hash: %x\n", block.PrevHash) - fmt.Printf("Data: %s\n", block.Data) - fmt.Printf("Hash: %x\n", block.Hash) - pow := blockchain.NewProof(block) - fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) - fmt.Println() - - if len(block.PrevHash) == 0 { - break - } - } -} - -func (cli *CommandLine) run() { - cli.validateArgs() - - addBlockCmd := flag.NewFlagSet("add", flag.ExitOnError) - printChainCmd := flag.NewFlagSet("print", flag.ExitOnError) - addBlockData := addBlockCmd.String("block", "", "Block data") - - switch os.Args[1] { - case "add": - err := addBlockCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - case "print": - err := printChainCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - default: - cli.printUsage() - runtime.Goexit() - } - - if addBlockCmd.Parsed() { - if *addBlockData == "" { - addBlockCmd.Usage() - runtime.Goexit() - } - cli.addBlock(*addBlockData) - } - - if printChainCmd.Parsed() { - cli.printChain() - } -} - -func main() { - defer os.Exit(0) - chain := blockchain.InitBlockChain() - defer chain.Database.Close() - - cli := CommandLine{chain} - cli.run() -} +package main + +import ( + "flag" + "fmt" + "os" + "runtime" + "strconv" + + "github.com/tensor-programming/golang-blockchain/blockchain" +) + +type CommandLine struct { + blockchain *blockchain.BlockChain +} + +func (cli *CommandLine) printUsage() { + fmt.Println("Usage:") + fmt.Println(" add -block BLOCK_DATA - add a block to the chain") + fmt.Println(" print - Prints the blocks in the chain") +} + +func (cli *CommandLine) validateArgs() { + if len(os.Args) < 2 { + cli.printUsage() + runtime.Goexit() + } +} + +func (cli *CommandLine) addBlock(data string) { + cli.blockchain.AddBlock(data) + fmt.Println("Added Block!") +} + +func (cli *CommandLine) printChain() { + iter := cli.blockchain.Iterator() + + for { + block := iter.Next() + + fmt.Printf("Prev. hash: %x\n", block.PrevHash) + fmt.Printf("Data: %s\n", block.Data) + fmt.Printf("Hash: %x\n", block.Hash) + pow := blockchain.NewProof(block) + fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) + fmt.Println() + + if len(block.PrevHash) == 0 { + break + } + } +} + +func (cli *CommandLine) run() { + cli.validateArgs() + + addBlockCmd := flag.NewFlagSet("add", flag.ExitOnError) + printChainCmd := flag.NewFlagSet("print", flag.ExitOnError) + addBlockData := addBlockCmd.String("block", "", "Block data") + + switch os.Args[1] { + case "add": + err := addBlockCmd.Parse(os.Args[2:]) + blockchain.Handle(err) + + case "print": + err := printChainCmd.Parse(os.Args[2:]) + blockchain.Handle(err) + + default: + cli.printUsage() + runtime.Goexit() + } + + if addBlockCmd.Parsed() { + if *addBlockData == "" { + addBlockCmd.Usage() + runtime.Goexit() + } + cli.addBlock(*addBlockData) + } + + if printChainCmd.Parsed() { + cli.printChain() + } +} + +func main() { + defer os.Exit(0) + chain := blockchain.InitBlockChain() + defer chain.Database.Close() + + cli := CommandLine{chain} + cli.run() +} From 3460d5a09af5e7f705e52add17a79e4d43e90313 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 26 Oct 2018 22:18:27 -0400 Subject: [PATCH 04/26] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3f3c9ad..2c4447ae 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Golang Blockchain -### In this tutorial, we look at how we can build a blockchain with Golang. +### In this tutorial, we add persistence via BadgerDB and a Command Line interface to our blockchain application. ## Run `go run main.go` to run the app, run `go build main.go` to build an executable file. -### Check out the Youtube Tutorial for this [Go Program](https://www.youtube.com/embed/aE4eDTUAE70). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. +### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/szOZ3p-5YIc). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. ### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). From 729670962785261dfe131cf5d75cdc66b0cc9fb0 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 29 Oct 2018 20:32:08 -0400 Subject: [PATCH 05/26] part 4 complete --- blockchain/block.go | 29 +++++-- blockchain/blockchain.go | 164 +++++++++++++++++++++++++++++++++----- blockchain/proof.go | 2 +- blockchain/transaction.go | 100 +++++++++++++++++++++++ main.go | 121 +++++++++++++++++++++------- 5 files changed, 355 insertions(+), 61 deletions(-) create mode 100644 blockchain/transaction.go diff --git a/blockchain/block.go b/blockchain/block.go index 05b056fa..145d1de1 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -2,19 +2,20 @@ package blockchain import ( "bytes" + "crypto/sha256" "encoding/gob" "log" ) type Block struct { - Hash []byte - Data []byte - PrevHash []byte - Nonce int + Hash []byte + Transactions []*Transaction + PrevHash []byte + Nonce int } -func CreateBlock(data string, prevHash []byte) *Block { - block := &Block{[]byte{}, []byte(data), prevHash, 0} +func CreateBlock(txs []*Transaction, prevHash []byte) *Block { + block := &Block{[]byte{}, txs, prevHash, 0} pow := NewProof(block) nonce, hash := pow.Run() @@ -24,8 +25,8 @@ func CreateBlock(data string, prevHash []byte) *Block { return block } -func Genesis() *Block { - return CreateBlock("Genesis", []byte{}) +func Genesis(coinbase *Transaction) *Block { + return CreateBlock([]*Transaction{coinbase}, []byte{}) } func (b *Block) Serialize() []byte { @@ -39,6 +40,18 @@ func (b *Block) Serialize() []byte { return res.Bytes() } +func (b *Block) HashTransactions() []byte { + var txHashes [][]byte + var txHash [32]byte + + for _, tx := range b.Transactions { + txHashes = append(txHashes, tx.ID) + } + txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) + + return txHash[:] +} + func Deserialize(data []byte) *Block { var block Block diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 088ddbec..900d7e32 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1,13 +1,18 @@ package blockchain import ( + "encoding/hex" "fmt" + "os" + "runtime" "github.com/dgraph-io/badger" ) const ( - dbPath = "./tmp/blocks" + dbPath = "./tmp/blocks" + dbFile = "./tmp/blocks/MANIFEST" + genesisData = "First Transaction from Genesis" ) type BlockChain struct { @@ -20,7 +25,20 @@ type BlockChainIterator struct { Database *badger.DB } -func InitBlockChain() *BlockChain { +func DBexists() bool { + if _, err := os.Stat(dbFile); os.IsNotExist(err) { + return false + } + + return true +} + +func CreateBlockChain(address string) *BlockChain { + if DBexists() == false { + fmt.Println("No existing blockchain found, please create one!") + runtime.Goexit() + } + var lastHash []byte opts := badger.DefaultOptions @@ -31,23 +49,46 @@ func InitBlockChain() *BlockChain { Handle(err) err = db.Update(func(txn *badger.Txn) error { - if _, err := txn.Get([]byte("lh")); err == badger.ErrKeyNotFound { - fmt.Println("No existing blockchain found") - genesis := Genesis() - fmt.Println("Genesis proved") - err = txn.Set(genesis.Hash, genesis.Serialize()) - Handle(err) - err = txn.Set([]byte("lh"), genesis.Hash) - - lastHash = genesis.Hash - - return err - } else { - item, err := txn.Get([]byte("lh")) - Handle(err) - lastHash, err = item.Value() - return err - } + item, err := txn.Get([]byte("lh")) + Handle(err) + lastHash, err = item.Value() + + return err + }) + Handle(err) + + chain := BlockChain{lastHash, db} + + return &chain +} + +func InitBlockChain(address string) *BlockChain { + var lastHash []byte + + if DBexists() { + fmt.Println("Blockchain already exists") + runtime.Goexit() + } + + opts := badger.DefaultOptions + opts.Dir = dbPath + opts.ValueDir = dbPath + + db, err := badger.Open(opts) + Handle(err) + + err = db.Update(func(txn *badger.Txn) error { + cbtx := CoinbaseTx(address, genesisData) + genesis := Genesis(cbtx) + fmt.Println("Genesis created") + err = txn.Set(genesis.Hash, genesis.Serialize()) + Handle(err) + err = txn.Set([]byte("lh"), genesis.Hash) + + lastHash = genesis.Hash + + return err + }) Handle(err) @@ -56,7 +97,7 @@ func InitBlockChain() *BlockChain { return &blockchain } -func (chain *BlockChain) AddBlock(data string) { +func (chain *BlockChain) AddBlock(transactions []*Transaction) { var lastHash []byte err := chain.Database.View(func(txn *badger.Txn) error { @@ -68,7 +109,7 @@ func (chain *BlockChain) AddBlock(data string) { }) Handle(err) - newBlock := CreateBlock(data, lastHash) + newBlock := CreateBlock(transactions, lastHash) err = chain.Database.Update(func(txn *badger.Txn) error { err := txn.Set(newBlock.Hash, newBlock.Serialize()) @@ -82,6 +123,87 @@ func (chain *BlockChain) AddBlock(data string) { Handle(err) } +func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction { + var unspentTxs []Transaction + + spentTXOs := make(map[string][]int) + + iter := chain.Iterator() + + for { + block := iter.Next() + + for _, tx := range block.Transactions { + txID := hex.EncodeToString(tx.ID) + + Outputs: + for outIdx, out := range tx.Outputs { + if spentTXOs[txID] != nil { + for _, spentOut := range spentTXOs[txID] { + if spentOut == outIdx { + continue Outputs + } + } + } + if out.CanBeUnlocked(address) { + unspentTxs = append(unspentTxs, *tx) + } + } + if tx.IsCoinbase() == false { + for _, in := range tx.Inputs { + if in.CanUnlock(address) { + inTxID := hex.EncodeToString(in.ID) + spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Out) + } + } + } + } + + if len(block.PrevHash) == 0 { + break + } + } + return unspentTxs +} + +func (chain *BlockChain) FindUTXO(address string) []TxOutput { + var UTXOs []TxOutput + unspentTransactions := chain.FindUnspentTransactions(address) + + for _, tx := range unspentTransactions { + for _, out := range tx.Outputs { + if out.CanBeUnlocked(address) { + UTXOs = append(UTXOs, out) + } + } + } + return UTXOs +} + +func (chain *BlockChain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { + unspentOuts := make(map[string][]int) + unspentTxs := chain.FindUnspentTransactions(address) + accumulated := 0 + +Work: + for _, tx := range unspentTxs { + txID := hex.EncodeToString(tx.ID) + + for outIdx, out := range tx.Outputs { + if out.CanBeUnlocked(address) && accumulated < amount { + accumulated += out.Value + unspentOuts[txID] = append(unspentOuts[txID], outIdx) + + if accumulated >= amount { + break Work + } + } + } + } + + return accumulated, unspentOuts +} + func (chain *BlockChain) Iterator() *BlockChainIterator { iter := &BlockChainIterator{chain.LastHash, chain.Database} diff --git a/blockchain/proof.go b/blockchain/proof.go index e2ce0c50..869ca685 100644 --- a/blockchain/proof.go +++ b/blockchain/proof.go @@ -41,7 +41,7 @@ func (pow *ProofOfWork) InitData(nonce int) []byte { data := bytes.Join( [][]byte{ pow.Block.PrevHash, - pow.Block.Data, + pow.Block.HashTransactions(), ToHex(int64(nonce)), ToHex(int64(Difficulty)), }, diff --git a/blockchain/transaction.go b/blockchain/transaction.go new file mode 100644 index 00000000..4e058d57 --- /dev/null +++ b/blockchain/transaction.go @@ -0,0 +1,100 @@ +package blockchain + +import ( + "bytes" + "crypto/sha256" + "encoding/gob" + "encoding/hex" + "fmt" + "log" +) + +const subsidy = 100 + +type Transaction struct { + ID []byte + Inputs []TxInput + Outputs []TxOutput +} + +type TxOutput struct { + Value int + PubKey string +} + +type TxInput struct { + ID []byte + Out int + Sig string +} + +func (in *TxInput) CanUnlock(data string) bool { + return in.Sig == data +} + +func (out *TxOutput) CanBeUnlocked(data string) bool { + return out.PubKey == data +} + +func CoinbaseTx(to, data string) *Transaction { + if data == "" { + data = fmt.Sprintf("Coins to %s", to) + + } + + txin := TxInput{[]byte{}, -1, data} + txout := TxOutput{subsidy, to} + tx := Transaction{nil, []TxInput{txin}, []TxOutput{txout}} + tx.SetID() + + return &tx +} + +func (tx *Transaction) IsCoinbase() bool { + return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1 +} + +func NewUTXOTransaction(from, to string, amount int, chain *BlockChain) *Transaction { + var inputs []TxInput + var outputs []TxOutput + + acc, validOutputs := chain.FindSpendableOutputs(from, amount) + + if acc < amount { + log.Panic("Error: not enough funds") + } + + for txid, outs := range validOutputs { + txID, err := hex.DecodeString(txid) + Handle(err) + + for _, out := range outs { + input := TxInput{txID, out, from} + inputs = append(inputs, input) + } + + } + + outputs = append(outputs, TxOutput{amount, to}) + + if acc > amount { + outputs = append(outputs, TxOutput{acc - amount, from}) + } + + tx := Transaction{nil, inputs, outputs} + tx.SetID() + + return &tx +} + +func (tx *Transaction) SetID() { + var encoded bytes.Buffer + var hash [32]byte + + encode := gob.NewEncoder(&encoded) + err := encode.Encode(tx) + Handle(err) + + hash = sha256.Sum256(encoded.Bytes()) + tx.ID = hash[:] +} diff --git a/main.go b/main.go index 72d43bfa..fa4982ce 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "log" "os" "runtime" "strconv" @@ -10,14 +11,34 @@ import ( "github.com/tensor-programming/golang-blockchain/blockchain" ) -type CommandLine struct { - blockchain *blockchain.BlockChain +type CommandLine struct{} + +func (cli *CommandLine) createBlockChain(address string) { + chain := blockchain.InitBlockChain(address) + chain.Database.Close() + fmt.Println("Finished!") +} + +func (cli *CommandLine) getBalance(address string) { + chain := blockchain.CreateBlockChain(address) + defer chain.Database.Close() + + balance := 0 + UTXOs := chain.FindUTXO(address) + + for _, out := range UTXOs { + balance += out.Value + } + + fmt.Printf("Balance of %s: %d\n", address, balance) } func (cli *CommandLine) printUsage() { fmt.Println("Usage:") - fmt.Println(" add -block BLOCK_DATA - add a block to the chain") - fmt.Println(" print - Prints the blocks in the chain") + fmt.Println(" getbalance -address ADDRESS - get the balance for an address") + fmt.Println(" createblockchain -address ADDRESS creates a blockchain and sends genesis reward to address") + fmt.Println(" printchain - Prints the blocks in the chain") + fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send amount of coins") } func (cli *CommandLine) validateArgs() { @@ -27,19 +48,15 @@ func (cli *CommandLine) validateArgs() { } } -func (cli *CommandLine) addBlock(data string) { - cli.blockchain.AddBlock(data) - fmt.Println("Added Block!") -} - func (cli *CommandLine) printChain() { - iter := cli.blockchain.Iterator() + chain := blockchain.CreateBlockChain("") + defer chain.Database.Close() + iter := chain.Iterator() for { block := iter.Next() fmt.Printf("Prev. hash: %x\n", block.PrevHash) - fmt.Printf("Data: %s\n", block.Data) fmt.Printf("Hash: %x\n", block.Hash) pow := blockchain.NewProof(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) @@ -51,45 +68,87 @@ func (cli *CommandLine) printChain() { } } +func (cli *CommandLine) send(from, to string, amount int) { + chain := blockchain.CreateBlockChain(from) + defer chain.Database.Close() + + tx := blockchain.NewUTXOTransaction(from, to, amount, chain) + chain.AddBlock([]*blockchain.Transaction{tx}) + fmt.Println("Success!") +} + func (cli *CommandLine) run() { cli.validateArgs() - addBlockCmd := flag.NewFlagSet("add", flag.ExitOnError) - printChainCmd := flag.NewFlagSet("print", flag.ExitOnError) - addBlockData := addBlockCmd.String("block", "", "Block data") + getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) + createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) + sendCmd := flag.NewFlagSet("send", flag.ExitOnError) + printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) - switch os.Args[1] { - case "add": - err := addBlockCmd.Parse(os.Args[2:]) - blockchain.Handle(err) + getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") + createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") + sendFrom := sendCmd.String("from", "", "Source wallet address") + sendTo := sendCmd.String("to", "", "Destination wallet address") + sendAmount := sendCmd.Int("amount", 0, "Amount to send") - case "print": + switch os.Args[1] { + case "getbalance": + err := getBalanceCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "createblockchain": + err := createBlockchainCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "printchain": err := printChainCmd.Parse(os.Args[2:]) - blockchain.Handle(err) - + if err != nil { + log.Panic(err) + } + case "send": + err := sendCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } default: cli.printUsage() - runtime.Goexit() + os.Exit(1) + } + + if getBalanceCmd.Parsed() { + if *getBalanceAddress == "" { + getBalanceCmd.Usage() + os.Exit(1) + } + cli.getBalance(*getBalanceAddress) } - if addBlockCmd.Parsed() { - if *addBlockData == "" { - addBlockCmd.Usage() - runtime.Goexit() + if createBlockchainCmd.Parsed() { + if *createBlockchainAddress == "" { + createBlockchainCmd.Usage() + os.Exit(1) } - cli.addBlock(*addBlockData) + cli.createBlockChain(*createBlockchainAddress) } if printChainCmd.Parsed() { cli.printChain() } + + if sendCmd.Parsed() { + if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 { + sendCmd.Usage() + os.Exit(1) + } + + cli.send(*sendFrom, *sendTo, *sendAmount) + } } func main() { defer os.Exit(0) - chain := blockchain.InitBlockChain() - defer chain.Database.Close() - - cli := CommandLine{chain} + cli := CommandLine{} cli.run() } From 1367ae254dd903e58b0698ab119dc833352ee312 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Tue, 30 Oct 2018 18:53:15 -0400 Subject: [PATCH 06/26] minor changes --- blockchain/blockchain.go | 4 ++-- main.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 900d7e32..41bf8334 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -33,9 +33,9 @@ func DBexists() bool { return true } -func CreateBlockChain(address string) *BlockChain { +func ContinueBlockChain(address string) *BlockChain { if DBexists() == false { - fmt.Println("No existing blockchain found, please create one!") + fmt.Println("No existing blockchain found, create one!") runtime.Goexit() } diff --git a/main.go b/main.go index fa4982ce..ebc10f2a 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,7 @@ func (cli *CommandLine) createBlockChain(address string) { } func (cli *CommandLine) getBalance(address string) { - chain := blockchain.CreateBlockChain(address) + chain := blockchain.ContinueBlockChain(address) defer chain.Database.Close() balance := 0 @@ -49,7 +49,7 @@ func (cli *CommandLine) validateArgs() { } func (cli *CommandLine) printChain() { - chain := blockchain.CreateBlockChain("") + chain := blockchain.ContinueBlockChain("") defer chain.Database.Close() iter := chain.Iterator() @@ -69,7 +69,7 @@ func (cli *CommandLine) printChain() { } func (cli *CommandLine) send(from, to string, amount int) { - chain := blockchain.CreateBlockChain(from) + chain := blockchain.ContinueBlockChain(from) defer chain.Database.Close() tx := blockchain.NewUTXOTransaction(from, to, amount, chain) From dd091a9a3fa730667de0b372d9d223e5dec9bb23 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Wed, 31 Oct 2018 19:06:03 -0400 Subject: [PATCH 07/26] add changes --- blockchain/block.go | 24 ++--- blockchain/blockchain.go | 48 +++++----- blockchain/proof.go | 2 +- blockchain/transaction.go | 197 +++++++++++++++++++------------------- main.go | 50 +++++----- 5 files changed, 159 insertions(+), 162 deletions(-) diff --git a/blockchain/block.go b/blockchain/block.go index 145d1de1..1e7bd95b 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -14,6 +14,18 @@ type Block struct { Nonce int } +func (b *Block) HashTransactions() []byte { + var txHashes [][]byte + var txHash [32]byte + + for _, tx := range b.Transactions { + txHashes = append(txHashes, tx.ID) + } + txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) + + return txHash[:] +} + func CreateBlock(txs []*Transaction, prevHash []byte) *Block { block := &Block{[]byte{}, txs, prevHash, 0} pow := NewProof(block) @@ -40,18 +52,6 @@ func (b *Block) Serialize() []byte { return res.Bytes() } -func (b *Block) HashTransactions() []byte { - var txHashes [][]byte - var txHash [32]byte - - for _, tx := range b.Transactions { - txHashes = append(txHashes, tx.ID) - } - txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) - - return txHash[:] -} - func Deserialize(data []byte) *Block { var block Block diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 41bf8334..81261550 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -123,6 +123,30 @@ func (chain *BlockChain) AddBlock(transactions []*Transaction) { Handle(err) } +func (chain *BlockChain) Iterator() *BlockChainIterator { + iter := &BlockChainIterator{chain.LastHash, chain.Database} + + return iter +} + +func (iter *BlockChainIterator) Next() *Block { + var block *Block + + err := iter.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get(iter.CurrentHash) + Handle(err) + encodedBlock, err := item.Value() + block = Deserialize(encodedBlock) + + return err + }) + Handle(err) + + iter.CurrentHash = block.PrevHash + + return block +} + func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction { var unspentTxs []Transaction @@ -203,27 +227,3 @@ Work: return accumulated, unspentOuts } - -func (chain *BlockChain) Iterator() *BlockChainIterator { - iter := &BlockChainIterator{chain.LastHash, chain.Database} - - return iter -} - -func (iter *BlockChainIterator) Next() *Block { - var block *Block - - err := iter.Database.View(func(txn *badger.Txn) error { - item, err := txn.Get(iter.CurrentHash) - Handle(err) - encodedBlock, err := item.Value() - block = Deserialize(encodedBlock) - - return err - }) - Handle(err) - - iter.CurrentHash = block.PrevHash - - return block -} diff --git a/blockchain/proof.go b/blockchain/proof.go index 869ca685..9a291e74 100644 --- a/blockchain/proof.go +++ b/blockchain/proof.go @@ -21,7 +21,7 @@ import ( // Requirements: // The First few bytes must contain 0s -const Difficulty = 18 +const Difficulty = 12 type ProofOfWork struct { Block *Block diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 4e058d57..9edf9abe 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -1,100 +1,97 @@ -package blockchain - -import ( - "bytes" - "crypto/sha256" - "encoding/gob" - "encoding/hex" - "fmt" - "log" -) - -const subsidy = 100 - -type Transaction struct { - ID []byte - Inputs []TxInput - Outputs []TxOutput -} - -type TxOutput struct { - Value int - PubKey string -} - -type TxInput struct { - ID []byte - Out int - Sig string -} - -func (in *TxInput) CanUnlock(data string) bool { - return in.Sig == data -} - -func (out *TxOutput) CanBeUnlocked(data string) bool { - return out.PubKey == data -} - -func CoinbaseTx(to, data string) *Transaction { - if data == "" { - data = fmt.Sprintf("Coins to %s", to) - - } - - txin := TxInput{[]byte{}, -1, data} - txout := TxOutput{subsidy, to} - tx := Transaction{nil, []TxInput{txin}, []TxOutput{txout}} - tx.SetID() - - return &tx -} - -func (tx *Transaction) IsCoinbase() bool { - return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1 -} - -func NewUTXOTransaction(from, to string, amount int, chain *BlockChain) *Transaction { - var inputs []TxInput - var outputs []TxOutput - - acc, validOutputs := chain.FindSpendableOutputs(from, amount) - - if acc < amount { - log.Panic("Error: not enough funds") - } - - for txid, outs := range validOutputs { - txID, err := hex.DecodeString(txid) - Handle(err) - - for _, out := range outs { - input := TxInput{txID, out, from} - inputs = append(inputs, input) - } - - } - - outputs = append(outputs, TxOutput{amount, to}) - - if acc > amount { - outputs = append(outputs, TxOutput{acc - amount, from}) - } - - tx := Transaction{nil, inputs, outputs} - tx.SetID() - - return &tx -} - -func (tx *Transaction) SetID() { - var encoded bytes.Buffer - var hash [32]byte - - encode := gob.NewEncoder(&encoded) - err := encode.Encode(tx) - Handle(err) - - hash = sha256.Sum256(encoded.Bytes()) - tx.ID = hash[:] -} +package blockchain + +import ( + "bytes" + "crypto/sha256" + "encoding/gob" + "encoding/hex" + "fmt" + "log" +) + +type Transaction struct { + ID []byte + Inputs []TxInput + Outputs []TxOutput +} + +type TxOutput struct { + Value int + PubKey string +} + +type TxInput struct { + ID []byte + Out int + Sig string +} + +func (tx *Transaction) SetID() { + var encoded bytes.Buffer + var hash [32]byte + + encode := gob.NewEncoder(&encoded) + err := encode.Encode(tx) + Handle(err) + + hash = sha256.Sum256(encoded.Bytes()) + tx.ID = hash[:] +} + +func CoinbaseTx(to, data string) *Transaction { + if data == "" { + data = fmt.Sprintf("Coins to %s", to) + } + + txin := TxInput{[]byte{}, -1, data} + txout := TxOutput{100, to} + + tx := Transaction{nil, []TxInput{txin}, []TxOutput{txout}} + tx.SetID() + + return &tx +} + +func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction { + var inputs []TxInput + var outputs []TxOutput + + acc, validOutputs := chain.FindSpendableOutputs(from, amount) + + if acc < amount { + log.Panic("Error: not enough funds") + } + + for txid, outs := range validOutputs { + txID, err := hex.DecodeString(txid) + Handle(err) + + for _, out := range outs { + input := TxInput{txID, out, from} + inputs = append(inputs, input) + } + } + + outputs = append(outputs, TxOutput{amount, to}) + + if acc > amount { + outputs = append(outputs, TxOutput{acc - amount, from}) + } + + tx := Transaction{nil, inputs, outputs} + tx.SetID() + + return &tx +} + +func (tx *Transaction) IsCoinbase() bool { + return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1 +} + +func (in *TxInput) CanUnlock(data string) bool { + return in.Sig == data +} + +func (out *TxOutput) CanBeUnlocked(data string) bool { + return out.PubKey == data +} diff --git a/main.go b/main.go index ebc10f2a..9f7a5303 100644 --- a/main.go +++ b/main.go @@ -13,26 +13,6 @@ import ( type CommandLine struct{} -func (cli *CommandLine) createBlockChain(address string) { - chain := blockchain.InitBlockChain(address) - chain.Database.Close() - fmt.Println("Finished!") -} - -func (cli *CommandLine) getBalance(address string) { - chain := blockchain.ContinueBlockChain(address) - defer chain.Database.Close() - - balance := 0 - UTXOs := chain.FindUTXO(address) - - for _, out := range UTXOs { - balance += out.Value - } - - fmt.Printf("Balance of %s: %d\n", address, balance) -} - func (cli *CommandLine) printUsage() { fmt.Println("Usage:") fmt.Println(" getbalance -address ADDRESS - get the balance for an address") @@ -68,11 +48,31 @@ func (cli *CommandLine) printChain() { } } +func (cli *CommandLine) createBlockChain(address string) { + chain := blockchain.InitBlockChain(address) + chain.Database.Close() + fmt.Println("Finished!") +} + +func (cli *CommandLine) getBalance(address string) { + chain := blockchain.ContinueBlockChain(address) + defer chain.Database.Close() + + balance := 0 + UTXOs := chain.FindUTXO(address) + + for _, out := range UTXOs { + balance += out.Value + } + + fmt.Printf("Balance of %s: %d\n", address, balance) +} + func (cli *CommandLine) send(from, to string, amount int) { chain := blockchain.ContinueBlockChain(from) defer chain.Database.Close() - tx := blockchain.NewUTXOTransaction(from, to, amount, chain) + tx := blockchain.NewTransaction(from, to, amount, chain) chain.AddBlock([]*blockchain.Transaction{tx}) fmt.Println("Success!") } @@ -114,13 +114,13 @@ func (cli *CommandLine) run() { } default: cli.printUsage() - os.Exit(1) + runtime.Goexit() } if getBalanceCmd.Parsed() { if *getBalanceAddress == "" { getBalanceCmd.Usage() - os.Exit(1) + runtime.Goexit() } cli.getBalance(*getBalanceAddress) } @@ -128,7 +128,7 @@ func (cli *CommandLine) run() { if createBlockchainCmd.Parsed() { if *createBlockchainAddress == "" { createBlockchainCmd.Usage() - os.Exit(1) + runtime.Goexit() } cli.createBlockChain(*createBlockchainAddress) } @@ -140,7 +140,7 @@ func (cli *CommandLine) run() { if sendCmd.Parsed() { if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 { sendCmd.Usage() - os.Exit(1) + runtime.Goexit() } cli.send(*sendFrom, *sendTo, *sendAmount) From 6e63b7376562ad25eaf15abf07e4049367f7e7f4 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Wed, 31 Oct 2018 20:16:48 -0400 Subject: [PATCH 08/26] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c4447ae..2ff21211 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Golang Blockchain -### In this tutorial, we add persistence via BadgerDB and a Command Line interface to our blockchain application. +### In this tutorial, we add transactions to our blockchain and we talk about inputs and outputs. ## Run `go run main.go` to run the app, run `go build main.go` to build an executable file. -### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/szOZ3p-5YIc). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. +### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/HNID8W2jgRM). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. ### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). From b23714e5ef69c4d1a25acd1a95b25bcb996fb061 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 5 Nov 2018 19:12:51 -0500 Subject: [PATCH 09/26] added part_5 --- blockchain/transaction.go | 19 ---- blockchain/tx.go | 20 ++++ cli/cli.go | 188 ++++++++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 4 + main.go | 147 +---------------------------- wallet.mmd | 6 ++ wallet/utils.go | 24 +++++ wallet/wallet.go | 73 +++++++++++++++ wallet/wallets.go | 90 ++++++++++++++++++ 10 files changed, 410 insertions(+), 163 deletions(-) create mode 100644 blockchain/tx.go create mode 100644 cli/cli.go create mode 100644 wallet.mmd create mode 100644 wallet/utils.go create mode 100644 wallet/wallet.go create mode 100644 wallet/wallets.go diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 9edf9abe..8ff3e89b 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -15,17 +15,6 @@ type Transaction struct { Outputs []TxOutput } -type TxOutput struct { - Value int - PubKey string -} - -type TxInput struct { - ID []byte - Out int - Sig string -} - func (tx *Transaction) SetID() { var encoded bytes.Buffer var hash [32]byte @@ -87,11 +76,3 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction func (tx *Transaction) IsCoinbase() bool { return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1 } - -func (in *TxInput) CanUnlock(data string) bool { - return in.Sig == data -} - -func (out *TxOutput) CanBeUnlocked(data string) bool { - return out.PubKey == data -} diff --git a/blockchain/tx.go b/blockchain/tx.go new file mode 100644 index 00000000..b1c62757 --- /dev/null +++ b/blockchain/tx.go @@ -0,0 +1,20 @@ +package blockchain + +type TxOutput struct { + Value int + PubKey string +} + +type TxInput struct { + ID []byte + Out int + Sig string +} + +func (in *TxInput) CanUnlock(data string) bool { + return in.Sig == data +} + +func (out *TxOutput) CanBeUnlocked(data string) bool { + return out.PubKey == data +} diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 00000000..d51081db --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,188 @@ +package cli + +import ( + "flag" + "fmt" + "log" + "os" + "runtime" + "strconv" + + "github.com/tensor-programming/golang-blockchain/blockchain" + "github.com/tensor-programming/golang-blockchain/wallet" +) + +type CommandLine struct{} + +func (cli *CommandLine) printUsage() { + fmt.Println("Usage:") + fmt.Println(" getbalance -address ADDRESS - get the balance for an address") + fmt.Println(" createblockchain -address ADDRESS creates a blockchain and sends genesis reward to address") + fmt.Println(" printchain - Prints the blocks in the chain") + fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send amount of coins") + fmt.Println(" createwallet - Creates a new Wallet") + fmt.Println(" listaddresses - Lists the addresses in our wallet file") +} + +func (cli *CommandLine) validateArgs() { + if len(os.Args) < 2 { + cli.printUsage() + runtime.Goexit() + } +} + +func (cli *CommandLine) listAddresses() { + wallets, _ := wallet.CreateWallets() + addresses := wallets.GetAllAddresses() + + for _, address := range addresses { + fmt.Println(address) + } +} + +func (cli *CommandLine) createWallet() { + wallets, _ := wallet.CreateWallets() + address := wallets.AddWallet() + wallets.SaveFile() + + fmt.Printf("New address is: %s\n", address) +} + +func (cli *CommandLine) printChain() { + chain := blockchain.ContinueBlockChain("") + defer chain.Database.Close() + iter := chain.Iterator() + + for { + block := iter.Next() + + fmt.Printf("Prev. hash: %x\n", block.PrevHash) + fmt.Printf("Hash: %x\n", block.Hash) + pow := blockchain.NewProof(block) + fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) + fmt.Println() + + if len(block.PrevHash) == 0 { + break + } + } +} + +func (cli *CommandLine) createBlockChain(address string) { + chain := blockchain.InitBlockChain(address) + chain.Database.Close() + fmt.Println("Finished!") +} + +func (cli *CommandLine) getBalance(address string) { + chain := blockchain.ContinueBlockChain(address) + defer chain.Database.Close() + + balance := 0 + UTXOs := chain.FindUTXO(address) + + for _, out := range UTXOs { + balance += out.Value + } + + fmt.Printf("Balance of %s: %d\n", address, balance) +} + +func (cli *CommandLine) send(from, to string, amount int) { + chain := blockchain.ContinueBlockChain(from) + defer chain.Database.Close() + + tx := blockchain.NewTransaction(from, to, amount, chain) + chain.AddBlock([]*blockchain.Transaction{tx}) + fmt.Println("Success!") +} + +func (cli *CommandLine) Run() { + cli.validateArgs() + + getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) + createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) + sendCmd := flag.NewFlagSet("send", flag.ExitOnError) + printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) + createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError) + listAddressesCmd := flag.NewFlagSet("listaddresses", flag.ExitOnError) + + + getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") + createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") + sendFrom := sendCmd.String("from", "", "Source wallet address") + sendTo := sendCmd.String("to", "", "Destination wallet address") + sendAmount := sendCmd.Int("amount", 0, "Amount to send") + + switch os.Args[1] { + case "getbalance": + err := getBalanceCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "createblockchain": + err := createBlockchainCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "listaddresses": + err := listAddressesCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "createwallet": + err := createWalletCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "printchain": + err := printChainCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "send": + err := sendCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + default: + cli.printUsage() + runtime.Goexit() + } + + if getBalanceCmd.Parsed() { + if *getBalanceAddress == "" { + getBalanceCmd.Usage() + runtime.Goexit() + } + cli.getBalance(*getBalanceAddress) + } + + if createBlockchainCmd.Parsed() { + if *createBlockchainAddress == "" { + createBlockchainCmd.Usage() + runtime.Goexit() + } + cli.createBlockChain(*createBlockchainAddress) + } + + if printChainCmd.Parsed() { + cli.printChain() + } + + if createWalletCmd.Parsed() { + cli.createWallet() + } + if listAddressesCmd.Parsed() { + cli.listAddresses() + } + + if sendCmd.Parsed() { + if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 { + sendCmd.Usage() + runtime.Goexit() + } + + cli.send(*sendFrom, *sendTo, *sendAmount) + } +} diff --git a/go.mod b/go.mod index 61f3006f..076e1c93 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ require ( github.com/dgraph-io/badger v1.5.4 github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect github.com/golang/protobuf v1.2.0 // indirect + github.com/mr-tron/base58 v1.1.0 github.com/pkg/errors v0.8.0 // indirect + golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect ) diff --git a/go.sum b/go.sum index aad3a217..bb88cfd7 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,11 @@ github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+ github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/main.go b/main.go index 9f7a5303..3102043c 100644 --- a/main.go +++ b/main.go @@ -1,154 +1,13 @@ package main import ( - "flag" - "fmt" - "log" "os" - "runtime" - "strconv" - "github.com/tensor-programming/golang-blockchain/blockchain" + "github.com/tensor-programming/golang-blockchain/cli" ) -type CommandLine struct{} - -func (cli *CommandLine) printUsage() { - fmt.Println("Usage:") - fmt.Println(" getbalance -address ADDRESS - get the balance for an address") - fmt.Println(" createblockchain -address ADDRESS creates a blockchain and sends genesis reward to address") - fmt.Println(" printchain - Prints the blocks in the chain") - fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send amount of coins") -} - -func (cli *CommandLine) validateArgs() { - if len(os.Args) < 2 { - cli.printUsage() - runtime.Goexit() - } -} - -func (cli *CommandLine) printChain() { - chain := blockchain.ContinueBlockChain("") - defer chain.Database.Close() - iter := chain.Iterator() - - for { - block := iter.Next() - - fmt.Printf("Prev. hash: %x\n", block.PrevHash) - fmt.Printf("Hash: %x\n", block.Hash) - pow := blockchain.NewProof(block) - fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) - fmt.Println() - - if len(block.PrevHash) == 0 { - break - } - } -} - -func (cli *CommandLine) createBlockChain(address string) { - chain := blockchain.InitBlockChain(address) - chain.Database.Close() - fmt.Println("Finished!") -} - -func (cli *CommandLine) getBalance(address string) { - chain := blockchain.ContinueBlockChain(address) - defer chain.Database.Close() - - balance := 0 - UTXOs := chain.FindUTXO(address) - - for _, out := range UTXOs { - balance += out.Value - } - - fmt.Printf("Balance of %s: %d\n", address, balance) -} - -func (cli *CommandLine) send(from, to string, amount int) { - chain := blockchain.ContinueBlockChain(from) - defer chain.Database.Close() - - tx := blockchain.NewTransaction(from, to, amount, chain) - chain.AddBlock([]*blockchain.Transaction{tx}) - fmt.Println("Success!") -} - -func (cli *CommandLine) run() { - cli.validateArgs() - - getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) - createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) - sendCmd := flag.NewFlagSet("send", flag.ExitOnError) - printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) - - getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") - createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") - sendFrom := sendCmd.String("from", "", "Source wallet address") - sendTo := sendCmd.String("to", "", "Destination wallet address") - sendAmount := sendCmd.Int("amount", 0, "Amount to send") - - switch os.Args[1] { - case "getbalance": - err := getBalanceCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - case "createblockchain": - err := createBlockchainCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - case "printchain": - err := printChainCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - case "send": - err := sendCmd.Parse(os.Args[2:]) - if err != nil { - log.Panic(err) - } - default: - cli.printUsage() - runtime.Goexit() - } - - if getBalanceCmd.Parsed() { - if *getBalanceAddress == "" { - getBalanceCmd.Usage() - runtime.Goexit() - } - cli.getBalance(*getBalanceAddress) - } - - if createBlockchainCmd.Parsed() { - if *createBlockchainAddress == "" { - createBlockchainCmd.Usage() - runtime.Goexit() - } - cli.createBlockChain(*createBlockchainAddress) - } - - if printChainCmd.Parsed() { - cli.printChain() - } - - if sendCmd.Parsed() { - if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 { - sendCmd.Usage() - runtime.Goexit() - } - - cli.send(*sendFrom, *sendTo, *sendAmount) - } -} - func main() { defer os.Exit(0) - cli := CommandLine{} - cli.run() + cmd := cli.CommandLine{} + cmd.Run() } diff --git a/wallet.mmd b/wallet.mmd new file mode 100644 index 00000000..51bea1ff --- /dev/null +++ b/wallet.mmd @@ -0,0 +1,6 @@ +graph TB +subgraph Wallet +pr(Private key) --> pb(Public key) + +pb --> a(Sddress) +end \ No newline at end of file diff --git a/wallet/utils.go b/wallet/utils.go new file mode 100644 index 00000000..9ef207f6 --- /dev/null +++ b/wallet/utils.go @@ -0,0 +1,24 @@ +package wallet + +import ( + "log" + + "github.com/mr-tron/base58" +) + +func Base58Encode(input []byte) []byte { + encode := base58.Encode(input) + + return []byte(encode) +} + +func Base58Decode(input []byte) []byte { + decode, err := base58.Decode(string(input[:])) + if err != nil { + log.Panic(err) + } + + return decode +} + +// 0 O l I + / diff --git a/wallet/wallet.go b/wallet/wallet.go new file mode 100644 index 00000000..a59df1af --- /dev/null +++ b/wallet/wallet.go @@ -0,0 +1,73 @@ +package wallet + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "log" + + "golang.org/x/crypto/ripemd160" +) + +const ( + checksumLength = 4 + version = byte(0x00) +) + +type Wallet struct { + PrivateKey ecdsa.PrivateKey + PublicKey []byte +} + +func (w Wallet) Address() []byte { + pubHash := PublicKeyHash(w.PublicKey) + + versionedHash := append([]byte{version}, pubHash...) + checksum := Checksum(versionedHash) + + fullHash := append(versionedHash, checksum...) + address := Base58Encode(fullHash) + + return address +} + +func NewKeyPair() (ecdsa.PrivateKey, []byte) { + curve := elliptic.P256() + + private, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + log.Panic(err) + } + + pub := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...) + return *private, pub +} + +func MakeWallet() *Wallet { + private, public := NewKeyPair() + wallet := Wallet{private, public} + + return &wallet +} + +func PublicKeyHash(pubKey []byte) []byte { + pubHash := sha256.Sum256(pubKey) + + hasher := ripemd160.New() + _, err := hasher.Write(pubHash[:]) + if err != nil { + log.Panic(err) + } + + publicRipMD := hasher.Sum(nil) + + return publicRipMD +} + +func Checksum(payload []byte) []byte { + firstHash := sha256.Sum256(payload) + secondHash := sha256.Sum256(firstHash[:]) + + return secondHash[:checksumLength] +} diff --git a/wallet/wallets.go b/wallet/wallets.go new file mode 100644 index 00000000..f09d0a7d --- /dev/null +++ b/wallet/wallets.go @@ -0,0 +1,90 @@ +package wallet + +import ( + "bytes" + "crypto/elliptic" + "encoding/gob" + "fmt" + "io/ioutil" + "log" + "os" +) + +const walletFile = "./tmp/wallets.data" + +type Wallets struct { + Wallets map[string]*Wallet +} + +func CreateWallets() (*Wallets, error) { + wallets := Wallets{} + wallets.Wallets = make(map[string]*Wallet) + + err := wallets.LoadFile() + + return &wallets, err +} + +func (ws *Wallets) AddWallet() string { + wallet := MakeWallet() + address := fmt.Sprintf("%s", wallet.Address()) + + ws.Wallets[address] = wallet + + return address +} + +func (ws *Wallets) GetAllAddresses() []string { + var addresses []string + + for address := range ws.Wallets { + addresses = append(addresses, address) + } + + return addresses +} + +func (ws Wallets) GetWallet(address string) Wallet { + return *ws.Wallets[address] +} + +func (ws *Wallets) LoadFile() error { + if _, err := os.Stat(walletFile); os.IsNotExist(err) { + return err + } + + var wallets Wallets + + fileContent, err := ioutil.ReadFile(walletFile) + if err != nil { + return err + } + + gob.Register(elliptic.P256()) + decoder := gob.NewDecoder(bytes.NewReader(fileContent)) + err = decoder.Decode(&wallets) + if err != nil { + return err + } + + ws.Wallets = wallets.Wallets + + return nil +} + +func (ws *Wallets) SaveFile() { + var content bytes.Buffer + + gob.Register(elliptic.P256()) + + encoder := gob.NewEncoder(&content) + err := encoder.Encode(ws) + if err != nil { + log.Panic(err) + } + + err = ioutil.WriteFile(walletFile, content.Bytes(), 0644) + if err != nil { + log.Panic(err) + } +} From 39fb7d9df4cae4987e685b7ad4a57de6926a8fd8 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Mon, 5 Nov 2018 19:25:07 -0500 Subject: [PATCH 10/26] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ff21211..225f87d7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Golang Blockchain -### In this tutorial, we add transactions to our blockchain and we talk about inputs and outputs. +### In this tutorial, we add a Wallet Module to our Blockchain app. ## Run `go run main.go` to run the app, run `go build main.go` to build an executable file. -### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/HNID8W2jgRM). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. +### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/O9rDas-0s2c). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. ### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). From 202d33f075c421598ec5bd2b25ac29ddfbebe47c Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 5 Nov 2018 19:25:39 -0500 Subject: [PATCH 11/26] update mermaid md graph --- wallet.mmd | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/wallet.mmd b/wallet.mmd index 51bea1ff..23605ef6 100644 --- a/wallet.mmd +++ b/wallet.mmd @@ -1,6 +1,16 @@ graph TB -subgraph Wallet -pr(Private key) --> pb(Public key) +pr(private key) --> ec(ecdsa) +ec --> pb(public key) -pb --> a(Sddress) -end \ No newline at end of file +pb --> s(sha256) +s --> r(ripemd160) +r --> k(public key hash) +k --> s1(sha256) +s1 --> s2(sha256) +s2 --> e(4 bytes) +e --> c +c(checksum) +c --> b(base 58) +k --> b +v(version) --> b +b --> a(address) From 698e3c76cae34b302f39d065486b8af6b2737241 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 5 Nov 2018 19:26:19 -0500 Subject: [PATCH 12/26] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ff21211..225f87d7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Golang Blockchain -### In this tutorial, we add transactions to our blockchain and we talk about inputs and outputs. +### In this tutorial, we add a Wallet Module to our Blockchain app. ## Run `go run main.go` to run the app, run `go build main.go` to build an executable file. -### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/HNID8W2jgRM). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. +### Check out the Youtube Tutorial for this [Go Program](https://youtu.be/O9rDas-0s2c). Here is our [Youtube Channel](https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg) Subscribe for more content. ### Check out our blog at [tensor-programming.com](http://tensor-programming.com/). From 6317d41fd1ae26922a0379a868c3baa11cabf670 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Tue, 6 Nov 2018 01:55:58 -0500 Subject: [PATCH 13/26] part_6 --- blockchain/block.go | 2 +- blockchain/blockchain.go | 72 ++++++++++++++--- blockchain/transaction.go | 161 ++++++++++++++++++++++++++++++++++++-- blockchain/tx.go | 40 +++++++--- cli/cli.go | 21 ++++- wallet/wallet.go | 11 +++ 6 files changed, 278 insertions(+), 29 deletions(-) diff --git a/blockchain/block.go b/blockchain/block.go index 1e7bd95b..5117c1b5 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -19,7 +19,7 @@ func (b *Block) HashTransactions() []byte { var txHash [32]byte for _, tx := range b.Transactions { - txHashes = append(txHashes, tx.ID) + txHashes = append(txHashes, tx.Hash()) } txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 81261550..a2232d29 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1,8 +1,12 @@ package blockchain import ( + "bytes" + "crypto/ecdsa" "encoding/hex" + "errors" "fmt" + "log" "os" "runtime" @@ -100,6 +104,12 @@ func InitBlockChain(address string) *BlockChain { func (chain *BlockChain) AddBlock(transactions []*Transaction) { var lastHash []byte + for _, tx := range transactions { + if chain.VerifyTransaction(tx) != true { + log.Panic("Invalid Transaction") + } + } + err := chain.Database.View(func(txn *badger.Txn) error { item, err := txn.Get([]byte("lh")) Handle(err) @@ -147,7 +157,7 @@ func (iter *BlockChainIterator) Next() *Block { return block } -func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction { +func (chain *BlockChain) FindUnspentTransactions(pubKeyHash []byte) []Transaction { var unspentTxs []Transaction spentTXOs := make(map[string][]int) @@ -169,13 +179,13 @@ func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction { } } } - if out.CanBeUnlocked(address) { + if out.IsLockedWithKey(pubKeyHash) { unspentTxs = append(unspentTxs, *tx) } } if tx.IsCoinbase() == false { for _, in := range tx.Inputs { - if in.CanUnlock(address) { + if in.UsesKey(pubKeyHash) { inTxID := hex.EncodeToString(in.ID) spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Out) } @@ -190,13 +200,13 @@ func (chain *BlockChain) FindUnspentTransactions(address string) []Transaction { return unspentTxs } -func (chain *BlockChain) FindUTXO(address string) []TxOutput { +func (chain *BlockChain) FindUTXO(pubKeyHash []byte) []TxOutput { var UTXOs []TxOutput - unspentTransactions := chain.FindUnspentTransactions(address) + unspentTransactions := chain.FindUnspentTransactions(pubKeyHash) for _, tx := range unspentTransactions { for _, out := range tx.Outputs { - if out.CanBeUnlocked(address) { + if out.IsLockedWithKey(pubKeyHash) { UTXOs = append(UTXOs, out) } } @@ -204,9 +214,9 @@ func (chain *BlockChain) FindUTXO(address string) []TxOutput { return UTXOs } -func (chain *BlockChain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { +func (chain *BlockChain) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { unspentOuts := make(map[string][]int) - unspentTxs := chain.FindUnspentTransactions(address) + unspentTxs := chain.FindUnspentTransactions(pubKeyHash) accumulated := 0 Work: @@ -214,7 +224,7 @@ Work: txID := hex.EncodeToString(tx.ID) for outIdx, out := range tx.Outputs { - if out.CanBeUnlocked(address) && accumulated < amount { + if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { accumulated += out.Value unspentOuts[txID] = append(unspentOuts[txID], outIdx) @@ -227,3 +237,47 @@ Work: return accumulated, unspentOuts } + +func (bc *BlockChain) FindTransaction(ID []byte) (Transaction, error) { + iter := bc.Iterator() + + for { + block := iter.Next() + + for _, tx := range block.Transactions { + if bytes.Compare(tx.ID, ID) == 0 { + return *tx, nil + } + } + + if len(block.PrevHash) == 0 { + break + } + } + + return Transaction{}, errors.New("Transaction does not exist") +} + +func (bc *BlockChain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) { + prevTXs := make(map[string]Transaction) + + for _, in := range tx.Inputs { + prevTX, err := bc.FindTransaction(in.ID) + Handle(err) + prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX + } + + tx.Sign(privKey, prevTXs) +} + +func (bc *BlockChain) VerifyTransaction(tx *Transaction) bool { + prevTXs := make(map[string]Transaction) + + for _, in := range tx.Inputs { + prevTX, err := bc.FindTransaction(in.ID) + Handle(err) + prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX + } + + return tx.Verify(prevTXs) +} diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 8ff3e89b..6e350e10 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -2,11 +2,18 @@ package blockchain import ( "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "crypto/sha256" "encoding/gob" "encoding/hex" "fmt" "log" + "math/big" + "strings" + + "github.com/tensor-programming/golang-blockchain/wallet" ) type Transaction struct { @@ -15,6 +22,29 @@ type Transaction struct { Outputs []TxOutput } +func (tx *Transaction) Hash() []byte { + var hash [32]byte + + txCopy := *tx + txCopy.ID = []byte{} + + hash = sha256.Sum256(txCopy.Serialize()) + + return hash[:] +} + +func (tx Transaction) Serialize() []byte { + var encoded bytes.Buffer + + enc := gob.NewEncoder(&encoded) + err := enc.Encode(tx) + if err != nil { + log.Panic(err) + } + + return encoded.Bytes() +} + func (tx *Transaction) SetID() { var encoded bytes.Buffer var hash [32]byte @@ -32,10 +62,10 @@ func CoinbaseTx(to, data string) *Transaction { data = fmt.Sprintf("Coins to %s", to) } - txin := TxInput{[]byte{}, -1, data} - txout := TxOutput{100, to} + txin := TxInput{[]byte{}, -1, nil, []byte(data)} + txout := NewTXOutput(100, to) - tx := Transaction{nil, []TxInput{txin}, []TxOutput{txout}} + tx := Transaction{nil, []TxInput{txin}, []TxOutput{*txout}} tx.SetID() return &tx @@ -45,7 +75,11 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction var inputs []TxInput var outputs []TxOutput - acc, validOutputs := chain.FindSpendableOutputs(from, amount) + wallets, err := wallet.CreateWallets() + Handle(err) + w := wallets.GetWallet(from) + pubKeyHash := wallet.PublicKeyHash(w.PublicKey) + acc, validOutputs := chain.FindSpendableOutputs(pubKeyHash, amount) if acc < amount { log.Panic("Error: not enough funds") @@ -56,19 +90,20 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction Handle(err) for _, out := range outs { - input := TxInput{txID, out, from} + input := TxInput{txID, out, nil, w.PublicKey} inputs = append(inputs, input) } } - outputs = append(outputs, TxOutput{amount, to}) + outputs = append(outputs, *NewTXOutput(amount, to)) if acc > amount { - outputs = append(outputs, TxOutput{acc - amount, from}) + outputs = append(outputs, *NewTXOutput(acc-amount, from)) } tx := Transaction{nil, inputs, outputs} - tx.SetID() + tx.ID = tx.Hash() + chain.SignTransaction(&tx, w.PrivateKey) return &tx } @@ -76,3 +111,113 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction func (tx *Transaction) IsCoinbase() bool { return len(tx.Inputs) == 1 && len(tx.Inputs[0].ID) == 0 && tx.Inputs[0].Out == -1 } + +func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) { + if tx.IsCoinbase() { + return + } + + for _, in := range tx.Inputs { + if prevTXs[hex.EncodeToString(in.ID)].ID == nil { + log.Panic("ERROR: Previous transaction is not correct") + } + } + + txCopy := tx.TrimmedCopy() + + for inId, in := range txCopy.Inputs { + prevTX := prevTXs[hex.EncodeToString(in.ID)] + txCopy.Inputs[inId].Signature = nil + txCopy.Inputs[inId].PubKey = prevTX.Outputs[in.Out].PubKeyHash + txCopy.ID = txCopy.Hash() + txCopy.Inputs[inId].PubKey = nil + + r, s, err := ecdsa.Sign(rand.Reader, &privKey, txCopy.ID) + Handle(err) + signature := append(r.Bytes(), s.Bytes()...) + + tx.Inputs[inId].Signature = signature + + } +} + +func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { + if tx.IsCoinbase() { + return true + } + + for _, in := range tx.Inputs { + if prevTXs[hex.EncodeToString(in.ID)].ID == nil { + log.Panic("Previous transaction not correct") + } + } + + txCopy := tx.TrimmedCopy() + curve := elliptic.P256() + + for inId, in := range tx.Inputs { + prevTx := prevTXs[hex.EncodeToString(in.ID)] + txCopy.Inputs[inId].Signature = nil + txCopy.Inputs[inId].PubKey = prevTx.Outputs[in.Out].PubKeyHash + txCopy.ID = txCopy.Hash() + txCopy.Inputs[inId].PubKey = nil + + r := big.Int{} + s := big.Int{} + + sigLen := len(in.Signature) + r.SetBytes(in.Signature[:(sigLen / 2)]) + s.SetBytes(in.Signature[(sigLen / 2):]) + + x := big.Int{} + y := big.Int{} + keyLen := len(in.PubKey) + x.SetBytes(in.PubKey[:(keyLen / 2)]) + y.SetBytes(in.PubKey[(keyLen / 2):]) + + rawPubKey := ecdsa.PublicKey{curve, &x, &y} + if ecdsa.Verify(&rawPubKey, txCopy.ID, &r, &s) == false { + return false + } + } + + return true +} + +func (tx *Transaction) TrimmedCopy() Transaction { + var inputs []TxInput + var outputs []TxOutput + + for _, in := range tx.Inputs { + inputs = append(inputs, TxInput{in.ID, in.Out, nil, nil}) + } + + for _, out := range tx.Outputs { + outputs = append(outputs, TxOutput{out.Value, out.PubKeyHash}) + } + + txCopy := Transaction{tx.ID, inputs, outputs} + + return txCopy +} + +func (tx Transaction) String() string { + var lines []string + + lines = append(lines, fmt.Sprintf("--- Transaction %x:", tx.ID)) + for i, input := range tx.Inputs { + lines = append(lines, fmt.Sprintf(" Input %d:", i)) + lines = append(lines, fmt.Sprintf(" TXID: %x", input.ID)) + lines = append(lines, fmt.Sprintf(" Out: %d", input.Out)) + lines = append(lines, fmt.Sprintf(" Signature: %x", input.Signature)) + lines = append(lines, fmt.Sprintf(" PubKey: %x", input.PubKey)) + } + + for i, output := range tx.Outputs { + lines = append(lines, fmt.Sprintf(" Output %d:", i)) + lines = append(lines, fmt.Sprintf(" Value: %d", output.Value)) + lines = append(lines, fmt.Sprintf(" Script: %x", output.PubKeyHash)) + } + + return strings.Join(lines, "\n") +} diff --git a/blockchain/tx.go b/blockchain/tx.go index b1c62757..d7005b84 100644 --- a/blockchain/tx.go +++ b/blockchain/tx.go @@ -1,20 +1,42 @@ package blockchain +import ( + "bytes" + + "github.com/tensor-programming/golang-blockchain/wallet" +) + type TxOutput struct { - Value int - PubKey string + Value int + PubKeyHash []byte } type TxInput struct { - ID []byte - Out int - Sig string + ID []byte + Out int + Signature []byte + PubKey []byte +} + +func (in *TxInput) UsesKey(pubKeyHash []byte) bool { + lockingHash := wallet.PublicKeyHash(in.PubKey) + + return bytes.Compare(lockingHash, pubKeyHash) == 0 +} + +func (out *TxOutput) Lock(address []byte) { + pubKeyHash := wallet.Base58Decode(address) + pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4] + out.PubKeyHash = pubKeyHash } -func (in *TxInput) CanUnlock(data string) bool { - return in.Sig == data +func (out *TxOutput) IsLockedWithKey(pubKeyHash []byte) bool { + return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0 } -func (out *TxOutput) CanBeUnlocked(data string) bool { - return out.PubKey == data +func NewTXOutput(value int, address string) *TxOutput { + txo := &TxOutput{value, nil} + txo.Lock([]byte(address)) + + return txo } diff --git a/cli/cli.go b/cli/cli.go index d51081db..f9b8cb8b 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -56,10 +56,13 @@ func (cli *CommandLine) printChain() { for { block := iter.Next() - fmt.Printf("Prev. hash: %x\n", block.PrevHash) fmt.Printf("Hash: %x\n", block.Hash) + fmt.Printf("Prev. hash: %x\n", block.PrevHash) pow := blockchain.NewProof(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) + for _, tx := range block.Transactions { + fmt.Println(tx) + } fmt.Println() if len(block.PrevHash) == 0 { @@ -69,17 +72,25 @@ func (cli *CommandLine) printChain() { } func (cli *CommandLine) createBlockChain(address string) { + if !wallet.ValidateAddress(address) { + log.Panic("Address is not Valid") + } chain := blockchain.InitBlockChain(address) chain.Database.Close() fmt.Println("Finished!") } func (cli *CommandLine) getBalance(address string) { + if !wallet.ValidateAddress(address) { + log.Panic("Address is not Valid") + } chain := blockchain.ContinueBlockChain(address) defer chain.Database.Close() balance := 0 - UTXOs := chain.FindUTXO(address) + pubKeyHash := wallet.Base58Decode([]byte(address)) + pubKeyHash = pubKeyHash[1 : len(pubKeyHash) - 4] + UTXOs := chain.FindUTXO(pubKeyHash) for _, out := range UTXOs { balance += out.Value @@ -89,6 +100,12 @@ func (cli *CommandLine) getBalance(address string) { } func (cli *CommandLine) send(from, to string, amount int) { + if !wallet.ValidateAddress(to) { + log.Panic("Address is not Valid") + } + if !wallet.ValidateAddress(from) { + log.Panic("Address is not Valid") + } chain := blockchain.ContinueBlockChain(from) defer chain.Database.Close() diff --git a/wallet/wallet.go b/wallet/wallet.go index a59df1af..c51888f7 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -1,6 +1,7 @@ package wallet import ( + "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -71,3 +72,13 @@ func Checksum(payload []byte) []byte { return secondHash[:checksumLength] } + +func ValidateAddress(address string) bool { + pubKeyHash := Base58Decode([]byte(address)) + actualChecksum := pubKeyHash[len(pubKeyHash)-checksumLength:] + version := pubKeyHash[0] + pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-checksumLength] + targetChecksum := Checksum(append([]byte{version}, pubKeyHash...)) + + return bytes.Compare(actualChecksum, targetChecksum) == 0 +} From cf71ad9b0cc5c873ed6960eeff4eea345b3285f5 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 12 Nov 2018 17:53:46 -0500 Subject: [PATCH 14/26] commit --- blockchain/blockchain.go | 62 +++-------- blockchain/transaction.go | 6 +- blockchain/tx.go | 21 ++++ blockchain/utxo.go | 219 ++++++++++++++++++++++++++++++++++++++ cli/cli.go | 34 +++++- utxo.png | Bin 0 -> 17369 bytes wallet.mmd | 30 +++--- 7 files changed, 300 insertions(+), 72 deletions(-) create mode 100644 blockchain/utxo.go create mode 100644 utxo.png diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index a2232d29..c7692fd8 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -101,7 +101,7 @@ func InitBlockChain(address string) *BlockChain { return &blockchain } -func (chain *BlockChain) AddBlock(transactions []*Transaction) { +func (chain *BlockChain) AddBlock(transactions []*Transaction) *Block { var lastHash []byte for _, tx := range transactions { @@ -131,6 +131,8 @@ func (chain *BlockChain) AddBlock(transactions []*Transaction) { return err }) Handle(err) + + return newBlock } func (chain *BlockChain) Iterator() *BlockChainIterator { @@ -157,9 +159,8 @@ func (iter *BlockChainIterator) Next() *Block { return block } -func (chain *BlockChain) FindUnspentTransactions(pubKeyHash []byte) []Transaction { - var unspentTxs []Transaction - +func (chain *BlockChain) FindUTXO() map[string]TxOutputs { + UTXO := make(map[string]TxOutputs) spentTXOs := make(map[string][]int) iter := chain.Iterator() @@ -179,16 +180,14 @@ func (chain *BlockChain) FindUnspentTransactions(pubKeyHash []byte) []Transactio } } } - if out.IsLockedWithKey(pubKeyHash) { - unspentTxs = append(unspentTxs, *tx) - } + outs := UTXO[txID] + outs.Outputs = append(outs.Outputs, out) + UTXO[txID] = outs } if tx.IsCoinbase() == false { for _, in := range tx.Inputs { - if in.UsesKey(pubKeyHash) { - inTxID := hex.EncodeToString(in.ID) - spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Out) - } + inTxID := hex.EncodeToString(in.ID) + spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Out) } } } @@ -197,45 +196,7 @@ func (chain *BlockChain) FindUnspentTransactions(pubKeyHash []byte) []Transactio break } } - return unspentTxs -} - -func (chain *BlockChain) FindUTXO(pubKeyHash []byte) []TxOutput { - var UTXOs []TxOutput - unspentTransactions := chain.FindUnspentTransactions(pubKeyHash) - - for _, tx := range unspentTransactions { - for _, out := range tx.Outputs { - if out.IsLockedWithKey(pubKeyHash) { - UTXOs = append(UTXOs, out) - } - } - } - return UTXOs -} - -func (chain *BlockChain) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { - unspentOuts := make(map[string][]int) - unspentTxs := chain.FindUnspentTransactions(pubKeyHash) - accumulated := 0 - -Work: - for _, tx := range unspentTxs { - txID := hex.EncodeToString(tx.ID) - - for outIdx, out := range tx.Outputs { - if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { - accumulated += out.Value - unspentOuts[txID] = append(unspentOuts[txID], outIdx) - - if accumulated >= amount { - break Work - } - } - } - } - - return accumulated, unspentOuts + return UTXO } func (bc *BlockChain) FindTransaction(ID []byte) (Transaction, error) { @@ -271,6 +232,7 @@ func (bc *BlockChain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) } func (bc *BlockChain) VerifyTransaction(tx *Transaction) bool { + prevTXs := make(map[string]Transaction) for _, in := range tx.Inputs { diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 6e350e10..30b7fd21 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -71,7 +71,7 @@ func CoinbaseTx(to, data string) *Transaction { return &tx } -func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction { +func NewTransaction(from, to string, amount int, UTXO *UTXOSet) *Transaction { var inputs []TxInput var outputs []TxOutput @@ -79,7 +79,7 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction Handle(err) w := wallets.GetWallet(from) pubKeyHash := wallet.PublicKeyHash(w.PublicKey) - acc, validOutputs := chain.FindSpendableOutputs(pubKeyHash, amount) + acc, validOutputs := UTXO.FindSpendableOutputs(pubKeyHash, amount) if acc < amount { log.Panic("Error: not enough funds") @@ -103,7 +103,7 @@ func NewTransaction(from, to string, amount int, chain *BlockChain) *Transaction tx := Transaction{nil, inputs, outputs} tx.ID = tx.Hash() - chain.SignTransaction(&tx, w.PrivateKey) + UTXO.Blockchain.SignTransaction(&tx, w.PrivateKey) return &tx } diff --git a/blockchain/tx.go b/blockchain/tx.go index d7005b84..39661db8 100644 --- a/blockchain/tx.go +++ b/blockchain/tx.go @@ -2,6 +2,7 @@ package blockchain import ( "bytes" + "encoding/gob" "github.com/tensor-programming/golang-blockchain/wallet" ) @@ -11,6 +12,10 @@ type TxOutput struct { PubKeyHash []byte } +type TxOutputs struct { + Outputs []TxOutput +} + type TxInput struct { ID []byte Out int @@ -40,3 +45,19 @@ func NewTXOutput(value int, address string) *TxOutput { return txo } + +func (outs TxOutputs) Serialize() []byte { + var buffer bytes.Buffer + encode := gob.NewEncoder(&buffer) + err := encode.Encode(outs) + Handle(err) + return buffer.Bytes() +} + +func DeserializeOutputs(data []byte) TxOutputs { + var outputs TxOutputs + decode := gob.NewDecoder(bytes.NewReader(data)) + err := decode.Decode(&outputs) + Handle(err) + return outputs +} diff --git a/blockchain/utxo.go b/blockchain/utxo.go new file mode 100644 index 00000000..571bb729 --- /dev/null +++ b/blockchain/utxo.go @@ -0,0 +1,219 @@ +package blockchain + +import ( + "bytes" + "encoding/hex" + "log" + + "github.com/dgraph-io/badger" +) + +var ( + utxoPrefix = []byte("utxo-") + prefixLength = len(utxoPrefix) +) + +type UTXOSet struct { + Blockchain *BlockChain +} + +func (u UTXOSet) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { + unspentOuts := make(map[string][]int) + accumulated := 0 + db := u.Blockchain.Database + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + item := it.Item() + k := item.Key() + v, err := item.Value() + Handle(err) + k = bytes.TrimPrefix(k, utxoPrefix) + txID := hex.EncodeToString(k) + outs := DeserializeOutputs(v) + + for outIdx, out := range outs.Outputs { + if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { + accumulated += out.Value + unspentOuts[txID] = append(unspentOuts[txID], outIdx) + } + } + } + return nil + }) + Handle(err) + + return accumulated, unspentOuts +} + +func (u UTXOSet) FindUnspentTransactions(pubKeyHash []byte) []TxOutput { + var UTXOs []TxOutput + + db := u.Blockchain.Database + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + item := it.Item() + v, err := item.Value() + Handle(err) + outs := DeserializeOutputs(v) + for _, out := range outs.Outputs { + if out.IsLockedWithKey(pubKeyHash) { + UTXOs = append(UTXOs, out) + } + } + + } + return nil + }) + Handle(err) + + return UTXOs +} + +func (u UTXOSet) CountTransactions() int { + db := u.Blockchain.Database + counter := 0 + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + counter++ + } + + return nil + }) + + Handle(err) + + return counter +} + +func (u UTXOSet) Reindex() { + db := u.Blockchain.Database + + u.DeleteByPrefix(utxoPrefix) + + UTXO := u.Blockchain.FindUTXO() + + err := db.Update(func(txn *badger.Txn) error { + for txId, outs := range UTXO { + key, err := hex.DecodeString(txId) + Handle(err) + key = append(utxoPrefix, key...) + + err = txn.Set(key, outs.Serialize()) + Handle(err) + } + + return nil + }) + Handle(err) +} + +func (u *UTXOSet) Update(block *Block) { + db := u.Blockchain.Database + + err := db.Update(func(txn *badger.Txn) error { + for _, tx := range block.Transactions { + if tx.IsCoinbase() == false { + for _, in := range tx.Inputs { + updatedOuts := TxOutputs{} + inID := append(utxoPrefix, in.ID...) + item, err := txn.Get(inID) + Handle(err) + v, err := item.Value() + Handle(err) + + outs := DeserializeOutputs(v) + + for outIdx, out := range outs.Outputs { + if outIdx != in.Out { + updatedOuts.Outputs = append(updatedOuts.Outputs, out) + } + } + + if len(updatedOuts.Outputs) == 0 { + if err := txn.Delete(inID); err != nil { + log.Panic(err) + } + } else { + if err := txn.Set(inID, updatedOuts.Serialize()); err != nil { + log.Panic(err) + } + } + } + } + newOutputs := TxOutputs{} + for _, out := range tx.Outputs { + newOutputs.Outputs = append(newOutputs.Outputs, out) + } + + txID := append(utxoPrefix, tx.ID...) + if err := txn.Set(txID, newOutputs.Serialize()); err != nil { + log.Panic(err) + } + } + + return nil + }) + Handle(err) +} + +func (u *UTXOSet) DeleteByPrefix(prefix []byte) { + deleteKeys := func(keysForDelete [][]byte) error { + if err := u.Blockchain.Database.Update(func(txn *badger.Txn) error { + for _, key := range keysForDelete { + if err := txn.Delete(key); err != nil { + return err + } + } + return nil + }); err != nil { + return err + } + return nil + } + + collectSize := 100000 + u.Blockchain.Database.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchValues = false + it := txn.NewIterator(opts) + defer it.Close() + + keysForDelete := make([][]byte, 0, collectSize) + keysCollected := 0 + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + key := it.Item().KeyCopy(nil) + keysForDelete = append(keysForDelete, key) + keysCollected++ + if keysCollected == collectSize { + if err := deleteKeys(keysForDelete); err != nil { + log.Panic(err) + } + keysForDelete = make([][]byte, 0, collectSize) + keysCollected = 0 + } + } + if keysCollected > 0 { + if err := deleteKeys(keysForDelete); err != nil { + log.Panic(err) + } + } + return nil + }) +} diff --git a/cli/cli.go b/cli/cli.go index f9b8cb8b..830815a0 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -22,6 +22,7 @@ func (cli *CommandLine) printUsage() { fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send amount of coins") fmt.Println(" createwallet - Creates a new Wallet") fmt.Println(" listaddresses - Lists the addresses in our wallet file") + fmt.Println(" reindexutxo - Rebuilds the UTXO set") } func (cli *CommandLine) validateArgs() { @@ -31,6 +32,16 @@ func (cli *CommandLine) validateArgs() { } } +func (cli *CommandLine) reindexUTXO() { + chain := blockchain.ContinueBlockChain("") + defer chain.Database.Close() + UTXOSet := blockchain.UTXOSet{chain} + UTXOSet.Reindex() + + count := UTXOSet.CountTransactions() + fmt.Printf("Done! There are %d transactions in the UTXO set.\n", count) +} + func (cli *CommandLine) listAddresses() { wallets, _ := wallet.CreateWallets() addresses := wallets.GetAllAddresses() @@ -77,6 +88,10 @@ func (cli *CommandLine) createBlockChain(address string) { } chain := blockchain.InitBlockChain(address) chain.Database.Close() + + UTXOSet := blockchain.UTXOSet{chain} + UTXOSet.Reindex() + fmt.Println("Finished!") } @@ -85,12 +100,13 @@ func (cli *CommandLine) getBalance(address string) { log.Panic("Address is not Valid") } chain := blockchain.ContinueBlockChain(address) + UTXOSet := blockchain.UTXOSet{chain} defer chain.Database.Close() balance := 0 pubKeyHash := wallet.Base58Decode([]byte(address)) pubKeyHash = pubKeyHash[1 : len(pubKeyHash) - 4] - UTXOs := chain.FindUTXO(pubKeyHash) + UTXOs := UTXOSet.FindUnspentTransactions(pubKeyHash) for _, out := range UTXOs { balance += out.Value @@ -107,10 +123,12 @@ func (cli *CommandLine) send(from, to string, amount int) { log.Panic("Address is not Valid") } chain := blockchain.ContinueBlockChain(from) + UTXOSet := blockchain.UTXOSet{chain} defer chain.Database.Close() - tx := blockchain.NewTransaction(from, to, amount, chain) - chain.AddBlock([]*blockchain.Transaction{tx}) + tx := blockchain.NewTransaction(from, to, amount, &UTXOSet) + block := chain.AddBlock([]*blockchain.Transaction{tx}) + UTXOSet.Update(block) fmt.Println("Success!") } @@ -123,7 +141,7 @@ func (cli *CommandLine) Run() { printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError) listAddressesCmd := flag.NewFlagSet("listaddresses", flag.ExitOnError) - + reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError) getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") @@ -132,6 +150,11 @@ func (cli *CommandLine) Run() { sendAmount := sendCmd.Int("amount", 0, "Amount to send") switch os.Args[1] { + case "reindexutxo": + err := reindexUTXOCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } case "getbalance": err := getBalanceCmd.Parse(os.Args[2:]) if err != nil { @@ -193,6 +216,9 @@ func (cli *CommandLine) Run() { if listAddressesCmd.Parsed() { cli.listAddresses() } + if reindexUTXOCmd.Parsed() { + cli.reindexUTXO() + } if sendCmd.Parsed() { if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 { diff --git a/utxo.png b/utxo.png new file mode 100644 index 0000000000000000000000000000000000000000..a0ba217ab6dcef620382ccfc8db8e410f8ef721f GIT binary patch literal 17369 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 literal 0 HcmV?d00001 diff --git a/wallet.mmd b/wallet.mmd index 23605ef6..7e01d2b0 100644 --- a/wallet.mmd +++ b/wallet.mmd @@ -1,16 +1,16 @@ -graph TB -pr(private key) --> ec(ecdsa) -ec --> pb(public key) +graph TD +subgraph Genesis +i1(Input) --> o1(Output) +end -pb --> s(sha256) -s --> r(ripemd160) -r --> k(public key hash) -k --> s1(sha256) -s1 --> s2(sha256) -s2 --> e(4 bytes) -e --> c -c(checksum) -c --> b(base 58) -k --> b -v(version) --> b -b --> a(address) +subgraph Block A +i2(Input) --> o2(Output) +o1 --> i2 +i2 --> o3(Unspent Output) +end + +subgraph Block B +i3(Input) --> o4(Unspent Output) +o2 --> i3 +i3 --> o5(Unspent Output) +end \ No newline at end of file From a2ec58de2678fefd6576a43ecafc9e382f0bc9ce Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Mon, 12 Nov 2018 19:58:33 -0500 Subject: [PATCH 15/26] part_8 --- blockchain/block.go | 8 ++---- blockchain/blockchain.go | 4 ++- blockchain/merkle.go | 59 ++++++++++++++++++++++++++++++++++++++ blockchain/transaction.go | 21 ++++---------- cli/cli.go | 5 ++-- go.mod | 4 +++ go.sum | 8 ++++++ utxo.png | Bin 17369 -> 0 bytes wallet.mmd | 28 +++++++++++------- 9 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 blockchain/merkle.go delete mode 100644 utxo.png diff --git a/blockchain/block.go b/blockchain/block.go index 5117c1b5..3dd836ba 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -2,7 +2,6 @@ package blockchain import ( "bytes" - "crypto/sha256" "encoding/gob" "log" ) @@ -16,14 +15,13 @@ type Block struct { func (b *Block) HashTransactions() []byte { var txHashes [][]byte - var txHash [32]byte for _, tx := range b.Transactions { - txHashes = append(txHashes, tx.Hash()) + txHashes = append(txHashes, tx.Serialize()) } - txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) + tree := NewMerkleTree(txHashes) - return txHash[:] + return tree.RootNode.Data } func CreateBlock(txs []*Transaction, prevHash []byte) *Block { diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index c7692fd8..11b9a97d 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -232,7 +232,9 @@ func (bc *BlockChain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) } func (bc *BlockChain) VerifyTransaction(tx *Transaction) bool { - + if tx.IsCoinbase() { + return true + } prevTXs := make(map[string]Transaction) for _, in := range tx.Inputs { diff --git a/blockchain/merkle.go b/blockchain/merkle.go new file mode 100644 index 00000000..33c11150 --- /dev/null +++ b/blockchain/merkle.go @@ -0,0 +1,59 @@ +package blockchain + +import "crypto/sha256" + +type MerkleTree struct { + RootNode *MerkleNode +} + +type MerkleNode struct { + Left *MerkleNode + Right *MerkleNode + Data []byte +} + +func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { + node := MerkleNode{} + + if left == nil && right == nil { + hash := sha256.Sum256(data) + node.Data = hash[:] + } else { + prevHashes := append(left.Data, right.Data...) + hash := sha256.Sum256(prevHashes) + node.Data = hash[:] + } + + node.Left = left + node.Right = right + + return &node +} + +func NewMerkleTree(data [][]byte) *MerkleTree { + var nodes []MerkleNode + + if len(data)%2 != 0 { + data = append(data, data[len(data)-1]) + } + + for _, dat := range data { + node := NewMerkleNode(nil, nil, dat) + nodes = append(nodes, *node) + } + + for i := 0; i < len(data)/2; i++ { + var level []MerkleNode + + for j := 0; j < len(nodes); j += 2 { + node := NewMerkleNode(&nodes[j], &nodes[j+1], nil) + level = append(level, *node) + } + + nodes = level + } + + tree := MerkleTree{&nodes[0]} + + return &tree +} diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 30b7fd21..31de7a61 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -45,28 +45,19 @@ func (tx Transaction) Serialize() []byte { return encoded.Bytes() } -func (tx *Transaction) SetID() { - var encoded bytes.Buffer - var hash [32]byte - - encode := gob.NewEncoder(&encoded) - err := encode.Encode(tx) - Handle(err) - - hash = sha256.Sum256(encoded.Bytes()) - tx.ID = hash[:] -} - func CoinbaseTx(to, data string) *Transaction { if data == "" { - data = fmt.Sprintf("Coins to %s", to) + randData := make([]byte, 24) + _, err := rand.Read(randData) + Handle(err) + data = fmt.Sprintf("%x", randData) } txin := TxInput{[]byte{}, -1, nil, []byte(data)} - txout := NewTXOutput(100, to) + txout := NewTXOutput(20, to) tx := Transaction{nil, []TxInput{txin}, []TxOutput{*txout}} - tx.SetID() + tx.ID = tx.Hash() return &tx } diff --git a/cli/cli.go b/cli/cli.go index 830815a0..acf470ba 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -87,7 +87,7 @@ func (cli *CommandLine) createBlockChain(address string) { log.Panic("Address is not Valid") } chain := blockchain.InitBlockChain(address) - chain.Database.Close() + defer chain.Database.Close() UTXOSet := blockchain.UTXOSet{chain} UTXOSet.Reindex() @@ -127,7 +127,8 @@ func (cli *CommandLine) send(from, to string, amount int) { defer chain.Database.Close() tx := blockchain.NewTransaction(from, to, amount, &UTXOSet) - block := chain.AddBlock([]*blockchain.Transaction{tx}) + cbTx := blockchain.CoinbaseTx(from, "") + block := chain.AddBlock([]*blockchain.Transaction{cbTx, tx}) UTXOSet.Update(block) fmt.Println("Success!") } diff --git a/go.mod b/go.mod index 076e1c93..db0e9296 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,15 @@ module github.com/tensor-programming/golang-blockchain require ( github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/badger v1.5.4 github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect github.com/golang/protobuf v1.2.0 // indirect github.com/mr-tron/base58 v1.1.0 github.com/pkg/errors v0.8.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.1.1 // indirect + github.com/stretchr/testify v1.2.2 golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect ) diff --git a/go.sum b/go.sum index bb88cfd7..6cad4822 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0= @@ -10,6 +12,12 @@ github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= diff --git a/utxo.png b/utxo.png deleted file mode 100644 index a0ba217ab6dcef620382ccfc8db8e410f8ef721f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17369 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 diff --git a/wallet.mmd b/wallet.mmd index 7e01d2b0..c42e9cec 100644 --- a/wallet.mmd +++ b/wallet.mmd @@ -1,16 +1,22 @@ graph TD -subgraph Genesis -i1(Input) --> o1(Output) -end -subgraph Block A -i2(Input) --> o2(Output) -o1 --> i2 -i2 --> o3(Unspent Output) +subgraph Merkle Tree +a(Merkle Root) --> b +a --> c(Sha256 Branch A + B) +c --> d(Branch A: sha256 tx1) +c --> e(Branch B: sha256 tx2) +b(Sha256 Branch C + D) --> f(Branch C: sha256 tx3) +b --> g(Branch D: sha256 tx4) + end +d --> tx1 +e --> tx2 +f --> tx3 +g --> tx4 -subgraph Block B -i3(Input) --> o4(Unspent Output) -o2 --> i3 -i3 --> o5(Unspent Output) +subgraph Serialized Transactions +tx1(Transaction One) +tx2(Transaction Two) +tx3(Transaction Three) +tx4(Transaction Four) end \ No newline at end of file From 4e3ae45a54358c3d8bc5eabce75bcca279266312 Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Thu, 15 Nov 2018 16:17:11 -0500 Subject: [PATCH 16/26] part_9 --- network/network.go | 495 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 network/network.go diff --git a/network/network.go b/network/network.go new file mode 100644 index 00000000..e0c97f1e --- /dev/null +++ b/network/network.go @@ -0,0 +1,495 @@ +package network + +import ( + "bytes" + "encoding/gob" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "syscall" + "runtime" + "os" + + "gopkg.in/vrecan/death.v3" + + "github.com/tensor-programming/golang-blockchain/blockchain" +) + +const ( + protocol = "tcp" + version = 1 + commandLength = 12 +) + +var ( + nodeAddress string + mineAddress string + KnownNodes = []string{"localhost:3000"} + blocksInTransit = [][]byte{} + memoryPool = make(map[string]blockchain.Transaction) +) + +type Addr struct { + AddrList []string +} + +type Block struct { + AddrFrom string + Block []byte +} + +type GetBlocks struct { + AddrFrom string +} + +type GetData struct { + AddrFrom string + Type string + ID []byte +} + +type Inv struct { + AddrFrom string + Type string + Items [][]byte +} + +type Tx struct { + AddrFrom string + Transaction []byte +} + +type Version struct { + Version int + BestHeight int + AddrFrom string +} + +func CmdToBytes(cmd string) []byte { + var bytes [commandLength]byte + + for i, c := range cmd { + bytes[i] = byte(c) + } + + return bytes[:] +} + +func BytesToCmd(bytes []byte) string { + var cmd []byte + + for _, b := range bytes { + if b != 0x0 { + cmd = append(cmd, b) + } + } + + return fmt.Sprintf("%s", cmd) +} + +func ExtractCmd(request []byte) []byte { + return request[:commandLength] +} + +func RequestBlocks() { + for _, node := range KnownNodes { + SendGetBlocks(node) + } +} + +func SendAddr(address string) { + nodes := Addr{KnownNodes} + nodes.AddrList = append(nodes.AddrList, nodeAddress) + payload := GobEncode(nodes) + request := append(CmdToBytes("addr"), payload...) + + SendData(address, request) +} + +func SendBlock(addr string, b *blockchain.Block) { + data := Block{nodeAddress, b.Serialize()} + payload := GobEncode(data) + request := append(CmdToBytes("block"), payload...) + + SendData(addr, request) +} + +func SendData(addr string, data []byte) { + conn, err := net.Dial(protocol, addr) + + if err != nil { + fmt.Printf("%s is not available\n", addr) + var updatedNodes []string + + for _, node := range KnownNodes { + if node != addr { + updatedNodes = append(updatedNodes, node) + } + } + + KnownNodes = updatedNodes + + return + } + + defer conn.Close() + + _, err = io.Copy(conn, bytes.NewReader(data)) + if err != nil { + log.Panic(err) + } +} + +func SendInv(address, kind string, items [][]byte) { + inventory := Inv{nodeAddress, kind, items} + payload := GobEncode(inventory) + request := append(CmdToBytes("inv"), payload...) + + SendData(address, request) +} + +func SendGetBlocks(address string) { + payload := GobEncode(GetBlocks{nodeAddress}) + request := append(CmdToBytes("getblocks"), payload...) + + SendData(address, request) +} + +func SendGetData(address, kind string, id []byte) { + payload := GobEncode(GetData{nodeAddress, kind, id}) + request := append(CmdToBytes("getdata"), payload...) + + SendData(address, request) +} + +func SendTx(addr string, tnx *blockchain.Transaction) { + data := Tx{nodeAddress, tnx.Serialize()} + payload := GobEncode(data) + request := append(CmdToBytes("tx"), payload...) + + SendData(addr, request) +} + +func SendVersion(addr string, chain *blockchain.BlockChain) { + bestHeight := chain.GetBestHeight() + payload := GobEncode(Version{version, bestHeight, nodeAddress}) + + request := append(CmdToBytes("version"), payload...) + + SendData(addr, request) +} + +func HandleAddr(request []byte) { + var buff bytes.Buffer + var payload Addr + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + + } + + KnownNodes = append(KnownNodes, payload.AddrList...) + fmt.Printf("there are %d known nodes\n", len(KnownNodes)) + RequestBlocks() +} + +func HandleBlock(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload Block + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + blockData := payload.Block + block := blockchain.Deserialize(blockData) + + fmt.Println("Recevied a new block!") + chain.AddBlock(block) + + fmt.Printf("Added block %x\n", block.Hash) + + if len(blocksInTransit) > 0 { + blockHash := blocksInTransit[0] + SendGetData(payload.AddrFrom, "block", blockHash) + + blocksInTransit = blocksInTransit[1:] + } else { + UTXOSet := blockchain.UTXOSet{chain} + UTXOSet.Reindex() + } +} + +func HandleInv(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload Inv + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + fmt.Printf("Recevied inventory with %d %s\n", len(payload.Items), payload.Type) + + if payload.Type == "block" { + blocksInTransit = payload.Items + + blockHash := payload.Items[0] + SendGetData(payload.AddrFrom, "block", blockHash) + + newInTransit := [][]byte{} + for _, b := range blocksInTransit { + if bytes.Compare(b, blockHash) != 0 { + newInTransit = append(newInTransit, b) + } + } + blocksInTransit = newInTransit + } + + if payload.Type == "tx" { + txID := payload.Items[0] + + if memoryPool[hex.EncodeToString(txID)].ID == nil { + SendGetData(payload.AddrFrom, "tx", txID) + } + } +} + +func HandleGetBlocks(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload GetBlocks + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + blocks := chain.GetBlockHashes() + SendInv(payload.AddrFrom, "block", blocks) +} + +func HandleGetData(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload GetData + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + if payload.Type == "block" { + block, err := chain.GetBlock([]byte(payload.ID)) + if err != nil { + return + } + + SendBlock(payload.AddrFrom, &block) + } + + if payload.Type == "tx" { + txID := hex.EncodeToString(payload.ID) + tx := memoryPool[txID] + + SendTx(payload.AddrFrom, &tx) + } +} + +func HandleTx(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload Tx + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + txData := payload.Transaction + tx := blockchain.DeserializeTransaction(txData) + memoryPool[hex.EncodeToString(tx.ID)] = tx + + fmt.Printf("%s, %d", nodeAddress, len(memoryPool)) + + if nodeAddress == KnownNodes[0] { + for _, node := range KnownNodes { + if node != nodeAddress && node != payload.AddrFrom { + SendInv(node, "tx", [][]byte{tx.ID}) + } + } + } else { + if len(memoryPool) >= 2 && len(mineAddress) > 0 { + MineTx(chain) + } + } +} + +func MineTx(chain *blockchain.BlockChain) { + var txs []*blockchain.Transaction + + for id := range memoryPool { + fmt.Printf("tx: %s\n", memoryPool[id].ID) + tx := memoryPool[id] + if chain.VerifyTransaction(&tx) { + txs = append(txs, &tx) + } + } + + if len(txs) == 0 { + fmt.Println("All Transactions are invalid") + return + } + + cbTx := blockchain.CoinbaseTx(mineAddress, "") + txs = append(txs, cbTx) + + newBlock := chain.MineBlock(txs) + UTXOSet := blockchain.UTXOSet{chain} + UTXOSet.Reindex() + + fmt.Println("New Block mined") + + for _, tx := range txs { + txID := hex.EncodeToString(tx.ID) + delete(memoryPool, txID) + } + + for _, node := range KnownNodes { + if node != nodeAddress { + SendInv(node, "block", [][]byte{newBlock.Hash}) + } + } + + if len(memoryPool) > 0 { + MineTx(chain) + } +} + +func HandleVersion(request []byte, chain *blockchain.BlockChain) { + var buff bytes.Buffer + var payload Version + + buff.Write(request[commandLength:]) + dec := gob.NewDecoder(&buff) + err := dec.Decode(&payload) + if err != nil { + log.Panic(err) + } + + bestHeight := chain.GetBestHeight() + otherHeight := payload.BestHeight + + if bestHeight < otherHeight { + SendGetBlocks(payload.AddrFrom) + } else if bestHeight > otherHeight { + SendVersion(payload.AddrFrom, chain) + } + + if !NodeIsKnown(payload.AddrFrom) { + KnownNodes = append(KnownNodes, payload.AddrFrom) + } +} + +func HandleConnection(conn net.Conn, chain *blockchain.BlockChain) { + req, err := ioutil.ReadAll(conn) + defer conn.Close() + + if err != nil { + log.Panic(err) + } + command := BytesToCmd(req[:commandLength]) + fmt.Printf("Received %s command\n", command) + + switch command { + case "addr": + HandleAddr(req) + case "block": + HandleBlock(req, chain) + case "inv": + HandleInv(req, chain) + case "getblocks": + HandleGetBlocks(req, chain) + case "getdata": + HandleGetData(req, chain) + case "tx": + HandleTx(req, chain) + case "version": + HandleVersion(req, chain) + default: + fmt.Println("Unknown command") + } + +} + +func StartServer(nodeID, minerAddress string) { + nodeAddress = fmt.Sprintf("localhost:%s", nodeID) + mineAddress = minerAddress + ln, err := net.Listen(protocol, nodeAddress) + if err != nil { + log.Panic(err) + } + defer ln.Close() + + chain := blockchain.ContinueBlockChain(nodeID) + defer chain.Database.Close() + go CloseDB(chain) + + if nodeAddress != KnownNodes[0] { + SendVersion(KnownNodes[0], chain) + } + for { + conn, err := ln.Accept() + if err != nil { + log.Panic(err) + } + go HandleConnection(conn, chain) + + } +} + +func GobEncode(data interface{}) []byte { + var buff bytes.Buffer + + enc := gob.NewEncoder(&buff) + err := enc.Encode(data) + if err != nil { + log.Panic(err) + } + + return buff.Bytes() +} + +func NodeIsKnown(addr string) bool { + for _, node := range KnownNodes { + if node == addr { + return true + } + } + + return false +} + +func CloseDB(chain *blockchain.BlockChain) { + d := death.NewDeath(syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + + d.WaitForDeathWithFunc(func() { + defer os.Exit(1) + defer runtime.Goexit() + chain.Database.Close() + }) +} \ No newline at end of file From bf60b2d555000afe8e408adc9db8ff85a8793ccf Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Thu, 15 Nov 2018 16:18:07 -0500 Subject: [PATCH 17/26] part_10 --- blockchain/block.go | 9 +- blockchain/blockchain.go | 193 ++++++++++++++++++++++++++++---------- blockchain/chain_iter.go | 32 +++++++ blockchain/transaction.go | 33 ++++--- cli/cli.go | 106 +++++++++++++++------ go.mod | 1 + go.sum | 2 + main.go | 1 + wallet/wallets.go | 12 ++- 9 files changed, 294 insertions(+), 95 deletions(-) create mode 100644 blockchain/chain_iter.go diff --git a/blockchain/block.go b/blockchain/block.go index 3dd836ba..5bbad639 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -4,13 +4,16 @@ import ( "bytes" "encoding/gob" "log" + "time" ) type Block struct { + Timestamp int64 Hash []byte Transactions []*Transaction PrevHash []byte Nonce int + Height int } func (b *Block) HashTransactions() []byte { @@ -24,8 +27,8 @@ func (b *Block) HashTransactions() []byte { return tree.RootNode.Data } -func CreateBlock(txs []*Transaction, prevHash []byte) *Block { - block := &Block{[]byte{}, txs, prevHash, 0} +func CreateBlock(txs []*Transaction, prevHash []byte, height int) *Block { + block := &Block{time.Now().Unix(), []byte{}, txs, prevHash, 0, height} pow := NewProof(block) nonce, hash := pow.Run() @@ -36,7 +39,7 @@ func CreateBlock(txs []*Transaction, prevHash []byte) *Block { } func Genesis(coinbase *Transaction) *Block { - return CreateBlock([]*Transaction{coinbase}, []byte{}) + return CreateBlock([]*Transaction{coinbase}, []byte{}, 0) } func (b *Block) Serialize() []byte { diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 11b9a97d..36b63fd0 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -8,14 +8,15 @@ import ( "fmt" "log" "os" + "path/filepath" "runtime" + "strings" "github.com/dgraph-io/badger" ) const ( - dbPath = "./tmp/blocks" - dbFile = "./tmp/blocks/MANIFEST" + dbPath = "./tmp/blocks_%s" genesisData = "First Transaction from Genesis" ) @@ -24,21 +25,17 @@ type BlockChain struct { Database *badger.DB } -type BlockChainIterator struct { - CurrentHash []byte - Database *badger.DB -} - -func DBexists() bool { - if _, err := os.Stat(dbFile); os.IsNotExist(err) { +func DBexists(path string) bool { + if _, err := os.Stat(path + "/MANIFEST"); os.IsNotExist(err) { return false } return true } -func ContinueBlockChain(address string) *BlockChain { - if DBexists() == false { +func ContinueBlockChain(nodeId string) *BlockChain { + path := fmt.Sprintf(dbPath, nodeId) + if DBexists(path) == false { fmt.Println("No existing blockchain found, create one!") runtime.Goexit() } @@ -46,10 +43,10 @@ func ContinueBlockChain(address string) *BlockChain { var lastHash []byte opts := badger.DefaultOptions - opts.Dir = dbPath - opts.ValueDir = dbPath + opts.Dir = path + opts.ValueDir = path - db, err := badger.Open(opts) + db, err := openDB(path, opts) Handle(err) err = db.Update(func(txn *badger.Txn) error { @@ -66,19 +63,18 @@ func ContinueBlockChain(address string) *BlockChain { return &chain } -func InitBlockChain(address string) *BlockChain { - var lastHash []byte - - if DBexists() { +func InitBlockChain(address, nodeId string) *BlockChain { + path := fmt.Sprintf(dbPath, nodeId) + if DBexists(path) { fmt.Println("Blockchain already exists") runtime.Goexit() } - + var lastHash []byte opts := badger.DefaultOptions - opts.Dir = dbPath - opts.ValueDir = dbPath + opts.Dir = path + opts.ValueDir = path - db, err := badger.Open(opts) + db, err := openDB(path, opts) Handle(err) err = db.Update(func(txn *badger.Txn) error { @@ -101,8 +97,99 @@ func InitBlockChain(address string) *BlockChain { return &blockchain } -func (chain *BlockChain) AddBlock(transactions []*Transaction) *Block { +func (chain *BlockChain) AddBlock(block *Block) { + err := chain.Database.Update(func(txn *badger.Txn) error { + if _, err := txn.Get(block.Hash); err == nil { + return nil + } + + blockData := block.Serialize() + err := txn.Set(block.Hash, blockData) + Handle(err) + + item, err := txn.Get([]byte("lh")) + Handle(err) + lastHash, _ := item.Value() + + item, err = txn.Get(lastHash) + Handle(err) + lastBlockData, _ := item.Value() + + lastBlock := Deserialize(lastBlockData) + + if block.Height > lastBlock.Height { + err = txn.Set([]byte("lh"), block.Hash) + Handle(err) + chain.LastHash = block.Hash + } + + return nil + }) + Handle(err) +} + +func (chain *BlockChain) GetBestHeight() int { + var lastBlock Block + + err := chain.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte("lh")) + Handle(err) + lastHash, _ := item.Value() + + item, err = txn.Get(lastHash) + Handle(err) + lastBlockData, _ := item.Value() + + lastBlock = *Deserialize(lastBlockData) + + return nil + }) + Handle(err) + + return lastBlock.Height +} + +func (chain *BlockChain) GetBlock(blockHash []byte) (Block, error) { + var block Block + + err := chain.Database.View(func(txn *badger.Txn) error { + if item, err := txn.Get(blockHash); err != nil { + return errors.New("Block is not found") + } else { + blockData, _ := item.Value() + + block = *Deserialize(blockData) + } + return nil + }) + if err != nil { + return block, err + } + + return block, nil +} + +func (chain *BlockChain) GetBlockHashes() [][]byte { + var blocks [][]byte + + iter := chain.Iterator() + + for { + block := iter.Next() + + blocks = append(blocks, block.Hash) + + if len(block.PrevHash) == 0 { + break + } + } + + return blocks +} + +func (chain *BlockChain) MineBlock(transactions []*Transaction) *Block { var lastHash []byte + var lastHeight int for _, tx := range transactions { if chain.VerifyTransaction(tx) != true { @@ -115,11 +202,19 @@ func (chain *BlockChain) AddBlock(transactions []*Transaction) *Block { Handle(err) lastHash, err = item.Value() + item, err = txn.Get(lastHash) + Handle(err) + lastBlockData, _ := item.Value() + + lastBlock := Deserialize(lastBlockData) + + lastHeight = lastBlock.Height + return err }) Handle(err) - newBlock := CreateBlock(transactions, lastHash) + newBlock := CreateBlock(transactions, lastHash, lastHeight+1) err = chain.Database.Update(func(txn *badger.Txn) error { err := txn.Set(newBlock.Hash, newBlock.Serialize()) @@ -135,30 +230,6 @@ func (chain *BlockChain) AddBlock(transactions []*Transaction) *Block { return newBlock } -func (chain *BlockChain) Iterator() *BlockChainIterator { - iter := &BlockChainIterator{chain.LastHash, chain.Database} - - return iter -} - -func (iter *BlockChainIterator) Next() *Block { - var block *Block - - err := iter.Database.View(func(txn *badger.Txn) error { - item, err := txn.Get(iter.CurrentHash) - Handle(err) - encodedBlock, err := item.Value() - block = Deserialize(encodedBlock) - - return err - }) - Handle(err) - - iter.CurrentHash = block.PrevHash - - return block -} - func (chain *BlockChain) FindUTXO() map[string]TxOutputs { UTXO := make(map[string]TxOutputs) spentTXOs := make(map[string][]int) @@ -245,3 +316,29 @@ func (bc *BlockChain) VerifyTransaction(tx *Transaction) bool { return tx.Verify(prevTXs) } + +func retry(dir string, originalOpts badger.Options) (*badger.DB, error) { + lockPath := filepath.Join(dir, "LOCK") + if err := os.Remove(lockPath); err != nil { + return nil, fmt.Errorf(`removing "LOCK": %s`, err) + } + retryOpts := originalOpts + retryOpts.Truncate = true + db, err := badger.Open(retryOpts) + return db, err +} + +func openDB(dir string, opts badger.Options) (*badger.DB, error) { + if db, err := badger.Open(opts); err != nil { + if strings.Contains(err.Error(), "LOCK") { + if db, err := retry(dir, opts); err == nil { + log.Println("database unlocked, value log truncated") + return db, nil + } + log.Println("could not unlock database:", err) + } + return nil, err + } else { + return db, nil + } +} diff --git a/blockchain/chain_iter.go b/blockchain/chain_iter.go new file mode 100644 index 00000000..49934a46 --- /dev/null +++ b/blockchain/chain_iter.go @@ -0,0 +1,32 @@ +package blockchain + +import "github.com/dgraph-io/badger" + +type BlockChainIterator struct { + CurrentHash []byte + Database *badger.DB +} + +func (chain *BlockChain) Iterator() *BlockChainIterator { + iter := &BlockChainIterator{chain.LastHash, chain.Database} + + return iter +} + +func (iter *BlockChainIterator) Next() *Block { + var block *Block + + err := iter.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get(iter.CurrentHash) + Handle(err) + encodedBlock, err := item.Value() + block = Deserialize(encodedBlock) + + return err + }) + Handle(err) + + iter.CurrentHash = block.PrevHash + + return block +} diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 31de7a61..10cf598f 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -45,6 +45,15 @@ func (tx Transaction) Serialize() []byte { return encoded.Bytes() } +func DeserializeTransaction(data []byte) Transaction { + var transaction Transaction + + decoder := gob.NewDecoder(bytes.NewReader(data)) + err := decoder.Decode(&transaction) + Handle(err) + return transaction +} + func CoinbaseTx(to, data string) *Transaction { if data == "" { randData := make([]byte, 24) @@ -62,13 +71,10 @@ func CoinbaseTx(to, data string) *Transaction { return &tx } -func NewTransaction(from, to string, amount int, UTXO *UTXOSet) *Transaction { +func NewTransaction(w *wallet.Wallet, to string, amount int, UTXO *UTXOSet) *Transaction { var inputs []TxInput var outputs []TxOutput - wallets, err := wallet.CreateWallets() - Handle(err) - w := wallets.GetWallet(from) pubKeyHash := wallet.PublicKeyHash(w.PublicKey) acc, validOutputs := UTXO.FindSpendableOutputs(pubKeyHash, amount) @@ -86,6 +92,8 @@ func NewTransaction(from, to string, amount int, UTXO *UTXOSet) *Transaction { } } + from := fmt.Sprintf("%s", w.Address()) + outputs = append(outputs, *NewTXOutput(amount, to)) if acc > amount { @@ -120,15 +128,15 @@ func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transac prevTX := prevTXs[hex.EncodeToString(in.ID)] txCopy.Inputs[inId].Signature = nil txCopy.Inputs[inId].PubKey = prevTX.Outputs[in.Out].PubKeyHash - txCopy.ID = txCopy.Hash() - txCopy.Inputs[inId].PubKey = nil - r, s, err := ecdsa.Sign(rand.Reader, &privKey, txCopy.ID) + dataToSign := fmt.Sprintf("%x\n", txCopy) + + r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign)) Handle(err) signature := append(r.Bytes(), s.Bytes()...) tx.Inputs[inId].Signature = signature - + txCopy.Inputs[inId].PubKey = nil } } @@ -150,8 +158,6 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { prevTx := prevTXs[hex.EncodeToString(in.ID)] txCopy.Inputs[inId].Signature = nil txCopy.Inputs[inId].PubKey = prevTx.Outputs[in.Out].PubKeyHash - txCopy.ID = txCopy.Hash() - txCopy.Inputs[inId].PubKey = nil r := big.Int{} s := big.Int{} @@ -166,10 +172,13 @@ func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool { x.SetBytes(in.PubKey[:(keyLen / 2)]) y.SetBytes(in.PubKey[(keyLen / 2):]) - rawPubKey := ecdsa.PublicKey{curve, &x, &y} - if ecdsa.Verify(&rawPubKey, txCopy.ID, &r, &s) == false { + dataToVerify := fmt.Sprintf("%x\n", txCopy) + + rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y} + if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false { return false } + txCopy.Inputs[inId].PubKey = nil } return true diff --git a/cli/cli.go b/cli/cli.go index acf470ba..d877844e 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -10,6 +10,7 @@ import ( "github.com/tensor-programming/golang-blockchain/blockchain" "github.com/tensor-programming/golang-blockchain/wallet" + "github.com/tensor-programming/golang-blockchain/network" ) type CommandLine struct{} @@ -19,10 +20,11 @@ func (cli *CommandLine) printUsage() { fmt.Println(" getbalance -address ADDRESS - get the balance for an address") fmt.Println(" createblockchain -address ADDRESS creates a blockchain and sends genesis reward to address") fmt.Println(" printchain - Prints the blocks in the chain") - fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send amount of coins") + fmt.Println(" send -from FROM -to TO -amount AMOUNT -mine - Send amount of coins. Then -mine flag is set, mine off of this node") fmt.Println(" createwallet - Creates a new Wallet") fmt.Println(" listaddresses - Lists the addresses in our wallet file") fmt.Println(" reindexutxo - Rebuilds the UTXO set") + fmt.Println(" startnode -miner ADDRESS - Start a node with ID specified in NODE_ID env. var. -miner enables mining") } func (cli *CommandLine) validateArgs() { @@ -32,8 +34,21 @@ func (cli *CommandLine) validateArgs() { } } -func (cli *CommandLine) reindexUTXO() { - chain := blockchain.ContinueBlockChain("") +func (cli *CommandLine) StartNode(nodeID, minerAddress string) { + fmt.Printf("Starting Node %s\n", nodeID) + + if len(minerAddress) > 0 { + if wallet.ValidateAddress(minerAddress) { + fmt.Println("Mining is on. Address to receive rewards: ", minerAddress) + } else { + log.Panic("Wrong miner address!") + } + } + network.StartServer(nodeID, minerAddress) +} + +func (cli *CommandLine) reindexUTXO(nodeID string) { + chain := blockchain.ContinueBlockChain(nodeID) defer chain.Database.Close() UTXOSet := blockchain.UTXOSet{chain} UTXOSet.Reindex() @@ -42,8 +57,8 @@ func (cli *CommandLine) reindexUTXO() { fmt.Printf("Done! There are %d transactions in the UTXO set.\n", count) } -func (cli *CommandLine) listAddresses() { - wallets, _ := wallet.CreateWallets() +func (cli *CommandLine) listAddresses(nodeID string) { + wallets, _ := wallet.CreateWallets(nodeID) addresses := wallets.GetAllAddresses() for _, address := range addresses { @@ -51,16 +66,16 @@ func (cli *CommandLine) listAddresses() { } } -func (cli *CommandLine) createWallet() { - wallets, _ := wallet.CreateWallets() +func (cli *CommandLine) createWallet(nodeID string) { + wallets, _ := wallet.CreateWallets(nodeID) address := wallets.AddWallet() - wallets.SaveFile() + wallets.SaveFile(nodeID) fmt.Printf("New address is: %s\n", address) } -func (cli *CommandLine) printChain() { - chain := blockchain.ContinueBlockChain("") +func (cli *CommandLine) printChain(nodeID string) { + chain := blockchain.ContinueBlockChain(nodeID) defer chain.Database.Close() iter := chain.Iterator() @@ -82,11 +97,11 @@ func (cli *CommandLine) printChain() { } } -func (cli *CommandLine) createBlockChain(address string) { +func (cli *CommandLine) createBlockChain(address, nodeID string) { if !wallet.ValidateAddress(address) { log.Panic("Address is not Valid") } - chain := blockchain.InitBlockChain(address) + chain := blockchain.InitBlockChain(address, nodeID) defer chain.Database.Close() UTXOSet := blockchain.UTXOSet{chain} @@ -95,11 +110,11 @@ func (cli *CommandLine) createBlockChain(address string) { fmt.Println("Finished!") } -func (cli *CommandLine) getBalance(address string) { +func (cli *CommandLine) getBalance(address, nodeID string) { if !wallet.ValidateAddress(address) { log.Panic("Address is not Valid") } - chain := blockchain.ContinueBlockChain(address) + chain := blockchain.ContinueBlockChain(nodeID) UTXOSet := blockchain.UTXOSet{chain} defer chain.Database.Close() @@ -115,27 +130,46 @@ func (cli *CommandLine) getBalance(address string) { fmt.Printf("Balance of %s: %d\n", address, balance) } -func (cli *CommandLine) send(from, to string, amount int) { +func (cli *CommandLine) send(from, to string, amount int, nodeID string, mineNow bool) { if !wallet.ValidateAddress(to) { log.Panic("Address is not Valid") } if !wallet.ValidateAddress(from) { log.Panic("Address is not Valid") } - chain := blockchain.ContinueBlockChain(from) + chain := blockchain.ContinueBlockChain(nodeID) UTXOSet := blockchain.UTXOSet{chain} defer chain.Database.Close() - tx := blockchain.NewTransaction(from, to, amount, &UTXOSet) - cbTx := blockchain.CoinbaseTx(from, "") - block := chain.AddBlock([]*blockchain.Transaction{cbTx, tx}) - UTXOSet.Update(block) + wallets, err := wallet.CreateWallets(nodeID) + if err != nil { + log.Panic(err) + } + wallet := wallets.GetWallet(from) + + tx := blockchain.NewTransaction(&wallet, to, amount, &UTXOSet) + if mineNow { + cbTx := blockchain.CoinbaseTx(from, "") + txs := []*blockchain.Transaction{cbTx, tx} + block := chain.MineBlock(txs) + UTXOSet.Update(block) + } else { + network.SendTx(network.KnownNodes[0], tx) + fmt.Println("send tx") + } + fmt.Println("Success!") } func (cli *CommandLine) Run() { cli.validateArgs() + nodeID := os.Getenv("NODE_ID") + if nodeID == "" { + fmt.Printf("NODE_ID env is not set!") + runtime.Goexit() + } + getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) sendCmd := flag.NewFlagSet("send", flag.ExitOnError) @@ -143,12 +177,15 @@ func (cli *CommandLine) Run() { createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError) listAddressesCmd := flag.NewFlagSet("listaddresses", flag.ExitOnError) reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError) + startNodeCmd := flag.NewFlagSet("startnode", flag.ExitOnError) getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") sendFrom := sendCmd.String("from", "", "Source wallet address") sendTo := sendCmd.String("to", "", "Destination wallet address") sendAmount := sendCmd.Int("amount", 0, "Amount to send") + sendMine := sendCmd.Bool("mine", false, "Mine immediately on the same node") + startNodeMiner := startNodeCmd.String("miner", "", "Enable mining mode and send reward to ADDRESS") switch os.Args[1] { case "reindexutxo": @@ -166,6 +203,11 @@ func (cli *CommandLine) Run() { if err != nil { log.Panic(err) } + case "startnode": + err := startNodeCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } case "listaddresses": err := listAddressesCmd.Parse(os.Args[2:]) if err != nil { @@ -196,7 +238,7 @@ func (cli *CommandLine) Run() { getBalanceCmd.Usage() runtime.Goexit() } - cli.getBalance(*getBalanceAddress) + cli.getBalance(*getBalanceAddress, nodeID) } if createBlockchainCmd.Parsed() { @@ -204,21 +246,21 @@ func (cli *CommandLine) Run() { createBlockchainCmd.Usage() runtime.Goexit() } - cli.createBlockChain(*createBlockchainAddress) + cli.createBlockChain(*createBlockchainAddress, nodeID) } if printChainCmd.Parsed() { - cli.printChain() + cli.printChain(nodeID) } if createWalletCmd.Parsed() { - cli.createWallet() + cli.createWallet(nodeID) } if listAddressesCmd.Parsed() { - cli.listAddresses() + cli.listAddresses(nodeID) } if reindexUTXOCmd.Parsed() { - cli.reindexUTXO() + cli.reindexUTXO(nodeID) } if sendCmd.Parsed() { @@ -227,6 +269,16 @@ func (cli *CommandLine) Run() { runtime.Goexit() } - cli.send(*sendFrom, *sendTo, *sendAmount) + cli.send(*sendFrom, *sendTo, *sendAmount, nodeID, *sendMine) + } + + if startNodeCmd.Parsed() { + nodeID := os.Getenv("NODE_ID") + if nodeID == "" { + startNodeCmd.Usage() + runtime.Goexit() + } + cli.StartNode(nodeID, *startNodeMiner) } } + diff --git a/go.mod b/go.mod index db0e9296..a9531075 100644 --- a/go.mod +++ b/go.mod @@ -13,4 +13,5 @@ require ( github.com/stretchr/testify v1.2.2 golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect + gopkg.in/vrecan/death.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 6cad4822..49038b71 100644 --- a/go.sum +++ b/go.sum @@ -22,3 +22,5 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PC golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +gopkg.in/vrecan/death.v3 v3.0.1 h1:qMzChssfxEvW9ckxucDyeLdvd/rhy4LBOyzN8oaFdEU= +gopkg.in/vrecan/death.v3 v3.0.1/go.mod h1:Jy+S9sSCa4cKJF59FMiiDO5/bLCsOtHC8sK3doI1vQM= diff --git a/main.go b/main.go index 3102043c..d1798302 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( func main() { defer os.Exit(0) + cmd := cli.CommandLine{} cmd.Run() } diff --git a/wallet/wallets.go b/wallet/wallets.go index f09d0a7d..f7b5288d 100644 --- a/wallet/wallets.go +++ b/wallet/wallets.go @@ -10,17 +10,17 @@ import ( "os" ) -const walletFile = "./tmp/wallets.data" +const walletFile = "./tmp/wallets_%s.data" type Wallets struct { Wallets map[string]*Wallet } -func CreateWallets() (*Wallets, error) { +func CreateWallets(nodeId string) (*Wallets, error) { wallets := Wallets{} wallets.Wallets = make(map[string]*Wallet) - err := wallets.LoadFile() + err := wallets.LoadFile(nodeId) return &wallets, err } @@ -48,7 +48,8 @@ func (ws Wallets) GetWallet(address string) Wallet { return *ws.Wallets[address] } -func (ws *Wallets) LoadFile() error { +func (ws *Wallets) LoadFile(nodeId string) error { + walletFile := fmt.Sprintf(walletFile, nodeId) if _, err := os.Stat(walletFile); os.IsNotExist(err) { return err } @@ -72,8 +73,9 @@ func (ws *Wallets) LoadFile() error { return nil } -func (ws *Wallets) SaveFile() { +func (ws *Wallets) SaveFile(nodeId string) { var content bytes.Buffer + walletFile := fmt.Sprintf(walletFile, nodeId) gob.Register(elliptic.P256()) From 715e13722a41f507178670ba765d22558d0d0527 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Sat, 23 Mar 2019 17:09:54 -0400 Subject: [PATCH 18/26] remove sum --- go.sum | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 go.sum diff --git a/go.sum b/go.sum deleted file mode 100644 index 49038b71..00000000 --- a/go.sum +++ /dev/null @@ -1,26 +0,0 @@ -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= -github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0= -github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -gopkg.in/vrecan/death.v3 v3.0.1 h1:qMzChssfxEvW9ckxucDyeLdvd/rhy4LBOyzN8oaFdEU= -gopkg.in/vrecan/death.v3 v3.0.1/go.mod h1:Jy+S9sSCa4cKJF59FMiiDO5/bLCsOtHC8sK3doI1vQM= From 6c219b3e17f78fd6029d1d467e3886f1adad7043 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 12 Apr 2019 19:31:17 -0400 Subject: [PATCH 19/26] Fix merkle loop problem --- blockchain/merkle.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 33c11150..a306e834 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -42,14 +42,17 @@ func NewMerkleTree(data [][]byte) *MerkleTree { nodes = append(nodes, *node) } - for i := 0; i < len(data)/2; i++ { +TreeLoop: + for { var level []MerkleNode for j := 0; j < len(nodes); j += 2 { + if len(nodes) == 1 { + break TreeLoop + } node := NewMerkleNode(&nodes[j], &nodes[j+1], nil) level = append(level, *node) } - nodes = level } From 85bfc0cb42ecdf1f7a1743180d9e229ab41a5618 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 12 Apr 2019 20:40:58 -0400 Subject: [PATCH 20/26] fix out-of-bounds --- blockchain/merkle.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/blockchain/merkle.go b/blockchain/merkle.go index a306e834..26260299 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -33,26 +33,26 @@ func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { func NewMerkleTree(data [][]byte) *MerkleTree { var nodes []MerkleNode - if len(data)%2 != 0 { - data = append(data, data[len(data)-1]) - } - for _, dat := range data { node := NewMerkleNode(nil, nil, dat) nodes = append(nodes, *node) } -TreeLoop: - for { - var level []MerkleNode + if len(nodes) == 0 { + log.Panic("No merkel nodes") + } - for j := 0; j < len(nodes); j += 2 { - if len(nodes) == 1 { - break TreeLoop - } - node := NewMerkleNode(&nodes[j], &nodes[j+1], nil) + for len(nodes) > 1 { + if len(nodes)%2 != 0 { + nodes = append(nodes, nodes[len(nodes)-1]) + } + + var level []MerkleNode + for i := 0; i < len(nodes); i += 2 { + node := NewMerkleNode(&nodes[i], &nodes[i+1], nil) level = append(level, *node) } + nodes = level } From bb517ba900f522a9359d487a32c578a0ce4ace8c Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 12 Apr 2019 20:48:11 -0400 Subject: [PATCH 21/26] add test for merkle tree --- merkle_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 merkle_test.go diff --git a/merkle_test.go b/merkle_test.go new file mode 100644 index 00000000..cdb51150 --- /dev/null +++ b/merkle_test.go @@ -0,0 +1,51 @@ +package blockchain + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewMerkleNode(t *testing.T) { + data := [][]byte{ + []byte("node1"), + []byte("node2"), + []byte("node3"), + []byte("node4"), + []byte("node5"), + []byte("node6"), + []byte("node7"), + } + + // level 1 + mn1 := NewMerkleNode(nil, nil, data[0]) + mn2 := NewMerkleNode(nil, nil, data[1]) + mn3 := NewMerkleNode(nil, nil, data[2]) + mn4 := NewMerkleNode(nil, nil, data[3]) + mn5 := NewMerkleNode(nil, nil, data[4]) + mn6 := NewMerkleNode(nil, nil, data[5]) + mn7 := NewMerkleNode(nil, nil, data[6]) + mn8 := NewMerkleNode(nil, nil, data[6]) + + // level 2 + mn9 := NewMerkleNode(mn1, mn2, nil) + mn10 := NewMerkleNode(mn3, mn4, nil) + mn11 := NewMerkleNode(mn5, mn6, nil) + mn12 := NewMerkleNode(mn7, mn8, nil) + + //level 3 + + mn13 := NewMerkleNode(mn9, mn10, nil) + mn14 := NewMerkleNode(mn11, mn12, nil) + + //level 4 + + mn15 := NewMerkleNode(mn13, mn14, nil) + + root := fmt.Sprintf("%x", mn15.Data) + tree := NewMerkleTree(data) + + assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") + +} From d6e4ec82d5c6ed0f5cb493a614334e7debdca531 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 12 Apr 2019 20:52:03 -0400 Subject: [PATCH 22/26] Delete merkle_test.go --- merkle_test.go | 51 -------------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 merkle_test.go diff --git a/merkle_test.go b/merkle_test.go deleted file mode 100644 index cdb51150..00000000 --- a/merkle_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package blockchain - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewMerkleNode(t *testing.T) { - data := [][]byte{ - []byte("node1"), - []byte("node2"), - []byte("node3"), - []byte("node4"), - []byte("node5"), - []byte("node6"), - []byte("node7"), - } - - // level 1 - mn1 := NewMerkleNode(nil, nil, data[0]) - mn2 := NewMerkleNode(nil, nil, data[1]) - mn3 := NewMerkleNode(nil, nil, data[2]) - mn4 := NewMerkleNode(nil, nil, data[3]) - mn5 := NewMerkleNode(nil, nil, data[4]) - mn6 := NewMerkleNode(nil, nil, data[5]) - mn7 := NewMerkleNode(nil, nil, data[6]) - mn8 := NewMerkleNode(nil, nil, data[6]) - - // level 2 - mn9 := NewMerkleNode(mn1, mn2, nil) - mn10 := NewMerkleNode(mn3, mn4, nil) - mn11 := NewMerkleNode(mn5, mn6, nil) - mn12 := NewMerkleNode(mn7, mn8, nil) - - //level 3 - - mn13 := NewMerkleNode(mn9, mn10, nil) - mn14 := NewMerkleNode(mn11, mn12, nil) - - //level 4 - - mn15 := NewMerkleNode(mn13, mn14, nil) - - root := fmt.Sprintf("%x", mn15.Data) - tree := NewMerkleTree(data) - - assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") - -} From e14978d1b38f52129396238a7d4d8709d0dbb115 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Fri, 12 Apr 2019 20:52:32 -0400 Subject: [PATCH 23/26] add merkle_test --- blockchain/chain_iter.go | 64 +++--- blockchain/merkle.go | 127 +++++------ blockchain/merkle_test.go | 51 +++++ blockchain/tx.go | 126 +++++------ blockchain/utxo.go | 438 +++++++++++++++++++------------------- 5 files changed, 430 insertions(+), 376 deletions(-) create mode 100644 blockchain/merkle_test.go diff --git a/blockchain/chain_iter.go b/blockchain/chain_iter.go index 49934a46..2137cde5 100644 --- a/blockchain/chain_iter.go +++ b/blockchain/chain_iter.go @@ -1,32 +1,32 @@ -package blockchain - -import "github.com/dgraph-io/badger" - -type BlockChainIterator struct { - CurrentHash []byte - Database *badger.DB -} - -func (chain *BlockChain) Iterator() *BlockChainIterator { - iter := &BlockChainIterator{chain.LastHash, chain.Database} - - return iter -} - -func (iter *BlockChainIterator) Next() *Block { - var block *Block - - err := iter.Database.View(func(txn *badger.Txn) error { - item, err := txn.Get(iter.CurrentHash) - Handle(err) - encodedBlock, err := item.Value() - block = Deserialize(encodedBlock) - - return err - }) - Handle(err) - - iter.CurrentHash = block.PrevHash - - return block -} +package blockchain + +import "github.com/dgraph-io/badger" + +type BlockChainIterator struct { + CurrentHash []byte + Database *badger.DB +} + +func (chain *BlockChain) Iterator() *BlockChainIterator { + iter := &BlockChainIterator{chain.LastHash, chain.Database} + + return iter +} + +func (iter *BlockChainIterator) Next() *Block { + var block *Block + + err := iter.Database.View(func(txn *badger.Txn) error { + item, err := txn.Get(iter.CurrentHash) + Handle(err) + encodedBlock, err := item.Value() + block = Deserialize(encodedBlock) + + return err + }) + Handle(err) + + iter.CurrentHash = block.PrevHash + + return block +} diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 26260299..11627986 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -1,62 +1,65 @@ -package blockchain - -import "crypto/sha256" - -type MerkleTree struct { - RootNode *MerkleNode -} - -type MerkleNode struct { - Left *MerkleNode - Right *MerkleNode - Data []byte -} - -func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { - node := MerkleNode{} - - if left == nil && right == nil { - hash := sha256.Sum256(data) - node.Data = hash[:] - } else { - prevHashes := append(left.Data, right.Data...) - hash := sha256.Sum256(prevHashes) - node.Data = hash[:] - } - - node.Left = left - node.Right = right - - return &node -} - -func NewMerkleTree(data [][]byte) *MerkleTree { - var nodes []MerkleNode - - for _, dat := range data { - node := NewMerkleNode(nil, nil, dat) - nodes = append(nodes, *node) - } - - if len(nodes) == 0 { - log.Panic("No merkel nodes") - } - - for len(nodes) > 1 { - if len(nodes)%2 != 0 { - nodes = append(nodes, nodes[len(nodes)-1]) - } - - var level []MerkleNode - for i := 0; i < len(nodes); i += 2 { - node := NewMerkleNode(&nodes[i], &nodes[i+1], nil) - level = append(level, *node) - } - - nodes = level - } - - tree := MerkleTree{&nodes[0]} - - return &tree -} +package blockchain + +import ( + "crypto/sha256" + "log" +) + +type MerkleTree struct { + RootNode *MerkleNode +} + +type MerkleNode struct { + Left *MerkleNode + Right *MerkleNode + Data []byte +} + +func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { + node := MerkleNode{} + + if left == nil && right == nil { + hash := sha256.Sum256(data) + node.Data = hash[:] + } else { + prevHashes := append(left.Data, right.Data...) + hash := sha256.Sum256(prevHashes) + node.Data = hash[:] + } + + node.Left = left + node.Right = right + + return &node +} + +func NewMerkleTree(data [][]byte) *MerkleTree { + var nodes []MerkleNode + + for _, dat := range data { + node := NewMerkleNode(nil, nil, dat) + nodes = append(nodes, *node) + } + + if len(nodes) == 0 { + log.Panic("No merkel nodes") + } + + for len(nodes) > 1 { + if len(nodes)%2 != 0 { + nodes = append(nodes, nodes[len(nodes)-1]) + } + + var level []MerkleNode + for i := 0; i < len(nodes); i += 2 { + node := NewMerkleNode(&nodes[i], &nodes[i+1], nil) + level = append(level, *node) + } + + nodes = level + } + + tree := MerkleTree{&nodes[0]} + + return &tree +} diff --git a/blockchain/merkle_test.go b/blockchain/merkle_test.go new file mode 100644 index 00000000..cdb51150 --- /dev/null +++ b/blockchain/merkle_test.go @@ -0,0 +1,51 @@ +package blockchain + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewMerkleNode(t *testing.T) { + data := [][]byte{ + []byte("node1"), + []byte("node2"), + []byte("node3"), + []byte("node4"), + []byte("node5"), + []byte("node6"), + []byte("node7"), + } + + // level 1 + mn1 := NewMerkleNode(nil, nil, data[0]) + mn2 := NewMerkleNode(nil, nil, data[1]) + mn3 := NewMerkleNode(nil, nil, data[2]) + mn4 := NewMerkleNode(nil, nil, data[3]) + mn5 := NewMerkleNode(nil, nil, data[4]) + mn6 := NewMerkleNode(nil, nil, data[5]) + mn7 := NewMerkleNode(nil, nil, data[6]) + mn8 := NewMerkleNode(nil, nil, data[6]) + + // level 2 + mn9 := NewMerkleNode(mn1, mn2, nil) + mn10 := NewMerkleNode(mn3, mn4, nil) + mn11 := NewMerkleNode(mn5, mn6, nil) + mn12 := NewMerkleNode(mn7, mn8, nil) + + //level 3 + + mn13 := NewMerkleNode(mn9, mn10, nil) + mn14 := NewMerkleNode(mn11, mn12, nil) + + //level 4 + + mn15 := NewMerkleNode(mn13, mn14, nil) + + root := fmt.Sprintf("%x", mn15.Data) + tree := NewMerkleTree(data) + + assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") + +} diff --git a/blockchain/tx.go b/blockchain/tx.go index 39661db8..4e733d5f 100644 --- a/blockchain/tx.go +++ b/blockchain/tx.go @@ -1,63 +1,63 @@ -package blockchain - -import ( - "bytes" - "encoding/gob" - - "github.com/tensor-programming/golang-blockchain/wallet" -) - -type TxOutput struct { - Value int - PubKeyHash []byte -} - -type TxOutputs struct { - Outputs []TxOutput -} - -type TxInput struct { - ID []byte - Out int - Signature []byte - PubKey []byte -} - -func (in *TxInput) UsesKey(pubKeyHash []byte) bool { - lockingHash := wallet.PublicKeyHash(in.PubKey) - - return bytes.Compare(lockingHash, pubKeyHash) == 0 -} - -func (out *TxOutput) Lock(address []byte) { - pubKeyHash := wallet.Base58Decode(address) - pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4] - out.PubKeyHash = pubKeyHash -} - -func (out *TxOutput) IsLockedWithKey(pubKeyHash []byte) bool { - return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0 -} - -func NewTXOutput(value int, address string) *TxOutput { - txo := &TxOutput{value, nil} - txo.Lock([]byte(address)) - - return txo -} - -func (outs TxOutputs) Serialize() []byte { - var buffer bytes.Buffer - encode := gob.NewEncoder(&buffer) - err := encode.Encode(outs) - Handle(err) - return buffer.Bytes() -} - -func DeserializeOutputs(data []byte) TxOutputs { - var outputs TxOutputs - decode := gob.NewDecoder(bytes.NewReader(data)) - err := decode.Decode(&outputs) - Handle(err) - return outputs -} +package blockchain + +import ( + "bytes" + "encoding/gob" + + "github.com/tensor-programming/golang-blockchain/wallet" +) + +type TxOutput struct { + Value int + PubKeyHash []byte +} + +type TxOutputs struct { + Outputs []TxOutput +} + +type TxInput struct { + ID []byte + Out int + Signature []byte + PubKey []byte +} + +func (in *TxInput) UsesKey(pubKeyHash []byte) bool { + lockingHash := wallet.PublicKeyHash(in.PubKey) + + return bytes.Compare(lockingHash, pubKeyHash) == 0 +} + +func (out *TxOutput) Lock(address []byte) { + pubKeyHash := wallet.Base58Decode(address) + pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4] + out.PubKeyHash = pubKeyHash +} + +func (out *TxOutput) IsLockedWithKey(pubKeyHash []byte) bool { + return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0 +} + +func NewTXOutput(value int, address string) *TxOutput { + txo := &TxOutput{value, nil} + txo.Lock([]byte(address)) + + return txo +} + +func (outs TxOutputs) Serialize() []byte { + var buffer bytes.Buffer + encode := gob.NewEncoder(&buffer) + err := encode.Encode(outs) + Handle(err) + return buffer.Bytes() +} + +func DeserializeOutputs(data []byte) TxOutputs { + var outputs TxOutputs + decode := gob.NewDecoder(bytes.NewReader(data)) + err := decode.Decode(&outputs) + Handle(err) + return outputs +} diff --git a/blockchain/utxo.go b/blockchain/utxo.go index 571bb729..a55df1ce 100644 --- a/blockchain/utxo.go +++ b/blockchain/utxo.go @@ -1,219 +1,219 @@ -package blockchain - -import ( - "bytes" - "encoding/hex" - "log" - - "github.com/dgraph-io/badger" -) - -var ( - utxoPrefix = []byte("utxo-") - prefixLength = len(utxoPrefix) -) - -type UTXOSet struct { - Blockchain *BlockChain -} - -func (u UTXOSet) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { - unspentOuts := make(map[string][]int) - accumulated := 0 - db := u.Blockchain.Database - - err := db.View(func(txn *badger.Txn) error { - opts := badger.DefaultIteratorOptions - - it := txn.NewIterator(opts) - defer it.Close() - - for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { - item := it.Item() - k := item.Key() - v, err := item.Value() - Handle(err) - k = bytes.TrimPrefix(k, utxoPrefix) - txID := hex.EncodeToString(k) - outs := DeserializeOutputs(v) - - for outIdx, out := range outs.Outputs { - if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { - accumulated += out.Value - unspentOuts[txID] = append(unspentOuts[txID], outIdx) - } - } - } - return nil - }) - Handle(err) - - return accumulated, unspentOuts -} - -func (u UTXOSet) FindUnspentTransactions(pubKeyHash []byte) []TxOutput { - var UTXOs []TxOutput - - db := u.Blockchain.Database - - err := db.View(func(txn *badger.Txn) error { - opts := badger.DefaultIteratorOptions - - it := txn.NewIterator(opts) - defer it.Close() - - for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { - item := it.Item() - v, err := item.Value() - Handle(err) - outs := DeserializeOutputs(v) - for _, out := range outs.Outputs { - if out.IsLockedWithKey(pubKeyHash) { - UTXOs = append(UTXOs, out) - } - } - - } - return nil - }) - Handle(err) - - return UTXOs -} - -func (u UTXOSet) CountTransactions() int { - db := u.Blockchain.Database - counter := 0 - - err := db.View(func(txn *badger.Txn) error { - opts := badger.DefaultIteratorOptions - - it := txn.NewIterator(opts) - defer it.Close() - for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { - counter++ - } - - return nil - }) - - Handle(err) - - return counter -} - -func (u UTXOSet) Reindex() { - db := u.Blockchain.Database - - u.DeleteByPrefix(utxoPrefix) - - UTXO := u.Blockchain.FindUTXO() - - err := db.Update(func(txn *badger.Txn) error { - for txId, outs := range UTXO { - key, err := hex.DecodeString(txId) - Handle(err) - key = append(utxoPrefix, key...) - - err = txn.Set(key, outs.Serialize()) - Handle(err) - } - - return nil - }) - Handle(err) -} - -func (u *UTXOSet) Update(block *Block) { - db := u.Blockchain.Database - - err := db.Update(func(txn *badger.Txn) error { - for _, tx := range block.Transactions { - if tx.IsCoinbase() == false { - for _, in := range tx.Inputs { - updatedOuts := TxOutputs{} - inID := append(utxoPrefix, in.ID...) - item, err := txn.Get(inID) - Handle(err) - v, err := item.Value() - Handle(err) - - outs := DeserializeOutputs(v) - - for outIdx, out := range outs.Outputs { - if outIdx != in.Out { - updatedOuts.Outputs = append(updatedOuts.Outputs, out) - } - } - - if len(updatedOuts.Outputs) == 0 { - if err := txn.Delete(inID); err != nil { - log.Panic(err) - } - } else { - if err := txn.Set(inID, updatedOuts.Serialize()); err != nil { - log.Panic(err) - } - } - } - } - newOutputs := TxOutputs{} - for _, out := range tx.Outputs { - newOutputs.Outputs = append(newOutputs.Outputs, out) - } - - txID := append(utxoPrefix, tx.ID...) - if err := txn.Set(txID, newOutputs.Serialize()); err != nil { - log.Panic(err) - } - } - - return nil - }) - Handle(err) -} - -func (u *UTXOSet) DeleteByPrefix(prefix []byte) { - deleteKeys := func(keysForDelete [][]byte) error { - if err := u.Blockchain.Database.Update(func(txn *badger.Txn) error { - for _, key := range keysForDelete { - if err := txn.Delete(key); err != nil { - return err - } - } - return nil - }); err != nil { - return err - } - return nil - } - - collectSize := 100000 - u.Blockchain.Database.View(func(txn *badger.Txn) error { - opts := badger.DefaultIteratorOptions - opts.PrefetchValues = false - it := txn.NewIterator(opts) - defer it.Close() - - keysForDelete := make([][]byte, 0, collectSize) - keysCollected := 0 - for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { - key := it.Item().KeyCopy(nil) - keysForDelete = append(keysForDelete, key) - keysCollected++ - if keysCollected == collectSize { - if err := deleteKeys(keysForDelete); err != nil { - log.Panic(err) - } - keysForDelete = make([][]byte, 0, collectSize) - keysCollected = 0 - } - } - if keysCollected > 0 { - if err := deleteKeys(keysForDelete); err != nil { - log.Panic(err) - } - } - return nil - }) -} +package blockchain + +import ( + "bytes" + "encoding/hex" + "log" + + "github.com/dgraph-io/badger" +) + +var ( + utxoPrefix = []byte("utxo-") + prefixLength = len(utxoPrefix) +) + +type UTXOSet struct { + Blockchain *BlockChain +} + +func (u UTXOSet) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { + unspentOuts := make(map[string][]int) + accumulated := 0 + db := u.Blockchain.Database + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + item := it.Item() + k := item.Key() + v, err := item.Value() + Handle(err) + k = bytes.TrimPrefix(k, utxoPrefix) + txID := hex.EncodeToString(k) + outs := DeserializeOutputs(v) + + for outIdx, out := range outs.Outputs { + if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { + accumulated += out.Value + unspentOuts[txID] = append(unspentOuts[txID], outIdx) + } + } + } + return nil + }) + Handle(err) + + return accumulated, unspentOuts +} + +func (u UTXOSet) FindUnspentTransactions(pubKeyHash []byte) []TxOutput { + var UTXOs []TxOutput + + db := u.Blockchain.Database + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + item := it.Item() + v, err := item.Value() + Handle(err) + outs := DeserializeOutputs(v) + for _, out := range outs.Outputs { + if out.IsLockedWithKey(pubKeyHash) { + UTXOs = append(UTXOs, out) + } + } + + } + return nil + }) + Handle(err) + + return UTXOs +} + +func (u UTXOSet) CountTransactions() int { + db := u.Blockchain.Database + counter := 0 + + err := db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + + it := txn.NewIterator(opts) + defer it.Close() + for it.Seek(utxoPrefix); it.ValidForPrefix(utxoPrefix); it.Next() { + counter++ + } + + return nil + }) + + Handle(err) + + return counter +} + +func (u UTXOSet) Reindex() { + db := u.Blockchain.Database + + u.DeleteByPrefix(utxoPrefix) + + UTXO := u.Blockchain.FindUTXO() + + err := db.Update(func(txn *badger.Txn) error { + for txId, outs := range UTXO { + key, err := hex.DecodeString(txId) + Handle(err) + key = append(utxoPrefix, key...) + + err = txn.Set(key, outs.Serialize()) + Handle(err) + } + + return nil + }) + Handle(err) +} + +func (u *UTXOSet) Update(block *Block) { + db := u.Blockchain.Database + + err := db.Update(func(txn *badger.Txn) error { + for _, tx := range block.Transactions { + if tx.IsCoinbase() == false { + for _, in := range tx.Inputs { + updatedOuts := TxOutputs{} + inID := append(utxoPrefix, in.ID...) + item, err := txn.Get(inID) + Handle(err) + v, err := item.Value() + Handle(err) + + outs := DeserializeOutputs(v) + + for outIdx, out := range outs.Outputs { + if outIdx != in.Out { + updatedOuts.Outputs = append(updatedOuts.Outputs, out) + } + } + + if len(updatedOuts.Outputs) == 0 { + if err := txn.Delete(inID); err != nil { + log.Panic(err) + } + } else { + if err := txn.Set(inID, updatedOuts.Serialize()); err != nil { + log.Panic(err) + } + } + } + } + newOutputs := TxOutputs{} + for _, out := range tx.Outputs { + newOutputs.Outputs = append(newOutputs.Outputs, out) + } + + txID := append(utxoPrefix, tx.ID...) + if err := txn.Set(txID, newOutputs.Serialize()); err != nil { + log.Panic(err) + } + } + + return nil + }) + Handle(err) +} + +func (u *UTXOSet) DeleteByPrefix(prefix []byte) { + deleteKeys := func(keysForDelete [][]byte) error { + if err := u.Blockchain.Database.Update(func(txn *badger.Txn) error { + for _, key := range keysForDelete { + if err := txn.Delete(key); err != nil { + return err + } + } + return nil + }); err != nil { + return err + } + return nil + } + + collectSize := 100000 + u.Blockchain.Database.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchValues = false + it := txn.NewIterator(opts) + defer it.Close() + + keysForDelete := make([][]byte, 0, collectSize) + keysCollected := 0 + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + key := it.Item().KeyCopy(nil) + keysForDelete = append(keysForDelete, key) + keysCollected++ + if keysCollected == collectSize { + if err := deleteKeys(keysForDelete); err != nil { + log.Panic(err) + } + keysForDelete = make([][]byte, 0, collectSize) + keysCollected = 0 + } + } + if keysCollected > 0 { + if err := deleteKeys(keysForDelete); err != nil { + log.Panic(err) + } + } + return nil + }) +} From 16414379fdc9d43d0a10cdb8f6e45b953a5771af Mon Sep 17 00:00:00 2001 From: tensor-programming Date: Sat, 5 Oct 2019 19:41:24 -0400 Subject: [PATCH 24/26] update to 1.13 --- blockchain/merkle.go | 24 +++++++++++------- blockchain/merkle_test.go | 51 +++++++++++++++++++++++++++++++++++++++ cli/cli.go | 26 ++++++++++---------- go.mod | 21 ++++++++-------- 4 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 blockchain/merkle_test.go diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 33c11150..142be7fa 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -1,6 +1,9 @@ package blockchain -import "crypto/sha256" +import ( + "crypto/sha256" + "log" +) type MerkleTree struct { RootNode *MerkleNode @@ -33,20 +36,23 @@ func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { func NewMerkleTree(data [][]byte) *MerkleTree { var nodes []MerkleNode - if len(data)%2 != 0 { - data = append(data, data[len(data)-1]) - } - for _, dat := range data { node := NewMerkleNode(nil, nil, dat) nodes = append(nodes, *node) } - for i := 0; i < len(data)/2; i++ { - var level []MerkleNode + if len(nodes) == 0 { + log.Panic("No merkel nodes") + } - for j := 0; j < len(nodes); j += 2 { - node := NewMerkleNode(&nodes[j], &nodes[j+1], nil) + for len(nodes) > 1 { + if len(nodes)%2 != 0 { + nodes = append(nodes, nodes[len(nodes)-1]) + } + + var level []MerkleNode + for i := 0; i < len(nodes); i += 2 { + node := NewMerkleNode(&nodes[i], &nodes[i+1], nil) level = append(level, *node) } diff --git a/blockchain/merkle_test.go b/blockchain/merkle_test.go new file mode 100644 index 00000000..9eb01b15 --- /dev/null +++ b/blockchain/merkle_test.go @@ -0,0 +1,51 @@ +package blockchain + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewMerkleNode(t *testing.T) { + data := [][]byte{ + []byte("node1"), + []byte("node2"), + []byte("node3"), + []byte("node4"), + []byte("node5"), + []byte("node6"), + []byte("node7"), + } + + // level 1 + mn1 := NewMerkleNode(nil, nil, data[0]) + mn2 := NewMerkleNode(nil, nil, data[1]) + mn3 := NewMerkleNode(nil, nil, data[2]) + mn4 := NewMerkleNode(nil, nil, data[3]) + mn5 := NewMerkleNode(nil, nil, data[4]) + mn6 := NewMerkleNode(nil, nil, data[5]) + mn7 := NewMerkleNode(nil, nil, data[6]) + mn8 := NewMerkleNode(nil, nil, data[6]) + + // level 2 + mn9 := NewMerkleNode(mn1, mn2, nil) + mn10 := NewMerkleNode(mn3, mn4, nil) + mn11 := NewMerkleNode(mn5, mn6, nil) + mn12 := NewMerkleNode(mn7, mn8, nil) + + //level 3 + + mn13 := NewMerkleNode(mn9, mn10, nil) + mn14 := NewMerkleNode(mn11, mn12, nil) + + //level 4 + + mn15 := NewMerkleNode(mn13, mn14, nil) + + root := fmt.Sprintf("%x", mn15.Data) + tree := NewMerkleTree(data) + + assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") + +} diff --git a/cli/cli.go b/cli/cli.go index d877844e..8d405077 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -9,8 +9,8 @@ import ( "strconv" "github.com/tensor-programming/golang-blockchain/blockchain" - "github.com/tensor-programming/golang-blockchain/wallet" "github.com/tensor-programming/golang-blockchain/network" + "github.com/tensor-programming/golang-blockchain/wallet" ) type CommandLine struct{} @@ -40,9 +40,9 @@ func (cli *CommandLine) StartNode(nodeID, minerAddress string) { if len(minerAddress) > 0 { if wallet.ValidateAddress(minerAddress) { fmt.Println("Mining is on. Address to receive rewards: ", minerAddress) - } else { - log.Panic("Wrong miner address!") - } + } else { + log.Panic("Wrong miner address!") + } } network.StartServer(nodeID, minerAddress) } @@ -64,6 +64,7 @@ func (cli *CommandLine) listAddresses(nodeID string) { for _, address := range addresses { fmt.Println(address) } + } func (cli *CommandLine) createWallet(nodeID string) { @@ -99,20 +100,20 @@ func (cli *CommandLine) printChain(nodeID string) { func (cli *CommandLine) createBlockChain(address, nodeID string) { if !wallet.ValidateAddress(address) { - log.Panic("Address is not Valid") + log.Panic("Address is not Valid") } chain := blockchain.InitBlockChain(address, nodeID) defer chain.Database.Close() UTXOSet := blockchain.UTXOSet{chain} UTXOSet.Reindex() - + fmt.Println("Finished!") } func (cli *CommandLine) getBalance(address, nodeID string) { if !wallet.ValidateAddress(address) { - log.Panic("Address is not Valid") + log.Panic("Address is not Valid") } chain := blockchain.ContinueBlockChain(nodeID) UTXOSet := blockchain.UTXOSet{chain} @@ -120,7 +121,7 @@ func (cli *CommandLine) getBalance(address, nodeID string) { balance := 0 pubKeyHash := wallet.Base58Decode([]byte(address)) - pubKeyHash = pubKeyHash[1 : len(pubKeyHash) - 4] + pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4] UTXOs := UTXOSet.FindUnspentTransactions(pubKeyHash) for _, out := range UTXOs { @@ -132,10 +133,10 @@ func (cli *CommandLine) getBalance(address, nodeID string) { func (cli *CommandLine) send(from, to string, amount int, nodeID string, mineNow bool) { if !wallet.ValidateAddress(to) { - log.Panic("Address is not Valid") + log.Panic("Address is not Valid") } if !wallet.ValidateAddress(from) { - log.Panic("Address is not Valid") + log.Panic("Address is not Valid") } chain := blockchain.ContinueBlockChain(nodeID) UTXOSet := blockchain.UTXOSet{chain} @@ -157,7 +158,7 @@ func (cli *CommandLine) send(from, to string, amount int, nodeID string, mineNow network.SendTx(network.KnownNodes[0], tx) fmt.Println("send tx") } - + fmt.Println("Success!") } @@ -203,7 +204,7 @@ func (cli *CommandLine) Run() { if err != nil { log.Panic(err) } - case "startnode": + case "startnode": err := startNodeCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) @@ -281,4 +282,3 @@ func (cli *CommandLine) Run() { cli.StartNode(nodeID, *startNodeMiner) } } - diff --git a/go.mod b/go.mod index a9531075..f33cd6cc 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,16 @@ module github.com/tensor-programming/golang-blockchain require ( - github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 // indirect github.com/dgraph-io/badger v1.5.4 - github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect - github.com/golang/protobuf v1.2.0 // indirect - github.com/mr-tron/base58 v1.1.0 - github.com/pkg/errors v0.8.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.1 // indirect - github.com/stretchr/testify v1.2.2 - golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 - golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect + github.com/dgryski/go-farm v0.0.0-20190323171310-30f6f3c2b8f8 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/mr-tron/base58 v1.1.1 + github.com/pkg/errors v0.8.1 // indirect + github.com/stretchr/testify v1.3.0 + golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 + golang.org/x/net v0.0.0-20190322120337-addf6b3196f6 // indirect gopkg.in/vrecan/death.v3 v3.0.1 ) + +go 1.13 From b673e7de25a6d8cea0c1becfd41288895d811a35 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Sat, 5 Oct 2019 20:14:58 -0400 Subject: [PATCH 25/26] Update merkle.go --- blockchain/merkle.go | 68 -------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 41ad7760..142be7fa 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -1,4 +1,3 @@ -<<<<<<< HEAD package blockchain import ( @@ -64,70 +63,3 @@ func NewMerkleTree(data [][]byte) *MerkleTree { return &tree } -======= -package blockchain - -import ( - "crypto/sha256" - "log" -) - -type MerkleTree struct { - RootNode *MerkleNode -} - -type MerkleNode struct { - Left *MerkleNode - Right *MerkleNode - Data []byte -} - -func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { - node := MerkleNode{} - - if left == nil && right == nil { - hash := sha256.Sum256(data) - node.Data = hash[:] - } else { - prevHashes := append(left.Data, right.Data...) - hash := sha256.Sum256(prevHashes) - node.Data = hash[:] - } - - node.Left = left - node.Right = right - - return &node -} - -func NewMerkleTree(data [][]byte) *MerkleTree { - var nodes []MerkleNode - - for _, dat := range data { - node := NewMerkleNode(nil, nil, dat) - nodes = append(nodes, *node) - } - - if len(nodes) == 0 { - log.Panic("No merkel nodes") - } - - for len(nodes) > 1 { - if len(nodes)%2 != 0 { - nodes = append(nodes, nodes[len(nodes)-1]) - } - - var level []MerkleNode - for i := 0; i < len(nodes); i += 2 { - node := NewMerkleNode(&nodes[i], &nodes[i+1], nil) - level = append(level, *node) - } - - nodes = level - } - - tree := MerkleTree{&nodes[0]} - - return &tree -} ->>>>>>> e14978d1b38f52129396238a7d4d8709d0dbb115 From 6180fe239312d2c0d3045b68f99d17122f442746 Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Sat, 5 Oct 2019 20:15:29 -0400 Subject: [PATCH 26/26] Update merkle_test.go --- blockchain/merkle_test.go | 53 --------------------------------------- 1 file changed, 53 deletions(-) diff --git a/blockchain/merkle_test.go b/blockchain/merkle_test.go index ba17fc31..ae9f566f 100644 --- a/blockchain/merkle_test.go +++ b/blockchain/merkle_test.go @@ -1,4 +1,3 @@ -<<<<<<< HEAD package blockchain import ( @@ -50,56 +49,4 @@ func TestNewMerkleNode(t *testing.T) { assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") } -======= -package blockchain - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewMerkleNode(t *testing.T) { - data := [][]byte{ - []byte("node1"), - []byte("node2"), - []byte("node3"), - []byte("node4"), - []byte("node5"), - []byte("node6"), - []byte("node7"), - } - - // level 1 - mn1 := NewMerkleNode(nil, nil, data[0]) - mn2 := NewMerkleNode(nil, nil, data[1]) - mn3 := NewMerkleNode(nil, nil, data[2]) - mn4 := NewMerkleNode(nil, nil, data[3]) - mn5 := NewMerkleNode(nil, nil, data[4]) - mn6 := NewMerkleNode(nil, nil, data[5]) - mn7 := NewMerkleNode(nil, nil, data[6]) - mn8 := NewMerkleNode(nil, nil, data[6]) - // level 2 - mn9 := NewMerkleNode(mn1, mn2, nil) - mn10 := NewMerkleNode(mn3, mn4, nil) - mn11 := NewMerkleNode(mn5, mn6, nil) - mn12 := NewMerkleNode(mn7, mn8, nil) - - //level 3 - - mn13 := NewMerkleNode(mn9, mn10, nil) - mn14 := NewMerkleNode(mn11, mn12, nil) - - //level 4 - - mn15 := NewMerkleNode(mn13, mn14, nil) - - root := fmt.Sprintf("%x", mn15.Data) - tree := NewMerkleTree(data) - - assert.Equal(t, root, fmt.Sprintf("%x", tree.RootNode.Data), "Merkle node root has is equal") - -} ->>>>>>> e14978d1b38f52129396238a7d4d8709d0dbb115