├── .gitignore ├── LICENSE ├── README.md ├── certs ├── keys1 │ ├── staker.crt │ └── staker.key ├── keys2 │ ├── staker.crt │ └── staker.key ├── keys3 │ ├── staker.crt │ └── staker.key ├── keys4 │ ├── staker.crt │ └── staker.key ├── keys5 │ ├── staker.crt │ └── staker.key ├── keys6 │ ├── staker.crt │ └── staker.key └── keys7 │ ├── staker.crt │ └── staker.key ├── cfg ├── cfg.go └── rpc.go ├── cmd ├── avawallet.go ├── callrpc.go ├── exit.go ├── network.go ├── procmanager.go ├── root.go ├── runscript.go ├── setoutput.go ├── startnode.go └── varstore.go ├── example.avash.yaml ├── example.network.yaml ├── go.mod ├── go.sum ├── main.go ├── network ├── init.sh ├── node_tools.go ├── ssh_client.go ├── ssh_config.go └── startnode.sh ├── node ├── cli_tools.go ├── config.go └── metadata.go ├── processmgr ├── process.go ├── process_test.go ├── processmanager.go └── processmanager_test.go ├── scripts ├── five_node_staking.lua └── mixed_staking_network.lua └── utils └── logging ├── level.go ├── log.go └── output.go /.gitignore: -------------------------------------------------------------------------------- 1 | avash 2 | stash/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Ava Labs, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Avash — The Avalanche Shell Client 2 | 3 | This is a temporary stateful shell execution environment used to deploy networks locally, manage their processes, and run network tests. 4 | 5 | Avash opens a shell environment of its own. This environment is completely wiped when Avash exits. Any Avalanche nodes deployed by Avash should be exited as well, leaving only their stash (containing only their log files) behind. 6 | 7 | Avash provides the ability to run Lua scripts which can execute a sequence of shell commands in Avash. This allows for automation of regular tasks. For instance, different network configurations can be programmed into a lua script and deployed as-needed, allowing for rapid tests against various network types. 8 | 9 | ## Installation 10 | 11 | ### Requirements 12 | 13 | * Golang 1.15.5+ 14 | * An Avalanche Client Implementing Avalanche Standard CLI Flags 15 | 16 | ### Quick Setup 17 | 18 | Install and build an Avalanche client 19 | 20 | ```zsh 21 | go get github.com/ava-labs/avash 22 | cd $GOPATH/src/github.com/ava-labs/avash 23 | go build 24 | ``` 25 | 26 | Now you can fire up a 5 node staking network: 27 | 28 | ```zsh 29 | ./avash 30 | Config file set: /Users/username/.avash.yaml 31 | Avash successfully configured. 32 | avash> runscript scripts/five_node_staking.lua 33 | RunScript: Running scripts/five_node_staking.lua 34 | RunScript: Successfully ran scripts/five_node_staking.lua 35 | ``` 36 | 37 | For full documentation of Avash configuration and commands, please see the official [Avalanche Documentation](https://docs.avax.network/build/tools/avash). 38 | 39 | ## Using Avash 40 | 41 | ### Opening a shell 42 | 43 | Super easy, just type `./avash` and it will open a shell environment. 44 | 45 | #### Configuration 46 | 47 | By default Avash will look for a configuration file named either `.avash.yaml` or `.avash.yml` located in the following paths. 48 | 49 | * `$HOME/` 50 | * `.` 51 | * `/etc/avash/` 52 | 53 | If no config file is found then Avash will create one at `$HOME/.avash.yaml`. 54 | 55 | ```zsh 56 | ./avash 57 | Config file not found: .avash.yaml 58 | Created empty config file: /Users/username/.avash.yaml 59 | ``` 60 | 61 | Alternatively you can pass in a `--config` flag with a path to your config file. **NOTE** you must put the full path. `~/` **will not** resolve to `$HOME/`. 62 | 63 | ```zsh 64 | ./avash --config=/Users/username/path/to/config/my-config-file.yaml 65 | Config file set: /Users/username/path/to/config/my-config-file.yaml 66 | Avash successfully configured. 67 | ``` 68 | 69 | If no config file is found at the path which was passed to `--config` then Avash will create one at `$HOME/`. Avash will use the filename which was passed to `--config`. 70 | 71 | ```zsh 72 | ./avash --config=/Users/username/path/to/config/my-config-file.yaml 73 | Config file not found: /Users/username/path/to/config/my-config-file.yaml 74 | Created empty config file: /Users/username/my-config-file.yaml 75 | ``` 76 | 77 | If you have multiple config files Avash will load the values from a single file in decreasing preference: 78 | 79 | * `--config` 80 | * `$HOME/` 81 | * `.` 82 | * `/etc/avash/` 83 | 84 | #### Help 85 | 86 | For your first command, type `help` in Avash to see the commands available. 87 | 88 | You can also type `help [command]` to see the list of options available for that command. 89 | 90 | Ex: 91 | 92 | ```zsh 93 | help procmanager 94 | help procmanager start 95 | ``` 96 | 97 | ### Commands 98 | 99 | * `avaxwallet` - Tools for interacting with Avalanche Payments over the network. 100 | * `callrpc` - Issues an RPC call to a node. 101 | * `exit` - Exit the shell. 102 | * `help` - Help about any command. 103 | * `network` - Tools for interacting with remote hosts. 104 | * `procmanager` - Access the process manager for the avash client. 105 | * `runscript` - Runs the provided script. 106 | * `setoutput` - Sets shell log output. 107 | * `startnode` - Starts a node process and gives it a name. 108 | * `varstore` - Tools for creating variable stores and printing variables within them. 109 | 110 | ### Writing Scripts 111 | 112 | Avash imports the [gopher-lua library](https://github.com/yuin/gopher-lua) to run lua scripts. 113 | 114 | Scripts have certain hooks available to them which allows the user to write code which invokes the current Avash environment. 115 | 116 | The functions available to Lua are: 117 | 118 | * `avash_call` - Takes a string and runs it as an Avash command, returning output 119 | * `avash_sleepmicro` - Takes an unsigned integer representing microseconds and sleeps for that long 120 | * `avash_setvar` - Takes a variable scope (string), a variable name (string), and a variable (string) and places it in the variable store. The scope must already have been created. 121 | 122 | When writing Lua, the standard Lua functionality is available to automate the execution of series of Avash commands. This allows a developer to automate: 123 | 124 | * Local network deployments 125 | * Sending transations, both virtuous and conflicting 126 | * Order transaction test cases 127 | * Save the value of UTXO sets and test results to disk 128 | * Compare the values of two nodes UTXO sets 129 | * Track expected results and compare them with real nodes 130 | 131 | Example Lua scripts are in [the `./scripts` directory](./scripts/). 132 | 133 | ### Funding a Wallet 134 | 135 | On a local network, the 3 blockchains on the default subnet—the X-Chain, C-Chain and P-Chain—each have a pre-funded private key, `PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN`. This private key has 300m AVAX on the X-Chain, 50m AVAX on the C-Chain and 30m AVAX on the P-Chain—20m of which is unlocked and 10m which is locked and stakeable. For more details, see [Fund a local test network tutorial](https://docs.avax.network/build/tutorials/platform/fund-a-local-test-network). 136 | -------------------------------------------------------------------------------- /certs/keys1/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMTVaGA8zMDE5MDcxMDE2 6 | MTIxNVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDKYSRw/W0YpYH/MTQhiFrR0m89l6yTuzLpDtjudr/5RnhIPvtqk7YIGm/m9l29 9 | xwR4J5r7SZGs+70yBetkbS+h7PwJ2rmWDwbrdyJKvVBhqf8kSn+VU2LePSIcJj19 10 | 3LDyWhV1H4lqNkUkcAR76Fh9qjMvA2p0vJ66+eDLXlph/RYapQx9HgOj/0BmAKMr 11 | YCyo5BhRih+Ougg8aK4G9PQTIA5G2wTWW2QkHxM/QppFjZd/XwQeJ2H6ubWMFc5f 12 | ttf6AzpJvFIDBu/JDCKWiCu5m8t4GL8w2OrIx8Js19lF4YYE2eojCreqgPi64S3o 13 | cqwKsDoySTw6/5iKQ5BUYwUXX3z7EXOqD8SMHefUKeczj4WvAaZLzR27qXm55EgR 14 | YQAIX4fhmY7NfSop3Wh0Eo62+JHoM/1g+UgOXlbnWpY95Mgd7/fwDSWLu4IxE0/u 15 | q8VufIbfC4yrY8qlTVfAffI1ldRdvJjPJBPiQ0CNrOl60LVptpkGc9shH7wZ2bP0 16 | bEnYKTgLAfOzD8Ut71O2AOIa80A1GNFl4Yle/MSNJOcQOSpgtWdREzIUoenAjfuz 17 | M4OeTr4cRg4+VYTAo9KHKriN1DuewNzGd8WjKAVHmcIMjqISLTlzMhdsdm+OmfQ6 18 | OvyX7v0GTOBbhP09NGcww5A0gCzXN18FS5oxnxe6OG9D0wIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQAqL1TWI1PTMm3JaXkhdTBe8tsk7+FsHAFzTcBVBsB8dkJNGhxb 20 | dlu7XIm+AyGUn0j8siz8qojKbO+rEPV/ImTH5W7Q36rXSdgvNUWpKrKIC5S8PUF5 21 | T4pH+lpYIlQHnTaKMuqH3nO3I40IhEhPaa2wAwy2kDlz46fJcr6aMzj6Zg43J5UK 22 | Zid+BQsiWAUau5V7CpC7GMCx4YdOZWWsT3dAsug9hvwTe81kK1JoTH0juwPTBH0t 23 | xUgUVIWyuweM1UwYF3n8Hmwq6B46YmujhMDKT+3lgqZt7eZ1XvieLdBRlVQWzOa/ 24 | 6QYTkrqwPZioKIStrxVGYjk40qECNodCSCIwRDgbnQubRWrdslxiIyc5blJNuOV+ 25 | jgv5d2EeUpwUjvpZuEV7FqPKGRgiG0jfl6Psms9gYUXd+y3ytG9HeoDNmLTSTBE4 26 | nCQXX935P2/xOuok6CpiGpP89DX7t8yiwk8LFNnY3rvv50nVy8kerVdnfHTmoMZ9 27 | /IBgojSIKov4lmPKdgzFfimzhbssVCa4DO/LIhTF7bQbH1ut/Oq7npdOpMjLYIBE 28 | 9lagvRVTVFwT/uwrCcXHCb21b/puwV94SNXVwt7BheFTFBdtxJrR4jjr2T5odLkX 29 | 6nQcY8V2OT7KOxn0KVc6pl3saJTLmL+H/3CtAao9NtmuUDapKINRSVNyvg== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys1/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys2/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMTlaGA8zMDE5MDcxMDE2 6 | MTIxOVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDdToR60na6NuR9iSAUMyzPXJNMWVQbLyT5/iZCiJ3BB4YWMBhfxpJWJiWXcM+z 9 | nDgpJuyCEeh5Dp6ZY3Fe7k6Hht6FmFpDjwnjpQmdkEKUg00G+ElPTp/UsmsPL+JA 10 | swPqBZWpMBS3dsXQNunMMtMGlrf5S0l6XX4y7kc/GTxYgveWZ9JtR/m2KNer+wjg 11 | BHqJ4rPqnHB30sDYPZg91Cz1Ak8Bb2w2I108zQVgKK6eIqNKXJJ/4pizSZdU4920 12 | wMxYBpnfDAchnxei9U/v3QbT7eKUI2fGr+hOWTIWU80+VeOBt8a6P4sS9AQh5/6G 13 | 8qwmAqO3YQ9dxN82iu/H3+N+GGa/M0r5rEWrzwIuFhwKvyQcpPRBm2yQnBnhL9G5 14 | kN6n4OBM0KsgZ3CYlHZSg4eWcNgBt1WCFsQc7vfUFaJnr8QP3pF4V/4Bok7wTO5H 15 | N0A1EYEVYuX53NGnrKVe+Fg9+xMOgXPWkUNqdvpI9ZbV3Z0S5866qF3/vBZrhgCr 16 | Kc5E/vMexBRe8Ki4wKqONVhi9WGUcRHvFEikc+7VrPj0YaG6zVLd+uOAJN81fKOP 17 | Yo4X4sZrMyPYl3OjGtMhfV4KvCaLEr1duOklqO6cCvGQ8iAlLVy3VJyW5GJ0D0Ky 18 | iAir4VNdAJKo1ZgiGivJLWulTfjUifCN9o115AiqJxiqwwIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQCQOdwD7eRIxBvbQHUc+m0TRzEa17BCfck1Y2WwN3TZXDGSkPVE 20 | 0uujA8SL3qi8/CTLGRqI9U3gRZJf+tJPBF/P021PEmyaFTS4htxcDxTxuZv2jCo9 21 | +XhUEyvRWitTmoy1esq3mkotVQHeTmQvwCsQJAhctVA/hRdJwmMPs1B8QxOUI6Bq 22 | SOBHa9CsXIzVOFv8FqE91PZA2ns30sKQYrrnbH99apfF5WglLUoyPwxf2e3AACh7 23 | beEdk45ivvKwi5Jk8nr85KDHYPlqkr0bd9Ehl8xplaNBdMPeRufqBDlztjcLJ3wo 24 | mnrt95gQMeSoLHY3UNsIRjbj43zImu7q9v/DD9ppQpu26aRDRmBNgLZA9GM5XnbZ 25 | RFi3VxLyqasGcSzaHwz5c7vOBOkOdlqcQzISRvWDxiN1HkAL+hkiQCuMchgORAgM 26 | wzPooa8rfWtLIpOXMpwuVGb/8rGNLEPovoCK9z6c+WZ+zkRo4+3TQkOMY66Xht7r 27 | Ahly3ler+Tyg6a5jXT92WKC/MXBYAy2ZQNoy204kNKevcH7R2cSkxITd3n5EacNy 28 | 5MAtCNIk7JweLCh9rLrLUBt+i4n44sP+LVhfWHemngA8CoF4n6eQ0pp0ixZTen0j 29 | 4uN0G2Nf+JeGMlqoObLWdIOdH/pbDppXGoZaKKDd7+bA74Fle5Uh7+1e3A== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys2/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys3/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMjJaGA8zMDE5MDcxMDE2 6 | MTIyMlowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQC8mVDToHbkUF2gRdVfpydZLNKeQ38d6HZFkUM3U1dWLZFSZNvagN8hlQvY/tQu 9 | 3A40p19WgKbzWZre3tg1Akw8Jztdz9gl4RMn142IIO3CiwIptkE0JopbZhmG5fAC 10 | 2n/MXQtfieI3hzeR04LW4JgLKzf3Nn8xZdlBgJfBmL5qUUnE7O7IbJGGma6gSD3e 11 | wetE6KQZtNtf0xRIv08doZKYwTl6ItkdGK76ufqq098GVwWvA1wSune4+MFgs9N4 12 | eFJj6Jyt85fiK/cwPx7KRdgYgBzrZQ4EPshRnwWrBTieOOaJvAA2RMxMEYzKRrJA 13 | AsYI1zxtNyqIUaBTcxmaz+NXUGW+wHwITic0Gp/XQm2Lwr/lxIV6OnAlL3CgbSXi 14 | rSnoG+eHQ+vDzBAcRDkTAgv/GUIzlfqT2StTK02uIBgJYzvFTG4plHitccRfy8wx 15 | sh5Z8xG99lmPQQtLsnlQAV+Li06Cb8CH4hUVoiWiVs5QAahqWmv5fpoX0Es26RyU 16 | HXGbjE202pyMMA7jUerUVKMijOoGZtcH6zB4p/dJ0TtToRwOgrA7NCI9AYVtqVXr 17 | XG/udj8ur2r1bTVwIbHsOeTEP3gY0mHRWm2E/bLjt9vbYIRUxR8xWnLkbeBziNTw 18 | g+36jdDF+6gu3cUz/nbSn8YY+Y1jjXuM3lqF8iMaAobhuwIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQAe2kC0HjKZU+dlnU2RlfBpB4QgzzrFE5N9A8F1MlE4vV3AzCg1 20 | RVdHPvniXzdNhDiiflK0l/cnrFv2X1TzYMrrA677/usHf2Bw0xjm/ipHOt5V+4TN 21 | mZAIA4IPl09gP28IZLc9xSuq4FoHeM8OTxhttOlINhqpG9P5d6bPezW6ZzI3CdPP 22 | CF69xK4GFlj/NQnAoFogid4ojYYNTj/cM4PYQU2KbrlzLyPuUk/CgwefXLMH87/H 23 | e3kPDev80Tjv2Pm5nD937fZfgrEoyolKxiRVcfZVMxR7qhPhizjueD0DAkfQIs7L 24 | YVSyx/qjEv2bBYaim5RQakUeHR1Xu5Xj/k5zr33t979ede50byQrcWm4H5JxnEpD 25 | JxJnFfDOU6o14SKGHSrao5Z4C3dI55DM84WLASnlMI5BK4XtS3notLNzG8dfWWhT 26 | 9m0Hcry+wPNDcGr8Mtj1los/0bMDqMHC4jcFW1hrXCUUs9RYzE+N/xoqwCQSgN1P 27 | E73uXTySWj5ovMR5TPF6PhcftLB/OziqO7FverEBpvGGHUAnUT61JtjodjXPbEdj 28 | 0VgyMOBY2y53HTXnx3dxeFZkUdRX/VZYy8tMK3MTY+7UIU5cWYnCZAo5LNcc0ukR 29 | S6WS9+6eaQ6XRjhfNUjx9a7FzqapWdtTedpipmBP1Njap3g29iUuVnLQeg== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys3/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys4/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMjVaGA8zMDE5MDcxMDE2 6 | MTIyNVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDZnDoDHE2nj82xDjH0Tb7OXMqQDHz+zbLidt6MSI1XB3vOAIEiPqrtenGnqRbV 9 | Fcm5GZxvxh4YQD8CjKSk1qgZJczs0DPSiGQ8Efl4PGO4xnEbllgL3PURPWp7mEV3 10 | oh6fxICgQKTBlT671EnFzB5lyJWpumRzvA1vyhBMsY8aO+xdq5LUFltYzBdvpgLX 11 | VaDwHZQ2PQEWtF0d0JO2N0WFFDGNmx6n8pKSeIAVDsTwZCZK+FCeeEyoGfXsINsc 12 | 0yCMQslawkfOMqA9yBV3Ji6QmFYKyGYt65MWGNqPA4XrIyliKwCCXwz9mjaWyN7r 13 | Ayw9cWlLMODNmDORWzGRZ5290MEAEIZsqjYHVitRTM/RnNIadToZGO0y5uAkM14c 14 | mTvnsK1CP92qtfSisq75W/I91drThoEtTK78UGOl/5Q1YBR08F+tSUWZWyHeI6UO 15 | BUCGC2bCtmzKMl7vU25lG6mbCR1JuQi6RYpnfMjXH36lV4S7fTvSwwuR03h2F3H1 16 | eFkWNG2lbFrW0dzDCPg3lXwmFQ65hUcQhctznoBz5C1lF2eW03wuVgxinnuVlJHj 17 | y/GrqmWsASn1PDuVs4k7k6DJfwyHAiA0uxXrGfxYvp7H8j4+2YOmWiWl5xYgrEDj 18 | ur5n8Zx46PHQer2Avq3sbEGEe1MCtXJlj3drd5Him3m+NQIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQA40ax0dAMrbWikaJ5s6kjaGkPkYuxHNJbw047Do0hjw+ncXsxc 20 | QDHmWcoHHpgMQCx0+vp8y+oKZ4pnqNfGSuOTo7/l05oQW/NbWw9mHwTiLMeI18/x 21 | Ay+5LpOasw+omqWLbdbbWqL0o/RvtBdK2rkcHzTVzECgGSoxUFfZD+ck2odpH+aR 22 | sQVu86AZVfclN2mjMyFSqMItqRcVw7rqr3Xy6FcgRQPykUnpguCEgcc9c54c1lQ9 23 | Zpddt4ezY7cTdk86oh7yA8QFchvtE9Zb5dJ5Vu9bdy9ig1kyscPTm+SeyhXRchUo 24 | ql4H/czGBVMHUY41wY2VFz7HitECcTAIpS6QvcxxgYevGNjZZxyZvEA8SYpLMZyb 25 | omk4enDTLd/xK1yF7VFodTDEyq63IAm0NTQZUVvIDfJeuzuNz55uxgdUq2RLpaJe 26 | 0bvrt9Obz+f5j2jonb2e0BuucwSdTyFXkUCxMW+piIUGkyrguAhlcHohDLEo2uB/ 27 | iQ4fosGqqsl47b+TezT5pSSblkgUjiwz6eDpM4lQpx22MxsHVlxFHrcBNm0Td92v 28 | FixrmllamAZbEz1tB//0bipKaOOZuhANJfrgN8BC6v2ahl4/SBuut09a0Azyxqpp 29 | uCsyTnfNEd1W6c6noaq24s+7W7KKLIekuNn1NunnHqKqriEuH1xlxxPjYA== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys4/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys5/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMjlaGA8zMDE5MDcxMDE2 6 | MTIyOVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDgK5r5vdHtJFEgw7hGE/lzKaHcvwzr32armq0k9tYchJXfT3k1j1lXtBAdcUN3 9 | gSRKjgzH/vjbn0ea3AiDCUd2Mck/n0KcJZ43S5I7ZjP7rbav296bKCZ1Hr7r5gXY 10 | Fhk+3aUsVfDUqAPBwyP8KeV31ARVA/s+WPeWqs69QXTdyJuBYE5pr40v1Sf+ebUI 11 | nZ37uGY3kiO0Ex/JgcoQsGJzrWD/ztbRCFIvrdNJZd0pGvMlmTKp7XsMR3cpvqk7 12 | 70//MLCdyGW/1IArTSuD1vd7mBX1JyVXKycYN0vIOtbgxPOFutUyqDOeP7o51q4i 13 | PS3dCRgfmn/hWLwy+CtJe0BGKsb4tk0tKxo0se8v9JA8mUtnmzmMt4Y9jijOrCOB 14 | 7XwWKmJYEm8N5Ubcy6cp2oL8vQVtzz3PXrkFt+3cFt1jrjdpQYgH4jykkWDeOjEf 15 | y1FCwzsNRudLTvLhfLn86/ZT4cLZ9JI7/WW0IPC8Fc7lhznJ+bIQUeEndaGdgVkx 16 | uEg0MxdrMr0jU0IFoXySRXNRzcDWZShEjBTv7tnFxLmoNU+uJb/KpMH6sRYi3zs8 17 | 5ecaMKNyG+LDmBahUlHx5hKAH49O8855+AMhsg91ONZJldjQX0oZrIKzK5BpsqeT 18 | l4c2Yt/fALiZaeFk1pBEsvVeMOBCIuWE+b4UIEaLAOhxfwIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQB+2VXnqRqfG7H2/K0lgzxT+X9r1u+YDn0EaUGAG71s70Qnqbpn 20 | X7tBmCKLN6XgPL0HrN933nwiYrmfb8S33zZ7kw8GJDvaTamLNyem4/8qTBQmnRwe 21 | 6rQ7SY2l73Ig87mR0WTi+rTnTTtc66+/jLtFeaj0Ycl9hBZXHKiULSGhsbUbwtkz 22 | iuNlANhoNKXNIABRImUq6OwYhEQN0DwHXj79wkpyDYjKZwHuEZUknc8Pl2oQPBke 23 | mil3tsrvGRkwhisnXX7tqh6rWKVZNJkO68hy7XO9aTXjbcB/7Y1K83ISNEyGPsH/ 24 | pwFyd/j8O4modwh7Ulww1/hwcqnqiEFE3KzxX2pMh7VxeAmX2t5eXFZOlRx1lecM 25 | XRkVu19lYDKQHGSrGxng+BFlSOB96e5kXIbuIXKpPAACoBQ/JZYbtHks9H8OtNYO 26 | P2joqmnQ9wGkE5co1Ii//j2tuoCRCpK86mmbTlyNYvK+1/kkKcsaiiWXNrQsrIDZ 27 | BFs0FwX5g24OP5+brxTlRZE01R6St8lQj4IUwAcIzG8fFmMCWaYavrCZTeYaEiyF 28 | A0X2VA/vZ7x9D5P9Z5OakMhrMW+hJTYrpH1rm6KR7B26iU2kJRxTX7xQ9lrksqfB 29 | 7lX+q0iheeYA4cHbGJNWwWgd+FQsK/PTeiyr4rfqututdWA0IxoLRc3XFw== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys5/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys6/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMzJaGA8zMDE5MDcxMDE2 6 | MTIzMlowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDR/hagLMoJD/x5mH9wpFgcxmWqcCv3D79r2GEKA3Dc5PjHxiSOZNWyZOVgCvrj 9 | RGyb84Ry4RyFUTzwMagh4T4DsWZa5zgxPunPLtscMKMWQrpsIQc2cjhMdr/P8JMB 10 | tdiSFk6ZCI5f8GWQzj56gDz5Sqk44lM8+2OtGCkgN6FHORrai6yRZYHG4U28PQUl 11 | UJ7EKnAzjJemGING0vS7xdlWI4qiNO1DCi6T7IStsDHnXVKRpVHZBAhLVWQHZjHy 12 | n7KzWACwkLvSvyfKz4Fd/y/ynodt8uL29tAt41Qp2v3LrwR3RLMhWdhz1qfiKnCf 13 | 6NXwzJDR0uW/7/3f2vKzX2gYE8+v5qw7d2PWX1o6CpPTaUkapz/WbcypSqxEBQR5 14 | hF/FkhgdJAW/pQ4Azq53IF3P6y/YW6rc+nU6tUdCZxveCoSJt1AvsxLiMcnB+2o7 15 | XhOH98cwSg92osjGCiiNn4w4IX6kESXjbSdV9QSmOYfpPO+V1SiGheEUpanQZUbC 16 | fCZ7ri37vaxuWgt70VNnKVwrZmgqXXvGVv4A+p2mIOnY2mQ+F79ZroWfzAM8Y3JD 17 | 0QdAwyGsAlwsfJJ5w5uN9UCPz1P5TYhigHda0rtcnKcjWIeB0mLpbTwujfDT3WV4 18 | 7+zIV/NuuTZ3ae0WIYyY9bYTnvVrqTKYJ8fm6B37IpQfQQIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQAiF1hep1HUcSMjqQ5AGnr9/2s0HEdhscqaPPy2GhmOpFqPfxP9 20 | 92DwjtxllT6DKcCIN8xvHHCfASZuXoFg1oBfEHw3ub2HBhoiKpOxWkUP52Y8JqL9 21 | fJ+PL5FH5OJGzJExfIyPINFGNeu06go1u6elFWBSyxIf1iLZBrcUElFnIryHpQ5Q 22 | HqpwaTniIpAveFF5i8lNFcNHGq6gZmM3q46UyeC/fqB/YVOWdbjGgPwn8EvH5Wjp 23 | u4ivXhyatmf5SoJznYBM/8c+v1bMtMcgosVDzI3c/USSd4fHAey8YDkXnUpUokoC 24 | Dg87FdnKSz/Ga7hi4PfIzW/srK4cmD8Cm0ZTKNDvaY0BPFaNY/CiXWBrP7iWz+2h 25 | mZ9NBdKBxdTARjhVh5LhZYHZapnexxsaeW57kTJwIzYlxGvOjVkyFR5emY4h1Ou8 26 | mf1Kw3pvEukAHsGsXSSJINufk2xJG9wjOuMFP7dn9bu45pQENHjTIJyQSrTIG0Ch 27 | RGcbu7v8BmqnS1koZ5kop9uZSnxQbi6hrZ7zT6IMNJNggccNjdDsX2bmyQ+iRxAx 28 | HV+pbYNupuxwfpJ3cOmXDdV7NQvs71AAzbGX6piQvuc0Cbjv39N8QWwtDtdb8JDt 29 | 8NM69ECcnEY0f0uFwx0KxJOxMPCYHBBtC2mnZiXNnwI14W5lnS3Fu/I9+g== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys6/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/keys7/staker.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi 4 | czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT 5 | c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMzRaGA8zMDE5MDcxMDE2 6 | MTIzNFowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs 7 | YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 8 | AQDZJPNNEwmzvpv1XIaBrC4ShGrw2PF9oYKPx5+yASMc5LsA6yBgX4SN55B+0UNo 9 | MMqFaKmtXj7FxIZOoXdxLhNDCS1R0ccDdRtN319YerqJrHIJLlpdSKdgXp612g0Q 10 | 4T9ssbTK8tDMucKabDgL0IG29nm2TQuGdH3iralbZauUMjKhs2CRDEOxIG2t4dkA 11 | Djhn0zTyFhckXAXvHQoG9LCcNIoXzr8yOLN9XdX7PUtb94AsVVbF09j1V7kEn6jy 12 | bS3yNpV8t5OuTtUwIySGiECfKnSpYJuTnStgohyHTLumLHj3BH7Wu3pqJFIrz5yG 13 | FZZnwvwQuYeo6iagLoEoFValMBp+uxHjJDTMfmKEqAhgMaouPnF+AUUph+RdCXyE 14 | mH2YIcyqpzv8YIbc/OV46i4F3onIomJPVT8rUOHfxM9rWrPEbqUvgpO717adoO00 15 | D1Xo5MdLCo3leYtC/nGfwiJM2AfYs1zoUT8EAdb1qRO1F/o2bY5AolwDyAE7qsmn 16 | tfyueBLTozhqrTzafXvftUL2NXQCZwb5QF61DylxVjL3QUCuu3fvBGcH1CZcoMeC 17 | u3C+gwQsZCFeFyksYP75f/iByRY3gf/1zv4RZc+h/u8cpmWGXB1dmXqpsP2oIh3F 18 | toK/6tS5q9Z7vqFXv5P81euzd4ZPPnGIeqnWuecfW74cXQIDAQABMA0GCSqGSIb3 19 | DQEBCwUAA4ICAQALfCXM1Of922Ix3qyCnkvHb+RNkqXNJlFkYQTjYngMNIrDNSMQ 20 | haHzDyLVp/Cnf+xqPLPCrQR3SBp2EVKlpEPzDOZeuNfQZRGtvmmoSRc9VNc3T4E9 21 | qHkULoo8JDOjCrM8MdUiJ378UzlydiGKU9GP5NYZi25+02hGt4rmIYD9uo4RY/zg 22 | SI6rxHcw7wESUkbWDwE0zqQ000lr1lZCwFdonuwh0CERNECU7PGQFbhxr6UMJkUP 23 | Zlua6R/rwvelElAqiKk5xq5NOQmVPkkqvr28nkQxEafs8Skm36lzQ0ETkRwfDTWk 24 | LLQj8oaW+/tk396LtcjGqVGnDxxyxICAMyalcwH680tKQyHrx01/xDm7SRhcQq53 25 | wZmIaYfUNWOed0+HTmx50BlEf1KNyVaJMWsnb63rUYlSeq81zwQSwDT9/XgUZmTw 26 | FoCB+oJOEQncDutR40DRhSM5awsypfz47GSAbaluSmqEVT/J8PoicVX7kaQ2Zrwl 27 | l2pdV54NS9Q+JbtOX94ET207QeggJPRGoKBqDyPfD1zCYcjO22WTCMDzhgGseHC9 28 | buDRSb7AEB3wYmm+1X6ISTDC+maKQHsIfc906leO57iuDrK3B8oS7P8RQ+JPzy9a 29 | yeXEa+Inysj3UCDWSZav3Tr6VADJYLlk0Zksk7cClV23FmdIru2zE+Zj1g== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /certs/keys7/staker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /cfg/cfg.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | // Package cfg manages the configuration file for avash 5 | package cfg 6 | 7 | import ( 8 | "fmt" 9 | "go/build" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | "strings" 14 | "time" 15 | 16 | "github.com/ava-labs/avash/utils/logging" 17 | "github.com/mitchellh/go-homedir" 18 | "github.com/spf13/viper" 19 | ) 20 | 21 | // Configuration is a shell-usable wrapper of the config file 22 | type Configuration struct { 23 | AvalancheLocation, DataDir string 24 | Log logging.Log 25 | } 26 | 27 | type configFile struct { 28 | AvalancheLocation, DataDir string 29 | Log configFileLog 30 | } 31 | 32 | type configFileLog struct { 33 | Terminal, LogFile, Dir string 34 | } 35 | 36 | // Config is a global instance of the shell configuration 37 | var Config Configuration 38 | 39 | // DefaultCfgName is the default config filename 40 | const DefaultCfgName = ".avash.yaml" 41 | 42 | // DefaultCfgNameShort is the default config filename with yml extension 43 | const DefaultCfgNameShort = ".avash.yml" 44 | 45 | // InitConfig initializes the config for commands to reference 46 | func InitConfig(cfgpath string) { 47 | cfgname := DefaultCfgName 48 | if cfgpath != "" { 49 | cfgpath, cfgname = filepath.Split(cfgpath) 50 | viper.AddConfigPath(cfgpath) 51 | } 52 | if !strings.HasSuffix(cfgname, ".yaml") && !strings.HasSuffix(cfgname, ".yml") { 53 | fmt.Println("Config filename must end with extension '.yaml' or '.yml'") 54 | os.Exit(1) 55 | } 56 | viper.SetConfigName(cfgname) 57 | viper.SetConfigType("yaml") 58 | viper.AddConfigPath("$HOME/") 59 | viper.AddConfigPath(".") 60 | viper.AddConfigPath("/etc/avash/") 61 | if err := viper.ReadInConfig(); err != nil { 62 | if _, ok := err.(viper.ConfigFileNotFoundError); !ok { 63 | fmt.Printf("Invalid config path: %s%s\n", cfgpath, cfgname) 64 | os.Exit(1) 65 | } 66 | 67 | // try finding filename with yml extension 68 | viper.SetConfigName(DefaultCfgNameShort) 69 | if err := viper.ReadInConfig(); err != nil { 70 | if _, ok := err.(viper.ConfigFileNotFoundError); ok { 71 | fmt.Printf("Config file not found: %s%s\n", cfgpath, cfgname) 72 | home, _ := homedir.Dir() 73 | os.OpenFile(home+"/"+cfgname, os.O_RDONLY|os.O_CREATE, 0644) 74 | fmt.Printf("Created empty config file: %s/%s\n", home, cfgname) 75 | viper.SetConfigFile(home + "/" + cfgname) 76 | } 77 | } 78 | } 79 | 80 | if err := viper.ReadInConfig(); err != nil { 81 | fmt.Println("Can't read config:", err) 82 | os.Exit(1) 83 | } 84 | 85 | var config configFile 86 | if err := viper.Unmarshal(&config); err != nil { 87 | fmt.Printf("Unable to decode config into struct, %v\n", err) 88 | os.Exit(1) 89 | } 90 | 91 | // Set default `avalancheLocation` if missing 92 | if config.AvalancheLocation == "" { 93 | gopath := os.Getenv("GOPATH") 94 | if gopath == "" { 95 | gopath = build.Default.GOPATH 96 | } 97 | config.AvalancheLocation = path.Join(gopath, "src", "github.com", "ava-labs", "avalanchego", "build", "avalanchego") 98 | } 99 | if _, err := os.Stat(config.AvalancheLocation); err != nil { 100 | fmt.Printf("Invalid avalanchego binary location: %s\n", config.AvalancheLocation) 101 | fmt.Println("Make sure your $GOPATH is set or provide a configuration file with a valid `avalancheLocation` value. See README.md for more details.") 102 | os.Exit(1) 103 | } 104 | 105 | // Set default `datadir` if missing 106 | if config.DataDir == "" { 107 | wd, _ := os.Getwd() 108 | defaultDataDir := wd + "/stash" 109 | config.DataDir = defaultDataDir 110 | } 111 | if err := os.MkdirAll(config.DataDir, os.ModePerm); err != nil { 112 | fmt.Println(err) 113 | os.Exit(1) 114 | } 115 | 116 | // Configure and create log 117 | logCfg := makeLogConfig(config.Log, config.DataDir) 118 | log, err := logging.New(logCfg) 119 | if err != nil { 120 | fmt.Println(err) 121 | os.Exit(1) 122 | } 123 | 124 | Config = Configuration{ 125 | AvalancheLocation: config.AvalancheLocation, 126 | DataDir: config.DataDir, 127 | Log: *log, 128 | } 129 | Config.Log.Info("Config file set: %s", viper.ConfigFileUsed()) 130 | Config.Log.Info("Avash successfully configured.") 131 | } 132 | 133 | func makeLogConfig(config configFileLog, dataDir string) logging.Config { 134 | terminalLvl, err := logging.ToLevel(config.Terminal) 135 | if err != nil && config.Terminal != "" { 136 | fmt.Printf("Invalid terminal log level '%s', defaulting to %s\n", config.Terminal, terminalLvl.String()) 137 | } 138 | logFileLvl, err := logging.ToLevel(config.LogFile) 139 | if err != nil && config.LogFile != "" { 140 | fmt.Printf("Invalid logfile log level '%s', defaulting to %s\n", config.LogFile, logFileLvl.String()) 141 | } 142 | if config.Dir == "" { 143 | defaultLogDir := dataDir + "/logs" 144 | config.Dir = defaultLogDir 145 | } 146 | return logging.Config{ 147 | RotationInterval: 24 * time.Hour, 148 | FileSize: 1 << 23, // 8 MB 149 | RotationSize: 7, 150 | FlushSize: 1, 151 | DisableContextualDisplaying: true, 152 | DisplayLevel: terminalLvl, 153 | LogLevel: logFileLvl, 154 | Directory: config.Dir, 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /cfg/rpc.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cfg 5 | 6 | import ( 7 | "net/http" 8 | 9 | "github.com/gorilla/mux" 10 | "github.com/gorilla/rpc" 11 | "github.com/gorilla/rpc/json" 12 | ) 13 | 14 | // AvashRPC is the RPCService handler for the avash service 15 | var AvashRPC *RPCService 16 | 17 | // RPCService is for maintaining a reference to the root JSON RPC server and the HTTP router 18 | type RPCService struct { 19 | RPCServer *rpc.Server 20 | HTTPRouter *mux.Router 21 | urlpath string 22 | host string 23 | port string 24 | endpoints map[string]interface{} 25 | } 26 | 27 | // RegisterServer registers the adds the rpc and http servers to the plugins service 28 | func (rpcsrv *RPCService) RegisterServer(s *rpc.Server, r *mux.Router) { 29 | rpcsrv.RPCServer = s 30 | rpcsrv.HTTPRouter = r 31 | } 32 | 33 | // Initialize creates the RPC server at the provided baseurl, hostname, and port 34 | func (rpcsrv *RPCService) Initialize(urlpath string, host string, port string) { 35 | rpcsrv.urlpath = urlpath 36 | rpcsrv.host = host 37 | rpcsrv.port = port 38 | if rpcsrv.urlpath == "" { 39 | rpcsrv.urlpath = "/rpc" 40 | } 41 | if host == "" { 42 | rpcsrv.host = "localhost" 43 | } 44 | if port == "" { 45 | rpcsrv.port = "9020" 46 | } 47 | s := rpc.NewServer() 48 | r := mux.NewRouter() 49 | rpcsrv.RegisterServer(s, r) 50 | s.RegisterCodec(json.NewCodec(), "application/json") 51 | s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8") 52 | s.RegisterService(rpcsrv, "") 53 | r.Handle(rpcsrv.urlpath, s) 54 | go http.ListenAndServe(rpcsrv.host+":"+rpcsrv.port, r) 55 | } 56 | 57 | // AddService registers the appropriate endpoint for the plugin given a symbol 58 | func (rpcsrv *RPCService) AddService(serviceInstance interface{}, endpoint string) { 59 | rpcsrv.RPCServer.RegisterService(serviceInstance, "") 60 | rpcsrv.HTTPRouter.Handle(rpcsrv.urlpath+"/"+endpoint, rpcsrv.RPCServer) 61 | } 62 | -------------------------------------------------------------------------------- /cmd/avawallet.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | 10 | "github.com/ava-labs/avalanchego/utils/crypto" 11 | "github.com/ava-labs/avalanchego/utils/formatting" 12 | "github.com/ava-labs/avash/cfg" 13 | "github.com/ava-labs/avash/node" 14 | pmgr "github.com/ava-labs/avash/processmgr" 15 | "github.com/spf13/cobra" 16 | 17 | "github.com/ybbus/jsonrpc" 18 | ) 19 | 20 | // AVAXWalletCmd represents the avaxwallet command 21 | var AVAXWalletCmd = &cobra.Command{ 22 | Use: "avaxwallet", 23 | Short: "Tools for interacting with AVAX Payments over the network.", 24 | Long: `Tools for interacting with AVAX Payments over the network. Using this 25 | command you can send, and get the status of a transaction.`, 26 | Run: func(cmd *cobra.Command, args []string) { 27 | cmd.Help() 28 | }, 29 | } 30 | 31 | const ( 32 | defaultEncoding = formatting.CB58 33 | ) 34 | 35 | // AVAXWalletNewKeyCmd creates a new private key 36 | var AVAXWalletNewKeyCmd = &cobra.Command{ 37 | Use: "newkey", 38 | Short: "Creates a random private key.", 39 | Long: `Creates a random private key.`, 40 | Run: func(cmd *cobra.Command, args []string) { 41 | log := cfg.Config.Log 42 | factory := crypto.FactorySECP256K1R{} 43 | if skGen, err := factory.NewPrivateKey(); err == nil { 44 | sk := skGen.(*crypto.PrivateKeySECP256K1R) 45 | str, err := formatting.EncodeWithChecksum(defaultEncoding, sk.Bytes()) 46 | if err != nil { 47 | log.Error("could not encode private key") 48 | } 49 | log.Info("PrivateKey: PrivateKey-%s", str) 50 | } else { 51 | log.Error("could not create private key") 52 | } 53 | }, 54 | } 55 | 56 | // AVAXWalletSendCmd will send a transaction through a node 57 | var AVAXWalletSendCmd = &cobra.Command{ 58 | Use: "send [node name] [tx string]", 59 | Short: "Sends a transaction to a node.", 60 | Long: `Sends a transaction to a node.`, 61 | Run: func(cmd *cobra.Command, args []string) { 62 | if len(args) >= 2 { 63 | log := cfg.Config.Log 64 | if meta, err := pmgr.ProcManager.Metadata(args[0]); err == nil { 65 | var md node.Metadata 66 | metaBytes := []byte(meta) 67 | if err := json.Unmarshal(metaBytes, &md); err == nil { 68 | jrpcloc := fmt.Sprintf("http://%s:%s/ext/bc/avm", md.Serverhost, md.HTTPport) 69 | rpcClient := jsonrpc.NewClient(jrpcloc) 70 | response, err := rpcClient.Call("avm.issueTx", struct { 71 | Tx string 72 | }{ 73 | Tx: args[1], 74 | }) 75 | if err != nil { 76 | log.Error("error sent tx: %s", args[1]) 77 | log.Error("rpcClient returned error: %s", err.Error()) 78 | } else if response.Error != nil { 79 | log.Error("error sent tx: %s", args[1]) 80 | log.Error("rpcClient returned error: %d, %s", response.Error.Code, response.Error.Message) 81 | } else { 82 | var s struct { 83 | TxID string 84 | } 85 | err = response.GetObject(&s) 86 | if err != nil { 87 | log.Error("error on parsing response: %s", err.Error()) 88 | } else { 89 | log.Info("TxID:%s", s.TxID) 90 | } 91 | } 92 | } else { 93 | log.Error("unable to unmarshal metadata for node %s: %s", args[0], err.Error()) 94 | } 95 | } else { 96 | log.Error("node not found: %s", args[0]) 97 | } 98 | } else { 99 | cmd.Help() 100 | } 101 | }, 102 | } 103 | 104 | // AVAXWalletStatusCmd will get the status of a transaction for a particular node 105 | var AVAXWalletStatusCmd = &cobra.Command{ 106 | Use: "status [node name] [tx id]", 107 | Short: "Checks the status of a transaction on a node.", 108 | Long: `Checks the status of a transaction on a node.`, 109 | Run: func(cmd *cobra.Command, args []string) { 110 | if len(args) >= 2 { 111 | log := cfg.Config.Log 112 | if meta, err := pmgr.ProcManager.Metadata(args[0]); err == nil { 113 | var md node.Metadata 114 | metaBytes := []byte(meta) 115 | if err := json.Unmarshal(metaBytes, &md); err == nil { 116 | jrpcloc := fmt.Sprintf("http://%s:%s/ext/bc/avm", md.Serverhost, md.HTTPport) 117 | rpcClient := jsonrpc.NewClient(jrpcloc) 118 | response, err := rpcClient.Call("avm.getTxStatus", struct { 119 | TxID string 120 | }{ 121 | TxID: args[1], 122 | }) 123 | if err != nil { 124 | log.Error("error sent txid: %s", args[1]) 125 | log.Error("rpcClient returned error: %s", err.Error()) 126 | } else if response.Error != nil { 127 | log.Error("error sent txid: %s", args[1]) 128 | log.Error("rpcClient returned error: %d, %s", response.Error.Code, response.Error.Message) 129 | } else { 130 | var s struct { 131 | Status string 132 | } 133 | err = response.GetObject(&s) 134 | if err != nil { 135 | log.Error("error on parsing response: %s", err.Error()) 136 | } else { 137 | log.Info("Status:%s", s.Status) 138 | } 139 | } 140 | } else { 141 | log.Error("unable to unmarshal metadata for node %s: %s", args[0], err.Error()) 142 | } 143 | } else { 144 | log.Error("node not found: %s", args[0]) 145 | } 146 | } else { 147 | cmd.Help() 148 | } 149 | }, 150 | } 151 | 152 | // AVAXWalletGetBalanceCmd will get the balance of an address from a node 153 | var AVAXWalletGetBalanceCmd = &cobra.Command{ 154 | Use: "balance [node name] [address]", 155 | Short: "Checks the balance of an address from a node.", 156 | Long: `Checks the balance of an address from a node.`, 157 | Run: func(cmd *cobra.Command, args []string) { 158 | if len(args) >= 2 { 159 | log := cfg.Config.Log 160 | if meta, err := pmgr.ProcManager.Metadata(args[0]); err == nil { 161 | var md node.Metadata 162 | metaBytes := []byte(meta) 163 | if err := json.Unmarshal(metaBytes, &md); err == nil { 164 | jrpcloc := fmt.Sprintf("http://%s:%s/ext/bc/avm", md.Serverhost, md.HTTPport) 165 | rpcClient := jsonrpc.NewClient(jrpcloc) 166 | response, err := rpcClient.Call("avm.getBalance", struct { 167 | Address string 168 | AssetID string 169 | }{ 170 | Address: args[1], 171 | AssetID: "AVAX", 172 | }) 173 | if err != nil { 174 | log.Error("error sent address: %s", args[1]) 175 | log.Error("rpcClient returned error: %s", err.Error()) 176 | } else if response.Error != nil { 177 | log.Error("error sent address: %s", args[1]) 178 | log.Error("rpcClient returned error: %d, %s", response.Error.Code, response.Error.Message) 179 | } else { 180 | var s struct { 181 | Balance string 182 | } 183 | err = response.GetObject(&s) 184 | if err != nil { 185 | log.Error("error on parsing response: %s", err.Error()) 186 | } else { 187 | log.Info("Balance: %s", s.Balance) 188 | } 189 | } 190 | } else { 191 | log.Error("unable to unmarshal metadata for node %s: %s", args[0], err.Error()) 192 | } 193 | } else { 194 | log.Error("node not found: %s", args[0]) 195 | } 196 | } else { 197 | cmd.Help() 198 | } 199 | }, 200 | } 201 | 202 | /* 203 | avaxwallet 204 | create [wallet name] -> "wallet created: " + [wallet name] 205 | addkey [wallet name] [private key] -> address 206 | balance [node name] [address] -> uint 207 | status [node name] [tx string] -> [status] 208 | maketx [wallet name] [destination address] [amount] -> txString 209 | refresh [node name] [wallet name] -> "wallet refreshed: " + [wallet name] 210 | remove [wallet name] [tx string] -> "transaction removed: " + [tx string] 211 | send [node name] [tx string] -> "sent tx: " [tx string] 212 | newkey -> privateKey 213 | */ 214 | 215 | func init() { 216 | AVAXWalletCmd.AddCommand(AVAXWalletNewKeyCmd) 217 | AVAXWalletCmd.AddCommand(AVAXWalletGetBalanceCmd) 218 | AVAXWalletCmd.AddCommand(AVAXWalletSendCmd) 219 | AVAXWalletCmd.AddCommand(AVAXWalletStatusCmd) 220 | } 221 | -------------------------------------------------------------------------------- /cmd/callrpc.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/ava-labs/avash/cfg" 8 | "github.com/ava-labs/avash/node" 9 | pmgr "github.com/ava-labs/avash/processmgr" 10 | "github.com/spf13/cobra" 11 | "github.com/ybbus/jsonrpc" 12 | ) 13 | 14 | // CallRPCCmd issues an RPC to a node endpoint using JSONRPC protocol 15 | var CallRPCCmd = &cobra.Command{ 16 | Use: "callrpc [node name] [endpoint] [method] [JSON params] [var scope] [var name]", 17 | Short: "Issues an RPC call to a node.", 18 | Long: `Issues an RPC call to a node endpoint for the specified method and params. 19 | Response is saved to the local varstore.`, 20 | Example: `callrpc n1 ext/bc/X avm.getBalance {"address":"X-KqpU28P2ipUxfTfwaT847wWxyXB4XuWad","assetID":"AVAX"} s v`, 21 | Args: cobra.MinimumNArgs(6), 22 | Run: func(cmd *cobra.Command, args []string) { 23 | log := cfg.Config.Log 24 | meta, err := pmgr.ProcManager.Metadata(args[0]) 25 | if err != nil { 26 | log.Error("process not found: %s", args[0]) 27 | return 28 | } 29 | var md node.Metadata 30 | if err = json.Unmarshal([]byte(meta), &md); err != nil { 31 | log.Error("unable to unmarshal metadata for process %s: %s", args[0], err.Error()) 32 | return 33 | } 34 | base := "http" 35 | if md.HTTPTLS { 36 | base = "https" 37 | } 38 | jrpcloc := fmt.Sprintf("%s://%s:%s/%s", base, md.Serverhost, md.HTTPport, args[1]) 39 | log.Info(jrpcloc) 40 | rpcClient := jsonrpc.NewClient(jrpcloc) 41 | argMap := make(map[string]interface{}) 42 | if err = json.Unmarshal([]byte(args[3]), &argMap); err != nil { 43 | log.Error("invalid JSON object: %s", args[3]) 44 | return 45 | } 46 | response, err := rpcClient.Call(args[2], argMap) 47 | if err != nil { 48 | log.Error("rpcClient returned error: %s", err.Error()) 49 | return 50 | } 51 | if response.Error != nil { 52 | log.Error("rpcClient returned error: %d, %s", response.Error.Code, response.Error.Message) 53 | return 54 | } 55 | resBytes, err := json.Marshal(response.Result) 56 | if err != nil { 57 | log.Error("rpcClient returned invalid JSON object: %v", response.Result) 58 | return 59 | } 60 | resVal := string(resBytes) 61 | log.Info("Response: %s", resVal) 62 | store, err := AvashVars.Get(args[4]) 63 | if err != nil { 64 | log.Error("store not found: %s", args[4]) 65 | return 66 | } 67 | store.Set(args[5], resVal) 68 | log.Info("Response saved to %q.%q", args[4], args[5]) 69 | }, 70 | } 71 | -------------------------------------------------------------------------------- /cmd/exit.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | package cmd 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/ava-labs/avash/cfg" 9 | pmgr "github.com/ava-labs/avash/processmgr" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // ExitCmd represents the exit command 14 | var ExitCmd = &cobra.Command{ 15 | Use: "exit", 16 | Short: "Exit the shell.", 17 | Long: `Exit the shell, attempting to gracefully stop all processes first.`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | pmgr.ProcManager.StopAllProcesses() 20 | if pmgr.ProcManager.HasRunning() { 21 | cfg.Config.Log.Fatal("Unable to stop all processes, exiting anyway...") 22 | os.Exit(1) 23 | } 24 | cfg.Config.Log.Info("Cleanup successful, exiting...") 25 | os.Exit(0) 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /cmd/network.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/ava-labs/avash/cfg" 5 | "github.com/ava-labs/avash/network" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // NetworkCommand represents the network command 10 | var NetworkCommand = &cobra.Command{ 11 | Use: "network", 12 | Short: "Tools for interacting with remote hosts.", 13 | Long: `Tools for interacting with remote hosts.`, 14 | Run: func(cmd *cobra.Command, args []string) { 15 | cmd.Help() 16 | }, 17 | } 18 | 19 | // SSHDeployCommand deploys a network config through an SSH client 20 | var SSHDeployCommand = &cobra.Command{ 21 | Use: "deploy [config file]", 22 | Short: "Deploys a remote network of nodes.", 23 | Long: `Deploys a remote network of nodes from the provided config file.`, 24 | Run: func(cmd *cobra.Command, args []string) { 25 | log := cfg.Config.Log 26 | netCfg, err := network.InitConfig(args[0]) 27 | if err != nil { 28 | log.Error(err.Error()) 29 | return 30 | } 31 | log.Info("Deployment starting... (this process typically takes 3-6 minutes depending on host)") 32 | if err := network.Deploy(netCfg, false); err != nil { 33 | log.Error(err.Error()) 34 | return 35 | } 36 | log.Info("All hosts finished.") 37 | }, 38 | } 39 | 40 | // SSHRemoveCommand removes a network config through an SSH client 41 | var SSHRemoveCommand = &cobra.Command{ 42 | Use: "remove [config file]", 43 | Short: "Removes a remote network of nodes.", 44 | Long: `Removes a remote network of nodes from the provided config file.`, 45 | Run: func(cmd *cobra.Command, args []string) { 46 | log := cfg.Config.Log 47 | netCfg, err := network.InitConfig(args[0]) 48 | if err != nil { 49 | log.Error(err.Error()) 50 | return 51 | } 52 | log.Info("Removal starting...") 53 | if err := network.Remove(netCfg, false); err != nil { 54 | log.Error(err.Error()) 55 | return 56 | } 57 | log.Info("All hosts finished.") 58 | }, 59 | } 60 | 61 | func init() { 62 | NetworkCommand.AddCommand(SSHDeployCommand) 63 | NetworkCommand.AddCommand(SSHRemoveCommand) 64 | } -------------------------------------------------------------------------------- /cmd/procmanager.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | "strconv" 8 | "time" 9 | 10 | "github.com/ava-labs/avash/cfg" 11 | pmgr "github.com/ava-labs/avash/processmgr" 12 | "github.com/olekukonko/tablewriter" 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | // ProcmanagerCmd represents the procmanager command 17 | var ProcmanagerCmd = &cobra.Command{ 18 | Use: "procmanager", 19 | Short: "Access the process manager for the avash client.", 20 | Long: `Access the process manager for the avash client. Using this 21 | command you can list, stop, and start processes registered with the 22 | process manager.`, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | cmd.Help() 25 | }, 26 | } 27 | 28 | // PMListCmd represents the list operation on the procmanager command 29 | var PMListCmd = &cobra.Command{ 30 | Use: "list", 31 | Short: "Lists the processes currently running.", 32 | Long: `Lists the processes currently running in tabular format.`, 33 | Run: func(cmd *cobra.Command, args []string) { 34 | table := tablewriter.NewWriter(AvalancheShell.rl.Stdout()) 35 | table = pmgr.ProcManager.ProcessTable(table) 36 | table.Render() 37 | }, 38 | } 39 | 40 | // PMMetadataCmd represents the list operation on the procmanager command 41 | var PMMetadataCmd = &cobra.Command{ 42 | Use: "metadata [node name]", 43 | Short: "Prints the metadata associated with the node name.", 44 | Long: `Prints the metadata associated with the node name.`, 45 | Run: func(cmd *cobra.Command, args []string) { 46 | if len(args) >= 1 && args[0] != "" { 47 | log := cfg.Config.Log 48 | name := args[0] 49 | metadata, err := pmgr.ProcManager.Metadata(name) 50 | if err != nil { 51 | log.Error(err.Error()) 52 | } 53 | log.Info(metadata) 54 | } else { 55 | cmd.Help() 56 | } 57 | }, 58 | } 59 | 60 | // PMStartCmd represents the start operation on the procmanager command 61 | var PMStartCmd = &cobra.Command{ 62 | Use: "start [node name] [optional: delay in secs]", 63 | Short: "Starts the process named if not currently running.", 64 | Long: `Starts the process named if not currently running.`, 65 | Run: func(cmd *cobra.Command, args []string) { 66 | if len(args) >= 1 && args[0] != "" { 67 | log := cfg.Config.Log 68 | name := args[0] 69 | delay := time.Duration(0) 70 | if len(args) >= 2 { 71 | if v, e := strconv.ParseInt(args[1], 10, 64); e == nil && v > 0 { 72 | delay = time.Duration(v) 73 | log.Info("process will start in %ds: %s", int(delay), name) 74 | } 75 | } 76 | start := func() { 77 | err := pmgr.ProcManager.StartProcess(name) 78 | if err != nil { 79 | log.Error(err.Error()) 80 | } 81 | } 82 | delayRun(start, delay) 83 | } else { 84 | cmd.Help() 85 | } 86 | }, 87 | } 88 | 89 | // PMStopCmd represents the stop operation on the procmanager command 90 | var PMStopCmd = &cobra.Command{ 91 | Use: "stop [node name] [optional: delay in secs]", 92 | Short: "Stops the process named if currently running.", 93 | Long: `Stops the process named if currently running.`, 94 | Run: func(cmd *cobra.Command, args []string) { 95 | if len(args) >= 1 && args[0] != "" { 96 | log := cfg.Config.Log 97 | name := args[0] 98 | delay := time.Duration(0) 99 | if len(args) >= 2 { 100 | if v, e := strconv.ParseInt(args[1], 10, 64); e == nil && v > 0 { 101 | delay = time.Duration(v) 102 | log.Info("process will stop in %ds: %s", int(delay), name) 103 | } 104 | } 105 | stop := func() { 106 | err := pmgr.ProcManager.StopProcess(name) 107 | if err != nil { 108 | log.Error(err.Error()) 109 | } 110 | } 111 | delayRun(stop, delay) 112 | } else { 113 | cmd.Help() 114 | } 115 | }, 116 | } 117 | 118 | // PMKillCmd represents the stop operation on the procmanager command 119 | var PMKillCmd = &cobra.Command{ 120 | Use: "kill [node name] [optional: delay in secs]", 121 | Short: "Kills the process named if currently running.", 122 | Long: `Kills the process named if currently running.`, 123 | Run: func(cmd *cobra.Command, args []string) { 124 | if len(args) >= 1 && args[0] != "" { 125 | log := cfg.Config.Log 126 | name := args[0] 127 | delay := time.Duration(0) 128 | if len(args) >= 2 { 129 | if v, e := strconv.ParseInt(args[1], 10, 64); e == nil && v > 0 { 130 | delay = time.Duration(v) 131 | log.Info("process will stop in %ds: %s", int(delay), name) 132 | } 133 | } 134 | kill := func() { 135 | err := pmgr.ProcManager.KillProcess(name) 136 | if err != nil { 137 | log.Error(err.Error()) 138 | } 139 | } 140 | delayRun(kill, delay) 141 | } else { 142 | cmd.Help() 143 | } 144 | }, 145 | } 146 | 147 | // PMKillAllCmd stops all processes in the procmanager 148 | var PMKillAllCmd = &cobra.Command{ 149 | Use: "killall [optional: delay in secs]", 150 | Short: "Kills all processes if currently running.", 151 | Long: `Kills all processes if currently running.`, 152 | Run: func(cmd *cobra.Command, args []string) { 153 | log := cfg.Config.Log 154 | delay := time.Duration(0) 155 | if len(args) >= 1 { 156 | if v, e := strconv.ParseInt(args[0], 10, 64); e == nil && v > 0 { 157 | delay = time.Duration(v) 158 | log.Info("all processes will be killed in %ds", int(delay)) 159 | } 160 | } 161 | delayRun(pmgr.ProcManager.KillAllProcesses, delay) 162 | }, 163 | } 164 | 165 | // PMStopAllCmd stops all processes in the procmanager 166 | var PMStopAllCmd = &cobra.Command{ 167 | Use: "stopall [optional: delay in secs]", 168 | Short: "Stops all processes if currently running.", 169 | Long: `Stops all processes if currently running.`, 170 | Run: func(cmd *cobra.Command, args []string) { 171 | log := cfg.Config.Log 172 | delay := time.Duration(0) 173 | if len(args) >= 1 { 174 | if v, e := strconv.ParseInt(args[0], 10, 64); e == nil && v > 0 { 175 | delay = time.Duration(v) 176 | log.Info("all processes will stop in %ds", int(delay)) 177 | } 178 | } 179 | delayRun(pmgr.ProcManager.StopAllProcesses, delay) 180 | }, 181 | } 182 | 183 | // PMStartAllCmd starts all processes in the procmanager 184 | var PMStartAllCmd = &cobra.Command{ 185 | Use: "startall [optional: delay in secs]", 186 | Short: "Starts all processes if currently stopped.", 187 | Long: `Starts all processes if currently stopped.`, 188 | Run: func(cmd *cobra.Command, args []string) { 189 | log := cfg.Config.Log 190 | delay := time.Duration(0) 191 | if len(args) >= 1 { 192 | if v, e := strconv.ParseInt(args[0], 10, 64); e == nil && v > 0 { 193 | delay = time.Duration(v) 194 | log.Info("all processes will start in %ds", int(delay)) 195 | } 196 | } 197 | delayRun(pmgr.ProcManager.StartAllProcesses, delay) 198 | }, 199 | } 200 | 201 | // PMRemoveCmd represents the list operation on the procmanager command 202 | var PMRemoveCmd = &cobra.Command{ 203 | Use: "remove [node name] [optional: delay in secs]", 204 | Short: "Removes the process named.", 205 | Long: `Removes the process named. It will stop the process if it is running.`, 206 | Run: func(cmd *cobra.Command, args []string) { 207 | if !(len(args) >= 1 && args[0] != "") { 208 | cmd.Help() 209 | } 210 | log := cfg.Config.Log 211 | name := args[0] 212 | delay := time.Duration(0) 213 | if len(args) >= 2 { 214 | if v, e := strconv.ParseInt(args[1], 10, 64); e == nil && v > 0 { 215 | delay = time.Duration(v) 216 | log.Info("process will be removed in %ds: %s", int(delay), name) 217 | } 218 | } 219 | remove := func() { 220 | err := pmgr.ProcManager.RemoveProcess(name) 221 | if err != nil { 222 | log.Error(err.Error()) 223 | } 224 | } 225 | delayRun(remove, delay) 226 | }, 227 | } 228 | 229 | // PMRemoveAllCmd represents the list operation on the procmanager command 230 | var PMRemoveAllCmd = &cobra.Command{ 231 | Use: "removeall [optional: delay in secs]", 232 | Short: "Removes all processes.", 233 | Long: `Removes all processes. It will stop the process if it is running.`, 234 | Run: func(cmd *cobra.Command, args []string) { 235 | log := cfg.Config.Log 236 | delay := time.Duration(0) 237 | if len(args) >= 1 { 238 | if v, e := strconv.ParseInt(args[0], 10, 64); e == nil && v > 0 { 239 | delay = time.Duration(v) 240 | log.Info("all processes will be removed in %ds", int(delay)) 241 | } 242 | } 243 | delayRun(pmgr.ProcManager.RemoveAllProcesses, delay) 244 | }, 245 | } 246 | 247 | func delayRun(f func(), delay time.Duration) { 248 | if delay == 0 { 249 | f() 250 | return 251 | } 252 | timer := time.NewTimer(delay * time.Second) 253 | go func() { 254 | <-timer.C 255 | f() 256 | }() 257 | } 258 | 259 | func init() { 260 | ProcmanagerCmd.AddCommand(PMKillCmd) 261 | ProcmanagerCmd.AddCommand(PMKillAllCmd) 262 | ProcmanagerCmd.AddCommand(PMListCmd) 263 | ProcmanagerCmd.AddCommand(PMMetadataCmd) 264 | ProcmanagerCmd.AddCommand(PMRemoveCmd) 265 | ProcmanagerCmd.AddCommand(PMRemoveAllCmd) 266 | ProcmanagerCmd.AddCommand(PMStopCmd) 267 | ProcmanagerCmd.AddCommand(PMStopAllCmd) 268 | ProcmanagerCmd.AddCommand(PMStartAllCmd) 269 | ProcmanagerCmd.AddCommand(PMStartCmd) 270 | } 271 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | // Package cmd implements cobra commander 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | "strings" 10 | 11 | "github.com/ava-labs/avash/cfg" 12 | "github.com/chzyer/readline" 13 | "github.com/spf13/cobra" 14 | "github.com/spf13/pflag" 15 | ) 16 | 17 | const usageTmpl string = `Usage:{{if .Runnable}} 18 | {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} 19 | {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} 20 | 21 | Aliases: 22 | {{.NameAndAliases}}{{end}}{{if .HasExample}} 23 | 24 | Examples: 25 | {{.Example}}{{end}}{{if .HasAvailableSubCommands}} 26 | 27 | Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} 28 | {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} 29 | 30 | Flags: 31 | {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} 32 | 33 | Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} 34 | {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} 35 | 36 | Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} 37 | ` 38 | 39 | type historyrecord struct { 40 | cmd *cobra.Command 41 | flags []string 42 | } 43 | 44 | // Shell is a helper struct for storing history and the instance of the shell prompt 45 | type Shell struct { 46 | history []historyrecord // array of maps, map keys are "command" and "stdout", "stderr" 47 | rl *readline.Instance 48 | root *cobra.Command 49 | } 50 | 51 | func (sh *Shell) addHistory(cmd *cobra.Command, flags []string) { 52 | hr := historyrecord{ 53 | cmd: cmd, 54 | flags: flags, 55 | } 56 | sh.history = append(sh.history, hr) 57 | } 58 | 59 | func completerFromRoot(c *cobra.Command) []readline.PrefixCompleterInterface { 60 | var children []readline.PrefixCompleterInterface 61 | for _, child := range c.Commands() { 62 | childPC := readline.PcItem(child.Name(), completerFromRoot(child)...) 63 | children = append(children, childPC) 64 | } 65 | return children 66 | } 67 | 68 | // ShellLoop is an execution loop for the terminal application 69 | func (sh *Shell) ShellLoop() { 70 | rootPC := completerFromRoot(sh.root) 71 | completer := readline.NewPrefixCompleter(rootPC...) 72 | rln, err := readline.NewEx(&readline.Config{ 73 | Prompt: "avash> ", 74 | AutoComplete: completer, 75 | }) 76 | sh.rl = rln 77 | if err != nil { 78 | panic(err) 79 | } 80 | defer sh.rl.Close() 81 | 82 | for { 83 | ln, err := sh.rl.Readline() 84 | if err != nil { 85 | continue 86 | } 87 | cmd, flags, err := sh.root.Find(strings.Fields(ln)) 88 | if err != nil { 89 | sh.rl.Terminal.Write([]byte(err.Error())) 90 | } 91 | sh.addHistory(cmd, flags) 92 | if err := cmd.ParseFlags(flags); err != nil { 93 | cfg.Config.Log.Error(err.Error()) 94 | continue 95 | } 96 | if err := cmd.ValidateArgs(flags); err != nil { 97 | cfg.Config.Log.Error(err.Error()) 98 | continue 99 | } 100 | cmd.Run(cmd, flags) 101 | } 102 | } 103 | 104 | // AvalancheShell is the shell for our little client 105 | var AvalancheShell *Shell 106 | var RootCmd *cobra.Command 107 | 108 | func init() { 109 | AvalancheShell = new(Shell) 110 | // allow config file path to be set by user 111 | var cfgpath string 112 | pflag.StringVar(&cfgpath, "config", cfg.DefaultCfgName, "Config file path") 113 | pflag.Parse() 114 | 115 | RootCmd = &cobra.Command{ 116 | Use: "avash", 117 | Short: "A shell environment for one or more Avalanche nodes", 118 | Long: "A shell environment for launching and interacting with multiple Avalanche nodes.", 119 | Args: cobra.NoArgs, 120 | Run: func(cmd *cobra.Command, args []string) { 121 | AvalancheShell.ShellLoop() 122 | }, 123 | SilenceUsage: true, 124 | } 125 | 126 | cfg.InitConfig(cfgpath) 127 | RootCmd.AddCommand(AVAXWalletCmd) 128 | RootCmd.AddCommand(CallRPCCmd) 129 | RootCmd.AddCommand(ExitCmd) 130 | RootCmd.AddCommand(NetworkCommand) 131 | RootCmd.AddCommand(ProcmanagerCmd) 132 | RootCmd.AddCommand(RunScriptCmd) 133 | RootCmd.AddCommand(SetOutputCmd) 134 | RootCmd.AddCommand(StartnodeCmd) 135 | RootCmd.AddCommand(VarStoreCmd) 136 | RootCmd.SetUsageTemplate(usageTmpl) 137 | 138 | AvalancheShell.root = RootCmd 139 | 140 | } 141 | 142 | // Execute runs the root command for avash 143 | func Execute() { 144 | if err := RootCmd.Execute(); err != nil { 145 | os.Exit(1) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /cmd/runscript.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | //"encoding/json" 8 | "io" 9 | "os" 10 | "strings" 11 | "time" 12 | 13 | //"context" 14 | 15 | "go.uber.org/multierr" 16 | 17 | "github.com/ava-labs/avash/cfg" 18 | "github.com/spf13/cobra" 19 | lua "github.com/yuin/gopher-lua" 20 | ) 21 | 22 | // RunScriptCmd represents the exit command 23 | var RunScriptCmd = &cobra.Command{ 24 | Use: "runscript [script file]", 25 | Short: "Runs the provided script.", 26 | Long: `Runs the script provided in the argument, relative to the present working directory.`, 27 | Run: func(cmd *cobra.Command, args []string) { 28 | if len(args) >= 1 { 29 | log := cfg.Config.Log 30 | L := lua.NewState( /*lua.Options{ 31 | RegistrySize: 1024 * 20, // this is the initial size of the registry 32 | RegistryMaxSize: 1024 * 8000, // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto grow 33 | RegistryGrowStep: 32, // this is how much to step up the registry by each time it runs out of space. The default is `32`. 34 | CallStackSize: 240, // this is the maximum callstack size of this LState 35 | MinimizeStackMemory: true, // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`. 36 | 37 | }*/) 38 | L.OpenLibs() 39 | defer L.Close() 40 | //ctx, cancel := context.WithCancel(context.Background()) 41 | //L.SetContext(ctx) 42 | //defer cancel() 43 | 44 | /* set new Lua functions here */ 45 | L.SetGlobal("avash_call", L.NewFunction(AvashCall)) 46 | L.SetGlobal("avash_sleepmicro", L.NewFunction(AvashSleepMicro)) 47 | L.SetGlobal("avash_setvar", L.NewFunction(AvashSetVar)) 48 | //L.SetGlobal("avash_coroutine", L.NewFunction(AvashCoroutine)) 49 | 50 | filename := args[0] 51 | log.Info("RunScript: Running " + filename) 52 | 53 | if err := L.DoFile(filename); err != nil { 54 | log.Error("RunScript: Failed to run " + filename + "\n" + err.Error()) 55 | } else { 56 | log.Info("RunScript: Successfully ran " + filename) 57 | } 58 | } else { 59 | cmd.Help() 60 | } 61 | }, 62 | } 63 | 64 | func capture() func() (string, error) { 65 | re, we, err := os.Pipe() 66 | if err != nil { 67 | panic(err) 68 | } 69 | ro, wo, err := os.Pipe() 70 | if err != nil { 71 | panic(err) 72 | } 73 | 74 | done := make(chan error, 1) 75 | 76 | saveO := os.Stdout 77 | os.Stdout = wo 78 | 79 | saveE := os.Stderr 80 | os.Stderr = we 81 | 82 | var buf strings.Builder 83 | 84 | go func() { 85 | _, StderrIoCopyError := io.Copy(&buf, re) 86 | StderrReadCloseError := re.Close() 87 | _, StdoutIoCopyError := io.Copy(&buf, ro) 88 | StdoutReadCloseError := ro.Close() 89 | AllErrors := multierr.Combine( 90 | StderrIoCopyError, 91 | StderrReadCloseError, 92 | StdoutIoCopyError, 93 | StdoutReadCloseError, 94 | ) 95 | done <- AllErrors 96 | }() 97 | 98 | return func() (string, error) { 99 | os.Stderr = saveE 100 | we.Close() 101 | os.Stdout = saveO 102 | wo.Close() 103 | err := <-done 104 | return buf.String(), err 105 | } 106 | } 107 | 108 | // AvashSleepMicro function to sleep for N microseconds 109 | func AvashSleepMicro(L *lua.LState) int { /* returns number of results */ 110 | lv := time.Duration(L.ToInt(1)) 111 | time.Sleep(lv * time.Microsecond) 112 | return 0 113 | } 114 | 115 | // AvashSetVar sets a variable to a string, necessary because `varstore set` can't deal with spaces yet 116 | func AvashSetVar(L *lua.LState) int { 117 | log := cfg.Config.Log 118 | varscope := L.ToString(1) 119 | varname := L.ToString(2) 120 | varvalue := L.ToString(3) 121 | if varscope == "" || varname == "" || varvalue == "" { 122 | log.Error("Error: AvashSetVar provided insufficient number of arguments, expected 3") 123 | return 0 124 | } 125 | if store, err := AvashVars.Get(varscope); err == nil { 126 | store.Set(varname, varvalue) 127 | } else { 128 | log.Error("Error: AvashSetVar scope not found: " + varscope) 129 | } 130 | return 0 131 | } 132 | 133 | // AvashCall hooks avash calls into scripts 134 | func AvashCall(L *lua.LState) int { /* returns number of results */ 135 | lv := L.ToString(1) /* get argument */ 136 | cmd, flags, err := AvalancheShell.root.Find(strings.Fields(lv)) 137 | if err != nil { 138 | AvalancheShell.rl.Terminal.Write([]byte(err.Error())) 139 | } 140 | AvalancheShell.addHistory(cmd, flags) 141 | cmd.ParseFlags(flags) 142 | captureDone := capture() 143 | cmd.Run(cmd, flags) 144 | capturedOutout, err := captureDone() 145 | log := cfg.Config.Log 146 | if err != nil { 147 | L.Push(lua.LString("Error: Unable to execute in capture: " + err.Error())) 148 | log.Error("Error: Unable to execute in capture: " + err.Error()) 149 | log.Error("Captured Output: " + capturedOutout) 150 | return 1 151 | } 152 | L.Push(lua.LString(strings.TrimSpace(capturedOutout))) /* push result */ 153 | return 1 /* number of results */ 154 | } 155 | 156 | /* 157 | // AvashCoroutine launches a simple Lua coroutine in Golang 158 | func AvashCoroutine(L *lua.LState) int { 159 | coname := L.ToString(1) // get coroutine name 160 | coscope := L.ToString(2) // get coroutine scope 161 | if coname == "" { 162 | fmt.Println("Error: AvashCoroutine provided insufficient number of arguments, expected at least 2") 163 | return 0 164 | } 165 | store, err := AvashVars.Get(coscope) 166 | if err != nil { 167 | fmt.Printf("Error: AvashCoroutine can't find output scope.\n") 168 | return 0 169 | } 170 | fn := L.GetGlobal(coname).(*lua.LFunction) // get coroutine function 171 | fnargs := []lua.LValue{} // get coroutine arguments 172 | paramLen := L.GetTop() // get top of the parameter stack 173 | // skip argument #1&2 (coname, coscope) and gather other arguments 174 | for i := 3; i <= paramLen; i++ { 175 | fnargs = append(fnargs, L.Get(i)) 176 | } 177 | // coroutine needs to start here 178 | go gocoro(L, store, fn, fnargs) 179 | return 0 180 | } 181 | 182 | // separated for readability, coroutine used in AvashCoroutine 183 | func gocoro(rootL *lua.LState, store varScope, fn *lua.LFunction, fnargs []lua.LValue) { 184 | co, cocancel := rootL.NewThread() // create a thread for the coroutine 185 | defer cocancel() 186 | store.Set("state", "yield") 187 | state, err, values := rootL.Resume(co, fn, fnargs...) 188 | for { 189 | if state == lua.ResumeError { 190 | stateErr(store, "Error: Coroutine state has error: %s\n", err.Error()) 191 | break 192 | } 193 | 194 | if state == lua.ResumeOK { 195 | v, err := json.MarshalIndent(values, "", " ") 196 | if err != nil { 197 | stateErr(store, "Error: Coroutine cannot marshal values: %s\n", err.Error()) 198 | break 199 | } 200 | store.Set("state", "ok") 201 | store.Set("values", string(v)) 202 | break 203 | } 204 | state, err, values = rootL.Resume(co, fn) 205 | } 206 | } 207 | 208 | func stateErr(store *varScope, estr string, args ...interface{}) { 209 | e := fmt.Sprintf(estr, args...) 210 | fmt.Print(e) 211 | store.Set("state", "error") 212 | store.Set("value", e) 213 | } 214 | */ 215 | -------------------------------------------------------------------------------- /cmd/setoutput.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | "github.com/ava-labs/avash/cfg" 8 | "github.com/ava-labs/avash/utils/logging" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // SetOutputCmd sets the shell output type and verbosity 13 | var SetOutputCmd = &cobra.Command{ 14 | Use: "setoutput [log output] [log level]", 15 | Short: "Sets log output.", 16 | Long: `Sets the log level of a specific log output type.`, 17 | Run: func(cmd *cobra.Command, args []string) { 18 | if len(args) < 2 { 19 | cmd.Help() 20 | return 21 | } 22 | log := cfg.Config.Log 23 | output, outErr := logging.ToOutput(args[0]) 24 | level, lvlErr := logging.ToLevel(args[1]) 25 | if outErr != nil { 26 | log.Error(outErr.Error()) 27 | return 28 | } 29 | if lvlErr != nil { 30 | log.Error(lvlErr.Error()) 31 | return 32 | } 33 | log.SetLevel(output, level) 34 | log.Info("%s log level set: %s", output.String(), level.String()) 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /cmd/startnode.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "strings" 11 | 12 | "github.com/kennygrant/sanitize" 13 | 14 | "github.com/ava-labs/avash/cfg" 15 | "github.com/ava-labs/avash/node" 16 | pmgr "github.com/ava-labs/avash/processmgr" 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | var flags node.Flags 21 | 22 | // StartnodeCmd represents the startnode command 23 | var StartnodeCmd = &cobra.Command{ 24 | Use: "startnode [node name] args...", 25 | Short: "Starts a node process and gives it a name.", 26 | Long: `Starts an Avalanche client node using pmgo and gives it a name. Example: 27 | startnode MyNode1 --public-ip=127.0.0.1 --staking-port=9651 --http-port=9650 ... `, 28 | Run: func(cmd *cobra.Command, args []string) { 29 | if len(args) < 1 { 30 | cmd.Help() 31 | return 32 | } 33 | log := cfg.Config.Log 34 | name := args[0] 35 | 36 | datadir := cfg.Config.DataDir 37 | basename := sanitize.BaseName(name) 38 | datapath := datadir + "/" + basename 39 | if basename == "" { 40 | log.Error("Process name can't be empty") 41 | return 42 | } 43 | 44 | err := validateConsensusArgs( 45 | flags.SnowSampleSize, 46 | flags.SnowQuorumSize, 47 | flags.SnowVirtuousCommitThreshold, 48 | flags.SnowRogueCommitThreshold, 49 | ) 50 | if err != nil { 51 | log.Error(err.Error()) 52 | return 53 | } 54 | 55 | args, md := node.FlagsToArgs(flags, sanitize.Path(datapath), false) 56 | defer func() { 57 | // Set flags to default for next `startnode` call 58 | flags = node.DefaultFlags() 59 | }() 60 | mdbytes, _ := json.MarshalIndent(md, " ", " ") 61 | metadata := string(mdbytes) 62 | meta := flags.Meta 63 | if meta != "" { 64 | metadata = meta 65 | } 66 | avalancheLocation := flags.ClientLocation 67 | if avalancheLocation == "" { 68 | avalancheLocation = cfg.Config.AvalancheLocation 69 | } 70 | err = pmgr.ProcManager.AddProcess(avalancheLocation, "avalanche node", args, name, metadata, nil, nil, nil) 71 | if err != nil { 72 | log.Error(err.Error()) 73 | return 74 | } 75 | log.Info("Created process %s.", name) 76 | pmgr.ProcManager.StartProcess(name) 77 | }, 78 | } 79 | 80 | func validateConsensusArgs(k int, alpha int, beta1 int, beta2 int) error { 81 | rulesfailed := []string(nil) 82 | if k <= 0 { 83 | rulesfailed = append(rulesfailed, "k > 0") 84 | } 85 | if alpha > k { 86 | rulesfailed = append(rulesfailed, "alpha <= k") 87 | } 88 | if (k / 2) >= alpha { 89 | rulesfailed = append(rulesfailed, "alpha > floor(k/2)") 90 | } 91 | if beta1 <= 0 { 92 | rulesfailed = append(rulesfailed, "beta1 > 0") 93 | } 94 | if beta1 > beta2 { 95 | rulesfailed = append(rulesfailed, "beta2 >= beta1") 96 | } 97 | if len(rulesfailed) == 0 { 98 | return nil 99 | } 100 | return errors.New("Invalid consensus params: \n" + strings.Join(rulesfailed, "\n")) 101 | } 102 | 103 | func init() { 104 | flags = node.DefaultFlags() 105 | StartnodeCmd.Flags().StringVar(&flags.ClientLocation, "client-location", flags.ClientLocation, "Path to AVA node client, defaulting to the config file's value.") 106 | StartnodeCmd.Flags().StringVar(&flags.Meta, "meta", flags.Meta, "Override default metadata for the node process.") 107 | StartnodeCmd.Flags().StringVar(&flags.DataDir, "data-dir", flags.DataDir, "Name of directory for the data stash.") 108 | 109 | StartnodeCmd.Flags().BoolVar(&flags.AssertionsEnabled, "assertions-enabled", flags.AssertionsEnabled, "Turn on assertion execution.") 110 | StartnodeCmd.Flags().BoolVar(&flags.Version, "version", flags.Version, "If this is `true`, print the version and quit. Defaults to `false`") 111 | StartnodeCmd.Flags().UintVar(&flags.TxFee, "tx-fee", flags.TxFee, "Transaction fee, in $nAVAX.") 112 | 113 | StartnodeCmd.Flags().StringVar(&flags.PluginDir, "plugin-dir", flags.PluginDir, "Directory to search for plugins") 114 | 115 | StartnodeCmd.Flags().BoolVar(&flags.APIAdminEnabled, "api-admin-enabled", flags.APIAdminEnabled, "If true, this node exposes the Admin API") 116 | StartnodeCmd.Flags().BoolVar(&flags.APIKeystoreEnabled, "api-keystore-enabled", flags.APIKeystoreEnabled, "If true, this node exposes the Keystore API") 117 | StartnodeCmd.Flags().BoolVar(&flags.APIMetricsEnabled, "api-metrics-enabled", flags.APIMetricsEnabled, "If true, this node exposes the Metrics API") 118 | StartnodeCmd.Flags().BoolVar(&flags.APIIPCsEnabled, "api-ipcs-enabled", flags.APIIPCsEnabled, "If true, IPCs can be opened") 119 | StartnodeCmd.Flags().BoolVar(&flags.APIHealthEnabled, "api-health-enabled", flags.APIHealthEnabled, "If set to `true`, this node will expose the Health API. Defaults to `true`") 120 | StartnodeCmd.Flags().BoolVar(&flags.APIInfoEnabled, "api-info-enabled", flags.APIInfoEnabled, "If set to `true`, this node will expose the Info API. Defaults to `true`") 121 | 122 | StartnodeCmd.Flags().StringVar(&flags.PublicIP, "public-ip", flags.PublicIP, "Public IP of this node.") 123 | StartnodeCmd.Flags().StringVar(&flags.DynamicUpdateDuration, "dynamic-update-duration", flags.DynamicUpdateDuration, "The time between poll events for `--dynamic-public-ip` or NAT traversal. The recommended minimum is 1 minute. Defaults to `5m`") 124 | StartnodeCmd.Flags().StringVar(&flags.DynamicPublicIP, "dynamic-public-ip", flags.DynamicPublicIP, "Valid values if param is present: `opendns`, `ifconfigco` or `ifconfigme`. This overrides `--public-ip`. If set, will poll the remote service every `--dynamic-update-duration` and update the node’s public IP address.") 125 | StartnodeCmd.Flags().StringVar(&flags.NetworkID, "network-id", flags.NetworkID, "Network ID this node will connect to.") 126 | StartnodeCmd.Flags().BoolVar(&flags.SignatureVerificationEnabled, "signature-verification-enabled", flags.SignatureVerificationEnabled, "Turn on signature verification.") 127 | 128 | StartnodeCmd.Flags().StringVar(&flags.HTTPHost, "http-host", flags.HTTPHost, "The address that HTTP APIs listen on.") 129 | StartnodeCmd.Flags().UintVar(&flags.HTTPPort, "http-port", flags.HTTPPort, "Port of the HTTP server.") 130 | StartnodeCmd.Flags().BoolVar(&flags.HTTPTLSEnabled, "http-tls-enabled", flags.HTTPTLSEnabled, "Upgrade the HTTP server to HTTPS.") 131 | StartnodeCmd.Flags().StringVar(&flags.HTTPTLSCertFile, "http-tls-cert-file", flags.HTTPTLSCertFile, "TLS certificate file for the HTTPS server.") 132 | StartnodeCmd.Flags().StringVar(&flags.HTTPTLSKeyFile, "http-tls-key-file", flags.HTTPTLSKeyFile, "TLS private key file for the HTTPS server.") 133 | 134 | StartnodeCmd.Flags().StringVar(&flags.BootstrapIPs, "bootstrap-ips", flags.BootstrapIPs, "Comma separated list of bootstrap nodes to connect to. Example: 127.0.0.1:9630,127.0.0.1:9620") 135 | StartnodeCmd.Flags().StringVar(&flags.BootstrapIDs, "bootstrap-ids", flags.BootstrapIDs, "Comma separated list of bootstrap peer ids to connect to. Example: NodeID-JR4dVmy6ffUGAKCBDkyCbeZbyHQBeDsET,NodeID-8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z") 136 | StartnodeCmd.Flags().StringVar(&flags.BootstrapBeaconConnectionTimeout, "bootstrap-beacon-connection-timeout", flags.BootstrapBeaconConnectionTimeout, "Timeout when attempting to connect to bootstrapping beacons.") 137 | 138 | StartnodeCmd.Flags().StringVar(&flags.DBType, "db-type", flags.DBType, "Type of the DB to use (memdb|leveldb|rocksdb)") 139 | StartnodeCmd.Flags().StringVar(&flags.DBDir, "db-dir", flags.DBDir, "Database directory for Avalanche state.") 140 | 141 | StartnodeCmd.Flags().StringVar(&flags.BuildDir, "build-dir", flags.BuildDir, "Path to the build directory.") 142 | 143 | StartnodeCmd.Flags().StringVar(&flags.LogLevel, "log-level", flags.LogLevel, "Specify the log level. Should be one of {verbo, debug, info, warn, error, fatal, off}") 144 | StartnodeCmd.Flags().StringVar(&flags.LogDir, "log-dir", flags.LogDir, "Name of directory for the node's logging.") 145 | StartnodeCmd.Flags().StringVar(&flags.LogDisplayLevel, "log-display-level", flags.LogDisplayLevel, "{Off, Fatal, Error, Warn, Info, Debug, Verbo}. The log level determines which events to display to the screen. If left blank, will default to the value provided to `--log-level`") 146 | StartnodeCmd.Flags().StringVar(&flags.LogDisplayHighlight, "log-display-highlight", flags.LogDisplayHighlight, "Whether to color/highlight display logs. Default highlights when the output is a terminal. Otherwise, should be one of {auto, plain, colors}") 147 | 148 | StartnodeCmd.Flags().IntVar(&flags.SnowAvalancheBatchSize, "snow-avalanche-batch-size", flags.SnowAvalancheBatchSize, "Number of operations to batch in each new vertex.") 149 | StartnodeCmd.Flags().IntVar(&flags.SnowAvalancheNumParents, "snow-avalanche-num-parents", flags.SnowAvalancheNumParents, "Number of vertexes for reference from each new vertex.") 150 | StartnodeCmd.Flags().IntVar(&flags.SnowSampleSize, "snow-sample-size", flags.SnowSampleSize, "Number of nodes to query for each network poll.") 151 | StartnodeCmd.Flags().IntVar(&flags.SnowQuorumSize, "snow-quorum-size", flags.SnowQuorumSize, "Alpha value to use for required number positive results.") 152 | StartnodeCmd.Flags().IntVar(&flags.SnowVirtuousCommitThreshold, "snow-virtuous-commit-threshold", flags.SnowVirtuousCommitThreshold, "Beta value to use for virtuous transactions.") 153 | StartnodeCmd.Flags().IntVar(&flags.SnowRogueCommitThreshold, "snow-rogue-commit-threshold", flags.SnowRogueCommitThreshold, "Beta value to use for rogue transactions.") 154 | StartnodeCmd.Flags().IntVar(&flags.SnowConcurrentRepolls, "snow-concurrent-repolls", flags.SnowConcurrentRepolls, "Snow consensus requires repolling transactions that are issued during low time of network usage. This parameter lets one define how aggressive the client will be in finalizing these pending transactions. This should only be changed after careful consideration of the tradeoffs of Snow consensus. The value must be at least `1` and at most `--snow-rogue-commit-threshold`. Defaults to `4`") 155 | StartnodeCmd.Flags().IntVar(&flags.MinDelegatorStake, "min-delegator-stake", flags.MinDelegatorStake, "The minimum stake, in nAVAX, that can be delegated to a validator of the Primary Network. Defaults to `25000000000` (25 AVAX) on Main Net. Defaults to `5000000` (.005 AVAX) on Test Net.") 156 | StartnodeCmd.Flags().StringVar(&flags.ConsensusShutdownTimeout, "consensus-shutdown-timeout", flags.ConsensusShutdownTimeout, "Timeout before killing an unresponsive chain. Defaults to `5s`") 157 | StartnodeCmd.Flags().StringVar(&flags.ConsensusGossipFrequency, "consensus-gossip-frequency", flags.ConsensusGossipFrequency, "Time between gossiping accepted frontiers. Defaults to `10s`") 158 | StartnodeCmd.Flags().IntVar(&flags.MinDelegationFee, "min-delegation-fee", flags.MinDelegationFee, "The minimum delegation fee that can be charged for delegation on the Primary Network, multiplied by `10,000` . Must be in the range `[0, 1000000]`. Defaults to `20000` (2%) on Main Net.") 159 | StartnodeCmd.Flags().IntVar(&flags.MinValidatorStake, "min-validator-stake", flags.MinValidatorStake, "The minimum stake, in nAVAX, required to validate the Primary Network. Defaults to `2000000000000` (2,000 AVAX) on Main Net. Defaults to `5000000` (.005 AVAX) on Test Net.") 160 | StartnodeCmd.Flags().StringVar(&flags.MaxStakeDuration, "max-stake-duration", flags.MaxStakeDuration, "The maximum staking duration, in seconds. Defaults to `8760h` (365 days) on Main Net.") 161 | StartnodeCmd.Flags().IntVar(&flags.MaxValidatorStake, "max-validator-stake", flags.MaxValidatorStake, "The maximum stake, in nAVAX, that can be placed on a validator on the primary network. Defaults to `3000000000000000` (3,000,000 AVAX) on Main Net. This includes stake provided by both the validator and by delegators to the validator.") 162 | StartnodeCmd.Flags().StringVar(&flags.StakeMintingPeriod, "stake-minting-period", flags.StakeMintingPeriod, "Consumption period of the staking function, in seconds. The Default on Main Net is `8760h` (365 days).") 163 | 164 | StartnodeCmd.Flags().BoolVar(&flags.StakingEnabled, "staking-enabled", flags.StakingEnabled, "Enable staking. If enabled, Network TLS is required.") 165 | StartnodeCmd.Flags().UintVar(&flags.StakingPort, "staking-port", flags.StakingPort, "Port of the consensus server.") 166 | StartnodeCmd.Flags().IntVar(&flags.StakingDisabledWeight, "staking-disabled-weight", flags.StakingDisabledWeight, "Weight to provide to each peer when staking is disabled. Defaults to `1`") 167 | StartnodeCmd.Flags().StringVar(&flags.StakingTLSCertFile, "staking-tls-cert-file", flags.StakingTLSCertFile, "TLS certificate file for staking connections. Relative to the avash binary if doesn't start with '/'. Ex: certs/keys1/staker.crt") 168 | StartnodeCmd.Flags().StringVar(&flags.StakingTLSKeyFile, "staking-tls-key-file", flags.StakingTLSKeyFile, "TLS private key file for staking connections. Relative to the avash binary if doesn't start with '/'. Ex: certs/keys1/staker.key") 169 | 170 | StartnodeCmd.Flags().BoolVar(&flags.APIAuthRequired, "api-auth-required", flags.APIAuthRequired, "If set to true, API calls require an authorization token. Defaults to `false`") 171 | StartnodeCmd.Flags().StringVar(&flags.APIAuthPasswordFileKey, "api-auth-password-file", flags.APIAuthPasswordFileKey, "Password file used to initially create/validate API authorization tokens. Can be changed via API call.") 172 | StartnodeCmd.Flags().StringVar(&flags.MinStakeDuration, "min-stake-duration", flags.MinStakeDuration, "Set the minimum staking duration. Ex: --min-stake-duration=5m") 173 | 174 | StartnodeCmd.Flags().StringVar(&flags.WhitelistedSubnets, "whitelisted-subnets", flags.WhitelistedSubnets, "Comma separated list of subnets that this node would validate if added to. Defaults to empty (will only validate the Primary Network)") 175 | 176 | StartnodeCmd.Flags().StringVar(&flags.ConfigFile, "config-file", flags.ConfigFile, "Config file specifies a JSON file to configure a node instead of specifying arguments via the command line. Command line arguments will override any options set in the config file.") 177 | 178 | StartnodeCmd.Flags().BoolVar(&flags.NetworkCompressionEnabled, "network-compression-enabled", flags.NetworkCompressionEnabled, "If true, compress Put, PushQuery, PeerList and Multiput messages sent to peers that support compression") 179 | 180 | StartnodeCmd.Flags().StringVar(&flags.IPCSChainIDs, "ipcs-chain-ids", flags.IPCSChainIDs, "Comma separated list of chain ids to connect to. There is no default value.") 181 | StartnodeCmd.Flags().StringVar(&flags.IPCSPath, "ipcs-path", flags.IPCSPath, "The directory (Unix) or named pipe prefix (Windows) for IPC sockets. Defaults to /tmp.") 182 | 183 | StartnodeCmd.Flags().IntVar(&flags.FDLimit, "fd-limit", flags.FDLimit, "Attempts to raise the process file descriptor limit to at least this value. Defaults to `32768`") 184 | 185 | StartnodeCmd.Flags().StringVar(&flags.BenchlistDuration, "benchlist-duration", flags.BenchlistDuration, "Amount of time a peer is benchlisted after surpassing `--benchlist-fail-threshold`. Defaults to `1h`") 186 | StartnodeCmd.Flags().IntVar(&flags.BenchlistFailThreshold, "benchlist-fail-threshold", flags.BenchlistFailThreshold, "Number of consecutive failed queries to a node before benching it (assuming all queries to it will fail). Defaults to `10`") 187 | StartnodeCmd.Flags().StringVar(&flags.BenchlistMinFailingDuration, "benchlist-min-failing-duration", flags.BenchlistMinFailingDuration, "Minimum amount of time messages to a peer must be failing before the peer is benched. Defaults to `5m`") 188 | StartnodeCmd.Flags().BoolVar(&flags.BenchlistPeerSummaryEnabled, "benchlist-peer-summary-enabled", flags.BenchlistPeerSummaryEnabled, "Enables peer specific query latency metrics. Defaults to `false`") 189 | 190 | StartnodeCmd.Flags().StringVar(&flags.NetworkInitialTimeout, "network-initial-timeout", flags.NetworkInitialTimeout, "Initial timeout value of the adaptive timeout manager, in nanoseconds. Defaults to `5s`") 191 | StartnodeCmd.Flags().StringVar(&flags.NetworkMinimumTimeout, "network-minimum-timeout", flags.NetworkMinimumTimeout, "Minimum timeout value of the adaptive timeout manager, in nanoseconds. Defaults to `5s`") 192 | StartnodeCmd.Flags().StringVar(&flags.NetworkMaximumTimeout, "network-maximum-timeout", flags.NetworkMaximumTimeout, "Maximum timeout value of the adaptive timeout manager, in nanoseconds. Defaults to `10s`") 193 | StartnodeCmd.Flags().Float64Var(&flags.NetworkHealthMaxSendFailRateKey, "network-health-max-send-fail-rate", flags.NetworkHealthMaxSendFailRateKey, "Network layer reports unhealthy if more than this portion of attempted message sends fail") 194 | StartnodeCmd.Flags().Float64Var(&flags.NetworkHealthMaxPortionSendQueueFillKey, "network-health-max-portion-send-queue-full", flags.NetworkHealthMaxPortionSendQueueFillKey, "Network layer returns unhealthy if more than this portion of the pending send queue is full") 195 | StartnodeCmd.Flags().StringVar(&flags.NetworkHealthMaxTimeSinceMsgSentKey, "network-health-max-time-since-msg-sent", flags.NetworkHealthMaxTimeSinceMsgSentKey, "Network layer returns unhealthy if haven't sent a message for at least this much time") 196 | StartnodeCmd.Flags().StringVar(&flags.NetworkHealthMaxTimeSinceMsgReceivedKey, "network-health-max-time-since-msg-received", flags.NetworkHealthMaxTimeSinceMsgReceivedKey, "Network layer returns unhealthy if haven't received a message for at least this much time") 197 | StartnodeCmd.Flags().IntVar(&flags.NetworkHealthMinConnPeers, "network-health-min-conn-peers", flags.NetworkHealthMinConnPeers, "Network layer returns unhealthy if connected to less than this many peers") 198 | StartnodeCmd.Flags().IntVar(&flags.NetworkTimeoutCoefficient, "network-timeout-coefficient", flags.NetworkTimeoutCoefficient, "Multiplied by average network response time to get the network timeout. Must be >= 1.") 199 | StartnodeCmd.Flags().StringVar(&flags.NetworkTimeoutHalflife, "network-timeout-halflife", flags.NetworkTimeoutHalflife, "Halflife of average network response time. Higher value --> network timeout is less volatile. Can't be 0.") 200 | gossipHelpMsg := fmt.Sprintf( 201 | "Gossip [%s] peers to [%s] peers every [%s]", 202 | "network-peer-list-size", 203 | "network-peer-list-gossip-size", 204 | "network-peer-list-gossip-frequency", 205 | ) 206 | StartnodeCmd.Flags().StringVar(&flags.NetworkPeerListGossipFrequency, "network-peer-list-gossip-frequency", flags.NetworkPeerListGossipFrequency, gossipHelpMsg) 207 | StartnodeCmd.Flags().IntVar(&flags.NetworkPeerListGossipSize, "network-peer-list-gossip-size", flags.NetworkPeerListGossipSize, gossipHelpMsg) 208 | StartnodeCmd.Flags().IntVar(&flags.NetworkPeerListSize, "network-peer-list-size", flags.NetworkPeerListSize, gossipHelpMsg) 209 | 210 | StartnodeCmd.Flags().Float64Var(&flags.UptimeRequirement, "uptime-requirement", flags.UptimeRequirement, "Fraction of time a validator must be online to receive rewards. Defaults to `0.6`") 211 | 212 | StartnodeCmd.Flags().IntVar(&flags.RetryBootstrapWarnFrequency, "bootstrap-retry-warn-frequency", flags.RetryBootstrapWarnFrequency, "Specifies how many times bootstrap should be retried") 213 | StartnodeCmd.Flags().BoolVar(&flags.RetryBootstrap, "bootstrap-retry-enabled", flags.RetryBootstrap, "Specifies whether bootstrap should be retried") 214 | 215 | StartnodeCmd.Flags().StringVar(&flags.HealthCheckFreqKey, "health-check-frequency", flags.HealthCheckFreqKey, "Time between health checks") 216 | StartnodeCmd.Flags().StringVar(&flags.HealthCheckAveragerHalflifeKey, "health-check-averager-halflife", flags.HealthCheckAveragerHalflifeKey, "Halflife of averager when calculating a running average in a health check") 217 | 218 | StartnodeCmd.Flags().IntVar(&flags.RouterHealthMaxOutstandingRequestsKey, "router-health-max-outstanding-requests", flags.RouterHealthMaxOutstandingRequestsKey, "Node reports unhealthy if there are more than this many outstanding consensus requests (Get, PullQuery, etc.) over all chains") 219 | StartnodeCmd.Flags().Float64Var(&flags.RouterHealthMaxDropRateKey, "router-health-max-drop-rate", flags.RouterHealthMaxDropRateKey, "Node reports unhealthy if the router drops more than this portion of messages.") 220 | 221 | StartnodeCmd.Flags().BoolVar(&flags.IndexEnabled, "index-enabled", flags.IndexEnabled, "If true, index all accepted containers and transactions and expose them via an API") 222 | StartnodeCmd.Flags().BoolVar(&flags.PluginModeEnabled, "plugin-mode-enabled", flags.PluginModeEnabled, "Whether the app should run as a plugin. Defaults to false.") 223 | StartnodeCmd.Flags().BoolVar(&flags.MeterVMsEnabled, "meter-vms-enabled", flags.MeterVMsEnabled, "Whether the MeterVMs should be enabled on each VM.") 224 | } 225 | -------------------------------------------------------------------------------- /cmd/varstore.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package cmd 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "path/filepath" 12 | 13 | "github.com/ava-labs/avash/cfg" 14 | "github.com/spf13/cobra" 15 | "github.com/yourbasic/radix" 16 | ) 17 | 18 | // VarScope is a scope of the variable 19 | type VarScope struct { 20 | Name string 21 | Variables map[string]string 22 | } 23 | 24 | // List lists the variables in the scope 25 | func (v *VarScope) List() []string { 26 | results := []string{} 27 | for k := range v.Variables { 28 | results = append(results, k) 29 | } 30 | return results 31 | } 32 | 33 | // Get gets the variable by name in the scope 34 | func (v *VarScope) Get(varname string) (string, error) { 35 | if variable, ok := v.Variables[varname]; ok { 36 | return variable, nil 37 | } 38 | return "", fmt.Errorf("variable not found: %s", varname) 39 | } 40 | 41 | // Set sets the variable at a name to a value 42 | func (v *VarScope) Set(varname string, value string) { 43 | v.Variables[varname] = value 44 | } 45 | 46 | // JSON returns the json representation of the variable scope 47 | func (v *VarScope) JSON() ([]byte, error) { 48 | return json.MarshalIndent(v, "", " ") 49 | } 50 | 51 | // VarStore stores scopes of variables to store 52 | type VarStore struct { 53 | Stores map[string]VarScope 54 | } 55 | 56 | // Create will make a new variable scope 57 | func (v *VarStore) Create(store string) error { 58 | if _, ok := v.Stores[store]; ok { 59 | return fmt.Errorf("store exists: %s", store) 60 | } 61 | v.Stores[store] = VarScope{ 62 | Name: store, 63 | Variables: map[string]string{}, 64 | } 65 | return nil 66 | } 67 | 68 | // List lists the scopes available 69 | func (v *VarStore) List() []string { 70 | results := []string{} 71 | for k := range v.Stores { 72 | results = append(results, k) 73 | } 74 | return results 75 | } 76 | 77 | // Get will retrieve the scope defined at the name passed in 78 | func (v *VarStore) Get(store string) (VarScope, error) { 79 | if variable, ok := v.Stores[store]; ok { 80 | return variable, nil 81 | } 82 | return VarScope{}, fmt.Errorf("store not found: %s", store) 83 | } 84 | 85 | // VarStoreCmd represents the vars command 86 | var VarStoreCmd = &cobra.Command{ 87 | Use: "varstore", 88 | Short: "Tools for creating variable stores and printing variables within them.", 89 | Long: `Tools for creating variable stores and printing variables within them. Using this 90 | command you can create variable stores, list all variables they store, and print data 91 | placed into these stores. Variable assigment and update is often managed by avash commands.`, 92 | Run: func(cmd *cobra.Command, args []string) { 93 | cmd.Help() 94 | }, 95 | } 96 | 97 | // VarStoreCreateCmd will attempt to get a genesis key and send a transaction 98 | var VarStoreCreateCmd = &cobra.Command{ 99 | Use: "create [store name]", 100 | Short: "Creates a variable store.", 101 | Long: `Creates a variable store. If it exists, it prints "name conflict" otherwise 102 | it prints "store created".`, 103 | Run: func(cmd *cobra.Command, args []string) { 104 | if len(args) >= 1 { 105 | log := cfg.Config.Log 106 | store := args[0] 107 | if err := AvashVars.Create(store); err == nil { 108 | log.Info("store created: " + store) 109 | } else { 110 | log.Error("name conflict: " + store) 111 | } 112 | } else { 113 | cmd.Help() 114 | } 115 | }, 116 | } 117 | 118 | // VarStoreListCmd will attempt to get a genesis key and send a transaction 119 | var VarStoreListCmd = &cobra.Command{ 120 | Use: "list [store name]", 121 | Short: "Lists all stores. If store provided, lists all variables in the store.", 122 | Long: `Lists all stores. If store provided, lists all variables in the store. 123 | If the store exists, it will print a new-line separated string of variables in 124 | this store. If the store does not exist, it will print "store not found".`, 125 | Run: func(cmd *cobra.Command, args []string) { 126 | log := cfg.Config.Log 127 | results := []string{} 128 | if len(args) >= 1 { 129 | if store, err := AvashVars.Get(args[0]); err == nil { 130 | results = store.List() 131 | } else { 132 | log.Error("store not found:" + args[0]) 133 | } 134 | } else { 135 | results = AvashVars.List() 136 | } 137 | radix.Sort(results) 138 | for _, v := range results { 139 | log.Info(v) 140 | } 141 | }, 142 | } 143 | 144 | // VarStorePrintCmd will attempt to get a genesis key and send a transaction 145 | var VarStorePrintCmd = &cobra.Command{ 146 | Use: "print [store] [variable]", 147 | Short: "Prints a variable that is within the store.", 148 | Long: `Prints a variable that is within the store. If it doesn't exist, it prints the default JSON string "{}".`, 149 | Run: func(cmd *cobra.Command, args []string) { 150 | if len(args) >= 2 { 151 | log := cfg.Config.Log 152 | if store, err := AvashVars.Get(args[0]); err == nil { 153 | if v, e := store.Get(args[1]); e == nil { 154 | log.Info(v) 155 | } else { 156 | log.Info("{}") 157 | } 158 | } else { 159 | log.Info("{}") 160 | } 161 | } else { 162 | cmd.Help() 163 | } 164 | }, 165 | } 166 | 167 | // VarStoreSetCmd will attempt to get a genesis key and send a transaction 168 | var VarStoreSetCmd = &cobra.Command{ 169 | Use: "set [store] [variable] [value]", 170 | Short: "Sets a simple variable that within the store.", 171 | Long: `Sets a simple variable that within the store. Store must exist. May not have spaces, even quoted. Existing values are overwritten.`, 172 | Run: func(cmd *cobra.Command, args []string) { 173 | if len(args) >= 3 { 174 | log := cfg.Config.Log 175 | if store, err := AvashVars.Get(args[0]); err == nil { 176 | store.Set(args[1], args[2]) 177 | log.Info("variable set: %q.%q=%q", args[0], args[1], args[2]) 178 | } else { 179 | log.Error("store not found: " + args[0]) 180 | } 181 | } else { 182 | cmd.Help() 183 | } 184 | }, 185 | } 186 | 187 | // VarStoreStoreDumpCmd writes the store to the filename specified in the stash 188 | var VarStoreStoreDumpCmd = &cobra.Command{ 189 | Use: "storedump [store] [filename]", 190 | Short: "Writes the store to a file.", 191 | Long: `Writes the store to a file.`, 192 | Run: func(cmd *cobra.Command, args []string) { 193 | if len(args) >= 2 { 194 | log := cfg.Config.Log 195 | if store, err := AvashVars.Get(args[0]); err == nil { 196 | stashdir := cfg.Config.DataDir 197 | basename := filepath.Base(args[1]) 198 | basedir := filepath.Dir(stashdir + "/" + args[1]) 199 | 200 | os.MkdirAll(basedir, os.ModePerm) 201 | outputfile := basedir + "/" + basename 202 | 203 | if marshalled, err := store.JSON(); err == nil { 204 | if err := ioutil.WriteFile(outputfile, marshalled, 0755); err != nil { 205 | log.Error("unable to write file: %s - %s", string(outputfile), err.Error()) 206 | } else { 207 | log.Info("VarStore written to: %s", outputfile) 208 | } 209 | } else { 210 | log.Error("unable to marshal: %s", err.Error()) 211 | } 212 | } else { 213 | log.Error("store not found: %s", args[0]) 214 | } 215 | } else { 216 | cmd.Help() 217 | } 218 | }, 219 | } 220 | 221 | // VarStoreVarDumpCmd writes the variable to the filename specified in the stash 222 | var VarStoreVarDumpCmd = &cobra.Command{ 223 | Use: "vardump [store] [variable] [filename]", 224 | Short: "Writes the variable to a file.", 225 | Long: `Writes the variable set to a file.`, 226 | Run: func(cmd *cobra.Command, args []string) { 227 | if len(args) >= 3 { 228 | log := cfg.Config.Log 229 | if store, err := AvashVars.Get(args[0]); err == nil { 230 | if variable, e := store.Get(args[1]); e == nil { 231 | stashdir := cfg.Config.DataDir 232 | basename := filepath.Base(args[2]) 233 | basedir := filepath.Dir(stashdir + "/" + args[2]) 234 | 235 | os.MkdirAll(basedir, os.ModePerm) 236 | outputfile := basedir + "/" + basename 237 | if err := ioutil.WriteFile(outputfile, []byte(variable), 0755); err != nil { 238 | log.Error("unable to write file: %s - %s", string(outputfile), err.Error()) 239 | } else { 240 | log.Info("VarStore written to: %s", outputfile) 241 | } 242 | } else { 243 | log.Error("variable not found: %s -> %s", args[0], args[1]) 244 | } 245 | } else { 246 | log.Error("store not found: %s", args[0]) 247 | } 248 | } else { 249 | cmd.Help() 250 | } 251 | }, 252 | } 253 | 254 | // AvashVars is the variable store. 255 | var AvashVars VarStore 256 | 257 | func init() { 258 | VarStoreCmd.AddCommand(VarStoreCreateCmd) 259 | VarStoreCmd.AddCommand(VarStoreStoreDumpCmd) 260 | VarStoreCmd.AddCommand(VarStoreListCmd) 261 | VarStoreCmd.AddCommand(VarStorePrintCmd) 262 | VarStoreCmd.AddCommand(VarStoreSetCmd) 263 | VarStoreCmd.AddCommand(VarStoreVarDumpCmd) 264 | AvashVars = VarStore{ 265 | Stores: map[string]VarScope{}, 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /example.avash.yaml: -------------------------------------------------------------------------------- 1 | avalancheLocation: <$GOPATH>/src/github.com/ava-labs/avalanchego/build/avalanchego 2 | datadir: <$GOPATH>/src/github.com/ava-labs/avash/stash 3 | log: 4 | terminal: info 5 | logfile: info 6 | dir: <$GOPATH>/src/github.com/ava-labs/avash/stash/logs 7 | -------------------------------------------------------------------------------- /example.network.yaml: -------------------------------------------------------------------------------- 1 | hosts: 2 | - name: host-1 3 | user: ubuntu 4 | ip: 0.0.0.0 5 | - name: host-2 6 | user: ec2-user 7 | ip: 0.0.0.0 8 | 9 | nodes: 10 | - class: default 11 | - class: staker 12 | flags: 13 | staking-enabled: true 14 | staking-tls-cert-file: certs/keys1/staker.crt 15 | staking-tls-key-file: certs/keys1/staker.key 16 | 17 | deploys: 18 | - host: host-1 19 | nodes: 20 | - name: n1 21 | class: default 22 | - name: n2 23 | class: default 24 | flags: 25 | http-port: 9660 26 | staking-port: 9661 27 | - host: host-2 28 | nodes: 29 | - name: stake-node 30 | class: staker 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ava-labs/avash 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.2.9 7 | github.com/ava-labs/avalanchego v1.7.1 8 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e 9 | github.com/gorilla/mux v1.8.0 10 | github.com/gorilla/rpc v1.2.0 11 | github.com/kennygrant/sanitize v1.2.4 12 | github.com/mitchellh/go-homedir v1.1.0 13 | github.com/olekukonko/tablewriter v0.0.5 14 | github.com/pkg/sftp v1.13.0 15 | github.com/spf13/cobra v1.1.3 16 | github.com/spf13/pflag v1.0.5 17 | github.com/spf13/viper v1.7.1 18 | github.com/stretchr/testify v1.7.0 19 | github.com/ybbus/jsonrpc v2.1.2+incompatible 20 | github.com/yourbasic/radix v0.0.0-20180308122924-cbe1cc82e907 21 | github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da 22 | go.uber.org/multierr v1.6.0 23 | golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc 24 | gopkg.in/yaml.v2 v2.4.0 25 | ) 26 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ava-labs/avash/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /network/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | command_exists() { 5 | command -v "$@" > /dev/null 2>&1 6 | } 7 | 8 | get_distribution() { 9 | lsb_dist="" 10 | # Every system that we officially support has /etc/os-release 11 | if [ -r /etc/os-release ]; then 12 | lsb_dist="$(. /etc/os-release && echo "$ID")" 13 | fi 14 | # Returning an empty string here should be alright since the 15 | # case statements don't act unless you provide an actual value 16 | echo "$lsb_dist" 17 | } 18 | 19 | # Set package manager based on OS distribution 20 | lsb_dist=$( get_distribution ) 21 | lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')" 22 | case "$lsb_dist" in 23 | ubuntu|debian|raspbian) 24 | pkg_manager="apt" 25 | ;; 26 | centos) 27 | pkg_manager="yum" 28 | ;; 29 | fedora) 30 | pkg_manager="dnf" 31 | ;; 32 | *) 33 | echo 34 | echo "ERROR: Unsupported distribution '$lsb_dist'" 35 | echo 36 | exit 1 37 | ;; 38 | esac 39 | 40 | # Install Git 41 | if ! command_exists git; then 42 | if command_exists $pkg_manager 43 | then 44 | sudo $pkg_manager install git <<< y 45 | else 46 | echo 47 | echo "ERROR: Missing expected package manager '$pkg_manager'" 48 | echo 49 | exit 1 50 | fi 51 | fi 52 | 53 | # Install Docker 54 | if ! command_exists docker; then 55 | curl -fsSL https://get.docker.com -o get-docker.sh 56 | sudo sh get-docker.sh 57 | fi 58 | sudo service docker start 59 | sudo usermod -aG docker $USER 60 | 61 | # Download avalanchego 62 | avalanche_remote="https://github.com/ava-labs/avalanchego.git" 63 | if [ ! -d avalanchego ] 64 | then 65 | git clone $avalanche_remote 66 | else 67 | cd avalanchego 68 | git_remote="$(git config --get remote.origin.url)" 69 | if [ $git_remote == $avalanche_remote ] 70 | then 71 | git pull 72 | cd .. 73 | else 74 | echo 75 | echo "ERROR: Existing directory 'avalanchego' not using '$avalanche_remote' as git remote.origin.url" 76 | echo 77 | exit 1 78 | fi 79 | fi -------------------------------------------------------------------------------- /network/node_tools.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "sync" 9 | 10 | survey "github.com/AlecAivazis/survey/v2" 11 | "github.com/kennygrant/sanitize" 12 | "github.com/ava-labs/avash/cfg" 13 | "github.com/ava-labs/avash/node" 14 | "golang.org/x/crypto/ssh" 15 | ) 16 | 17 | const ( 18 | datadir string = "./stash" 19 | ) 20 | 21 | // HostAuth represents a full set of host SSH credentials 22 | type HostAuth struct { 23 | User, IP string 24 | Auth ssh.AuthMethod 25 | } 26 | 27 | // InitHost initializes a host environment to be able to run nodes. 28 | func InitHost(user, ip string, auth ssh.AuthMethod) error { 29 | const cfp string = "./init.sh" 30 | cmds := []string{ 31 | "chmod 777 " + cfp, 32 | cfp, 33 | } 34 | 35 | client, err := NewSSH(user, ip, auth) 36 | if err != nil { 37 | return err 38 | } 39 | defer client.Close() 40 | 41 | if err := client.CopyFile("network/init.sh", cfp); err != nil { 42 | return err 43 | } 44 | defer client.RemovePath(cfp) 45 | 46 | if err := client.Run(cmds); err != nil { 47 | return err 48 | } 49 | return nil 50 | } 51 | 52 | // InitAuth creates a mapping of host names to SSH authentications 53 | // Requires user input at terminal 54 | func InitAuth(deploys []DeployConfig) (map[string]HostAuth, error) { 55 | m := make(map[string]HostAuth) 56 | 57 | var authAll bool 58 | if len(deploys) > 1 { 59 | var authPrompt = &survey.Confirm{ 60 | Message: "Would you like to use the same SSH credentials for all hosts?:", 61 | } 62 | survey.AskOne(authPrompt, &authAll) 63 | } else { 64 | authAll = false 65 | } 66 | 67 | var authFunc func(*string) ssh.AuthMethod 68 | if authAll { 69 | auth := PromptAuth(nil) 70 | authFunc = func(_ *string) ssh.AuthMethod { 71 | return auth 72 | } 73 | } else { 74 | authFunc = PromptAuth 75 | } 76 | 77 | for _, deploy := range deploys { 78 | auth := authFunc(&deploy.IP) 79 | if auth == nil { 80 | return nil, fmt.Errorf("Authentication cancelled") 81 | } 82 | m[deploy.IP] = HostAuth{deploy.User, deploy.IP, auth} 83 | } 84 | return m, nil 85 | } 86 | 87 | // Deploy deploys nodes to hosts as specified in `config` 88 | func Deploy(deploys []DeployConfig, isPrompt bool) error { 89 | log := cfg.Config.Log 90 | const cfp string = "./startnode.sh" 91 | 92 | authMap, err := InitAuth(deploys) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | var wg sync.WaitGroup 98 | wg.Add(len(deploys)) 99 | for _, deploy := range deploys { 100 | go func(deploy DeployConfig) { 101 | defer wg.Done() 102 | hostAuth := authMap[deploy.IP] 103 | user, ip, auth := hostAuth.User, hostAuth.IP, hostAuth.Auth 104 | 105 | if err := InitHost(user, ip, auth); err != nil { 106 | log.Error("%s: %s", hostAuth.IP, err.Error()) 107 | return 108 | } 109 | 110 | client, err := NewSSH(user, ip, auth) 111 | if err != nil { 112 | log.Error("%s: %s", ip, err.Error()) 113 | return 114 | } 115 | defer client.Close() 116 | 117 | if err := client.CopyFile("network/startnode.sh", cfp); err != nil { 118 | log.Error("%s: %s", ip, err.Error()) 119 | return 120 | } 121 | defer client.RemovePath(cfp) 122 | 123 | cmds := []string{ 124 | fmt.Sprintf("chmod 777 %s", cfp), 125 | } 126 | for _, n := range deploy.Nodes { 127 | if err := configureCLIFiles(&n.Flags, datadir, client); err != nil { 128 | log.Error("%s: %s", ip, err.Error()) 129 | return 130 | } 131 | basename := sanitize.BaseName(n.Name) 132 | datapath := datadir + "/" + basename 133 | flags, _ := node.FlagsToArgs(n.Flags, datapath, true) 134 | args := strings.Join(flags, " ") 135 | cmd := fmt.Sprintf("%s --name=%s %s", cfp, n.Name, args) 136 | cmds = append(cmds, cmd) 137 | } 138 | 139 | if err := client.Run(cmds); err != nil { 140 | log.Error("%s: %s", ip, err.Error()) 141 | return 142 | } 143 | log.Info("%s: successfully deployed", ip) 144 | }(deploy) 145 | } 146 | wg.Wait() 147 | return nil 148 | } 149 | 150 | // Remove removes nodes from hosts as specified in `config` 151 | func Remove(deploys []DeployConfig, isPrompt bool) error { 152 | log := cfg.Config.Log 153 | 154 | authMap, err := InitAuth(deploys) 155 | if err != nil { 156 | return err 157 | } 158 | 159 | var wg sync.WaitGroup 160 | wg.Add(len(deploys)) 161 | for _, deploy := range deploys { 162 | go func(deploy DeployConfig) { 163 | defer wg.Done() 164 | hostAuth := authMap[deploy.IP] 165 | user, ip, auth := hostAuth.User, hostAuth.IP, hostAuth.Auth 166 | 167 | client, err := NewSSH(user, ip, auth) 168 | if err != nil { 169 | log.Error("%s: %s", ip, err.Error()) 170 | return 171 | } 172 | defer client.Close() 173 | 174 | var cmds []string 175 | for _, n := range deploy.Nodes { 176 | tmpCmds := []string{ 177 | fmt.Sprintf("docker stop %s", n.Name), 178 | fmt.Sprintf("docker rm %s", n.Name), 179 | } 180 | cmds = append(cmds, tmpCmds...) 181 | } 182 | 183 | if err := client.Run(cmds); err != nil { 184 | log.Error("%s: %s", ip, err.Error()) 185 | return 186 | } 187 | log.Info("%s: successfully removed", ip) 188 | }(deploy) 189 | } 190 | wg.Wait() 191 | return nil 192 | } 193 | 194 | func configureCLIFiles(flags *node.Flags, datadir string, client *SSHClient) error { 195 | wd, _ := os.Getwd() 196 | if fp := flags.HTTPTLSCertFile; fp != "" { 197 | if string(fp[0]) != "/" { 198 | fp = fmt.Sprintf("%s/%s", wd, fp) 199 | } 200 | cfp := datadir + "/" + filepath.Base(fp) 201 | if err := client.CopyFile(fp, cfp); err != nil { 202 | return fmt.Errorf("%s: %s", err.Error(), fp) 203 | } 204 | flags.HTTPTLSCertFile = cfp 205 | } 206 | if fp := flags.HTTPTLSKeyFile; fp != "" { 207 | if string(fp[0]) != "/" { 208 | fp = fmt.Sprintf("%s/%s", wd, fp) 209 | } 210 | cfp := datadir + "/" + filepath.Base(fp) 211 | if err := client.CopyFile(fp, cfp); err != nil { 212 | return fmt.Errorf("%s: %s", err.Error(), fp) 213 | } 214 | flags.HTTPTLSKeyFile = cfp 215 | } 216 | if fp := flags.StakingTLSCertFile; fp != "" { 217 | if string(fp[0]) != "/" { 218 | fp = fmt.Sprintf("%s/%s", wd, fp) 219 | } 220 | cfp := datadir + "/" + filepath.Base(fp) 221 | if err := client.CopyFile(fp, cfp); err != nil { 222 | return fmt.Errorf("%s: %s", err.Error(), fp) 223 | } 224 | flags.StakingTLSCertFile = cfp 225 | } 226 | if fp := flags.StakingTLSKeyFile; fp != "" { 227 | if string(fp[0]) != "/" { 228 | fp = fmt.Sprintf("%s/%s", wd, fp) 229 | } 230 | cfp := datadir + "/" + filepath.Base(fp) 231 | if err := client.CopyFile(fp, cfp); err != nil { 232 | return fmt.Errorf("%s: %s", err.Error(), fp) 233 | } 234 | flags.StakingTLSKeyFile = cfp 235 | } 236 | return nil 237 | } 238 | -------------------------------------------------------------------------------- /network/ssh_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "net" 7 | "os" 8 | 9 | survey "github.com/AlecAivazis/survey/v2" 10 | "github.com/ava-labs/avash/cfg" 11 | "github.com/pkg/sftp" 12 | "golang.org/x/crypto/ssh" 13 | "golang.org/x/crypto/ssh/agent" 14 | ) 15 | 16 | // SSHClient implements an SSH client 17 | type SSHClient struct { 18 | *ssh.Client 19 | ip string 20 | } 21 | 22 | func promptRetry(err error) bool { 23 | cfg.Config.Log.Error(err.Error()) 24 | var retry bool 25 | var retryPrompt = &survey.Confirm{ 26 | Message: "Would you like to retry?:", 27 | } 28 | survey.AskOne(retryPrompt, &retry) 29 | return retry 30 | } 31 | 32 | func promptPassword() ssh.AuthMethod { 33 | var pw string 34 | pwPrompt := &survey.Password{ 35 | Message: "Password:", 36 | } 37 | survey.AskOne(pwPrompt, &pw) 38 | return ssh.Password(pw) 39 | } 40 | 41 | func promptKeyFile() ssh.AuthMethod { 42 | var fp string 43 | fpPrompt := &survey.Input{ 44 | Message: "Full path to key file:", 45 | } 46 | for { 47 | survey.AskOne(fpPrompt, &fp) 48 | buff, err := ioutil.ReadFile(fp) 49 | if err != nil { 50 | if promptRetry(err) { 51 | continue 52 | } 53 | return nil 54 | } 55 | key, err := ssh.ParsePrivateKey(buff) 56 | if err != nil { 57 | if promptRetry(err) { 58 | continue 59 | } 60 | return nil 61 | } 62 | return ssh.PublicKeys(key) 63 | } 64 | } 65 | 66 | func sshAgent() ssh.AuthMethod { 67 | sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) 68 | if err != nil { 69 | cfg.Config.Log.Error(err.Error()) 70 | return nil 71 | } 72 | return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers) 73 | } 74 | 75 | // PromptAuth returns an `ssh.AuthMethod` object for establishing an `SSHClient` 76 | // Requires user input at terminal 77 | func PromptAuth(ip *string) ssh.AuthMethod { 78 | const strPassword string = "password" 79 | const strKeyFile string = "key file" 80 | const strAgent string = "ssh agent" 81 | var sshMethod string 82 | msg := "Choose a method to provide SSH credentials" 83 | if ip != nil { 84 | msg += " for " + *ip 85 | } 86 | msg += ":" 87 | authPrompt := &survey.Select{ 88 | Message: msg, 89 | Options: []string{strPassword, strKeyFile, strAgent, "quit"}, 90 | } 91 | var auth ssh.AuthMethod 92 | for { 93 | survey.AskOne(authPrompt, &sshMethod) 94 | switch sshMethod { 95 | case strPassword: 96 | auth = promptPassword() 97 | case strKeyFile: 98 | auth = promptKeyFile() 99 | case strAgent: 100 | auth = sshAgent() 101 | default: 102 | return nil 103 | } 104 | if auth == nil { 105 | continue 106 | } 107 | return auth 108 | } 109 | } 110 | 111 | func promptClient(config *ssh.ClientConfig) *SSHClient { 112 | var host string 113 | hostPrompt := &survey.Input{ 114 | Message: "Target host IP address:", 115 | } 116 | var port string 117 | portPrompt := &survey.Input{ 118 | Message: "Target port:", 119 | Default: "22", 120 | } 121 | for { 122 | survey.AskOne(hostPrompt, &host) 123 | survey.AskOne(portPrompt, &port) 124 | client, err := ssh.Dial("tcp", host + ":" + port, config) 125 | if err != nil { 126 | if promptRetry(err) { 127 | continue 128 | } 129 | return nil 130 | } 131 | return &SSHClient{client, host} 132 | } 133 | } 134 | 135 | // NewSSH instantiates a new SSH client 136 | func NewSSH(user, ip string, auth ssh.AuthMethod) (*SSHClient, error) { 137 | sshConfig := &ssh.ClientConfig{ 138 | User: user, 139 | Auth: []ssh.AuthMethod{auth}, 140 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 141 | } 142 | client, err := ssh.Dial("tcp", ip + ":22", sshConfig) 143 | if err != nil { 144 | return nil, err 145 | } 146 | return &SSHClient{client, ip}, nil 147 | } 148 | 149 | // Run runs a series of commands on remote host and waits for exit 150 | func (client *SSHClient) Run(cmds []string) error { 151 | log := cfg.Config.Log 152 | for _, cmd := range cmds { 153 | if err := func(cmd string) error { 154 | session, err := client.NewSession() 155 | if err != nil { 156 | return err 157 | } 158 | defer session.Close() 159 | modes := ssh.TerminalModes{ 160 | ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud 161 | ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud 162 | } 163 | if err := session.RequestPty("xterm", 80, 40, modes); err != nil { 164 | return err 165 | } 166 | 167 | log.Info("%s: running command: %s", client.ip, cmd) 168 | bytes, err := session.CombinedOutput(cmd) 169 | sessionOutput := string(bytes) 170 | if err != nil { 171 | log.Error("%s: %s", client.ip, sessionOutput) 172 | return err 173 | } 174 | if sessionOutput != "" { 175 | log.Debug("%s: %s", client.ip, sessionOutput) 176 | } 177 | return nil 178 | }(cmd); err != nil { 179 | return err 180 | } 181 | } 182 | return nil 183 | } 184 | 185 | // CopyFile copies the contents of `fp` to `cfp` through client 186 | func (client *SSHClient) CopyFile(fp string, cfp string) error { 187 | sftpClient, err := sftp.NewClient(client.Client) 188 | if err != nil { 189 | return err 190 | } 191 | defer sftpClient.Close() 192 | 193 | dstFile, err := sftpClient.Create(cfp) 194 | if err != nil { 195 | return err 196 | } 197 | defer dstFile.Close() 198 | 199 | srcFile, err := os.Open(fp) 200 | if err != nil { 201 | return err 202 | } 203 | defer srcFile.Close() 204 | 205 | numBytes, err := io.Copy(dstFile, srcFile) 206 | if err != nil { 207 | return err 208 | } 209 | cfg.Config.Log.Debug("%s: %d bytes copied: %s --> %s", client.ip, numBytes, fp, cfp) 210 | return nil 211 | } 212 | 213 | // RemovePath removes file or directory `path` through client 214 | func (client *SSHClient) RemovePath(path string) error { 215 | sftpClient, err := sftp.NewClient(client.Client) 216 | if err != nil { 217 | return err 218 | } 219 | defer sftpClient.Close() 220 | 221 | if err := sftpClient.Remove(path); err != nil { 222 | return err 223 | } 224 | cfg.Config.Log.Debug("%s: removed: %s", client.ip, path) 225 | return nil 226 | } 227 | -------------------------------------------------------------------------------- /network/ssh_config.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "reflect" 7 | "github.com/ava-labs/avash/node" 8 | "gopkg.in/yaml.v2" 9 | ) 10 | 11 | // RawConfig is a raw network configuration 12 | type RawConfig struct { 13 | Hosts []struct{ 14 | Name, User, IP string 15 | } 16 | Nodes []struct{ 17 | Class string 18 | Flags node.FlagsYAML 19 | } 20 | Deploys []struct{ 21 | Host string 22 | Nodes []struct{ 23 | Name, Class string 24 | Flags node.FlagsYAML 25 | } 26 | } 27 | } 28 | 29 | // HostConfig is a host configuration 30 | type HostConfig struct { 31 | User, IP string 32 | } 33 | 34 | // NodeConfig is a node configuration 35 | type NodeConfig struct { 36 | Name string 37 | Flags node.Flags 38 | } 39 | 40 | // DeployConfig is a deploy instruction for a particular host 41 | type DeployConfig struct { 42 | User, IP string 43 | Nodes []NodeConfig 44 | } 45 | 46 | // InitConfig returns a network configuration from `cfgpath` 47 | func InitConfig(cfgpath string) ([]DeployConfig, error) { 48 | bytes, err := ioutil.ReadFile(cfgpath) 49 | if err != nil { 50 | return nil, err 51 | } 52 | var cfg RawConfig 53 | if err := yaml.Unmarshal(bytes, &cfg); err != nil { 54 | return nil, err 55 | } 56 | if err := validateConfig(cfg, cfgpath); err != nil { 57 | return nil, err 58 | } 59 | deployCfg := buildDeploy(cfg) 60 | return deployCfg, nil 61 | } 62 | 63 | func validateConfig(cfg RawConfig, cfgpath string) error { 64 | if len(cfg.Hosts) == 0 { 65 | return fmt.Errorf("Config must contain at least one host: %s", cfgpath) 66 | } 67 | isHost := make(map[string]bool) 68 | isIP := make(map[string]bool) 69 | for _, host := range cfg.Hosts { 70 | if host.Name == "" { 71 | return fmt.Errorf("%s: host missing name", cfgpath) 72 | } 73 | if isHost[host.Name] { 74 | return fmt.Errorf("%s: duplicate host name: %s", cfgpath, host.Name) 75 | } 76 | if host.User == "" { 77 | return fmt.Errorf("%s: host missing user: %s", cfgpath, host.Name) 78 | } 79 | if host.IP == "" { 80 | return fmt.Errorf("%s: host missing IP address: %s", cfgpath, host.Name) 81 | } 82 | if isIP[host.IP] { 83 | return fmt.Errorf("%s: duplicate host IP address: %s", cfgpath, host.IP) 84 | } 85 | isHost[host.Name] = true 86 | } 87 | if len(cfg.Nodes) == 0 { 88 | return fmt.Errorf("%s: config must contain at least one node definition", cfgpath) 89 | } 90 | isNode := make(map[string]bool) 91 | for _, n := range cfg.Nodes { 92 | if n.Class == "" { 93 | return fmt.Errorf("%s: node definition missing class name", cfgpath) 94 | } 95 | if isNode[n.Class] { 96 | return fmt.Errorf("%s: duplicate node class name: %s", cfgpath, n.Class) 97 | } 98 | isNode[n.Class] = true 99 | } 100 | if len(cfg.Deploys) == 0 { 101 | return fmt.Errorf("%s: config must contain at least one deploy", cfgpath) 102 | } 103 | isDeployHost := make(map[string]bool) 104 | for _, deploy := range cfg.Deploys { 105 | if deploy.Host == "" { 106 | return fmt.Errorf("%s: deploy missing host name", cfgpath) 107 | } 108 | if !isHost[deploy.Host] { 109 | return fmt.Errorf("%s: deploy with undefined host name: %s", cfgpath, deploy.Host) 110 | } 111 | if isDeployHost[deploy.Host] { 112 | return fmt.Errorf("%s: duplicate deploy host target: %s", cfgpath, deploy.Host) 113 | } 114 | if len(deploy.Nodes) == 0 { 115 | return fmt.Errorf("%s: deploy must target at least one node", cfgpath) 116 | } 117 | isDeployHost[deploy.Host] = true 118 | isDeployNode := make(map[string]bool) 119 | for _, n := range deploy.Nodes { 120 | if n.Name == "" { 121 | return fmt.Errorf("%s: deploy node missing name", cfgpath) 122 | } 123 | if isDeployNode[n.Name] { 124 | return fmt.Errorf("%s: duplicate deploy node name for host '%s': %s", cfgpath, deploy.Host, n.Name) 125 | } 126 | if n.Class == "" { 127 | return fmt.Errorf("%s: deploy node missing class name", cfgpath) 128 | } 129 | if !isNode[n.Class] { 130 | return fmt.Errorf("%s: deploy node with undefined class name: %s", cfgpath, n.Class) 131 | } 132 | isDeployNode[n.Name] = true 133 | } 134 | } 135 | return nil 136 | } 137 | 138 | func buildDeploy(config RawConfig) []DeployConfig { 139 | hostMap := make(map[string]HostConfig) 140 | for _, host := range config.Hosts { 141 | hostMap[host.Name] = HostConfig{host.User, host.IP} 142 | } 143 | nodeMap := make(map[string]node.FlagsYAML) 144 | for _, n := range config.Nodes { 145 | nodeMap[n.Class] = n.Flags 146 | } 147 | var deploys []DeployConfig 148 | for _, deploy := range config.Deploys { 149 | host := hostMap[deploy.Host] 150 | var nodes []NodeConfig 151 | for _, n := range deploy.Nodes { 152 | flags := nodeMap[n.Class] 153 | // Override node class flags with deployment flags 154 | overrideFlags(&flags, n.Flags) 155 | nodes = append(nodes, NodeConfig{ 156 | Name: n.Name, 157 | Flags: node.ConvertYAML(flags), 158 | }) 159 | } 160 | deploys = append(deploys, DeployConfig{ 161 | User: host.User, 162 | IP: host.IP, 163 | Nodes: nodes, 164 | }) 165 | } 166 | return deploys 167 | } 168 | 169 | func overrideFlags(origFlags *node.FlagsYAML, overFlags node.FlagsYAML) { 170 | orig := reflect.Indirect(reflect.ValueOf(origFlags)) 171 | over := reflect.ValueOf(overFlags) 172 | for i := 0; i < orig.NumField(); i++ { 173 | if orig.Field(i).IsNil() { 174 | orig.Field(i).Set(over.Field(i)) 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /network/startnode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | DATA_DIR="$(pwd)" 5 | CTNR_DIR="/data" 6 | 7 | # Node name 8 | NAME="" 9 | 10 | # Required flag defaults 11 | H_PORT="9650" 12 | S_PORT="9651" 13 | 14 | # Process flags 15 | FLAGS="" 16 | for arg in "$@" 17 | do 18 | case "$arg" in 19 | --name=*) 20 | NAME="${arg#*=}" 21 | ;; 22 | --assertions-enabled=*|\ 23 | --tx-fee=*|\ 24 | --network-id=*|\ 25 | --public-ip=*|\ 26 | --dynamic-update-duration=*|\ 27 | --dynamic-public-ip=*|\ 28 | --signature-verification-enabled=*|\ 29 | --api-admin-enabled=*|\ 30 | --api-ipcs-enabled=*|\ 31 | --api-keystore-enabled=*|\ 32 | --api-metrics-enabled=*|\ 33 | --http-tls-enabled=*|\ 34 | --http-tls-cert-file=*|\ 35 | --http-tls-key-file=*|\ 36 | --bootstrap-ips=*|\ 37 | --bootstrap-ids=*|\ 38 | --bootstrap-beacon-connection-timeout=*|\ 39 | --db-type=*|\ 40 | --log-level=*|\ 41 | --benchlist-duration=*|\ 42 | --benchlist-fail-threshold=*|\ 43 | --consensus-gossip-frequency=*|\ 44 | --benchlist-min-failing-duration=*|\ 45 | --benchlist-peer-summary-enabled=*|\ 46 | --version=*|\ 47 | --snow-avalanche-batch-size=*|\ 48 | --snow-avalanche-num-parents=*|\ 49 | --snow-sample-size=*|\ 50 | --snow-quorum-size=*|\ 51 | --snow-virtuous-commit-threshold=*|\ 52 | --snow-rogue-commit-threshold=*|\ 53 | --uptime-requirement=*|\ 54 | --min-delegator-stake=*|\ 55 | --consensus-shutdown-timeout=*|\ 56 | --min-delegation-fee=*|\ 57 | --min-validator-stake=*|\ 58 | --max-stake-duration=*|\ 59 | --max-validator-stake=*|\ 60 | --snow-concurrent-repolls=*|\ 61 | --stake-minting-period=*|\ 62 | --network-initial-timeout=*|\ 63 | --network-minimum-timeout=*|\ 64 | --network-maximum-timeout=*|\ 65 | --network-health-max-send-fail-rate=*|\ 66 | --index-enabled=*|\ 67 | --network-health-max-portion-send-queue-full=*|\ 68 | --network-health-max-time-since-msg-sent=*|\ 69 | --network-health-max-time-since-msg-received=*|\ 70 | --network-health-min-conn-peers=*|\ 71 | --network-timeout-coefficient=*|\ 72 | --network-timeout-halflife=*|\ 73 | --bootstrap-retry-warn-frequency=*|\ 74 | --bootstrap-retry-enabled=*|\ 75 | --health-check-averager-halflife=*|\ 76 | --health-check-frequency=*|\ 77 | --router-health-max-outstanding-requests=*|\ 78 | --router-health-max-drop-rate=*|\ 79 | --snow-epoch-first-transition=*|\ 80 | --snow-epoch-duration=*|\ 81 | --staking-enabled=*|\ 82 | --staking-disabled-weight=*|\ 83 | --staking-tls-key-file=*|\ 84 | --staking-tls-cert-file=*) 85 | FLAGS+="${arg} " 86 | ;; 87 | --api-auth-required=*|\ 88 | --api-auth-password-file=*|\ 89 | --min-stake-duration=*|\ 90 | --meter-vms-enabled=*|\ 91 | --whitelisted-subnets=*|\ 92 | --api-health-enabled=*|\ 93 | --config-file=*|\ 94 | --api-info-enabled=*|\ 95 | --network-compression-enabled=*|\ 96 | --ipcs-chain-ids=*|\ 97 | --ipcs-path=*|\ 98 | --log-display-level=*|\ 99 | --log-display-highlight=*|\ 100 | --fd-limit=*|\ 101 | --http-host=*|\ 102 | --db-dir=*|\ 103 | --build-dir=*|\ 104 | --log-dir=*|\ 105 | --plugin-dir=*) 106 | FLAGS+="${arg%=*}=$CTNR_DIR/${arg#*=} " 107 | ;; 108 | --data-dir=*) 109 | DATA_DIR+="/${arg#*=}" 110 | ;; 111 | --http-port=*) 112 | H_PORT="${arg#*=}" 113 | ;; 114 | --staking-port=*) 115 | S_PORT="${arg#*=}" 116 | ;; 117 | *) 118 | echo 119 | echo "ERROR: Unsupported flag '${arg%%=*}'" 120 | echo 121 | exit 1 122 | ;; 123 | esac 124 | done 125 | FLAGS+="--http-port=${H_PORT} " 126 | FLAGS+="--staking-port=${S_PORT} " 127 | 128 | if [ -z $NAME ]; then 129 | echo 130 | echo "ERROR: Missing node name argument (--name=[node name] required)" 131 | echo 132 | exit 1 133 | fi 134 | 135 | # Build and run Docker image as daemon 136 | AVALANCHE_COMMIT="$(git --git-dir="./avalanchego/.git" rev-parse --short HEAD)" 137 | docker_image="$(docker images -q avalanchego-$AVALANCHE_COMMIT:latest 2> /dev/null)" 138 | if [ -z $docker_image ]; then 139 | ./avalanchego/scripts/build_image.sh 140 | fi 141 | mkdir -p $DATA_DIR 142 | docker run -d --name $NAME \ 143 | -v $DATA_DIR:$CTNR_DIR \ 144 | -p $H_PORT:$H_PORT \ 145 | -p $S_PORT:$S_PORT \ 146 | avalanchego-$AVALANCHE_COMMIT \ 147 | /avalanchego/build/avalanche $FLAGS 148 | -------------------------------------------------------------------------------- /node/cli_tools.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // FlagsToArgs converts a `Flags` struct into a CLI command flag string 11 | func FlagsToArgs(flags Flags, basedir string, sepBase bool) ([]string, Metadata) { 12 | // Port targets 13 | httpPortString := strconv.FormatUint(uint64(flags.HTTPPort), 10) 14 | stakingPortString := strconv.FormatUint(uint64(flags.StakingPort), 10) 15 | 16 | // Paths/directories 17 | dbPath := basedir + "/" + flags.DBDir 18 | dbType := flags.DBType 19 | dataPath := basedir + "/" + flags.DataDir 20 | logPath := basedir + "/" + flags.LogDir 21 | if sepBase { 22 | dbPath = flags.DBDir 23 | dataPath = flags.DataDir 24 | logPath = flags.LogDir 25 | } 26 | 27 | wd, _ := os.Getwd() 28 | // If the path given in the flag doesn't begin with "/", treat it as relative 29 | // to the directory of the avash binary 30 | httpCertFile := flags.HTTPTLSCertFile 31 | if httpCertFile != "" && string(httpCertFile[0]) != "/" && !sepBase { 32 | httpCertFile = fmt.Sprintf("%s/%s", wd, httpCertFile) 33 | } 34 | 35 | httpKeyFile := flags.HTTPTLSKeyFile 36 | if httpKeyFile != "" && string(httpKeyFile[0]) != "/" && !sepBase { 37 | httpKeyFile = fmt.Sprintf("%s/%s", wd, httpKeyFile) 38 | } 39 | 40 | stakerCertFile := flags.StakingTLSCertFile 41 | if stakerCertFile != "" && string(stakerCertFile[0]) != "/" && !sepBase { 42 | stakerCertFile = fmt.Sprintf("%s/%s", wd, stakerCertFile) 43 | } 44 | 45 | stakerKeyFile := flags.StakingTLSKeyFile 46 | if stakerKeyFile != "" && string(stakerKeyFile[0]) != "/" && !sepBase { 47 | stakerKeyFile = fmt.Sprintf("%s/%s", wd, stakerKeyFile) 48 | } 49 | 50 | args := []string{ 51 | "--assertions-enabled=" + strconv.FormatBool(flags.AssertionsEnabled), 52 | "--version=" + strconv.FormatBool(flags.Version), 53 | "--tx-fee=" + strconv.FormatUint(uint64(flags.TxFee), 10), 54 | "--public-ip=" + flags.PublicIP, 55 | "--dynamic-update-duration=" + flags.DynamicUpdateDuration, 56 | "--dynamic-public-ip=" + flags.DynamicPublicIP, 57 | "--network-id=" + flags.NetworkID, 58 | "--signature-verification-enabled=" + strconv.FormatBool(flags.SignatureVerificationEnabled), 59 | "--api-admin-enabled=" + strconv.FormatBool(flags.APIAdminEnabled), 60 | "--api-ipcs-enabled=" + strconv.FormatBool(flags.APIIPCsEnabled), 61 | "--api-keystore-enabled=" + strconv.FormatBool(flags.APIKeystoreEnabled), 62 | "--api-metrics-enabled=" + strconv.FormatBool(flags.APIMetricsEnabled), 63 | "--http-host=" + flags.HTTPHost, 64 | "--http-port=" + httpPortString, 65 | "--http-tls-enabled=" + strconv.FormatBool(flags.HTTPTLSEnabled), 66 | "--http-tls-cert-file=" + httpCertFile, 67 | "--http-tls-key-file=" + httpKeyFile, 68 | "--bootstrap-ips=" + flags.BootstrapIPs, 69 | "--bootstrap-ids=" + flags.BootstrapIDs, 70 | "--bootstrap-beacon-connection-timeout=" + flags.BootstrapBeaconConnectionTimeout, 71 | "--db-type=" + dbType, 72 | "--db-dir=" + dbPath, 73 | "--plugin-dir=" + flags.PluginDir, 74 | "--build-dir=" + flags.BuildDir, 75 | "--log-level=" + flags.LogLevel, 76 | "--log-dir=" + logPath, 77 | "--log-display-level=" + flags.LogDisplayLevel, 78 | "--log-display-highlight=" + flags.LogDisplayHighlight, 79 | "--snow-avalanche-batch-size=" + strconv.Itoa(flags.SnowAvalancheBatchSize), 80 | "--snow-avalanche-num-parents=" + strconv.Itoa(flags.SnowAvalancheNumParents), 81 | "--snow-sample-size=" + strconv.Itoa(flags.SnowSampleSize), 82 | "--snow-quorum-size=" + strconv.Itoa(flags.SnowQuorumSize), 83 | "--snow-virtuous-commit-threshold=" + strconv.Itoa(flags.SnowVirtuousCommitThreshold), 84 | "--min-delegator-stake=" + strconv.Itoa(flags.MinDelegatorStake), 85 | "--consensus-shutdown-timeout=" + flags.ConsensusShutdownTimeout, 86 | "--consensus-gossip-frequency=" + flags.ConsensusGossipFrequency, 87 | "--min-delegation-fee=" + strconv.Itoa(flags.MinDelegationFee), 88 | "--min-validator-stake=" + strconv.Itoa(flags.MinValidatorStake), 89 | "--max-stake-duration=" + flags.MaxStakeDuration, 90 | "--max-validator-stake=" + strconv.Itoa(flags.MaxValidatorStake), 91 | "--snow-concurrent-repolls=" + strconv.Itoa(flags.SnowConcurrentRepolls), 92 | "--stake-minting-period=" + flags.StakeMintingPeriod, 93 | "--network-initial-timeout=" + flags.NetworkInitialTimeout, 94 | "--network-minimum-timeout=" + flags.NetworkMinimumTimeout, 95 | "--network-maximum-timeout=" + flags.NetworkMaximumTimeout, 96 | fmt.Sprintf("--network-health-max-send-fail-rate=%f", flags.NetworkHealthMaxSendFailRateKey), 97 | fmt.Sprintf("--network-health-max-portion-send-queue-full=%f", flags.NetworkHealthMaxPortionSendQueueFillKey), 98 | "--network-health-max-time-since-msg-sent=" + flags.NetworkHealthMaxTimeSinceMsgSentKey, 99 | "--network-health-max-time-since-msg-received=" + flags.NetworkHealthMaxTimeSinceMsgReceivedKey, 100 | "--network-health-min-conn-peers=" + strconv.Itoa(flags.NetworkHealthMinConnPeers), 101 | "--network-timeout-coefficient=" + strconv.Itoa(flags.NetworkTimeoutCoefficient), 102 | "--network-timeout-halflife=" + flags.NetworkTimeoutHalflife, 103 | "--network-peer-list-gossip-frequency=" + flags.NetworkPeerListGossipFrequency, 104 | "--network-peer-list-gossip-size=" + strconv.Itoa(flags.NetworkPeerListGossipSize), 105 | "--network-peer-list-size=" + strconv.Itoa(flags.NetworkPeerListSize), 106 | "--staking-enabled=" + strconv.FormatBool(flags.StakingEnabled), 107 | "--staking-port=" + stakingPortString, 108 | "--staking-disabled-weight=" + strconv.Itoa(flags.StakingDisabledWeight), 109 | "--staking-tls-key-file=" + stakerKeyFile, 110 | "--staking-tls-cert-file=" + stakerCertFile, 111 | "--api-auth-required=" + strconv.FormatBool(flags.APIAuthRequired), 112 | "--api-auth-password-file=" + flags.APIAuthPasswordFileKey, 113 | "--min-stake-duration=" + flags.MinStakeDuration, 114 | "--whitelisted-subnets=" + flags.WhitelistedSubnets, 115 | "--api-health-enabled=" + strconv.FormatBool(flags.APIHealthEnabled), 116 | "--config-file=" + flags.ConfigFile, 117 | "--api-info-enabled=" + strconv.FormatBool(flags.APIInfoEnabled), 118 | "--network-compression-enabled=" + strconv.FormatBool(flags.NetworkCompressionEnabled), 119 | "--ipcs-chain-ids=" + flags.IPCSChainIDs, 120 | "--ipcs-path=" + flags.IPCSPath, 121 | "--fd-limit=" + strconv.Itoa(flags.FDLimit), 122 | "--benchlist-duration=" + flags.BenchlistDuration, 123 | "--benchlist-fail-threshold=" + strconv.Itoa(flags.BenchlistFailThreshold), 124 | "--benchlist-min-failing-duration=" + flags.BenchlistMinFailingDuration, 125 | "--benchlist-peer-summary-enabled=" + strconv.FormatBool(flags.BenchlistPeerSummaryEnabled), 126 | fmt.Sprintf("--uptime-requirement=%f", flags.UptimeRequirement), 127 | "--bootstrap-retry-enabled=" + strconv.FormatBool(flags.RetryBootstrap), 128 | "--health-check-averager-halflife=" + flags.HealthCheckAveragerHalflifeKey, 129 | "--health-check-frequency=" + flags.HealthCheckFreqKey, 130 | "--router-health-max-outstanding-requests=" + strconv.Itoa(flags.RouterHealthMaxOutstandingRequestsKey), 131 | fmt.Sprintf("--router-health-max-drop-rate=%f", flags.RouterHealthMaxDropRateKey), 132 | "--index-enabled=" + strconv.FormatBool(flags.IndexEnabled), 133 | "--plugin-mode-enabled=" + strconv.FormatBool(flags.PluginModeEnabled), 134 | "--meter-vms-enabled=" + strconv.FormatBool(flags.MeterVMsEnabled), 135 | } 136 | if sepBase { 137 | args = append(args, "--data-dir="+basedir) 138 | } 139 | args = removeEmptyFlags(args) 140 | 141 | metadata := Metadata{ 142 | Serverhost: flags.PublicIP, 143 | Stakingport: stakingPortString, 144 | HTTPport: httpPortString, 145 | HTTPTLS: flags.HTTPTLSEnabled, 146 | Dbdir: dbPath, 147 | Datadir: dataPath, 148 | Logsdir: logPath, 149 | Loglevel: flags.LogLevel, 150 | StakingEnabled: flags.StakingEnabled, 151 | StakerCertPath: stakerCertFile, 152 | StakerKeyPath: stakerKeyFile, 153 | } 154 | 155 | return args, metadata 156 | } 157 | 158 | func removeEmptyFlags(args []string) []string { 159 | var res []string 160 | for _, f := range args { 161 | tmp := strings.TrimSpace(f) 162 | if !strings.HasSuffix(tmp, "=") { 163 | res = append(res, tmp) 164 | } 165 | } 166 | return res 167 | } 168 | -------------------------------------------------------------------------------- /node/config.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // Flags represents available CLI flags when starting a node 8 | type Flags struct { 9 | // Avash metadata 10 | ClientLocation string 11 | Meta string 12 | DataDir string 13 | 14 | // Assertions 15 | AssertionsEnabled bool 16 | 17 | // Version 18 | Version bool 19 | 20 | // TX fees 21 | TxFee uint 22 | 23 | // IP 24 | PublicIP string 25 | DynamicUpdateDuration string 26 | DynamicPublicIP string 27 | 28 | // Network ID 29 | NetworkID string 30 | 31 | // Crypto 32 | SignatureVerificationEnabled bool 33 | 34 | // APIs 35 | APIAdminEnabled bool 36 | APIIPCsEnabled bool 37 | APIKeystoreEnabled bool 38 | APIMetricsEnabled bool 39 | APIHealthEnabled bool 40 | APIInfoEnabled bool 41 | 42 | // HTTP 43 | HTTPHost string 44 | HTTPPort uint 45 | HTTPTLSEnabled bool 46 | HTTPTLSCertFile string 47 | HTTPTLSKeyFile string 48 | 49 | // Bootstrapping 50 | BootstrapIPs string 51 | BootstrapIDs string 52 | BootstrapBeaconConnectionTimeout string 53 | 54 | // Database 55 | DBType string 56 | DBDir string 57 | 58 | // Build 59 | BuildDir string 60 | 61 | // Plugins 62 | PluginDir string 63 | 64 | // Logging 65 | LogLevel string 66 | LogDir string 67 | LogDisplayLevel string 68 | LogDisplayHighlight string 69 | 70 | // Consensus 71 | SnowAvalancheBatchSize int 72 | SnowAvalancheNumParents int 73 | SnowSampleSize int 74 | SnowQuorumSize int 75 | SnowVirtuousCommitThreshold int 76 | SnowRogueCommitThreshold int 77 | SnowConcurrentRepolls int 78 | MinDelegatorStake int 79 | ConsensusShutdownTimeout string 80 | ConsensusGossipFrequency string 81 | MinDelegationFee int 82 | MinValidatorStake int 83 | MaxStakeDuration string 84 | MaxValidatorStake int 85 | 86 | // Staking 87 | StakingEnabled bool 88 | StakeMintingPeriod string 89 | StakingPort uint 90 | StakingDisabledWeight int 91 | StakingTLSKeyFile string 92 | StakingTLSCertFile string 93 | 94 | // Auth 95 | APIAuthRequired bool 96 | APIAuthPasswordFileKey string 97 | MinStakeDuration string 98 | 99 | // Whitelisted Subnets 100 | WhitelistedSubnets string 101 | 102 | // Config 103 | ConfigFile string 104 | 105 | // IPCS 106 | IPCSChainIDs string 107 | IPCSPath string 108 | 109 | // File Descriptor Limit 110 | FDLimit int 111 | 112 | // Benchlist 113 | BenchlistFailThreshold int 114 | BenchlistMinFailingDuration string 115 | BenchlistPeerSummaryEnabled bool 116 | BenchlistDuration string 117 | 118 | // Network 119 | NetworkInitialTimeout string 120 | NetworkMinimumTimeout string 121 | NetworkMaximumTimeout string 122 | NetworkHealthMaxSendFailRateKey float64 123 | NetworkHealthMaxPortionSendQueueFillKey float64 124 | NetworkHealthMaxTimeSinceMsgSentKey string 125 | NetworkHealthMaxTimeSinceMsgReceivedKey string 126 | NetworkHealthMinConnPeers int 127 | NetworkTimeoutCoefficient int 128 | NetworkTimeoutHalflife string 129 | NetworkCompressionEnabled bool 130 | 131 | // Peer List Gossiping 132 | NetworkPeerListGossipFrequency string 133 | NetworkPeerListGossipSize int 134 | NetworkPeerListSize int 135 | 136 | // Uptime Requirement 137 | UptimeRequirement float64 138 | 139 | // Retry 140 | RetryBootstrapWarnFrequency int 141 | RetryBootstrap bool 142 | 143 | // Health 144 | HealthCheckAveragerHalflifeKey string 145 | HealthCheckFreqKey string 146 | 147 | // Router 148 | RouterHealthMaxOutstandingRequestsKey int 149 | RouterHealthMaxDropRateKey float64 150 | 151 | IndexEnabled bool 152 | 153 | PluginModeEnabled bool 154 | 155 | MeterVMsEnabled bool 156 | } 157 | 158 | // FlagsYAML mimics Flags but uses pointers for proper YAML interpretation 159 | // Note: FlagsYAML and Flags must always be identical in fields, otherwise parsing will break 160 | type FlagsYAML struct { 161 | ClientLocation *string `yaml:"-"` 162 | Meta *string `yaml:"-"` 163 | DataDir *string `yaml:"-"` 164 | AssertionsEnabled *bool `yaml:"assertions-enabled,omitempty"` 165 | Version *bool `yaml:"version,omitempty"` 166 | TxFee *uint `yaml:"tx-fee,omitempty"` 167 | PublicIP *string `yaml:"public-ip,omitempty"` 168 | DynamicPublicIP *string `yaml:"dynamic-public-ip,omitempty"` 169 | NetworkID *string `yaml:"network-id,omitempty"` 170 | SignatureVerificationEnabled *bool `yaml:"signature-verification-enabled,omitempty"` 171 | APIAdminEnabled *bool `yaml:"api-admin-enabled,omitempty"` 172 | APIIPCsEnabled *bool `yaml:"api-ipcs-enabled,omitempty"` 173 | APIKeystoreEnabled *bool `yaml:"api-keystore-enabled,omitempty"` 174 | APIMetricsEnabled *bool `yaml:"api-metrics-enabled,omitempty"` 175 | HTTPHost *string `yaml:"http-host,omitempty"` 176 | HTTPPort *uint `yaml:"http-port,omitempty"` 177 | HTTPTLSEnabled *bool `yaml:"http-tls-enabled,omitempty"` 178 | HTTPTLSCertFile *string `yaml:"http-tls-cert-file,omitempty"` 179 | HTTPTLSKeyFile *string `yaml:"http-tls-key-file,omitempty"` 180 | BootstrapIPs *string `yaml:"bootstrap-ips,omitempty"` 181 | BootstrapIDs *string `yaml:"bootstrap-ids,omitempty"` 182 | BootstrapBeaconConnectionTimeout *string `yaml:"bootstrap-beacon-connection-timeout,omitempty"` 183 | DBType *string `yaml:"db-type,omitempty"` 184 | DBDir *string `yaml:"db-dir,omitempty"` 185 | BuildDir *string `yaml:"build-dir,omitempty"` 186 | PluginDir *string `yaml:"plugin-dir,omitempty"` 187 | LogLevel *string `yaml:"log-level,omitempty"` 188 | LogDir *string `yaml:"log-dir,omitempty"` 189 | LogDisplayLevel *string `yaml:"log-display-level,omitempty"` 190 | LogDisplayHighlight *string `yaml:"log-display-highlight,omitempty"` 191 | SnowAvalancheBatchSize *int `yaml:"snow-avalanche-batch-size,omitempty"` 192 | SnowAvalancheNumParents *int `yaml:"snow-avalanche-num-parents,omitempty"` 193 | SnowSampleSize *int `yaml:"snow-sample-size,omitempty"` 194 | SnowQuorumSize *int `yaml:"snow-quorum-size,omitempty"` 195 | SnowVirtuousCommitThreshold *int `yaml:"snow-virtuous-commit-threshold,omitempty"` 196 | SnowRogueCommitThreshold *int `yaml:"snow-rogue-commit-threshold,omitempty"` 197 | SnowConcurrentRepolls *int `yaml:"snow-concurrent-repolls,omitempty"` 198 | MinDelegatorStake *int `yaml:"min-delegator-stake,omitempty"` 199 | ConsensusShutdownTimeout *string `yaml:"consensus-shutdown-timeout,omitempty"` 200 | ConsensusGossipFrequency *string `yaml:"consensus-gossip-frequency,omitempty"` 201 | MinDelegationFee *int `yaml:"min-delegation-fee,omitempty"` 202 | MinValidatorStake *int `yaml:"min-validator-stake,omitempty"` 203 | MaxStakeDuration *string `yaml:"max-stake-duration,omitempty"` 204 | MaxValidatorStake *int `yaml:"max-validator-stake,omitempty"` 205 | StakeMintingPeriod *string `yaml:"stake-minting-period,omitempty"` 206 | NetworkInitialTimeout *string `yaml:"network-initial-timeout,omitempty"` 207 | NetworkMinimumTimeout *string `yaml:"network-minimum-timeout,omitempty"` 208 | NetworkMaximumTimeout *string `yaml:"network-maximum-timeout,omitempty"` 209 | NetworkHealthMaxSendFailRateKey *float64 `yaml:"network-health-max-send-fail-rate,omitempty"` 210 | NetworkHealthMaxPortionSendQueueFillKey *float64 `yaml:"network-health-max-portion-send-queue-full"` 211 | NetworkHealthMaxTimeSinceMsgSentKey *string `yaml:"network-health-max-time-since-msg-sent,omitempty"` 212 | NetworkHealthMaxTimeSinceMsgReceivedKey *string `yaml:"network-health-max-time-since-msg-received,omitempty"` 213 | NetworkHealthMinConnPeers *int `yaml:"network-health-min-conn-peers,omitempty"` 214 | NetworkTimeoutCoefficient *int `yaml:"network-timeout-coefficient,omitempty"` 215 | NetworkTimeoutHalflife *string `yaml:"network-timeout-halflife,omitempty"` 216 | NetworkPeerListGossipFrequency *string `yaml:"network-peer-list-gossip-frequency,omitempty"` 217 | NetworkPeerListGossipSize *int `yaml:"network-peer-list-gossip-size,omitempty"` 218 | NetworkPeerListSize *int `yaml:"network-peer-list-size,omitempty"` 219 | StakingEnabled *bool `yaml:"staking-enabled,omitempty"` 220 | StakingPort *uint `yaml:"staking-port,omitempty"` 221 | StakingDisabledWeight *int `yaml:"staking-disabled-weight,omitempty"` 222 | StakingTLSKeyFile *string `yaml:"staking-tls-key-file,omitempty"` 223 | StakingTLSCertFile *string `yaml:"staking-tls-cert-file,omitempty"` 224 | APIAuthRequired *bool `yaml:"api-auth-required,omitempty"` 225 | APIAuthPasswordFileKey *string `yaml:"api-auth-password-file,omitempty"` 226 | MinStakeDuration *string `yaml:"min-stake-duration,omitempty"` 227 | WhitelistedSubnets *string `yaml:"whitelisted-subnets,omitempty"` 228 | APIHealthEnabled *bool `yaml:"api-health-enabled,omitempty"` 229 | ConfigFile *string `yaml:"config-file,omitempty"` 230 | APIInfoEnabled *bool `yaml:"api-info-enabled,omitempty"` 231 | NetworkCompressionEnabled *bool `yaml:"network-compression-enabled,omitempty"` 232 | IPCSChainIDs *string `yaml:"ipcs-chain-ids,omitempty"` 233 | IPCSPath *string `yaml:"ipcs-path,omitempty"` 234 | FDLimit *int `yaml:"fd-limit,omitempty"` 235 | BenchlistDuration *string `yaml:"benchlist-duration,omitempty"` 236 | BenchlistFailThreshold *int `yaml:"benchlist-fail-threshold,omitempty"` 237 | BenchlistMinFailingDuration *string `yaml:"benchlist-min-failing-duration,omitempty"` 238 | BenchlistPeerSummaryEnabled *bool `yaml:"benchlist-peer-summary-enabled,omitempty"` 239 | UptimeRequirement *float64 `yaml:"uptime-requirement,omitempty"` 240 | RetryBootstrapWarnFrequency *uint `yaml:"bootstrap-retry-warn-frequency,omitempty"` 241 | RetryBootstrap *bool `yaml:"bootstrap-retry-enabled,omitempty"` 242 | HealthCheckAveragerHalflifeKey *string `yaml:"health-check-averager-halflife,omitempty"` 243 | HealthCheckFreqKey *string `yaml:"health-check-frequency,omitempty"` 244 | RouterHealthMaxOutstandingRequestsKey *int `yaml:"router-health-max-outstanding-requests,omitempty"` 245 | RouterHealthMaxDropRateKey *float64 `yaml:"router-health-max-drop-rate,omitempty"` 246 | IndexEnabled *bool `yaml:"index-enabled,omitempty"` 247 | PluginModeEnabled *bool `yaml:"plugin-mode-enabled,omitempty"` 248 | } 249 | 250 | // SetDefaults sets any zero-value field to its default value 251 | func (flags *Flags) SetDefaults() { 252 | f := reflect.Indirect(reflect.ValueOf(flags)) 253 | d := reflect.ValueOf(DefaultFlags()) 254 | for i := 0; i < f.NumField(); i++ { 255 | if f.Field(i).IsZero() { 256 | f.Field(i).Set(d.Field(i)) 257 | } 258 | } 259 | } 260 | 261 | // ConvertYAML converts a FlagsYAML struct into a Flags struct 262 | func ConvertYAML(flags FlagsYAML) Flags { 263 | var result Flags 264 | res := reflect.Indirect(reflect.ValueOf(&result)) 265 | f := reflect.ValueOf(flags) 266 | d := reflect.ValueOf(DefaultFlags()) 267 | for i := 0; i < res.NumField(); i++ { 268 | if f.Field(i).IsNil() { 269 | res.Field(i).Set(d.Field(i)) 270 | } else { 271 | res.Field(i).Set(f.Field(i).Elem()) 272 | } 273 | } 274 | return result 275 | } 276 | 277 | // DefaultFlags returns Avash-specific default node flags 278 | func DefaultFlags() Flags { 279 | return Flags{ 280 | ClientLocation: "", 281 | Meta: "", 282 | DataDir: "", 283 | AssertionsEnabled: true, 284 | Version: false, 285 | TxFee: 1000000, 286 | PublicIP: "127.0.0.1", 287 | DynamicUpdateDuration: "5m", 288 | DynamicPublicIP: "", 289 | NetworkID: "local", 290 | SignatureVerificationEnabled: true, 291 | APIAdminEnabled: true, 292 | APIIPCsEnabled: true, 293 | APIKeystoreEnabled: true, 294 | APIMetricsEnabled: true, 295 | HTTPHost: "127.0.0.1", 296 | HTTPPort: 9650, 297 | HTTPTLSEnabled: false, 298 | HTTPTLSCertFile: "", 299 | HTTPTLSKeyFile: "", 300 | BootstrapIPs: "", 301 | BootstrapIDs: "", 302 | BootstrapBeaconConnectionTimeout: "60s", 303 | DBType: "memdb", 304 | DBDir: "db", 305 | BuildDir: "", 306 | PluginDir: "", 307 | LogLevel: "info", 308 | LogDir: "logs", 309 | LogDisplayLevel: "", // defaults to the value provided to --log-level 310 | LogDisplayHighlight: "colors", 311 | SnowAvalancheBatchSize: 30, 312 | SnowAvalancheNumParents: 5, 313 | SnowSampleSize: 20, 314 | SnowQuorumSize: 16, 315 | SnowVirtuousCommitThreshold: 15, 316 | SnowRogueCommitThreshold: 20, 317 | SnowConcurrentRepolls: 4, 318 | MinDelegatorStake: 5000000, 319 | ConsensusShutdownTimeout: "5s", 320 | ConsensusGossipFrequency: "10s", 321 | MinDelegationFee: 20000, 322 | MinValidatorStake: 5000000, 323 | MaxStakeDuration: "8760h", 324 | MaxValidatorStake: 3000000000000000, 325 | StakeMintingPeriod: "8760h", 326 | NetworkInitialTimeout: "5s", 327 | NetworkMinimumTimeout: "5s", 328 | NetworkMaximumTimeout: "10s", 329 | NetworkHealthMaxSendFailRateKey: 0.9, 330 | NetworkHealthMaxPortionSendQueueFillKey: 0.9, 331 | NetworkHealthMaxTimeSinceMsgSentKey: "1m", 332 | NetworkHealthMaxTimeSinceMsgReceivedKey: "1m", 333 | NetworkHealthMinConnPeers: 1, 334 | NetworkTimeoutCoefficient: 2, 335 | NetworkTimeoutHalflife: "5m", 336 | NetworkPeerListGossipFrequency: "1m", 337 | NetworkPeerListGossipSize: 50, 338 | NetworkPeerListSize: 20, 339 | StakingEnabled: false, 340 | StakingPort: 9651, 341 | StakingDisabledWeight: 1, 342 | StakingTLSKeyFile: "", 343 | StakingTLSCertFile: "", 344 | APIAuthRequired: false, 345 | APIAuthPasswordFileKey: "", 346 | MinStakeDuration: "336h", 347 | APIHealthEnabled: true, 348 | ConfigFile: "", 349 | WhitelistedSubnets: "", 350 | APIInfoEnabled: true, 351 | NetworkCompressionEnabled: true, 352 | IPCSChainIDs: "", 353 | IPCSPath: "/tmp", 354 | FDLimit: 32768, 355 | BenchlistDuration: "1h", 356 | BenchlistFailThreshold: 10, 357 | BenchlistMinFailingDuration: "5m", 358 | BenchlistPeerSummaryEnabled: false, 359 | UptimeRequirement: 0.6, 360 | RetryBootstrapWarnFrequency: 50, 361 | RetryBootstrap: true, 362 | HealthCheckAveragerHalflifeKey: "10s", 363 | HealthCheckFreqKey: "30s", 364 | RouterHealthMaxOutstandingRequestsKey: 1024, 365 | RouterHealthMaxDropRateKey: 1, 366 | IndexEnabled: false, 367 | PluginModeEnabled: false, 368 | MeterVMsEnabled: false, 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /node/metadata.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | // Metadata struct for storing metadata, available to commands 4 | type Metadata struct { 5 | Serverhost string `json:"public-ip"` 6 | Stakingport string `json:"staking-port"` 7 | HTTPport string `json:"http-port"` 8 | HTTPTLS bool `json:"http-tls-enabled"` 9 | Dbdir string `json:"db-dir"` 10 | Datadir string `json:"data-dir"` 11 | Logsdir string `json:"log-dir"` 12 | Loglevel string `json:"log-level"` 13 | StakingEnabled bool `json:"staking-enabled"` 14 | StakerCertPath string `json:"staking-tls-cert-file"` 15 | StakerKeyPath string `json:"staking-tls-key-file"` 16 | } 17 | -------------------------------------------------------------------------------- /processmgr/process.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | package processmgr 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "io" 10 | "os" 11 | "os/exec" 12 | 13 | "github.com/ava-labs/avash/cfg" 14 | ) 15 | 16 | // InputHandler is a generic function for handling input from cin 17 | type InputHandler func(p []byte) (bool, error) 18 | 19 | // OutputHandler recieves the information 20 | type OutputHandler func(b bytes.Buffer) error 21 | 22 | // Process declares the necessary data for tracking a process 23 | type Process struct { 24 | cmdstr string 25 | args []string 26 | cmd *exec.Cmd 27 | name string 28 | proctype string 29 | metadata string 30 | running bool 31 | failed bool 32 | output io.ReadCloser 33 | errput io.ReadCloser 34 | input io.WriteCloser 35 | cout chan []byte 36 | cerr chan []byte 37 | cin chan []byte 38 | stop chan bool 39 | kill chan bool 40 | fail chan error 41 | inhandle InputHandler 42 | outhandle OutputHandler 43 | errhandle OutputHandler 44 | } 45 | 46 | // Start begins a new process 47 | func (p *Process) Start(done chan bool) { 48 | log := cfg.Config.Log 49 | if p.running { 50 | log.Error("Process is already running, cannot start: %s", p.name) 51 | done <- true 52 | return 53 | } 54 | log.Info("Starting process %s.", p.name) 55 | p.cmd = exec.Command(p.cmdstr, p.args...) 56 | log.Info("Command: %s\n", p.cmd.Args) 57 | 58 | selfStopped := false 59 | go func() { 60 | err := p.cmd.Start() 61 | if err != nil { 62 | p.fail <- err 63 | return 64 | } 65 | p.running = true 66 | p.failed = false 67 | done <- true 68 | err = p.cmd.Wait() 69 | if !selfStopped { 70 | p.fail <- err 71 | } 72 | }() 73 | 74 | closegen := func() { 75 | p.cmd.Stdin = nil 76 | p.cmd.Stderr = nil 77 | p.cmd.Stdout = nil 78 | p.cmd.Wait() 79 | p.cmd.Process = nil 80 | } 81 | 82 | defer closegen() 83 | 84 | for { 85 | select { 86 | case sp := <-p.stop: 87 | log.Info("Calling stop() on %s", p.name) 88 | if sp { 89 | selfStopped = true 90 | if err := p.endProcess(false); err != nil { 91 | log.Error("SIGINT failed on process: %s: %s", p.name, err.Error()) 92 | p.stop <- false 93 | return 94 | } 95 | log.Info("SIGINT called on process: %s", p.name) 96 | p.stop <- true 97 | return 98 | } 99 | case kl := <-p.kill: 100 | log.Info("Calling kill() on %s.", p.name) 101 | if kl { 102 | selfStopped = true 103 | if err := p.endProcess(true); err != nil { 104 | log.Error("SIGTERM failed on process: %s: %s", p.name, err.Error()) 105 | p.kill <- false 106 | return 107 | } 108 | log.Info("SIGTERM called on process: %s", p.name) 109 | p.kill <- true 110 | return 111 | } 112 | case fl := <-p.fail: 113 | p.failed = true 114 | errMsg := "inspect for process validity (command, args, flags) or FATAL output in related logs" 115 | if fl != nil { 116 | errMsg = fl.Error() 117 | } 118 | log.Error("Process failure: %s: %s", p.name, errMsg) 119 | // Specific case for a bad `p.cmd.Start()` call 120 | if !p.running { 121 | done <- false 122 | } 123 | p.running = false 124 | return 125 | } 126 | } 127 | } 128 | 129 | // Stop ends a process with SIGINT 130 | func (p *Process) Stop() error { 131 | if !p.running { 132 | return fmt.Errorf("Process is not running, cannot stop: %s", p.name) 133 | } 134 | p.stop <- true 135 | result := <-p.stop 136 | p.running = false 137 | if !result { 138 | p.failed = true 139 | return fmt.Errorf("Unable to properly stop process: %s", p.name) 140 | } 141 | return nil 142 | } 143 | 144 | // Kill ends a process with SIGTERM 145 | func (p *Process) Kill() error { 146 | if !p.running { 147 | return fmt.Errorf("Process is not running, cannot kill: %s", p.name) 148 | } 149 | p.kill <- true 150 | result := <-p.kill 151 | p.running = false 152 | if !result { 153 | p.failed = true 154 | return fmt.Errorf("Unable to properly kill process: %s", p.name) 155 | } 156 | return nil 157 | } 158 | 159 | func (p *Process) endProcess(killer bool) error { 160 | if killer { 161 | if err := p.cmd.Process.Kill(); err != nil { 162 | return err 163 | } 164 | } else { 165 | if err := p.cmd.Process.Signal(os.Interrupt); err != nil { 166 | if err := p.cmd.Process.Kill(); err != nil { 167 | return err 168 | } 169 | } 170 | } 171 | return nil 172 | } 173 | -------------------------------------------------------------------------------- /processmgr/process_test.go: -------------------------------------------------------------------------------- 1 | package processmgr 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | func newTestProcess(code uint) (*Process, chan bool) { 9 | var cmdstr string 10 | var args []string 11 | switch code { 12 | case 0: 13 | cmdstr = "sleep" 14 | args = []string{"10"} 15 | case 1: 16 | cmdstr = "fake-command" 17 | args = nil 18 | } 19 | return &Process{ 20 | cmdstr: cmdstr, 21 | args: args, 22 | stop: make(chan bool), 23 | kill: make(chan bool), 24 | fail: make(chan error), 25 | }, make(chan bool) 26 | } 27 | 28 | // Calls `p.Start` with `d` and returns a `WaitGroup` to block on `p` stopping 29 | func syncStart(p *Process, d chan bool) *sync.WaitGroup { 30 | var wg sync.WaitGroup 31 | wg.Add(1) 32 | go func() { 33 | defer wg.Done() 34 | p.Start(d) 35 | }() 36 | return &wg 37 | } 38 | 39 | func TestProcessStart(t *testing.T) { 40 | p1, d1 := newTestProcess(0) 41 | p2, d2 := newTestProcess(1) 42 | p3, d3 := newTestProcess(0) 43 | p4, d4 := newTestProcess(0) 44 | p5, d5 := newTestProcess(0) 45 | p6, d6 := newTestProcess(1) 46 | 47 | t.Run("GoodExec", func(t *testing.T) { 48 | go p1.Start(d1) 49 | <-d1 50 | 51 | t.Logf("%+v", p1) 52 | if proc := p1.cmd.Process; proc == nil { 53 | t.Fatalf("P.Cmd.Process returned %v expected not %v", proc, nil) 54 | } else if running := p1.running; running != true { 55 | t.Fatalf("P.Running returned %t expected %t", running, true) 56 | } else if failed := p1.failed; failed != false { 57 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 58 | } 59 | }) 60 | t.Run("BadExec", func(t *testing.T) { 61 | go p2.Start(d2) 62 | <-d2 63 | 64 | if proc := p2.cmd.Process; proc != nil { 65 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 66 | } else if running := p2.running; running != false { 67 | t.Fatalf("P.Running returned %t expected %t", running, false) 68 | } else if failed := p2.failed; failed != true { 69 | t.Fatalf("P.Failed returned %t expected %t", failed, true) 70 | } 71 | }) 72 | t.Run("KilledExec", func(t *testing.T) { 73 | done := make(chan bool) 74 | go func() { 75 | p3.Start(d3) 76 | done <- true 77 | }() 78 | <-d3 79 | p3.cmd.Process.Kill() 80 | <-done 81 | 82 | if proc := p3.cmd.Process; proc != nil { 83 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 84 | } else if running := p3.running; running != false { 85 | t.Fatalf("P.Running returned %t expected %t", running, false) 86 | } else if failed := p3.failed; failed != true { 87 | t.Fatalf("P.Failed returned %t expected %t", failed, true) 88 | } 89 | }) 90 | t.Run("Running", func(t *testing.T) { 91 | go p4.Start(d4) 92 | <-d4 93 | go p4.Start(d4) 94 | <-d4 95 | 96 | if proc := p4.cmd.Process; proc == nil { 97 | t.Fatalf("P.Cmd.Process returned %v expected not %v", proc, nil) 98 | } else if running := p4.running; running != true { 99 | t.Fatalf("P.Running returned %t expected %t", running, true) 100 | } else if failed := p4.failed; failed != false { 101 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 102 | } 103 | }) 104 | t.Run("GoodFailed", func(t *testing.T) { 105 | p5.failed = true 106 | go p5.Start(d5) 107 | <-d5 108 | 109 | if proc := p5.cmd.Process; proc == nil { 110 | t.Fatalf("P.Cmd.Process returned %v expected not %v", proc, nil) 111 | } else if running := p5.running; running != true { 112 | t.Fatalf("P.Running returned %t expected %t", running, true) 113 | } else if failed := p5.failed; failed != false { 114 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 115 | } 116 | }) 117 | t.Run("BadFailed", func(t *testing.T) { 118 | p6.failed = true 119 | go p6.Start(d6) 120 | <-d6 121 | 122 | if proc := p6.cmd.Process; proc != nil { 123 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 124 | } else if running := p6.running; running != false { 125 | t.Fatalf("P.Running returned %t expected %t", running, false) 126 | } else if failed := p6.failed; failed != true { 127 | t.Fatalf("P.Failed returned %t expected %t", failed, true) 128 | } 129 | }) 130 | 131 | if p1.cmd.Process != nil { 132 | p1.cmd.Process.Kill() 133 | } 134 | if p2.cmd.Process != nil { 135 | p2.cmd.Process.Kill() 136 | } 137 | if p3.cmd.Process != nil { 138 | p3.cmd.Process.Kill() 139 | } 140 | if p4.cmd.Process != nil { 141 | p4.cmd.Process.Kill() 142 | } 143 | if p5.cmd.Process != nil { 144 | p5.cmd.Process.Kill() 145 | } 146 | if p6.cmd.Process != nil { 147 | p6.cmd.Process.Kill() 148 | } 149 | } 150 | 151 | func TestProcessStop(t *testing.T) { 152 | p1, d1 := newTestProcess(0) 153 | p2, d2 := newTestProcess(0) 154 | p3, d3 := newTestProcess(1) 155 | 156 | t.Run("Running", func(t *testing.T) { 157 | wg := syncStart(p1, d1) 158 | <-d1 159 | p1.Stop() 160 | wg.Wait() 161 | 162 | if proc := p1.cmd.Process; proc != nil { 163 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 164 | } else if running := p1.running; running != false { 165 | t.Fatalf("P.Running returned %t expected %t", running, false) 166 | } else if failed := p1.failed; failed != false { 167 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 168 | } 169 | }) 170 | t.Run("Stopped", func(t *testing.T) { 171 | wg := syncStart(p2, d2) 172 | <-d2 173 | p2.Stop() 174 | p2.Stop() 175 | wg.Wait() 176 | 177 | if proc := p2.cmd.Process; proc != nil { 178 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 179 | } else if running := p2.running; running != false { 180 | t.Fatalf("P.Running returned %t expected %t", running, false) 181 | } else if failed := p2.failed; failed != false { 182 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 183 | } 184 | }) 185 | t.Run("Failed", func(t *testing.T) { 186 | wg := syncStart(p3, d3) 187 | <-d3 188 | p3.Stop() 189 | wg.Wait() 190 | 191 | if proc := p3.cmd.Process; proc != nil { 192 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 193 | } else if running := p3.running; running != false { 194 | t.Fatalf("P.Running returned %t expected %t", running, false) 195 | } else if failed := p3.failed; failed != true { 196 | t.Fatalf("P.Failed returned %t expected %t", failed, true) 197 | } 198 | }) 199 | 200 | if p1.cmd.Process != nil { 201 | p1.cmd.Process.Kill() 202 | } 203 | if p2.cmd.Process != nil { 204 | p2.cmd.Process.Kill() 205 | } 206 | if p3.cmd.Process != nil { 207 | p3.cmd.Process.Kill() 208 | } 209 | } 210 | 211 | func TestProcessKill(t *testing.T) { 212 | p1, d1 := newTestProcess(0) 213 | p2, d2 := newTestProcess(0) 214 | p3, d3 := newTestProcess(1) 215 | 216 | t.Run("Running", func(t *testing.T) { 217 | wg := syncStart(p1, d1) 218 | <-d1 219 | p1.Kill() 220 | wg.Wait() 221 | 222 | if proc := p1.cmd.Process; proc != nil { 223 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 224 | } else if running := p1.running; running != false { 225 | t.Fatalf("P.Running returned %t expected %t", running, false) 226 | } else if failed := p1.failed; failed != false { 227 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 228 | } 229 | }) 230 | t.Run("Stopped", func(t *testing.T) { 231 | wg := syncStart(p2, d2) 232 | <-d2 233 | p2.Stop() 234 | p2.Kill() 235 | wg.Wait() 236 | 237 | if proc := p2.cmd.Process; proc != nil { 238 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 239 | } else if running := p2.running; running != false { 240 | t.Fatalf("P.Running returned %t expected %t", running, false) 241 | } else if failed := p2.failed; failed != false { 242 | t.Fatalf("P.Failed returned %t expected %t", failed, false) 243 | } 244 | }) 245 | t.Run("Failed", func(t *testing.T) { 246 | wg := syncStart(p3, d3) 247 | <-d3 248 | p3.Kill() 249 | wg.Wait() 250 | 251 | if proc := p3.cmd.Process; proc != nil { 252 | t.Fatalf("P.Cmd.Process returned %v expected %v", proc, nil) 253 | } else if running := p3.running; running != false { 254 | t.Fatalf("P.Running returned %t expected %t", running, false) 255 | } else if failed := p3.failed; failed != true { 256 | t.Fatalf("P.Failed returned %t expected %t", failed, true) 257 | } 258 | }) 259 | 260 | if p1.cmd.Process != nil { 261 | p1.cmd.Process.Kill() 262 | } 263 | if p2.cmd.Process != nil { 264 | p2.cmd.Process.Kill() 265 | } 266 | if p3.cmd.Process != nil { 267 | p3.cmd.Process.Kill() 268 | } 269 | } -------------------------------------------------------------------------------- /processmgr/processmanager.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 AVA Labs, Inc. 2 | // All rights reserved. 3 | 4 | // Package processmgr manages processes launched by avash 5 | package processmgr 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/ava-labs/avash/cfg" 12 | "github.com/olekukonko/tablewriter" 13 | ) 14 | 15 | // ProcessManager is a system for managing processes in the system 16 | type ProcessManager struct { 17 | // Key: Process name 18 | // Value: The corresponding process 19 | processes map[string]*Process 20 | } 21 | 22 | // AddProcess places a process into the process manager with an associated name 23 | func (pm *ProcessManager) AddProcess(cmdstr string, proctype string, args []string, name string, metadata string, ih InputHandler, oh OutputHandler, eh OutputHandler) error { 24 | pname := strings.TrimSpace(name) 25 | if pname == "" { 26 | return fmt.Errorf("Process name cannot be empty") 27 | } 28 | _, exists := pm.processes[pname] 29 | if exists { 30 | return fmt.Errorf("Process with name %s already exists", pname) 31 | } 32 | cout := make(chan []byte) 33 | cerr := make(chan []byte) 34 | cin := make(chan []byte) 35 | stop := make(chan bool) 36 | kill := make(chan bool) 37 | fail := make(chan error) 38 | p := &Process{ 39 | cmdstr: cmdstr, 40 | args: args, 41 | name: pname, 42 | proctype: proctype, 43 | metadata: metadata, 44 | cout: cout, 45 | cerr: cerr, 46 | cin: cin, 47 | stop: stop, 48 | kill: kill, 49 | fail: fail, 50 | inhandle: ih, 51 | outhandle: oh, 52 | errhandle: eh, 53 | } 54 | pm.processes[name] = p 55 | 56 | return nil 57 | } 58 | 59 | // StartProcess starts the process at the name 60 | func (pm *ProcessManager) StartProcess(name string) error { 61 | p, ok := pm.processes[name] 62 | if !ok { 63 | return fmt.Errorf("Process does not exist, cannot start: %s", name) 64 | } 65 | done := make(chan bool) 66 | go p.Start(done) 67 | <-done 68 | return nil 69 | } 70 | 71 | // StopProcess stops the process at the name 72 | func (pm *ProcessManager) StopProcess(name string) error { 73 | p, ok := pm.processes[name] 74 | if !ok { 75 | return fmt.Errorf("Process does not exist, cannot stop: %s", name) 76 | } 77 | return p.Stop() 78 | } 79 | 80 | // StopAllProcesses calls Stop() on every running process, logging errors 81 | func (pm *ProcessManager) StopAllProcesses() { 82 | existsRunning := false 83 | for name := range pm.processes { 84 | if pm.processes[name].running { 85 | existsRunning = true 86 | err := pm.StopProcess(name) 87 | if err != nil { 88 | cfg.Config.Log.Error(err.Error()) 89 | } 90 | } 91 | } 92 | if !existsRunning { 93 | cfg.Config.Log.Info("No processes currently running.") 94 | } 95 | } 96 | 97 | // KillProcess kills the process at the name 98 | func (pm *ProcessManager) KillProcess(name string) error { 99 | p, ok := pm.processes[name] 100 | if !ok { 101 | return fmt.Errorf("Process does not exist, cannot kill: %s", name) 102 | } 103 | return p.Kill() 104 | } 105 | 106 | // KillAllProcesses calls Kill() on every running process, logging errors 107 | func (pm *ProcessManager) KillAllProcesses() { 108 | existsRunning := false 109 | for name := range pm.processes { 110 | if pm.processes[name].running { 111 | existsRunning = true 112 | err := pm.KillProcess(name) 113 | if err != nil { 114 | cfg.Config.Log.Error(err.Error()) 115 | } 116 | } 117 | } 118 | if !existsRunning { 119 | cfg.Config.Log.Info("No processes currently running.") 120 | } 121 | } 122 | 123 | // StartAllProcesses calls Start() on every stopped process, logging errors 124 | func (pm *ProcessManager) StartAllProcesses() { 125 | existsStopped := false 126 | for name := range pm.processes { 127 | if !pm.processes[name].running { 128 | existsStopped = true 129 | err := pm.StartProcess(name) 130 | if err != nil { 131 | cfg.Config.Log.Error(err.Error()) 132 | } 133 | } 134 | } 135 | if !existsStopped { 136 | cfg.Config.Log.Info("All processes currently running.") 137 | } 138 | } 139 | 140 | // RemoveProcess removes a process from the list of available named processes 141 | func (pm *ProcessManager) RemoveProcess(name string) error { 142 | if _, ok := pm.processes[name]; !ok { 143 | return fmt.Errorf("Process does not exist, cannot remove: %s", name) 144 | } 145 | if pm.processes[name].running { 146 | if err := pm.StopProcess(name); err != nil { 147 | return err 148 | } 149 | } 150 | delete(pm.processes, name) 151 | cfg.Config.Log.Info("Process removed: %s", name) 152 | return nil 153 | } 154 | 155 | // RemoveAllProcesses removes a process from the list of available named processes 156 | func (pm *ProcessManager) RemoveAllProcesses() { 157 | pm.StopAllProcesses() 158 | totalProcesses := len(pm.processes) 159 | processesRemoved := 0 160 | for name := range pm.processes { 161 | if err := pm.RemoveProcess(name); err != nil { 162 | cfg.Config.Log.Error(err.Error()) 163 | continue 164 | } 165 | processesRemoved++ 166 | } 167 | cfg.Config.Log.Info("%d/%d processes removed", processesRemoved, totalProcesses) 168 | } 169 | 170 | // ProcessTable returns a formatted metadata table for the data provided 171 | func (pm *ProcessManager) ProcessTable(table *tablewriter.Table) *tablewriter.Table { 172 | table.SetHeader([]string{"Name", "Status", "Metadata", "Command"}) 173 | table.SetBorder(false) 174 | 175 | table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgBlueColor}, 176 | tablewriter.Colors{tablewriter.Bold, tablewriter.BgMagentaColor, tablewriter.FgWhiteColor}, 177 | tablewriter.Colors{tablewriter.Bold, tablewriter.BgMagentaColor, tablewriter.FgWhiteColor}, 178 | tablewriter.Colors{tablewriter.Bold, tablewriter.BgMagentaColor, tablewriter.FgWhiteColor}) 179 | 180 | table.SetColumnColor(tablewriter.Colors{tablewriter.Bold}, 181 | tablewriter.Colors{tablewriter.Normal}, 182 | tablewriter.Colors{tablewriter.Normal}, 183 | tablewriter.Colors{tablewriter.Normal}) 184 | psd := pm.ProcessSummary() 185 | table.AppendBulk(*psd) 186 | table.SetReflowDuringAutoWrap(true) 187 | return table 188 | } 189 | 190 | // ProcessSummary returns data table of all processes and their statuses 191 | func (pm *ProcessManager) ProcessSummary() *[][]string { 192 | var data [][]string 193 | for _, val := range pm.processes { 194 | var running string 195 | if val.running { 196 | running = "running" 197 | } else if val.failed { 198 | running = "defunct" 199 | } else { 200 | running = "stopped" 201 | } 202 | cmd := val.cmdstr + " " + strings.Join(val.args, " ") 203 | line := []string{val.name, running, val.metadata, cmd} 204 | data = append(data, line) 205 | } 206 | return &data 207 | 208 | } 209 | 210 | // Metadata returns the metadata given the process name 211 | func (pm *ProcessManager) Metadata(name string) (string, error) { 212 | if name == "" { 213 | return "", fmt.Errorf("Process name required") 214 | } 215 | if p, ok := pm.processes[name]; ok { 216 | return p.metadata, nil 217 | } 218 | return "", fmt.Errorf("Process does not exist, cannot get metadata: %s", name) 219 | } 220 | 221 | // HasRunning returns true if there exists a running process, otherwise false 222 | func (pm *ProcessManager) HasRunning() bool { 223 | for _, val := range pm.processes { 224 | if val.running { 225 | return true 226 | } 227 | } 228 | return false 229 | } 230 | 231 | // ProcManager is a global 232 | var ProcManager = ProcessManager{ 233 | processes: make(map[string]*Process), 234 | } 235 | -------------------------------------------------------------------------------- /processmgr/processmanager_test.go: -------------------------------------------------------------------------------- 1 | package processmgr 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAddProcess(t *testing.T) { 12 | pm := ProcessManager{ 13 | processes: make(map[string]*Process), 14 | } 15 | 16 | cmd0 := "cmd0" 17 | proctype0 := "type0" 18 | args0 := []string{"arg0"} 19 | name0 := "test0" 20 | metadata0 := "data0" 21 | 22 | cmd1 := "cmd1" 23 | proctype1 := "type1" 24 | args1 := []string{"arg1"} 25 | name1 := "test1" 26 | metadata1 := "data1" 27 | 28 | testProcInitWith := func(t *testing.T, p *Process, cmd string, proctype string, args []string, name string, metadata string) { 29 | if p.cmdstr != cmd { 30 | t.Fatalf("P.Cmdstr returned %s expected %s", p.cmdstr, cmd) 31 | } else if p.proctype != proctype { 32 | t.Fatalf("P.Proctype returned %s expected %s", p.proctype, proctype) 33 | } else if !reflect.DeepEqual(p.args, args) { 34 | t.Fatalf("P.Args returned %v expected %v", p.args, args) 35 | } else if p.name != name { 36 | t.Fatalf("P.Name returned %s expected %s", p.name, name) 37 | } else if p.metadata != metadata { 38 | t.Fatalf("P.Metadata returned %s expected %s", p.metadata, metadata) 39 | } 40 | } 41 | 42 | t.Run("InitAdd", func(t *testing.T) { 43 | pm.AddProcess(cmd0, proctype0, args0, name0, metadata0, nil, nil, nil) 44 | 45 | if count := len(pm.processes); count != 1 { 46 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 47 | } else if _, ok := pm.processes[name0]; !ok { 48 | t.Fatalf("PM.Processes does not contain %s", name0) 49 | } 50 | 51 | testProcInitWith(t, pm.processes[name0], cmd0, proctype0, args0, name0, metadata0) 52 | }) 53 | t.Run("DuplicateAdd", func(t *testing.T) { 54 | pm.AddProcess("fake", "fake", []string{"fake"}, name0, "fake", nil, nil, nil) 55 | 56 | if count := len(pm.processes); count != 1 { 57 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 58 | } else if _, ok := pm.processes[name0]; !ok { 59 | t.Fatalf("PM.Processes does not contain %s", name0) 60 | } 61 | 62 | testProcInitWith(t, pm.processes[name0], cmd0, proctype0, args0, name0, metadata0) 63 | }) 64 | t.Run("NewAdd", func(t *testing.T) { 65 | pm.AddProcess(cmd1, proctype1, args1, name1, metadata1, nil, nil, nil) 66 | 67 | if count := len(pm.processes); count != 2 { 68 | t.Fatalf("PM.Processes has length %d expected %d", count, 2) 69 | } else if _, ok := pm.processes[name0]; !ok { 70 | t.Fatalf("PM.Processes does not contain %s", name0) 71 | } else if _, ok := pm.processes[name1]; !ok { 72 | t.Fatalf("PM.Processes does not contain %s", name1) 73 | } 74 | 75 | testProcInitWith(t, pm.processes[name1], cmd1, proctype1, args1, name1, metadata1) 76 | }) 77 | t.Run("EmptyNameAdd", func(t *testing.T) { 78 | pm.AddProcess("fake", "fake", []string{"fake"}, "", "fake", nil, nil, nil) 79 | 80 | if count := len(pm.processes); count != 2 { 81 | t.Fatalf("PM.Processes has length %d expected %d", count, 2) 82 | } else if _, ok := pm.processes[""]; ok { 83 | t.Fatalf("PM.Processes contains process with empty name") 84 | } else if _, ok := pm.processes[name0]; !ok { 85 | t.Fatalf("PM.Processes does not contain %s", name0) 86 | } else if _, ok := pm.processes[name1]; !ok { 87 | t.Fatalf("PM.Processes does not contain %s", name1) 88 | } 89 | }) 90 | } 91 | 92 | func TestRemoveProcess(t *testing.T) { 93 | pm := ProcessManager{ 94 | processes: make(map[string]*Process), 95 | } 96 | name0 := "test0" 97 | name1 := "test1" 98 | name2 := "fake" 99 | pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name0, "data", nil, nil, nil) 100 | pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name1, "data", nil, nil, nil) 101 | 102 | t.Run("ExistingProc", func(t *testing.T) { 103 | pm.RemoveProcess(name0) 104 | 105 | if count := len(pm.processes); count != 1 { 106 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 107 | } else if _, ok := pm.processes[name0]; ok { 108 | t.Fatalf("PM.Processes contains %s", name0) 109 | } else if _, ok := pm.processes[name1]; !ok { 110 | t.Fatalf("PM.Processes does not contain %s", name1) 111 | } 112 | }) 113 | t.Run("MissingProc", func(t *testing.T) { 114 | err := pm.RemoveProcess(name2) 115 | 116 | if dneErr := fmt.Sprintf("Process does not exist, cannot remove: %s", name2); err.Error() != dneErr { 117 | t.Fatalf("PM.RemoveProcess returned %v expected %v", err, dneErr) 118 | } else if count := len(pm.processes); count != 1 { 119 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 120 | } else if _, ok := pm.processes[name1]; !ok { 121 | t.Fatalf("PM.Processes does not contain %s", name1) 122 | } 123 | }) 124 | } 125 | 126 | func TestStartProcess(t *testing.T) { 127 | pm := ProcessManager{ 128 | processes: make(map[string]*Process), 129 | } 130 | name0 := "test" 131 | name1 := "fake" 132 | pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name0, "data", nil, nil, nil) 133 | 134 | t.Run("ExistingProc", func(t *testing.T) { 135 | err := pm.StartProcess(name0) 136 | 137 | if err != nil { 138 | t.Fatalf("PM.StartProcess returned %v expected %v", err, nil) 139 | } else if count := len(pm.processes); count != 1 { 140 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 141 | } else if _, ok := pm.processes[name0]; !ok { 142 | t.Fatalf("PM.Processes does not contain %s", name0) 143 | } 144 | }) 145 | t.Run("MissingProc", func(t *testing.T) { 146 | err := pm.StartProcess(name1) 147 | 148 | if dneErr := fmt.Sprintf("Process does not exist, cannot start: %s", name1); err.Error() != dneErr { 149 | t.Fatalf("PM.StartProcess returned %v expected %v", err, dneErr) 150 | } else if count := len(pm.processes); count != 1 { 151 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 152 | } else if _, ok := pm.processes[name0]; !ok { 153 | t.Fatalf("PM.Processes does not contain %s", name0) 154 | } 155 | }) 156 | } 157 | 158 | func TestRemoveAllProcesses(t *testing.T) { 159 | pm := ProcessManager{ 160 | processes: make(map[string]*Process), 161 | } 162 | 163 | name := "procname-" 164 | for i := 0; i < 5; i++ { 165 | _ = pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name+string(rune(i)), "data", nil, nil, nil) 166 | } 167 | 168 | assert.Len(t, pm.processes, 5) 169 | pm.RemoveAllProcesses() 170 | assert.Len(t, pm.processes, 0) 171 | } 172 | 173 | func TestStopProcess(t *testing.T) { 174 | pm := ProcessManager{ 175 | processes: make(map[string]*Process), 176 | } 177 | name0 := "test" 178 | name1 := "fake" 179 | pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name0, "data", nil, nil, nil) 180 | 181 | t.Run("ExistingProc", func(t *testing.T) { 182 | pm.StopProcess(name0) 183 | 184 | if count := len(pm.processes); count != 1 { 185 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 186 | } else if _, ok := pm.processes[name0]; !ok { 187 | t.Fatalf("PM.Processes does not contain %s", name0) 188 | } 189 | }) 190 | t.Run("MissingProc", func(t *testing.T) { 191 | err := pm.StopProcess(name1) 192 | 193 | if dneErr := fmt.Sprintf("Process does not exist, cannot stop: %s", name1); err.Error() != dneErr { 194 | t.Fatalf("PM.StopProcess returned %v expected %v", err, dneErr) 195 | } else if count := len(pm.processes); count != 1 { 196 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 197 | } else if _, ok := pm.processes[name0]; !ok { 198 | t.Fatalf("PM.Processes does not contain %s", name0) 199 | } 200 | }) 201 | } 202 | 203 | func TestKillProcess(t *testing.T) { 204 | pm := ProcessManager{ 205 | processes: make(map[string]*Process), 206 | } 207 | name0 := "test" 208 | name1 := "fake" 209 | pm.AddProcess("cmd", "fake-cmd", []string{"arg"}, name0, "data", nil, nil, nil) 210 | 211 | t.Run("ExistingProc", func(t *testing.T) { 212 | pm.KillProcess(name0) 213 | 214 | if count := len(pm.processes); count != 1 { 215 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 216 | } else if _, ok := pm.processes[name0]; !ok { 217 | t.Fatalf("PM.Processes does not contain %s", name0) 218 | } 219 | }) 220 | t.Run("MissingProc", func(t *testing.T) { 221 | err := pm.KillProcess(name1) 222 | 223 | if dneErr := fmt.Sprintf("Process does not exist, cannot kill: %s", name1); err.Error() != dneErr { 224 | t.Fatalf("PM.KillProcess returned %v expected %v", err, dneErr) 225 | } else if count := len(pm.processes); count != 1 { 226 | t.Fatalf("PM.Processes has length %d expected %d", count, 1) 227 | } else if _, ok := pm.processes[name0]; !ok { 228 | t.Fatalf("PM.Processes does not contain %s", name0) 229 | } 230 | }) 231 | } 232 | -------------------------------------------------------------------------------- /scripts/five_node_staking.lua: -------------------------------------------------------------------------------- 1 | cmds = { 2 | "startnode node1 --db-type=memdb --staking-enabled=true --http-port=9650 --staking-port=9651 --log-level=debug --bootstrap-ips= --staking-tls-cert-file=certs/keys1/staker.crt --staking-tls-key-file=certs/keys1/staker.key", 3 | "startnode node2 --db-type=memdb --staking-enabled=true --http-port=9652 --staking-port=9653 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys2/staker.crt --staking-tls-key-file=certs/keys2/staker.key", 4 | "startnode node3 --db-type=memdb --staking-enabled=true --http-port=9654 --staking-port=9655 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys3/staker.crt --staking-tls-key-file=certs/keys3/staker.key", 5 | "startnode node4 --db-type=memdb --staking-enabled=true --http-port=9656 --staking-port=9657 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys4/staker.crt --staking-tls-key-file=certs/keys4/staker.key", 6 | "startnode node5 --db-type=memdb --staking-enabled=true --http-port=9658 --staking-port=9659 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys5/staker.crt --staking-tls-key-file=certs/keys5/staker.key", 7 | } 8 | 9 | for key, cmd in ipairs(cmds) do 10 | avash_call(cmd) 11 | end 12 | -------------------------------------------------------------------------------- /scripts/mixed_staking_network.lua: -------------------------------------------------------------------------------- 1 | cmds = { 2 | "startnode node1 --db-type=memdb --staking-enabled=true --http-port=9650 --staking-port=9651 --log-level=debug --bootstrap-ips= --staking-tls-cert-file=certs/keys1/staker.crt --staking-tls-key-file=certs/keys1/staker.key", 3 | "startnode node2 --db-type=memdb --staking-enabled=true --http-port=9652 --staking-port=9653 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys2/staker.crt --staking-tls-key-file=certs/keys2/staker.key", 4 | "startnode node3 --db-type=memdb --staking-enabled=true --http-port=9654 --staking-port=9655 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys3/staker.crt --staking-tls-key-file=certs/keys3/staker.key", 5 | "startnode node4 --db-type=memdb --staking-enabled=true --http-port=9656 --staking-port=9657 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys4/staker.crt --staking-tls-key-file=certs/keys4/staker.key", 6 | "startnode node5 --db-type=memdb --staking-enabled=true --http-port=9658 --staking-port=9659 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys5/staker.crt --staking-tls-key-file=certs/keys5/staker.key", 7 | "startnode node6 --db-type=memdb --staking-enabled=true --http-port=9660 --staking-port=9671 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys6/staker.crt --staking-tls-key-file=certs/keys6/staker.key", 8 | "startnode node7 --db-type=memdb --staking-enabled=true --http-port=9662 --staking-port=9673 --log-level=debug --bootstrap-ips=127.0.0.1:9651 --bootstrap-ids=NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg --staking-tls-cert-file=certs/keys7/staker.crt --staking-tls-key-file=certs/keys7/staker.key", 9 | } 10 | 11 | for key, cmd in ipairs(cmds) do 12 | avash_call(cmd) 13 | end 14 | -------------------------------------------------------------------------------- /utils/logging/level.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "github.com/ava-labs/avalanchego/utils/logging" 5 | ) 6 | 7 | // Level ... 8 | type Level = logging.Level 9 | 10 | // ToLevel ... 11 | func ToLevel(l string) (Level, error) { 12 | return logging.ToLevel(l) 13 | } 14 | -------------------------------------------------------------------------------- /utils/logging/log.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "github.com/ava-labs/avalanchego/utils/logging" 5 | ) 6 | 7 | // Log is a wrapper struct for the shell output log 8 | type Log struct { 9 | logging.Logger 10 | } 11 | 12 | // Config is a struct representation of the `log` field in the config file 13 | type Config = logging.Config 14 | 15 | // New ... 16 | func New(config Config) (*Log, error) { 17 | logFactory := logging.NewFactory(config) 18 | log, err := logFactory.Make("avash") 19 | if err != nil { 20 | return nil, err 21 | } 22 | return &Log{log}, nil 23 | } 24 | 25 | // SetLevel ... 26 | func (l *Log) SetLevel(out Output, lvl Level) { 27 | switch out { 28 | case Terminal: 29 | l.SetDisplayLevel(lvl) 30 | case LogFile: 31 | l.SetLogLevel(lvl) 32 | case All: 33 | l.SetDisplayLevel(lvl) 34 | l.SetLogLevel(lvl) 35 | default: 36 | // do nothing 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /utils/logging/output.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Output ... 9 | type Output int 10 | 11 | // Enum ... 12 | const ( 13 | Terminal Output = iota 14 | LogFile 15 | All 16 | ) 17 | 18 | // ToOutput ... 19 | func ToOutput(s string) (Output, error) { 20 | switch strings.ToUpper(s) { 21 | case "TERMINAL": 22 | return Terminal, nil 23 | case "LOGFILE": 24 | return LogFile, nil 25 | case "ALL": 26 | return All, nil 27 | default: 28 | return Terminal, fmt.Errorf("unknown log output: %s", s) 29 | } 30 | } 31 | 32 | func (o Output) String() string { 33 | switch o { 34 | case Terminal: 35 | return "TERMINAL" 36 | case LogFile: 37 | return "LOGFILE" 38 | case All: 39 | return "ALL" 40 | default: 41 | return "?????" 42 | } 43 | } 44 | --------------------------------------------------------------------------------