├── Blockchain.png ├── blockchain.pptx ├── README.md ├── core ├── blockchain_test.go ├── crypto.go ├── consensus.go └── blockchain.go └── main.go /Blockchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suyuanhxx/blockchain/HEAD/Blockchain.png -------------------------------------------------------------------------------- /blockchain.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suyuanhxx/blockchain/HEAD/blockchain.pptx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blockchain 2 | 1. 区块链相关数据结构 3 | 2. “创世区块”的创建 4 | 3. 定义新建区块,交易,注册节点方法 5 | 4. 定义简易`pow`,`hash`方法 6 | 5. 创建http server服务 7 | 6. 共识算法,冲突链 -------------------------------------------------------------------------------- /core/blockchain_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | func Test_RegisterNode(t *testing.T) { 9 | blockchain := new(Blockchain) 10 | blockchain.RegisterNode("1111") 11 | } 12 | 13 | func Test_Pow(t *testing.T) { 14 | n := Pow() 15 | fmt.Print(n) 16 | } 17 | -------------------------------------------------------------------------------- /core/crypto.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/hex" 5 | "crypto/sha256" 6 | "encoding/json" 7 | ) 8 | 9 | var hash256 = sha256.New() 10 | 11 | func Hash(block Block) string { 12 | blockString, _ := json.Marshal(block) 13 | return Sha256(string(blockString)) 14 | } 15 | 16 | func Sha256(str string) string { 17 | hash256.Write([]byte(str)) 18 | return hex.EncodeToString(hash256.Sum(nil)) 19 | } 20 | -------------------------------------------------------------------------------- /core/consensus.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "time" 5 | "math" 6 | "fmt" 7 | "strconv" 8 | ) 9 | 10 | func (b *Blockchain) Pow(lashBlock Block) int { 11 | lastProof := lashBlock.Proof 12 | lastHash := Hash(lashBlock) 13 | proof := 0 14 | for !b.ValidProof(lastProof, proof, lastHash) { 15 | proof += 1 16 | } 17 | return proof 18 | } 19 | 20 | /** 21 | Meet the requirements proof constituted by a string that start with n prefix '0' 22 | */ 23 | func (b *Blockchain) ValidProof(lastProof int, proof int, lastHash string) bool { 24 | guess := string(lastProof) + string(proof) + lastHash 25 | guessHash := Sha256(guess) 26 | return guessHash[:4] == "0000" 27 | } 28 | 29 | func Pow() int { 30 | timestamp, message := time.Now().String(), "This is a random message." 31 | nonce, guess, throttle := 0, 99999999, 100000 32 | 33 | payload := timestamp + message 34 | target := int(math.Pow(2, 6)) / throttle 35 | payloadHash := Sha256(payload) 36 | 37 | start := time.Now() 38 | for guess > target { 39 | nonce += 1 40 | str := Sha256(Sha256(string(nonce) + payloadHash))[0:8] 41 | guess, _ = strconv.Atoi(str) 42 | fmt.Println(guess) 43 | } 44 | end := time.Now() 45 | fmt.Println("consensus takes time is: ", end.Sub(start)) 46 | return guess 47 | } 48 | 49 | func Pos() { 50 | 51 | } 52 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/satori/go.uuid" 5 | "strings" 6 | . "./core" 7 | "time" 8 | "net/http" 9 | "log" 10 | "encoding/json" 11 | ) 12 | 13 | type Response struct { 14 | Message string `json:"message"` 15 | Index int `json:"index"` 16 | Transaction []Transaction `json:"transaction"` 17 | Proof int `json:"proof"` 18 | PreviousHash string `json:"previousHash"` 19 | Chain []Block `json:"chain,omitempty"` 20 | NewChain []Block `json:"newChain,omitempty"` 21 | TotalNodes []interface{} `json:"totalNodes,omitempty"` 22 | } 23 | 24 | var ud, _ = uuid.NewV1() 25 | var nodeIdentifier = strings.Replace(ud.String(), "-", "", -1) 26 | 27 | var blockchain *Blockchain 28 | 29 | func mine(w http.ResponseWriter, r *http.Request) { 30 | lastBlock := blockchain.LastBlock() 31 | proof := blockchain.Pow(lastBlock) 32 | 33 | blockchain.NewTransaction("0", nodeIdentifier, 1) 34 | 35 | // Forge the new Block by adding it to the chain 36 | previousHash := Hash(lastBlock) 37 | block := blockchain.NewBlock(proof, previousHash) 38 | 39 | response := Response{Message: "New Block Forged", 40 | Index: block.Index, Transaction: block.Transactions, 41 | Proof: block.Proof, PreviousHash: block.PreviousHash} 42 | 43 | resp, _ := json.Marshal(response) 44 | 45 | w.Write(resp) 46 | 47 | } 48 | 49 | func NewTransaction(sender string, recipient string, amount int) *Response { 50 | index := blockchain.NewTransaction(sender, recipient, amount) 51 | response := Response{Message: "Transaction will be added to Block " + string(index), Index: index} 52 | return &response 53 | } 54 | 55 | func fullChain(w http.ResponseWriter, r *http.Request) { 56 | chain := new(Chain) 57 | chain.Chain = blockchain.Chain 58 | chain.Length = len(blockchain.Chain) 59 | 60 | resp, _ := json.Marshal(chain) 61 | w.Write(resp) 62 | } 63 | 64 | func RegisterNodes(nodes []string) *Response { 65 | if len(nodes) <= 0 { 66 | return nil 67 | } 68 | 69 | for _, node := range nodes { 70 | blockchain.RegisterNode(node) 71 | } 72 | 73 | response := Response{Message: "New nodes have been added", 74 | TotalNodes: blockchain.Nodes.ToSlice()} 75 | return &response 76 | } 77 | 78 | func Consensus() *Response { 79 | replaced := blockchain.ResolveConflicts() 80 | 81 | response := new(Response) 82 | if replaced { 83 | response.Message = "Our chain was replaced" 84 | response.NewChain = blockchain.Chain 85 | } else { 86 | response.Message = "Our chain is authoritative" 87 | response.Chain = blockchain.Chain 88 | } 89 | return response 90 | } 91 | 92 | type HandlersFunc func(http.ResponseWriter, *http.Request) 93 | 94 | var handlersMap = make(map[string]HandlersFunc) 95 | 96 | type Server struct { 97 | } 98 | 99 | func (*Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 100 | if h, ok := handlersMap[r.URL.String()]; ok { 101 | h(w, r) 102 | } 103 | } 104 | 105 | func main() { 106 | blockchain = blockchain.New() 107 | 108 | server := &http.Server{ 109 | Addr: ":8000", 110 | Handler: &Server{}, 111 | ReadTimeout: 10 * time.Second, 112 | WriteTimeout: 10 * time.Second, 113 | MaxHeaderBytes: 1 << 20, 114 | } 115 | 116 | handlersMap["/mine"] = mine 117 | handlersMap["/chain"] = fullChain 118 | log.Fatal(server.ListenAndServe()) 119 | 120 | } 121 | -------------------------------------------------------------------------------- /core/blockchain.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | "fmt" 7 | "net/http" 8 | "github.com/deckarep/golang-set" 9 | ) 10 | 11 | type Block struct { 12 | Index int `json:"index"` 13 | Timestamp string `json:"timestamp"` 14 | Transactions []Transaction `json:"transactions"` 15 | Proof int `json:"proof"` 16 | PreviousHash string `json:"previous_hash"` 17 | } 18 | 19 | type Transaction struct { 20 | Amount int `json:"amount"` 21 | Recipient string `json:"recipient"` 22 | Sender string `json:"sender"` 23 | } 24 | 25 | type Chain struct { 26 | Chain []Block `json:"chain"` 27 | Length int `json:"length"` 28 | } 29 | 30 | type Blockchain struct { 31 | CurrentTransactions []Transaction 32 | Chain []Block 33 | Nodes mapset.Set 34 | } 35 | 36 | func (t *Blockchain) RegisterNode(host string) { 37 | if t.Nodes == nil { 38 | t.Nodes = mapset.NewSet() 39 | } 40 | t.Nodes.Add(host) 41 | } 42 | 43 | // Create the genesis block 44 | func (t *Blockchain) New() *Blockchain { 45 | t = new(Blockchain) 46 | t.NewBlock(100, "1") 47 | return t 48 | } 49 | 50 | func (t *Blockchain) ValidChain(chain []Block) bool { 51 | lastBlock := chain[0] 52 | currentIndex := 1 53 | 54 | for ; currentIndex < len(chain); { 55 | block := chain[currentIndex] 56 | fmt.Println(lastBlock, block) 57 | if block.PreviousHash != Hash(lastBlock) { 58 | return false 59 | } 60 | 61 | if !t.ValidProof(lastBlock.Proof, block.Proof, lastBlock.PreviousHash) { 62 | return false 63 | } 64 | lastBlock = block 65 | currentIndex += 1 66 | } 67 | return true 68 | } 69 | 70 | func (t *Blockchain) ResolveConflicts() bool { 71 | var newChain []Block 72 | 73 | maxLength := len(t.Chain) 74 | 75 | for node := range t.Nodes.Iter() { 76 | resp, error := http.Get("http://" + node.(string) + "/chain") 77 | if error != nil { 78 | continue 79 | } 80 | defer resp.Body.Close() 81 | var chain Chain 82 | json.NewDecoder(resp.Body).Decode(&chain) 83 | 84 | if chain.Length > maxLength && t.ValidChain(chain.Chain) { 85 | maxLength = chain.Length 86 | newChain = chain.Chain 87 | } 88 | } 89 | 90 | if newChain != nil { 91 | t.Chain = newChain 92 | return true 93 | } 94 | return false 95 | 96 | } 97 | 98 | func (t *Blockchain) NewBlock(proof int, previousHash string) Block { 99 | block := new(Block) 100 | block.Index = len(t.Chain) + 1 101 | block.Timestamp = time.Now().String() 102 | block.Proof = proof 103 | if previousHash != "" { 104 | block.PreviousHash = previousHash 105 | } else { 106 | block.PreviousHash = Hash(t.Chain[len(t.Chain)-1]) 107 | } 108 | 109 | if len(t.CurrentTransactions) != 0 { 110 | block.Transactions = append(block.Transactions, t.CurrentTransactions[0]) 111 | } 112 | t.CurrentTransactions = []Transaction{} 113 | t.Chain = append(t.Chain, *block) 114 | return *block 115 | } 116 | 117 | func (t *Blockchain) NewTransaction(sender string, recipient string, amount int) int { 118 | transaction := new(Transaction) 119 | transaction.Sender = sender 120 | transaction.Recipient = recipient 121 | transaction.Amount = amount 122 | t.CurrentTransactions = append(t.CurrentTransactions, *transaction) 123 | return t.LastBlock().Index + 1 124 | } 125 | 126 | func (t *Blockchain) LastBlock() Block { 127 | return t.Chain[len(t.Chain)-1] 128 | } --------------------------------------------------------------------------------