├── .dockerignore ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── api ├── routes.go └── server.go ├── cmd ├── blocklator.go └── server │ └── main.go ├── docker-compose.yaml ├── docker └── Dockerfile ├── docs ├── block2.png ├── block3.png ├── ledger1.png └── ledger2.png ├── front ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── assets │ └── logo.png │ ├── common │ └── consts.js │ ├── components │ ├── BlockHead.vue │ ├── Cert.vue │ ├── GroupOrg.vue │ ├── HelloWorld.vue │ ├── Nav.vue │ └── Transaction.vue │ ├── main.js │ ├── router │ └── index.js │ └── views │ ├── About.vue │ ├── Block.vue │ ├── BlockList.vue │ ├── Config.vue │ ├── Home.vue │ ├── Ledger.vue │ └── Transaction.vue ├── go.mod ├── go.sum └── pkg ├── block ├── block.go ├── blockfile.go ├── cert.go ├── config.go ├── consts.go ├── ledger.go ├── ledgerutil.go ├── model.go ├── transaction.go └── txrwset.go └── store └── cache.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | 3 | front/node_modules 4 | npm-debug.log 5 | 6 | docs 7 | tmp 8 | ./docker -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [master,main] 6 | pull_request: 7 | branches: [master,main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build and Push the Docker image 16 | uses: elgohr/Publish-Docker-Github-Action@master 17 | with: 18 | name: tinywell/blocklator 19 | username: ${{ secrets.DOCKER_USERNAME }} 20 | password: ${{ secrets.DOCKER_PASSWORD }} 21 | dockerfile: ./docker/Dockerfile 22 | tag_names: true 23 | cache: true 24 | - name: executing remote ssh commands using key 25 | uses: appleboy/ssh-action@master 26 | with: 27 | host: ${{ secrets.REMOTE_HOST }} 28 | username: ${{ secrets.REMOTE_USERNAME }} 29 | key: ${{ secrets.REMOTE_SSH_KEY }} 30 | script: | 31 | cd blocklator && ./update.sh 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Editor directories and files 4 | .idea 5 | .vscode 6 | *.suo 7 | *.ntvs* 8 | *.njsproj 9 | *.sln 10 | *.sw? 11 | 12 | # backend 13 | tmp 14 | vendor 15 | 16 | # Binaries for programs and plugins 17 | *.exe 18 | *.exe~ 19 | *.dll 20 | *.so 21 | *.dylib 22 | 23 | # Test binary, build with `go test -c` 24 | *.test 25 | 26 | # Output of the go coverage tool, specifically when used with LiteIDE 27 | *.out 28 | 29 | *.log 30 | 31 | # front 32 | 33 | node_modules 34 | /dist 35 | 36 | # local env files 37 | .env.local 38 | .env.*.local 39 | 40 | # Log files 41 | npm-debug.log* 42 | yarn-debug.log* 43 | yarn-error.log* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 tinywell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: front 2 | front: 3 | cd ./front && npm run build 4 | 5 | .PHONY: backend 6 | backend: 7 | go build -v -o blocklator cmd/blocklator.go 8 | 9 | .PHONY: docker 10 | docker: 11 | docker build -f ./docker/Dockerfile -t tinywell/blocklator . 12 | 13 | .PHONY: start 14 | start: front backend 15 | ./blocklator server 16 | 17 | .PHONY: clean 18 | clean: 19 | rm -rf ./front/dist blocklator -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub forks](https://img.shields.io/github/forks/tinywell/blocklator)](https://github.com/tinywell/blocklator/network) 2 | [![GitHub stars](https://img.shields.io/github/stars/tinywell/blocklator)](https://github.com/tinywell/blocklator/stargazers) 3 | [![GitHub license](https://img.shields.io/github/license/tinywell/blocklator)](https://github.com/tinywell/blocklator) 4 | # blocklator 5 | 6 | 这是一个用于解析 hyperledger/fabric 区块链网络产生的区块和账本的 web 小工具。可以解析查看配置区块中的组织结构、共识参数等,也可以解析交易区块中各交易提案和回复的详细数据。还可以直接解析账本文件,提取出其中的所有区块并解析。 7 | 8 | 前端使用 vue,后端使用 gin 提供 RESTful API,并由 gin 代理前端编译后的静态网页文件。 9 | 10 | # 安装部署 11 | 12 | ## 获取源码 13 | 14 | ``` 15 | git clone https://github.com/tinywell/blocklator.git 16 | ``` 17 | 18 | ## 本地编译部署 19 | 20 | 本地编译需要有 Node 环境及 go 环境 21 | 22 | 编译: 23 | 24 | ``` 25 | cd blocklator 26 | make front 27 | make backend 28 | ``` 29 | 30 | 启动: 31 | 32 | ``` 33 | ./blocklator server 34 | ``` 35 | 36 | 或者 37 | 38 | ``` 39 | make start 40 | ``` 41 | 42 | ## docker 编译部署 43 | 44 | docker 编译部署需要有 docker 环境 45 | 46 | 编译: 47 | 48 | ``` 49 | cd blocklator 50 | make docker 51 | ``` 52 | 53 | 启动: 54 | 55 | ``` 56 | docker-compose up -d 57 | ``` 58 | 59 | # 使用说明 60 | 61 | ## 区块解析 62 | 63 | 区块解析可以直接拖拽或点击上传区块文件(比如通过 `peer channel fetch` 命令或其他方式从通道中获取的区块文件)进行解析,也可以直接粘贴经过 base64 编码的区块数据。 64 | ![img](./docs/block3.png) 65 | 66 | 比如交易区块,会解析出交易提案相关请求参数以及回复信息、背书信息等。 67 | ![img](./docs/block2.png) 68 | 69 | ## 账本解析 70 | 71 | 账本解析可以直接拖拽或上传通道账本(比如 peer 节点中维护的 blockfile_000000),解析出其中的所有区块,展示区块列表,并支持查看区块详情。 72 | ![img](./docs/ledger1.png) 73 | ![img](./docs/ledger2.png) 74 | -------------------------------------------------------------------------------- /api/routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | ginstatic "github.com/gin-contrib/static" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // CollectRouter return gin router 10 | func CollectRouter(mode, static string) *gin.Engine { 11 | if len(mode) > 0 { 12 | gin.SetMode(mode) 13 | } 14 | r := gin.Default() 15 | r.Use(cors.Default()) 16 | r.Use(ginstatic.Serve("/", ginstatic.LocalFile(static, false))) 17 | // r.Static("/front", static) 18 | r.POST("/api/block/file", BlockFile) 19 | r.POST("/api/block/raw", BlockRaw) 20 | r.POST("/api/ledger/file", LedgerFile) 21 | r.GET("/api/ledger/blocks", LedgerBlocks) 22 | 23 | return r 24 | } 25 | -------------------------------------------------------------------------------- /api/server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "encoding/json" 7 | "io/ioutil" 8 | "net/http" 9 | "strconv" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/pkg/errors" 13 | 14 | "github.com/tinywell/blocklator/pkg/block" 15 | "github.com/tinywell/blocklator/pkg/store" 16 | ) 17 | 18 | // Pagination params 19 | const ( 20 | PageSize = 20 21 | ) 22 | 23 | var ( 24 | // Cache cache 25 | Cache store.Cache 26 | ) 27 | 28 | func init() { 29 | Cache = store.NewCache() 30 | } 31 | 32 | // Ledger ledger data 33 | type Ledger struct { 34 | Pagination bool `json:"pagination" db:"pagination"` 35 | Key string `json:"key,omitempty" db:"key"` 36 | Blocks []*block.Desc `json:"blocks" db:"blocks"` 37 | CurPage int `json:"cur_page" db:"cur_page"` 38 | Total int `json:"total" db:"total"` 39 | } 40 | 41 | // BlockFile translate block data in file 42 | func BlockFile(c *gin.Context) { 43 | rfile, err := c.FormFile("block") 44 | if err != nil { 45 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 46 | return 47 | } 48 | file, err := rfile.Open() 49 | if err != nil { 50 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 51 | return 52 | } 53 | blockraw, err := ioutil.ReadAll(file) 54 | if err != nil { 55 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 56 | return 57 | } 58 | blocklator, err := block.NewBlocklator(blockraw) 59 | if err != nil { 60 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 61 | return 62 | } 63 | desc, err := blocklator.ToDesc() 64 | if err != nil { 65 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 66 | return 67 | } 68 | 69 | c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "data": desc}) 70 | return 71 | } 72 | 73 | // BlockRaw translate block data in base64 format 74 | func BlockRaw(c *gin.Context) { 75 | data := c.PostForm("block") 76 | if len(data) == 0 { 77 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "需要上传区块数据"}) 78 | return 79 | } 80 | blockraw, err := base64.StdEncoding.DecodeString(data) 81 | if err != nil { 82 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 83 | return 84 | } 85 | blocklator, err := block.NewBlocklator(blockraw) 86 | if err != nil { 87 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 88 | return 89 | } 90 | desc, err := blocklator.ToDesc() 91 | if err != nil { 92 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 93 | return 94 | } 95 | 96 | c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "data": desc}) 97 | return 98 | } 99 | 100 | // LedgerFile retrive ledger file into blocks 101 | func LedgerFile(c *gin.Context) { 102 | lfile, err := c.FormFile("ledger") 103 | if err != nil { 104 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 105 | return 106 | } 107 | ledger, err := lfile.Open() 108 | defer ledger.Close() 109 | if err != nil { 110 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 111 | return 112 | } 113 | 114 | ledgerLator := block.NewLedgerlator(bufio.NewReader(ledger)) 115 | blocks, err := ledgerLator.RetriveBlocks() 116 | if err != nil { 117 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error()}) 118 | return 119 | } 120 | 121 | // blockStrs := []string{} 122 | blockSums := []*block.Desc{} 123 | for _, b := range blocks { 124 | // blockStrs = append(blockStrs, base64.StdEncoding.EncodeToString(b)) 125 | bl, err := block.NewBlocklatorFromLedgerRaw(b) 126 | if err != nil { 127 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "create blocklator error").Error()}) 128 | return 129 | } 130 | sum, err := bl.ToDesc() 131 | if err != nil { 132 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "get block summary").Error()}) 133 | return 134 | } 135 | blockSums = append(blockSums, sum) 136 | } 137 | ledgerRsp := &Ledger{ 138 | Total: len(blockSums), 139 | Blocks: blockSums, 140 | } 141 | data, err := json.Marshal(blockSums) 142 | if err != nil { 143 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "marsh block summary error").Error()}) 144 | return 145 | } 146 | if len(data) > 4*1024*1024 || len(blockSums) > PageSize { 147 | key, err := Cache.KeyGen(data) 148 | if err != nil { 149 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "generate block summar keyy error").Error()}) 150 | return 151 | } 152 | 153 | Cache.Store(key, int64(len(data)), blockSums) 154 | ledgerRsp.Key = key 155 | ledgerRsp.CurPage = 1 156 | ledgerRsp.Pagination = true 157 | ledgerRsp.Blocks = blockSums[0:PageSize] 158 | } 159 | c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "data": ledgerRsp}) 160 | return 161 | } 162 | 163 | // LedgerBlocks get blocks by pagination 164 | func LedgerBlocks(c *gin.Context) { 165 | key := c.Query("key") 166 | page := c.Query("page") 167 | if len(key) == 0 || len(page) == 0 { 168 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.New("paaination query need key and page").Error()}) 169 | return 170 | } 171 | cp, err := strconv.Atoi(page) 172 | if err != nil { 173 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "decode page param error").Error()}) 174 | return 175 | } 176 | blocks, err := Cache.Load(key) 177 | if err != nil { 178 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": errors.WithMessage(err, "get data cache error").Error()}) 179 | return 180 | } 181 | if blockSum, ok := blocks.([]*block.Desc); ok { 182 | ledgerRsp := Ledger{ 183 | Pagination: true, 184 | CurPage: cp, 185 | Key: key, 186 | Total: len(blockSum), 187 | } 188 | start := (cp - 1) * PageSize 189 | end := cp * PageSize 190 | if end > len(blockSum) { 191 | end = len(blockSum) 192 | } 193 | ledgerRsp.Blocks = blockSum[start:end] 194 | c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "data": ledgerRsp}) 195 | return 196 | } 197 | c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "data cache invalid ,please reload your ledger file"}) 198 | return 199 | } 200 | -------------------------------------------------------------------------------- /cmd/blocklator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/tinywell/blocklator/cmd/server" 9 | ) 10 | 11 | var ( 12 | rootCmd = &cobra.Command{ 13 | Use: "blocklator", 14 | Short: "blocklator is a tool to translate fabric block", 15 | } 16 | ) 17 | 18 | func main() { 19 | rootCmd.AddCommand(server.ServerCmd) 20 | 21 | if err := rootCmd.Execute(); err != nil { 22 | fmt.Println(err) 23 | os.Exit(1) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | 8 | // pprof for server 9 | _ "net/http/pprof" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/spf13/cobra" 13 | "github.com/tinywell/blocklator/api" 14 | ) 15 | 16 | var ( 17 | port int 18 | production bool 19 | static string 20 | 21 | // ServerCmd server subcommand 22 | ServerCmd = &cobra.Command{ 23 | Use: "server", 24 | Short: "server is a backend web service use of gin", 25 | Run: func(cmd *cobra.Command, args []string) { 26 | Execute() 27 | }, 28 | } 29 | ) 30 | 31 | func init() { 32 | ServerCmd.PersistentFlags().IntVarP(&port, "port", "p", 8080, "listen port") 33 | ServerCmd.PersistentFlags().BoolVarP(&production, "production", "m", false, "is in production mode") 34 | ServerCmd.PersistentFlags().StringVarP(&static, "static", "s", "./front/dist", "front page path") 35 | } 36 | 37 | // Execute execute server command 38 | func Execute() { 39 | 40 | go func() { 41 | log.Println(http.ListenAndServe("localhost:6060", nil)) 42 | }() 43 | 44 | var mode = gin.DebugMode 45 | if production { 46 | mode = gin.ReleaseMode 47 | } 48 | 49 | r := api.CollectRouter(mode, static) 50 | 51 | if err := r.Run(fmt.Sprintf("0.0.0.0:%d", port)); err != nil { 52 | panic(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | blocklator: 5 | container_name: blocklator 6 | image: tinywell/blocklator:latest 7 | tty: true 8 | stdin_open: true 9 | # environment: 10 | # 11 | ports: 12 | - 8080:80 13 | working_dir: /app 14 | # command: 15 | # volumes: 16 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ## node 前端编译 2 | FROM node:16.14-alpine as nodeBuilder 3 | 4 | WORKDIR /app 5 | 6 | COPY ./front ./ 7 | 8 | # RUN npm config set registry https://registry.npm.taobao.org && \ 9 | # npm install 10 | 11 | RUN npm install 12 | 13 | RUN npm run build 14 | 15 | ## go 后端编译 16 | FROM golang:1.13-alpine as goBuilder 17 | 18 | WORKDIR /app 19 | 20 | COPY ./ ./ 21 | 22 | # RUN go env -w GOPROXY="https://goproxy.cn,direct" && \ 23 | # go build -v -o blocklator ./cmd/blocklator.go 24 | 25 | RUN go build -v -o blocklator ./cmd/blocklator.go 26 | 27 | ## 构建运行镜像 28 | FROM alpine:latest 29 | 30 | WORKDIR /app 31 | 32 | COPY --from=nodebuilder /app/dist/ ./dist 33 | COPY --from=goBuilder /app/blocklator ./ 34 | 35 | EXPOSE 80 36 | 37 | CMD [ "./blocklator","server","-m","-p","80","-s","./dist"] -------------------------------------------------------------------------------- /docs/block2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/docs/block2.png -------------------------------------------------------------------------------- /docs/block3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/docs/block3.png -------------------------------------------------------------------------------- /docs/ledger1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/docs/ledger1.png -------------------------------------------------------------------------------- /docs/ledger2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/docs/ledger2.png -------------------------------------------------------------------------------- /front/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /front/README.md: -------------------------------------------------------------------------------- 1 | # front 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /front/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blocklator", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve --port 8081", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.6.4", 11 | "element-ui": "^2.13.2", 12 | "vue": "^2.6.11", 13 | "vue-router": "^3.1.6" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "~4.3.0", 17 | "@vue/cli-plugin-router": "~4.3.0", 18 | "@vue/cli-service": "~4.3.0", 19 | "axios": ">=0.21.2", 20 | "less": "^3.0.4", 21 | "less-loader": "^5.0.0", 22 | "vue-template-compiler": "^2.6.11" 23 | }, 24 | "browserslist": [ 25 | "> 1%", 26 | "last 2 versions", 27 | "not dead" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /front/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/front/public/favicon.ico -------------------------------------------------------------------------------- /front/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 18 | 19 | 20 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /front/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 38 | -------------------------------------------------------------------------------- /front/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinywell/blocklator/3c5d65d5d83ff01f1cf2def1acd7d3c6f9eeb400/front/src/assets/logo.png -------------------------------------------------------------------------------- /front/src/common/consts.js: -------------------------------------------------------------------------------- 1 | let ServerAddrPre = '/api'; 2 | if(process.env.NODE_ENV==="development"){ 3 | ServerAddrPre = 'http://localhost:8080/api'; 4 | } 5 | 6 | 7 | const BlockTypeOpts = [ 8 | { text: '配置区块', value: 1 }, 9 | { text: '交易区块', value: 0 }, 10 | ]; 11 | 12 | const BlockTypeOptsMap = {}; 13 | BlockTypeOpts.forEach((item) => { BlockTypeOptsMap[item.value] = item.text; }); 14 | 15 | 16 | export default { 17 | BlockTypeOpts, 18 | BlockTypeOptsMap, 19 | ServerAddrPre 20 | } -------------------------------------------------------------------------------- /front/src/components/BlockHead.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 47 | 48 | -------------------------------------------------------------------------------- /front/src/components/Cert.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /front/src/components/GroupOrg.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 40 | 41 | -------------------------------------------------------------------------------- /front/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 29 | 31 | -------------------------------------------------------------------------------- /front/src/components/Nav.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /front/src/components/Transaction.vue: -------------------------------------------------------------------------------- 1 | 133 | 134 | 146 | 147 | -------------------------------------------------------------------------------- /front/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | import App from './App.vue' 5 | import router from './router' 6 | import axios from 'axios'; 7 | import consts from './common/consts'; 8 | 9 | Vue.prototype.$consts = consts; 10 | Vue.prototype.$axios = axios 11 | 12 | Vue.config.productionTip = false 13 | Vue.use(ElementUI); 14 | 15 | new Vue({ 16 | router, 17 | render: h => h(App) 18 | }).$mount('#app') 19 | -------------------------------------------------------------------------------- /front/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Block from '../views/Block.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | name: 'Block', 11 | component: Block 12 | }, 13 | { 14 | path: '/about', 15 | name: 'About', 16 | // route level code-splitting 17 | // this generates a separate chunk (about.[hash].js) for this route 18 | // which is lazy-loaded when the route is visited. 19 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 20 | }, 21 | { 22 | path: '/config', 23 | name: 'Config', 24 | component: () => import(/* webpackChunkName: "Config" */ '../views/Config.vue') 25 | }, 26 | { 27 | path: '/transaction', 28 | name: 'Transaction', 29 | component: () => import(/* webpackChunkName: "Transaction" */ '../views/Transaction.vue') 30 | }, 31 | { 32 | path: '/block', 33 | name: 'Block', 34 | component: () => import(/* webpackChunkName: "Block" */ '../views/Block.vue') 35 | }, 36 | { 37 | path: '/ledger', 38 | name: 'Ledger', 39 | component: () => import(/* webpackChunkName: "Ledger" */ '../views/Ledger.vue') 40 | }, 41 | { 42 | path: '/blocklist', 43 | name: 'BlockList', 44 | component: () => import(/* webpackChunkName: "BlockList" */ '../views/BlockList.vue') 45 | }, 46 | ] 47 | 48 | const router = new VueRouter({ 49 | mode: 'history', 50 | routes 51 | }) 52 | 53 | export default router 54 | -------------------------------------------------------------------------------- /front/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /front/src/views/Block.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 102 | 103 | -------------------------------------------------------------------------------- /front/src/views/BlockList.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 122 | 123 | -------------------------------------------------------------------------------- /front/src/views/Config.vue: -------------------------------------------------------------------------------- 1 | 230 | 231 | 374 | 375 | -------------------------------------------------------------------------------- /front/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /front/src/views/Ledger.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 52 | 53 | -------------------------------------------------------------------------------- /front/src/views/Transaction.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 75 | 76 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tinywell/blocklator 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gin-contrib/cors v1.3.1 7 | github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 8 | github.com/gin-gonic/gin v1.7.7 9 | github.com/golang/protobuf v1.3.3 10 | github.com/hyperledger/fabric v1.4.7 11 | github.com/hyperledger/fabric-protos-go v0.0.0-20200506201313-25f6564b9ac4 12 | github.com/kr/text v0.2.0 // indirect 13 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 14 | github.com/modern-go/reflect2 v1.0.1 // indirect 15 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 16 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect 17 | github.com/pkg/errors v0.9.1 18 | github.com/spf13/cobra v1.0.0 19 | github.com/stretchr/testify v1.6.1 // indirect 20 | github.com/sykesm/zap-logfmt v0.0.3 // indirect 21 | golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect 22 | golang.org/x/text v0.3.3 // indirect 23 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a // indirect 24 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect 25 | gopkg.in/yaml.v2 v2.3.0 // indirect 26 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 5 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 6 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 8 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 9 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 10 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 11 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 12 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 13 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 14 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 15 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 16 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 17 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 18 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 21 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 23 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 24 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 25 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 26 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 27 | github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA= 28 | github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk= 29 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 30 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 31 | github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 h1:xLG16iua01X7Gzms9045s2Y2niNpvSY/Zb1oBwgNYZY= 32 | github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2/go.mod h1:VhW/Ch/3FhimwZb8Oj+qJmdMmoB8r7lmJ5auRjm50oQ= 33 | github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= 34 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 35 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 36 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 37 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 38 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 39 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 40 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 41 | github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= 42 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 43 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 44 | github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= 45 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 46 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 47 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 48 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 49 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 50 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 51 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 52 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 53 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 54 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 55 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 56 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 57 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 60 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 61 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 62 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 63 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 64 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 65 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 66 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 67 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 68 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 69 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 70 | github.com/hyperledger/fabric v1.4.7 h1:weX0z8WybFIj2TTF0QOa+vBxwxBRZToRms3E1wtfRZk= 71 | github.com/hyperledger/fabric v1.4.7/go.mod h1:tGFAOCT696D3rG0Vofd2dyWYLySHlh0aQjf7Q1HAju0= 72 | github.com/hyperledger/fabric-protos-go v0.0.0-20200506201313-25f6564b9ac4 h1:75hBp86WljV3uQ7Q/wbO5w8ahfLAzxH7jfT5kVy2n6g= 73 | github.com/hyperledger/fabric-protos-go v0.0.0-20200506201313-25f6564b9ac4/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= 74 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 75 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 76 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 77 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 78 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 79 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 80 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 81 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 82 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 83 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 84 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 85 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 86 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 87 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 88 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 89 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 90 | github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= 91 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 92 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 93 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 94 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 95 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 96 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 97 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 98 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 99 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 100 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 101 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 102 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 103 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 104 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 105 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 106 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 107 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 108 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 109 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 110 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= 111 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 112 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 113 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 114 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 115 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 116 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 117 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 118 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 119 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 120 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 121 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 122 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 123 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 124 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 125 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 126 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 127 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 128 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 129 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 130 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 131 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 132 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 133 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 134 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 135 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 136 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 137 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 138 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 139 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 140 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 141 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 142 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 143 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 144 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 145 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 146 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 147 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 148 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 149 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 150 | github.com/sykesm/zap-logfmt v0.0.3 h1:3Wrhf7+I9JEUD8B6KPtDAr9j2jrS0/EPLy7GCE1t/+U= 151 | github.com/sykesm/zap-logfmt v0.0.3/go.mod h1:AuBd9xQjAe3URrWT1BBDk2v2onAZHkZkWRMiYZXiZWA= 152 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 153 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 154 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 155 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 156 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 157 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 158 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 159 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 160 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 161 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 162 | go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= 163 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 164 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 165 | go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= 166 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 167 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= 168 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 169 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 170 | go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw= 171 | go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 172 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 173 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 174 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 175 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 176 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 177 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 178 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 179 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 180 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 181 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 182 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 183 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 184 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 185 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 186 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 187 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 188 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 189 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 190 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 191 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 192 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 193 | golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= 194 | golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 195 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 196 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 197 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 198 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 199 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 200 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 201 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 202 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 203 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 204 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 205 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 206 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 207 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 208 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 209 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 210 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 211 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 212 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 213 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 214 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 215 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 216 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 217 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 218 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 219 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 220 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 221 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 222 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 223 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= 224 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 225 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 226 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 227 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 228 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 229 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= 230 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 231 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 232 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 233 | google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= 234 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 235 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 236 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 237 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 238 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 239 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 240 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 241 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 242 | gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 243 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 244 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 245 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 246 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 247 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 248 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 249 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 250 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 251 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 252 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 253 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 254 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 255 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 256 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 257 | -------------------------------------------------------------------------------- /pkg/block/block.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "encoding/base64" 5 | "strings" 6 | 7 | "github.com/golang/protobuf/proto" 8 | "github.com/hyperledger/fabric-protos-go/common" 9 | "github.com/hyperledger/fabric-protos-go/peer" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // Blocklator for block translate 14 | type Blocklator struct { 15 | block *common.Block 16 | } 17 | 18 | // NewBlocklator return new Blocklator 19 | func NewBlocklator(raw []byte) (*Blocklator, error) { 20 | cb := &common.Block{} 21 | err := proto.Unmarshal(raw, cb) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return &Blocklator{ 26 | block: cb, 27 | }, nil 28 | } 29 | 30 | // NewBlocklatorFromLedgerRaw return new Blocklator from block data in ledger 31 | func NewBlocklatorFromLedgerRaw(raw []byte) (*Blocklator, error) { 32 | block, err := ExtractLeaderBlock(raw) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &Blocklator{ 37 | block: block, 38 | }, nil 39 | } 40 | 41 | // GetBlockNum return block num 42 | func (bl *Blocklator) GetBlockNum() uint64 { 43 | return bl.block.Header.Number 44 | } 45 | 46 | // GetBlockHash return block hash 47 | func (bl *Blocklator) GetBlockHash() string { 48 | // hash := bl.block.Header.DataHash 49 | hash := HeaderHash(bl.block.Header) 50 | // return strings.ToUpper(hex.EncodeToString(hash)) 51 | return strings.ToUpper(base64.StdEncoding.EncodeToString(hash)) 52 | } 53 | 54 | // GetBlockPrehash return block previoous hash 55 | func (bl *Blocklator) GetBlockPrehash() string { 56 | hash := bl.block.Header.PreviousHash 57 | // return strings.ToUpper(hex.EncodeToString(hash)) 58 | return strings.ToUpper(base64.StdEncoding.EncodeToString(hash)) 59 | } 60 | 61 | // GetChannel return channel id 62 | func (bl *Blocklator) GetChannel() (string, error) { 63 | if len(bl.block.Data.Data) < 1 { 64 | return "", errors.New("invalid block") 65 | } 66 | env := &common.Envelope{} 67 | err := proto.Unmarshal(bl.block.Data.Data[0], env) 68 | if err != nil { 69 | return "", err 70 | } 71 | payload := &common.Payload{} 72 | err = proto.Unmarshal(env.Payload, payload) 73 | if err != nil { 74 | return "", err 75 | } 76 | ch := &common.ChannelHeader{} 77 | err = proto.Unmarshal(payload.Header.ChannelHeader, ch) 78 | if err != nil { 79 | return "", err 80 | } 81 | return ch.ChannelId, nil 82 | } 83 | 84 | // GetMetaDataLastConfig get the last config block num 85 | func (bl *Blocklator) GetMetaDataLastConfig() (uint64, error) { 86 | if len(bl.block.Metadata.Metadata) > int(common.BlockMetadataIndex_LAST_CONFIG) { 87 | meta := bl.block.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] 88 | cmeta := &common.Metadata{} 89 | err := proto.Unmarshal(meta, cmeta) 90 | if err != nil { 91 | return 0, errors.Wrap(err, "unmarshal metadata error") 92 | } 93 | index := cmeta.Value 94 | cindex := &common.LastConfig{} 95 | err = proto.Unmarshal(index, cindex) 96 | if err != nil { 97 | return 0, errors.Wrap(err, "unmarshal lastconfig error") 98 | } 99 | return cindex.Index, nil 100 | } 101 | return 0, errors.New("no metadata for lastconfig") 102 | } 103 | 104 | // GetMetaDataTransFilter 获取交易有效性标志 105 | func (bl *Blocklator) GetMetaDataTransFilter() ([]bool, error) { 106 | txfilters := []bool{} 107 | if len(bl.block.Metadata.Metadata) > int(common.BlockMetadataIndex_TRANSACTIONS_FILTER) { 108 | filter := bl.block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] 109 | for _, f := range filter { 110 | tf := false 111 | if peer.TxValidationCode(f) == peer.TxValidationCode_VALID { 112 | tf = true 113 | } 114 | txfilters = append(txfilters, tf) 115 | } 116 | return txfilters, nil 117 | } 118 | return nil, errors.New("no metadata for transaction filter") 119 | } 120 | 121 | // GetMetaDataTransValidationCode 获取交易有效性标志 122 | func (bl *Blocklator) GetMetaDataTransValidationCode() ([]peer.TxValidationCode, error) { 123 | txfilters := []peer.TxValidationCode{} 124 | if len(bl.block.Metadata.Metadata) > int(common.BlockMetadataIndex_TRANSACTIONS_FILTER) { 125 | filter := bl.block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] 126 | for _, f := range filter { 127 | txfilters = append(txfilters, peer.TxValidationCode(f)) 128 | } 129 | return txfilters, nil 130 | } 131 | return nil, errors.New("no metadata for transaction filter") 132 | } 133 | 134 | // GetCommitHash get commit hash of the block 135 | func (bl *Blocklator) GetCommitHash() (string, error) { 136 | if len(bl.block.Metadata.Metadata) > int(common.BlockMetadataIndex_COMMIT_HASH) { 137 | meta := &common.Metadata{} 138 | hash := bl.block.Metadata.Metadata[common.BlockMetadataIndex_COMMIT_HASH] 139 | err := proto.Unmarshal(hash, meta) 140 | if err != nil { 141 | return "", err 142 | } 143 | // return strings.ToUpper(hex.EncodeToString(meta.Value)), nil 144 | return strings.ToUpper(base64.StdEncoding.EncodeToString(meta.Value)), nil 145 | } 146 | return "", errors.New("no commit hash,invalid block metadata") 147 | } 148 | 149 | // GetConfig get config from block 150 | func (bl *Blocklator) GetConfig() *common.Config { 151 | if len(bl.block.Data.Data) > 1 || len(bl.block.Data.Data) < 1 { 152 | return nil 153 | } 154 | env := &common.Envelope{} 155 | err := proto.Unmarshal(bl.block.Data.Data[0], env) 156 | if err != nil { 157 | return nil 158 | } 159 | payload := &common.Payload{} 160 | err = proto.Unmarshal(env.Payload, payload) 161 | if err != nil { 162 | return nil 163 | } 164 | 165 | cfgenv := &common.ConfigEnvelope{} 166 | err = proto.Unmarshal(payload.Data, cfgenv) 167 | if err != nil { 168 | return nil 169 | } 170 | return cfgenv.Config 171 | } 172 | 173 | // GetTransactions get transction envelops from block 174 | func (bl *Blocklator) GetTransactions() []*common.Envelope { 175 | envs := []*common.Envelope{} 176 | for _, d := range bl.block.Data.Data { 177 | env := &common.Envelope{} 178 | err := proto.Unmarshal(d, env) 179 | if err != nil { 180 | continue 181 | } 182 | envs = append(envs, env) 183 | } 184 | return envs 185 | } 186 | 187 | // GetSummary get block summary info 188 | func (bl *Blocklator) GetSummary() (*Summary, error) { 189 | sum := &Summary{} 190 | sum.BlockNum = bl.GetBlockNum() 191 | channel, err := bl.GetChannel() 192 | if err != nil { 193 | sum.Channel = "" 194 | } 195 | sum.Channel = channel 196 | sum.Hash = bl.GetBlockHash() 197 | sum.PreHash = bl.GetBlockPrehash() 198 | if err != nil { 199 | return nil, err 200 | } 201 | sum.LastConfig, err = bl.GetMetaDataLastConfig() 202 | if err != nil { 203 | return nil, err 204 | } 205 | config := bl.GetConfig() 206 | if config == nil { 207 | sum.Type = BlockTypeTrans 208 | filters, err := bl.GetMetaDataTransFilter() 209 | if err != nil { 210 | return nil, err 211 | } 212 | sum.TransCount = len(filters) 213 | sum.CommitHash, err = bl.GetCommitHash() 214 | } else { 215 | sum.Type = BlockTypeConfig 216 | } 217 | return sum, nil 218 | } 219 | 220 | // ToDesc block to Desc 221 | func (bl *Blocklator) ToDesc() (*Desc, error) { 222 | blockdesc := &Desc{} 223 | blockdesc.BlockNum = bl.GetBlockNum() 224 | channel, err := bl.GetChannel() 225 | if err != nil { 226 | blockdesc.Channel = "" 227 | } 228 | blockdesc.Channel = channel 229 | blockdesc.Hash = bl.GetBlockHash() 230 | blockdesc.PreHash = bl.GetBlockPrehash() 231 | if err != nil { 232 | return nil, err 233 | } 234 | blockdesc.LastConfig, err = bl.GetMetaDataLastConfig() 235 | if err != nil { 236 | return nil, err 237 | } 238 | config := bl.GetConfig() 239 | if config == nil { 240 | blockdesc.Type = BlockTypeTrans 241 | filters, err := bl.GetMetaDataTransFilter() 242 | if err != nil { 243 | return nil, err 244 | } 245 | codes, err := bl.GetMetaDataTransValidationCode() 246 | if err != nil { 247 | return nil, err 248 | } 249 | trans := bl.GetTransactions() 250 | for i, t := range trans { 251 | translator, err := NewTranslator(t) 252 | if err != nil { 253 | continue 254 | } 255 | desc := translator.ToDesc() 256 | if len(filters) > i { 257 | desc.Filter = filters[i] 258 | } 259 | if len(codes) > i { 260 | desc.ValidationCode = peer.TxValidationCode_name[int32(codes[i])] 261 | } 262 | blockdesc.Transactions = append(blockdesc.Transactions, desc) 263 | } 264 | blockdesc.TransCount = len(trans) 265 | blockdesc.CommitHash, err = bl.GetCommitHash() 266 | } else { 267 | cfg := NewConfiglator(config) 268 | cfgdesc := cfg.ToDesc() 269 | blockdesc.Type = BlockTypeConfig 270 | blockdesc.Config = cfgdesc 271 | } 272 | return blockdesc, nil 273 | } 274 | -------------------------------------------------------------------------------- /pkg/block/blockfile.go: -------------------------------------------------------------------------------- 1 | package block 2 | -------------------------------------------------------------------------------- /pkg/block/cert.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "crypto/x509" 5 | "encoding/pem" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | var certPool *sync.Pool 13 | 14 | func init() { 15 | certPool = &sync.Pool{ 16 | New: func() interface{} { 17 | return &Cert{} 18 | }, 19 | } 20 | } 21 | 22 | // Cert cert 23 | type Cert struct { 24 | Pem string `json:"pem,omitempty" db:"pem"` 25 | CN string `json:"cn,omitempty" db:"cn"` 26 | OU string `json:"ou,omitempty" db:"ou"` 27 | Org string `json:"org,omitempty" db:"org"` 28 | } 29 | 30 | // NewCert return new Cert 31 | func NewCert(certRaw []byte) (*Cert, error) { 32 | c := &Cert{ 33 | Pem: string(certRaw), 34 | } 35 | // c := certPool.Get().(*Cert) 36 | // c.Pem = string(certRaw) 37 | b, _ := pem.Decode([]byte(certRaw)) 38 | if b == nil { 39 | return nil, errors.New("decode cert pem error") 40 | } 41 | cert, err := x509.ParseCertificate(b.Bytes) 42 | if err != nil { 43 | return nil, errors.Wrap(err, "parse cert error") 44 | } 45 | c.CN = cert.Subject.CommonName 46 | c.OU = strings.Join(cert.Subject.OrganizationalUnit, ",") 47 | c.Org = strings.Join(cert.Issuer.Organization, ",") 48 | return c, nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/block/config.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang/protobuf/proto" 7 | "github.com/hyperledger/fabric-protos-go/common" 8 | "github.com/hyperledger/fabric-protos-go/msp" 9 | "github.com/hyperledger/fabric-protos-go/orderer" 10 | "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 11 | "github.com/hyperledger/fabric-protos-go/peer" 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // Configlator 配置信息解析 16 | type Configlator struct { 17 | config *common.Config 18 | } 19 | 20 | // NewConfiglator 返回一个 Configlator 21 | func NewConfiglator(config *common.Config) *Configlator { 22 | return &Configlator{ 23 | config: config, 24 | } 25 | } 26 | 27 | // ToDesc return ConfigDesc 28 | func (c *Configlator) ToDesc() *ConfigDesc { 29 | desc := &ConfigDesc{} 30 | desc.ApplicationOrgs = c.GetApplicationOrgs() 31 | desc.ConsortiumOrgs = c.GetConsortiumOrgs() 32 | desc.OrdererOrgs = c.GetOrdererOrgs() 33 | desc.Consensus = c.GetConsensusInfo() 34 | desc.Values = c.GetValues() 35 | desc.ApplicationValues = c.GetApplicationValues() 36 | return desc 37 | } 38 | 39 | // GetValues 解析配置块中的基本参数信息 40 | func (c *Configlator) GetValues() *ConfigValues { 41 | values := &ConfigValues{} 42 | pha := &common.HashingAlgorithm{} 43 | err := proto.Unmarshal(c.config.ChannelGroup.Values[HashingAlgorithmKey].Value, pha) 44 | if err != nil { 45 | return nil 46 | } 47 | values.HashingAlgorithm = pha.Name 48 | 49 | if ct, ok := c.config.ChannelGroup.Values[ConsortiumKey]; ok { 50 | pct := &common.Consortium{} 51 | err = proto.Unmarshal(ct.Value, pct) 52 | if err != nil { 53 | return nil 54 | } 55 | values.Consortium = pct.Name 56 | } 57 | pod := &common.OrdererAddresses{} 58 | if oa, ok := c.config.ChannelGroup.Values[OrdererAddressesKey]; ok { 59 | err = proto.Unmarshal(oa.Value, pod) 60 | if err != nil { 61 | return nil 62 | } 63 | } 64 | values.OrdererAddresses = pod.Addresses 65 | cap := &common.Capabilities{} 66 | if capV, ok := c.config.ChannelGroup.Values[CapabilitiesKey]; ok { 67 | err = proto.Unmarshal(capV.Value, cap) 68 | if err != nil { 69 | return nil 70 | } 71 | caps := []string{} 72 | for k := range cap.Capabilities { 73 | caps = append(caps, k) 74 | } 75 | values.Capabilities = caps 76 | } 77 | 78 | return values 79 | } 80 | 81 | // GetConsensusInfo 解析配置块中的共识相关配置信息 82 | func (c *Configlator) GetConsensusInfo() *ConsensusInfo { 83 | info := &ConsensusInfo{} 84 | ct := &orderer.ConsensusType{} 85 | err := proto.Unmarshal(c.config.ChannelGroup.Groups[OrdererGroupKey].Values[ConsensusTypeKey].Value, ct) 86 | if err != nil { 87 | return nil 88 | } 89 | info.Type = ct.Type 90 | switch ct.Type { 91 | case "etcdraft": 92 | meta := &etcdraft.ConfigMetadata{} 93 | err = proto.Unmarshal(ct.Metadata, meta) 94 | if err != nil { 95 | break 96 | } 97 | info.RaftMetadata = meta 98 | case "kafka": 99 | brokers := &orderer.KafkaBrokers{} 100 | err = proto.Unmarshal(c.config.ChannelGroup.Groups[OrdererGroupKey].Values[KafkaBrokersKey].Value, brokers) 101 | if err != nil { 102 | break 103 | } 104 | info.Borkers = brokers.Brokers 105 | default: 106 | break 107 | } 108 | bs := &orderer.BatchSize{} 109 | err = proto.Unmarshal(c.config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchSizeKey].Value, bs) 110 | if err != nil { 111 | return nil 112 | } 113 | info.MaxMessageCount = bs.MaxMessageCount 114 | info.PreferredMaxBytes = bs.PreferredMaxBytes 115 | info.AbsoluteMaxBytes = bs.AbsoluteMaxBytes 116 | bt := &orderer.BatchTimeout{} 117 | err = proto.Unmarshal(c.config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchTimeoutKey].Value, bt) 118 | if err != nil { 119 | return nil 120 | } 121 | info.BatchTimeOut = bt.Timeout 122 | cap := &common.Capabilities{} 123 | if capV, ok := c.config.ChannelGroup.Groups[OrdererGroupKey].Values[CapabilitiesKey]; ok { 124 | err = proto.Unmarshal(capV.Value, cap) 125 | if err != nil { 126 | return nil 127 | } 128 | caps := []string{} 129 | for k := range cap.Capabilities { 130 | caps = append(caps, k) 131 | } 132 | info.Capabilities = caps 133 | } 134 | return info 135 | } 136 | 137 | // GetApplicationValues 解析 Application 节中的 Values 信息 138 | func (c *Configlator) GetApplicationValues() *ApplicationValues { 139 | if _, ok := c.config.ChannelGroup.Groups[ApplicationGroupKey]; !ok { 140 | return nil 141 | } 142 | values := &ApplicationValues{} 143 | cap := &common.Capabilities{} 144 | if capV, ok := c.config.ChannelGroup.Groups[ApplicationGroupKey].Values[CapabilitiesKey]; ok { 145 | err := proto.Unmarshal(capV.Value, cap) 146 | if err != nil { 147 | return nil 148 | } 149 | caps := []string{} 150 | for k := range cap.Capabilities { 151 | caps = append(caps, k) 152 | } 153 | values.Capabilities = caps 154 | } 155 | acl := &peer.ACLs{} 156 | if aclV, ok := c.config.ChannelGroup.Groups[ApplicationGroupKey].Values[ACLsKey]; ok { 157 | err := proto.Unmarshal(aclV.Value, acl) 158 | if err != nil { 159 | return nil 160 | } 161 | acls := make(map[string]string) 162 | for k, v := range acl.Acls { 163 | acls[k] = v.PolicyRef 164 | } 165 | values.ACLs = acls 166 | } 167 | return values 168 | } 169 | 170 | // GetOrdererOrgs 解析配置块中的 orderer 组织 171 | func (c *Configlator) GetOrdererOrgs() []*GroupOrg { 172 | orgs := []*GroupOrg{} 173 | if _, ok := c.config.ChannelGroup.Groups[OrdererGroupKey]; ok { 174 | for _, g := range c.config.ChannelGroup.Groups[OrdererGroupKey].Groups { 175 | mspvalue := g.Values[MSPKey].Value 176 | org, err := c.getOrg(mspvalue) 177 | if err != nil { 178 | fmt.Println("getorg error:", err.Error()) 179 | continue 180 | } 181 | if _, ok := g.Values[EndpointsKey]; ok { 182 | ep := g.Values[EndpointsKey].Value 183 | pbep := &common.OrdererAddresses{} 184 | err = proto.Unmarshal(ep, pbep) 185 | if err != nil { 186 | fmt.Println(err) 187 | continue 188 | } 189 | org.Endpoints = append(org.Endpoints, pbep.Addresses...) 190 | } 191 | orgs = append(orgs, org) 192 | } 193 | } 194 | return orgs 195 | } 196 | 197 | // GetApplicationOrgs 解析配置块中的应用组织 198 | func (c *Configlator) GetApplicationOrgs() []*GroupOrg { 199 | orgs := []*GroupOrg{} 200 | if _, ok := c.config.ChannelGroup.Groups[ApplicationGroupKey]; ok { 201 | for _, g := range c.config.ChannelGroup.Groups[ApplicationGroupKey].Groups { 202 | mspvalue := g.Values[MSPKey].Value 203 | org, err := c.getOrg(mspvalue) 204 | if err != nil { 205 | continue 206 | } 207 | if _, ok := g.Values[AnchorPeersKey]; ok { 208 | acp := g.Values[AnchorPeersKey].Value 209 | pbacp := &peer.AnchorPeers{} 210 | err = proto.Unmarshal(acp, pbacp) 211 | if err != nil { 212 | continue 213 | } 214 | 215 | for _, p := range pbacp.AnchorPeers { 216 | org.Endpoints = append(org.Endpoints, fmt.Sprintf("%s:%d", p.Host, p.Port)) 217 | } 218 | } 219 | orgs = append(orgs, org) 220 | } 221 | } 222 | return orgs 223 | } 224 | 225 | // GetConsortiumOrgs 解析系统配置块中的联盟组织 226 | func (c *Configlator) GetConsortiumOrgs() map[string][]*GroupOrg { 227 | corgs := make(map[string][]*GroupOrg) 228 | 229 | if _, ok := c.config.ChannelGroup.Groups[ConsortiumsGroupKey]; ok { 230 | for cn, g := range c.config.ChannelGroup.Groups[ConsortiumsGroupKey].Groups { 231 | orgs := []*GroupOrg{} 232 | for _, o := range g.Groups { 233 | mspvalue := o.Values[MSPKey].Value 234 | org, err := c.getOrg(mspvalue) 235 | if err != nil { 236 | continue 237 | } 238 | orgs = append(orgs, org) 239 | } 240 | corgs[cn] = orgs 241 | } 242 | } 243 | return corgs 244 | } 245 | 246 | func (c *Configlator) getOrg(mspvalue []byte) (*GroupOrg, error) { 247 | mspc := &msp.MSPConfig{} 248 | err := proto.Unmarshal(mspvalue, mspc) 249 | if err != nil { 250 | return nil, errors.Wrap(err, "unmarshal MSPConfig") 251 | } 252 | org := &GroupOrg{} 253 | switch mspc.Type { 254 | case FABRIC: 255 | mspcfg := &msp.FabricMSPConfig{} 256 | err = proto.Unmarshal(mspc.Config, mspcfg) 257 | if err != nil { 258 | return nil, errors.Wrap(err, "unmarshal FabricMSPConfig error") 259 | } 260 | org.Type = mspc.Type 261 | org.TypeName = "FABRIC" 262 | org.Name = mspcfg.Name 263 | if len(mspcfg.Admins) > 0 { 264 | org.Admin = string(mspcfg.Admins[0]) 265 | } 266 | if len(mspcfg.RootCerts) > 0 { 267 | org.RootCert = string(mspcfg.RootCerts[0]) 268 | } 269 | if len(mspcfg.TlsRootCerts) > 0 { 270 | org.TLSRootCert = string(mspcfg.TlsRootCerts[0]) 271 | } 272 | org.RevocationList = mspcfg.RevocationList 273 | case IDEMIX: 274 | mspcfg := &msp.IdemixMSPConfig{} 275 | err = proto.Unmarshal(mspc.Config, mspcfg) 276 | if err != nil { 277 | return nil, errors.Wrap(err, "unmarshal IdemixMSPConfig error") 278 | } 279 | org.Type = mspc.Type 280 | org.TypeName = "IDEMIX" 281 | org.Name = mspcfg.Name 282 | default: 283 | return nil, errors.Errorf("unexpected msp type:%d", mspc.Type) 284 | } 285 | return org, nil 286 | } 287 | -------------------------------------------------------------------------------- /pkg/block/consts.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | // const 4 | const ( 5 | FABRIC int32 = iota // MSP is of FABRIC type 6 | IDEMIX // MSP is of IDEMIX type 7 | OTHER // MSP is of OTHER TYPE 8 | 9 | // These values are fixed for the genesis block. 10 | msgVersion = 0 11 | epoch = 0 12 | 13 | // ConsortiumKey is the key for the ConfigValue of a 14 | // Consortium. 15 | ConsortiumKey = "Consortium" 16 | 17 | // HashingAlgorithmKey is the key for the ConfigValue of a 18 | // HashingAlgorithm. 19 | HashingAlgorithmKey = "HashingAlgorithm" 20 | 21 | // BlockDataHashingStructureKey is the key for the ConfigValue 22 | // of a BlockDataHashingStructure. 23 | BlockDataHashingStructureKey = "BlockDataHashingStructure" 24 | 25 | // OrdererAddressesKey is the key for the ConfigValue, OrdererAddresses. 26 | OrdererAddressesKey = "OrdererAddresses" 27 | 28 | // CapabilitiesKey is the key for the ConfigValue, capabilities. 29 | // CapabiltiesKey can be used at the channel, application, and orderer levels. 30 | CapabilitiesKey = "Capabilities" 31 | 32 | // EndpointsKey is the key for the ConfigValue, Endpoints in 33 | // a OrdererOrgGroup. 34 | EndpointsKey = "Endpoints" 35 | 36 | // MSPKey is the key for the ConfigValue, MSP. 37 | MSPKey = "MSP" 38 | 39 | // ConsensusTypeKey is the key for the ConfigValue, ConsensusType. 40 | ConsensusTypeKey = "ConsensusType" 41 | 42 | // BatchSizeKey is the key for the ConfigValue, BatchSize. 43 | BatchSizeKey = "BatchSize" 44 | 45 | // BatchTimeoutKey is the key for the ConfigValue, BatchSize. 46 | BatchTimeoutKey = "BatchTimeout" 47 | 48 | // KafkaBrokersKey is the key for the ConfigValue, KafkaBrokers. 49 | KafkaBrokersKey = "KafkaBrokers" 50 | 51 | // HashingAlgorithmKey is the key for the ConfigValue, HashingAlgorithm. 52 | 53 | // AdminsPolicyKey is the key used for the admin policy. 54 | AdminsPolicyKey = "Admins" 55 | 56 | // ReadersPolicyKey is the key used for the read policy. 57 | ReadersPolicyKey = "Readers" 58 | 59 | // WritersPolicyKey is the key used for the write policy. 60 | WritersPolicyKey = "Writers" 61 | 62 | // EndorsementPolicyKey is the key used for the endorsement policy. 63 | EndorsementPolicyKey = "Endorsement" 64 | 65 | // LifecycleEndorsementPolicyKey is the key used for the lifecycle endorsement 66 | // policy. 67 | LifecycleEndorsementPolicyKey = "LifecycleEndorsement" 68 | 69 | // BlockValidationPolicyKey is the key used for the block validation policy in 70 | // the OrdererOrgGroup. 71 | BlockValidationPolicyKey = "BlockValidation" 72 | 73 | // ChannelCreationPolicyKey is the key used in the consortium config to denote 74 | // the policy to be used in evaluating whether a channel creation request 75 | // is authorized. 76 | ChannelCreationPolicyKey = "ChannelCreationPolicy" 77 | 78 | // ChannelGroupKey is the group name for the channel config. 79 | ChannelGroupKey = "Channel" 80 | 81 | // ConsortiumsGroupKey is the group name for the consortiums config. 82 | ConsortiumsGroupKey = "Consortiums" 83 | 84 | // OrdererGroupKey is the group name for the orderer config. 85 | OrdererGroupKey = "Orderer" 86 | 87 | // ApplicationGroupKey is the group name for the Application config. 88 | ApplicationGroupKey = "Application" 89 | 90 | // ACLsKey is the name of the ACLs config. 91 | ACLsKey = "ACLs" 92 | 93 | // AnchorPeersKey is the key name for the AnchorPeers ConfigValue. 94 | AnchorPeersKey = "AnchorPeers" 95 | 96 | // ImplicitMetaPolicyType is the 'Type' string for implicit meta policies. 97 | ImplicitMetaPolicyType = "ImplicitMeta" 98 | 99 | // SignaturePolicyType is the 'Type' string for signature policies. 100 | SignaturePolicyType = "Signature" 101 | 102 | ordererAdminsPolicyName = "/Channel/Orderer/Admins" 103 | ) 104 | -------------------------------------------------------------------------------- /pkg/block/ledger.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | 7 | "github.com/golang/protobuf/proto" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | const ( 12 | peekBytes = 8 13 | ) 14 | 15 | // Ledgerlator translator for ledger data 16 | type Ledgerlator struct { 17 | ledger *bufio.Reader 18 | } 19 | 20 | // NewLedgerlator return new ledgerlator from a Reader 21 | func NewLedgerlator(ledger *bufio.Reader) *Ledgerlator { 22 | return &Ledgerlator{ 23 | ledger: ledger, 24 | } 25 | } 26 | 27 | // RetriveBlocks retrive blocks from ledger 28 | func (l Ledgerlator) RetriveBlocks() ([][]byte, error) { 29 | blocks := [][]byte{} 30 | for { 31 | 32 | peek, err := l.ledger.Peek(peekBytes) 33 | if err != nil { 34 | if err == io.EOF { 35 | break 36 | } 37 | return nil, errors.Wrap(err, "read peek info error") 38 | } 39 | length, n := proto.DecodeVarint(peek) 40 | if n == 0 { 41 | return nil, errors.New("peek decode not enough") 42 | } 43 | 44 | l.ledger.Discard(n) 45 | block := make([]byte, length) 46 | _, err = io.ReadAtLeast(l.ledger, block, int(length)) 47 | if err != nil { 48 | return nil, errors.Wrap(err, "read block bytes error") 49 | } 50 | blocks = append(blocks, block) 51 | } 52 | return blocks, nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/block/ledgerutil.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/asn1" 6 | "math/big" 7 | 8 | "github.com/hyperledger/fabric-protos-go/common" 9 | ledgerutil "github.com/hyperledger/fabric/common/ledger/util" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // ExtractLeaderBlock extract block from ledger serialize block 14 | func ExtractLeaderBlock(data []byte) (*common.Block, error) { 15 | block := &common.Block{} 16 | var err error 17 | buf := ledgerutil.NewBuffer(data) 18 | if block.Header, err = extractHeader(buf); err != nil { 19 | return nil, errors.WithMessage(err, "extract header error") 20 | } 21 | if block.Data, err = extractData(buf); err != nil { 22 | return nil, errors.WithMessage(err, "extract data error") 23 | } 24 | if block.Metadata, err = extractMetadata(buf); err != nil { 25 | return nil, errors.WithMessage(err, "extract metadata error") 26 | } 27 | return block, err 28 | } 29 | 30 | func extractHeader(buf *ledgerutil.Buffer) (*common.BlockHeader, error) { 31 | header := &common.BlockHeader{} 32 | var err error 33 | if header.Number, err = buf.DecodeVarint(); err != nil { 34 | return nil, errors.Wrap(err, "error decoding the block number") 35 | } 36 | if header.DataHash, err = buf.DecodeRawBytes(false); err != nil { 37 | return nil, errors.Wrap(err, "error decoding the data hash") 38 | } 39 | if header.PreviousHash, err = buf.DecodeRawBytes(false); err != nil { 40 | return nil, errors.Wrap(err, "error decoding the previous hash") 41 | } 42 | if len(header.PreviousHash) == 0 { 43 | header.PreviousHash = nil 44 | } 45 | return header, nil 46 | } 47 | 48 | func extractData(buf *ledgerutil.Buffer) (*common.BlockData, error) { 49 | data := &common.BlockData{} 50 | var numItems uint64 51 | var err error 52 | 53 | if numItems, err = buf.DecodeVarint(); err != nil { 54 | return nil, errors.Wrap(err, "error decoding the length of block data") 55 | } 56 | for i := uint64(0); i < numItems; i++ { 57 | var txEnvBytes []byte 58 | 59 | if txEnvBytes, err = buf.DecodeRawBytes(false); err != nil { 60 | return nil, errors.Wrap(err, "error decoding the transaction enevelope") 61 | } 62 | 63 | data.Data = append(data.Data, txEnvBytes) 64 | } 65 | return data, nil 66 | } 67 | 68 | func extractMetadata(buf *ledgerutil.Buffer) (*common.BlockMetadata, error) { 69 | metadata := &common.BlockMetadata{} 70 | var numItems uint64 71 | var metadataEntry []byte 72 | var err error 73 | if numItems, err = buf.DecodeVarint(); err != nil { 74 | return nil, errors.Wrap(err, "error decoding the length of block metadata") 75 | } 76 | for i := uint64(0); i < numItems; i++ { 77 | if metadataEntry, err = buf.DecodeRawBytes(false); err != nil { 78 | return nil, errors.Wrap(err, "error decoding the block metadata") 79 | } 80 | metadata.Metadata = append(metadata.Metadata, metadataEntry) 81 | } 82 | return metadata, nil 83 | } 84 | 85 | type asn1Header struct { 86 | Number *big.Int 87 | PreviousHash []byte 88 | DataHash []byte 89 | } 90 | 91 | // HeaderBytes ... 92 | func HeaderBytes(b *common.BlockHeader) []byte { 93 | asn1Header := asn1Header{ 94 | PreviousHash: b.PreviousHash, 95 | DataHash: b.DataHash, 96 | Number: new(big.Int).SetUint64(b.Number), 97 | } 98 | result, err := asn1.Marshal(asn1Header) 99 | if err != nil { 100 | // Errors should only arise for types which cannot be encoded, since the 101 | // BlockHeader type is known a-priori to contain only encodable types, an 102 | // error here is fatal and should not be propogated 103 | panic(err) 104 | } 105 | return result 106 | } 107 | 108 | // HeaderHash ... 109 | func HeaderHash(b *common.BlockHeader) []byte { 110 | sum := sha256.Sum256(HeaderBytes(b)) 111 | return sum[:] 112 | } 113 | -------------------------------------------------------------------------------- /pkg/block/model.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/hyperledger/fabric-protos-go/common" 7 | "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 8 | "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 9 | "github.com/hyperledger/fabric-protos-go/peer" 10 | ) 11 | 12 | // block type 13 | const ( 14 | BlockTypeTrans = iota 15 | BlockTypeConfig 16 | ) 17 | 18 | // GroupOrg 配置块中的组织信息 19 | type GroupOrg struct { 20 | Type int32 `json:"type" db:"type"` 21 | TypeName string `json:"type_name" db:"type_name"` 22 | Name string `json:"name" db:"name"` 23 | RootCert string `json:"root_cert" db:"root_cert"` 24 | TLSRootCert string `json:"tls_root_cert" db:"tls_root_cert"` 25 | Admin string `json:"admin" db:"admin"` 26 | RevocationList [][]byte `json:"revocation_list" db:"revocation_list"` 27 | Endpoints []string `json:"endpoints" db:"endpoints"` // peer: anchorpeers orderer: ordereraddress 28 | } 29 | 30 | // ConsensusInfo 配置快中的共识信息 31 | type ConsensusInfo struct { 32 | Type string `json:"type,omitempty" db:"type"` 33 | RaftMetadata *etcdraft.ConfigMetadata `json:"raft_metadata,omitempty" db:"raft_metadata"` 34 | MaxMessageCount uint32 `json:"max_message_count,omitempty" db:"max_message_count"` 35 | AbsoluteMaxBytes uint32 `json:"absolute_max_bytes,omitempty" db:"absolute_max_bytes"` 36 | PreferredMaxBytes uint32 `json:"preferred_max_bytes,omitempty" db:"preferred_max_bytes"` 37 | BatchTimeOut string `json:"batch_time_out,omitempty" db:"batch_time_out"` 38 | Borkers []string `json:"borkers,omitempty" db:"borkers"` 39 | Capabilities []string `json:"capabilities,omitempty" db:"capabilities"` 40 | } 41 | 42 | // ConfigValues config values 43 | type ConfigValues struct { 44 | Consortium string `json:"consortium,omitempty" db:"consortium"` 45 | HashingAlgorithm string `json:"hashing_algorithm,omitempty" db:"hashing_algorithm"` 46 | OrdererAddresses []string `json:"orderer_addresses,omitempty" db:"orderer_addresses"` 47 | BlockDataHashingWidth int `json:"block_data_hashing_width,omitempty" db:"block_data_hashing_width"` 48 | Capabilities []string `json:"capabilities,omitempty" db:"capabilities"` 49 | } 50 | 51 | // ApplicationValues values of application 52 | type ApplicationValues struct { 53 | ACLs map[string]string 54 | Capabilities []string `json:"capabilities,omitempty" db:"capabilities"` 55 | } 56 | 57 | // TxRwSet 交易读写集 58 | type TxRwSet struct { 59 | NsRwSets []*NsRwSet `json:"ns_rw_sets,omitempty" db:"ns_rw_sets"` 60 | } 61 | 62 | // NsRwSet encapsulates 'kvrwset.KVRWSet' proto message for a specific name space (chaincode) 63 | type NsRwSet struct { 64 | NameSpace string `json:"name_space,omitempty" db:"name_space"` 65 | KvRwSet *kvrwset.KVRWSet `json:"kv_rw_set,omitempty" db:"kv_rw_set"` 66 | CollHashedRwSets []*CollHashedRwSet `json:"coll_hashed_rw_sets,omitempty" db:"coll_hashed_rw_sets"` 67 | } 68 | 69 | // CollHashedRwSet encapsulates 'kvrwset.HashedRWSet' proto message for a specific collection 70 | type CollHashedRwSet struct { 71 | CollectionName string `json:"collection_name,omitempty" db:"collection_name"` 72 | HashedRwSet *kvrwset.HashedRWSet `json:"hashed_rw_set,omitempty" db:"hashed_rw_set"` 73 | PvtRwSetHash []byte `json:"pvt_rw_set_hash,omitempty" db:"pvt_rw_set_hash"` 74 | } 75 | 76 | //Summary block summary info 77 | type Summary struct { 78 | BlockNum uint64 `json:"block_num" db:"block_num"` 79 | Hash string `json:"hash" db:"hash"` 80 | PreHash string `json:"pre_hash" db:"pre_hash"` 81 | Channel string `json:"channel" db:"channel"` 82 | Type int `json:"type" db:"type"` // 0: transaction 1: config 83 | TransCount int `json:"trans_count" db:"trans_count"` 84 | CommitHash string `json:"commit_hash" db:"commit_hash"` 85 | LastConfig uint64 `json:"last_config" db:"last_config"` 86 | } 87 | 88 | // Desc block description 89 | type Desc struct { 90 | Summary 91 | // BlockNum uint64 `json:"block_num" db:"block_num"` 92 | // Hash string `json:"hash" db:"hash"` 93 | // PreHash string `json:"pre_hash" db:"pre_hash"` 94 | // Channel string `json:"channel" db:"channel"` 95 | // Type int `json:"type" db:"type"` // 0: transaction 1: config 96 | Config *ConfigDesc `json:"config" db:"config"` 97 | Transactions []*TranDesc `json:"transactions" db:"transactions"` 98 | // TransCount int `json:"trans_count" db:"trans_count"` 99 | // CommitHash string `json:"commit_hash" db:"commit_hash"` 100 | // LastConfig uint64 `json:"last_config" db:"last_config"` 101 | } 102 | 103 | // ConfigDesc config description 104 | type ConfigDesc struct { 105 | OrdererOrgs []*GroupOrg `json:"orderer_orgs" db:"orderer_orgs"` 106 | ConsortiumOrgs map[string][]*GroupOrg `json:"consortium_orgs" db:"consortium_orgs"` 107 | ApplicationOrgs []*GroupOrg `json:"application_orgs" db:"application_orgs"` 108 | ApplicationValues *ApplicationValues `json:"application_values" ` 109 | Values *ConfigValues `json:"values" db:"values"` 110 | Consensus *ConsensusInfo `json:"consensus" db:"consensus"` 111 | } 112 | 113 | // SignInfo sign info 114 | type SignInfo struct { 115 | MSPID string `json:"mspid,omitempty" db:"mspid"` 116 | Cert *Cert `json:"cert,omitempty" db:"cert"` 117 | Signature string `json:"signature,omitempty" db:"signature"` 118 | } 119 | 120 | // TranDesc transaction description 121 | type TranDesc struct { 122 | Channel string `json:"channel" db:"channel"` 123 | TxID string `json:"tx_id" db:"tx_id"` 124 | Time time.Time `json:"time" db:"time"` 125 | Chaincode string `json:"chaincode" db:"chaincode"` 126 | Func string `json:"func" db:"func"` 127 | Args []string `json:"args" db:"args"` 128 | Resp struct { 129 | Status int32 `json:"status" db:"status"` 130 | Message string `json:"message" db:"message"` 131 | Data string `json:"data" db:"data"` 132 | } `json:"resp" db:"resp"` 133 | TxRwSet *TxRwSet `json:"tx_rw_set" ` 134 | Filter bool `json:"filter" db:"filter"` 135 | ValidationCode string `json:"validation_code" db:"validation_code"` 136 | Signer *SignInfo `json:"signer" db:"signer"` 137 | Endorsers []*SignInfo `json:"endorsers," db:"endorsers"` 138 | } 139 | 140 | // Envelope clean struct for envelope 141 | type Envelope struct { 142 | Payload struct { 143 | Header struct { 144 | ChannelHeader *common.ChannelHeader 145 | SignatureHeader *common.SignatureHeader 146 | } 147 | Transaction struct { 148 | Header *common.SignatureHeader 149 | ChaincodeAction struct { 150 | Proposal struct { 151 | Input *peer.ChaincodeSpec 152 | } 153 | Response struct { 154 | ProposalHash []byte 155 | ChaincodeAction *peer.ChaincodeAction 156 | RWSet *TxRwSet 157 | } 158 | Endorses []*peer.Endorsement 159 | } 160 | } 161 | } 162 | Signature []byte 163 | } 164 | -------------------------------------------------------------------------------- /pkg/block/transaction.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "time" 7 | 8 | "github.com/golang/protobuf/proto" 9 | "github.com/hyperledger/fabric-protos-go/common" 10 | "github.com/hyperledger/fabric-protos-go/msp" 11 | "github.com/hyperledger/fabric-protos-go/peer" 12 | ) 13 | 14 | // Translator translator for transaction envelope 15 | type Translator struct { 16 | env *common.Envelope 17 | innerEnv *Envelope 18 | } 19 | 20 | // NewTranslator return new Translator 21 | func NewTranslator(env *common.Envelope) (*Translator, error) { 22 | translator := &Translator{ 23 | env: env, 24 | innerEnv: &Envelope{ 25 | Signature: env.Signature, 26 | }, 27 | } 28 | if err := translator.init(); err != nil { 29 | return nil, err 30 | } 31 | return translator, nil 32 | } 33 | 34 | func (t *Translator) init() error { 35 | payload := &common.Payload{} 36 | err := proto.Unmarshal(t.env.Payload, payload) 37 | if err != nil { 38 | return err 39 | } 40 | trans := &peer.Transaction{} 41 | err = proto.Unmarshal(payload.Data, trans) 42 | if err != nil { 43 | return err 44 | } 45 | if err = t.unHeader(payload.Header); err != nil { 46 | return err 47 | } 48 | if len(trans.Actions) < 1 { 49 | return errors.New("no transaction in block") 50 | } 51 | if err = t.unAction(trans.Actions[0]); err != nil { 52 | return err 53 | } 54 | return nil 55 | } 56 | 57 | func (t *Translator) unHeader(header *common.Header) error { 58 | ch := &common.ChannelHeader{} 59 | err := proto.Unmarshal(header.ChannelHeader, ch) 60 | if err != nil { 61 | return err 62 | } 63 | t.innerEnv.Payload.Header.ChannelHeader = ch 64 | sh := &common.SignatureHeader{} 65 | err = proto.Unmarshal(header.SignatureHeader, sh) 66 | if err != nil { 67 | return err 68 | } 69 | t.innerEnv.Payload.Header.SignatureHeader = sh 70 | return nil 71 | } 72 | 73 | func (t *Translator) unAction(action *peer.TransactionAction) error { 74 | ccp := &peer.ChaincodeActionPayload{} 75 | err := proto.Unmarshal(action.Payload, ccp) 76 | if err != nil { 77 | return err 78 | } 79 | t.innerEnv.Payload.Transaction.ChaincodeAction.Endorses = ccp.Action.Endorsements 80 | pp := &peer.ChaincodeProposalPayload{} 81 | err = proto.Unmarshal(ccp.ChaincodeProposalPayload, pp) 82 | if err != nil { 83 | return err 84 | } 85 | ppi := &peer.ChaincodeInvocationSpec{} 86 | err = proto.Unmarshal(pp.Input, ppi) 87 | if err != nil { 88 | return err 89 | } 90 | t.innerEnv.Payload.Transaction.ChaincodeAction.Proposal.Input = ppi.ChaincodeSpec 91 | 92 | ccresp := &peer.ProposalResponsePayload{} 93 | err = proto.Unmarshal(ccp.Action.ProposalResponsePayload, ccresp) 94 | if err != nil { 95 | return err 96 | } 97 | t.innerEnv.Payload.Transaction.ChaincodeAction.Response.ProposalHash = ccresp.ProposalHash 98 | cca := &peer.ChaincodeAction{} 99 | err = proto.Unmarshal(ccresp.Extension, cca) 100 | if err != nil { 101 | return err 102 | } 103 | t.innerEnv.Payload.Transaction.ChaincodeAction.Response.ChaincodeAction = cca 104 | txrwset := &TxRwSet{} 105 | err = txrwset.FromProtoBytes(cca.Results) 106 | if err != nil { 107 | return err 108 | } 109 | t.innerEnv.Payload.Transaction.ChaincodeAction.Response.RWSet = txrwset 110 | return nil 111 | } 112 | 113 | // ToDesc transaction block to TranDesc 114 | func (t *Translator) ToDesc() *TranDesc { 115 | td := &TranDesc{} 116 | td.Channel = t.innerEnv.Payload.Header.ChannelHeader.ChannelId 117 | td.TxID = t.innerEnv.Payload.Header.ChannelHeader.TxId 118 | ts := t.innerEnv.Payload.Header.ChannelHeader.Timestamp 119 | td.Time = time.Unix(ts.Seconds, int64(ts.Nanos)) 120 | td.Chaincode = t.innerEnv.Payload.Transaction.ChaincodeAction.Proposal.Input.ChaincodeId.Name 121 | td.Func = string(t.innerEnv.Payload.Transaction.ChaincodeAction.Proposal.Input.Input.Args[0]) 122 | for _, a := range t.innerEnv.Payload.Transaction.ChaincodeAction.Proposal.Input.Input.Args[1:] { 123 | td.Args = append(td.Args, string(a)) 124 | } 125 | td.Resp.Status = t.innerEnv.Payload.Transaction.ChaincodeAction.Response.ChaincodeAction.Response.Status 126 | td.Resp.Message = t.innerEnv.Payload.Transaction.ChaincodeAction.Response.ChaincodeAction.Response.Message 127 | td.Resp.Data = string(t.innerEnv.Payload.Transaction.ChaincodeAction.Response.ChaincodeAction.Response.Payload) 128 | td.TxRwSet = t.innerEnv.Payload.Transaction.ChaincodeAction.Response.RWSet 129 | seri := t.innerEnv.Payload.Header.SignatureHeader.Creator 130 | creator := &msp.SerializedIdentity{} 131 | err := proto.Unmarshal(seri, creator) 132 | if err != nil { 133 | //TODO: 134 | } 135 | si := &SignInfo{ 136 | MSPID: creator.Mspid, 137 | Signature: base64.StdEncoding.EncodeToString(t.innerEnv.Signature), 138 | } 139 | si.Cert, err = NewCert(creator.IdBytes) 140 | if err != nil { 141 | //TODO: 142 | } 143 | td.Signer = si 144 | for _, e := range t.innerEnv.Payload.Transaction.ChaincodeAction.Endorses { 145 | er := &msp.SerializedIdentity{} 146 | err := proto.Unmarshal(e.Endorser, er) 147 | if err != nil { 148 | //TODO: 149 | } 150 | si := &SignInfo{ 151 | MSPID: er.Mspid, 152 | Signature: base64.StdEncoding.EncodeToString(e.Signature), 153 | } 154 | cert, err := NewCert(er.IdBytes) 155 | if err != nil { 156 | //TODO: 157 | } 158 | si.Cert = cert 159 | td.Endorsers = append(td.Endorsers, si) 160 | } 161 | return td 162 | } 163 | 164 | // GetTxID return TxID 165 | func (t *Translator) GetTxID() string { 166 | return t.innerEnv.Payload.Header.ChannelHeader.TxId 167 | } 168 | 169 | // GetChannel return channel id 170 | func (t *Translator) GetChannel() string { 171 | return t.innerEnv.Payload.Header.ChannelHeader.ChannelId 172 | } 173 | -------------------------------------------------------------------------------- /pkg/block/txrwset.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "github.com/hyperledger/fabric-protos-go/ledger/rwset" 6 | "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 7 | ) 8 | 9 | // FromProtoBytes 解析 TxRwSet 10 | func (txRwSet *TxRwSet) FromProtoBytes(protoBytes []byte) error { 11 | protoMsg := &rwset.TxReadWriteSet{} 12 | var err error 13 | var txRwSetTemp *TxRwSet 14 | if err = proto.Unmarshal(protoBytes, protoMsg); err != nil { 15 | return err 16 | } 17 | if txRwSetTemp, err = TxRwSetFromProtoMsg(protoMsg); err != nil { 18 | return err 19 | } 20 | txRwSet.NsRwSets = txRwSetTemp.NsRwSets 21 | return nil 22 | } 23 | 24 | // TxRwSetFromProtoMsg ... 25 | func TxRwSetFromProtoMsg(protoMsg *rwset.TxReadWriteSet) (*TxRwSet, error) { 26 | txRwSet := &TxRwSet{} 27 | var nsRwSet *NsRwSet 28 | var err error 29 | for _, nsRwSetProtoMsg := range protoMsg.NsRwset { 30 | if nsRwSet, err = nsRwSetFromProtoMsg(nsRwSetProtoMsg); err != nil { 31 | return nil, err 32 | } 33 | if nsRwSet.NameSpace == "lscc" { 34 | continue 35 | } 36 | txRwSet.NsRwSets = append(txRwSet.NsRwSets, nsRwSet) 37 | } 38 | return txRwSet, nil 39 | } 40 | 41 | func nsRwSetFromProtoMsg(protoMsg *rwset.NsReadWriteSet) (*NsRwSet, error) { 42 | nsRwSet := &NsRwSet{NameSpace: protoMsg.Namespace, KvRwSet: &kvrwset.KVRWSet{}} 43 | if err := proto.Unmarshal(protoMsg.Rwset, nsRwSet.KvRwSet); err != nil { 44 | return nil, err 45 | } 46 | return nsRwSet, nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/store/cache.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/base64" 6 | "errors" 7 | "fmt" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // clean params 13 | const ( 14 | Experied = time.Minute * 10 15 | ScanInterval = time.Minute 16 | MaxCacheSize = 150 * 1024 * 1024 // byte 17 | ) 18 | 19 | // Cache cache 20 | type Cache interface { 21 | Store(key string, size int64, data interface{}) error 22 | Load(key string) (interface{}, error) 23 | KeyGen(raw []byte) (string, error) 24 | } 25 | 26 | // MapCache cache base map 27 | type MapCache struct { 28 | cache *sync.Map 29 | create map[string]time.Time 30 | size map[string]int64 31 | stop chan struct{} 32 | cacheLock *sync.RWMutex 33 | } 34 | 35 | // NewCache return new mapcache 36 | func NewCache() *MapCache { 37 | cache := &MapCache{ 38 | cache: &sync.Map{}, 39 | create: make(map[string]time.Time), 40 | stop: make(chan struct{}), 41 | size: make(map[string]int64), 42 | cacheLock: &sync.RWMutex{}, 43 | } 44 | go cache.scan() 45 | return cache 46 | } 47 | 48 | // KeyGen genarate a cache key by cache data 49 | func (c *MapCache) KeyGen(raw []byte) (string, error) { 50 | sha := sha256.New() 51 | _, err := sha.Write(raw) 52 | if err != nil { 53 | return "", err 54 | } 55 | res := sha.Sum([]byte{}) 56 | return base64.StdEncoding.EncodeToString(res), nil 57 | } 58 | 59 | // PreStore store prepare 60 | func (c *MapCache) preStore(key string, size int64) { 61 | if _, ok := c.create[key]; ok { 62 | return 63 | } 64 | var total int64 = 0 65 | for _, v := range c.size { 66 | total += v 67 | } 68 | fmt.Println("total size:", total) 69 | if total+size > MaxCacheSize { 70 | c.cacheLock.Lock() 71 | c.create = make(map[string]time.Time) 72 | c.cache = &sync.Map{} 73 | c.size = make(map[string]int64) 74 | c.cacheLock.Unlock() 75 | fmt.Println("clear all") 76 | } 77 | c.size[key] = size 78 | } 79 | 80 | // Store cache data in MapCache,return key 81 | func (c *MapCache) Store(key string, size int64, data interface{}) error { 82 | c.preStore(key, size) 83 | c.cacheLock.RLock() 84 | defer c.cacheLock.RUnlock() 85 | if _, ok := c.create[key]; ok { 86 | return nil 87 | } 88 | c.cache.Store(key, data) 89 | c.create[key] = time.Now() 90 | return nil 91 | } 92 | 93 | // Load load data by key 94 | func (c *MapCache) Load(key string) (interface{}, error) { 95 | value, ok := c.cache.Load(key) 96 | if ok { 97 | return value, nil 98 | } 99 | return nil, errors.New("no data") 100 | } 101 | 102 | func (c *MapCache) scan() { 103 | tick := time.NewTicker(ScanInterval) 104 | for { 105 | select { 106 | case <-tick.C: 107 | go c.clean() 108 | case <-c.stop: 109 | tick.Stop() 110 | return 111 | } 112 | } 113 | } 114 | func (c *MapCache) clean() { 115 | for k, v := range c.create { 116 | if time.Now().Sub(v) > Experied { 117 | fmt.Println("clean: ", k) 118 | c.cache.Delete(k) 119 | delete(c.create, k) 120 | delete(c.size, k) 121 | } 122 | } 123 | } 124 | --------------------------------------------------------------------------------