- 取得連結
- X
- 以電子郵件傳送
- 其他應用程式
程式語言:Go
GitHub
簡介:用 go 重新實現
yeasy/blockchain_guide
Code a simple P2P blockchain in Go!
izqui/blockchain
bitcoinbook/bitcoinbook
- Package:
- flag
- net/http
- crypto/sha256
- encoding/json
GitHub
簡介:用 go 重新實現
程式碼
資料夾架構
web.go
blockchain.go
|--web.go |--core |---blockchain.go
web.go
- package main
- import (
- "encoding/json"
- "flag"
- "fmt"
- "log"
- "net/http"
- "strconv"
- "./core"
- "github.com/pkg/errors"
- )
- // Instantiate the Blockchain
- var blockchain = core.NewBlockchain()
- func main() {
- host := flag.String("host", "127.0.0.1", "port to listen on")
- port := flag.String("port", "6060", "port number")
- flag.Parse()
- mux := http.NewServeMux()
- mux.HandleFunc("/chain/", fullChain)
- mux.HandleFunc("/transactions/new", newTransactions)
- mux.HandleFunc("/mine/", mine)
- mux.HandleFunc("/nodes/register", registerNodes)
- mux.HandleFunc("/nodes/resolve", consensus)
- addr := *host + ":" + *port
- log.Println("Listening on ", addr)
- err := http.ListenAndServe(addr, mux)
- if err != nil {
- log.Fatal("ListenAndServe: ", err)
- }
- }
- func fullChain(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodGet {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(blockchain)
- }
- func newTransactions(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodPost {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- if err := req.ParseForm(); err != nil {
- fmt.Fprintf(w, "error:%s", err)
- return
- }
- form := make(map[string]string)
- for _, k := range []string{"sender", "recipient", "amount"} {
- v, ok := req.Form[k]
- if !ok {
- fmt.Fprintf(w, "Missing %s\n", k)
- continue
- }
- form[k] = v[0]
- }
- amount, err := strconv.ParseFloat(form["amount"], 64)
- if err != nil {
- fmt.Fprintf(w, "amount:%s is not a number", form["amount"])
- return
- }
- blockchain.NewTransaction(form["sender"], form["recipient"], amount)
- fmt.Fprintf(w, "CurrentTransactions: %s", blockchain.CurrentTransactions)
- }
- func mine(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodGet {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- blockchain.NewBlock()
- fmt.Fprintf(w, "Chain: %s", blockchain.Chain)
- }
- func registerNodes(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodPost {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- if err := req.ParseForm(); err != nil {
- fmt.Fprintf(w, "error:%s", err)
- return
- }
- addrs, ok := req.Form["addr"]
- if !ok {
- fmt.Fprintln(w, "Missing addr")
- return
- }
- for _, addr := range addrs {
- blockchain.RegisterNode(addr)
- }
- for node := range blockchain.Nodes {
- fmt.Fprintf(w, "%s,\n", node)
- }
- }
- func consensus(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodGet {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- newBlockchain := &core.Blockchain{}
- for k := range blockchain.Nodes {
- s := k + "/chain/"
- log.Printf("update by %s\n", s)
- err := getJSON(s, newBlockchain)
- if err != nil {
- log.Printf("getJSON Error: %s\n", err)
- continue
- }
- changed, err := blockchain.ResolveConflicts(newBlockchain)
- if err != nil {
- log.Printf("blockchain.ResolveConflicts Error: %s\n", err)
- continue
- }
- if changed {
- fmt.Fprintln(w, "updated")
- }
- }
- fmt.Fprintf(w, "chain: %s", blockchain.Chain)
- }
- func getJSON(url string, target interface{}) error {
- r, err := http.Get(url)
- if err != nil {
- return errors.Wrapf(err, "http.Get(%s)", url)
- }
- defer r.Body.Close()
- return json.NewDecoder(r.Body).Decode(target)
- }
blockchain.go
- package core
- import (
- "crypto/sha256"
- "encoding/json"
- "fmt"
- "time"
- "github.com/pkg/errors"
- )
- // Transaction 交易記錄
- type Transaction struct {
- Sender string `json:"sender,omitempty"`
- Recipient string `json:"recipient,omitempty"`
- Amount float64 `json:"amount,omitempty"`
- }
- // Block 區塊
- type Block struct {
- Index int `json:"index,omitempty"`
- Timestamp time.Time `json:"timestamp,omitempty"`
- Transactions []*Transaction `json:"transactions,omitempty"`
- Proof int `json:"proof,omitempty"`
- PreviousHash string `json:"previous_hash,omitempty"`
- }
- // Blockchain 區塊鍊
- type Blockchain struct {
- Chain []*Block `json:"chain,omitempty"`
- Nodes map[string]bool `json:"nodes,omitempty"`
- CurrentTransactions []*Transaction `json:"current_transactions,omitempty"`
- }
- // NewBlockchain create Blockchain
- func NewBlockchain() *Blockchain {
- bc := &Blockchain{}
- bc.Nodes = make(map[string]bool)
- // Create the genesis block
- bc.NewBlock()
- return bc
- }
- // NewBlock 建立新的區塊
- func (bc *Blockchain) NewBlock() error {
- var previousHash string
- var err error
- if len(bc.Chain) == 0 {
- previousHash = "1"
- } else {
- previousHash, err = bc.Chain[len(bc.Chain)-1].hash()
- if err != nil {
- return errors.Wrap(err, "bc.Chain[len(bc.Chain)-1].hash")
- }
- }
- b := &Block{
- Index: len(bc.Chain) + 1,
- Timestamp: time.Now(),
- Transactions: bc.CurrentTransactions,
- PreviousHash: previousHash,
- }
- b.proofWork()
- bc.CurrentTransactions = []*Transaction{}
- bc.Chain = append(bc.Chain, b)
- return nil
- }
- //NewTransaction 建立交易
- func (bc *Blockchain) NewTransaction(sender, recipient string, amount float64) {
- t := &Transaction{
- Sender: sender,
- Recipient: recipient,
- Amount: amount,
- }
- bc.CurrentTransactions = append(bc.CurrentTransactions, t)
- }
- //RegisterNode 註冊節點,節點用來更新區塊鍊
- func (bc *Blockchain) RegisterNode(address string) {
- node := address
- bc.Nodes[node] = true
- }
- //validChain 驗證區塊鍊
- func (bc *Blockchain) validChain() (bool, error) {
- var previousHash string
- for i, b := range bc.Chain {
- ok, err := b.validProof()
- if err != nil {
- return false, errors.Wrap(err, "b.validProof")
- }
- if !ok {
- return false, nil
- }
- if i == 0 {
- previousHash, err = b.hash()
- if err != nil {
- return false, errors.Wrap(err, "b.hash")
- }
- continue
- }
- if previousHash != b.PreviousHash {
- return false, nil
- }
- previousHash, err = b.hash()
- if err != nil {
- return false, errors.Wrap(err, "b.hash")
- }
- }
- return true, nil
- }
- //ResolveConflicts 與其他節點比對,確認目前正確的區塊鍊
- func (bc *Blockchain) ResolveConflicts(newbc *Blockchain) (bool, error) {
- ok, err := newbc.validChain()
- if err != nil {
- return false, errors.Wrap(err, "newbc.validChain")
- }
- if ok && len(newbc.Chain) > len(bc.Chain) {
- bc.Chain = make([]*Block, len(newbc.Chain))
- copy(bc.Chain, newbc.Chain)
- return true, nil
- }
- return false, nil
- }
- func (bc *Blockchain) String() string {
- return fmt.Sprintf("chain:%+v\nnodes:%+v\ncurrentTransactions:%+v", bc.Chain, bc.Nodes, bc.CurrentTransactions)
- }
- // hash sha256
- func (b *Block) hash() (string, error) {
- s, err := json.Marshal(b)
- if err != nil {
- return "", errors.Wrap(err, "json.Marshal")
- }
- sum := sha256.Sum256(s)
- return fmt.Sprintf("%x", sum), nil
- }
- // 超簡單驗證
- func (b *Block) validProof() (bool, error) {
- h, err := b.hash()
- if err != nil {
- return false, errors.Wrap(err, "b.hash")
- }
- return h[:1] == "0", nil
- }
- //proofWork 挖礦工作
- func (b *Block) proofWork() error {
- b.Proof = 0
- for {
- ok, err := b.validProof()
- if err != nil {
- return errors.Wrap(err, "b.validProof")
- }
- if ok {
- break
- }
- b.Proof++
- }
- return nil
- }
- func (b *Block) String() string {
- return fmt.Sprintf("{\n#%d %s\ntransactions:%+v\nproof:%d\nprevious Hash:%s\n}", b.Index,
- b.Timestamp,
- b.Transactions,
- b.Proof,
- b.PreviousHash,
- )
- }
- func (t *Transaction) String() string {
- return fmt.Sprintf("{s:%s, r:%s, $%.2f}", t.Sender, t.Recipient, t.Amount)
- }
參考
My Blockchain in Goyeasy/blockchain_guide
Code a simple P2P blockchain in Go!
izqui/blockchain
bitcoinbook/bitcoinbook
留言
張貼留言