├── go.mod ├── logo.png ├── .gitignore ├── .github └── workflows │ └── build-ubuntu.yml ├── Makefile ├── README.md ├── main.go └── LICENSE /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nknorg/nkn-node-sampler 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-node-sampler/main/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *~ 3 | .DS_Store 4 | build 5 | *.log 6 | *.exe 7 | nkn-node-sampler 8 | -------------------------------------------------------------------------------- /.github/workflows/build-ubuntu.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Build Ubuntu 5 | 6 | on: 7 | push: 8 | # enable next line to limit for specific branches 9 | # branches: [ "master" ] 10 | pull_request: 11 | # branches: [ "master" ] 12 | 13 | jobs: 14 | 15 | build-ubuntu: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v3 22 | with: 23 | go-version: '1.20' 24 | 25 | - name: Build 26 | run: make 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL:=local_or_with_proxy 2 | 3 | USE_PROXY=GOPROXY=https://goproxy.io 4 | VERSION:=$(shell git describe --abbrev=7 --dirty --always --tags) 5 | BUILD=go build -ldflags "-s -w -X main.Version=$(VERSION)" 6 | BUILD_DIR=build 7 | BIN_NAME=nkn-node-sampler 8 | MAIN=main.go 9 | ifdef GOARM 10 | BIN_DIR=$(GOOS)-$(GOARCH)v$(GOARM) 11 | else 12 | BIN_DIR=$(GOOS)-$(GOARCH) 13 | endif 14 | 15 | .PHONY: local 16 | local: 17 | $(BUILD) -o $(BIN_NAME) $(MAIN) 18 | 19 | .PHONY: local_with_proxy 20 | local_with_proxy: 21 | $(USE_PROXY) $(BUILD) -o $(BIN_NAME) $(MAIN) 22 | 23 | .PHONY: local_or_with_proxy 24 | local_or_with_proxy: 25 | ${MAKE} local || ${MAKE} local_with_proxy 26 | 27 | .PHONY: build 28 | build: 29 | rm -rf $(BUILD_DIR)/$(BIN_DIR) 30 | mkdir -p $(BUILD_DIR)/$(BIN_DIR) 31 | GOOS=$(GOOS) GOARCH=$(GOARCH) $(BUILD) -o $(BUILD_DIR)/$(BIN_DIR)/$(BIN_NAME)$(EXT) $(MAIN) 32 | ${MAKE} zip 33 | 34 | .PHONY: tar 35 | tar: 36 | cd $(BUILD_DIR) && rm -f $(BIN_DIR).tar.gz && tar --exclude ".DS_Store" --exclude "__MACOSX" -czvf $(BIN_DIR).tar.gz $(BIN_DIR) 37 | 38 | .PHONY: zip 39 | zip: 40 | cd $(BUILD_DIR) && rm -f $(BIN_DIR).zip && zip --symlinks --exclude "*.DS_Store*" --exclude "*__MACOSX*" -r $(BIN_DIR).zip $(BIN_DIR) 41 | 42 | .PHONY: all 43 | all: 44 | ${MAKE} build GOOS=darwin GOARCH=amd64 45 | ${MAKE} build GOOS=linux GOARCH=amd64 46 | ${MAKE} build GOOS=linux GOARCH=arm64 47 | ${MAKE} build GOOS=linux GOARCH=arm GOARM=5 48 | ${MAKE} build GOOS=linux GOARCH=arm GOARM=6 49 | ${MAKE} build GOOS=linux GOARCH=arm GOARM=7 50 | ${MAKE} build GOOS=windows GOARCH=amd64 EXT=.exe 51 | ${MAKE} build GOOS=windows GOARCH=386 EXT=.exe 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NKN Node Sampler 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/nknorg/nkn-node-sampler)](https://goreportcard.com/report/github.com/nknorg/nkn-node-sampler) [![Build Status](https://github.com/nknorg/nkn-node-sampler/actions/workflows/build-ubuntu.yml/badge.svg)](https://github.com/nknorg/nkn-node-sampler/actions/workflows/build-ubuntu.yml) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing) 4 | 5 | ![nkn](logo.png) 6 | 7 | NKN node crawler with random sampling. Get an estimate of the following network 8 | stats within just a few seconds and minimal resource (CPU, RAM, network): 9 | 10 | - Total number of nodes in the network 11 | - Total number of messages being relayed by the network per second 12 | 13 | ## Build 14 | 15 | ```shell 16 | make 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```shell 22 | ./nkn-node-sampler 23 | ``` 24 | 25 | ### Control concurrency and sampling size 26 | 27 | ```shell 28 | ./nkn-node-sampler -m 8 -n 8 29 | ``` 30 | 31 | - `m`: how many concurrency network request to make 32 | - `n`: how many steps to sample 33 | - `m x n` controls the sampling size 34 | 35 | ## Contributing 36 | 37 | **Can I submit a bug, suggestion or feature request?** 38 | 39 | Yes. Please open an issue for that. 40 | 41 | **Can I contribute patches?** 42 | 43 | Yes, we appreciate your help! To make contributions, please fork the repo, push 44 | your changes to the forked repo with signed-off commits, and open a pull request 45 | here. 46 | 47 | Please sign off your commit. This means adding a line "Signed-off-by: Name 48 | " at the end of each commit, indicating that you wrote the code and have 49 | the right to pass it on as an open source patch. This can be done automatically 50 | by adding -s when committing: 51 | 52 | ```shell 53 | git commit -s 54 | ``` 55 | 56 | ## Community 57 | 58 | - [Forum](https://forum.nkn.org/) 59 | - [Discord](https://discord.gg/c7mTynX) 60 | - [Telegram](https://t.me/nknorg) 61 | - [Reddit](https://www.reddit.com/r/nknblockchain/) 62 | - [Twitter](https://twitter.com/NKN_ORG) 63 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "log" 11 | "math" 12 | "math/big" 13 | "net" 14 | "net/http" 15 | "net/url" 16 | "os" 17 | "strings" 18 | "sync" 19 | "time" 20 | ) 21 | 22 | type rpcSuccessorResponse struct { 23 | Result []string `json:"result"` 24 | } 25 | 26 | type rpcChordRingInfoResponse struct { 27 | Result struct { 28 | LocalNode struct { 29 | ID string `json:"id"` 30 | RelayMessageCount uint64 `json:"relayMessageCount"` 31 | Uptime int `json:"uptime"` 32 | } `json:"localNode"` 33 | Successors []struct { 34 | Addr string `json:"addr"` 35 | ID string `json:"id"` 36 | } `json:"successors"` 37 | Predecessors []struct { 38 | Addr string `json:"addr"` 39 | ID string `json:"id"` 40 | } `json:"predecessors"` 41 | } `json:"result"` 42 | } 43 | 44 | var ( 45 | // Version string 46 | Version string 47 | ) 48 | 49 | var ( 50 | totalSpace *big.Int = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil) 51 | ) 52 | 53 | func makeRPCRequest(url string, method string, params interface{}) ([]byte, error) { 54 | requestBody := struct { 55 | Method string `json:"method"` 56 | Params interface{} `json:"params"` 57 | }{ 58 | Method: method, 59 | Params: params, 60 | } 61 | 62 | jsonData, err := json.Marshal(requestBody) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | client := &http.Client{ 68 | Timeout: time.Second * 10, 69 | Transport: &http.Transport{ 70 | DialContext: (&net.Dialer{ 71 | Timeout: time.Second * 5, 72 | KeepAlive: time.Second * 30, 73 | }).DialContext, 74 | TLSHandshakeTimeout: time.Second * 5, 75 | ResponseHeaderTimeout: time.Second * 5, 76 | ExpectContinueTimeout: time.Second * 1, 77 | }, 78 | } 79 | 80 | resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonData)) 81 | if err != nil { 82 | return nil, err 83 | } 84 | defer resp.Body.Close() 85 | 86 | return io.ReadAll(resp.Body) 87 | } 88 | 89 | func tcpAddrToRPCAddr(tcpAddr string) (string, error) { 90 | parsedURL, err := url.Parse(tcpAddr) 91 | if err != nil { 92 | return "", err 93 | } 94 | host := strings.Split(parsedURL.Host, ":")[0] 95 | return fmt.Sprintf("http://%s:30003", host), nil 96 | } 97 | 98 | func sampleNodes(startKey *big.Int, initialRPCAddress string, n int) (int, uint64, int, *big.Int, error) { 99 | findSuccessorReq := struct { 100 | Key string `json:"key"` 101 | }{ 102 | Key: startKey.Text(16), 103 | } 104 | 105 | respBody, err := makeRPCRequest(initialRPCAddress, "findsuccessoraddrs", findSuccessorReq) 106 | if err != nil { 107 | return 0, 0, 0, nil, fmt.Errorf("Error making RPC request: %v", err) 108 | } 109 | 110 | var successorResp rpcSuccessorResponse 111 | if err := json.Unmarshal(respBody, &successorResp); err != nil || len(successorResp.Result) == 0 { 112 | return 0, 0, 0, nil, fmt.Errorf("Error parsing findsuccessoraddrs response: %v", err) 113 | } 114 | 115 | rpcAddresses := make([]string, 1) 116 | if len(successorResp.Result) == 0 { 117 | return 0, 0, 0, nil, fmt.Errorf("Found no successors") 118 | } 119 | 120 | firstAddr := successorResp.Result[0] 121 | rpcAddresses[0], err = tcpAddrToRPCAddr(firstAddr) 122 | if err != nil { 123 | return 0, 0, 0, nil, fmt.Errorf("Error parsing URL from successor address: %v", err) 124 | } 125 | 126 | nodesVisited := 1 127 | lastID := startKey.Text(16) 128 | var totalRelay uint64 129 | totalUptime := 0 130 | for i := 0; i <= n; i++ { 131 | var responseBody []byte 132 | for _, rpcAddress := range rpcAddresses { 133 | responseBody, err = makeRPCRequest(rpcAddress, "getchordringinfo", struct{}{}) 134 | if err == nil { 135 | break 136 | } 137 | } 138 | if err != nil { 139 | log.Println("Error getting chord ring info:", err) 140 | break 141 | } 142 | 143 | var chordRingResp rpcChordRingInfoResponse 144 | if err := json.Unmarshal(responseBody, &chordRingResp); err != nil { 145 | log.Println("Error unmarshalling chord ring info response:", err) 146 | break 147 | } 148 | 149 | successors := chordRingResp.Result.Successors 150 | predecessors := chordRingResp.Result.Predecessors 151 | if i > 0 { 152 | found := false 153 | for j, pred := range predecessors { 154 | if pred.ID == lastID { 155 | nodesVisited += j + 1 156 | found = true 157 | break 158 | } 159 | } 160 | if !found { 161 | log.Println("Prev ID not found in predecessors") 162 | break 163 | } 164 | } 165 | 166 | lastID = chordRingResp.Result.LocalNode.ID 167 | totalRelay += chordRingResp.Result.LocalNode.RelayMessageCount 168 | totalUptime += chordRingResp.Result.LocalNode.Uptime 169 | 170 | if len(successors) < 1 { 171 | log.Println("Not enough successors") 172 | break 173 | } 174 | 175 | rpcAddresses = make([]string, 0) 176 | mid := len(successors) - int(2*math.Sqrt(float64(len(successors)))) 177 | for index := mid - 1; index <= mid+1; index++ { 178 | if index > 0 && index < len(successors) { 179 | rpcAddress, err := tcpAddrToRPCAddr(successors[index].Addr) 180 | if err != nil { 181 | log.Println("Error parsing URL from successor address:", err) 182 | continue 183 | } 184 | rpcAddresses = append(rpcAddresses, rpcAddress) 185 | } 186 | } 187 | if len(rpcAddresses) == 0 { 188 | break 189 | } 190 | } 191 | 192 | var totalArea *big.Int 193 | lastKey := new(big.Int) 194 | lastKey.SetString(lastID, 16) 195 | 196 | if startKey.Cmp(lastKey) > 0 { 197 | totalArea = new(big.Int).Sub(totalSpace, startKey) 198 | totalArea.Add(totalArea, lastKey) 199 | } else { 200 | totalArea = new(big.Int).Sub(lastKey, startKey) 201 | } 202 | 203 | return nodesVisited, totalRelay, totalUptime, totalArea, nil 204 | } 205 | 206 | func main() { 207 | timeStart := time.Now() 208 | 209 | var ( 210 | rpcAddr string 211 | m, n int 212 | jsonOut string 213 | version bool 214 | ) 215 | 216 | flag.StringVar(&rpcAddr, "rpc", "http://seed.nkn.org:30003", "Initial RPC address in the form ip:port") 217 | flag.IntVar(&m, "m", 8, "Number of concurrent goroutines") 218 | flag.IntVar(&n, "n", 8, "Number of steps to repeat") 219 | flag.StringVar(&jsonOut, "json", "", "Write output to json file") 220 | flag.BoolVar(&version, "version", false, "Print version") 221 | flag.Parse() 222 | 223 | if version { 224 | fmt.Println(Version) 225 | os.Exit(0) 226 | } 227 | 228 | if len(rpcAddr) == 0 { 229 | log.Fatal("RPC address is required") 230 | } 231 | 232 | wg := &sync.WaitGroup{} 233 | nodeCountChan := make(chan int, m) 234 | areaChan := make(chan *big.Int, m) 235 | relayCountChan := make(chan uint64, m) 236 | uptimeChan := make(chan int, m) 237 | 238 | S, err := rand.Int(rand.Reader, totalSpace) 239 | if err != nil { 240 | log.Fatal(err) 241 | } 242 | 243 | for i := 0; i < m; i++ { 244 | wg.Add(1) 245 | offset := new(big.Int).Mul(totalSpace, big.NewInt(int64(i))) 246 | offset.Div(offset, big.NewInt(int64(m))) 247 | startKey := new(big.Int).Add(S, offset) 248 | startKey.Mod(startKey, totalSpace) 249 | 250 | go func() { 251 | defer wg.Done() 252 | nodeCount, relayCount, uptime, area, err := sampleNodes(startKey, rpcAddr, n) 253 | if err != nil { 254 | log.Println(err) 255 | } 256 | nodeCountChan <- nodeCount 257 | relayCountChan <- relayCount 258 | uptimeChan <- uptime 259 | areaChan <- area 260 | }() 261 | } 262 | 263 | wg.Wait() 264 | close(nodeCountChan) 265 | close(areaChan) 266 | close(relayCountChan) 267 | close(uptimeChan) 268 | 269 | totalNodesVisited := 0 270 | var totalRelay uint64 271 | var totalUptime int 272 | totalArea := big.NewInt(0) 273 | for nodesVisited := range nodeCountChan { 274 | totalNodesVisited += nodesVisited 275 | } 276 | for relayCount := range relayCountChan { 277 | totalRelay += relayCount 278 | } 279 | for uptime := range uptimeChan { 280 | totalUptime += uptime 281 | } 282 | for area := range areaChan { 283 | if area != nil { 284 | totalArea.Add(totalArea, area) 285 | } 286 | } 287 | 288 | if totalArea.Sign() == 0 { 289 | log.Fatal("Error: Total area covered is zero, cannot estimate total number of nodes.") 290 | } 291 | estimatedTotalNodes := new(big.Int).Div(new(big.Int).Mul(big.NewInt(int64(totalNodesVisited)), totalSpace), totalArea).Int64() 292 | uncertainty := new(big.Int).Div(new(big.Int).Mul(big.NewInt(int64(math.Sqrt(float64(totalNodesVisited)))), totalSpace), totalArea).Int64() 293 | estimatedRelayPerSecond := float64(totalRelay) / float64(totalUptime) * float64(estimatedTotalNodes) / (math.Log2(float64(estimatedTotalNodes)) / 2) 294 | 295 | if len(jsonOut) > 0 { 296 | jsonStr, err := json.Marshal(map[string]interface{}{ 297 | "nodesVisited": totalNodesVisited, 298 | "areaCovered": float64(totalNodesVisited) / float64(estimatedTotalNodes), 299 | "nodesEstimated": estimatedTotalNodes, 300 | "nodesUncertainty": uncertainty, 301 | "relayPerSecond": estimatedRelayPerSecond, 302 | "updateTime": time.Now().Unix(), 303 | }) 304 | if err != nil { 305 | log.Fatalln("Json formatter error:", err) 306 | } 307 | err = os.WriteFile(jsonOut, jsonStr, 0644) 308 | if err != nil { 309 | log.Fatalln("Write output to file error:", err) 310 | } 311 | } else { 312 | log.Printf("Total nodes visited: %d\n", totalNodesVisited) 313 | log.Printf("Total area covered: %.2f%%\n", 100*float64(totalNodesVisited)/float64(estimatedTotalNodes)) 314 | log.Printf("Estimated total number of nodes in the network: %d +- %d\n", estimatedTotalNodes, uncertainty) 315 | log.Printf("Estimated network relay per second: %.0f\n", estimatedRelayPerSecond) 316 | } 317 | log.Printf("Time used: %v\n", time.Since(timeStart)) 318 | } 319 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------