并不复杂
可以参考详细的解释
https://shusunny.github.io/sunnyblog/blockchain/simple-blockchain.html
package main import ( "crypto/md5" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "github.com/gorilla/mux" "io" "log" "net/http" "time" ) type Block struct { Pos int Data BookCheckout Timestamp string Hash string PrevHash string } type BookCheckout struct { BookID string `json:"book_id"` User string `json:"user"` CheckoutDate string `json:"checkout_date"` IsGenesis bool `json:"is_genesis"` } type Book struct { ID string `json:"id"` Title string `json:"title"` Author string `json:"author"` PublishDate string `json:"publish_date"` ISBN string `json:"isbn"` } func (b *Block) generateHash() { bytes, _ := json.Marshal(b.Data) data := string(b.Pos) + b.Timestamp + string(bytes) + b.PrevHash hash := sha256.New() hash.Write([]byte(data)) b.Hash = hex.EncodeToString(hash.Sum(nil)) } func CreateBlock(prevBlock *Block, checkoutItem BookCheckout) *Block { block := &Block{} block.Pos = prevBlock.Pos + 1 block.Timestamp = time.Now().String() block.Data = checkoutItem block.PrevHash = prevBlock.Hash block.generateHash() return block } type BlockChain struct { blocks []*Block } var Blockchain *BlockChain func (bc *BlockChain) AddBlock(data BookCheckout) { prevBlock := bc.blocks[len(bc.blocks) - 1] block := CreateBlock(prevBlock, data) // todo validate if validBlock(block, prevBlock) { bc.blocks = append(bc.blocks, block) } } func GenesisBlock() *Block { return CreateBlock(&Block{}, BookCheckout{IsGenesis:true}) } func NewBlockchain() *BlockChain { return &BlockChain{[]*Block{GenesisBlock()}} } func validBlock(block, prevBlock *Block) bool { if prevBlock.Hash != block.PrevHash { return false } if !block.validateHash(block.Hash) { return false } if prevBlock.Pos + 1 != block.Pos { return false } return true } func (b *Block) validateHash(hash string) bool { b.generateHash() if b.Hash != hash { return false } return true } func getBlockChain(w http.ResponseWriter, r *http.Request) { jbytes, err := json.MarshalIndent(Blockchain.blocks, "", "") if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(err) return } io.WriteString(w, string(jbytes)) } func writeBlock(w http.ResponseWriter, r *http.Request) { var checkoutItem BookCheckout if err := json.NewDecoder(r.Body).Decode(&checkoutItem); err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("could not write Block: %v", err) w.Write([]byte("could not write block")) return } Blockchain.AddBlock(checkoutItem) resp, err := json.MarshalIndent(checkoutItem, "", " ") if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("could not marshal payload: %v", err) w.Write([]byte("could not write block")) return } w.WriteHeader(http.StatusOK) w.Write(resp) } func newBook(w http.ResponseWriter, r *http.Request) { var book Book if err := json.NewDecoder(r.Body).Decode(&book); err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("could not create: %v", err) w.Write([]byte("could not create new Book")) return } h := md5.New() io.WriteString(h, book.ISBN + book.PublishDate) book.ID = fmt.Sprintf("%x", h.Sum(nil)) resp, err := json.MarshalIndent(book, "", " ") if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("could not marshal payload: %v", err) w.Write([]byte("could not save book data")) return } w.WriteHeader(http.StatusOK) w.Write(resp) } func main() { Blockchain = NewBlockchain() r := mux.NewRouter() r.HandleFunc("/", getBlockChain).Methods("GET") r.HandleFunc("/", writeBlock).Methods("POST") r.HandleFunc("/new", newBook).Methods("POST") go func() { for _, block := range Blockchain.blocks { fmt.Printf("Prev.hash: %x ", block.PrevHash) bytes, _ := json.MarshalIndent(block.Data, "", " ") fmt.Printf("Data: %v ", string(bytes)) fmt.Printf("Hash: %x ", block.Hash) fmt.Println() } }() log.Println("Listening on port 3000") log.Fatal(http.ListenAndServe(":3000", r)) }
end