     1	/*
     2	  My Block Chain: main
     3	*/
     4	package main
     5	
     6	import (
     7		"flag"
     8		"fmt"
     9		"github.com/labstack/echo"
    10		"github.com/labstack/echo/middleware"
    11		"net/http"
    12		"strconv"
    13	
    14		"./Block"
    15		"./P2P"
    16	)
    17	
    18	const (
    19		HOST     = "127.0.0.1"
    20		API_PORT = 3000
    21		P2P_PORT = 4000
    22	
    23		INIT            = "/init/"
    24		BLOCKLIST       = "/blocks"
    25		BLOCK           = "/block/"
    26		NODELIST        = "/nodes"
    27		NODE            = "/node/"
    28		MALICIOUS_BLOCK = "/malicious_block/"
    29	
    30		debug_mode = false
    31	)
    32	
    33	var (
    34		p2p *P2P.P2PNetwork
    35		bc  *Block.BlockChain
    36	)
    37	
    38	// ブロック一覧取得
    39	func listBlocks(c echo.Context) error {
    40		fmt.Println("listBlocks:")
    41		blocks := bc.ListBlock()
    42		return c.JSON(http.StatusOK, blocks)
    43	}
    44	
    45	// 特定のブロックの内容を取得
    46	func getBlock(c echo.Context) error {
    47		id := c.Param("id")
    48		fmt.Println("getBlock: ", id)
    49	
    50		// ブロック検索
    51		// データの中身で検索
    52		block := bc.GetBlockByData([]byte(id))
    53		if block != nil {
    54			return c.JSON(http.StatusOK, block)
    55		}
    56		// indexで検索
    57		index, err := strconv.Atoi(id)
    58		if err == nil {
    59			block := bc.GetBlockByIndex(index)
    60			if block != nil {
    61				return c.JSON(http.StatusOK, block)
    62			}
    63		}
    64		// ハッシュで検索
    65		block = bc.GetBlock(id)
    66		if block != nil {
    67			return c.JSON(http.StatusOK, block)
    68		}
    69	
    70		return echo.NewHTTPError(http.StatusNotFound, "Block is not found.id="+id)
    71	}
    72	
    73	// ネットワークに接続しているサーバ一覧を取得
    74	func listNodes(c echo.Context) error {
    75		fmt.Println("listNodes:")
    76		nodes := p2p.List()
    77		return c.JSON(http.StatusOK, nodes)
    78	}
    79	
    80	type Data struct {
    81		Data string `json:"data"`
    82	}
    83	
    84	// ブロックに記録するデータを渡し、ブロック作成を依頼する
    85	func createBlock(c echo.Context) error {
    86		fmt.Println("createBlock:")
    87	
    88		if bc.IsMining() {
    89			// マイニングは同時実行しない
    90			return echo.NewHTTPError(http.StatusConflict, "Already Mining")
    91		}
    92	
    93		data := new(Data)
    94		err := c.Bind(data)
    95		if err != nil {
    96			fmt.Println(err)
    97			return echo.NewHTTPError(http.StatusBadRequest, "Invalid data info.")
    98		}
    99		// データ保存処理
   100		bc.SaveData([]byte(data.Data))
   101	
   102		return c.NoContent(http.StatusOK)
   103	}
   104	
   105	// ネットワークにサーバを追加
   106	func addNode(c echo.Context) error {
   107		fmt.Println("addNode:")
   108	
   109		node := new(P2P.Node)
   110	
   111		// サーバ情報取得(JOSN形式のリクエストから情報を取り出す)
   112		err := c.Bind(node)
   113		if err != nil {
   114			fmt.Println(err)
   115			return echo.NewHTTPError(http.StatusBadRequest, "Invalid server info.")
   116		}
   117	
   118		// サーバ追加
   119		id, err := p2p.Add(node)
   120		if debug_mode {
   121			fmt.Println(id)
   122			fmt.Println(err)
   123		}
   124	
   125		return c.NoContent(http.StatusOK)
   126	}
   127	
   128	// ブロックチェーンの初期化
   129	func initBlockChain(c echo.Context) error {
   130		id := c.Param("id")
   131		fmt.Println("initBlockChain: ", id)
   132	
   133		index, err := strconv.Atoi(id)
   134		if err != nil {
   135			return echo.NewHTTPError(http.StatusBadRequest, "Invalid Hight.")
   136		}
   137		bc.SyncBlockChain(index)
   138	
   139		return c.NoContent(http.StatusOK)
   140	}
   141	
   142	type BlockModify struct {
   143		Hight int    `json:"hight"`
   144		Data  string `json:"data"`
   145	}
   146	
   147	// データの書き換え
   148	func maliciousBlock(c echo.Context) error {
   149		fmt.Println("maliciousBlock:")
   150	
   151		data := new(BlockModify)
   152	
   153		// リクエストデータを取得(JOSN形式のリクエストから情報を取り出す)
   154		err := c.Bind(data)
   155		if err != nil {
   156			return echo.NewHTTPError(http.StatusBadRequest, "Invalid data.")
   157		}
   158	
   159		bc.Modify(data.Hight, data.Data)
   160	
   161		return c.NoContent(http.StatusOK)
   162	}
   163	
   164	// バージョン番号を返す
   165	func requestHandler(c echo.Context) error {
   166		return c.String(http.StatusOK, "My Block Chain Ver0.1")
   167	}
   168	
   169	// メイン処理
   170	func main() {
   171	
   172		// オプションの解析
   173		apiport := flag.Int("apiport", API_PORT, "API port number")
   174		p2pport := flag.Int("p2pport", P2P_PORT, "P2P port number")
   175		host := flag.String("host", HOST, "p2p port number")
   176		first := flag.Bool("first", false, "first server")
   177		flag.Parse()
   178	
   179		api_port := uint16(*apiport)
   180		p2p_port := uint16(*p2pport)
   181		my_host := *host
   182	
   183		fmt.Println("HOST:", my_host)
   184		fmt.Println("API port:", api_port)
   185		fmt.Println("P2P port:", p2p_port)
   186	
   187		// P2Pモジュールの初期化
   188		p2p = new(P2P.P2PNetwork)
   189		_, err := p2p.Init(my_host, api_port, p2p_port)
   190		if err == nil {
   191			fmt.Println("P2P module initialized.")
   192		} else {
   193			fmt.Println(err)
   194			return
   195		}
   196	
   197		// Block Chainモジュールの初期化
   198		bc = new(Block.BlockChain)
   199		_, err = bc.Init(p2p, true) // 本当は１つ目のサーバのみtrueで他のものはfalse、そしてgenesisブロックも転送が必要
   200		if err == nil {
   201			fmt.Println("Block Chain module initialized.")
   202		} else {
   203			fmt.Println(err)
   204			return
   205		}
   206		if *first {
   207			bc.Initialized()
   208		}
   209		if debug_mode {
   210			fmt.Println(p2p)
   211			fmt.Println(bc)
   212		}
   213	
   214		// アクション登録
   215		p2p.SetAction(P2P.CMD_NEWBLOCK, bc.NewBlock)
   216		p2p.SetAction(P2P.CMD_ADDSRV, p2p.AddSrv)
   217		p2p.SetAction(P2P.CMD_SENDBLOCK, bc.SendBlock)
   218		p2p.SetAction(P2P.CMD_MININGBLOCK, bc.MiningBlock)
   219		p2p.SetAction(P2P.CMD_MODIFYDATA, bc.ModifyData)
   220	
   221		// Echoセットアップ
   222		e := echo.New()
   223	
   224		// アクセスログの設定
   225		e.Use(middleware.Logger())
   226	
   227		// エラー発生時の対処設定
   228		e.Use(middleware.Recover())
   229	
   230		// ブラウザからjavascriptを使ってAPI呼び出しできるようにCORS対応
   231		e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
   232			AllowOrigins: []string{"*"},
   233			AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE, echo.HEAD},
   234		}))
   235	
   236		// リクエストハンドラ登録
   237		e.GET("/", requestHandler)
   238		e.GET(BLOCKLIST, listBlocks)
   239		e.GET(BLOCK+":id", getBlock)
   240		e.POST(BLOCK, createBlock)
   241		e.GET(NODELIST, listNodes)
   242		e.POST(NODE, addNode)
   243		e.PUT(NODE, addNode)
   244		e.POST(MALICIOUS_BLOCK, maliciousBlock)
   245	
   246		e.POST(INIT+":id", initBlockChain)
   247	
   248		// サーバの起動
   249		e.Logger.Fatal(e.Start(my_host + ":" + strconv.FormatInt(int64(api_port), 10)))
   250	}
